mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 00:33:55 -06:00
* rust: better integration with clippy, rustfmt and rustdoc
* rust: interior mutability types * rust: add a bit operations module * rust: first part of QOM rework * kvm: remove unnecessary #ifdef * clock: small cleanups, improve handling of Clock lifetimes -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmdZqFkUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroOzRwf/SYUD+CJCn2x7kUH/JG893jwN1WbJ meGZ0PQDUpOZJFWg6T4g0MuW4O+Wevy2pF4SfGojgqaYxKBbTQVkeliDEMyNUxpr vSKXego0K3pkX3cRDXNVTaXFbsHsMt/3pfzMQM6ocF9qbL+Emvx7Og6WdAcyJ4hc lA17EHlnrWKUSnqN/Ow/pZXsa4ijCklXFFh4barfbdGVhMQc2QekUU45GsP2AvGT NkXTQC05HqxBaAIDeSxbprDSzNihyT71dAooVoxqKboprPu5uoUSJwgaD8rADPr4 EOfsz61V4mji+DWDcIzTtYoAdY41vVXI9lvCKOcCFkimA29xO0W6P7mG2w== =JSh5 -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * rust: better integration with clippy, rustfmt and rustdoc * rust: interior mutability types * rust: add a bit operations module * rust: first part of QOM rework * kvm: remove unnecessary #ifdef * clock: small cleanups, improve handling of Clock lifetimes # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmdZqFkUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroOzRwf/SYUD+CJCn2x7kUH/JG893jwN1WbJ # meGZ0PQDUpOZJFWg6T4g0MuW4O+Wevy2pF4SfGojgqaYxKBbTQVkeliDEMyNUxpr # vSKXego0K3pkX3cRDXNVTaXFbsHsMt/3pfzMQM6ocF9qbL+Emvx7Og6WdAcyJ4hc # lA17EHlnrWKUSnqN/Ow/pZXsa4ijCklXFFh4barfbdGVhMQc2QekUU45GsP2AvGT # NkXTQC05HqxBaAIDeSxbprDSzNihyT71dAooVoxqKboprPu5uoUSJwgaD8rADPr4 # EOfsz61V4mji+DWDcIzTtYoAdY41vVXI9lvCKOcCFkimA29xO0W6P7mG2w== # =JSh5 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 11 Dec 2024 09:57:29 EST # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (49 commits) rust: qom: change the parent type to an associated type rust: qom: split ObjectType from ObjectImpl trait rust: qom: move bridge for TypeInfo functions out of pl011 rust: qdev: move bridge for realize and reset functions out of pl011 rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro rust: qom: move ClassInitImpl to the instance side rust: qom: convert type_info! macro to an associated const rust: qom: rename Class trait to ClassInitImpl rust: qom: add default definitions for ObjectImpl rust: add a bit operation module rust: add bindings for interrupt sources rust: define prelude rust: cell: add BQL-enforcing RefCell variant rust: cell: add BQL-enforcing Cell variant bql: check that the BQL is not dropped within marked sections qom/object: Remove type_register() script/codeconverter/qom_type_info: Deprecate MakeTypeRegisterStatic and MakeTypeRegisterNotStatic ui: Replace type_register() with type_register_static() target/xtensa: Replace type_register() with type_register_static() target/sparc: Replace type_register() with type_register_static() ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
1eec82cc06
67 changed files with 1927 additions and 445 deletions
|
@ -40,7 +40,7 @@ build-system-ubuntu:
|
||||||
job: amd64-ubuntu2204-container
|
job: amd64-ubuntu2204-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: ubuntu2204
|
IMAGE: ubuntu2204
|
||||||
CONFIGURE_ARGS: --enable-docs
|
CONFIGURE_ARGS: --enable-docs --enable-rust
|
||||||
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
|
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build
|
MAKE_CHECK_ARGS: check-build
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ build-system-debian:
|
||||||
job: amd64-debian-container
|
job: amd64-debian-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian
|
IMAGE: debian
|
||||||
CONFIGURE_ARGS: --with-coroutine=sigaltstack
|
CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust
|
||||||
TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu
|
TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu
|
||||||
sparc-softmmu xtensa-softmmu
|
sparc-softmmu xtensa-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build
|
MAKE_CHECK_ARGS: check-build
|
||||||
|
|
|
@ -46,3 +46,27 @@ check-python-tox:
|
||||||
QEMU_JOB_OPTIONAL: 1
|
QEMU_JOB_OPTIONAL: 1
|
||||||
needs:
|
needs:
|
||||||
job: python-container
|
job: python-container
|
||||||
|
|
||||||
|
check-rust-tools-nightly:
|
||||||
|
extends: .base_job_template
|
||||||
|
stage: test
|
||||||
|
image: $CI_REGISTRY_IMAGE/qemu/fedora-rust-nightly:$QEMU_CI_CONTAINER_TAG
|
||||||
|
script:
|
||||||
|
- source scripts/ci/gitlab-ci-section
|
||||||
|
- section_start test "Running Rust code checks"
|
||||||
|
- cd build
|
||||||
|
- pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} fmt --check
|
||||||
|
- make clippy
|
||||||
|
- make rustdoc
|
||||||
|
- section_end test
|
||||||
|
variables:
|
||||||
|
GIT_DEPTH: 1
|
||||||
|
allow_failure: true
|
||||||
|
needs:
|
||||||
|
- job: build-system-fedora-rust-nightly
|
||||||
|
artifacts: true
|
||||||
|
artifacts:
|
||||||
|
when: on_success
|
||||||
|
expire_in: 2 days
|
||||||
|
paths:
|
||||||
|
- rust/target/doc
|
||||||
|
|
|
@ -1731,7 +1731,7 @@ static void armsse_register_types(void)
|
||||||
.class_init = armsse_class_init,
|
.class_init = armsse_class_init,
|
||||||
.class_data = (void *)&armsse_variants[i],
|
.class_data = (void *)&armsse_variants[i],
|
||||||
};
|
};
|
||||||
type_register(&ti);
|
type_register_static(&ti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2065,8 +2065,8 @@ static const TypeInfo smmuv3_iommu_memory_region_info = {
|
||||||
|
|
||||||
static void smmuv3_register_types(void)
|
static void smmuv3_register_types(void)
|
||||||
{
|
{
|
||||||
type_register(&smmuv3_type_info);
|
type_register_static(&smmuv3_type_info);
|
||||||
type_register(&smmuv3_iommu_memory_region_info);
|
type_register_static(&smmuv3_iommu_memory_region_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(smmuv3_register_types)
|
type_init(smmuv3_register_types)
|
||||||
|
|
|
@ -1894,7 +1894,7 @@ static void m25p80_register_types(void)
|
||||||
.class_init = m25p80_class_init,
|
.class_init = m25p80_class_init,
|
||||||
.class_data = (void *)&known_devices[i],
|
.class_data = (void *)&known_devices[i],
|
||||||
};
|
};
|
||||||
type_register(&ti);
|
type_register_static(&ti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,16 +44,12 @@ Clock *clock_new(Object *parent, const char *name)
|
||||||
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque,
|
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque,
|
||||||
unsigned int events)
|
unsigned int events)
|
||||||
{
|
{
|
||||||
|
assert(OBJECT(clk)->parent);
|
||||||
clk->callback = cb;
|
clk->callback = cb;
|
||||||
clk->callback_opaque = opaque;
|
clk->callback_opaque = opaque;
|
||||||
clk->callback_events = events;
|
clk->callback_events = events;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clock_clear_callback(Clock *clk)
|
|
||||||
{
|
|
||||||
clock_set_callback(clk, NULL, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool clock_set(Clock *clk, uint64_t period)
|
bool clock_set(Clock *clk, uint64_t period)
|
||||||
{
|
{
|
||||||
if (clk->period == period) {
|
if (clk->period == period) {
|
||||||
|
@ -168,6 +164,16 @@ static void clock_period_prop_get(Object *obj, Visitor *v, const char *name,
|
||||||
visit_type_uint64(v, name, &period, errp);
|
visit_type_uint64(v, name, &period, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clock_unparent(Object *obj)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Callback are registered by the parent, which might die anytime after
|
||||||
|
* it's unparented the children. Avoid having a callback to a deleted
|
||||||
|
* object in case the clock is still referenced somewhere else (eg: by
|
||||||
|
* a clock output).
|
||||||
|
*/
|
||||||
|
clock_set_callback(CLOCK(obj), NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void clock_initfn(Object *obj)
|
static void clock_initfn(Object *obj)
|
||||||
{
|
{
|
||||||
|
@ -200,11 +206,17 @@ static void clock_finalizefn(Object *obj)
|
||||||
g_free(clk->canonical_path);
|
g_free(clk->canonical_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clock_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
klass->unparent = clock_unparent;
|
||||||
|
}
|
||||||
|
|
||||||
static const TypeInfo clock_info = {
|
static const TypeInfo clock_info = {
|
||||||
.name = TYPE_CLOCK,
|
.name = TYPE_CLOCK,
|
||||||
.parent = TYPE_OBJECT,
|
.parent = TYPE_OBJECT,
|
||||||
.instance_size = sizeof(Clock),
|
.instance_size = sizeof(Clock),
|
||||||
.instance_init = clock_initfn,
|
.instance_init = clock_initfn,
|
||||||
|
.class_init = clock_class_init,
|
||||||
.instance_finalize = clock_finalizefn,
|
.instance_finalize = clock_finalizefn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* Add a new clock in a device
|
* Add a new clock in a device
|
||||||
*/
|
*/
|
||||||
static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
|
static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
|
||||||
bool output, Clock *clk)
|
bool alias, bool output, Clock *clk)
|
||||||
{
|
{
|
||||||
NamedClockList *ncl;
|
NamedClockList *ncl;
|
||||||
|
|
||||||
|
@ -38,39 +38,8 @@ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
|
||||||
*/
|
*/
|
||||||
ncl = g_new0(NamedClockList, 1);
|
ncl = g_new0(NamedClockList, 1);
|
||||||
ncl->name = g_strdup(name);
|
ncl->name = g_strdup(name);
|
||||||
|
ncl->alias = alias;
|
||||||
ncl->output = output;
|
ncl->output = output;
|
||||||
ncl->alias = (clk != NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Trying to create a clock whose name clashes with some other
|
|
||||||
* clock or property is a bug in the caller and we will abort().
|
|
||||||
*/
|
|
||||||
if (clk == NULL) {
|
|
||||||
clk = CLOCK(object_new(TYPE_CLOCK));
|
|
||||||
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
|
|
||||||
if (output) {
|
|
||||||
/*
|
|
||||||
* Remove object_new()'s initial reference.
|
|
||||||
* Note that for inputs, the reference created by object_new()
|
|
||||||
* will be deleted in qdev_finalize_clocklist().
|
|
||||||
*/
|
|
||||||
object_unref(OBJECT(clk));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
object_property_add_link(OBJECT(dev), name,
|
|
||||||
object_get_typename(OBJECT(clk)),
|
|
||||||
(Object **) &ncl->clock,
|
|
||||||
NULL, OBJ_PROP_LINK_STRONG);
|
|
||||||
/*
|
|
||||||
* Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
|
|
||||||
* object reference count gets decremented on property deletion.
|
|
||||||
* However object_property_add_link does not increment it since it
|
|
||||||
* doesn't know the linked object. Increment it here to ensure the
|
|
||||||
* aliased clock stays alive during this device life-time.
|
|
||||||
*/
|
|
||||||
object_ref(OBJECT(clk));
|
|
||||||
}
|
|
||||||
|
|
||||||
ncl->clock = clk;
|
ncl->clock = clk;
|
||||||
|
|
||||||
QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
|
QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
|
||||||
|
@ -84,14 +53,11 @@ void qdev_finalize_clocklist(DeviceState *dev)
|
||||||
|
|
||||||
QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
|
QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
|
||||||
QLIST_REMOVE(ncl, node);
|
QLIST_REMOVE(ncl, node);
|
||||||
if (!ncl->output && !ncl->alias) {
|
if (!ncl->alias) {
|
||||||
/*
|
/*
|
||||||
* We kept a reference on the input clock to ensure it lives up to
|
* We kept a reference on the input clock to ensure it lives up to
|
||||||
* this point so we can safely remove the callback.
|
* this point; it is used by the monitor to show the frequency.
|
||||||
* It avoids having a callback to a deleted object if ncl->clock
|
|
||||||
* is still referenced somewhere else (eg: by a clock output).
|
|
||||||
*/
|
*/
|
||||||
clock_clear_callback(ncl->clock);
|
|
||||||
object_unref(OBJECT(ncl->clock));
|
object_unref(OBJECT(ncl->clock));
|
||||||
}
|
}
|
||||||
g_free(ncl->name);
|
g_free(ncl->name);
|
||||||
|
@ -101,29 +67,25 @@ void qdev_finalize_clocklist(DeviceState *dev)
|
||||||
|
|
||||||
Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
|
Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
|
||||||
{
|
{
|
||||||
NamedClockList *ncl;
|
Clock *clk = CLOCK(object_new(TYPE_CLOCK));
|
||||||
|
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
|
||||||
|
|
||||||
assert(name);
|
qdev_init_clocklist(dev, name, false, true, clk);
|
||||||
|
return clk;
|
||||||
ncl = qdev_init_clocklist(dev, name, true, NULL);
|
|
||||||
|
|
||||||
return ncl->clock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
|
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
|
||||||
ClockCallback *callback, void *opaque,
|
ClockCallback *callback, void *opaque,
|
||||||
unsigned int events)
|
unsigned int events)
|
||||||
{
|
{
|
||||||
NamedClockList *ncl;
|
Clock *clk = CLOCK(object_new(TYPE_CLOCK));
|
||||||
|
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
|
||||||
assert(name);
|
|
||||||
|
|
||||||
ncl = qdev_init_clocklist(dev, name, false, NULL);
|
|
||||||
|
|
||||||
|
qdev_init_clocklist(dev, name, false, false, clk);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
clock_set_callback(ncl->clock, callback, opaque, events);
|
clock_set_callback(clk, callback, opaque, events);
|
||||||
}
|
}
|
||||||
return ncl->clock;
|
return clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
|
void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
|
||||||
|
@ -194,15 +156,25 @@ Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
|
||||||
Clock *qdev_alias_clock(DeviceState *dev, const char *name,
|
Clock *qdev_alias_clock(DeviceState *dev, const char *name,
|
||||||
DeviceState *alias_dev, const char *alias_name)
|
DeviceState *alias_dev, const char *alias_name)
|
||||||
{
|
{
|
||||||
NamedClockList *ncl;
|
NamedClockList *ncl = qdev_get_clocklist(dev, name);
|
||||||
|
Clock *clk = ncl->clock;
|
||||||
|
|
||||||
assert(name && alias_name);
|
ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk);
|
||||||
|
|
||||||
ncl = qdev_get_clocklist(dev, name);
|
object_property_add_link(OBJECT(alias_dev), alias_name,
|
||||||
|
TYPE_CLOCK,
|
||||||
|
(Object **) &ncl->clock,
|
||||||
|
NULL, OBJ_PROP_LINK_STRONG);
|
||||||
|
/*
|
||||||
|
* Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
|
||||||
|
* object reference count gets decremented on property deletion.
|
||||||
|
* However object_property_add_link does not increment it since it
|
||||||
|
* doesn't know the linked object. Increment it here to ensure the
|
||||||
|
* aliased clock stays alive during this device life-time.
|
||||||
|
*/
|
||||||
|
object_ref(OBJECT(clk));
|
||||||
|
|
||||||
qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock);
|
return clk;
|
||||||
|
|
||||||
return ncl->clock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
|
void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
|
||||||
|
|
|
@ -1774,7 +1774,7 @@ static void e1000_register_types(void)
|
||||||
type_info.class_data = (void *)info;
|
type_info.class_data = (void *)info;
|
||||||
type_info.class_init = e1000_class_init;
|
type_info.class_init = e1000_class_init;
|
||||||
|
|
||||||
type_register(&type_info);
|
type_register_static(&type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2102,7 +2102,7 @@ static void eepro100_register_types(void)
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
type_register(&type_info);
|
type_register_static(&type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4723,7 +4723,7 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
|
||||||
static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \
|
static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \
|
||||||
{ \
|
{ \
|
||||||
MACHINE_VER_DELETION(__VA_ARGS__); \
|
MACHINE_VER_DELETION(__VA_ARGS__); \
|
||||||
type_register(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \
|
type_register_static(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \
|
||||||
} \
|
} \
|
||||||
type_init(MACHINE_VER_SYM(register, spapr, __VA_ARGS__))
|
type_init(MACHINE_VER_SYM(register, spapr, __VA_ARGS__))
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ static void m48t59_isa_register_types(void)
|
||||||
for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) {
|
||||||
isa_type_info.name = m48txx_isa_info[i].bus_name;
|
isa_type_info.name = m48txx_isa_info[i].bus_name;
|
||||||
isa_type_info.class_data = &m48txx_isa_info[i];
|
isa_type_info.class_data = &m48txx_isa_info[i];
|
||||||
type_register(&isa_type_info);
|
type_register_static(&isa_type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -679,7 +679,7 @@ static void m48t59_register_types(void)
|
||||||
for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) {
|
||||||
sysbus_type_info.name = m48txx_sysbus_info[i].bus_name;
|
sysbus_type_info.name = m48txx_sysbus_info[i].bus_name;
|
||||||
sysbus_type_info.class_data = &m48txx_sysbus_info[i];
|
sysbus_type_info.class_data = &m48txx_sysbus_info[i];
|
||||||
type_register(&sysbus_type_info);
|
type_register_static(&sysbus_type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2576,7 +2576,7 @@ static void megasas_register_types(void)
|
||||||
type_info.class_init = megasas_class_init;
|
type_info.class_init = megasas_class_init;
|
||||||
type_info.interfaces = info->interfaces;
|
type_info.interfaces = info->interfaces;
|
||||||
|
|
||||||
type_register(&type_info);
|
type_register_static(&type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1450,7 +1450,7 @@ static const TypeInfo mptsas_info = {
|
||||||
|
|
||||||
static void mptsas_register_types(void)
|
static void mptsas_register_types(void)
|
||||||
{
|
{
|
||||||
type_register(&mptsas_info);
|
type_register_static(&mptsas_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(mptsas_register_types)
|
type_init(mptsas_register_types)
|
||||||
|
|
|
@ -384,7 +384,7 @@ static void tmp421_register_types(void)
|
||||||
.class_init = tmp421_class_init,
|
.class_init = tmp421_class_init,
|
||||||
.class_data = (void *) &devices[i],
|
.class_data = (void *) &devices[i],
|
||||||
};
|
};
|
||||||
type_register(&ti);
|
type_register_static(&ti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,7 +228,7 @@ static void ehci_pci_register_types(void)
|
||||||
for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) {
|
||||||
ehci_type_info.name = ehci_pci_info[i].name;
|
ehci_type_info.name = ehci_pci_info[i].name;
|
||||||
ehci_type_info.class_data = ehci_pci_info + i;
|
ehci_type_info.class_data = ehci_pci_info + i;
|
||||||
type_register(&ehci_type_info);
|
type_register_static(&ehci_type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1362,7 +1362,7 @@ static void uhci_register_types(void)
|
||||||
for (i = 0; i < ARRAY_SIZE(uhci_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(uhci_info); i++) {
|
||||||
uhci_type_info.name = uhci_info[i].name;
|
uhci_type_info.name = uhci_info[i].name;
|
||||||
uhci_type_info.class_data = uhci_info + i;
|
uhci_type_info.class_data = uhci_info + i;
|
||||||
type_register(&uhci_type_info);
|
type_register_static(&uhci_type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2511,9 +2511,9 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t)
|
||||||
base_type_info.class_data = (void *)t;
|
base_type_info.class_data = (void *)t;
|
||||||
}
|
}
|
||||||
|
|
||||||
type_register(&base_type_info);
|
type_register_static(&base_type_info);
|
||||||
if (generic_type_info.name) {
|
if (generic_type_info.name) {
|
||||||
type_register(&generic_type_info);
|
type_register_static(&generic_type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t->non_transitional_name) {
|
if (t->non_transitional_name) {
|
||||||
|
@ -2527,7 +2527,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t)
|
||||||
{ }
|
{ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
type_register(&non_transitional_type_info);
|
type_register_static(&non_transitional_type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t->transitional_name) {
|
if (t->transitional_name) {
|
||||||
|
@ -2544,7 +2544,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t)
|
||||||
{ }
|
{ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
type_register(&transitional_type_info);
|
type_register_static(&transitional_type_info);
|
||||||
}
|
}
|
||||||
g_free(base_name);
|
g_free(base_name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,14 +141,6 @@ Clock *clock_new(Object *parent, const char *name);
|
||||||
void clock_set_callback(Clock *clk, ClockCallback *cb,
|
void clock_set_callback(Clock *clk, ClockCallback *cb,
|
||||||
void *opaque, unsigned int events);
|
void *opaque, unsigned int events);
|
||||||
|
|
||||||
/**
|
|
||||||
* clock_clear_callback:
|
|
||||||
* @clk: the clock to delete the callback from
|
|
||||||
*
|
|
||||||
* Unregister the callback registered with clock_set_callback.
|
|
||||||
*/
|
|
||||||
void clock_clear_callback(Clock *clk);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clock_set_source:
|
* clock_set_source:
|
||||||
* @clk: the clock.
|
* @clk: the clock.
|
||||||
|
|
|
@ -319,7 +319,7 @@ extern const size_t pc_compat_2_3_len;
|
||||||
}; \
|
}; \
|
||||||
static void pc_machine_init_##suffix(void) \
|
static void pc_machine_init_##suffix(void) \
|
||||||
{ \
|
{ \
|
||||||
type_register(&pc_machine_type_##suffix); \
|
type_register_static(&pc_machine_type_##suffix); \
|
||||||
} \
|
} \
|
||||||
type_init(pc_machine_init_##suffix)
|
type_init(pc_machine_init_##suffix)
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ extern const size_t pc_compat_2_3_len;
|
||||||
static void MACHINE_VER_SYM(register, namesym, __VA_ARGS__)(void) \
|
static void MACHINE_VER_SYM(register, namesym, __VA_ARGS__)(void) \
|
||||||
{ \
|
{ \
|
||||||
MACHINE_VER_DELETION(__VA_ARGS__); \
|
MACHINE_VER_DELETION(__VA_ARGS__); \
|
||||||
type_register(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \
|
type_register_static(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \
|
||||||
} \
|
} \
|
||||||
type_init(MACHINE_VER_SYM(register, namesym, __VA_ARGS__));
|
type_init(MACHINE_VER_SYM(register, namesym, __VA_ARGS__));
|
||||||
|
|
||||||
|
|
|
@ -262,6 +262,21 @@ AioContext *iohandler_get_aio_context(void);
|
||||||
*/
|
*/
|
||||||
bool bql_locked(void);
|
bool bql_locked(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bql_block: Allow/deny releasing the BQL
|
||||||
|
*
|
||||||
|
* The Big QEMU Lock (BQL) is used to provide interior mutability to
|
||||||
|
* Rust code, but this only works if other threads cannot run while
|
||||||
|
* the Rust code has an active borrow. This is because C code in
|
||||||
|
* other threads could come in and mutate data under the Rust code's
|
||||||
|
* feet.
|
||||||
|
*
|
||||||
|
* @increase: Whether to increase or decrease the blocking counter.
|
||||||
|
* Releasing the BQL while the counter is nonzero triggers
|
||||||
|
* an assertion failure.
|
||||||
|
*/
|
||||||
|
void bql_block_unlock(bool increase);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qemu_in_main_thread: return whether it's possible to safely access
|
* qemu_in_main_thread: return whether it's possible to safely access
|
||||||
* the global state of the block layer.
|
* the global state of the block layer.
|
||||||
|
|
|
@ -880,24 +880,10 @@ const char *object_get_typename(const Object *obj);
|
||||||
* type_register_static:
|
* type_register_static:
|
||||||
* @info: The #TypeInfo of the new type.
|
* @info: The #TypeInfo of the new type.
|
||||||
*
|
*
|
||||||
* @info and all of the strings it points to should exist for the life time
|
|
||||||
* that the type is registered.
|
|
||||||
*
|
|
||||||
* Returns: the new #Type.
|
* Returns: the new #Type.
|
||||||
*/
|
*/
|
||||||
Type type_register_static(const TypeInfo *info);
|
Type type_register_static(const TypeInfo *info);
|
||||||
|
|
||||||
/**
|
|
||||||
* type_register:
|
|
||||||
* @info: The #TypeInfo of the new type
|
|
||||||
*
|
|
||||||
* Unlike type_register_static(), this call does not require @info or its
|
|
||||||
* string members to continue to exist after the call returns.
|
|
||||||
*
|
|
||||||
* Returns: the new #Type.
|
|
||||||
*/
|
|
||||||
Type type_register(const TypeInfo *info);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* type_register_static_array:
|
* type_register_static_array:
|
||||||
* @infos: The array of the new type #TypeInfo structures.
|
* @infos: The array of the new type #TypeInfo structures.
|
||||||
|
|
57
meson.build
57
meson.build
|
@ -3,6 +3,8 @@ project('qemu', ['c'], meson_version: '>=1.5.0',
|
||||||
'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'],
|
'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'],
|
||||||
version: files('VERSION'))
|
version: files('VERSION'))
|
||||||
|
|
||||||
|
meson.add_devenv({ 'MESON_BUILD_ROOT' : meson.project_build_root() })
|
||||||
|
|
||||||
add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true)
|
add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true)
|
||||||
add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow'])
|
add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow'])
|
||||||
add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough'])
|
add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough'])
|
||||||
|
@ -118,7 +120,28 @@ if have_rust
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if have_rust
|
if have_rust
|
||||||
|
rustc_args = [find_program('scripts/rust/rustc_args.py'),
|
||||||
|
'--rustc-version', rustc.version(),
|
||||||
|
'--workspace', meson.project_source_root() / 'rust']
|
||||||
|
if get_option('strict_rust_lints')
|
||||||
|
rustc_args += ['--strict-lints']
|
||||||
|
endif
|
||||||
|
|
||||||
rustfmt = find_program('rustfmt', required: false)
|
rustfmt = find_program('rustfmt', required: false)
|
||||||
|
|
||||||
|
rustc_lint_args = run_command(rustc_args, '--lints',
|
||||||
|
capture: true, check: true).stdout().strip().splitlines()
|
||||||
|
|
||||||
|
# Apart from procedural macros, our Rust executables will often link
|
||||||
|
# with C code, so include all the libraries that C code needs. This
|
||||||
|
# is safe; https://github.com/rust-lang/rust/pull/54675 says that
|
||||||
|
# passing -nodefaultlibs to the linker "was more ideological to
|
||||||
|
# start with than anything".
|
||||||
|
add_project_arguments(rustc_lint_args +
|
||||||
|
['--cfg', 'MESON', '-C', 'default-linker-libraries'],
|
||||||
|
native: false, language: 'rust')
|
||||||
|
add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'],
|
||||||
|
native: true, language: 'rust')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
dtrace = not_found
|
dtrace = not_found
|
||||||
|
@ -3397,36 +3420,8 @@ endif
|
||||||
# Generated sources #
|
# Generated sources #
|
||||||
#####################
|
#####################
|
||||||
|
|
||||||
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
|
config_host_h = configure_file(output: 'config-host.h', configuration: config_host_data)
|
||||||
|
genh += config_host_h
|
||||||
if have_rust
|
|
||||||
rustc_args = run_command(
|
|
||||||
find_program('scripts/rust/rustc_args.py'),
|
|
||||||
'--config-headers', meson.project_build_root() / 'config-host.h',
|
|
||||||
capture : true,
|
|
||||||
check: true).stdout().strip().split()
|
|
||||||
|
|
||||||
# Prohibit code that is forbidden in Rust 2024
|
|
||||||
rustc_args += ['-D', 'unsafe_op_in_unsafe_fn']
|
|
||||||
|
|
||||||
# Occasionally, we may need to silence warnings and clippy lints that
|
|
||||||
# were only introduced in newer Rust compiler versions. Do not croak
|
|
||||||
# in that case; a CI job with rust_strict_lints == true ensures that
|
|
||||||
# we do not have misspelled allow() attributes.
|
|
||||||
if not get_option('strict_rust_lints')
|
|
||||||
rustc_args += ['-A', 'unknown_lints']
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Apart from procedural macros, our Rust executables will often link
|
|
||||||
# with C code, so include all the libraries that C code needs. This
|
|
||||||
# is safe; https://github.com/rust-lang/rust/pull/54675 says that
|
|
||||||
# passing -nodefaultlibs to the linker "was more ideological to
|
|
||||||
# start with than anything".
|
|
||||||
add_project_arguments(rustc_args + ['-C', 'default-linker-libraries'],
|
|
||||||
native: false, language: 'rust')
|
|
||||||
|
|
||||||
add_project_arguments(rustc_args, native: true, language: 'rust')
|
|
||||||
endif
|
|
||||||
|
|
||||||
hxtool = find_program('scripts/hxtool')
|
hxtool = find_program('scripts/hxtool')
|
||||||
shaderinclude = find_program('scripts/shaderinclude.py')
|
shaderinclude = find_program('scripts/shaderinclude.py')
|
||||||
|
@ -4089,7 +4084,7 @@ if have_rust
|
||||||
bindings_rs = rust.bindgen(
|
bindings_rs = rust.bindgen(
|
||||||
input: 'rust/wrapper.h',
|
input: 'rust/wrapper.h',
|
||||||
dependencies: common_ss.all_dependencies(),
|
dependencies: common_ss.all_dependencies(),
|
||||||
output: 'bindings.rs',
|
output: 'bindings.inc.rs',
|
||||||
include_directories: include_directories('.', 'include'),
|
include_directories: include_directories('.', 'include'),
|
||||||
bindgen_version: ['>=0.60.0'],
|
bindgen_version: ['>=0.60.0'],
|
||||||
args: bindgen_args,
|
args: bindgen_args,
|
||||||
|
|
|
@ -175,17 +175,12 @@ static TypeImpl *type_register_internal(const TypeInfo *info)
|
||||||
return ti;
|
return ti;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeImpl *type_register(const TypeInfo *info)
|
TypeImpl *type_register_static(const TypeInfo *info)
|
||||||
{
|
{
|
||||||
assert(info->parent);
|
assert(info->parent);
|
||||||
return type_register_internal(info);
|
return type_register_internal(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeImpl *type_register_static(const TypeInfo *info)
|
|
||||||
{
|
|
||||||
return type_register(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_register_static_array(const TypeInfo *infos, int nr_infos)
|
void type_register_static_array(const TypeInfo *infos, int nr_infos)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -5,3 +5,85 @@ members = [
|
||||||
"qemu-api",
|
"qemu-api",
|
||||||
"hw/char/pl011",
|
"hw/char/pl011",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
unexpected_cfgs = { level = "deny", check-cfg = [
|
||||||
|
'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
|
||||||
|
'cfg(has_offset_of)'] }
|
||||||
|
|
||||||
|
# Occasionally, we may need to silence warnings and clippy lints that
|
||||||
|
# were only introduced in newer Rust compiler versions. Do not croak
|
||||||
|
# in that case; a CI job with rust_strict_lints == true disables this
|
||||||
|
# and ensures that we do not have misspelled allow() attributes.
|
||||||
|
unknown_lints = "allow"
|
||||||
|
|
||||||
|
# Prohibit code that is forbidden in Rust 2024
|
||||||
|
unsafe_op_in_unsafe_fn = "deny"
|
||||||
|
|
||||||
|
[workspace.lints.rustdoc]
|
||||||
|
private_intra_doc_links = "allow"
|
||||||
|
|
||||||
|
broken_intra_doc_links = "deny"
|
||||||
|
invalid_html_tags = "deny"
|
||||||
|
invalid_rust_codeblocks = "deny"
|
||||||
|
bare_urls = "deny"
|
||||||
|
unescaped_backticks = "deny"
|
||||||
|
redundant_explicit_links = "deny"
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
# default-warn lints
|
||||||
|
result_unit_err = "allow"
|
||||||
|
should_implement_trait = "deny"
|
||||||
|
# can be for a reason, e.g. in callbacks
|
||||||
|
unused_self = "allow"
|
||||||
|
|
||||||
|
# default-allow lints
|
||||||
|
as_underscore = "deny"
|
||||||
|
assertions_on_result_states = "deny"
|
||||||
|
bool_to_int_with_if = "deny"
|
||||||
|
borrow_as_ptr = "deny"
|
||||||
|
cast_lossless = "deny"
|
||||||
|
dbg_macro = "deny"
|
||||||
|
debug_assert_with_mut_call = "deny"
|
||||||
|
derive_partial_eq_without_eq = "deny"
|
||||||
|
doc_markdown = "deny"
|
||||||
|
empty_structs_with_brackets = "deny"
|
||||||
|
ignored_unit_patterns = "deny"
|
||||||
|
implicit_clone = "deny"
|
||||||
|
macro_use_imports = "deny"
|
||||||
|
missing_const_for_fn = "deny"
|
||||||
|
missing_safety_doc = "deny"
|
||||||
|
multiple_crate_versions = "deny"
|
||||||
|
mut_mut = "deny"
|
||||||
|
needless_bitwise_bool = "deny"
|
||||||
|
needless_pass_by_ref_mut = "deny"
|
||||||
|
no_effect_underscore_binding = "deny"
|
||||||
|
option_option = "deny"
|
||||||
|
or_fun_call = "deny"
|
||||||
|
ptr_as_ptr = "deny"
|
||||||
|
pub_underscore_fields = "deny"
|
||||||
|
redundant_clone = "deny"
|
||||||
|
redundant_closure_for_method_calls = "deny"
|
||||||
|
redundant_else = "deny"
|
||||||
|
redundant_pub_crate = "deny"
|
||||||
|
ref_binding_to_reference = "deny"
|
||||||
|
ref_option_ref = "deny"
|
||||||
|
return_self_not_must_use = "deny"
|
||||||
|
same_name_method = "deny"
|
||||||
|
semicolon_inside_block = "deny"
|
||||||
|
shadow_unrelated = "deny"
|
||||||
|
significant_drop_in_scrutinee = "deny"
|
||||||
|
significant_drop_tightening = "deny"
|
||||||
|
suspicious_operation_groupings = "deny"
|
||||||
|
transmute_ptr_to_ptr = "deny"
|
||||||
|
transmute_undefined_repr = "deny"
|
||||||
|
type_repetition_in_bounds = "deny"
|
||||||
|
used_underscore_binding = "deny"
|
||||||
|
|
||||||
|
# nice to have, but cannot be enabled yet
|
||||||
|
#wildcard_imports = "deny" # still have many bindings::* imports
|
||||||
|
#ptr_cast_constness = "deny" # needs 1.65.0 for cast_mut()/cast_const()
|
||||||
|
|
||||||
|
# these may have false positives
|
||||||
|
#option_if_let_else = "deny"
|
||||||
|
cognitive_complexity = "deny"
|
||||||
|
|
2
rust/hw/char/pl011/.gitignore
vendored
2
rust/hw/char/pl011/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
# Ignore generated bindings file overrides.
|
|
||||||
src/bindings.rs.inc
|
|
|
@ -21,3 +21,6 @@ bilge = { version = "0.2.0" }
|
||||||
bilge-impl = { version = "0.2.0" }
|
bilge-impl = { version = "0.2.0" }
|
||||||
qemu_api = { path = "../../../qemu-api" }
|
qemu_api = { path = "../../../qemu-api" }
|
||||||
qemu_api_macros = { path = "../../../qemu-api-macros" }
|
qemu_api_macros = { path = "../../../qemu-api-macros" }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use core::ptr::{addr_of, addr_of_mut, NonNull};
|
use core::ptr::{addr_of_mut, NonNull};
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::CStr,
|
||||||
os::raw::{c_int, c_uchar, c_uint, c_void},
|
os::raw::{c_int, c_uchar, c_uint, c_void},
|
||||||
|
@ -12,10 +12,14 @@ use qemu_api::{
|
||||||
bindings::{self, *},
|
bindings::{self, *},
|
||||||
c_str,
|
c_str,
|
||||||
definitions::ObjectImpl,
|
definitions::ObjectImpl,
|
||||||
device_class::TYPE_SYS_BUS_DEVICE,
|
device_class::DeviceImpl,
|
||||||
|
impl_device_class,
|
||||||
|
irq::InterruptSource,
|
||||||
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
device_class,
|
||||||
memory_ops::PL011_OPS,
|
memory_ops::PL011_OPS,
|
||||||
registers::{self, Interrupt},
|
registers::{self, Interrupt},
|
||||||
RegisterOffset,
|
RegisterOffset,
|
||||||
|
@ -94,7 +98,7 @@ pub struct PL011State {
|
||||||
/// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
|
/// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
|
||||||
/// ```
|
/// ```
|
||||||
#[doc(alias = "irq")]
|
#[doc(alias = "irq")]
|
||||||
pub interrupts: [qemu_irq; 6usize],
|
pub interrupts: [InterruptSource; IRQMASK.len()],
|
||||||
#[doc(alias = "clk")]
|
#[doc(alias = "clk")]
|
||||||
pub clock: NonNull<Clock>,
|
pub clock: NonNull<Clock>,
|
||||||
#[doc(alias = "migrate_clk")]
|
#[doc(alias = "migrate_clk")]
|
||||||
|
@ -103,15 +107,15 @@ pub struct PL011State {
|
||||||
device_id: DeviceId,
|
device_id: DeviceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PL011State {
|
unsafe impl ObjectType for PL011State {
|
||||||
type Class = PL011Class;
|
type Class = PL011Class;
|
||||||
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
|
|
||||||
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
||||||
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
|
}
|
||||||
const ABSTRACT: bool = false;
|
|
||||||
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
|
impl ObjectImpl for PL011State {
|
||||||
const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
type ParentType = SysBusDevice;
|
||||||
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
|
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -119,13 +123,18 @@ pub struct PL011Class {
|
||||||
_inner: [u8; 0],
|
_inner: [u8; 0],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl qemu_api::definitions::Class for PL011Class {
|
impl DeviceImpl for PL011State {
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
fn properties() -> &'static [Property] {
|
||||||
Some(crate::device_class::pl011_class_init);
|
&device_class::PL011_PROPERTIES
|
||||||
const CLASS_BASE_INIT: Option<
|
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
|
||||||
> = None;
|
|
||||||
}
|
}
|
||||||
|
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||||
|
Some(&device_class::VMSTATE_PL011)
|
||||||
|
}
|
||||||
|
const REALIZE: Option<fn(&mut Self)> = Some(Self::realize);
|
||||||
|
const RESET: Option<fn(&mut Self)> = Some(Self::reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_device_class!(PL011State);
|
||||||
|
|
||||||
impl PL011State {
|
impl PL011State {
|
||||||
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
||||||
|
@ -139,7 +148,8 @@ impl PL011State {
|
||||||
unsafe fn init(&mut self) {
|
unsafe fn init(&mut self) {
|
||||||
const CLK_NAME: &CStr = c_str!("clk");
|
const CLK_NAME: &CStr = c_str!("clk");
|
||||||
|
|
||||||
let dev = addr_of_mut!(*self).cast::<DeviceState>();
|
let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::<SysBusDevice>()) };
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
//
|
//
|
||||||
// self and self.iomem are guaranteed to be valid at this point since callers
|
// self and self.iomem are guaranteed to be valid at this point since callers
|
||||||
|
@ -150,15 +160,18 @@ impl PL011State {
|
||||||
addr_of_mut!(*self).cast::<Object>(),
|
addr_of_mut!(*self).cast::<Object>(),
|
||||||
&PL011_OPS,
|
&PL011_OPS,
|
||||||
addr_of_mut!(*self).cast::<c_void>(),
|
addr_of_mut!(*self).cast::<c_void>(),
|
||||||
Self::TYPE_INFO.name,
|
Self::TYPE_NAME.as_ptr(),
|
||||||
0x1000,
|
0x1000,
|
||||||
);
|
);
|
||||||
let sbd = addr_of_mut!(*self).cast::<SysBusDevice>();
|
|
||||||
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
|
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
|
||||||
for irq in self.interrupts.iter_mut() {
|
|
||||||
sysbus_init_irq(sbd, irq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for irq in self.interrupts.iter() {
|
||||||
|
sbd.init_irq(irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dev = addr_of_mut!(*self).cast::<DeviceState>();
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
//
|
//
|
||||||
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
|
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
|
||||||
|
@ -498,8 +511,7 @@ impl PL011State {
|
||||||
pub fn update(&self) {
|
pub fn update(&self) {
|
||||||
let flags = self.int_level & self.int_enabled;
|
let flags = self.int_level & self.int_enabled;
|
||||||
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
|
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
|
||||||
// SAFETY: self.interrupts have been initialized in init().
|
irq.set(flags & i != 0);
|
||||||
unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,30 +609,17 @@ pub unsafe extern "C" fn pl011_create(
|
||||||
chr: *mut Chardev,
|
chr: *mut Chardev,
|
||||||
) -> *mut DeviceState {
|
) -> *mut DeviceState {
|
||||||
unsafe {
|
unsafe {
|
||||||
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
|
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_NAME.as_ptr());
|
||||||
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
|
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
|
||||||
|
|
||||||
qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr);
|
qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr);
|
||||||
sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error);
|
sysbus_realize_and_unref(sysbus, addr_of_mut!(error_fatal));
|
||||||
sysbus_mmio_map(sysbus, 0, addr);
|
sysbus_mmio_map(sysbus, 0, addr);
|
||||||
sysbus_connect_irq(sysbus, 0, irq);
|
sysbus_connect_irq(sysbus, 0, irq);
|
||||||
dev
|
dev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
|
||||||
/// readable/writeable from one thread at any time.
|
|
||||||
pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
|
|
||||||
unsafe {
|
|
||||||
debug_assert!(!obj.is_null());
|
|
||||||
let mut state = NonNull::new_unchecked(obj.cast::<PL011State>());
|
|
||||||
state.as_mut().init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, qemu_api_macros::Object)]
|
#[derive(Debug, qemu_api_macros::Object)]
|
||||||
/// PL011 Luminary device model.
|
/// PL011 Luminary device model.
|
||||||
|
@ -633,37 +632,30 @@ pub struct PL011LuminaryClass {
|
||||||
_inner: [u8; 0],
|
_inner: [u8; 0],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PL011Luminary {
|
||||||
/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
|
/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
/// We expect the FFI user of this function to pass a valid pointer, that
|
||||||
/// the same size as [`PL011Luminary`]. We also expect the device is
|
/// has the same size as [`PL011Luminary`]. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) {
|
unsafe fn init(&mut self) {
|
||||||
unsafe {
|
self.parent_obj.device_id = DeviceId::Luminary;
|
||||||
debug_assert!(!obj.is_null());
|
|
||||||
let mut state = NonNull::new_unchecked(obj.cast::<PL011Luminary>());
|
|
||||||
let state = state.as_mut();
|
|
||||||
state.parent_obj.device_id = DeviceId::Luminary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl qemu_api::definitions::Class for PL011LuminaryClass {
|
unsafe impl ObjectType for PL011Luminary {
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
type Class = PL011LuminaryClass;
|
||||||
None;
|
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
|
||||||
const CLASS_BASE_INIT: Option<
|
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
|
||||||
> = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PL011Luminary {
|
impl ObjectImpl for PL011Luminary {
|
||||||
type Class = PL011LuminaryClass;
|
type ParentType = PL011State;
|
||||||
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
|
|
||||||
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
|
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
||||||
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
|
|
||||||
const ABSTRACT: bool = false;
|
|
||||||
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
|
|
||||||
const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DeviceImpl for PL011Luminary {}
|
||||||
|
|
||||||
|
impl_device_class!(PL011Luminary);
|
||||||
|
|
|
@ -92,37 +92,3 @@ qemu_api::declare_properties! {
|
||||||
default = true
|
default = true
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_api::device_class_init! {
|
|
||||||
pl011_class_init,
|
|
||||||
props => PL011_PROPERTIES,
|
|
||||||
realize_fn => Some(pl011_realize),
|
|
||||||
legacy_reset_fn => Some(pl011_reset),
|
|
||||||
vmsd => VMSTATE_PL011,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
|
||||||
/// readable/writeable from one thread at any time.
|
|
||||||
pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
|
|
||||||
unsafe {
|
|
||||||
assert!(!dev.is_null());
|
|
||||||
let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
|
|
||||||
state.as_mut().realize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
|
||||||
/// readable/writeable from one thread at any time.
|
|
||||||
pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
|
|
||||||
unsafe {
|
|
||||||
assert!(!dev.is_null());
|
|
||||||
let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
|
|
||||||
state.as_mut().reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,28 +14,15 @@
|
||||||
//! the [`registers`] module for register types.
|
//! the [`registers`] module for register types.
|
||||||
|
|
||||||
#![deny(
|
#![deny(
|
||||||
rustdoc::broken_intra_doc_links,
|
|
||||||
rustdoc::redundant_explicit_links,
|
|
||||||
clippy::correctness,
|
clippy::correctness,
|
||||||
clippy::suspicious,
|
clippy::suspicious,
|
||||||
clippy::complexity,
|
clippy::complexity,
|
||||||
clippy::perf,
|
clippy::perf,
|
||||||
clippy::cargo,
|
clippy::cargo,
|
||||||
clippy::nursery,
|
clippy::nursery,
|
||||||
clippy::style,
|
clippy::style
|
||||||
// restriction group
|
|
||||||
clippy::dbg_macro,
|
|
||||||
clippy::as_underscore,
|
|
||||||
clippy::assertions_on_result_states,
|
|
||||||
// pedantic group
|
|
||||||
clippy::doc_markdown,
|
|
||||||
clippy::borrow_as_ptr,
|
|
||||||
clippy::cast_lossless,
|
|
||||||
clippy::option_if_let_else,
|
|
||||||
clippy::missing_const_for_fn,
|
|
||||||
clippy::cognitive_complexity,
|
|
||||||
clippy::missing_safety_doc,
|
|
||||||
)]
|
)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
#![allow(clippy::result_unit_err)]
|
#![allow(clippy::result_unit_err)]
|
||||||
|
|
||||||
extern crate bilge;
|
extern crate bilge;
|
||||||
|
|
|
@ -33,7 +33,9 @@ unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint)
|
||||||
// SAFETY: self.char_backend is a valid CharBackend instance after it's been
|
// SAFETY: self.char_backend is a valid CharBackend instance after it's been
|
||||||
// initialized in realize().
|
// initialized in realize().
|
||||||
let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) };
|
let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) };
|
||||||
unsafe { qemu_chr_fe_accept_input(cb_ptr) };
|
unsafe {
|
||||||
|
qemu_chr_fe_accept_input(cb_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,25 @@ subdir('qemu-api-macros')
|
||||||
subdir('qemu-api')
|
subdir('qemu-api')
|
||||||
|
|
||||||
subdir('hw')
|
subdir('hw')
|
||||||
|
|
||||||
|
cargo = find_program('cargo', required: false)
|
||||||
|
|
||||||
|
if cargo.found()
|
||||||
|
run_target('clippy',
|
||||||
|
command: [config_host['MESON'], 'devenv',
|
||||||
|
'--workdir', '@CURRENT_SOURCE_DIR@',
|
||||||
|
cargo, 'clippy', '--tests'],
|
||||||
|
depends: bindings_rs)
|
||||||
|
|
||||||
|
run_target('rustfmt',
|
||||||
|
command: [config_host['MESON'], 'devenv',
|
||||||
|
'--workdir', '@CURRENT_SOURCE_DIR@',
|
||||||
|
cargo, 'fmt'],
|
||||||
|
depends: bindings_rs)
|
||||||
|
|
||||||
|
run_target('rustdoc',
|
||||||
|
command: [config_host['MESON'], 'devenv',
|
||||||
|
'--workdir', '@CURRENT_SOURCE_DIR@',
|
||||||
|
cargo, 'doc', '--no-deps', '--document-private-items'],
|
||||||
|
depends: bindings_rs)
|
||||||
|
endif
|
||||||
|
|
|
@ -20,3 +20,6 @@ proc-macro = true
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2", features = ["extra-traits"] }
|
syn = { version = "2", features = ["extra-traits"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
2
rust/qemu-api/.gitignore
vendored
2
rust/qemu-api/.gitignore
vendored
|
@ -1,2 +1,2 @@
|
||||||
# Ignore generated bindings file overrides.
|
# Ignore generated bindings file overrides.
|
||||||
src/bindings.rs
|
/src/bindings.inc.rs
|
||||||
|
|
|
@ -20,9 +20,9 @@ qemu_api_macros = { path = "../qemu-api-macros" }
|
||||||
version_check = "~0.9"
|
version_check = "~0.9"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["debug_cell"]
|
||||||
allocator = []
|
allocator = []
|
||||||
|
debug_cell = []
|
||||||
|
|
||||||
[lints.rust]
|
[lints]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
|
workspace = true
|
||||||
'cfg(has_offset_of)'] }
|
|
||||||
|
|
|
@ -5,13 +5,15 @@ This library exports helper Rust types, Rust macros and C FFI bindings for inter
|
||||||
The C bindings can be generated with `bindgen`, using this build target:
|
The C bindings can be generated with `bindgen`, using this build target:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ ninja bindings.rs
|
$ make bindings.inc.rs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Generate Rust documentation
|
## Generate Rust documentation
|
||||||
|
|
||||||
To generate docs for this crate, including private items:
|
Common Cargo tasks can be performed from the QEMU build directory
|
||||||
|
|
||||||
```sh
|
```console
|
||||||
cargo doc --no-deps --document-private-items
|
$ make clippy
|
||||||
|
$ make rustfmt
|
||||||
|
$ make rustdoc
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,22 +2,47 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::path::Path;
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::symlink as symlink_file;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::fs::symlink_file;
|
||||||
|
use std::{env, fs::remove_file, io::Result, path::Path};
|
||||||
|
|
||||||
use version_check as rustc;
|
use version_check as rustc;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<()> {
|
||||||
if !Path::new("src/bindings.rs").exists() {
|
// Placing bindings.inc.rs in the source directory is supported
|
||||||
panic!(
|
// but not documented or encouraged.
|
||||||
"No generated C bindings found! Either build them manually with bindgen or with meson \
|
let path = env::var("MESON_BUILD_ROOT")
|
||||||
(`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson."
|
.unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
|
||||||
);
|
|
||||||
|
let file = format!("{}/bindings.inc.rs", path);
|
||||||
|
let file = Path::new(&file);
|
||||||
|
if !Path::new(&file).exists() {
|
||||||
|
panic!(concat!(
|
||||||
|
"\n",
|
||||||
|
" No generated C bindings found! Maybe you wanted one of\n",
|
||||||
|
" `make clippy`, `make rustfmt`, `make rustdoc`?\n",
|
||||||
|
"\n",
|
||||||
|
" For other uses of `cargo`, start a subshell with\n",
|
||||||
|
" `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n",
|
||||||
|
" the top of the build tree."
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let dest_path = format!("{}/bindings.inc.rs", out_dir);
|
||||||
|
let dest_path = Path::new(&dest_path);
|
||||||
|
if dest_path.symlink_metadata().is_ok() {
|
||||||
|
remove_file(dest_path)?;
|
||||||
|
}
|
||||||
|
symlink_file(file, dest_path)?;
|
||||||
|
|
||||||
// Check for available rustc features
|
// Check for available rustc features
|
||||||
if rustc::is_min_version("1.77.0").unwrap_or(false) {
|
if rustc::is_min_version("1.77.0").unwrap_or(false) {
|
||||||
println!("cargo:rustc-cfg=has_offset_of");
|
println!("cargo:rustc-cfg=has_offset_of");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
_qemu_api_cfg = ['--cfg', 'MESON']
|
_qemu_api_cfg = run_command(rustc_args,
|
||||||
|
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
|
||||||
|
capture: true, check: true).stdout().strip().splitlines()
|
||||||
|
|
||||||
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
||||||
if rustc.version().version_compare('>=1.77.0')
|
if rustc.version().version_compare('>=1.77.0')
|
||||||
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
||||||
endif
|
endif
|
||||||
|
if get_option('debug_mutex')
|
||||||
|
_qemu_api_cfg += ['--feature', 'debug_cell']
|
||||||
|
endif
|
||||||
|
|
||||||
_qemu_api_rs = static_library(
|
_qemu_api_rs = static_library(
|
||||||
'qemu_api',
|
'qemu_api',
|
||||||
structured_sources(
|
structured_sources(
|
||||||
[
|
[
|
||||||
'src/lib.rs',
|
'src/lib.rs',
|
||||||
|
'src/bindings.rs',
|
||||||
|
'src/bitops.rs',
|
||||||
|
'src/cell.rs',
|
||||||
'src/c_str.rs',
|
'src/c_str.rs',
|
||||||
'src/definitions.rs',
|
'src/definitions.rs',
|
||||||
'src/device_class.rs',
|
'src/device_class.rs',
|
||||||
|
'src/irq.rs',
|
||||||
'src/offset_of.rs',
|
'src/offset_of.rs',
|
||||||
|
'src/prelude.rs',
|
||||||
|
'src/sysbus.rs',
|
||||||
'src/vmstate.rs',
|
'src/vmstate.rs',
|
||||||
'src/zeroable.rs',
|
'src/zeroable.rs',
|
||||||
],
|
],
|
||||||
|
|
29
rust/qemu-api/src/bindings.rs
Normal file
29
rust/qemu-api/src/bindings.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#![allow(
|
||||||
|
dead_code,
|
||||||
|
improper_ctypes_definitions,
|
||||||
|
improper_ctypes,
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
non_upper_case_globals,
|
||||||
|
unsafe_op_in_unsafe_fn,
|
||||||
|
clippy::pedantic,
|
||||||
|
clippy::restriction,
|
||||||
|
clippy::style,
|
||||||
|
clippy::missing_const_for_fn,
|
||||||
|
clippy::useless_transmute,
|
||||||
|
clippy::missing_safety_doc
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[cfg(MESON)]
|
||||||
|
include!("bindings.inc.rs");
|
||||||
|
|
||||||
|
#[cfg(not(MESON))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
|
||||||
|
|
||||||
|
unsafe impl Send for Property {}
|
||||||
|
unsafe impl Sync for Property {}
|
||||||
|
unsafe impl Sync for TypeInfo {}
|
||||||
|
unsafe impl Sync for VMStateDescription {}
|
||||||
|
unsafe impl Sync for VMStateField {}
|
||||||
|
unsafe impl Sync for VMStateInfo {}
|
119
rust/qemu-api/src/bitops.rs
Normal file
119
rust/qemu-api/src/bitops.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright (C) 2024 Intel Corporation.
|
||||||
|
// Author(s): Zhao Liu <zhai1.liu@intel.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
//! This module provides bit operation extensions to integer types.
|
||||||
|
//! It is usually included via the `qemu_api` prelude.
|
||||||
|
|
||||||
|
use std::ops::{
|
||||||
|
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
|
||||||
|
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Trait for extensions to integer types
|
||||||
|
pub trait IntegerExt:
|
||||||
|
Add<Self, Output = Self> + AddAssign<Self> +
|
||||||
|
BitAnd<Self, Output = Self> + BitAndAssign<Self> +
|
||||||
|
BitOr<Self, Output = Self> + BitOrAssign<Self> +
|
||||||
|
BitXor<Self, Output = Self> + BitXorAssign<Self> +
|
||||||
|
Copy +
|
||||||
|
Div<Self, Output = Self> + DivAssign<Self> +
|
||||||
|
Eq +
|
||||||
|
Mul<Self, Output = Self> + MulAssign<Self> +
|
||||||
|
Not<Output = Self> + Ord + PartialOrd +
|
||||||
|
Rem<Self, Output = Self> + RemAssign<Self> +
|
||||||
|
Shl<Self, Output = Self> + ShlAssign<Self> +
|
||||||
|
Shl<u32, Output = Self> + ShlAssign<u32> + // add more as needed
|
||||||
|
Shr<Self, Output = Self> + ShrAssign<Self> +
|
||||||
|
Shr<u32, Output = Self> + ShrAssign<u32> // add more as needed
|
||||||
|
{
|
||||||
|
const BITS: u32;
|
||||||
|
const MAX: Self;
|
||||||
|
const MIN: Self;
|
||||||
|
const ONE: Self;
|
||||||
|
const ZERO: Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn bit(start: u32) -> Self
|
||||||
|
{
|
||||||
|
debug_assert!(start < Self::BITS);
|
||||||
|
|
||||||
|
Self::ONE << start
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn mask(start: u32, length: u32) -> Self
|
||||||
|
{
|
||||||
|
/* FIXME: Implement a more elegant check with error handling support? */
|
||||||
|
debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start);
|
||||||
|
|
||||||
|
(Self::MAX >> (Self::BITS - length)) << start
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn deposit<U: IntegerExt>(self, start: u32, length: u32,
|
||||||
|
fieldval: U) -> Self
|
||||||
|
where Self: From<U>
|
||||||
|
{
|
||||||
|
debug_assert!(length <= U::BITS);
|
||||||
|
|
||||||
|
let mask = Self::mask(start, length);
|
||||||
|
(self & !mask) | ((Self::from(fieldval) << start) & mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn extract(self, start: u32, length: u32) -> Self
|
||||||
|
{
|
||||||
|
let mask = Self::mask(start, length);
|
||||||
|
(self & mask) >> start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_num_ext {
|
||||||
|
($type:ty) => {
|
||||||
|
impl IntegerExt for $type {
|
||||||
|
const BITS: u32 = <$type>::BITS;
|
||||||
|
const MAX: Self = <$type>::MAX;
|
||||||
|
const MIN: Self = <$type>::MIN;
|
||||||
|
const ONE: Self = 1;
|
||||||
|
const ZERO: Self = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_num_ext!(u8);
|
||||||
|
impl_num_ext!(u16);
|
||||||
|
impl_num_ext!(u32);
|
||||||
|
impl_num_ext!(u64);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deposit() {
|
||||||
|
assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15);
|
||||||
|
assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract() {
|
||||||
|
assert_eq!(15u32.extract(2, 4), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bit() {
|
||||||
|
assert_eq!(u8::bit(7), 128);
|
||||||
|
assert_eq!(u32::bit(16), 0x10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mask() {
|
||||||
|
assert_eq!(u8::mask(7, 1), 128);
|
||||||
|
assert_eq!(u32::mask(8, 8), 0xff00);
|
||||||
|
}
|
||||||
|
}
|
822
rust/qemu-api/src/cell.rs
Normal file
822
rust/qemu-api/src/cell.rs
Normal file
|
@ -0,0 +1,822 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// This file is based on library/core/src/cell.rs from
|
||||||
|
// Rust 1.82.0.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any
|
||||||
|
// person obtaining a copy of this software and associated
|
||||||
|
// documentation files (the "Software"), to deal in the
|
||||||
|
// Software without restriction, including without
|
||||||
|
// limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software
|
||||||
|
// is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
//! BQL-protected mutable containers.
|
||||||
|
//!
|
||||||
|
//! Rust memory safety is based on this rule: Given an object `T`, it is only
|
||||||
|
//! possible to have one of the following:
|
||||||
|
//!
|
||||||
|
//! - Having several immutable references (`&T`) to the object (also known as
|
||||||
|
//! **aliasing**).
|
||||||
|
//! - Having one mutable reference (`&mut T`) to the object (also known as
|
||||||
|
//! **mutability**).
|
||||||
|
//!
|
||||||
|
//! This is enforced by the Rust compiler. However, there are situations where
|
||||||
|
//! this rule is not flexible enough. Sometimes it is required to have multiple
|
||||||
|
//! references to an object and yet mutate it. In particular, QEMU objects
|
||||||
|
//! usually have their pointer shared with the "outside world very early in
|
||||||
|
//! their lifetime", for example when they create their
|
||||||
|
//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual
|
||||||
|
//! parts of a device must be made mutable in a controlled manner through the
|
||||||
|
//! use of cell types.
|
||||||
|
//!
|
||||||
|
//! [`BqlCell<T>`] and [`BqlRefCell<T>`] allow doing this via the Big QEMU Lock.
|
||||||
|
//! While they are essentially the same single-threaded primitives that are
|
||||||
|
//! available in `std::cell`, the BQL allows them to be used from a
|
||||||
|
//! multi-threaded context and to share references across threads, while
|
||||||
|
//! maintaining Rust's safety guarantees. For this reason, unlike
|
||||||
|
//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the
|
||||||
|
//! `Sync` trait.
|
||||||
|
//!
|
||||||
|
//! BQL checks are performed in debug builds but can be optimized away in
|
||||||
|
//! release builds, providing runtime safety during development with no overhead
|
||||||
|
//! in production.
|
||||||
|
//!
|
||||||
|
//! The two provide different ways of handling interior mutability.
|
||||||
|
//! `BqlRefCell` is best suited for data that is primarily accessed by the
|
||||||
|
//! device's own methods, where multiple reads and writes can be grouped within
|
||||||
|
//! a single borrow and a mutable reference can be passed around. Instead,
|
||||||
|
//! [`BqlCell`] is a better choice when sharing small pieces of data with
|
||||||
|
//! external code (especially C code), because it provides simple get/set
|
||||||
|
//! operations that can be used one at a time.
|
||||||
|
//!
|
||||||
|
//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell`
|
||||||
|
//! counterparts, they are not interchangeable. Using `std::cell` types in
|
||||||
|
//! QEMU device implementations is usually incorrect and can lead to
|
||||||
|
//! thread-safety issues.
|
||||||
|
//!
|
||||||
|
//! ## `BqlCell<T>`
|
||||||
|
//!
|
||||||
|
//! [`BqlCell<T>`] implements interior mutability by moving values in and out of
|
||||||
|
//! the cell. That is, an `&mut T` to the inner value can never be obtained as
|
||||||
|
//! long as the cell is shared. The value itself cannot be directly obtained
|
||||||
|
//! without copying it, cloning it, or replacing it with something else. This
|
||||||
|
//! type provides the following methods, all of which can be called only while
|
||||||
|
//! the BQL is held:
|
||||||
|
//!
|
||||||
|
//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method
|
||||||
|
//! retrieves the current interior value by duplicating it.
|
||||||
|
//! - For types that implement [`Default`], the [`take`](BqlCell::take) method
|
||||||
|
//! replaces the current interior value with [`Default::default()`] and
|
||||||
|
//! returns the replaced value.
|
||||||
|
//! - All types have:
|
||||||
|
//! - [`replace`](BqlCell::replace): replaces the current interior value and
|
||||||
|
//! returns the replaced value.
|
||||||
|
//! - [`set`](BqlCell::set): this method replaces the interior value,
|
||||||
|
//! dropping the replaced value.
|
||||||
|
//!
|
||||||
|
//! ## `BqlRefCell<T>`
|
||||||
|
//!
|
||||||
|
//! [`BqlRefCell<T>`] uses Rust's lifetimes to implement "dynamic borrowing", a
|
||||||
|
//! process whereby one can claim temporary, exclusive, mutable access to the
|
||||||
|
//! inner value:
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! fn clear_interrupts(&self, val: u32) {
|
||||||
|
//! // A mutable borrow gives read-write access to the registers
|
||||||
|
//! let mut regs = self.registers.borrow_mut();
|
||||||
|
//! let old = regs.interrupt_status();
|
||||||
|
//! regs.update_interrupt_status(old & !val);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Borrows for `BqlRefCell<T>`s are tracked at _runtime_, unlike Rust's native
|
||||||
|
//! reference types which are entirely tracked statically, at compile time.
|
||||||
|
//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
|
||||||
|
//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The
|
||||||
|
//! thread will panic if these rules are violated or if the BQL is not held.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cell::{Cell, UnsafeCell},
|
||||||
|
cmp::Ordering,
|
||||||
|
fmt,
|
||||||
|
marker::PhantomData,
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
ptr::NonNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::bindings;
|
||||||
|
|
||||||
|
// TODO: When building doctests do not include the actual BQL, because cargo
|
||||||
|
// does not know how to link them to libqemuutil. This can be fixed by
|
||||||
|
// running rustdoc from "meson test" instead of relying on cargo.
|
||||||
|
pub fn bql_locked() -> bool {
|
||||||
|
// SAFETY: the function does nothing but return a thread-local bool
|
||||||
|
!cfg!(MESON) || unsafe { bindings::bql_locked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bql_block_unlock(increase: bool) {
|
||||||
|
if cfg!(MESON) {
|
||||||
|
// SAFETY: this only adjusts a counter
|
||||||
|
unsafe {
|
||||||
|
bindings::bql_block_unlock(increase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mutable memory location that is protected by the Big QEMU Lock.
|
||||||
|
///
|
||||||
|
/// # Memory layout
|
||||||
|
///
|
||||||
|
/// `BqlCell<T>` has the same in-memory representation as its inner type `T`.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct BqlCell<T> {
|
||||||
|
value: UnsafeCell<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex,
|
||||||
|
// except it is stored out-of-line
|
||||||
|
unsafe impl<T: Send> Send for BqlCell<T> {}
|
||||||
|
unsafe impl<T: Send> Sync for BqlCell<T> {}
|
||||||
|
|
||||||
|
impl<T: Copy> Clone for BqlCell<T> {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> BqlCell<T> {
|
||||||
|
BqlCell::new(self.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Default for BqlCell<T> {
|
||||||
|
/// Creates a `BqlCell<T>`, with the `Default` value for T.
|
||||||
|
#[inline]
|
||||||
|
fn default() -> BqlCell<T> {
|
||||||
|
BqlCell::new(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq + Copy> PartialEq for BqlCell<T> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &BqlCell<T>) -> bool {
|
||||||
|
self.get() == other.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq + Copy> Eq for BqlCell<T> {}
|
||||||
|
|
||||||
|
impl<T: PartialOrd + Copy> PartialOrd for BqlCell<T> {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &BqlCell<T>) -> Option<Ordering> {
|
||||||
|
self.get().partial_cmp(&other.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ord + Copy> Ord for BqlCell<T> {
|
||||||
|
#[inline]
|
||||||
|
fn cmp(&self, other: &BqlCell<T>) -> Ordering {
|
||||||
|
self.get().cmp(&other.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for BqlCell<T> {
|
||||||
|
/// Creates a new `BqlCell<T>` containing the given value.
|
||||||
|
fn from(t: T) -> BqlCell<T> {
|
||||||
|
BqlCell::new(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug + Copy> fmt::Debug for BqlCell<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.get().fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Display + Copy> fmt::Display for BqlCell<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.get().fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BqlCell<T> {
|
||||||
|
/// Creates a new `BqlCell` containing the given value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlCell::new(5);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: T) -> BqlCell<T> {
|
||||||
|
BqlCell {
|
||||||
|
value: UnsafeCell::new(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the contained value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlCell::new(5);
|
||||||
|
///
|
||||||
|
/// c.set(10);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn set(&self, val: T) {
|
||||||
|
self.replace(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the contained value with `val`, and returns the old contained
|
||||||
|
/// value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let cell = BqlCell::new(5);
|
||||||
|
/// assert_eq!(cell.get(), 5);
|
||||||
|
/// assert_eq!(cell.replace(10), 5);
|
||||||
|
/// assert_eq!(cell.get(), 10);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn replace(&self, val: T) -> T {
|
||||||
|
assert!(bql_locked());
|
||||||
|
// SAFETY: This can cause data races if called from multiple threads,
|
||||||
|
// but it won't happen as long as C code accesses the value
|
||||||
|
// under BQL protection only.
|
||||||
|
mem::replace(unsafe { &mut *self.value.get() }, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwraps the value, consuming the cell.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlCell::new(5);
|
||||||
|
/// let five = c.into_inner();
|
||||||
|
///
|
||||||
|
/// assert_eq!(five, 5);
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
assert!(bql_locked());
|
||||||
|
self.value.into_inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> BqlCell<T> {
|
||||||
|
/// Returns a copy of the contained value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlCell::new(5);
|
||||||
|
///
|
||||||
|
/// let five = c.get();
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn get(&self) -> T {
|
||||||
|
assert!(bql_locked());
|
||||||
|
// SAFETY: This can cause data races if called from multiple threads,
|
||||||
|
// but it won't happen as long as C code accesses the value
|
||||||
|
// under BQL protection only.
|
||||||
|
unsafe { *self.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BqlCell<T> {
|
||||||
|
/// Returns a raw pointer to the underlying data in this cell.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlCell::new(5);
|
||||||
|
///
|
||||||
|
/// let ptr = c.as_ptr();
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub const fn as_ptr(&self) -> *mut T {
|
||||||
|
self.value.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> BqlCell<T> {
|
||||||
|
/// Takes the value of the cell, leaving `Default::default()` in its place.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlCell::new(5);
|
||||||
|
/// let five = c.take();
|
||||||
|
///
|
||||||
|
/// assert_eq!(five, 5);
|
||||||
|
/// assert_eq!(c.into_inner(), 0);
|
||||||
|
/// ```
|
||||||
|
pub fn take(&self) -> T {
|
||||||
|
self.replace(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mutable memory location with dynamically checked borrow rules,
|
||||||
|
/// protected by the Big QEMU Lock.
|
||||||
|
///
|
||||||
|
/// See the [module-level documentation](self) for more.
|
||||||
|
///
|
||||||
|
/// # Memory layout
|
||||||
|
///
|
||||||
|
/// `BqlRefCell<T>` starts with the same in-memory representation as its
|
||||||
|
/// inner type `T`.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct BqlRefCell<T> {
|
||||||
|
// It is important that this is the first field (which is not the case
|
||||||
|
// for std::cell::BqlRefCell), so that we can use offset_of! on it.
|
||||||
|
// UnsafeCell and repr(C) both prevent usage of niches.
|
||||||
|
value: UnsafeCell<T>,
|
||||||
|
borrow: Cell<BorrowFlag>,
|
||||||
|
// Stores the location of the earliest currently active borrow.
|
||||||
|
// This gets updated whenever we go from having zero borrows
|
||||||
|
// to having a single borrow. When a borrow occurs, this gets included
|
||||||
|
// in the panic message
|
||||||
|
#[cfg(feature = "debug_cell")]
|
||||||
|
borrowed_at: Cell<Option<&'static std::panic::Location<'static>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Positive values represent the number of `BqlRef` active. Negative values
|
||||||
|
// represent the number of `BqlRefMut` active. Right now QEMU's implementation
|
||||||
|
// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping
|
||||||
|
// components of a `BqlRefCell` (e.g., different ranges of a slice).
|
||||||
|
//
|
||||||
|
// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely
|
||||||
|
// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of
|
||||||
|
// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or
|
||||||
|
// underflow. However, this is not a guarantee, as a pathological program could
|
||||||
|
// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all
|
||||||
|
// code must explicitly check for overflow and underflow in order to avoid
|
||||||
|
// unsafety, or at least behave correctly in the event that overflow or
|
||||||
|
// underflow happens (e.g., see BorrowRef::new).
|
||||||
|
type BorrowFlag = isize;
|
||||||
|
const UNUSED: BorrowFlag = 0;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn is_writing(x: BorrowFlag) -> bool {
|
||||||
|
x < UNUSED
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn is_reading(x: BorrowFlag) -> bool {
|
||||||
|
x > UNUSED
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BqlRefCell<T> {
|
||||||
|
/// Creates a new `BqlRefCell` containing `value`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlRefCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlRefCell::new(5);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: T) -> BqlRefCell<T> {
|
||||||
|
BqlRefCell {
|
||||||
|
value: UnsafeCell::new(value),
|
||||||
|
borrow: Cell::new(UNUSED),
|
||||||
|
#[cfg(feature = "debug_cell")]
|
||||||
|
borrowed_at: Cell::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This ensures the panicking code is outlined from `borrow_mut` for
|
||||||
|
// `BqlRefCell`.
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
#[cfg(feature = "debug_cell")]
|
||||||
|
fn panic_already_borrowed(source: &Cell<Option<&'static std::panic::Location<'static>>>) -> ! {
|
||||||
|
// If a borrow occurred, then we must already have an outstanding borrow,
|
||||||
|
// so `borrowed_at` will be `Some`
|
||||||
|
panic!("already borrowed at {:?}", source.take().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
#[cfg(not(feature = "debug_cell"))]
|
||||||
|
fn panic_already_borrowed() -> ! {
|
||||||
|
panic!("already borrowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BqlRefCell<T> {
|
||||||
|
#[inline]
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
fn panic_already_borrowed(&self) -> ! {
|
||||||
|
#[cfg(feature = "debug_cell")]
|
||||||
|
{
|
||||||
|
panic_already_borrowed(&self.borrowed_at)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "debug_cell"))]
|
||||||
|
{
|
||||||
|
panic_already_borrowed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Immutably borrows the wrapped value.
|
||||||
|
///
|
||||||
|
/// The borrow lasts until the returned `BqlRef` exits scope. Multiple
|
||||||
|
/// immutable borrows can be taken out at the same time.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently mutably borrowed.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlRefCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlRefCell::new(5);
|
||||||
|
///
|
||||||
|
/// let borrowed_five = c.borrow();
|
||||||
|
/// let borrowed_five2 = c.borrow();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// An example of panic:
|
||||||
|
///
|
||||||
|
/// ```should_panic
|
||||||
|
/// use qemu_api::cell::BqlRefCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlRefCell::new(5);
|
||||||
|
///
|
||||||
|
/// let m = c.borrow_mut();
|
||||||
|
/// let b = c.borrow(); // this causes a panic
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn borrow(&self) -> BqlRef<'_, T> {
|
||||||
|
if let Some(b) = BorrowRef::new(&self.borrow) {
|
||||||
|
// `borrowed_at` is always the *first* active borrow
|
||||||
|
if b.borrow.get() == 1 {
|
||||||
|
#[cfg(feature = "debug_cell")]
|
||||||
|
self.borrowed_at.set(Some(std::panic::Location::caller()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bql_block_unlock(true);
|
||||||
|
|
||||||
|
// SAFETY: `BorrowRef` ensures that there is only immutable access
|
||||||
|
// to the value while borrowed.
|
||||||
|
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
|
||||||
|
BqlRef { value, borrow: b }
|
||||||
|
} else {
|
||||||
|
self.panic_already_borrowed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutably borrows the wrapped value.
|
||||||
|
///
|
||||||
|
/// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s
|
||||||
|
/// derived from it exit scope. The value cannot be borrowed while this
|
||||||
|
/// borrow is active.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently borrowed.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlRefCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlRefCell::new("hello".to_owned());
|
||||||
|
///
|
||||||
|
/// *c.borrow_mut() = "bonjour".to_owned();
|
||||||
|
///
|
||||||
|
/// assert_eq!(&*c.borrow(), "bonjour");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// An example of panic:
|
||||||
|
///
|
||||||
|
/// ```should_panic
|
||||||
|
/// use qemu_api::cell::BqlRefCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlRefCell::new(5);
|
||||||
|
/// let m = c.borrow();
|
||||||
|
///
|
||||||
|
/// let b = c.borrow_mut(); // this causes a panic
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn borrow_mut(&self) -> BqlRefMut<'_, T> {
|
||||||
|
if let Some(b) = BorrowRefMut::new(&self.borrow) {
|
||||||
|
#[cfg(feature = "debug_cell")]
|
||||||
|
{
|
||||||
|
self.borrowed_at.set(Some(std::panic::Location::caller()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: this only adjusts a counter
|
||||||
|
bql_block_unlock(true);
|
||||||
|
|
||||||
|
// SAFETY: `BorrowRefMut` guarantees unique access.
|
||||||
|
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
|
||||||
|
BqlRefMut {
|
||||||
|
value,
|
||||||
|
_borrow: b,
|
||||||
|
marker: PhantomData,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.panic_already_borrowed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a raw pointer to the underlying data in this cell.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use qemu_api::cell::BqlRefCell;
|
||||||
|
///
|
||||||
|
/// let c = BqlRefCell::new(5);
|
||||||
|
///
|
||||||
|
/// let ptr = c.as_ptr();
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub const fn as_ptr(&self) -> *mut T {
|
||||||
|
self.value.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is
|
||||||
|
// stored out-of-line. Even though BqlRefCell includes Cells, they are
|
||||||
|
// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU
|
||||||
|
// Lock cannot be released while any borrows is active.
|
||||||
|
unsafe impl<T> Send for BqlRefCell<T> where T: Send {}
|
||||||
|
unsafe impl<T> Sync for BqlRefCell<T> {}
|
||||||
|
|
||||||
|
impl<T: Clone> Clone for BqlRefCell<T> {
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently mutably borrowed.
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
fn clone(&self) -> BqlRefCell<T> {
|
||||||
|
BqlRefCell::new(self.borrow().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `source` is currently mutably borrowed.
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
self.value.get_mut().clone_from(&source.borrow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Default for BqlRefCell<T> {
|
||||||
|
/// Creates a `BqlRefCell<T>`, with the `Default` value for T.
|
||||||
|
#[inline]
|
||||||
|
fn default() -> BqlRefCell<T> {
|
||||||
|
BqlRefCell::new(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for BqlRefCell<T> {
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value in either `BqlRefCell` is currently mutably
|
||||||
|
/// borrowed.
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &BqlRefCell<T>) -> bool {
|
||||||
|
*self.borrow() == *other.borrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq> Eq for BqlRefCell<T> {}
|
||||||
|
|
||||||
|
impl<T: PartialOrd> PartialOrd for BqlRefCell<T> {
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value in either `BqlRefCell` is currently mutably
|
||||||
|
/// borrowed.
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &BqlRefCell<T>) -> Option<Ordering> {
|
||||||
|
self.borrow().partial_cmp(&*other.borrow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ord> Ord for BqlRefCell<T> {
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value in either `BqlRefCell` is currently mutably
|
||||||
|
/// borrowed.
|
||||||
|
#[inline]
|
||||||
|
fn cmp(&self, other: &BqlRefCell<T>) -> Ordering {
|
||||||
|
self.borrow().cmp(&*other.borrow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for BqlRefCell<T> {
|
||||||
|
/// Creates a new `BqlRefCell<T>` containing the given value.
|
||||||
|
fn from(t: T) -> BqlRefCell<T> {
|
||||||
|
BqlRefCell::new(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BorrowRef<'b> {
|
||||||
|
borrow: &'b Cell<BorrowFlag>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> BorrowRef<'b> {
|
||||||
|
#[inline]
|
||||||
|
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRef<'b>> {
|
||||||
|
let b = borrow.get().wrapping_add(1);
|
||||||
|
if !is_reading(b) {
|
||||||
|
// Incrementing borrow can result in a non-reading value (<= 0) in these cases:
|
||||||
|
// 1. It was < 0, i.e. there are writing borrows, so we can't allow a read
|
||||||
|
// borrow due to Rust's reference aliasing rules
|
||||||
|
// 2. It was isize::MAX (the max amount of reading borrows) and it overflowed
|
||||||
|
// into isize::MIN (the max amount of writing borrows) so we can't allow an
|
||||||
|
// additional read borrow because isize can't represent so many read borrows
|
||||||
|
// (this can only happen if you mem::forget more than a small constant amount
|
||||||
|
// of `BqlRef`s, which is not good practice)
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Incrementing borrow can result in a reading value (> 0) in these cases:
|
||||||
|
// 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read
|
||||||
|
// borrow
|
||||||
|
// 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is
|
||||||
|
// large enough to represent having one more read borrow
|
||||||
|
borrow.set(b);
|
||||||
|
Some(BorrowRef { borrow })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BorrowRef<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let borrow = self.borrow.get();
|
||||||
|
debug_assert!(is_reading(borrow));
|
||||||
|
self.borrow.set(borrow - 1);
|
||||||
|
bql_block_unlock(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for BorrowRef<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
BorrowRef::new(self.borrow).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a borrowed reference to a value in a `BqlRefCell` box.
|
||||||
|
/// A wrapper type for an immutably borrowed value from a `BqlRefCell<T>`.
|
||||||
|
///
|
||||||
|
/// See the [module-level documentation](self) for more.
|
||||||
|
pub struct BqlRef<'b, T: 'b> {
|
||||||
|
// NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a
|
||||||
|
// `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops.
|
||||||
|
// `NonNull` is also covariant over `T`, just like we would have with `&T`.
|
||||||
|
value: NonNull<T>,
|
||||||
|
borrow: BorrowRef<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for BqlRef<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
// SAFETY: the value is accessible as long as we hold our borrow.
|
||||||
|
unsafe { self.value.as_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b, T> BqlRef<'b, T> {
|
||||||
|
/// Copies a `BqlRef`.
|
||||||
|
///
|
||||||
|
/// The `BqlRefCell` is already immutably borrowed, so this cannot fail.
|
||||||
|
///
|
||||||
|
/// This is an associated function that needs to be used as
|
||||||
|
/// `BqlRef::clone(...)`. A `Clone` implementation or a method would
|
||||||
|
/// interfere with the widespread use of `r.borrow().clone()` to clone
|
||||||
|
/// the contents of a `BqlRefCell`.
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
#[allow(clippy::should_implement_trait)]
|
||||||
|
pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> {
|
||||||
|
BqlRef {
|
||||||
|
value: orig.value,
|
||||||
|
borrow: orig.borrow.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for BqlRef<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
(**self).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Display> fmt::Display for BqlRef<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
(**self).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BorrowRefMut<'b> {
|
||||||
|
borrow: &'b Cell<BorrowFlag>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> BorrowRefMut<'b> {
|
||||||
|
#[inline]
|
||||||
|
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
|
||||||
|
// There must currently be no existing references when borrow_mut() is
|
||||||
|
// called, so we explicitly only allow going from UNUSED to UNUSED - 1.
|
||||||
|
match borrow.get() {
|
||||||
|
UNUSED => {
|
||||||
|
borrow.set(UNUSED - 1);
|
||||||
|
Some(BorrowRefMut { borrow })
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BorrowRefMut<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let borrow = self.borrow.get();
|
||||||
|
debug_assert!(is_writing(borrow));
|
||||||
|
self.borrow.set(borrow + 1);
|
||||||
|
bql_block_unlock(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper type for a mutably borrowed value from a `BqlRefCell<T>`.
|
||||||
|
///
|
||||||
|
/// See the [module-level documentation](self) for more.
|
||||||
|
pub struct BqlRefMut<'b, T: 'b> {
|
||||||
|
// NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a
|
||||||
|
// `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops.
|
||||||
|
value: NonNull<T>,
|
||||||
|
_borrow: BorrowRefMut<'b>,
|
||||||
|
// `NonNull` is covariant over `T`, so we need to reintroduce invariance.
|
||||||
|
marker: PhantomData<&'b mut T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for BqlRefMut<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
// SAFETY: the value is accessible as long as we hold our borrow.
|
||||||
|
unsafe { self.value.as_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for BqlRefMut<'_, T> {
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
// SAFETY: the value is accessible as long as we hold our borrow.
|
||||||
|
unsafe { self.value.as_mut() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for BqlRefMut<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
(**self).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
(**self).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,20 +8,122 @@ use std::{ffi::CStr, os::raw::c_void};
|
||||||
|
|
||||||
use crate::bindings::{Object, ObjectClass, TypeInfo};
|
use crate::bindings::{Object, ObjectClass, TypeInfo};
|
||||||
|
|
||||||
/// Trait a type must implement to be registered with QEMU.
|
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
|
||||||
pub trait ObjectImpl {
|
// SAFETY: obj is an instance of T, since rust_instance_init<T>
|
||||||
type Class;
|
// is called from QOM core as the instance_init function
|
||||||
const TYPE_INFO: TypeInfo;
|
// for class T
|
||||||
const TYPE_NAME: &'static CStr;
|
unsafe { T::INSTANCE_INIT.unwrap()(&mut *obj.cast::<T>()) }
|
||||||
const PARENT_TYPE_NAME: Option<&'static CStr>;
|
|
||||||
const ABSTRACT: bool;
|
|
||||||
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)>;
|
|
||||||
const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)>;
|
|
||||||
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Class {
|
unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) {
|
||||||
|
// SAFETY: obj is an instance of T, since rust_instance_post_init<T>
|
||||||
|
// is called from QOM core as the instance_post_init function
|
||||||
|
// for class T
|
||||||
|
//
|
||||||
|
// FIXME: it's not really guaranteed that there are no backpointers to
|
||||||
|
// obj; it's quite possible that they have been created by instance_init().
|
||||||
|
// The receiver should be &self, not &mut self.
|
||||||
|
T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::<T>() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait exposed by all structs corresponding to QOM objects.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// For classes declared in C:
|
||||||
|
///
|
||||||
|
/// - `Class` and `TYPE` must match the data in the `TypeInfo`;
|
||||||
|
///
|
||||||
|
/// - the first field of the struct must be of the instance type corresponding
|
||||||
|
/// to the superclass, as declared in the `TypeInfo`
|
||||||
|
///
|
||||||
|
/// - likewise, the first field of the `Class` struct must be of the class type
|
||||||
|
/// corresponding to the superclass
|
||||||
|
///
|
||||||
|
/// For classes declared in Rust and implementing [`ObjectImpl`]:
|
||||||
|
///
|
||||||
|
/// - the struct must be `#[repr(C)]`;
|
||||||
|
///
|
||||||
|
/// - the first field of the struct must be of the instance struct corresponding
|
||||||
|
/// to the superclass, which is `ObjectImpl::ParentType`
|
||||||
|
///
|
||||||
|
/// - likewise, the first field of the `Class` must be of the class struct
|
||||||
|
/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`.
|
||||||
|
pub unsafe trait ObjectType: Sized {
|
||||||
|
/// The QOM class object corresponding to this struct. Not used yet.
|
||||||
|
type Class;
|
||||||
|
|
||||||
|
/// The name of the type, which can be passed to `object_new()` to
|
||||||
|
/// generate an instance of this type.
|
||||||
|
const TYPE_NAME: &'static CStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait a type must implement to be registered with QEMU.
|
||||||
|
pub trait ObjectImpl: ObjectType + ClassInitImpl {
|
||||||
|
/// The parent of the type. This should match the first field of
|
||||||
|
/// the struct that implements `ObjectImpl`:
|
||||||
|
type ParentType: ObjectType;
|
||||||
|
|
||||||
|
/// Whether the object can be instantiated
|
||||||
|
const ABSTRACT: bool = false;
|
||||||
|
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
||||||
|
|
||||||
|
/// Function that is called to initialize an object. The parent class will
|
||||||
|
/// have already been initialized so the type is only responsible for
|
||||||
|
/// initializing its own members.
|
||||||
|
///
|
||||||
|
/// FIXME: The argument is not really a valid reference. `&mut
|
||||||
|
/// MaybeUninit<Self>` would be a better description.
|
||||||
|
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = None;
|
||||||
|
|
||||||
|
/// Function that is called to finish initialization of an object, once
|
||||||
|
/// `INSTANCE_INIT` functions have been called.
|
||||||
|
const INSTANCE_POST_INIT: Option<fn(&mut Self)> = None;
|
||||||
|
|
||||||
|
const TYPE_INFO: TypeInfo = TypeInfo {
|
||||||
|
name: Self::TYPE_NAME.as_ptr(),
|
||||||
|
parent: Self::ParentType::TYPE_NAME.as_ptr(),
|
||||||
|
instance_size: core::mem::size_of::<Self>(),
|
||||||
|
instance_align: core::mem::align_of::<Self>(),
|
||||||
|
instance_init: match Self::INSTANCE_INIT {
|
||||||
|
None => None,
|
||||||
|
Some(_) => Some(rust_instance_init::<Self>),
|
||||||
|
},
|
||||||
|
instance_post_init: match Self::INSTANCE_POST_INIT {
|
||||||
|
None => None,
|
||||||
|
Some(_) => Some(rust_instance_post_init::<Self>),
|
||||||
|
},
|
||||||
|
instance_finalize: Self::INSTANCE_FINALIZE,
|
||||||
|
abstract_: Self::ABSTRACT,
|
||||||
|
class_size: core::mem::size_of::<Self::Class>(),
|
||||||
|
class_init: <Self as ClassInitImpl>::CLASS_INIT,
|
||||||
|
class_base_init: <Self as ClassInitImpl>::CLASS_BASE_INIT,
|
||||||
|
class_data: core::ptr::null_mut(),
|
||||||
|
interfaces: core::ptr::null_mut(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait used to fill in a class struct.
|
||||||
|
///
|
||||||
|
/// Each QOM class that has virtual methods describes them in a
|
||||||
|
/// _class struct_. Class structs include a parent field corresponding
|
||||||
|
/// to the vtable of the parent class, all the way up to [`ObjectClass`].
|
||||||
|
/// Each QOM type has one such class struct.
|
||||||
|
///
|
||||||
|
/// The Rust implementation of methods will usually come from a trait
|
||||||
|
/// like [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl).
|
||||||
|
pub trait ClassInitImpl {
|
||||||
|
/// Function that is called after all parent class initialization
|
||||||
|
/// has occurred. On entry, the virtual method pointers are set to
|
||||||
|
/// the default values coming from the parent classes; the function
|
||||||
|
/// can change them to override virtual methods of a parent class.
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)>;
|
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)>;
|
||||||
|
|
||||||
|
/// Called on descendent classes after all parent class initialization
|
||||||
|
/// has occurred, but before the class itself is initialized. This
|
||||||
|
/// is only useful if a class is not a leaf, and can be used to undo
|
||||||
|
/// the effects of copying the contents of the parent's class struct
|
||||||
|
/// to the descendants.
|
||||||
const CLASS_BASE_INIT: Option<
|
const CLASS_BASE_INIT: Option<
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
||||||
>;
|
>;
|
||||||
|
@ -64,28 +166,3 @@ macro_rules! module_init {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! type_info {
|
|
||||||
($t:ty) => {
|
|
||||||
$crate::bindings::TypeInfo {
|
|
||||||
name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(),
|
|
||||||
parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME {
|
|
||||||
pname.as_ptr()
|
|
||||||
} else {
|
|
||||||
::core::ptr::null_mut()
|
|
||||||
},
|
|
||||||
instance_size: ::core::mem::size_of::<$t>(),
|
|
||||||
instance_align: ::core::mem::align_of::<$t>(),
|
|
||||||
instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
|
|
||||||
instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
|
|
||||||
instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
|
|
||||||
abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
|
|
||||||
class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(),
|
|
||||||
class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT,
|
|
||||||
class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
|
|
||||||
class_data: ::core::ptr::null_mut(),
|
|
||||||
interfaces: ::core::ptr::null_mut(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,32 +2,112 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::{ffi::CStr, os::raw::c_void};
|
||||||
|
|
||||||
use crate::bindings;
|
use crate::{
|
||||||
|
bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
|
||||||
|
prelude::*,
|
||||||
|
zeroable::Zeroable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Trait providing the contents of [`DeviceClass`].
|
||||||
|
pub trait DeviceImpl {
|
||||||
|
/// _Realization_ is the second stage of device creation. It contains
|
||||||
|
/// all operations that depend on device properties and can fail (note:
|
||||||
|
/// this is not yet supported for Rust devices).
|
||||||
|
///
|
||||||
|
/// If not `None`, the parent class's `realize` method is overridden
|
||||||
|
/// with the function pointed to by `REALIZE`.
|
||||||
|
const REALIZE: Option<fn(&mut Self)> = None;
|
||||||
|
|
||||||
|
/// If not `None`, the parent class's `reset` method is overridden
|
||||||
|
/// with the function pointed to by `RESET`.
|
||||||
|
///
|
||||||
|
/// Rust does not yet support the three-phase reset protocol; this is
|
||||||
|
/// usually okay for leaf classes.
|
||||||
|
const RESET: Option<fn(&mut Self)> = None;
|
||||||
|
|
||||||
|
/// An array providing the properties that the user can set on the
|
||||||
|
/// device. Not a `const` because referencing statics in constants
|
||||||
|
/// is unstable until Rust 1.83.0.
|
||||||
|
fn properties() -> &'static [Property] {
|
||||||
|
&[Zeroable::ZERO; 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `VMStateDescription` providing the migration format for the device
|
||||||
|
/// Not a `const` because referencing statics in constants is unstable
|
||||||
|
/// until Rust 1.83.0.
|
||||||
|
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function is only called through the QOM machinery and
|
||||||
|
/// the `impl_device_class!` macro.
|
||||||
|
/// We expect the FFI user of this function to pass a valid pointer that
|
||||||
|
/// can be downcasted to type `T`. We also expect the device is
|
||||||
|
/// readable/writeable from one thread at any time.
|
||||||
|
unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) {
|
||||||
|
assert!(!dev.is_null());
|
||||||
|
let state = dev.cast::<T>();
|
||||||
|
T::REALIZE.unwrap()(unsafe { &mut *state });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// We expect the FFI user of this function to pass a valid pointer that
|
||||||
|
/// can be downcasted to type `T`. We also expect the device is
|
||||||
|
/// readable/writeable from one thread at any time.
|
||||||
|
unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) {
|
||||||
|
assert!(!dev.is_null());
|
||||||
|
let state = dev.cast::<T>();
|
||||||
|
T::RESET.unwrap()(unsafe { &mut *state });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// We expect the FFI user of this function to pass a valid pointer that
|
||||||
|
/// can be downcasted to type `DeviceClass`, because `T` implements
|
||||||
|
/// `DeviceImpl`.
|
||||||
|
pub unsafe extern "C" fn rust_device_class_init<T: DeviceImpl>(
|
||||||
|
klass: *mut ObjectClass,
|
||||||
|
_: *mut c_void,
|
||||||
|
) {
|
||||||
|
let mut dc = ::core::ptr::NonNull::new(klass.cast::<DeviceClass>()).unwrap();
|
||||||
|
unsafe {
|
||||||
|
let dc = dc.as_mut();
|
||||||
|
if <T as DeviceImpl>::REALIZE.is_some() {
|
||||||
|
dc.realize = Some(rust_realize_fn::<T>);
|
||||||
|
}
|
||||||
|
if <T as DeviceImpl>::RESET.is_some() {
|
||||||
|
bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>));
|
||||||
|
}
|
||||||
|
if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
|
||||||
|
dc.vmsd = vmsd;
|
||||||
|
}
|
||||||
|
bindings::device_class_set_props(dc, <T as DeviceImpl>::properties().as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! device_class_init {
|
macro_rules! impl_device_class {
|
||||||
($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
|
($type:ty) => {
|
||||||
pub unsafe extern "C" fn $func(
|
impl $crate::definitions::ClassInitImpl for $type {
|
||||||
klass: *mut $crate::bindings::ObjectClass,
|
const CLASS_INIT: Option<
|
||||||
_: *mut ::std::os::raw::c_void,
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void),
|
||||||
) {
|
> = Some($crate::device_class::rust_device_class_init::<$type>);
|
||||||
let mut dc =
|
const CLASS_BASE_INIT: Option<
|
||||||
::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void),
|
||||||
unsafe {
|
> = None;
|
||||||
dc.as_mut().realize = $realize_fn;
|
|
||||||
dc.as_mut().vmsd = &$vmsd;
|
|
||||||
$crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
|
|
||||||
$crate::bindings::device_class_set_props(dc.as_mut(), $props.as_ptr());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! define_property {
|
macro_rules! define_property {
|
||||||
($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
|
($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => {
|
||||||
$crate::bindings::Property {
|
$crate::bindings::Property {
|
||||||
// use associated function syntax for type checking
|
// use associated function syntax for type checking
|
||||||
name: ::std::ffi::CStr::as_ptr($name),
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
|
@ -38,7 +118,7 @@ macro_rules! define_property {
|
||||||
..$crate::zeroable::Zeroable::ZERO
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => {
|
($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => {
|
||||||
$crate::bindings::Property {
|
$crate::bindings::Property {
|
||||||
// use associated function syntax for type checking
|
// use associated function syntax for type checking
|
||||||
name: ::std::ffi::CStr::as_ptr($name),
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
|
@ -67,8 +147,8 @@ macro_rules! declare_properties {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround until we can use --generate-cstr in bindgen.
|
unsafe impl ObjectType for bindings::DeviceState {
|
||||||
pub const TYPE_DEVICE: &CStr =
|
type Class = bindings::DeviceClass;
|
||||||
|
const TYPE_NAME: &'static CStr =
|
||||||
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
|
||||||
pub const TYPE_SYS_BUS_DEVICE: &CStr =
|
}
|
||||||
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
|
|
||||||
|
|
91
rust/qemu-api/src/irq.rs
Normal file
91
rust/qemu-api/src/irq.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright 2024 Red Hat, Inc.
|
||||||
|
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
//! Bindings for interrupt sources
|
||||||
|
|
||||||
|
use core::ptr;
|
||||||
|
use std::{marker::PhantomData, os::raw::c_int};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bindings::{qemu_set_irq, IRQState},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Interrupt sources are used by devices to pass changes to a value (typically
|
||||||
|
/// a boolean). The interrupt sink is usually an interrupt controller or
|
||||||
|
/// GPIO controller.
|
||||||
|
///
|
||||||
|
/// As far as devices are concerned, interrupt sources are always active-high:
|
||||||
|
/// for example, `InterruptSource<bool>`'s [`raise`](InterruptSource::raise)
|
||||||
|
/// method sends a `true` value to the sink. If the guest has to see a
|
||||||
|
/// different polarity, that change is performed by the board between the
|
||||||
|
/// device and the interrupt controller.
|
||||||
|
///
|
||||||
|
/// Interrupts are implemented as a pointer to the interrupt "sink", which has
|
||||||
|
/// type [`IRQState`]. A device exposes its source as a QOM link property using
|
||||||
|
/// a function such as
|
||||||
|
/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and
|
||||||
|
/// initially leaves the pointer to a NULL value, representing an unconnected
|
||||||
|
/// interrupt. To connect it, whoever creates the device fills the pointer with
|
||||||
|
/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because
|
||||||
|
/// devices are generally shared objects, interrupt sources are an example of
|
||||||
|
/// the interior mutability pattern.
|
||||||
|
///
|
||||||
|
/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell`
|
||||||
|
/// allows access from whatever thread has it.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct InterruptSource<T = bool>
|
||||||
|
where
|
||||||
|
c_int: From<T>,
|
||||||
|
{
|
||||||
|
cell: BqlCell<*mut IRQState>,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptSource<bool> {
|
||||||
|
/// Send a low (`false`) value to the interrupt sink.
|
||||||
|
pub fn lower(&self) {
|
||||||
|
self.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a high-low pulse to the interrupt sink.
|
||||||
|
pub fn pulse(&self) {
|
||||||
|
self.set(true);
|
||||||
|
self.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a high (`true`) value to the interrupt sink.
|
||||||
|
pub fn raise(&self) {
|
||||||
|
self.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InterruptSource<T>
|
||||||
|
where
|
||||||
|
c_int: From<T>,
|
||||||
|
{
|
||||||
|
/// Send `level` to the interrupt sink.
|
||||||
|
pub fn set(&self, level: T) {
|
||||||
|
let ptr = self.cell.get();
|
||||||
|
// SAFETY: the pointer is retrieved under the BQL and remains valid
|
||||||
|
// until the BQL is released, which is after qemu_set_irq() is entered.
|
||||||
|
unsafe {
|
||||||
|
qemu_set_irq(ptr, level.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState {
|
||||||
|
self.cell.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InterruptSource {
|
||||||
|
fn default() -> Self {
|
||||||
|
InterruptSource {
|
||||||
|
cell: BqlCell::new(ptr::null_mut()),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,35 +4,22 @@
|
||||||
|
|
||||||
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
|
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
|
||||||
|
|
||||||
#[allow(
|
|
||||||
dead_code,
|
|
||||||
improper_ctypes_definitions,
|
|
||||||
improper_ctypes,
|
|
||||||
non_camel_case_types,
|
|
||||||
non_snake_case,
|
|
||||||
non_upper_case_globals,
|
|
||||||
unsafe_op_in_unsafe_fn,
|
|
||||||
clippy::missing_const_for_fn,
|
|
||||||
clippy::too_many_arguments,
|
|
||||||
clippy::approx_constant,
|
|
||||||
clippy::use_self,
|
|
||||||
clippy::useless_transmute,
|
|
||||||
clippy::missing_safety_doc,
|
|
||||||
)]
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub mod bindings;
|
pub mod bindings;
|
||||||
|
|
||||||
unsafe impl Send for bindings::Property {}
|
// preserve one-item-per-"use" syntax, it is clearer
|
||||||
unsafe impl Sync for bindings::Property {}
|
// for prelude-like modules
|
||||||
unsafe impl Sync for bindings::TypeInfo {}
|
#[rustfmt::skip]
|
||||||
unsafe impl Sync for bindings::VMStateDescription {}
|
pub mod prelude;
|
||||||
unsafe impl Sync for bindings::VMStateField {}
|
|
||||||
unsafe impl Sync for bindings::VMStateInfo {}
|
|
||||||
|
|
||||||
|
pub mod bitops;
|
||||||
pub mod c_str;
|
pub mod c_str;
|
||||||
|
pub mod cell;
|
||||||
pub mod definitions;
|
pub mod definitions;
|
||||||
pub mod device_class;
|
pub mod device_class;
|
||||||
|
pub mod irq;
|
||||||
pub mod offset_of;
|
pub mod offset_of;
|
||||||
|
pub mod sysbus;
|
||||||
pub mod vmstate;
|
pub mod vmstate;
|
||||||
pub mod zeroable;
|
pub mod zeroable;
|
||||||
|
|
||||||
|
|
10
rust/qemu-api/src/prelude.rs
Normal file
10
rust/qemu-api/src/prelude.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2024 Red Hat, Inc.
|
||||||
|
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
pub use crate::bitops::IntegerExt;
|
||||||
|
|
||||||
|
pub use crate::cell::BqlCell;
|
||||||
|
pub use crate::cell::BqlRefCell;
|
||||||
|
|
||||||
|
pub use crate::definitions::ObjectType;
|
33
rust/qemu-api/src/sysbus.rs
Normal file
33
rust/qemu-api/src/sysbus.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2024 Red Hat, Inc.
|
||||||
|
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
use std::{ffi::CStr, ptr::addr_of};
|
||||||
|
|
||||||
|
pub use bindings::{SysBusDevice, SysBusDeviceClass};
|
||||||
|
|
||||||
|
use crate::{bindings, cell::bql_locked, irq::InterruptSource, prelude::*};
|
||||||
|
|
||||||
|
unsafe impl ObjectType for SysBusDevice {
|
||||||
|
type Class = SysBusDeviceClass;
|
||||||
|
const TYPE_NAME: &'static CStr =
|
||||||
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysBusDevice {
|
||||||
|
/// Return `self` cast to a mutable pointer, for use in calls to C code.
|
||||||
|
const fn as_mut_ptr(&self) -> *mut SysBusDevice {
|
||||||
|
addr_of!(*self) as *mut _
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expose an interrupt source outside the device as a qdev GPIO output.
|
||||||
|
/// Note that the ordering of calls to `init_irq` is important, since
|
||||||
|
/// whoever creates the sysbus device will refer to the interrupts with
|
||||||
|
/// a number that corresponds to the order of calls to `init_irq`.
|
||||||
|
pub fn init_irq(&self, irq: &InterruptSource) {
|
||||||
|
assert!(bql_locked());
|
||||||
|
unsafe {
|
||||||
|
bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ use std::ptr;
|
||||||
/// behavior. This trait in principle could be implemented as just:
|
/// behavior. This trait in principle could be implemented as just:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// const ZERO: Self = unsafe {
|
/// pub unsafe trait Zeroable: Default {
|
||||||
/// ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init()
|
/// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
|
||||||
/// },
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The need for a manual implementation is only because `zeroed()` cannot
|
/// The need for a manual implementation is only because `zeroed()` cannot
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::{ffi::CStr, os::raw::c_void};
|
use std::ffi::CStr;
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::*,
|
bindings::*, c_str, declare_properties, define_property, definitions::ObjectImpl,
|
||||||
c_str, declare_properties, define_property,
|
device_class::DeviceImpl, impl_device_class, prelude::*, zeroable::Zeroable,
|
||||||
definitions::{Class, ObjectImpl},
|
|
||||||
device_class, device_class_init,
|
|
||||||
zeroable::Zeroable,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -45,35 +42,29 @@ fn test_device_decl_macros() {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
device_class_init! {
|
unsafe impl ObjectType for DummyState {
|
||||||
dummy_class_init,
|
type Class = DummyClass;
|
||||||
props => DUMMY_PROPERTIES,
|
const TYPE_NAME: &'static CStr = c_str!("dummy");
|
||||||
realize_fn => None,
|
|
||||||
legacy_reset_fn => None,
|
|
||||||
vmsd => VMSTATE,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for DummyState {
|
impl ObjectImpl for DummyState {
|
||||||
type Class = DummyClass;
|
type ParentType = DeviceState;
|
||||||
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
|
|
||||||
const TYPE_NAME: &'static CStr = c_str!("dummy");
|
|
||||||
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
|
|
||||||
const ABSTRACT: bool = false;
|
const ABSTRACT: bool = false;
|
||||||
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Class for DummyClass {
|
impl DeviceImpl for DummyState {
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
fn properties() -> &'static [Property] {
|
||||||
Some(dummy_class_init);
|
&DUMMY_PROPERTIES
|
||||||
const CLASS_BASE_INIT: Option<
|
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
|
||||||
> = None;
|
|
||||||
}
|
}
|
||||||
|
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||||
|
Some(&VMSTATE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_device_class!(DummyState);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
module_call_init(module_init_type::MODULE_INIT_QOM);
|
module_call_init(module_init_type::MODULE_INIT_QOM);
|
||||||
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()) as *mut _);
|
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -901,26 +901,6 @@ class TypeRegisterCall(FileMatch):
|
||||||
regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register'),
|
regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register'),
|
||||||
r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n')
|
r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n')
|
||||||
|
|
||||||
class MakeTypeRegisterStatic(TypeRegisterCall):
|
|
||||||
"""Make type_register() call static if variable is static const"""
|
|
||||||
def gen_patches(self):
|
|
||||||
var = self.file.find_match(TypeInfoVar, self.name)
|
|
||||||
if var is None:
|
|
||||||
self.warn("can't find TypeInfo var declaration for %s", self.name)
|
|
||||||
return
|
|
||||||
if var.is_static() and var.is_const():
|
|
||||||
yield self.group_match('func_name').make_patch('type_register_static')
|
|
||||||
|
|
||||||
class MakeTypeRegisterNotStatic(TypeRegisterStaticCall):
|
|
||||||
"""Make type_register() call static if variable is static const"""
|
|
||||||
def gen_patches(self):
|
|
||||||
var = self.file.find_match(TypeInfoVar, self.name)
|
|
||||||
if var is None:
|
|
||||||
self.warn("can't find TypeInfo var declaration for %s", self.name)
|
|
||||||
return
|
|
||||||
if not var.is_static() or not var.is_const():
|
|
||||||
yield self.group_match('func_name').make_patch('type_register')
|
|
||||||
|
|
||||||
class TypeInfoMacro(FileMatch):
|
class TypeInfoMacro(FileMatch):
|
||||||
"""TYPE_INFO macro usage"""
|
"""TYPE_INFO macro usage"""
|
||||||
regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n')
|
regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
|
@ -25,31 +25,110 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Iterable, List, Mapping, Optional, Set
|
||||||
|
|
||||||
from typing import List
|
try:
|
||||||
|
import tomllib
|
||||||
|
except ImportError:
|
||||||
|
import tomli as tomllib
|
||||||
|
|
||||||
|
STRICT_LINTS = {"unknown_lints", "warnings"}
|
||||||
|
|
||||||
|
|
||||||
def generate_cfg_flags(header: str) -> List[str]:
|
class CargoTOML:
|
||||||
|
tomldata: Mapping[Any, Any]
|
||||||
|
workspace_data: Mapping[Any, Any]
|
||||||
|
check_cfg: Set[str]
|
||||||
|
|
||||||
|
def __init__(self, path: Optional[str], workspace: Optional[str]):
|
||||||
|
if path is not None:
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
self.tomldata = tomllib.load(f)
|
||||||
|
else:
|
||||||
|
self.tomldata = {"lints": {"workspace": True}}
|
||||||
|
|
||||||
|
if workspace is not None:
|
||||||
|
with open(workspace, 'rb') as f:
|
||||||
|
self.workspace_data = tomllib.load(f)
|
||||||
|
if "workspace" not in self.workspace_data:
|
||||||
|
self.workspace_data["workspace"] = {}
|
||||||
|
|
||||||
|
self.check_cfg = set(self.find_check_cfg())
|
||||||
|
|
||||||
|
def find_check_cfg(self) -> Iterable[str]:
|
||||||
|
toml_lints = self.lints
|
||||||
|
rust_lints = toml_lints.get("rust", {})
|
||||||
|
cfg_lint = rust_lints.get("unexpected_cfgs", {})
|
||||||
|
return cfg_lint.get("check-cfg", [])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lints(self) -> Mapping[Any, Any]:
|
||||||
|
return self.get_table("lints", True)
|
||||||
|
|
||||||
|
def get_table(self, key: str, can_be_workspace: bool = False) -> Mapping[Any, Any]:
|
||||||
|
table = self.tomldata.get(key, {})
|
||||||
|
if can_be_workspace and table.get("workspace", False) is True:
|
||||||
|
table = self.workspace_data["workspace"].get(key, {})
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LintFlag:
|
||||||
|
flags: List[str]
|
||||||
|
priority: int
|
||||||
|
|
||||||
|
|
||||||
|
def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[str]:
|
||||||
|
"""Converts Cargo.toml lints to rustc -A/-D/-F/-W flags."""
|
||||||
|
|
||||||
|
toml_lints = cargo_toml.lints
|
||||||
|
|
||||||
|
lint_list = []
|
||||||
|
for k, v in toml_lints.items():
|
||||||
|
prefix = "" if k == "rust" else k + "::"
|
||||||
|
for lint, data in v.items():
|
||||||
|
level = data if isinstance(data, str) else data["level"]
|
||||||
|
priority = 0 if isinstance(data, str) else data.get("priority", 0)
|
||||||
|
if level == "deny":
|
||||||
|
flag = "-D"
|
||||||
|
elif level == "allow":
|
||||||
|
flag = "-A"
|
||||||
|
elif level == "warn":
|
||||||
|
flag = "-W"
|
||||||
|
elif level == "forbid":
|
||||||
|
flag = "-F"
|
||||||
|
else:
|
||||||
|
raise Exception(f"invalid level {level} for {prefix}{lint}")
|
||||||
|
|
||||||
|
# This may change if QEMU ever invokes clippy-driver or rustdoc by
|
||||||
|
# hand. For now, check the syntax but do not add non-rustc lints to
|
||||||
|
# the command line.
|
||||||
|
if k == "rust" and not (strict_lints and lint in STRICT_LINTS):
|
||||||
|
lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority))
|
||||||
|
|
||||||
|
if strict_lints:
|
||||||
|
for lint in STRICT_LINTS:
|
||||||
|
lint_list.append(LintFlag(flags=["-D", lint], priority=1000000))
|
||||||
|
|
||||||
|
lint_list.sort(key=lambda x: x.priority)
|
||||||
|
for lint in lint_list:
|
||||||
|
yield from lint.flags
|
||||||
|
|
||||||
|
|
||||||
|
def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]:
|
||||||
"""Converts defines from config[..].h headers to rustc --cfg flags."""
|
"""Converts defines from config[..].h headers to rustc --cfg flags."""
|
||||||
|
|
||||||
def cfg_name(name: str) -> str:
|
|
||||||
"""Filter function for C #defines"""
|
|
||||||
if (
|
|
||||||
name.startswith("CONFIG_")
|
|
||||||
or name.startswith("TARGET_")
|
|
||||||
or name.startswith("HAVE_")
|
|
||||||
):
|
|
||||||
return name
|
|
||||||
return ""
|
|
||||||
|
|
||||||
with open(header, encoding="utf-8") as cfg:
|
with open(header, encoding="utf-8") as cfg:
|
||||||
config = [l.split()[1:] for l in cfg if l.startswith("#define")]
|
config = [l.split()[1:] for l in cfg if l.startswith("#define")]
|
||||||
|
|
||||||
cfg_list = []
|
cfg_list = []
|
||||||
for cfg in config:
|
for cfg in config:
|
||||||
name = cfg_name(cfg[0])
|
name = cfg[0]
|
||||||
if not name:
|
if f'cfg({name})' not in cargo_toml.check_cfg:
|
||||||
continue
|
continue
|
||||||
if len(cfg) >= 2 and cfg[1] != "1":
|
if len(cfg) >= 2 and cfg[1] != "1":
|
||||||
continue
|
continue
|
||||||
|
@ -59,7 +138,6 @@ def generate_cfg_flags(header: str) -> List[str]:
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
# pylint: disable=missing-function-docstring
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("-v", "--verbose", action="store_true")
|
parser.add_argument("-v", "--verbose", action="store_true")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -71,12 +149,83 @@ def main() -> None:
|
||||||
required=False,
|
required=False,
|
||||||
default=[],
|
default=[],
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
metavar="TOML_FILE",
|
||||||
|
action="store",
|
||||||
|
dest="cargo_toml",
|
||||||
|
help="path to Cargo.toml file",
|
||||||
|
nargs='?',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--workspace",
|
||||||
|
metavar="DIR",
|
||||||
|
action="store",
|
||||||
|
dest="workspace",
|
||||||
|
help="path to root of the workspace",
|
||||||
|
required=False,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--features",
|
||||||
|
action="store_true",
|
||||||
|
dest="features",
|
||||||
|
help="generate --check-cfg arguments for features",
|
||||||
|
required=False,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--lints",
|
||||||
|
action="store_true",
|
||||||
|
dest="lints",
|
||||||
|
help="generate arguments from [lints] table",
|
||||||
|
required=False,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--rustc-version",
|
||||||
|
metavar="VERSION",
|
||||||
|
dest="rustc_version",
|
||||||
|
action="store",
|
||||||
|
help="version of rustc",
|
||||||
|
required=False,
|
||||||
|
default="1.0.0",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--strict-lints",
|
||||||
|
action="store_true",
|
||||||
|
dest="strict_lints",
|
||||||
|
help="apply stricter checks (for nightly Rust)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logging.debug("args: %s", args)
|
logging.debug("args: %s", args)
|
||||||
|
|
||||||
|
rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2]))
|
||||||
|
if args.workspace:
|
||||||
|
workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve()
|
||||||
|
cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml))
|
||||||
|
else:
|
||||||
|
cargo_toml = CargoTOML(args.cargo_toml, None)
|
||||||
|
|
||||||
|
if args.lints:
|
||||||
|
for tok in generate_lint_flags(cargo_toml, args.strict_lints):
|
||||||
|
print(tok)
|
||||||
|
|
||||||
|
if rustc_version >= (1, 80):
|
||||||
|
if args.lints:
|
||||||
|
for cfg in sorted(cargo_toml.check_cfg):
|
||||||
|
print("--check-cfg")
|
||||||
|
print(cfg)
|
||||||
|
if args.features:
|
||||||
|
for feature in cargo_toml.get_table("features"):
|
||||||
|
if feature != "default":
|
||||||
|
print("--check-cfg")
|
||||||
|
print(f'cfg(feature,values("{feature}"))')
|
||||||
|
|
||||||
for header in args.config_headers:
|
for header in args.config_headers:
|
||||||
for tok in generate_cfg_flags(header):
|
for tok in generate_cfg_flags(header, cargo_toml):
|
||||||
print(tok)
|
print(tok)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
|
|
||||||
|
static uint32_t bql_unlock_blocked;
|
||||||
|
|
||||||
bool bql_locked(void)
|
bool bql_locked(void)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -12,4 +14,17 @@ void bql_lock_impl(const char *file, int line)
|
||||||
|
|
||||||
void bql_unlock(void)
|
void bql_unlock(void)
|
||||||
{
|
{
|
||||||
|
assert(!bql_unlock_blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bql_block_unlock(bool increase)
|
||||||
|
{
|
||||||
|
uint32_t new_value;
|
||||||
|
|
||||||
|
assert(bql_locked());
|
||||||
|
|
||||||
|
/* check for overflow! */
|
||||||
|
new_value = bql_unlock_blocked + increase - !increase;
|
||||||
|
assert((new_value > bql_unlock_blocked) == increase);
|
||||||
|
bql_unlock_blocked = new_value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -514,6 +514,20 @@ bool qemu_in_vcpu_thread(void)
|
||||||
|
|
||||||
QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked)
|
QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked)
|
||||||
|
|
||||||
|
static uint32_t bql_unlock_blocked;
|
||||||
|
|
||||||
|
void bql_block_unlock(bool increase)
|
||||||
|
{
|
||||||
|
uint32_t new_value;
|
||||||
|
|
||||||
|
assert(bql_locked());
|
||||||
|
|
||||||
|
/* check for overflow! */
|
||||||
|
new_value = bql_unlock_blocked + increase - !increase;
|
||||||
|
assert((new_value > bql_unlock_blocked) == increase);
|
||||||
|
bql_unlock_blocked = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
bool bql_locked(void)
|
bool bql_locked(void)
|
||||||
{
|
{
|
||||||
return get_bql_locked();
|
return get_bql_locked();
|
||||||
|
@ -540,6 +554,7 @@ void bql_lock_impl(const char *file, int line)
|
||||||
void bql_unlock(void)
|
void bql_unlock(void)
|
||||||
{
|
{
|
||||||
g_assert(bql_locked());
|
g_assert(bql_locked());
|
||||||
|
g_assert(!bql_unlock_blocked);
|
||||||
set_bql_locked(false);
|
set_bql_locked(false);
|
||||||
qemu_mutex_unlock(&bql);
|
qemu_mutex_unlock(&bql);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2765,7 +2765,7 @@ void arm_cpu_register(const ARMCPUInfo *info)
|
||||||
};
|
};
|
||||||
|
|
||||||
type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name);
|
type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name);
|
||||||
type_register(&type_info);
|
type_register_static(&type_info);
|
||||||
g_free((void *)type_info.name);
|
g_free((void *)type_info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -841,7 +841,7 @@ void aarch64_cpu_register(const ARMCPUInfo *info)
|
||||||
};
|
};
|
||||||
|
|
||||||
type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name);
|
type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name);
|
||||||
type_register(&type_info);
|
type_register_static(&type_info);
|
||||||
g_free((void *)type_info.name);
|
g_free((void *)type_info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6429,7 +6429,7 @@ static void x86_register_cpu_model_type(const char *name, X86CPUModel *model)
|
||||||
.class_data = model,
|
.class_data = model,
|
||||||
};
|
};
|
||||||
|
|
||||||
type_register(&ti);
|
type_register_static(&ti);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,7 @@
|
||||||
|
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
|
|
||||||
#ifdef CONFIG_KVM
|
/* always false if !CONFIG_KVM */
|
||||||
|
|
||||||
#define kvm_pit_in_kernel() \
|
#define kvm_pit_in_kernel() \
|
||||||
(kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
|
(kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
|
||||||
#define kvm_pic_in_kernel() \
|
#define kvm_pic_in_kernel() \
|
||||||
|
@ -22,14 +21,6 @@
|
||||||
#define kvm_ioapic_in_kernel() \
|
#define kvm_ioapic_in_kernel() \
|
||||||
(kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
|
(kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define kvm_pit_in_kernel() 0
|
|
||||||
#define kvm_pic_in_kernel() 0
|
|
||||||
#define kvm_ioapic_in_kernel() 0
|
|
||||||
|
|
||||||
#endif /* CONFIG_KVM */
|
|
||||||
|
|
||||||
bool kvm_has_smm(void);
|
bool kvm_has_smm(void);
|
||||||
bool kvm_enable_x2apic(void);
|
bool kvm_enable_x2apic(void);
|
||||||
bool kvm_hv_vpindex_settable(void);
|
bool kvm_hv_vpindex_settable(void);
|
||||||
|
|
|
@ -626,7 +626,7 @@ static void mips_register_cpudef_type(const struct mips_def_t *def)
|
||||||
.class_data = (void *)def,
|
.class_data = (void *)def,
|
||||||
};
|
};
|
||||||
|
|
||||||
type_register(&ti);
|
type_register_static(&ti);
|
||||||
g_free(typename);
|
g_free(typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2633,7 +2633,7 @@ static int kvm_ppc_register_host_cpu_type(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc));
|
type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc));
|
||||||
type_register(&type_info);
|
type_register_static(&type_info);
|
||||||
/* override TCG default cpu type with 'host' cpu model */
|
/* override TCG default cpu type with 'host' cpu model */
|
||||||
object_class_foreach(pseries_machine_class_fixup, TYPE_SPAPR_MACHINE,
|
object_class_foreach(pseries_machine_class_fixup, TYPE_SPAPR_MACHINE,
|
||||||
false, NULL);
|
false, NULL);
|
||||||
|
|
|
@ -1014,7 +1014,7 @@ static void sparc_register_cpudef_type(const struct sparc_def_t *def)
|
||||||
.class_data = (void *)def,
|
.class_data = (void *)def,
|
||||||
};
|
};
|
||||||
|
|
||||||
type_register(&ti);
|
type_register_static(&ti);
|
||||||
g_free(typename);
|
g_free(typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ void xtensa_register_core(XtensaConfigList *node)
|
||||||
node->next = xtensa_cores;
|
node->next = xtensa_cores;
|
||||||
xtensa_cores = node;
|
xtensa_cores = node;
|
||||||
type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name);
|
type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name);
|
||||||
type_register(&type);
|
type_register_static(&type);
|
||||||
g_free((gpointer)type.name);
|
g_free((gpointer)type.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ ENV PYTHON "/usr/bin/python3"
|
||||||
RUN dnf install -y wget
|
RUN dnf install -y wget
|
||||||
ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo
|
ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo
|
||||||
ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc
|
ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc
|
||||||
|
ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo
|
||||||
RUN set -eux && \
|
RUN set -eux && \
|
||||||
rustArch='x86_64-unknown-linux-gnu' && \
|
rustArch='x86_64-unknown-linux-gnu' && \
|
||||||
rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \
|
rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \
|
||||||
|
@ -165,10 +166,13 @@ RUN set -eux && \
|
||||||
./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \
|
./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \
|
||||||
chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \
|
chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \
|
||||||
/usr/local/cargo/bin/rustup --version && \
|
/usr/local/cargo/bin/rustup --version && \
|
||||||
|
/usr/local/cargo/bin/rustup run nightly cargo --version && \
|
||||||
/usr/local/cargo/bin/rustup run nightly rustc --version && \
|
/usr/local/cargo/bin/rustup run nightly rustc --version && \
|
||||||
|
test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \
|
||||||
test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"
|
test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"
|
||||||
ENV PATH=$CARGO_HOME/bin:$PATH
|
ENV PATH=$CARGO_HOME/bin:$PATH
|
||||||
RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli
|
RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli
|
||||||
|
RUN $CARGO --list
|
||||||
# As a final step configure the user (if env is defined)
|
# As a final step configure the user (if env is defined)
|
||||||
ARG USER
|
ARG USER
|
||||||
ARG UID
|
ARG UID
|
||||||
|
|
|
@ -121,6 +121,7 @@ fedora_rustup_nightly_extras = [
|
||||||
"RUN dnf install -y wget\n",
|
"RUN dnf install -y wget\n",
|
||||||
"ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n",
|
"ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n",
|
||||||
"ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n",
|
"ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n",
|
||||||
|
"ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\n",
|
||||||
"RUN set -eux && \\\n",
|
"RUN set -eux && \\\n",
|
||||||
" rustArch='x86_64-unknown-linux-gnu' && \\\n",
|
" rustArch='x86_64-unknown-linux-gnu' && \\\n",
|
||||||
" rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \\\n",
|
" rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \\\n",
|
||||||
|
@ -131,10 +132,13 @@ fedora_rustup_nightly_extras = [
|
||||||
" ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \\\n",
|
" ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \\\n",
|
||||||
" chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \\\n",
|
" chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \\\n",
|
||||||
" /usr/local/cargo/bin/rustup --version && \\\n",
|
" /usr/local/cargo/bin/rustup --version && \\\n",
|
||||||
|
" /usr/local/cargo/bin/rustup run nightly cargo --version && \\\n",
|
||||||
" /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n",
|
" /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n",
|
||||||
|
' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n',
|
||||||
' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n',
|
' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n',
|
||||||
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
|
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
|
||||||
'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n',
|
'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n',
|
||||||
|
'RUN $CARGO --list\n',
|
||||||
]
|
]
|
||||||
|
|
||||||
ubuntu2204_bindgen_extras = [
|
ubuntu2204_bindgen_extras = [
|
||||||
|
|
|
@ -1073,6 +1073,6 @@ void qemu_console_early_init(void)
|
||||||
{
|
{
|
||||||
/* set the default vc driver */
|
/* set the default vc driver */
|
||||||
if (!object_class_by_name(TYPE_CHARDEV_VC)) {
|
if (!object_class_by_name(TYPE_CHARDEV_VC)) {
|
||||||
type_register(&char_vc_type_info);
|
type_register_static(&char_vc_type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,7 +476,7 @@ early_dbus_init(DisplayOptions *opts)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
type_register(&dbus_vc_type_info);
|
type_register_static(&dbus_vc_type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
2
ui/gtk.c
2
ui/gtk.c
|
@ -2540,7 +2540,7 @@ static void early_gtk_display_init(DisplayOptions *opts)
|
||||||
keycode_map = gd_get_keymap(&keycode_maplen);
|
keycode_map = gd_get_keymap(&keycode_maplen);
|
||||||
|
|
||||||
#if defined(CONFIG_VTE)
|
#if defined(CONFIG_VTE)
|
||||||
type_register(&char_gd_vc_type_info);
|
type_register_static(&char_gd_vc_type_info);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ static void spice_app_display_early_init(DisplayOptions *opts)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_register(&char_vc_type_info);
|
type_register_static(&char_vc_type_info);
|
||||||
|
|
||||||
sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
|
sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
|
||||||
qopts = qemu_opts_create(list, NULL, 0, &error_abort);
|
qopts = qemu_opts_create(list, NULL, 0, &error_abort);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue