mirror of
https://github.com/qemu/qemu.git
synced 2025-08-07 14:43:53 +00:00
* rust: cleanups
* rust: integration tests * rust/pl011: add support for migration * rust/pl011: add TYPE_PL011_LUMINARY device * rust: add support for older compilers and bindgen -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmcrrtIUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroPIBwf/W0Jo87UauGYufhEmoPvWG1EAQEqP EzNTzem9Iw92VdiSTkAtED0/TSd8RBJOwDfjjusVXZtuMPwpRNgXaFhYTT5gFTMj Nk3NZGaX/mbNrtdrukdx9mvUWeovytdZDZccTNkpc3oyiqY9NEz06wZ0tCNJEot6 qO3dEtKXTOQTdx2R3o0oS+2OFDGEEPxZ0PuXN3sClN4iZhGfcIDsjGAWxEh6mCDy VxqKPdax1Ig1w7M+JMclnpOsVHwcefjHiToNPwhCEGelJ9BZilkViuvBzsVRJJz3 ptYyywBE0FT8MiKQ/wyf7U64qoizJuIgHoQnUGj98hdgvbUUiW5jcBNY3A== =s591 -----END PGP SIGNATURE----- Merge tag 'for-upstream-rust' of https://gitlab.com/bonzini/qemu into staging * rust: cleanups * rust: integration tests * rust/pl011: add support for migration * rust/pl011: add TYPE_PL011_LUMINARY device * rust: add support for older compilers and bindgen # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmcrrtIUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroPIBwf/W0Jo87UauGYufhEmoPvWG1EAQEqP # EzNTzem9Iw92VdiSTkAtED0/TSd8RBJOwDfjjusVXZtuMPwpRNgXaFhYTT5gFTMj # Nk3NZGaX/mbNrtdrukdx9mvUWeovytdZDZccTNkpc3oyiqY9NEz06wZ0tCNJEot6 # qO3dEtKXTOQTdx2R3o0oS+2OFDGEEPxZ0PuXN3sClN4iZhGfcIDsjGAWxEh6mCDy # VxqKPdax1Ig1w7M+JMclnpOsVHwcefjHiToNPwhCEGelJ9BZilkViuvBzsVRJJz3 # ptYyywBE0FT8MiKQ/wyf7U64qoizJuIgHoQnUGj98hdgvbUUiW5jcBNY3A== # =s591 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 06 Nov 2024 18:00:50 GMT # 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-rust' of https://gitlab.com/bonzini/qemu: (39 commits) dockerfiles: install bindgen from cargo on Ubuntu 22.04 rust: make rustfmt optional rust: allow older version of bindgen rust: do not use --generate-cstr rust: allow version 1.63.0 of rustc rust: clean up detection of the language rust: do not use MaybeUninit::zeroed() rust: introduce alternative implementation of offset_of! rust: create a cargo workspace rust: synchronize dependencies between subprojects and Cargo.lock rust: silence unknown warnings for the sake of old compilers rust: introduce a c_str macro rust: use std::os::raw instead of core::ffi rust: fix cfgs of proc-macro2 for 1.63.0 rust: patch bilge-impl to allow compilation with 1.63.0 rust/pl011: Use correct masks for IBRD and FBRD rust/pl011: remove commented out C code rust/pl011: add TYPE_PL011_LUMINARY device rust/pl011: move CLK_NAME static to function scope rust/pl011: add support for migration ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
a1dacb6691
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -5,3 +5,5 @@
|
|||||||
*.rs diff=rust
|
*.rs diff=rust
|
||||||
*.rs.inc diff=rust
|
*.rs.inc diff=rust
|
||||||
Cargo.lock diff=toml merge=binary
|
Cargo.lock diff=toml merge=binary
|
||||||
|
|
||||||
|
*.patch -text -whitespace
|
||||||
|
@ -128,7 +128,7 @@ build-system-fedora-rust-nightly:
|
|||||||
job: amd64-fedora-rust-nightly-container
|
job: amd64-fedora-rust-nightly-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: fedora-rust-nightly
|
IMAGE: fedora-rust-nightly
|
||||||
CONFIGURE_ARGS: --disable-docs --enable-rust
|
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
|
||||||
TARGETS: aarch64-softmmu
|
TARGETS: aarch64-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build
|
MAKE_CHECK_ARGS: check-build
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
@ -107,6 +107,18 @@ Python build dependencies
|
|||||||
required, it may be necessary to fetch python modules from the Python
|
required, it may be necessary to fetch python modules from the Python
|
||||||
Package Index (PyPI) via ``pip``, in order to build QEMU.
|
Package Index (PyPI) via ``pip``, in order to build QEMU.
|
||||||
|
|
||||||
|
Rust build dependencies
|
||||||
|
QEMU is generally conservative in adding new Rust dependencies, and all
|
||||||
|
of them are included in the distributed tarballs. One exception is the
|
||||||
|
bindgen tool, which is too big to package and distribute. The minimum
|
||||||
|
supported version of bindgen is 0.60.x. For distributions that do not
|
||||||
|
include bindgen or have an older version, it is recommended to install
|
||||||
|
a newer version using ``cargo install bindgen-cli``.
|
||||||
|
|
||||||
|
Developers may want to use Cargo-based tools in the QEMU source tree;
|
||||||
|
this requires Cargo 1.74.0. Note that Cargo is not required in order
|
||||||
|
to build QEMU.
|
||||||
|
|
||||||
Optional build dependencies
|
Optional build dependencies
|
||||||
Build components whose absence does not affect the ability to build
|
Build components whose absence does not affect the ability to build
|
||||||
QEMU may not be available in distros, or may be too old for QEMU's
|
QEMU may not be available in distros, or may be too old for QEMU's
|
||||||
|
@ -749,7 +749,7 @@ const PropertyInfo qdev_prop_array = {
|
|||||||
|
|
||||||
/* --- public helpers --- */
|
/* --- public helpers --- */
|
||||||
|
|
||||||
static Property *qdev_prop_walk(Property *props, const char *name)
|
static const Property *qdev_prop_walk(const Property *props, const char *name)
|
||||||
{
|
{
|
||||||
if (!props) {
|
if (!props) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -763,10 +763,10 @@ static Property *qdev_prop_walk(Property *props, const char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property *qdev_prop_find(DeviceState *dev, const char *name)
|
static const Property *qdev_prop_find(DeviceState *dev, const char *name)
|
||||||
{
|
{
|
||||||
ObjectClass *class;
|
ObjectClass *class;
|
||||||
Property *prop;
|
const Property *prop;
|
||||||
|
|
||||||
/* device properties */
|
/* device properties */
|
||||||
class = object_get_class(OBJECT(dev));
|
class = object_get_class(OBJECT(dev));
|
||||||
@ -840,7 +840,7 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
|
|||||||
|
|
||||||
void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
|
void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
|
||||||
{
|
{
|
||||||
Property *prop;
|
const Property *prop;
|
||||||
|
|
||||||
prop = qdev_prop_find(dev, name);
|
prop = qdev_prop_find(dev, name);
|
||||||
object_property_set_str(OBJECT(dev), name,
|
object_property_set_str(OBJECT(dev), name,
|
||||||
@ -956,7 +956,7 @@ const PropertyInfo qdev_prop_size = {
|
|||||||
/* --- object link property --- */
|
/* --- object link property --- */
|
||||||
|
|
||||||
static ObjectProperty *create_link_property(ObjectClass *oc, const char *name,
|
static ObjectProperty *create_link_property(ObjectClass *oc, const char *name,
|
||||||
Property *prop)
|
const Property *prop)
|
||||||
{
|
{
|
||||||
return object_class_property_add_link(oc, name, prop->link_type,
|
return object_class_property_add_link(oc, name, prop->link_type,
|
||||||
prop->offset,
|
prop->offset,
|
||||||
@ -969,7 +969,7 @@ const PropertyInfo qdev_prop_link = {
|
|||||||
.create = create_link_property,
|
.create = create_link_property,
|
||||||
};
|
};
|
||||||
|
|
||||||
void qdev_property_add_static(DeviceState *dev, Property *prop)
|
void qdev_property_add_static(DeviceState *dev, const Property *prop)
|
||||||
{
|
{
|
||||||
Object *obj = OBJECT(dev);
|
Object *obj = OBJECT(dev);
|
||||||
ObjectProperty *op;
|
ObjectProperty *op;
|
||||||
@ -980,7 +980,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop)
|
|||||||
field_prop_getter(prop->info),
|
field_prop_getter(prop->info),
|
||||||
field_prop_setter(prop->info),
|
field_prop_setter(prop->info),
|
||||||
prop->info->release,
|
prop->info->release,
|
||||||
prop);
|
(Property *)prop);
|
||||||
|
|
||||||
object_property_set_description(obj, prop->name,
|
object_property_set_description(obj, prop->name,
|
||||||
prop->info->description);
|
prop->info->description);
|
||||||
@ -994,7 +994,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void qdev_class_add_property(DeviceClass *klass, const char *name,
|
static void qdev_class_add_property(DeviceClass *klass, const char *name,
|
||||||
Property *prop)
|
const Property *prop)
|
||||||
{
|
{
|
||||||
ObjectClass *oc = OBJECT_CLASS(klass);
|
ObjectClass *oc = OBJECT_CLASS(klass);
|
||||||
ObjectProperty *op;
|
ObjectProperty *op;
|
||||||
@ -1007,7 +1007,7 @@ static void qdev_class_add_property(DeviceClass *klass, const char *name,
|
|||||||
field_prop_getter(prop->info),
|
field_prop_getter(prop->info),
|
||||||
field_prop_setter(prop->info),
|
field_prop_setter(prop->info),
|
||||||
prop->info->release,
|
prop->info->release,
|
||||||
prop);
|
(Property *)prop);
|
||||||
}
|
}
|
||||||
if (prop->set_default) {
|
if (prop->set_default) {
|
||||||
prop->info->set_default_value(op, prop);
|
prop->info->set_default_value(op, prop);
|
||||||
@ -1046,7 +1046,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v,
|
|||||||
* Do not use this in new code! QOM Properties added through this interface
|
* Do not use this in new code! QOM Properties added through this interface
|
||||||
* will be given names in the "legacy" namespace.
|
* will be given names in the "legacy" namespace.
|
||||||
*/
|
*/
|
||||||
static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop)
|
static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop)
|
||||||
{
|
{
|
||||||
g_autofree char *name = NULL;
|
g_autofree char *name = NULL;
|
||||||
|
|
||||||
@ -1058,12 +1058,12 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop)
|
|||||||
name = g_strdup_printf("legacy-%s", prop->name);
|
name = g_strdup_printf("legacy-%s", prop->name);
|
||||||
object_class_property_add(OBJECT_CLASS(dc), name, "str",
|
object_class_property_add(OBJECT_CLASS(dc), name, "str",
|
||||||
prop->info->print ? qdev_get_legacy_property : prop->info->get,
|
prop->info->print ? qdev_get_legacy_property : prop->info->get,
|
||||||
NULL, NULL, prop);
|
NULL, NULL, (Property *)prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_class_set_props(DeviceClass *dc, Property *props)
|
void device_class_set_props(DeviceClass *dc, const Property *props)
|
||||||
{
|
{
|
||||||
Property *prop;
|
const Property *prop;
|
||||||
|
|
||||||
dc->props_ = props;
|
dc->props_ = props;
|
||||||
for (prop = props; prop && prop->name; prop++) {
|
for (prop = props; prop && prop->name; prop++) {
|
||||||
|
@ -136,7 +136,7 @@ struct DeviceClass {
|
|||||||
* ensures a compile-time error if someone attempts to assign
|
* ensures a compile-time error if someone attempts to assign
|
||||||
* dc->props directly.
|
* dc->props directly.
|
||||||
*/
|
*/
|
||||||
Property *props_;
|
const Property *props_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @user_creatable: Can user instantiate with -device / device_add?
|
* @user_creatable: Can user instantiate with -device / device_add?
|
||||||
@ -941,7 +941,7 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev);
|
|||||||
* you attempt to add an existing property defined by a parent class.
|
* you attempt to add an existing property defined by a parent class.
|
||||||
* To modify an inherited property you need to use????
|
* To modify an inherited property you need to use????
|
||||||
*/
|
*/
|
||||||
void device_class_set_props(DeviceClass *dc, Property *props);
|
void device_class_set_props(DeviceClass *dc, const Property *props);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_class_set_parent_realize() - set up for chaining realize fns
|
* device_class_set_parent_realize() - set up for chaining realize fns
|
||||||
|
@ -37,7 +37,7 @@ struct PropertyInfo {
|
|||||||
int (*print)(Object *obj, Property *prop, char *dest, size_t len);
|
int (*print)(Object *obj, Property *prop, char *dest, size_t len);
|
||||||
void (*set_default_value)(ObjectProperty *op, const Property *prop);
|
void (*set_default_value)(ObjectProperty *op, const Property *prop);
|
||||||
ObjectProperty *(*create)(ObjectClass *oc, const char *name,
|
ObjectProperty *(*create)(ObjectClass *oc, const char *name,
|
||||||
Property *prop);
|
const Property *prop);
|
||||||
ObjectPropertyAccessor *get;
|
ObjectPropertyAccessor *get;
|
||||||
ObjectPropertyAccessor *set;
|
ObjectPropertyAccessor *set;
|
||||||
ObjectPropertyRelease *release;
|
ObjectPropertyRelease *release;
|
||||||
@ -223,7 +223,7 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj,
|
|||||||
* On error, store error in @errp. Static properties access data in a struct.
|
* On error, store error in @errp. Static properties access data in a struct.
|
||||||
* The type of the QOM property is derived from prop->info.
|
* The type of the QOM property is derived from prop->info.
|
||||||
*/
|
*/
|
||||||
void qdev_property_add_static(DeviceState *dev, Property *prop);
|
void qdev_property_add_static(DeviceState *dev, const Property *prop);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qdev_alias_all_properties: Create aliases on source for all target properties
|
* qdev_alias_all_properties: Create aliases on source for all target properties
|
||||||
|
137
meson.build
137
meson.build
@ -15,6 +15,7 @@ meson.add_postconf_script(find_program('scripts/symlink-install-tree.py'))
|
|||||||
|
|
||||||
not_found = dependency('', required: false)
|
not_found = dependency('', required: false)
|
||||||
keyval = import('keyval')
|
keyval = import('keyval')
|
||||||
|
rust = import('rust')
|
||||||
ss = import('sourceset')
|
ss = import('sourceset')
|
||||||
fs = import('fs')
|
fs = import('fs')
|
||||||
|
|
||||||
@ -52,6 +53,17 @@ cpu = host_machine.cpu_family()
|
|||||||
|
|
||||||
target_dirs = config_host['TARGET_DIRS'].split()
|
target_dirs = config_host['TARGET_DIRS'].split()
|
||||||
|
|
||||||
|
# type of binaries to build
|
||||||
|
have_linux_user = false
|
||||||
|
have_bsd_user = false
|
||||||
|
have_system = false
|
||||||
|
foreach target : target_dirs
|
||||||
|
have_linux_user = have_linux_user or target.endswith('linux-user')
|
||||||
|
have_bsd_user = have_bsd_user or target.endswith('bsd-user')
|
||||||
|
have_system = have_system or target.endswith('-softmmu')
|
||||||
|
endforeach
|
||||||
|
have_user = have_linux_user or have_bsd_user
|
||||||
|
|
||||||
############
|
############
|
||||||
# Programs #
|
# Programs #
|
||||||
############
|
############
|
||||||
@ -70,21 +82,45 @@ if host_os == 'darwin' and \
|
|||||||
all_languages += ['objc']
|
all_languages += ['objc']
|
||||||
objc = meson.get_compiler('objc')
|
objc = meson.get_compiler('objc')
|
||||||
endif
|
endif
|
||||||
have_rust = false
|
|
||||||
if not get_option('rust').disabled() and add_languages('rust', required: get_option('rust'), native: false) \
|
have_rust = add_languages('rust', native: false,
|
||||||
and add_languages('rust', required: get_option('rust'), native: true)
|
required: get_option('rust').disable_auto_if(not have_system))
|
||||||
|
have_rust = have_rust and add_languages('rust', native: true,
|
||||||
|
required: get_option('rust').disable_auto_if(not have_system))
|
||||||
|
if have_rust
|
||||||
rustc = meson.get_compiler('rust')
|
rustc = meson.get_compiler('rust')
|
||||||
have_rust = true
|
if rustc.version().version_compare('<1.63.0')
|
||||||
if rustc.version().version_compare('<1.80.0')
|
|
||||||
if get_option('rust').enabled()
|
if get_option('rust').enabled()
|
||||||
error('rustc version ' + rustc.version() + ' is unsupported: Please upgrade to at least 1.80.0')
|
error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.63.0')
|
||||||
else
|
else
|
||||||
warning('rustc version ' + rustc.version() + ' is unsupported: Disabling Rust compilation. Please upgrade to at least 1.80.0 to use Rust.')
|
warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.')
|
||||||
|
message('Please upgrade to at least 1.63.0 to use Rust.')
|
||||||
have_rust = false
|
have_rust = false
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if have_rust
|
||||||
|
bindgen = find_program('bindgen', required: get_option('rust'))
|
||||||
|
if not bindgen.found() or bindgen.version().version_compare('<0.60.0')
|
||||||
|
if get_option('rust').enabled()
|
||||||
|
error('bindgen version ' + bindgen.version() + ' is unsupported. You can install a new version with "cargo install bindgen-cli"')
|
||||||
|
else
|
||||||
|
if bindgen.found()
|
||||||
|
warning('bindgen version ' + bindgen.version() + ' is unsupported, disabling Rust compilation.')
|
||||||
|
else
|
||||||
|
warning('bindgen not found, disabling Rust compilation.')
|
||||||
|
endif
|
||||||
|
message('To use Rust you can install a new version with "cargo install bindgen-cli"')
|
||||||
|
have_rust = false
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if have_rust
|
||||||
|
rustfmt = find_program('rustfmt', required: false)
|
||||||
|
endif
|
||||||
|
|
||||||
dtrace = not_found
|
dtrace = not_found
|
||||||
stap = not_found
|
stap = not_found
|
||||||
if 'dtrace' in get_option('trace_backends')
|
if 'dtrace' in get_option('trace_backends')
|
||||||
@ -185,17 +221,6 @@ have_vhost_net_vdpa = have_vhost_vdpa and get_option('vhost_net').allowed()
|
|||||||
have_vhost_net_kernel = have_vhost_kernel and get_option('vhost_net').allowed()
|
have_vhost_net_kernel = have_vhost_kernel and get_option('vhost_net').allowed()
|
||||||
have_vhost_net = have_vhost_net_kernel or have_vhost_net_user or have_vhost_net_vdpa
|
have_vhost_net = have_vhost_net_kernel or have_vhost_net_user or have_vhost_net_vdpa
|
||||||
|
|
||||||
# type of binaries to build
|
|
||||||
have_linux_user = false
|
|
||||||
have_bsd_user = false
|
|
||||||
have_system = false
|
|
||||||
foreach target : target_dirs
|
|
||||||
have_linux_user = have_linux_user or target.endswith('linux-user')
|
|
||||||
have_bsd_user = have_bsd_user or target.endswith('bsd-user')
|
|
||||||
have_system = have_system or target.endswith('-softmmu')
|
|
||||||
endforeach
|
|
||||||
have_user = have_linux_user or have_bsd_user
|
|
||||||
|
|
||||||
have_tools = get_option('tools') \
|
have_tools = get_option('tools') \
|
||||||
.disable_auto_if(not have_system) \
|
.disable_auto_if(not have_system) \
|
||||||
.allowed()
|
.allowed()
|
||||||
@ -3374,6 +3399,35 @@ endif
|
|||||||
|
|
||||||
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
|
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
|
||||||
|
|
||||||
|
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')
|
||||||
qapi_gen = find_program('scripts/qapi-gen.py')
|
qapi_gen = find_program('scripts/qapi-gen.py')
|
||||||
@ -3971,32 +4025,37 @@ common_all = static_library('common',
|
|||||||
implicit_include_directories: false,
|
implicit_include_directories: false,
|
||||||
dependencies: common_ss.all_dependencies())
|
dependencies: common_ss.all_dependencies())
|
||||||
|
|
||||||
if have_rust and have_system
|
if have_rust
|
||||||
rustc_args = run_command(
|
# We would like to use --generate-cstr, but it is only available
|
||||||
find_program('scripts/rust/rustc_args.py'),
|
# starting with bindgen 0.66.0. The oldest supported versions
|
||||||
'--config-headers', meson.project_build_root() / 'config-host.h',
|
# is 0.60.x (Debian 12 has 0.60.1) which introduces --allowlist-file.
|
||||||
capture : true,
|
|
||||||
check: true).stdout().strip().split()
|
|
||||||
rustc_args += ['-D', 'unsafe_op_in_unsafe_fn']
|
|
||||||
bindgen_args = [
|
bindgen_args = [
|
||||||
'--disable-header-comment',
|
'--disable-header-comment',
|
||||||
'--raw-line', '// @generated',
|
'--raw-line', '// @generated',
|
||||||
'--ctypes-prefix', 'core::ffi',
|
'--ctypes-prefix', 'std::os::raw',
|
||||||
'--formatter', 'rustfmt',
|
|
||||||
'--generate-block',
|
'--generate-block',
|
||||||
'--generate-cstr',
|
|
||||||
'--impl-debug',
|
'--impl-debug',
|
||||||
'--merge-extern-blocks',
|
|
||||||
'--no-doc-comments',
|
'--no-doc-comments',
|
||||||
'--use-core',
|
|
||||||
'--with-derive-default',
|
'--with-derive-default',
|
||||||
'--no-size_t-is-usize',
|
|
||||||
'--no-layout-tests',
|
'--no-layout-tests',
|
||||||
'--no-prepend-enum-name',
|
'--no-prepend-enum-name',
|
||||||
'--allowlist-file', meson.project_source_root() + '/include/.*',
|
'--allowlist-file', meson.project_source_root() + '/include/.*',
|
||||||
'--allowlist-file', meson.project_source_root() + '/.*',
|
'--allowlist-file', meson.project_source_root() + '/.*',
|
||||||
'--allowlist-file', meson.project_build_root() + '/.*'
|
'--allowlist-file', meson.project_build_root() + '/.*'
|
||||||
]
|
]
|
||||||
|
if not rustfmt.found()
|
||||||
|
if bindgen.version().version_compare('<0.65.0')
|
||||||
|
bindgen_args += ['--no-rustfmt-bindings']
|
||||||
|
else
|
||||||
|
bindgen_args += ['--formatter', 'none']
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if bindgen.version().version_compare('<0.61.0')
|
||||||
|
# default in 0.61+
|
||||||
|
bindgen_args += ['--size_t-is-usize']
|
||||||
|
else
|
||||||
|
bindgen_args += ['--merge-extern-blocks']
|
||||||
|
endif
|
||||||
c_enums = [
|
c_enums = [
|
||||||
'DeviceCategory',
|
'DeviceCategory',
|
||||||
'GpioPolarity',
|
'GpioPolarity',
|
||||||
@ -4027,12 +4086,12 @@ if have_rust and have_system
|
|||||||
# this case you must pass the path to `clang` and `libclang` to your build
|
# this case you must pass the path to `clang` and `libclang` to your build
|
||||||
# command invocation using the environment variables CLANG_PATH and
|
# command invocation using the environment variables CLANG_PATH and
|
||||||
# LIBCLANG_PATH
|
# LIBCLANG_PATH
|
||||||
bindings_rs = import('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.rs',
|
||||||
include_directories: include_directories('.', 'include'),
|
include_directories: include_directories('.', 'include'),
|
||||||
bindgen_version: ['>=0.69.4'],
|
bindgen_version: ['>=0.60.0'],
|
||||||
args: bindgen_args,
|
args: bindgen_args,
|
||||||
)
|
)
|
||||||
subdir('rust')
|
subdir('rust')
|
||||||
@ -4040,6 +4099,7 @@ endif
|
|||||||
|
|
||||||
|
|
||||||
feature_to_c = find_program('scripts/feature_to_c.py')
|
feature_to_c = find_program('scripts/feature_to_c.py')
|
||||||
|
rust_root_crate = find_program('scripts/rust/rust_root_crate.sh')
|
||||||
|
|
||||||
if host_os == 'darwin'
|
if host_os == 'darwin'
|
||||||
entitlement = find_program('scripts/entitlement.sh')
|
entitlement = find_program('scripts/entitlement.sh')
|
||||||
@ -4132,7 +4192,7 @@ foreach target : target_dirs
|
|||||||
arch_srcs += target_specific.sources()
|
arch_srcs += target_specific.sources()
|
||||||
arch_deps += target_specific.dependencies()
|
arch_deps += target_specific.dependencies()
|
||||||
|
|
||||||
if have_rust and have_system
|
if have_rust and target_type == 'system'
|
||||||
target_rust = rust_devices_ss.apply(config_target, strict: false)
|
target_rust = rust_devices_ss.apply(config_target, strict: false)
|
||||||
crates = []
|
crates = []
|
||||||
foreach dep : target_rust.dependencies()
|
foreach dep : target_rust.dependencies()
|
||||||
@ -4141,7 +4201,7 @@ foreach target : target_dirs
|
|||||||
if crates.length() > 0
|
if crates.length() > 0
|
||||||
rlib_rs = custom_target('rust_' + target.underscorify() + '.rs',
|
rlib_rs = custom_target('rust_' + target.underscorify() + '.rs',
|
||||||
output: 'rust_' + target.underscorify() + '.rs',
|
output: 'rust_' + target.underscorify() + '.rs',
|
||||||
command: [find_program('scripts/rust/rust_root_crate.sh')] + crates,
|
command: [rust_root_crate, crates],
|
||||||
capture: true,
|
capture: true,
|
||||||
build_by_default: true,
|
build_by_default: true,
|
||||||
build_always_stale: true)
|
build_always_stale: true)
|
||||||
@ -4149,7 +4209,6 @@ foreach target : target_dirs
|
|||||||
rlib_rs,
|
rlib_rs,
|
||||||
dependencies: target_rust.dependencies(),
|
dependencies: target_rust.dependencies(),
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
rust_args: rustc_args,
|
|
||||||
rust_abi: 'c')
|
rust_abi: 'c')
|
||||||
arch_deps += declare_dependency(link_whole: [rlib])
|
arch_deps += declare_dependency(link_whole: [rlib])
|
||||||
endif
|
endif
|
||||||
@ -4495,9 +4554,11 @@ else
|
|||||||
endif
|
endif
|
||||||
summary_info += {'Rust support': have_rust}
|
summary_info += {'Rust support': have_rust}
|
||||||
if have_rust
|
if have_rust
|
||||||
summary_info += {'rustc version': rustc.version()}
|
|
||||||
summary_info += {'rustc': ' '.join(rustc.cmd_array())}
|
|
||||||
summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']}
|
summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']}
|
||||||
|
summary_info += {'rustc': ' '.join(rustc.cmd_array())}
|
||||||
|
summary_info += {'rustc version': rustc.version()}
|
||||||
|
summary_info += {'bindgen': bindgen.full_path()}
|
||||||
|
summary_info += {'bindgen version': bindgen.version()}
|
||||||
endif
|
endif
|
||||||
option_cflags = (get_option('debug') ? ['-g'] : [])
|
option_cflags = (get_option('debug') ? ['-g'] : [])
|
||||||
if get_option('optimization') != 'plain'
|
if get_option('optimization') != 'plain'
|
||||||
|
@ -380,3 +380,5 @@ option('x86_version', type : 'combo', choices : ['0', '1', '2', '3', '4'], value
|
|||||||
|
|
||||||
option('rust', type: 'feature', value: 'disabled',
|
option('rust', type: 'feature', value: 'disabled',
|
||||||
description: 'Rust support')
|
description: 'Rust support')
|
||||||
|
option('strict_rust_lints', type: 'boolean', value: false,
|
||||||
|
description: 'Enable stricter set of Rust warnings')
|
||||||
|
4
rust/hw/char/pl011/Cargo.lock → rust/Cargo.lock
generated
4
rust/hw/char/pl011/Cargo.lock → rust/Cargo.lock
generated
@ -91,6 +91,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "qemu_api"
|
name = "qemu_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"qemu_api_macros",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "qemu_api_macros"
|
name = "qemu_api_macros"
|
7
rust/Cargo.toml
Normal file
7
rust/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"qemu-api-macros",
|
||||||
|
"qemu-api",
|
||||||
|
"hw/char/pl011",
|
||||||
|
]
|
@ -1,3 +1,2 @@
|
|||||||
config X_PL011_RUST
|
config X_PL011_RUST
|
||||||
bool
|
bool
|
||||||
default y if HAVE_RUST
|
|
||||||
|
@ -21,6 +21,3 @@ 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" }
|
||||||
|
|
||||||
# Do not include in any global workspace
|
|
||||||
[workspace]
|
|
||||||
|
@ -2,14 +2,17 @@
|
|||||||
// 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::{
|
use core::ptr::{addr_of, addr_of_mut, NonNull};
|
||||||
ffi::{c_int, c_uchar, c_uint, c_void, CStr},
|
use std::{
|
||||||
ptr::{addr_of, addr_of_mut, NonNull},
|
ffi::CStr,
|
||||||
|
os::raw::{c_int, c_uchar, c_uint, c_void},
|
||||||
};
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::{self, *},
|
bindings::{self, *},
|
||||||
|
c_str,
|
||||||
definitions::ObjectImpl,
|
definitions::ObjectImpl,
|
||||||
|
device_class::TYPE_SYS_BUS_DEVICE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -18,15 +21,42 @@
|
|||||||
RegisterOffset,
|
RegisterOffset,
|
||||||
};
|
};
|
||||||
|
|
||||||
static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
/// Integer Baud Rate Divider, `UARTIBRD`
|
||||||
|
const IBRD_MASK: u32 = 0xffff;
|
||||||
|
|
||||||
|
/// Fractional Baud Rate Divider, `UARTFBRD`
|
||||||
|
const FBRD_MASK: u32 = 0x3f;
|
||||||
|
|
||||||
const DATA_BREAK: u32 = 1 << 10;
|
const DATA_BREAK: u32 = 1 << 10;
|
||||||
|
|
||||||
/// QEMU sourced constant.
|
/// QEMU sourced constant.
|
||||||
pub const PL011_FIFO_DEPTH: usize = 16_usize;
|
pub const PL011_FIFO_DEPTH: usize = 16_usize;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum DeviceId {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Arm = 0,
|
||||||
|
Luminary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<hwaddr> for DeviceId {
|
||||||
|
type Output = c_uchar;
|
||||||
|
|
||||||
|
fn index(&self, idx: hwaddr) -> &Self::Output {
|
||||||
|
match self {
|
||||||
|
Self::Arm => &Self::PL011_ID_ARM[idx as usize],
|
||||||
|
Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceId {
|
||||||
|
const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
||||||
|
const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1];
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, qemu_api_macros::Object)]
|
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
|
||||||
/// PL011 Device Model in QEMU
|
/// PL011 Device Model in QEMU
|
||||||
pub struct PL011State {
|
pub struct PL011State {
|
||||||
pub parent_obj: SysBusDevice,
|
pub parent_obj: SysBusDevice,
|
||||||
@ -69,6 +99,8 @@ pub struct PL011State {
|
|||||||
pub clock: NonNull<Clock>,
|
pub clock: NonNull<Clock>,
|
||||||
#[doc(alias = "migrate_clk")]
|
#[doc(alias = "migrate_clk")]
|
||||||
pub migrate_clock: bool,
|
pub migrate_clock: bool,
|
||||||
|
/// The byte string that identifies the device.
|
||||||
|
device_id: DeviceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PL011State {
|
impl ObjectImpl for PL011State {
|
||||||
@ -88,17 +120,13 @@ pub struct PL011Class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl qemu_api::definitions::Class for PL011Class {
|
impl qemu_api::definitions::Class for PL011Class {
|
||||||
const CLASS_INIT: Option<
|
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void),
|
Some(crate::device_class::pl011_class_init);
|
||||||
> = Some(crate::device_class::pl011_class_init);
|
|
||||||
const CLASS_BASE_INIT: Option<
|
const CLASS_BASE_INIT: Option<
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void),
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
||||||
> = None;
|
> = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[used]
|
|
||||||
pub static CLK_NAME: &CStr = c"clk";
|
|
||||||
|
|
||||||
impl PL011State {
|
impl PL011State {
|
||||||
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
||||||
///
|
///
|
||||||
@ -108,7 +136,9 @@ impl PL011State {
|
|||||||
/// `PL011State` type. It must not be called more than once on the same
|
/// `PL011State` type. It must not be called more than once on the same
|
||||||
/// location/instance. All its fields are expected to hold unitialized
|
/// location/instance. All its fields are expected to hold unitialized
|
||||||
/// values with the sole exception of `parent_obj`.
|
/// values with the sole exception of `parent_obj`.
|
||||||
pub unsafe fn init(&mut self) {
|
unsafe fn init(&mut self) {
|
||||||
|
const CLK_NAME: &CStr = c_str!("clk");
|
||||||
|
|
||||||
let dev = addr_of_mut!(*self).cast::<DeviceState>();
|
let dev = addr_of_mut!(*self).cast::<DeviceState>();
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
//
|
//
|
||||||
@ -148,23 +178,18 @@ pub unsafe fn init(&mut self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(
|
pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> {
|
||||||
&mut self,
|
|
||||||
offset: hwaddr,
|
|
||||||
_size: core::ffi::c_uint,
|
|
||||||
) -> std::ops::ControlFlow<u64, u64> {
|
|
||||||
use RegisterOffset::*;
|
use RegisterOffset::*;
|
||||||
|
|
||||||
std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) {
|
std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) {
|
||||||
Err(v) if (0x3f8..0x400).contains(&v) => {
|
Err(v) if (0x3f8..0x400).contains(&v) => {
|
||||||
u64::from(PL011_ID_ARM[((offset - 0xfe0) >> 2) as usize])
|
u64::from(self.device_id[(offset - 0xfe0) >> 2])
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
Ok(DR) => {
|
Ok(DR) => {
|
||||||
// s->flags &= ~PL011_FLAG_RXFF;
|
|
||||||
self.flags.set_receive_fifo_full(false);
|
self.flags.set_receive_fifo_full(false);
|
||||||
let c = self.read_fifo[self.read_pos];
|
let c = self.read_fifo[self.read_pos];
|
||||||
if self.read_count > 0 {
|
if self.read_count > 0 {
|
||||||
@ -172,11 +197,9 @@ pub fn read(
|
|||||||
self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
|
self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
|
||||||
}
|
}
|
||||||
if self.read_count == 0 {
|
if self.read_count == 0 {
|
||||||
// self.flags |= PL011_FLAG_RXFE;
|
|
||||||
self.flags.set_receive_fifo_empty(true);
|
self.flags.set_receive_fifo_empty(true);
|
||||||
}
|
}
|
||||||
if self.read_count + 1 == self.read_trigger {
|
if self.read_count + 1 == self.read_trigger {
|
||||||
//self.int_level &= ~ INT_RX;
|
|
||||||
self.int_level &= !registers::INT_RX;
|
self.int_level &= !registers::INT_RX;
|
||||||
}
|
}
|
||||||
// Update error bits.
|
// Update error bits.
|
||||||
@ -346,13 +369,6 @@ fn loopback_mdmctrl(&mut self) {
|
|||||||
* dealt with here.
|
* dealt with here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD |
|
|
||||||
// PL011_FLAG_DSR | PL011_FLAG_CTS);
|
|
||||||
//fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0;
|
|
||||||
//fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0;
|
|
||||||
//fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0;
|
|
||||||
//fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0;
|
|
||||||
//
|
|
||||||
self.flags.set_ring_indicator(self.control.out_2());
|
self.flags.set_ring_indicator(self.control.out_2());
|
||||||
self.flags.set_data_carrier_detect(self.control.out_1());
|
self.flags.set_data_carrier_detect(self.control.out_1());
|
||||||
self.flags.set_clear_to_send(self.control.request_to_send());
|
self.flags.set_clear_to_send(self.control.request_to_send());
|
||||||
@ -363,10 +379,6 @@ fn loopback_mdmctrl(&mut self) {
|
|||||||
let mut il = self.int_level;
|
let mut il = self.int_level;
|
||||||
|
|
||||||
il &= !Interrupt::MS;
|
il &= !Interrupt::MS;
|
||||||
//il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0;
|
|
||||||
//il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0;
|
|
||||||
//il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0;
|
|
||||||
//il |= (fr & PL011_FLAG_RI) ? INT_RI : 0;
|
|
||||||
|
|
||||||
if self.flags.data_set_ready() {
|
if self.flags.data_set_ready() {
|
||||||
il |= Interrupt::DSR as u32;
|
il |= Interrupt::DSR as u32;
|
||||||
@ -472,10 +484,8 @@ pub fn put_fifo(&mut self, value: c_uint) {
|
|||||||
let slot = (self.read_pos + self.read_count) & (depth - 1);
|
let slot = (self.read_pos + self.read_count) & (depth - 1);
|
||||||
self.read_fifo[slot] = value;
|
self.read_fifo[slot] = value;
|
||||||
self.read_count += 1;
|
self.read_count += 1;
|
||||||
// s->flags &= ~PL011_FLAG_RXFE;
|
|
||||||
self.flags.set_receive_fifo_empty(false);
|
self.flags.set_receive_fifo_empty(false);
|
||||||
if self.read_count == depth {
|
if self.read_count == depth {
|
||||||
//s->flags |= PL011_FLAG_RXFF;
|
|
||||||
self.flags.set_receive_fifo_full(true);
|
self.flags.set_receive_fifo_full(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,6 +502,27 @@ pub fn update(&self) {
|
|||||||
unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) };
|
unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
|
||||||
|
/* Sanity-check input state */
|
||||||
|
if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
|
||||||
|
// Older versions of PL011 didn't ensure that the single
|
||||||
|
// character in the FIFO in FIFO-disabled mode is in
|
||||||
|
// element 0 of the array; convert to follow the current
|
||||||
|
// code's assumptions.
|
||||||
|
self.read_fifo[0] = self.read_fifo[self.read_pos];
|
||||||
|
self.read_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ibrd &= IBRD_MASK;
|
||||||
|
self.fbrd &= FBRD_MASK;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Which bits in the interrupt status matter for each outbound IRQ line ?
|
/// Which bits in the interrupt status matter for each outbound IRQ line ?
|
||||||
@ -514,7 +545,6 @@ pub fn update(&self) {
|
|||||||
/// 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 has
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
/// the same size as [`PL011State`]. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
|
pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
|
||||||
unsafe {
|
unsafe {
|
||||||
debug_assert!(!opaque.is_null());
|
debug_assert!(!opaque.is_null());
|
||||||
@ -530,12 +560,7 @@ pub fn update(&self) {
|
|||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
///
|
///
|
||||||
/// The buffer and size arguments must also be valid.
|
/// The buffer and size arguments must also be valid.
|
||||||
#[no_mangle]
|
pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) {
|
||||||
pub unsafe extern "C" fn pl011_receive(
|
|
||||||
opaque: *mut core::ffi::c_void,
|
|
||||||
buf: *const u8,
|
|
||||||
size: core::ffi::c_int,
|
|
||||||
) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
debug_assert!(!opaque.is_null());
|
debug_assert!(!opaque.is_null());
|
||||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||||
@ -554,8 +579,7 @@ pub fn update(&self) {
|
|||||||
/// 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 has
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
/// the same size as [`PL011State`]. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
#[no_mangle]
|
pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
|
||||||
pub unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
debug_assert!(!opaque.is_null());
|
debug_assert!(!opaque.is_null());
|
||||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||||
@ -576,7 +600,7 @@ pub fn update(&self) {
|
|||||||
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
|
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
|
||||||
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
|
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
|
||||||
|
|
||||||
qdev_prop_set_chr(dev, bindings::TYPE_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!(error_fatal) as *mut *mut Error);
|
||||||
sysbus_mmio_map(sysbus, 0, addr);
|
sysbus_mmio_map(sysbus, 0, addr);
|
||||||
sysbus_connect_irq(sysbus, 0, irq);
|
sysbus_connect_irq(sysbus, 0, irq);
|
||||||
@ -589,7 +613,6 @@ pub fn update(&self) {
|
|||||||
/// 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 has
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
/// the same size as [`PL011State`]. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
|
pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
|
||||||
unsafe {
|
unsafe {
|
||||||
debug_assert!(!obj.is_null());
|
debug_assert!(!obj.is_null());
|
||||||
@ -597,3 +620,50 @@ pub fn update(&self) {
|
|||||||
state.as_mut().init();
|
state.as_mut().init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, qemu_api_macros::Object)]
|
||||||
|
/// PL011 Luminary device model.
|
||||||
|
pub struct PL011Luminary {
|
||||||
|
parent_obj: PL011State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PL011LuminaryClass {
|
||||||
|
_inner: [u8; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||||
|
/// the same size as [`PL011Luminary`]. We also expect the device is
|
||||||
|
/// readable/writeable from one thread at any time.
|
||||||
|
pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) {
|
||||||
|
unsafe {
|
||||||
|
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 {
|
||||||
|
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
||||||
|
None;
|
||||||
|
const CLASS_BASE_INIT: Option<
|
||||||
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
||||||
|
> = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for PL011Luminary {
|
||||||
|
type Class = PL011LuminaryClass;
|
||||||
|
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
|
||||||
|
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
@ -3,33 +3,93 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
use std::os::raw::{c_int, c_void};
|
||||||
|
|
||||||
use qemu_api::{bindings::*, definitions::ObjectImpl};
|
use qemu_api::{
|
||||||
|
bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections,
|
||||||
|
vmstate_uint32, vmstate_uint32_array, vmstate_unused, zeroable::Zeroable,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::device::PL011State;
|
use crate::device::{PL011State, PL011_FIFO_DEPTH};
|
||||||
|
|
||||||
|
extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
|
||||||
|
unsafe {
|
||||||
|
debug_assert!(!opaque.is_null());
|
||||||
|
let state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||||
|
state.as_ref().migrate_clock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Migration subsection for [`PL011State`] clock.
|
||||||
|
pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
|
||||||
|
name: c_str!("pl011/clock").as_ptr(),
|
||||||
|
version_id: 1,
|
||||||
|
minimum_version_id: 1,
|
||||||
|
needed: Some(pl011_clock_needed),
|
||||||
|
fields: vmstate_fields! {
|
||||||
|
vmstate_clock!(clock, PL011State),
|
||||||
|
},
|
||||||
|
..Zeroable::ZERO
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
|
||||||
|
unsafe {
|
||||||
|
debug_assert!(!opaque.is_null());
|
||||||
|
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||||
|
let result = state.as_mut().post_load(version_id as u32);
|
||||||
|
if result.is_err() {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[used]
|
|
||||||
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
||||||
name: PL011State::TYPE_INFO.name,
|
name: c_str!("pl011").as_ptr(),
|
||||||
unmigratable: true,
|
version_id: 2,
|
||||||
..unsafe { ::core::mem::MaybeUninit::<VMStateDescription>::zeroed().assume_init() }
|
minimum_version_id: 2,
|
||||||
|
post_load: Some(pl011_post_load),
|
||||||
|
fields: vmstate_fields! {
|
||||||
|
vmstate_unused!(core::mem::size_of::<u32>()),
|
||||||
|
vmstate_uint32!(flags, PL011State),
|
||||||
|
vmstate_uint32!(line_control, PL011State),
|
||||||
|
vmstate_uint32!(receive_status_error_clear, PL011State),
|
||||||
|
vmstate_uint32!(control, PL011State),
|
||||||
|
vmstate_uint32!(dmacr, PL011State),
|
||||||
|
vmstate_uint32!(int_enabled, PL011State),
|
||||||
|
vmstate_uint32!(int_level, PL011State),
|
||||||
|
vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH),
|
||||||
|
vmstate_uint32!(ilpr, PL011State),
|
||||||
|
vmstate_uint32!(ibrd, PL011State),
|
||||||
|
vmstate_uint32!(fbrd, PL011State),
|
||||||
|
vmstate_uint32!(ifl, PL011State),
|
||||||
|
vmstate_int32!(read_pos, PL011State),
|
||||||
|
vmstate_int32!(read_count, PL011State),
|
||||||
|
vmstate_int32!(read_trigger, PL011State),
|
||||||
|
},
|
||||||
|
subsections: vmstate_subsections! {
|
||||||
|
VMSTATE_PL011_CLOCK
|
||||||
|
},
|
||||||
|
..Zeroable::ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
qemu_api::declare_properties! {
|
qemu_api::declare_properties! {
|
||||||
PL011_PROPERTIES,
|
PL011_PROPERTIES,
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c"chardev",
|
c_str!("chardev"),
|
||||||
PL011State,
|
PL011State,
|
||||||
char_backend,
|
char_backend,
|
||||||
unsafe { &qdev_prop_chr },
|
unsafe { &qdev_prop_chr },
|
||||||
CharBackend
|
CharBackend
|
||||||
),
|
),
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c"migrate-clk",
|
c_str!("migrate-clk"),
|
||||||
PL011State,
|
PL011State,
|
||||||
migrate_clock,
|
migrate_clock,
|
||||||
unsafe { &qdev_prop_bool },
|
unsafe { &qdev_prop_bool },
|
||||||
bool
|
bool,
|
||||||
|
default = true
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +106,6 @@
|
|||||||
/// 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 has
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
/// the same size as [`PL011State`]. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
|
pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert!(!dev.is_null());
|
assert!(!dev.is_null());
|
||||||
@ -60,7 +119,6 @@
|
|||||||
/// 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 has
|
||||||
/// the same size as [`PL011State`]. We also expect the device is
|
/// the same size as [`PL011State`]. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// readable/writeable from one thread at any time.
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
|
pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert!(!dev.is_null());
|
assert!(!dev.is_null());
|
||||||
|
@ -36,16 +36,20 @@
|
|||||||
clippy::cognitive_complexity,
|
clippy::cognitive_complexity,
|
||||||
clippy::missing_safety_doc,
|
clippy::missing_safety_doc,
|
||||||
)]
|
)]
|
||||||
|
#![allow(clippy::result_unit_err)]
|
||||||
|
|
||||||
extern crate bilge;
|
extern crate bilge;
|
||||||
extern crate bilge_impl;
|
extern crate bilge_impl;
|
||||||
extern crate qemu_api;
|
extern crate qemu_api;
|
||||||
|
|
||||||
|
use qemu_api::c_str;
|
||||||
|
|
||||||
pub mod device;
|
pub mod device;
|
||||||
pub mod device_class;
|
pub mod device_class;
|
||||||
pub mod memory_ops;
|
pub mod memory_ops;
|
||||||
|
|
||||||
pub const TYPE_PL011: &::core::ffi::CStr = c"pl011";
|
pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011");
|
||||||
|
pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary");
|
||||||
|
|
||||||
/// Offset of each register from the base memory address of the device.
|
/// Offset of each register from the base memory address of the device.
|
||||||
///
|
///
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
// 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::{mem::MaybeUninit, ptr::NonNull};
|
use core::ptr::NonNull;
|
||||||
|
use std::os::raw::{c_uint, c_void};
|
||||||
|
|
||||||
use qemu_api::bindings::*;
|
use qemu_api::{bindings::*, zeroable::Zeroable};
|
||||||
|
|
||||||
use crate::device::PL011State;
|
use crate::device::PL011State;
|
||||||
|
|
||||||
@ -14,20 +15,15 @@
|
|||||||
read_with_attrs: None,
|
read_with_attrs: None,
|
||||||
write_with_attrs: None,
|
write_with_attrs: None,
|
||||||
endianness: device_endian::DEVICE_NATIVE_ENDIAN,
|
endianness: device_endian::DEVICE_NATIVE_ENDIAN,
|
||||||
valid: unsafe { MaybeUninit::<MemoryRegionOps__bindgen_ty_1>::zeroed().assume_init() },
|
valid: Zeroable::ZERO,
|
||||||
impl_: MemoryRegionOps__bindgen_ty_2 {
|
impl_: MemoryRegionOps__bindgen_ty_2 {
|
||||||
min_access_size: 4,
|
min_access_size: 4,
|
||||||
max_access_size: 4,
|
max_access_size: 4,
|
||||||
..unsafe { MaybeUninit::<MemoryRegionOps__bindgen_ty_2>::zeroed().assume_init() }
|
..Zeroable::ZERO
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[no_mangle]
|
unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 {
|
||||||
unsafe extern "C" fn pl011_read(
|
|
||||||
opaque: *mut core::ffi::c_void,
|
|
||||||
addr: hwaddr,
|
|
||||||
size: core::ffi::c_uint,
|
|
||||||
) -> u64 {
|
|
||||||
assert!(!opaque.is_null());
|
assert!(!opaque.is_null());
|
||||||
let mut state = unsafe { NonNull::new_unchecked(opaque.cast::<PL011State>()) };
|
let mut state = unsafe { NonNull::new_unchecked(opaque.cast::<PL011State>()) };
|
||||||
let val = unsafe { state.as_mut().read(addr, size) };
|
let val = unsafe { state.as_mut().read(addr, size) };
|
||||||
@ -44,13 +40,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) {
|
||||||
unsafe extern "C" fn pl011_write(
|
|
||||||
opaque: *mut core::ffi::c_void,
|
|
||||||
addr: hwaddr,
|
|
||||||
data: u64,
|
|
||||||
_size: core::ffi::c_uint,
|
|
||||||
) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert!(!opaque.is_null());
|
assert!(!opaque.is_null());
|
||||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||||
|
47
rust/qemu-api-macros/Cargo.lock
generated
47
rust/qemu-api-macros/Cargo.lock
generated
@ -1,47 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.86"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qemu_api_macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.36"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.72"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|
@ -19,7 +19,4 @@ proc-macro = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = "2"
|
syn = { version = "2", features = ["extra-traits"] }
|
||||||
|
|
||||||
# Do not include in any global workspace
|
|
||||||
[workspace]
|
|
||||||
|
@ -2,7 +2,7 @@ quote_dep = dependency('quote-1-rs', native: true)
|
|||||||
syn_dep = dependency('syn-2-rs', native: true)
|
syn_dep = dependency('syn-2-rs', native: true)
|
||||||
proc_macro2_dep = dependency('proc-macro2-1-rs', native: true)
|
proc_macro2_dep = dependency('proc-macro2-1-rs', native: true)
|
||||||
|
|
||||||
_qemu_api_macros_rs = import('rust').proc_macro(
|
_qemu_api_macros_rs = rust.proc_macro(
|
||||||
'qemu_api_macros',
|
'qemu_api_macros',
|
||||||
files('src/lib.rs'),
|
files('src/lib.rs'),
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
|
@ -3,41 +3,92 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use proc_macro2::Span;
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
use quote::{quote, quote_spanned};
|
||||||
|
use syn::{
|
||||||
|
parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field,
|
||||||
|
Fields, Ident, Type, Visibility,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompileError(String, Span);
|
||||||
|
|
||||||
|
impl From<CompileError> for proc_macro2::TokenStream {
|
||||||
|
fn from(err: CompileError) -> Self {
|
||||||
|
let CompileError(msg, span) = err;
|
||||||
|
quote_spanned! { span => compile_error!(#msg); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> {
|
||||||
|
let expected = parse_quote! { #[repr(C)] };
|
||||||
|
|
||||||
|
if input.attrs.iter().any(|attr| attr == &expected) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(CompileError(
|
||||||
|
format!("#[repr(C)] required for {}", msg),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(Object)]
|
#[proc_macro_derive(Object)]
|
||||||
pub fn derive_object(input: TokenStream) -> TokenStream {
|
pub fn derive_object(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
let name = input.ident;
|
let name = input.ident;
|
||||||
let module_static = format_ident!("__{}_LOAD_MODULE", name);
|
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#[allow(non_upper_case_globals)]
|
::qemu_api::module_init! {
|
||||||
#[used]
|
MODULE_INIT_QOM => unsafe {
|
||||||
#[cfg_attr(target_os = "linux", link_section = ".ctors")]
|
::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO);
|
||||||
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
|
|
||||||
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
|
|
||||||
pub static #module_static: extern "C" fn() = {
|
|
||||||
extern "C" fn __register() {
|
|
||||||
unsafe {
|
|
||||||
::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
extern "C" fn __load() {
|
|
||||||
unsafe {
|
|
||||||
::qemu_api::bindings::register_module_init(
|
|
||||||
Some(__register),
|
|
||||||
::qemu_api::bindings::module_init_type::MODULE_INIT_QOM
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__load
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fields(input: &DeriveInput) -> Result<&Punctuated<Field, Comma>, CompileError> {
|
||||||
|
if let Data::Struct(s) = &input.data {
|
||||||
|
if let Fields::Named(fs) = &s.fields {
|
||||||
|
Ok(&fs.named)
|
||||||
|
} else {
|
||||||
|
Err(CompileError(
|
||||||
|
"Cannot generate offsets for unnamed fields.".to_string(),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(CompileError(
|
||||||
|
"Cannot generate offsets for union or enum.".to_string(),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip::macros(quote)]
|
||||||
|
fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, CompileError> {
|
||||||
|
is_c_repr(&input, "#[derive(offsets)]")?;
|
||||||
|
|
||||||
|
let name = &input.ident;
|
||||||
|
let fields = get_fields(&input)?;
|
||||||
|
let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
|
||||||
|
let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect();
|
||||||
|
let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
::qemu_api::with_offsets! {
|
||||||
|
struct #name {
|
||||||
|
#(#field_vis #field_names: #field_types,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(offsets)]
|
||||||
|
pub fn derive_offsets(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let expanded = derive_offsets_or_error(input).unwrap_or_else(Into::into);
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
7
rust/qemu-api/Cargo.lock
generated
7
rust/qemu-api/Cargo.lock
generated
@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qemu_api"
|
|
||||||
version = "0.1.0"
|
|
@ -14,13 +14,15 @@ keywords = []
|
|||||||
categories = []
|
categories = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
qemu_api_macros = { path = "../qemu-api-macros" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
version_check = "~0.9"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
allocator = []
|
allocator = []
|
||||||
|
|
||||||
# Do not include in any global workspace
|
|
||||||
[workspace]
|
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)'] }
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
|
||||||
|
'cfg(has_offset_of)'] }
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use version_check as rustc;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if !Path::new("src/bindings.rs").exists() {
|
if !Path::new("src/bindings.rs").exists() {
|
||||||
panic!(
|
panic!(
|
||||||
@ -11,4 +13,11 @@ fn main() {
|
|||||||
(`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson."
|
(`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for available rustc features
|
||||||
|
if rustc::is_min_version("1.77.0").unwrap_or(false) {
|
||||||
|
println!("cargo:rustc-cfg=has_offset_of");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,54 @@
|
|||||||
|
_qemu_api_cfg = ['--cfg', 'MESON']
|
||||||
|
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
||||||
|
if rustc.version().version_compare('>=1.77.0')
|
||||||
|
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
||||||
|
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/c_str.rs',
|
||||||
'src/definitions.rs',
|
'src/definitions.rs',
|
||||||
'src/device_class.rs',
|
'src/device_class.rs',
|
||||||
|
'src/offset_of.rs',
|
||||||
|
'src/vmstate.rs',
|
||||||
|
'src/zeroable.rs',
|
||||||
],
|
],
|
||||||
{'.' : bindings_rs},
|
{'.' : bindings_rs},
|
||||||
),
|
),
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
rust_args: rustc_args + [
|
rust_args: _qemu_api_cfg,
|
||||||
'--cfg', 'MESON',
|
|
||||||
# '--cfg', 'feature="allocator"',
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
qemu_api_macros,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rust.test('rust-qemu-api-tests', _qemu_api_rs,
|
||||||
|
suite: ['unit', 'rust'])
|
||||||
|
|
||||||
qemu_api = declare_dependency(
|
qemu_api = declare_dependency(
|
||||||
link_with: _qemu_api_rs,
|
link_with: _qemu_api_rs,
|
||||||
|
dependencies: qemu_api_macros,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Rust executables do not support objects, so add an intermediate step.
|
||||||
|
rust_qemu_api_objs = static_library(
|
||||||
|
'rust_qemu_api_objs',
|
||||||
|
objects: [libqom.extract_all_objects(recursive: false),
|
||||||
|
libhwcore.extract_all_objects(recursive: false)])
|
||||||
|
|
||||||
|
test('rust-qemu-api-integration',
|
||||||
|
executable(
|
||||||
|
'rust-qemu-api-integration',
|
||||||
|
'tests/tests.rs',
|
||||||
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
|
rust_args: ['--test'],
|
||||||
|
install: false,
|
||||||
|
dependencies: [qemu_api, qemu_api_macros],
|
||||||
|
link_whole: [rust_qemu_api_objs, libqemuutil]),
|
||||||
|
args: [
|
||||||
|
'--test',
|
||||||
|
'--format', 'pretty',
|
||||||
|
],
|
||||||
|
protocol: 'rust',
|
||||||
|
suite: ['unit', 'rust'])
|
||||||
|
53
rust/qemu-api/src/c_str.rs
Normal file
53
rust/qemu-api/src/c_str.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2024 Red Hat, Inc.
|
||||||
|
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
/// Given a string constant _without_ embedded or trailing NULs, return
|
||||||
|
/// a `CStr`.
|
||||||
|
///
|
||||||
|
/// Needed for compatibility with Rust <1.77.
|
||||||
|
macro_rules! c_str {
|
||||||
|
($str:expr) => {{
|
||||||
|
const STRING: &str = concat!($str, "\0");
|
||||||
|
const BYTES: &[u8] = STRING.as_bytes();
|
||||||
|
|
||||||
|
// "for" is not allowed in const context... oh well,
|
||||||
|
// everybody loves some lisp. This could be turned into
|
||||||
|
// a procedural macro if this is a problem; alternatively
|
||||||
|
// Rust 1.72 makes CStr::from_bytes_with_nul a const function.
|
||||||
|
const fn f(b: &[u8], i: usize) {
|
||||||
|
if i == b.len() - 1 {
|
||||||
|
} else if b[i] == 0 {
|
||||||
|
panic!("c_str argument contains NUL")
|
||||||
|
} else {
|
||||||
|
f(b, i + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(BYTES, 0);
|
||||||
|
|
||||||
|
// SAFETY: absence of NULs apart from the final byte was checked above
|
||||||
|
unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) }
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
use crate::c_str;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cstr_macro() {
|
||||||
|
let good = c_str!("🦀");
|
||||||
|
let good_bytes = b"\xf0\x9f\xa6\x80\0";
|
||||||
|
assert_eq!(good.to_bytes_with_nul(), good_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cstr_macro_const() {
|
||||||
|
const GOOD: &CStr = c_str!("🦀");
|
||||||
|
const GOOD_BYTES: &[u8] = b"\xf0\x9f\xa6\x80\0";
|
||||||
|
assert_eq!(GOOD.to_bytes_with_nul(), GOOD_BYTES);
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
//! Definitions required by QEMU when registering a device.
|
//! Definitions required by QEMU when registering a device.
|
||||||
|
|
||||||
use ::core::ffi::{c_void, CStr};
|
use std::{ffi::CStr, os::raw::c_void};
|
||||||
|
|
||||||
use crate::bindings::{Object, ObjectClass, TypeInfo};
|
use crate::bindings::{Object, ObjectClass, TypeInfo};
|
||||||
|
|
||||||
@ -29,46 +29,40 @@ pub trait Class {
|
|||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! module_init {
|
macro_rules! module_init {
|
||||||
($func:expr, $type:expr) => {
|
($type:ident => $body:block) => {
|
||||||
#[used]
|
const _: () = {
|
||||||
#[cfg_attr(target_os = "linux", link_section = ".ctors")]
|
#[used]
|
||||||
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
|
#[cfg_attr(
|
||||||
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
|
not(any(target_vendor = "apple", target_os = "windows")),
|
||||||
pub static LOAD_MODULE: extern "C" fn() = {
|
link_section = ".init_array"
|
||||||
extern "C" fn __load() {
|
)]
|
||||||
unsafe {
|
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
|
||||||
$crate::bindings::register_module_init(Some($func), $type);
|
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
|
||||||
}
|
pub static LOAD_MODULE: extern "C" fn() = {
|
||||||
}
|
extern "C" fn init_fn() {
|
||||||
|
|
||||||
__load
|
|
||||||
};
|
|
||||||
};
|
|
||||||
(qom: $func:ident => $body:block) => {
|
|
||||||
// NOTE: To have custom identifiers for the ctor func we need to either supply
|
|
||||||
// them directly as a macro argument or create them with a proc macro.
|
|
||||||
#[used]
|
|
||||||
#[cfg_attr(target_os = "linux", link_section = ".ctors")]
|
|
||||||
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
|
|
||||||
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
|
|
||||||
pub static LOAD_MODULE: extern "C" fn() = {
|
|
||||||
extern "C" fn __load() {
|
|
||||||
#[no_mangle]
|
|
||||||
unsafe extern "C" fn $func() {
|
|
||||||
$body
|
$body
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
extern "C" fn ctor_fn() {
|
||||||
$crate::bindings::register_module_init(
|
unsafe {
|
||||||
Some($func),
|
$crate::bindings::register_module_init(
|
||||||
$crate::bindings::module_init_type::MODULE_INIT_QOM,
|
Some(init_fn),
|
||||||
);
|
$crate::bindings::module_init_type::$type,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
__load
|
ctor_fn
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// shortcut because it's quite common that $body needs unsafe {}
|
||||||
|
($type:ident => unsafe $body:block) => {
|
||||||
|
$crate::module_init! {
|
||||||
|
$type => { unsafe { $body } }
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -81,13 +75,13 @@ macro_rules! type_info {
|
|||||||
} else {
|
} else {
|
||||||
::core::ptr::null_mut()
|
::core::ptr::null_mut()
|
||||||
},
|
},
|
||||||
instance_size: ::core::mem::size_of::<$t>() as $crate::bindings::size_t,
|
instance_size: ::core::mem::size_of::<$t>(),
|
||||||
instance_align: ::core::mem::align_of::<$t>() as $crate::bindings::size_t,
|
instance_align: ::core::mem::align_of::<$t>(),
|
||||||
instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
|
instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
|
||||||
instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
|
instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
|
||||||
instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
|
instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
|
||||||
abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
|
abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
|
||||||
class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>() as $crate::bindings::size_t,
|
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_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_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
|
||||||
class_data: ::core::ptr::null_mut(),
|
class_data: ::core::ptr::null_mut(),
|
||||||
|
@ -2,127 +2,73 @@
|
|||||||
// 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::sync::OnceLock;
|
use std::ffi::CStr;
|
||||||
|
|
||||||
use crate::bindings::Property;
|
use crate::bindings;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! device_class_init {
|
macro_rules! device_class_init {
|
||||||
($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
|
($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn $func(
|
pub unsafe extern "C" fn $func(
|
||||||
klass: *mut $crate::bindings::ObjectClass,
|
klass: *mut $crate::bindings::ObjectClass,
|
||||||
_: *mut ::core::ffi::c_void,
|
_: *mut ::std::os::raw::c_void,
|
||||||
) {
|
) {
|
||||||
let mut dc =
|
let mut dc =
|
||||||
::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
|
::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
|
||||||
dc.as_mut().realize = $realize_fn;
|
unsafe {
|
||||||
dc.as_mut().vmsd = &$vmsd;
|
dc.as_mut().realize = $realize_fn;
|
||||||
$crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
|
dc.as_mut().vmsd = &$vmsd;
|
||||||
$crate::bindings::device_class_set_props(dc.as_mut(), $props.as_mut_ptr());
|
$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:expr, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
|
($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
|
||||||
$crate::bindings::Property {
|
$crate::bindings::Property {
|
||||||
name: {
|
// use associated function syntax for type checking
|
||||||
#[used]
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
static _TEMP: &::core::ffi::CStr = $name;
|
|
||||||
_TEMP.as_ptr()
|
|
||||||
},
|
|
||||||
info: $prop,
|
info: $prop,
|
||||||
offset: ::core::mem::offset_of!($state, $field)
|
offset: $crate::offset_of!($state, $field) as isize,
|
||||||
.try_into()
|
|
||||||
.expect("Could not fit offset value to type"),
|
|
||||||
bitnr: 0,
|
|
||||||
bitmask: 0,
|
|
||||||
set_default: true,
|
set_default: true,
|
||||||
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval.into() },
|
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
|
||||||
arrayoffset: 0,
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
arrayinfo: ::core::ptr::null(),
|
|
||||||
arrayfieldsize: 0,
|
|
||||||
link_type: ::core::ptr::null(),
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr$(,)*) => {
|
($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => {
|
||||||
$crate::bindings::Property {
|
$crate::bindings::Property {
|
||||||
name: {
|
// use associated function syntax for type checking
|
||||||
#[used]
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
static _TEMP: &::core::ffi::CStr = $name;
|
|
||||||
_TEMP.as_ptr()
|
|
||||||
},
|
|
||||||
info: $prop,
|
info: $prop,
|
||||||
offset: ::core::mem::offset_of!($state, $field)
|
offset: $crate::offset_of!($state, $field) as isize,
|
||||||
.try_into()
|
|
||||||
.expect("Could not fit offset value to type"),
|
|
||||||
bitnr: 0,
|
|
||||||
bitmask: 0,
|
|
||||||
set_default: false,
|
set_default: false,
|
||||||
defval: $crate::bindings::Property__bindgen_ty_1 { i: 0 },
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
arrayoffset: 0,
|
|
||||||
arrayinfo: ::core::ptr::null(),
|
|
||||||
arrayfieldsize: 0,
|
|
||||||
link_type: ::core::ptr::null(),
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Properties<const N: usize>(pub OnceLock<[Property; N]>, pub fn() -> [Property; N]);
|
|
||||||
|
|
||||||
impl<const N: usize> Properties<N> {
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut Property {
|
|
||||||
_ = self.0.get_or_init(self.1);
|
|
||||||
self.0.get_mut().unwrap().as_mut_ptr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! declare_properties {
|
macro_rules! declare_properties {
|
||||||
($ident:ident, $($prop:expr),*$(,)*) => {
|
($ident:ident, $($prop:expr),*$(,)*) => {
|
||||||
|
pub static $ident: [$crate::bindings::Property; {
|
||||||
const fn _calc_prop_len() -> usize {
|
|
||||||
let mut len = 1;
|
let mut len = 1;
|
||||||
$({
|
$({
|
||||||
_ = stringify!($prop);
|
_ = stringify!($prop);
|
||||||
len += 1;
|
len += 1;
|
||||||
})*
|
})*
|
||||||
len
|
len
|
||||||
}
|
}] = [
|
||||||
const PROP_LEN: usize = _calc_prop_len();
|
$($prop),*,
|
||||||
|
$crate::zeroable::Zeroable::ZERO,
|
||||||
fn _make_properties() -> [$crate::bindings::Property; PROP_LEN] {
|
];
|
||||||
[
|
|
||||||
$($prop),*,
|
|
||||||
unsafe { ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init() },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub static mut $ident: $crate::device_class::Properties<PROP_LEN> = $crate::device_class::Properties(::std::sync::OnceLock::new(), _make_properties);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
// workaround until we can use --generate-cstr in bindgen.
|
||||||
macro_rules! vm_state_description {
|
pub const TYPE_DEVICE: &CStr =
|
||||||
($(#[$outer:meta])*
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
|
||||||
$name:ident,
|
pub const TYPE_SYS_BUS_DEVICE: &CStr =
|
||||||
$(name: $vname:expr,)*
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
|
||||||
$(unmigratable: $um_val:expr,)*
|
|
||||||
) => {
|
|
||||||
#[used]
|
|
||||||
$(#[$outer])*
|
|
||||||
pub static $name: $crate::bindings::VMStateDescription = $crate::bindings::VMStateDescription {
|
|
||||||
$(name: {
|
|
||||||
#[used]
|
|
||||||
static VMSTATE_NAME: &::core::ffi::CStr = $vname;
|
|
||||||
$vname.as_ptr()
|
|
||||||
},)*
|
|
||||||
unmigratable: true,
|
|
||||||
..unsafe { ::core::mem::MaybeUninit::<$crate::bindings::VMStateDescription>::zeroed().assume_init() }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -26,14 +26,20 @@ unsafe impl Send for bindings::Property {}
|
|||||||
unsafe impl Sync for bindings::Property {}
|
unsafe impl Sync for bindings::Property {}
|
||||||
unsafe impl Sync for bindings::TypeInfo {}
|
unsafe impl Sync for bindings::TypeInfo {}
|
||||||
unsafe impl Sync for bindings::VMStateDescription {}
|
unsafe impl Sync for bindings::VMStateDescription {}
|
||||||
|
unsafe impl Sync for bindings::VMStateField {}
|
||||||
|
unsafe impl Sync for bindings::VMStateInfo {}
|
||||||
|
|
||||||
|
pub mod c_str;
|
||||||
pub mod definitions;
|
pub mod definitions;
|
||||||
pub mod device_class;
|
pub mod device_class;
|
||||||
|
pub mod offset_of;
|
||||||
|
pub mod vmstate;
|
||||||
|
pub mod zeroable;
|
||||||
|
|
||||||
#[cfg(test)]
|
use std::{
|
||||||
mod tests;
|
alloc::{GlobalAlloc, Layout},
|
||||||
|
os::raw::c_void,
|
||||||
use std::alloc::{GlobalAlloc, Layout};
|
};
|
||||||
|
|
||||||
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -47,8 +53,8 @@ fn g_aligned_alloc0(
|
|||||||
|
|
||||||
#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
|
#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn qemu_memalign(alignment: usize, size: usize) -> *mut ::core::ffi::c_void;
|
fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void;
|
||||||
fn qemu_vfree(ptr: *mut ::core::ffi::c_void);
|
fn qemu_vfree(ptr: *mut c_void);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -113,7 +119,7 @@ fn default() -> Self {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
const _: [(); 8] = [(); ::core::mem::size_of::<*mut ::core::ffi::c_void>()];
|
const _: [(); 8] = [(); ::core::mem::size_of::<*mut c_void>()];
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for QemuAllocator {
|
unsafe impl GlobalAlloc for QemuAllocator {
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
@ -164,3 +170,6 @@ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_offset_of)]
|
||||||
|
pub use core::mem::offset_of;
|
||||||
|
161
rust/qemu-api/src/offset_of.rs
Normal file
161
rust/qemu-api/src/offset_of.rs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/// This macro provides the same functionality as `core::mem::offset_of`,
|
||||||
|
/// except that only one level of field access is supported. The declaration
|
||||||
|
/// of the struct must be wrapped with `with_offsets! { }`.
|
||||||
|
///
|
||||||
|
/// It is needed because `offset_of!` was only stabilized in Rust 1.77.
|
||||||
|
#[cfg(not(has_offset_of))]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! offset_of {
|
||||||
|
($Container:ty, $field:ident) => {
|
||||||
|
<$Container>::OFFSET_TO__.$field
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper for struct declarations, that allows using `offset_of!` in
|
||||||
|
/// versions of Rust prior to 1.77
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! with_offsets {
|
||||||
|
// This method to generate field offset constants comes from:
|
||||||
|
//
|
||||||
|
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df
|
||||||
|
//
|
||||||
|
// used under MIT license with permission of Yandros aka Daniel Henry-Mantilla
|
||||||
|
(
|
||||||
|
$(#[$struct_meta:meta])*
|
||||||
|
$struct_vis:vis
|
||||||
|
struct $StructName:ident {
|
||||||
|
$(
|
||||||
|
$(#[$field_meta:meta])*
|
||||||
|
$field_vis:vis
|
||||||
|
$field_name:ident : $field_ty:ty
|
||||||
|
),*
|
||||||
|
$(,)?
|
||||||
|
}
|
||||||
|
) => (
|
||||||
|
#[cfg(not(has_offset_of))]
|
||||||
|
const _: () = {
|
||||||
|
struct StructOffsetsHelper<T>(std::marker::PhantomData<T>);
|
||||||
|
const END_OF_PREV_FIELD: usize = 0;
|
||||||
|
|
||||||
|
// populate StructOffsetsHelper<T> with associated consts,
|
||||||
|
// one for each field
|
||||||
|
$crate::with_offsets! {
|
||||||
|
@struct $StructName
|
||||||
|
@names [ $($field_name)* ]
|
||||||
|
@tys [ $($field_ty ,)*]
|
||||||
|
}
|
||||||
|
|
||||||
|
// now turn StructOffsetsHelper<T>'s consts into a single struct,
|
||||||
|
// applying field visibility. This provides better error messages
|
||||||
|
// than if offset_of! used StructOffsetsHelper::<T> directly.
|
||||||
|
pub
|
||||||
|
struct StructOffsets {
|
||||||
|
$(
|
||||||
|
$field_vis
|
||||||
|
$field_name: usize,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
impl $StructName {
|
||||||
|
pub
|
||||||
|
const OFFSET_TO__: StructOffsets = StructOffsets {
|
||||||
|
$(
|
||||||
|
$field_name: StructOffsetsHelper::<$StructName>::$field_name,
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
@struct $StructName:ident
|
||||||
|
@names []
|
||||||
|
@tys []
|
||||||
|
) => ();
|
||||||
|
|
||||||
|
(
|
||||||
|
@struct $StructName:ident
|
||||||
|
@names [$field_name:ident $($other_names:tt)*]
|
||||||
|
@tys [$field_ty:ty , $($other_tys:tt)*]
|
||||||
|
) => (
|
||||||
|
#[allow(non_local_definitions)]
|
||||||
|
#[allow(clippy::modulo_one)]
|
||||||
|
impl StructOffsetsHelper<$StructName> {
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
const $field_name: usize = {
|
||||||
|
const ALIGN: usize = std::mem::align_of::<$field_ty>();
|
||||||
|
const TRAIL: usize = END_OF_PREV_FIELD % ALIGN;
|
||||||
|
END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const _: () = {
|
||||||
|
const END_OF_PREV_FIELD: usize =
|
||||||
|
StructOffsetsHelper::<$StructName>::$field_name +
|
||||||
|
std::mem::size_of::<$field_ty>()
|
||||||
|
;
|
||||||
|
$crate::with_offsets! {
|
||||||
|
@struct $StructName
|
||||||
|
@names [$($other_names)*]
|
||||||
|
@tys [$($other_tys)*]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::offset_of;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Foo {
|
||||||
|
a: u16,
|
||||||
|
b: u32,
|
||||||
|
c: u64,
|
||||||
|
d: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Bar {
|
||||||
|
pub a: u16,
|
||||||
|
pub b: u64,
|
||||||
|
c: Foo,
|
||||||
|
d: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::with_offsets! {
|
||||||
|
#[repr(C)]
|
||||||
|
struct Bar {
|
||||||
|
pub a: u16,
|
||||||
|
pub b: u64,
|
||||||
|
c: Foo,
|
||||||
|
d: u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Baz {
|
||||||
|
b: u32,
|
||||||
|
a: u8,
|
||||||
|
}
|
||||||
|
crate::with_offsets! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Baz {
|
||||||
|
b: u32,
|
||||||
|
a: u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_offset_of() {
|
||||||
|
const OFFSET_TO_C: usize = offset_of!(Bar, c);
|
||||||
|
|
||||||
|
assert_eq!(offset_of!(Bar, a), 0);
|
||||||
|
assert_eq!(offset_of!(Bar, b), 8);
|
||||||
|
assert_eq!(OFFSET_TO_C, 16);
|
||||||
|
assert_eq!(offset_of!(Bar, d), 40);
|
||||||
|
|
||||||
|
assert_eq!(offset_of!(Baz, b), 0);
|
||||||
|
assert_eq!(offset_of!(Baz, a), 4);
|
||||||
|
}
|
||||||
|
}
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright 2024, Linaro Limited
|
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
bindings::*, declare_properties, define_property, device_class_init, vm_state_description,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_device_decl_macros() {
|
|
||||||
// Test that macros can compile.
|
|
||||||
vm_state_description! {
|
|
||||||
VMSTATE,
|
|
||||||
name: c"name",
|
|
||||||
unmigratable: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct DummyState {
|
|
||||||
pub char_backend: CharBackend,
|
|
||||||
pub migrate_clock: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_properties! {
|
|
||||||
DUMMY_PROPERTIES,
|
|
||||||
define_property!(
|
|
||||||
c"chardev",
|
|
||||||
DummyState,
|
|
||||||
char_backend,
|
|
||||||
unsafe { &qdev_prop_chr },
|
|
||||||
CharBackend
|
|
||||||
),
|
|
||||||
define_property!(
|
|
||||||
c"migrate-clk",
|
|
||||||
DummyState,
|
|
||||||
migrate_clock,
|
|
||||||
unsafe { &qdev_prop_bool },
|
|
||||||
bool
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
device_class_init! {
|
|
||||||
dummy_class_init,
|
|
||||||
props => DUMMY_PROPERTIES,
|
|
||||||
realize_fn => None,
|
|
||||||
reset_fn => None,
|
|
||||||
vmsd => VMSTATE,
|
|
||||||
}
|
|
||||||
}
|
|
360
rust/qemu-api/src/vmstate.rs
Normal file
360
rust/qemu-api/src/vmstate.rs
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
// Copyright 2024, Linaro Limited
|
||||||
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
//! Helper macros to declare migration state for device models.
|
||||||
|
//!
|
||||||
|
//! Some macros are direct equivalents to the C macros declared in
|
||||||
|
//! `include/migration/vmstate.h` while
|
||||||
|
//! [`vmstate_subsections`](crate::vmstate_subsections) and
|
||||||
|
//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when
|
||||||
|
//! declaring a device model state struct.
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UNUSED_BUFFER")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_unused_buffer {
|
||||||
|
($field_exists_fn:expr, $version_id:expr, $size:expr) => {{
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: c_str!("unused").as_ptr(),
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
offset: 0,
|
||||||
|
size: $size,
|
||||||
|
start: 0,
|
||||||
|
num: 0,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
|
||||||
|
flags: VMStateFlags::VMS_BUFFER,
|
||||||
|
vmsd: ::core::ptr::null(),
|
||||||
|
version_id: $version_id,
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: $field_exists_fn,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UNUSED_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_unused_v {
|
||||||
|
($version_id:expr, $size:expr) => {{
|
||||||
|
$crate::vmstate_unused_buffer!(None, $version_id, $size)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UNUSED")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_unused {
|
||||||
|
($size:expr) => {{
|
||||||
|
$crate::vmstate_unused_v!(0, $size)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_SINGLE_TEST")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_single_test {
|
||||||
|
($field_name:ident, $struct_name:ty, $field_exists_fn:expr, $version_id:expr, $info:expr, $size:expr) => {{
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: ::core::concat!(::core::stringify!($field_name), 0)
|
||||||
|
.as_bytes()
|
||||||
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
offset: $crate::offset_of!($struct_name, $field_name),
|
||||||
|
size: $size,
|
||||||
|
start: 0,
|
||||||
|
num: 0,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
info: unsafe { $info },
|
||||||
|
flags: VMStateFlags::VMS_SINGLE,
|
||||||
|
vmsd: ::core::ptr::null(),
|
||||||
|
version_id: $version_id,
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: $field_exists_fn,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_SINGLE")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_single {
|
||||||
|
($field_name:ident, $struct_name:ty, $version_id:expr, $info:expr, $size:expr) => {{
|
||||||
|
$crate::vmstate_single_test!($field_name, $struct_name, None, $version_id, $info, $size)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UINT32_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_uint32_v {
|
||||||
|
($field_name:ident, $struct_name:ty, $version_id:expr) => {{
|
||||||
|
$crate::vmstate_single!(
|
||||||
|
$field_name,
|
||||||
|
$struct_name,
|
||||||
|
$version_id,
|
||||||
|
::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32),
|
||||||
|
::core::mem::size_of::<u32>()
|
||||||
|
)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UINT32")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_uint32 {
|
||||||
|
($field_name:ident, $struct_name:ty) => {{
|
||||||
|
$crate::vmstate_uint32_v!($field_name, $struct_name, 0)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_INT32_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_int32_v {
|
||||||
|
($field_name:ident, $struct_name:ty, $version_id:expr) => {{
|
||||||
|
$crate::vmstate_single!(
|
||||||
|
$field_name,
|
||||||
|
$struct_name,
|
||||||
|
$version_id,
|
||||||
|
::core::ptr::addr_of!($crate::bindings::vmstate_info_int32),
|
||||||
|
::core::mem::size_of::<i32>()
|
||||||
|
)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_INT32")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_int32 {
|
||||||
|
($field_name:ident, $struct_name:ty) => {{
|
||||||
|
$crate::vmstate_int32_v!($field_name, $struct_name, 0)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_ARRAY")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_array {
|
||||||
|
($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr, $info:expr, $size:expr) => {{
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: ::core::concat!(::core::stringify!($field_name), 0)
|
||||||
|
.as_bytes()
|
||||||
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
offset: $crate::offset_of!($struct_name, $field_name),
|
||||||
|
size: $size,
|
||||||
|
start: 0,
|
||||||
|
num: $length as _,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
info: unsafe { $info },
|
||||||
|
flags: VMStateFlags::VMS_ARRAY,
|
||||||
|
vmsd: ::core::ptr::null(),
|
||||||
|
version_id: $version_id,
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: None,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UINT32_ARRAY_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_uint32_array_v {
|
||||||
|
($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr) => {{
|
||||||
|
$crate::vmstate_array!(
|
||||||
|
$field_name,
|
||||||
|
$struct_name,
|
||||||
|
$length,
|
||||||
|
$version_id,
|
||||||
|
::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32),
|
||||||
|
::core::mem::size_of::<u32>()
|
||||||
|
)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_UINT32_ARRAY")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_uint32_array {
|
||||||
|
($field_name:ident, $struct_name:ty, $length:expr) => {{
|
||||||
|
$crate::vmstate_uint32_array_v!($field_name, $struct_name, $length, 0)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_STRUCT_POINTER_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_struct_pointer_v {
|
||||||
|
($field_name:ident, $struct_name:ty, $version_id:expr, $vmsd:expr, $type:ty) => {{
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: ::core::concat!(::core::stringify!($field_name), 0)
|
||||||
|
.as_bytes()
|
||||||
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
offset: $crate::offset_of!($struct_name, $field_name),
|
||||||
|
size: ::core::mem::size_of::<*const $type>(),
|
||||||
|
start: 0,
|
||||||
|
num: 0,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
info: ::core::ptr::null(),
|
||||||
|
flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0),
|
||||||
|
vmsd: unsafe { $vmsd },
|
||||||
|
version_id: $version_id,
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: None,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_ARRAY_OF_POINTER")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_array_of_pointer {
|
||||||
|
($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $info:expr, $type:ty) => {{
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: ::core::concat!(::core::stringify!($field_name), 0)
|
||||||
|
.as_bytes()
|
||||||
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
|
version_id: $version_id,
|
||||||
|
num: $num as _,
|
||||||
|
info: unsafe { $info },
|
||||||
|
size: ::core::mem::size_of::<*const $type>(),
|
||||||
|
flags: VMStateFlags(VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0),
|
||||||
|
offset: $crate::offset_of!($struct_name, $field_name),
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
start: 0,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
vmsd: ::core::ptr::null(),
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: None,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_ARRAY_OF_POINTER_TO_STRUCT")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_array_of_pointer_to_struct {
|
||||||
|
($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $vmsd:expr, $type:ty) => {{
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: ::core::concat!(::core::stringify!($field_name), 0)
|
||||||
|
.as_bytes()
|
||||||
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
|
version_id: $version_id,
|
||||||
|
num: $num as _,
|
||||||
|
vmsd: unsafe { $vmsd },
|
||||||
|
size: ::core::mem::size_of::<*const $type>(),
|
||||||
|
flags: VMStateFlags(
|
||||||
|
VMStateFlags::VMS_ARRAY.0
|
||||||
|
| VMStateFlags::VMS_STRUCT.0
|
||||||
|
| VMStateFlags::VMS_ARRAY_OF_POINTER.0,
|
||||||
|
),
|
||||||
|
offset: $crate::offset_of!($struct_name, $field_name),
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
start: 0,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
vmsd: ::core::ptr::null(),
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: None,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_CLOCK_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_clock_v {
|
||||||
|
($field_name:ident, $struct_name:ty, $version_id:expr) => {{
|
||||||
|
$crate::vmstate_struct_pointer_v!(
|
||||||
|
$field_name,
|
||||||
|
$struct_name,
|
||||||
|
$version_id,
|
||||||
|
::core::ptr::addr_of!($crate::bindings::vmstate_clock),
|
||||||
|
$crate::bindings::Clock
|
||||||
|
)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_CLOCK")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_clock {
|
||||||
|
($field_name:ident, $struct_name:ty) => {{
|
||||||
|
$crate::vmstate_clock_v!($field_name, $struct_name, 0)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_ARRAY_CLOCK_V")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_array_clock_v {
|
||||||
|
($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr) => {{
|
||||||
|
$crate::vmstate_array_of_pointer_to_struct!(
|
||||||
|
$field_name,
|
||||||
|
$struct_name,
|
||||||
|
$num,
|
||||||
|
$version_id,
|
||||||
|
::core::ptr::addr_of!($crate::bindings::vmstate_clock),
|
||||||
|
$crate::bindings::Clock
|
||||||
|
)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "VMSTATE_ARRAY_CLOCK")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_array_clock {
|
||||||
|
($field_name:ident, $struct_name:ty, $num:expr) => {{
|
||||||
|
$crate::vmstate_array_clock_v!($field_name, $struct_name, $name, 0)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper macro to declare a list of
|
||||||
|
/// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return
|
||||||
|
/// a pointer to the array of values it created.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_fields {
|
||||||
|
($($field:expr),*$(,)*) => {{
|
||||||
|
static _FIELDS: &[$crate::bindings::VMStateField] = &[
|
||||||
|
$($field),*,
|
||||||
|
$crate::bindings::VMStateField {
|
||||||
|
name: ::core::ptr::null(),
|
||||||
|
err_hint: ::core::ptr::null(),
|
||||||
|
offset: 0,
|
||||||
|
size: 0,
|
||||||
|
start: 0,
|
||||||
|
num: 0,
|
||||||
|
num_offset: 0,
|
||||||
|
size_offset: 0,
|
||||||
|
info: ::core::ptr::null(),
|
||||||
|
flags: VMStateFlags::VMS_END,
|
||||||
|
vmsd: ::core::ptr::null(),
|
||||||
|
version_id: 0,
|
||||||
|
struct_version_id: 0,
|
||||||
|
field_exists: None,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
_FIELDS.as_ptr()
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A transparent wrapper type for the `subsections` field of
|
||||||
|
/// [`VMStateDescription`](crate::bindings::VMStateDescription).
|
||||||
|
///
|
||||||
|
/// This is necessary to be able to declare subsection descriptions as statics,
|
||||||
|
/// because the only way to implement `Sync` for a foreign type (and `*const`
|
||||||
|
/// pointers are foreign types in Rust) is to create a wrapper struct and
|
||||||
|
/// `unsafe impl Sync` for it.
|
||||||
|
///
|
||||||
|
/// This struct is used in the
|
||||||
|
/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]);
|
||||||
|
|
||||||
|
unsafe impl Sync for VMStateSubsectionsWrapper {}
|
||||||
|
|
||||||
|
/// Helper macro to declare a list of subsections
|
||||||
|
/// ([`VMStateDescription`](`crate::bindings::VMStateDescription`)) into a
|
||||||
|
/// static and return a pointer to the array of pointers it created.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vmstate_subsections {
|
||||||
|
($($subsection:expr),*$(,)*) => {{
|
||||||
|
static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[
|
||||||
|
$({
|
||||||
|
static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection;
|
||||||
|
::core::ptr::addr_of!(_SUBSECTION)
|
||||||
|
}),*,
|
||||||
|
::core::ptr::null()
|
||||||
|
]);
|
||||||
|
_SUBSECTIONS.0.as_ptr()
|
||||||
|
}}
|
||||||
|
}
|
86
rust/qemu-api/src/zeroable.rs
Normal file
86
rust/qemu-api/src/zeroable.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
/// Encapsulates the requirement that
|
||||||
|
/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
|
||||||
|
/// behavior. This trait in principle could be implemented as just:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// const ZERO: Self = unsafe {
|
||||||
|
/// ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init()
|
||||||
|
/// },
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The need for a manual implementation is only because `zeroed()` cannot
|
||||||
|
/// be used as a `const fn` prior to Rust 1.75.0. Once we can assume a new
|
||||||
|
/// enough version of the compiler, we could provide a `#[derive(Zeroable)]`
|
||||||
|
/// macro to check at compile-time that all struct fields are Zeroable, and
|
||||||
|
/// use the above blanket implementation of the `ZERO` constant.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Because the implementation of `ZERO` is manual, it does not make
|
||||||
|
/// any assumption on the safety of `zeroed()`. However, other users of the
|
||||||
|
/// trait could use it that way. Do not add this trait to a type unless
|
||||||
|
/// all-zeroes is a valid value for the type. In particular, remember that
|
||||||
|
/// raw pointers can be zero, but references and `NonNull<T>` cannot
|
||||||
|
pub unsafe trait Zeroable: Default {
|
||||||
|
const ZERO: Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {
|
||||||
|
const ZERO: Self = Self { i: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Zeroable for crate::bindings::Property {
|
||||||
|
const ZERO: Self = Self {
|
||||||
|
name: ptr::null(),
|
||||||
|
info: ptr::null(),
|
||||||
|
offset: 0,
|
||||||
|
bitnr: 0,
|
||||||
|
bitmask: 0,
|
||||||
|
set_default: false,
|
||||||
|
defval: Zeroable::ZERO,
|
||||||
|
arrayoffset: 0,
|
||||||
|
arrayinfo: ptr::null(),
|
||||||
|
arrayfieldsize: 0,
|
||||||
|
link_type: ptr::null(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Zeroable for crate::bindings::VMStateDescription {
|
||||||
|
const ZERO: Self = Self {
|
||||||
|
name: ptr::null(),
|
||||||
|
unmigratable: false,
|
||||||
|
early_setup: false,
|
||||||
|
version_id: 0,
|
||||||
|
minimum_version_id: 0,
|
||||||
|
priority: crate::bindings::MigrationPriority::MIG_PRI_DEFAULT,
|
||||||
|
pre_load: None,
|
||||||
|
post_load: None,
|
||||||
|
pre_save: None,
|
||||||
|
post_save: None,
|
||||||
|
needed: None,
|
||||||
|
dev_unplug_pending: None,
|
||||||
|
fields: ptr::null(),
|
||||||
|
subsections: ptr::null(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {
|
||||||
|
const ZERO: Self = Self {
|
||||||
|
min_access_size: 0,
|
||||||
|
max_access_size: 0,
|
||||||
|
unaligned: false,
|
||||||
|
accepts: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {
|
||||||
|
const ZERO: Self = Self {
|
||||||
|
min_access_size: 0,
|
||||||
|
max_access_size: 0,
|
||||||
|
unaligned: false,
|
||||||
|
};
|
||||||
|
}
|
79
rust/qemu-api/tests/tests.rs
Normal file
79
rust/qemu-api/tests/tests.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2024, Linaro Limited
|
||||||
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
use std::{ffi::CStr, os::raw::c_void};
|
||||||
|
|
||||||
|
use qemu_api::{
|
||||||
|
bindings::*,
|
||||||
|
c_str, declare_properties, define_property,
|
||||||
|
definitions::{Class, ObjectImpl},
|
||||||
|
device_class, device_class_init,
|
||||||
|
zeroable::Zeroable,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_decl_macros() {
|
||||||
|
// Test that macros can compile.
|
||||||
|
pub static VMSTATE: VMStateDescription = VMStateDescription {
|
||||||
|
name: c_str!("name").as_ptr(),
|
||||||
|
unmigratable: true,
|
||||||
|
..Zeroable::ZERO
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(qemu_api_macros::offsets)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(qemu_api_macros::Object)]
|
||||||
|
pub struct DummyState {
|
||||||
|
pub _parent: DeviceState,
|
||||||
|
pub migrate_clock: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct DummyClass {
|
||||||
|
pub _parent: DeviceClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_properties! {
|
||||||
|
DUMMY_PROPERTIES,
|
||||||
|
define_property!(
|
||||||
|
c_str!("migrate-clk"),
|
||||||
|
DummyState,
|
||||||
|
migrate_clock,
|
||||||
|
unsafe { &qdev_prop_bool },
|
||||||
|
bool
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
device_class_init! {
|
||||||
|
dummy_class_init,
|
||||||
|
props => DUMMY_PROPERTIES,
|
||||||
|
realize_fn => None,
|
||||||
|
legacy_reset_fn => None,
|
||||||
|
vmsd => VMSTATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for DummyState {
|
||||||
|
type Class = DummyClass;
|
||||||
|
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 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 {
|
||||||
|
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
||||||
|
Some(dummy_class_init);
|
||||||
|
const CLASS_BASE_INIT: Option<
|
||||||
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
||||||
|
> = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
module_call_init(module_init_type::MODULE_INIT_QOM);
|
||||||
|
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()) as *mut _);
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,23 @@
|
|||||||
* in order to generate C FFI compatible Rust bindings.
|
* in order to generate C FFI compatible Rust bindings.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef __CLANG_STDATOMIC_H
|
||||||
|
#define __CLANG_STDATOMIC_H
|
||||||
|
/*
|
||||||
|
* Fix potential missing stdatomic.h error in case bindgen does not insert the
|
||||||
|
* correct libclang header paths on its own. We do not use stdatomic.h symbols
|
||||||
|
* in QEMU code, so it's fine to declare dummy types instead.
|
||||||
|
*/
|
||||||
|
typedef enum memory_order {
|
||||||
|
memory_order_relaxed,
|
||||||
|
memory_order_consume,
|
||||||
|
memory_order_acquire,
|
||||||
|
memory_order_release,
|
||||||
|
memory_order_acq_rel,
|
||||||
|
memory_order_seq_cst,
|
||||||
|
} memory_order;
|
||||||
|
#endif /* __CLANG_STDATOMIC_H */
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu-io.h"
|
#include "qemu-io.h"
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
packages:
|
packages:
|
||||||
- bash
|
- bash
|
||||||
- bc
|
- bc
|
||||||
- bindgen
|
|
||||||
- bison
|
- bison
|
||||||
- bsdextrautils
|
- bsdextrautils
|
||||||
- bzip2
|
- bzip2
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
packages:
|
packages:
|
||||||
- bash
|
- bash
|
||||||
- bc
|
- bc
|
||||||
- bindgen
|
|
||||||
- bison
|
- bison
|
||||||
- bsdextrautils
|
- bsdextrautils
|
||||||
- bzip2
|
- bzip2
|
||||||
|
@ -47,6 +47,8 @@ meson_options_help() {
|
|||||||
printf "%s\n" ' getrandom()'
|
printf "%s\n" ' getrandom()'
|
||||||
printf "%s\n" ' --enable-safe-stack SafeStack Stack Smash Protection (requires'
|
printf "%s\n" ' --enable-safe-stack SafeStack Stack Smash Protection (requires'
|
||||||
printf "%s\n" ' clang/llvm and coroutine backend ucontext)'
|
printf "%s\n" ' clang/llvm and coroutine backend ucontext)'
|
||||||
|
printf "%s\n" ' --enable-strict-rust-lints'
|
||||||
|
printf "%s\n" ' Enable stricter set of Rust warnings'
|
||||||
printf "%s\n" ' --enable-strip Strip targets on install'
|
printf "%s\n" ' --enable-strip Strip targets on install'
|
||||||
printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (slow)'
|
printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (slow)'
|
||||||
printf "%s\n" ' --enable-trace-backends=CHOICES'
|
printf "%s\n" ' --enable-trace-backends=CHOICES'
|
||||||
@ -495,6 +497,8 @@ _meson_option_parse() {
|
|||||||
--disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;;
|
--disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;;
|
||||||
--enable-stack-protector) printf "%s" -Dstack_protector=enabled ;;
|
--enable-stack-protector) printf "%s" -Dstack_protector=enabled ;;
|
||||||
--disable-stack-protector) printf "%s" -Dstack_protector=disabled ;;
|
--disable-stack-protector) printf "%s" -Dstack_protector=disabled ;;
|
||||||
|
--enable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=true ;;
|
||||||
|
--disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;;
|
||||||
--enable-strip) printf "%s" -Dstrip=true ;;
|
--enable-strip) printf "%s" -Dstrip=true ;;
|
||||||
--disable-strip) printf "%s" -Dstrip=false ;;
|
--disable-strip) printf "%s" -Dstrip=false ;;
|
||||||
--sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;;
|
--sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;;
|
||||||
|
@ -5,3 +5,4 @@ source_filename = bilge-impl-0.2.0.tar.gz
|
|||||||
source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8
|
source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = bilge-impl-0.2-rs
|
patch_directory = bilge-impl-0.2-rs
|
||||||
|
diff_files = bilge-impl-1.63.0.patch
|
||||||
|
45
subprojects/packagefiles/bilge-impl-1.63.0.patch
Normal file
45
subprojects/packagefiles/bilge-impl-1.63.0.patch
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
--- a/src/shared/discriminant_assigner.rs
|
||||||
|
+++ b/src/shared/discriminant_assigner.rs
|
||||||
|
@@ -26,20 +26,20 @@
|
||||||
|
let discriminant_expr = &discriminant.1;
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
|
||||||
|
- let Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) = discriminant_expr else {
|
||||||
|
+ if let Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) = discriminant_expr {
|
||||||
|
+ let discriminant_value: u128 = int.base10_parse().unwrap_or_else(unreachable);
|
||||||
|
+ if discriminant_value > self.max_value() {
|
||||||
|
+ abort!(variant, "Value of variant exceeds the given number of bits")
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ Some(discriminant_value)
|
||||||
|
+ } else {
|
||||||
|
abort!(
|
||||||
|
discriminant_expr,
|
||||||
|
"variant `{}` is not a number", variant_name;
|
||||||
|
help = "only literal integers currently supported"
|
||||||
|
)
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- let discriminant_value: u128 = int.base10_parse().unwrap_or_else(unreachable);
|
||||||
|
- if discriminant_value > self.max_value() {
|
||||||
|
- abort!(variant, "Value of variant exceeds the given number of bits")
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- Some(discriminant_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign(&mut self, variant: &Variant) -> u128 {
|
||||||
|
--- a/src/shared/fallback.rs
|
||||||
|
+++ b/src/shared/fallback.rs
|
||||||
|
@@ -22,8 +22,9 @@
|
||||||
|
}
|
||||||
|
Unnamed(fields) => {
|
||||||
|
let variant_fields = fields.unnamed.iter();
|
||||||
|
- let Ok(fallback_value) = variant_fields.exactly_one() else {
|
||||||
|
- abort!(variant, "fallback variant must have exactly one field"; help = "use only one field or change to a unit variant")
|
||||||
|
+ let fallback_value = match variant_fields.exactly_one() {
|
||||||
|
+ Ok(ok) => ok,
|
||||||
|
+ _ => abort!(variant, "fallback variant must have exactly one field"; help = "use only one field or change to a unit variant")
|
||||||
|
};
|
||||||
|
|
||||||
|
if !is_last_variant {
|
@ -15,7 +15,9 @@ _proc_macro2_rs = static_library(
|
|||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
rust_args: [
|
rust_args: [
|
||||||
'--cfg', 'feature="proc-macro"',
|
'--cfg', 'feature="proc-macro"',
|
||||||
'--cfg', 'span_locations',
|
'--cfg', 'no_literal_byte_character',
|
||||||
|
'--cfg', 'no_literal_c_string',
|
||||||
|
'--cfg', 'no_source_text',
|
||||||
'--cfg', 'wrap_proc_macro',
|
'--cfg', 'wrap_proc_macro',
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
@ -24,6 +24,7 @@ _syn_rs = static_library(
|
|||||||
'--cfg', 'feature="printing"',
|
'--cfg', 'feature="printing"',
|
||||||
'--cfg', 'feature="clone-impls"',
|
'--cfg', 'feature="clone-impls"',
|
||||||
'--cfg', 'feature="proc-macro"',
|
'--cfg', 'feature="proc-macro"',
|
||||||
|
'--cfg', 'feature="extra-traits"',
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
quote_dep,
|
quote_dep,
|
||||||
|
@ -752,7 +752,7 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
|
#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
|
||||||
|
|
||||||
static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
|
static void qdev_print_props(Monitor *mon, DeviceState *dev, const Property *props,
|
||||||
int indent)
|
int indent)
|
||||||
{
|
{
|
||||||
if (!props)
|
if (!props)
|
||||||
|
@ -13,7 +13,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
|||||||
eatmydata apt-get install --no-install-recommends -y \
|
eatmydata apt-get install --no-install-recommends -y \
|
||||||
bash \
|
bash \
|
||||||
bc \
|
bc \
|
||||||
bindgen \
|
|
||||||
bison \
|
bison \
|
||||||
bsdextrautils \
|
bsdextrautils \
|
||||||
bzip2 \
|
bzip2 \
|
||||||
@ -150,6 +149,11 @@ ENV LANG "en_US.UTF-8"
|
|||||||
ENV MAKE "/usr/bin/make"
|
ENV MAKE "/usr/bin/make"
|
||||||
ENV NINJA "/usr/bin/ninja"
|
ENV NINJA "/usr/bin/ninja"
|
||||||
ENV PYTHON "/usr/bin/python3"
|
ENV PYTHON "/usr/bin/python3"
|
||||||
|
ENV CARGO_HOME=/usr/local/cargo
|
||||||
|
ENV PATH=$CARGO_HOME/bin:$PATH
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
|
||||||
|
apt install -y --no-install-recommends cargo
|
||||||
|
RUN cargo install bindgen-cli
|
||||||
# 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
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
mappings:
|
mappings:
|
||||||
|
# Too old on Ubuntu 22.04; we install it from cargo instead
|
||||||
|
bindgen:
|
||||||
|
Ubuntu2204:
|
||||||
|
|
||||||
flake8:
|
flake8:
|
||||||
OpenSUSELeap15:
|
OpenSUSELeap15:
|
||||||
|
|
||||||
|
@ -137,6 +137,14 @@ fedora_rustup_nightly_extras = [
|
|||||||
'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',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ubuntu2204_bindgen_extras = [
|
||||||
|
"ENV CARGO_HOME=/usr/local/cargo\n",
|
||||||
|
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
|
||||||
|
"RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n",
|
||||||
|
" apt install -y --no-install-recommends cargo\n",
|
||||||
|
'RUN cargo install bindgen-cli\n',
|
||||||
|
]
|
||||||
|
|
||||||
def cross_build(prefix, targets):
|
def cross_build(prefix, targets):
|
||||||
conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix)
|
conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix)
|
||||||
targets = "ENV DEF_TARGET_LIST %s\n" % (targets)
|
targets = "ENV DEF_TARGET_LIST %s\n" % (targets)
|
||||||
@ -157,7 +165,8 @@ try:
|
|||||||
trailer="".join(debian12_extras))
|
trailer="".join(debian12_extras))
|
||||||
generate_dockerfile("fedora", "fedora-40")
|
generate_dockerfile("fedora", "fedora-40")
|
||||||
generate_dockerfile("opensuse-leap", "opensuse-leap-15")
|
generate_dockerfile("opensuse-leap", "opensuse-leap-15")
|
||||||
generate_dockerfile("ubuntu2204", "ubuntu-2204")
|
generate_dockerfile("ubuntu2204", "ubuntu-2204",
|
||||||
|
trailer="".join(ubuntu2204_bindgen_extras))
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-fatal Rust-enabled build
|
# Non-fatal Rust-enabled build
|
||||||
|
Loading…
Reference in New Issue
Block a user