mirror of https://github.com/xemu-project/xemu.git
* 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
|
@ -5,3 +5,5 @@
|
|||
*.rs diff=rust
|
||||
*.rs.inc diff=rust
|
||||
Cargo.lock diff=toml merge=binary
|
||||
|
||||
*.patch -text -whitespace
|
||||
|
|
|
@ -128,7 +128,7 @@ build-system-fedora-rust-nightly:
|
|||
job: amd64-fedora-rust-nightly-container
|
||||
variables:
|
||||
IMAGE: fedora-rust-nightly
|
||||
CONFIGURE_ARGS: --disable-docs --enable-rust
|
||||
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
|
||||
TARGETS: aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
allow_failure: true
|
||||
|
|
|
@ -107,6 +107,18 @@ Python build dependencies
|
|||
required, it may be necessary to fetch python modules from the Python
|
||||
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
|
||||
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
|
||||
|
|
|
@ -749,7 +749,7 @@ const PropertyInfo qdev_prop_array = {
|
|||
|
||||
/* --- 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) {
|
||||
return NULL;
|
||||
|
@ -763,10 +763,10 @@ static Property *qdev_prop_walk(Property *props, const char *name)
|
|||
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;
|
||||
Property *prop;
|
||||
const Property *prop;
|
||||
|
||||
/* device properties */
|
||||
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)
|
||||
{
|
||||
Property *prop;
|
||||
const Property *prop;
|
||||
|
||||
prop = qdev_prop_find(dev, name);
|
||||
object_property_set_str(OBJECT(dev), name,
|
||||
|
@ -956,7 +956,7 @@ const PropertyInfo qdev_prop_size = {
|
|||
/* --- object link property --- */
|
||||
|
||||
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,
|
||||
prop->offset,
|
||||
|
@ -969,7 +969,7 @@ const PropertyInfo qdev_prop_link = {
|
|||
.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);
|
||||
ObjectProperty *op;
|
||||
|
@ -980,7 +980,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop)
|
|||
field_prop_getter(prop->info),
|
||||
field_prop_setter(prop->info),
|
||||
prop->info->release,
|
||||
prop);
|
||||
(Property *)prop);
|
||||
|
||||
object_property_set_description(obj, prop->name,
|
||||
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,
|
||||
Property *prop)
|
||||
const Property *prop)
|
||||
{
|
||||
ObjectClass *oc = OBJECT_CLASS(klass);
|
||||
ObjectProperty *op;
|
||||
|
@ -1007,7 +1007,7 @@ static void qdev_class_add_property(DeviceClass *klass, const char *name,
|
|||
field_prop_getter(prop->info),
|
||||
field_prop_setter(prop->info),
|
||||
prop->info->release,
|
||||
prop);
|
||||
(Property *)prop);
|
||||
}
|
||||
if (prop->set_default) {
|
||||
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
|
||||
* 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;
|
||||
|
||||
|
@ -1058,12 +1058,12 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop)
|
|||
name = g_strdup_printf("legacy-%s", prop->name);
|
||||
object_class_property_add(OBJECT_CLASS(dc), name, "str",
|
||||
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;
|
||||
for (prop = props; prop && prop->name; prop++) {
|
||||
|
|
|
@ -136,7 +136,7 @@ struct DeviceClass {
|
|||
* ensures a compile-time error if someone attempts to assign
|
||||
* dc->props directly.
|
||||
*/
|
||||
Property *props_;
|
||||
const Property *props_;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* 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
|
||||
|
|
|
@ -37,7 +37,7 @@ struct PropertyInfo {
|
|||
int (*print)(Object *obj, Property *prop, char *dest, size_t len);
|
||||
void (*set_default_value)(ObjectProperty *op, const Property *prop);
|
||||
ObjectProperty *(*create)(ObjectClass *oc, const char *name,
|
||||
Property *prop);
|
||||
const Property *prop);
|
||||
ObjectPropertyAccessor *get;
|
||||
ObjectPropertyAccessor *set;
|
||||
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.
|
||||
* 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
|
||||
|
|
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)
|
||||
keyval = import('keyval')
|
||||
rust = import('rust')
|
||||
ss = import('sourceset')
|
||||
fs = import('fs')
|
||||
|
||||
|
@ -52,6 +53,17 @@ cpu = host_machine.cpu_family()
|
|||
|
||||
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 #
|
||||
############
|
||||
|
@ -70,21 +82,45 @@ if host_os == 'darwin' and \
|
|||
all_languages += ['objc']
|
||||
objc = meson.get_compiler('objc')
|
||||
endif
|
||||
have_rust = false
|
||||
if not get_option('rust').disabled() and add_languages('rust', required: get_option('rust'), native: false) \
|
||||
and add_languages('rust', required: get_option('rust'), native: true)
|
||||
|
||||
have_rust = add_languages('rust', native: false,
|
||||
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')
|
||||
have_rust = true
|
||||
if rustc.version().version_compare('<1.80.0')
|
||||
if rustc.version().version_compare('<1.63.0')
|
||||
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
|
||||
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
|
||||
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
|
||||
stap = not_found
|
||||
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 = 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') \
|
||||
.disable_auto_if(not have_system) \
|
||||
.allowed()
|
||||
|
@ -3374,6 +3399,35 @@ endif
|
|||
|
||||
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')
|
||||
shaderinclude = find_program('scripts/shaderinclude.py')
|
||||
qapi_gen = find_program('scripts/qapi-gen.py')
|
||||
|
@ -3971,32 +4025,37 @@ common_all = static_library('common',
|
|||
implicit_include_directories: false,
|
||||
dependencies: common_ss.all_dependencies())
|
||||
|
||||
if have_rust and have_system
|
||||
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()
|
||||
rustc_args += ['-D', 'unsafe_op_in_unsafe_fn']
|
||||
if have_rust
|
||||
# We would like to use --generate-cstr, but it is only available
|
||||
# starting with bindgen 0.66.0. The oldest supported versions
|
||||
# is 0.60.x (Debian 12 has 0.60.1) which introduces --allowlist-file.
|
||||
bindgen_args = [
|
||||
'--disable-header-comment',
|
||||
'--raw-line', '// @generated',
|
||||
'--ctypes-prefix', 'core::ffi',
|
||||
'--formatter', 'rustfmt',
|
||||
'--ctypes-prefix', 'std::os::raw',
|
||||
'--generate-block',
|
||||
'--generate-cstr',
|
||||
'--impl-debug',
|
||||
'--merge-extern-blocks',
|
||||
'--no-doc-comments',
|
||||
'--use-core',
|
||||
'--with-derive-default',
|
||||
'--no-size_t-is-usize',
|
||||
'--no-layout-tests',
|
||||
'--no-prepend-enum-name',
|
||||
'--allowlist-file', meson.project_source_root() + '/include/.*',
|
||||
'--allowlist-file', meson.project_source_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 = [
|
||||
'DeviceCategory',
|
||||
'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
|
||||
# command invocation using the environment variables CLANG_PATH and
|
||||
# LIBCLANG_PATH
|
||||
bindings_rs = import('rust').bindgen(
|
||||
bindings_rs = rust.bindgen(
|
||||
input: 'rust/wrapper.h',
|
||||
dependencies: common_ss.all_dependencies(),
|
||||
output: 'bindings.rs',
|
||||
include_directories: include_directories('.', 'include'),
|
||||
bindgen_version: ['>=0.69.4'],
|
||||
bindgen_version: ['>=0.60.0'],
|
||||
args: bindgen_args,
|
||||
)
|
||||
subdir('rust')
|
||||
|
@ -4040,6 +4099,7 @@ endif
|
|||
|
||||
|
||||
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'
|
||||
entitlement = find_program('scripts/entitlement.sh')
|
||||
|
@ -4132,7 +4192,7 @@ foreach target : target_dirs
|
|||
arch_srcs += target_specific.sources()
|
||||
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)
|
||||
crates = []
|
||||
foreach dep : target_rust.dependencies()
|
||||
|
@ -4141,7 +4201,7 @@ foreach target : target_dirs
|
|||
if crates.length() > 0
|
||||
rlib_rs = custom_target('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,
|
||||
build_by_default: true,
|
||||
build_always_stale: true)
|
||||
|
@ -4149,7 +4209,6 @@ foreach target : target_dirs
|
|||
rlib_rs,
|
||||
dependencies: target_rust.dependencies(),
|
||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||
rust_args: rustc_args,
|
||||
rust_abi: 'c')
|
||||
arch_deps += declare_dependency(link_whole: [rlib])
|
||||
endif
|
||||
|
@ -4495,9 +4554,11 @@ else
|
|||
endif
|
||||
summary_info += {'Rust support': 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 += {'rustc': ' '.join(rustc.cmd_array())}
|
||||
summary_info += {'rustc version': rustc.version()}
|
||||
summary_info += {'bindgen': bindgen.full_path()}
|
||||
summary_info += {'bindgen version': bindgen.version()}
|
||||
endif
|
||||
option_cflags = (get_option('debug') ? ['-g'] : [])
|
||||
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',
|
||||
description: 'Rust support')
|
||||
option('strict_rust_lints', type: 'boolean', value: false,
|
||||
description: 'Enable stricter set of Rust warnings')
|
||||
|
|
|
@ -91,6 +91,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "qemu_api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"qemu_api_macros",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qemu_api_macros"
|
|
@ -0,0 +1,7 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"qemu-api-macros",
|
||||
"qemu-api",
|
||||
"hw/char/pl011",
|
||||
]
|
|
@ -1,3 +1,2 @@
|
|||
config X_PL011_RUST
|
||||
bool
|
||||
default y if HAVE_RUST
|
||||
|
|
|
@ -21,6 +21,3 @@ bilge = { version = "0.2.0" }
|
|||
bilge-impl = { version = "0.2.0" }
|
||||
qemu_api = { path = "../../../qemu-api" }
|
||||
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>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use core::{
|
||||
ffi::{c_int, c_uchar, c_uint, c_void, CStr},
|
||||
ptr::{addr_of, addr_of_mut, NonNull},
|
||||
use core::ptr::{addr_of, addr_of_mut, NonNull};
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
os::raw::{c_int, c_uchar, c_uint, c_void},
|
||||
};
|
||||
|
||||
use qemu_api::{
|
||||
bindings::{self, *},
|
||||
c_str,
|
||||
definitions::ObjectImpl,
|
||||
device_class::TYPE_SYS_BUS_DEVICE,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -18,15 +21,42 @@ use crate::{
|
|||
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;
|
||||
|
||||
/// QEMU sourced constant.
|
||||
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)]
|
||||
#[derive(Debug, qemu_api_macros::Object)]
|
||||
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
|
||||
/// PL011 Device Model in QEMU
|
||||
pub struct PL011State {
|
||||
pub parent_obj: SysBusDevice,
|
||||
|
@ -69,6 +99,8 @@ pub struct PL011State {
|
|||
pub clock: NonNull<Clock>,
|
||||
#[doc(alias = "migrate_clk")]
|
||||
pub migrate_clock: bool,
|
||||
/// The byte string that identifies the device.
|
||||
device_id: DeviceId,
|
||||
}
|
||||
|
||||
impl ObjectImpl for PL011State {
|
||||
|
@ -88,17 +120,13 @@ pub struct PL011Class {
|
|||
}
|
||||
|
||||
impl qemu_api::definitions::Class for PL011Class {
|
||||
const CLASS_INIT: Option<
|
||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void),
|
||||
> = Some(crate::device_class::pl011_class_init);
|
||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
||||
Some(crate::device_class::pl011_class_init);
|
||||
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;
|
||||
}
|
||||
|
||||
#[used]
|
||||
pub static CLK_NAME: &CStr = c"clk";
|
||||
|
||||
impl 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
|
||||
/// location/instance. All its fields are expected to hold unitialized
|
||||
/// 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>();
|
||||
// SAFETY:
|
||||
//
|
||||
|
@ -148,23 +178,18 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
&mut self,
|
||||
offset: hwaddr,
|
||||
_size: core::ffi::c_uint,
|
||||
) -> std::ops::ControlFlow<u64, u64> {
|
||||
pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> {
|
||||
use RegisterOffset::*;
|
||||
|
||||
std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) {
|
||||
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(_) => {
|
||||
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
||||
0
|
||||
}
|
||||
Ok(DR) => {
|
||||
// s->flags &= ~PL011_FLAG_RXFF;
|
||||
self.flags.set_receive_fifo_full(false);
|
||||
let c = self.read_fifo[self.read_pos];
|
||||
if self.read_count > 0 {
|
||||
|
@ -172,11 +197,9 @@ impl PL011State {
|
|||
self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
|
||||
}
|
||||
if self.read_count == 0 {
|
||||
// self.flags |= PL011_FLAG_RXFE;
|
||||
self.flags.set_receive_fifo_empty(true);
|
||||
}
|
||||
if self.read_count + 1 == self.read_trigger {
|
||||
//self.int_level &= ~ INT_RX;
|
||||
self.int_level &= !registers::INT_RX;
|
||||
}
|
||||
// Update error bits.
|
||||
|
@ -346,13 +369,6 @@ impl PL011State {
|
|||
* 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_data_carrier_detect(self.control.out_1());
|
||||
self.flags.set_clear_to_send(self.control.request_to_send());
|
||||
|
@ -363,10 +379,6 @@ impl PL011State {
|
|||
let mut il = self.int_level;
|
||||
|
||||
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() {
|
||||
il |= Interrupt::DSR as u32;
|
||||
|
@ -472,10 +484,8 @@ impl PL011State {
|
|||
let slot = (self.read_pos + self.read_count) & (depth - 1);
|
||||
self.read_fifo[slot] = value;
|
||||
self.read_count += 1;
|
||||
// s->flags &= ~PL011_FLAG_RXFE;
|
||||
self.flags.set_receive_fifo_empty(false);
|
||||
if self.read_count == depth {
|
||||
//s->flags |= PL011_FLAG_RXFF;
|
||||
self.flags.set_receive_fifo_full(true);
|
||||
}
|
||||
|
||||
|
@ -492,6 +502,27 @@ impl PL011State {
|
|||
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 ?
|
||||
|
@ -514,7 +545,6 @@ pub const IRQMASK: [u32; 6] = [
|
|||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||
/// the same size as [`PL011State`]. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
|
||||
unsafe {
|
||||
debug_assert!(!opaque.is_null());
|
||||
|
@ -530,12 +560,7 @@ pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
|
|||
/// readable/writeable from one thread at any time.
|
||||
///
|
||||
/// The buffer and size arguments must also be valid.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_receive(
|
||||
opaque: *mut core::ffi::c_void,
|
||||
buf: *const u8,
|
||||
size: core::ffi::c_int,
|
||||
) {
|
||||
pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) {
|
||||
unsafe {
|
||||
debug_assert!(!opaque.is_null());
|
||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||
|
@ -554,8 +579,7 @@ pub unsafe extern "C" fn pl011_receive(
|
|||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||
/// the same size as [`PL011State`]. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) {
|
||||
pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
|
||||
unsafe {
|
||||
debug_assert!(!opaque.is_null());
|
||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||
|
@ -576,7 +600,7 @@ pub unsafe extern "C" fn pl011_create(
|
|||
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
|
||||
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_mmio_map(sysbus, 0, addr);
|
||||
sysbus_connect_irq(sysbus, 0, irq);
|
||||
|
@ -589,7 +613,6 @@ pub unsafe extern "C" fn pl011_create(
|
|||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||
/// the same size as [`PL011State`]. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
|
||||
unsafe {
|
||||
debug_assert!(!obj.is_null());
|
||||
|
@ -597,3 +620,50 @@ pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
|
|||
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
|
||||
|
||||
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 {
|
||||
name: PL011State::TYPE_INFO.name,
|
||||
unmigratable: true,
|
||||
..unsafe { ::core::mem::MaybeUninit::<VMStateDescription>::zeroed().assume_init() }
|
||||
name: c_str!("pl011").as_ptr(),
|
||||
version_id: 2,
|
||||
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! {
|
||||
PL011_PROPERTIES,
|
||||
qemu_api::define_property!(
|
||||
c"chardev",
|
||||
c_str!("chardev"),
|
||||
PL011State,
|
||||
char_backend,
|
||||
unsafe { &qdev_prop_chr },
|
||||
CharBackend
|
||||
),
|
||||
qemu_api::define_property!(
|
||||
c"migrate-clk",
|
||||
c_str!("migrate-clk"),
|
||||
PL011State,
|
||||
migrate_clock,
|
||||
unsafe { &qdev_prop_bool },
|
||||
bool
|
||||
bool,
|
||||
default = true
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -46,7 +106,6 @@ qemu_api::device_class_init! {
|
|||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||
/// the same size as [`PL011State`]. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
|
||||
unsafe {
|
||||
assert!(!dev.is_null());
|
||||
|
@ -60,7 +119,6 @@ pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut E
|
|||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||
/// the same size as [`PL011State`]. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
|
||||
unsafe {
|
||||
assert!(!dev.is_null());
|
||||
|
|
|
@ -36,16 +36,20 @@
|
|||
clippy::cognitive_complexity,
|
||||
clippy::missing_safety_doc,
|
||||
)]
|
||||
#![allow(clippy::result_unit_err)]
|
||||
|
||||
extern crate bilge;
|
||||
extern crate bilge_impl;
|
||||
extern crate qemu_api;
|
||||
|
||||
use qemu_api::c_str;
|
||||
|
||||
pub mod device;
|
||||
pub mod device_class;
|
||||
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.
|
||||
///
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// 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;
|
||||
|
||||
|
@ -14,20 +15,15 @@ pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps {
|
|||
read_with_attrs: None,
|
||||
write_with_attrs: None,
|
||||
endianness: device_endian::DEVICE_NATIVE_ENDIAN,
|
||||
valid: unsafe { MaybeUninit::<MemoryRegionOps__bindgen_ty_1>::zeroed().assume_init() },
|
||||
valid: Zeroable::ZERO,
|
||||
impl_: MemoryRegionOps__bindgen_ty_2 {
|
||||
min_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 core::ffi::c_void,
|
||||
addr: hwaddr,
|
||||
size: core::ffi::c_uint,
|
||||
) -> u64 {
|
||||
unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 {
|
||||
assert!(!opaque.is_null());
|
||||
let mut state = unsafe { NonNull::new_unchecked(opaque.cast::<PL011State>()) };
|
||||
let val = unsafe { state.as_mut().read(addr, size) };
|
||||
|
@ -44,13 +40,7 @@ unsafe extern "C" fn pl011_read(
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pl011_write(
|
||||
opaque: *mut core::ffi::c_void,
|
||||
addr: hwaddr,
|
||||
data: u64,
|
||||
_size: core::ffi::c_uint,
|
||||
) {
|
||||
unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) {
|
||||
unsafe {
|
||||
assert!(!opaque.is_null());
|
||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||
|
|
|
@ -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]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "2"
|
||||
|
||||
# Do not include in any global workspace
|
||||
[workspace]
|
||||
syn = { version = "2", features = ["extra-traits"] }
|
||||
|
|
|
@ -2,7 +2,7 @@ quote_dep = dependency('quote-1-rs', native: true)
|
|||
syn_dep = dependency('syn-2-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',
|
||||
files('src/lib.rs'),
|
||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||
|
|
|
@ -3,41 +3,92 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use proc_macro2::Span;
|
||||
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)]
|
||||
pub fn derive_object(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = input.ident;
|
||||
let module_static = format_ident!("__{}_LOAD_MODULE", name);
|
||||
|
||||
let expanded = quote! {
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[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 #module_static: extern "C" fn() = {
|
||||
extern "C" fn __register() {
|
||||
unsafe {
|
||||
::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO);
|
||||
}
|
||||
::qemu_api::module_init! {
|
||||
MODULE_INIT_QOM => 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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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 = []
|
||||
|
||||
[dependencies]
|
||||
qemu_api_macros = { path = "../qemu-api-macros" }
|
||||
|
||||
[build-dependencies]
|
||||
version_check = "~0.9"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
allocator = []
|
||||
|
||||
# Do not include in any global workspace
|
||||
[workspace]
|
||||
|
||||
[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 version_check as rustc;
|
||||
|
||||
fn main() {
|
||||
if !Path::new("src/bindings.rs").exists() {
|
||||
panic!(
|
||||
|
@ -11,4 +13,11 @@ fn main() {
|
|||
(`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',
|
||||
structured_sources(
|
||||
[
|
||||
'src/lib.rs',
|
||||
'src/c_str.rs',
|
||||
'src/definitions.rs',
|
||||
'src/device_class.rs',
|
||||
'src/offset_of.rs',
|
||||
'src/vmstate.rs',
|
||||
'src/zeroable.rs',
|
||||
],
|
||||
{'.' : bindings_rs},
|
||||
),
|
||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||
rust_abi: 'rust',
|
||||
rust_args: rustc_args + [
|
||||
'--cfg', 'MESON',
|
||||
# '--cfg', 'feature="allocator"',
|
||||
],
|
||||
dependencies: [
|
||||
qemu_api_macros,
|
||||
],
|
||||
rust_args: _qemu_api_cfg,
|
||||
)
|
||||
|
||||
rust.test('rust-qemu-api-tests', _qemu_api_rs,
|
||||
suite: ['unit', 'rust'])
|
||||
|
||||
qemu_api = declare_dependency(
|
||||
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'])
|
||||
|
|
|
@ -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.
|
||||
|
||||
use ::core::ffi::{c_void, CStr};
|
||||
use std::{ffi::CStr, os::raw::c_void};
|
||||
|
||||
use crate::bindings::{Object, ObjectClass, TypeInfo};
|
||||
|
||||
|
@ -29,46 +29,40 @@ pub trait Class {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! module_init {
|
||||
($func:expr, $type:expr) => {
|
||||
#[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() {
|
||||
unsafe {
|
||||
$crate::bindings::register_module_init(Some($func), $type);
|
||||
}
|
||||
}
|
||||
|
||||
__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() {
|
||||
($type:ident => $body:block) => {
|
||||
const _: () = {
|
||||
#[used]
|
||||
#[cfg_attr(
|
||||
not(any(target_vendor = "apple", target_os = "windows")),
|
||||
link_section = ".init_array"
|
||||
)]
|
||||
#[cfg_attr(target_vendor = "apple", 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 init_fn() {
|
||||
$body
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::bindings::register_module_init(
|
||||
Some($func),
|
||||
$crate::bindings::module_init_type::MODULE_INIT_QOM,
|
||||
);
|
||||
extern "C" fn ctor_fn() {
|
||||
unsafe {
|
||||
$crate::bindings::register_module_init(
|
||||
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]
|
||||
|
@ -81,13 +75,13 @@ macro_rules! type_info {
|
|||
} else {
|
||||
::core::ptr::null_mut()
|
||||
},
|
||||
instance_size: ::core::mem::size_of::<$t>() as $crate::bindings::size_t,
|
||||
instance_align: ::core::mem::align_of::<$t>() as $crate::bindings::size_t,
|
||||
instance_size: ::core::mem::size_of::<$t>(),
|
||||
instance_align: ::core::mem::align_of::<$t>(),
|
||||
instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
|
||||
instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
|
||||
instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
|
||||
abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
|
||||
class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>() 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_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
|
||||
class_data: ::core::ptr::null_mut(),
|
||||
|
|
|
@ -2,127 +2,73 @@
|
|||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// 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_rules! device_class_init {
|
||||
($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(
|
||||
klass: *mut $crate::bindings::ObjectClass,
|
||||
_: *mut ::core::ffi::c_void,
|
||||
_: *mut ::std::os::raw::c_void,
|
||||
) {
|
||||
let mut dc =
|
||||
::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
|
||||
dc.as_mut().realize = $realize_fn;
|
||||
dc.as_mut().vmsd = &$vmsd;
|
||||
$crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
|
||||
$crate::bindings::device_class_set_props(dc.as_mut(), $props.as_mut_ptr());
|
||||
unsafe {
|
||||
dc.as_mut().realize = $realize_fn;
|
||||
dc.as_mut().vmsd = &$vmsd;
|
||||
$crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
|
||||
$crate::bindings::device_class_set_props(dc.as_mut(), $props.as_ptr());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_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 {
|
||||
name: {
|
||||
#[used]
|
||||
static _TEMP: &::core::ffi::CStr = $name;
|
||||
_TEMP.as_ptr()
|
||||
},
|
||||
// use associated function syntax for type checking
|
||||
name: ::std::ffi::CStr::as_ptr($name),
|
||||
info: $prop,
|
||||
offset: ::core::mem::offset_of!($state, $field)
|
||||
.try_into()
|
||||
.expect("Could not fit offset value to type"),
|
||||
bitnr: 0,
|
||||
bitmask: 0,
|
||||
offset: $crate::offset_of!($state, $field) as isize,
|
||||
set_default: true,
|
||||
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval.into() },
|
||||
arrayoffset: 0,
|
||||
arrayinfo: ::core::ptr::null(),
|
||||
arrayfieldsize: 0,
|
||||
link_type: ::core::ptr::null(),
|
||||
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
|
||||
..$crate::zeroable::Zeroable::ZERO
|
||||
}
|
||||
};
|
||||
($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr$(,)*) => {
|
||||
($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => {
|
||||
$crate::bindings::Property {
|
||||
name: {
|
||||
#[used]
|
||||
static _TEMP: &::core::ffi::CStr = $name;
|
||||
_TEMP.as_ptr()
|
||||
},
|
||||
// use associated function syntax for type checking
|
||||
name: ::std::ffi::CStr::as_ptr($name),
|
||||
info: $prop,
|
||||
offset: ::core::mem::offset_of!($state, $field)
|
||||
.try_into()
|
||||
.expect("Could not fit offset value to type"),
|
||||
bitnr: 0,
|
||||
bitmask: 0,
|
||||
offset: $crate::offset_of!($state, $field) as isize,
|
||||
set_default: false,
|
||||
defval: $crate::bindings::Property__bindgen_ty_1 { i: 0 },
|
||||
arrayoffset: 0,
|
||||
arrayinfo: ::core::ptr::null(),
|
||||
arrayfieldsize: 0,
|
||||
link_type: ::core::ptr::null(),
|
||||
..$crate::zeroable::Zeroable::ZERO
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[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_rules! declare_properties {
|
||||
($ident:ident, $($prop:expr),*$(,)*) => {
|
||||
|
||||
const fn _calc_prop_len() -> usize {
|
||||
pub static $ident: [$crate::bindings::Property; {
|
||||
let mut len = 1;
|
||||
$({
|
||||
_ = stringify!($prop);
|
||||
len += 1;
|
||||
})*
|
||||
len
|
||||
}
|
||||
const PROP_LEN: usize = _calc_prop_len();
|
||||
|
||||
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);
|
||||
}] = [
|
||||
$($prop),*,
|
||||
$crate::zeroable::Zeroable::ZERO,
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! vm_state_description {
|
||||
($(#[$outer:meta])*
|
||||
$name:ident,
|
||||
$(name: $vname:expr,)*
|
||||
$(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() }
|
||||
};
|
||||
}
|
||||
}
|
||||
// workaround until we can use --generate-cstr in bindgen.
|
||||
pub const TYPE_DEVICE: &CStr =
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
|
||||
pub const TYPE_SYS_BUS_DEVICE: &CStr =
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
|
||||
|
|
|
@ -26,14 +26,20 @@ unsafe impl Send for bindings::Property {}
|
|||
unsafe impl Sync for bindings::Property {}
|
||||
unsafe impl Sync for bindings::TypeInfo {}
|
||||
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 device_class;
|
||||
pub mod offset_of;
|
||||
pub mod vmstate;
|
||||
pub mod zeroable;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::alloc::{GlobalAlloc, Layout};
|
||||
use std::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
os::raw::c_void,
|
||||
};
|
||||
|
||||
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
||||
extern "C" {
|
||||
|
@ -47,8 +53,8 @@ extern "C" {
|
|||
|
||||
#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
|
||||
extern "C" {
|
||||
fn qemu_memalign(alignment: usize, size: usize) -> *mut ::core::ffi::c_void;
|
||||
fn qemu_vfree(ptr: *mut ::core::ffi::c_void);
|
||||
fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void;
|
||||
fn qemu_vfree(ptr: *mut c_void);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -113,7 +119,7 @@ impl Default for QemuAllocator {
|
|||
}
|
||||
|
||||
// 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 fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
|
@ -164,3 +170,6 @@ unsafe impl GlobalAlloc for QemuAllocator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(has_offset_of)]
|
||||
pub use core::mem::offset_of;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
#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/module.h"
|
||||
#include "qemu-io.h"
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
packages:
|
||||
- bash
|
||||
- bc
|
||||
- bindgen
|
||||
- bison
|
||||
- bsdextrautils
|
||||
- bzip2
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
packages:
|
||||
- bash
|
||||
- bc
|
||||
- bindgen
|
||||
- bison
|
||||
- bsdextrautils
|
||||
- bzip2
|
||||
|
|
|
@ -47,6 +47,8 @@ meson_options_help() {
|
|||
printf "%s\n" ' getrandom()'
|
||||
printf "%s\n" ' --enable-safe-stack SafeStack Stack Smash Protection (requires'
|
||||
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-tcg-interpreter TCG with bytecode interpreter (slow)'
|
||||
printf "%s\n" ' --enable-trace-backends=CHOICES'
|
||||
|
@ -495,6 +497,8 @@ _meson_option_parse() {
|
|||
--disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;;
|
||||
--enable-stack-protector) printf "%s" -Dstack_protector=enabled ;;
|
||||
--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 ;;
|
||||
--disable-strip) printf "%s" -Dstrip=false ;;
|
||||
--sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;;
|
||||
|
|
|
@ -5,3 +5,4 @@ source_filename = bilge-impl-0.2.0.tar.gz
|
|||
source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8
|
||||
#method = cargo
|
||||
patch_directory = bilge-impl-0.2-rs
|
||||
diff_files = bilge-impl-1.63.0.patch
|
||||
|
|
|
@ -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_args: [
|
||||
'--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',
|
||||
],
|
||||
dependencies: [
|
||||
|
|
|
@ -24,6 +24,7 @@ _syn_rs = static_library(
|
|||
'--cfg', 'feature="printing"',
|
||||
'--cfg', 'feature="clone-impls"',
|
||||
'--cfg', 'feature="proc-macro"',
|
||||
'--cfg', 'feature="extra-traits"',
|
||||
],
|
||||
dependencies: [
|
||||
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__)
|
||||
|
||||
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)
|
||||
{
|
||||
if (!props)
|
||||
|
|
|
@ -13,7 +13,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
|||
eatmydata apt-get install --no-install-recommends -y \
|
||||
bash \
|
||||
bc \
|
||||
bindgen \
|
||||
bison \
|
||||
bsdextrautils \
|
||||
bzip2 \
|
||||
|
@ -150,6 +149,11 @@ ENV LANG "en_US.UTF-8"
|
|||
ENV MAKE "/usr/bin/make"
|
||||
ENV NINJA "/usr/bin/ninja"
|
||||
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)
|
||||
ARG USER
|
||||
ARG UID
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
mappings:
|
||||
# Too old on Ubuntu 22.04; we install it from cargo instead
|
||||
bindgen:
|
||||
Ubuntu2204:
|
||||
|
||||
flake8:
|
||||
OpenSUSELeap15:
|
||||
|
||||
|
|
|
@ -137,6 +137,14 @@ fedora_rustup_nightly_extras = [
|
|||
'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):
|
||||
conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix)
|
||||
targets = "ENV DEF_TARGET_LIST %s\n" % (targets)
|
||||
|
@ -157,7 +165,8 @@ try:
|
|||
trailer="".join(debian12_extras))
|
||||
generate_dockerfile("fedora", "fedora-40")
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue