* [PATCH v2 2/3] hw/char/pl011: Pad PL011State struct to same size as Rust impl
2025-03-21 11:25 [PATCH v2 0/3] rust: Fix PL011State size mismatch assert Peter Maydell
2025-03-21 11:25 ` [PATCH v2 1/3] rust: assertions: add static_assert Peter Maydell
@ 2025-03-21 11:25 ` Peter Maydell
2025-03-21 11:25 ` [PATCH v2 3/3] rust: pl011: Check size of state struct at compile time Peter Maydell
2 siblings, 0 replies; 4+ messages in thread
From: Peter Maydell @ 2025-03-21 11:25 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-rust, Paolo Bonzini, Zhao Liu
We have some users of the PL011 struct which embed it directly into
their own state structs. This means that the Rust version of the
device must have a state struct that is the same size or smaller
than the C struct.
In commit 9b642097d6b7 ("rust: pl011: switch to safe chardev operation")
the Rust PL011 state struct changed from having a bindings::CharBackend
to a chardev::CharBackend, which made it grow larger than the C
version. This results in an assertion at startup when QEMU was
built with Rust enabled:
$ qemu-system-arm -M raspi2b -display none
ERROR:../../qom/object.c:562:object_initialize_with_type: assertion
failed: (size >= type->instance_size)
The long-term better approach to this problem would be to move
our C device code patterns away from "embed a struct" and (back)
to "have a pointer to the device", so we can make the C PL011State
struct a private implementation detail rather than exposed to
its users.
For the short term, add a padding field at the end of the C struct
so it's big enough that the Rust state struct can fit.
Fixes: 9b642097d6b7 ("rust: pl011: switch to safe chardev operation")
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/char/pl011.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
index 4fcaf3d7d30..299ca9b18bb 100644
--- a/include/hw/char/pl011.h
+++ b/include/hw/char/pl011.h
@@ -52,6 +52,11 @@ struct PL011State {
Clock *clk;
bool migrate_clk;
const unsigned char *id;
+ /*
+ * Since some users embed this struct directly, we must
+ * ensure that the C struct is at least as big as the Rust one.
+ */
+ uint8_t padding_for_rust[16];
};
DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v2 3/3] rust: pl011: Check size of state struct at compile time
2025-03-21 11:25 [PATCH v2 0/3] rust: Fix PL011State size mismatch assert Peter Maydell
2025-03-21 11:25 ` [PATCH v2 1/3] rust: assertions: add static_assert Peter Maydell
2025-03-21 11:25 ` [PATCH v2 2/3] hw/char/pl011: Pad PL011State struct to same size as Rust impl Peter Maydell
@ 2025-03-21 11:25 ` Peter Maydell
2 siblings, 0 replies; 4+ messages in thread
From: Peter Maydell @ 2025-03-21 11:25 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-rust, Paolo Bonzini, Zhao Liu
The PL011 device's C implementation exposes its PL011State struct to
users of the device, and one common usage pattern is to embed that
struct into the user's own state struct. (The internals of the
struct are technically visible to the C user of the device, but in
practice are treated as implementation details.)
This means that the Rust version of the state struct must not be
larger than the C version's struct; otherwise it will trip a runtime
assertion in object_initialize_type() when the C user attempts to
in-place initialize the type.
Add a compile-time assertion on the Rust side, so that if we
accidentally make the Rust device state larger we know immediately
that we need to expand the padding in the C version of the struct.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
v1->v2: fix alpha order in 'use'; use 'size_of' and
'qemu_api::bindings::PL011State rather than 'mem::size_of'
and 'bindings::PL011State'.
---
rust/wrapper.h | 1 +
rust/hw/char/pl011/src/device.rs | 9 ++++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/rust/wrapper.h b/rust/wrapper.h
index d927ad6799d..d4fec546571 100644
--- a/rust/wrapper.h
+++ b/rust/wrapper.h
@@ -65,3 +65,4 @@ typedef enum memory_order {
#include "exec/memattrs.h"
#include "qemu/timer.h"
#include "exec/address-spaces.h"
+#include "hw/char/pl011.h"
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index f137b49feaf..bf88e0b00a0 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,7 +2,7 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{ffi::CStr, ptr::addr_of_mut};
+use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut};
use qemu_api::{
chardev::{CharBackend, Chardev, Event},
@@ -12,6 +12,7 @@
prelude::*,
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
qom::{ObjectImpl, Owned, ParentField},
+ static_assert,
sysbus::{SysBusDevice, SysBusDeviceImpl},
vmstate::VMStateDescription,
};
@@ -124,6 +125,12 @@ pub struct PL011State {
pub migrate_clock: bool,
}
+// Some C users of this device embed its state struct into their own
+// structs, so the size of the Rust version must not be any larger
+// than the size of the C one. If this assert triggers you need to
+// expand the padding_for_rust[] array in the C PL011State struct.
+static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>());
+
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
#[repr(C)]
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread