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:
Peter Maydell
2024-11-06 21:27:47 +00:00
45 changed files with 1379 additions and 414 deletions
+2
View File
@@ -5,3 +5,5 @@
*.rs diff=rust
*.rs.inc diff=rust
Cargo.lock diff=toml merge=binary
*.patch -text -whitespace
+1 -1
View File
@@ -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
+12
View File
@@ -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
View File
@@ -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++) {
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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'
+2
View File
@@ -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
View File
@@ -91,6 +91,10 @@ dependencies = [
[[package]]
name = "qemu_api"
version = "0.1.0"
dependencies = [
"qemu_api_macros",
"version_check",
]
[[package]]
name = "qemu_api_macros"
+7
View File
@@ -0,0 +1,7 @@
[workspace]
resolver = "2"
members = [
"qemu-api-macros",
"qemu-api",
"hw/char/pl011",
]
-1
View File
@@ -1,3 +1,2 @@
config X_PL011_RUST
bool
default y if HAVE_RUST
-3
View File
@@ -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]
+116 -46
View File
@@ -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;
}
+69 -11
View File
@@ -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());
+5 -1
View File
@@ -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.
///
+7 -17
View File
@@ -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>());
-47
View File
@@ -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"
+1 -4
View File
@@ -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"] }
+1 -1
View File
@@ -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'],
+77 -26
View File
@@ -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