mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
+13
-13
@@ -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
|
||||
|
||||
+99
-38
@@ -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')
|
||||
|
||||
+4
@@ -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>());
|
||||
|
||||
Generated
-47
@@ -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)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user