mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-10 19:14:58 -06:00
* scripts: dump stdin on meson-buildoptions error
* rust: introduce qemu_api::cell::Opaque<> * rust: express pinning requirements for timers * rust: hpet: decode HPET registers into enums * rust: cell: add full example of declaring a SysBusDevice * rust: qom: remove operations on &mut -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmfNbXwUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroNjpwf+ODnG0XzHt7LSag695zs5fVLK353m vLAHJ0bsmHoR4V+jEc+eaY7esDx5TLB9SRX/NvDsumJ9xnGYxXVn8Ti5GNHpa/xd qSReB6X3E8fqG5e3AffUJGJnxrD8dHJ733RsyJBZqJc9sWkUnSiEBb5lGu7br6oC fFyfiGweYboQ4AsiQUDtEN+tQsTWNkdThYEzq+dpnZrDJHNnw5e/rRwmqCUnEsLU PfwhrOGJ3OkIUtdgHStuNfiN9sqjXV5DXmZVa9L2We8FEQdkhBzg3TC0ez0gFG/1 W0P6JwfWk9Z+y/ERxkaycSXmabM0zUiFF1UJNgKEXp5iuPnRFC82OtRSUg== =de1b -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * scripts: dump stdin on meson-buildoptions error * rust: introduce qemu_api::cell::Opaque<> * rust: express pinning requirements for timers * rust: hpet: decode HPET registers into enums * rust: cell: add full example of declaring a SysBusDevice * rust: qom: remove operations on &mut # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmfNbXwUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroNjpwf+ODnG0XzHt7LSag695zs5fVLK353m # vLAHJ0bsmHoR4V+jEc+eaY7esDx5TLB9SRX/NvDsumJ9xnGYxXVn8Ti5GNHpa/xd # qSReB6X3E8fqG5e3AffUJGJnxrD8dHJ733RsyJBZqJc9sWkUnSiEBb5lGu7br6oC # fFyfiGweYboQ4AsiQUDtEN+tQsTWNkdThYEzq+dpnZrDJHNnw5e/rRwmqCUnEsLU # PfwhrOGJ3OkIUtdgHStuNfiN9sqjXV5DXmZVa9L2We8FEQdkhBzg3TC0ez0gFG/1 # W0P6JwfWk9Z+y/ERxkaycSXmabM0zUiFF1UJNgKEXp5iuPnRFC82OtRSUg== # =de1b # -----END PGP SIGNATURE----- # gpg: Signature made Sun 09 Mar 2025 18:29:16 HKT # 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' of https://gitlab.com/bonzini/qemu: (25 commits) rust: pl011: Allow NULL chardev argument to pl011_create() meson.build: default to -gsplit-dwarf for debug info rust: qom: remove operations on &mut rust: cell: add full example of declaring a SysBusDevice rust: hpet: decode HPET registers into enums rust: pl011: pass around registers::Data rust: pl011: switch to safe chardev operation rust: pl011: clean up visibilities of callbacks rust: pl011: move register definitions out of lib.rs rust: chardev: provide basic bindings to character devices rust: bindings: remove more unnecessary Send/Sync impls rust: chardev: wrap Chardev with Opaque<> rust: memory: wrap MemoryRegion with Opaque<> rust: sysbus: wrap SysBusDevice with Opaque<> rust: hpet: do not access fields of SysBusDevice rust: qdev: wrap Clock and DeviceState with Opaque<> rust: qom: wrap Object with Opaque<> rust: irq: wrap IRQState with Opaque<> rust: timer: wrap QEMUTimer with Opaque<> and express pinning requirements rust: hpet: embed Timer without the Option and Box indirection ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
1843a0c01d
25 changed files with 1551 additions and 1010 deletions
|
@ -296,15 +296,35 @@ of ``&mut self``; access to internal fields must use *interior mutability*
|
|||
to go from a shared reference to a ``&mut``.
|
||||
|
||||
Whenever C code provides you with an opaque ``void *``, avoid converting it
|
||||
to a Rust mutable reference, and use a shared reference instead. Rust code
|
||||
will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which
|
||||
enforce that locking rules for the "Big QEMU Lock" are respected. These cell
|
||||
types are also known to the ``vmstate`` crate, which is able to "look inside"
|
||||
them when building an in-memory representation of a ``struct``'s layout.
|
||||
Note that the same is not true of a ``RefCell`` or ``Mutex``.
|
||||
to a Rust mutable reference, and use a shared reference instead. The
|
||||
``qemu_api::cell`` module provides wrappers that can be used to tell the
|
||||
Rust compiler about interior mutability, and optionally to enforce locking
|
||||
rules for the "Big QEMU Lock". In the future, similar cell types might
|
||||
also be provided for ``AioContext``-based locking as well.
|
||||
|
||||
In the future, similar cell types might also be provided for ``AioContext``-based
|
||||
locking as well.
|
||||
In particular, device code will usually rely on the ``BqlRefCell`` and
|
||||
``BqlCell`` type to ensure that data is accessed correctly under the
|
||||
"Big QEMU Lock". These cell types are also known to the ``vmstate``
|
||||
crate, which is able to "look inside" them when building an in-memory
|
||||
representation of a ``struct``'s layout. Note that the same is not true
|
||||
of a ``RefCell`` or ``Mutex``.
|
||||
|
||||
Bindings code instead will usually use the ``Opaque`` type, which hides
|
||||
the contents of the underlying struct and can be easily converted to
|
||||
a raw pointer, for use in calls to C functions. It can be used for
|
||||
example as follows::
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct Object(Opaque<bindings::Object>);
|
||||
|
||||
where the special ``derive`` macro provides useful methods such as
|
||||
``from_raw``, ``as_ptr`, ``as_mut_ptr`` and ``raw_get``. The bindings will
|
||||
then manually check for the big QEMU lock with assertions, which allows
|
||||
the wrapper to be declared thread-safe::
|
||||
|
||||
unsafe impl Send for Object {}
|
||||
unsafe impl Sync for Object {}
|
||||
|
||||
Writing bindings to C code
|
||||
''''''''''''''''''''''''''
|
||||
|
|
15
meson.build
15
meson.build
|
@ -601,6 +601,10 @@ if get_option('tsan')
|
|||
qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags
|
||||
endif
|
||||
|
||||
if get_option('debug') and get_option('split_debug')
|
||||
qemu_cflags += '-gsplit-dwarf'
|
||||
endif
|
||||
|
||||
# Detect support for PT_GNU_RELRO + DT_BIND_NOW.
|
||||
# The combination is known as "full relro", because .got.plt is read-only too.
|
||||
qemu_ldflags += cc.get_supported_link_arguments('-Wl,-z,relro', '-Wl,-z,now')
|
||||
|
@ -4015,7 +4019,7 @@ libchardev = static_library('chardev', chardev_ss.sources() + genh,
|
|||
build_by_default: false)
|
||||
|
||||
chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false),
|
||||
dependencies: chardev_ss.dependencies())
|
||||
dependencies: [chardev_ss.dependencies(), io])
|
||||
|
||||
hwcore_ss = hwcore_ss.apply({})
|
||||
libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh,
|
||||
|
@ -4100,13 +4104,6 @@ if have_rust
|
|||
foreach enum : c_bitfields
|
||||
bindgen_args += ['--bitfield-enum', enum]
|
||||
endforeach
|
||||
c_nocopy = [
|
||||
'QEMUTimer',
|
||||
]
|
||||
# Used to customize Drop trait
|
||||
foreach struct : c_nocopy
|
||||
bindgen_args += ['--no-copy', struct]
|
||||
endforeach
|
||||
|
||||
# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
|
||||
#
|
||||
|
@ -4590,6 +4587,8 @@ if have_rust
|
|||
summary_info += {'bindgen': bindgen.full_path()}
|
||||
summary_info += {'bindgen version': bindgen.version()}
|
||||
endif
|
||||
# option_cflags is purely for the summary display, meson will pass
|
||||
# -g/-O options directly
|
||||
option_cflags = (get_option('debug') ? ['-g'] : [])
|
||||
if get_option('optimization') != 'plain'
|
||||
option_cflags += ['-O' + get_option('optimization')]
|
||||
|
|
|
@ -362,6 +362,8 @@ option('debug_mutex', type: 'boolean', value: false,
|
|||
description: 'mutex debugging support')
|
||||
option('debug_stack_usage', type: 'boolean', value: false,
|
||||
description: 'measure coroutine stack usage')
|
||||
option('split_debug', type: 'boolean', value: true,
|
||||
description: 'split debug info from object files')
|
||||
option('qom_cast_debug', type: 'boolean', value: true,
|
||||
description: 'cast debugging support')
|
||||
option('slirp_smbd', type : 'feature', value : 'auto',
|
||||
|
|
|
@ -37,6 +37,8 @@ result_unit_err = "allow"
|
|||
should_implement_trait = "deny"
|
||||
# can be for a reason, e.g. in callbacks
|
||||
unused_self = "allow"
|
||||
# common in device crates
|
||||
upper_case_acronyms = "allow"
|
||||
|
||||
# default-allow lints
|
||||
as_ptr_cast_mut = "deny"
|
||||
|
|
|
@ -2,18 +2,10 @@
|
|||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
os::raw::{c_int, c_void},
|
||||
ptr::{addr_of, addr_of_mut, NonNull},
|
||||
};
|
||||
use std::{ffi::CStr, ptr::addr_of_mut};
|
||||
|
||||
use qemu_api::{
|
||||
bindings::{
|
||||
qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers,
|
||||
qemu_chr_fe_write_all, CharBackend, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK,
|
||||
},
|
||||
chardev::Chardev,
|
||||
chardev::{CharBackend, Chardev, Event},
|
||||
impl_vmstate_forward,
|
||||
irq::{IRQState, InterruptSource},
|
||||
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
|
||||
|
@ -26,10 +18,13 @@ use qemu_api::{
|
|||
|
||||
use crate::{
|
||||
device_class,
|
||||
registers::{self, Interrupt},
|
||||
RegisterOffset,
|
||||
registers::{self, Interrupt, RegisterOffset},
|
||||
};
|
||||
|
||||
// TODO: You must disable the UART before any of the control registers are
|
||||
// reprogrammed. When the UART is disabled in the middle of transmission or
|
||||
// reception, it completes the current character before stopping
|
||||
|
||||
/// Integer Baud Rate Divider, `UARTIBRD`
|
||||
const IBRD_MASK: u32 = 0xffff;
|
||||
|
||||
|
@ -232,14 +227,14 @@ impl PL011Registers {
|
|||
&mut self,
|
||||
offset: RegisterOffset,
|
||||
value: u32,
|
||||
char_backend: *mut CharBackend,
|
||||
char_backend: &CharBackend,
|
||||
) -> bool {
|
||||
// eprintln!("write offset {offset} value {value}");
|
||||
use RegisterOffset::*;
|
||||
match offset {
|
||||
DR => {
|
||||
// interrupts always checked
|
||||
let _ = self.loopback_tx(value);
|
||||
let _ = self.loopback_tx(value.into());
|
||||
self.int_level |= Interrupt::TX.0;
|
||||
return true;
|
||||
}
|
||||
|
@ -266,17 +261,9 @@ impl PL011Registers {
|
|||
self.reset_tx_fifo();
|
||||
}
|
||||
let update = (self.line_control.send_break() != new_val.send_break()) && {
|
||||
let mut break_enable: c_int = new_val.send_break().into();
|
||||
// SAFETY: self.char_backend is a valid CharBackend instance after it's been
|
||||
// initialized in realize().
|
||||
unsafe {
|
||||
qemu_chr_fe_ioctl(
|
||||
char_backend,
|
||||
CHR_IOCTL_SERIAL_SET_BREAK as i32,
|
||||
addr_of_mut!(break_enable).cast::<c_void>(),
|
||||
);
|
||||
}
|
||||
self.loopback_break(break_enable > 0)
|
||||
let break_enable = new_val.send_break();
|
||||
let _ = char_backend.send_break(break_enable);
|
||||
self.loopback_break(break_enable)
|
||||
};
|
||||
self.line_control = new_val;
|
||||
self.set_read_trigger();
|
||||
|
@ -314,7 +301,7 @@ impl PL011Registers {
|
|||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn loopback_tx(&mut self, value: u32) -> bool {
|
||||
fn loopback_tx(&mut self, value: registers::Data) -> bool {
|
||||
// Caveat:
|
||||
//
|
||||
// In real hardware, TX loopback happens at the serial-bit level
|
||||
|
@ -383,7 +370,7 @@ impl PL011Registers {
|
|||
}
|
||||
|
||||
fn loopback_break(&mut self, enable: bool) -> bool {
|
||||
enable && self.loopback_tx(registers::Data::BREAK.into())
|
||||
enable && self.loopback_tx(registers::Data::BREAK)
|
||||
}
|
||||
|
||||
fn set_read_trigger(&mut self) {
|
||||
|
@ -442,11 +429,11 @@ impl PL011Registers {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn put_fifo(&mut self, value: u32) -> bool {
|
||||
pub fn put_fifo(&mut self, value: registers::Data) -> bool {
|
||||
let depth = self.fifo_depth();
|
||||
assert!(depth > 0);
|
||||
let slot = (self.read_pos + self.read_count) & (depth - 1);
|
||||
self.read_fifo[slot] = registers::Data::from(value);
|
||||
self.read_fifo[slot] = value;
|
||||
self.read_count += 1;
|
||||
self.flags.set_receive_fifo_empty(false);
|
||||
if self.read_count == depth {
|
||||
|
@ -534,7 +521,7 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, offset: hwaddr, _size: u32) -> u64 {
|
||||
fn read(&self, offset: hwaddr, _size: u32) -> u64 {
|
||||
match RegisterOffset::try_from(offset) {
|
||||
Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
|
||||
let device_id = self.get_class().device_id;
|
||||
|
@ -548,37 +535,30 @@ impl PL011State {
|
|||
let (update_irq, result) = self.regs.borrow_mut().read(field);
|
||||
if update_irq {
|
||||
self.update();
|
||||
unsafe {
|
||||
qemu_chr_fe_accept_input(addr_of!(self.char_backend) as *mut _);
|
||||
}
|
||||
self.char_backend.accept_input();
|
||||
}
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, offset: hwaddr, value: u64, _size: u32) {
|
||||
fn write(&self, offset: hwaddr, value: u64, _size: u32) {
|
||||
let mut update_irq = false;
|
||||
if let Ok(field) = RegisterOffset::try_from(offset) {
|
||||
// qemu_chr_fe_write_all() calls into the can_receive
|
||||
// callback, so handle writes before entering PL011Registers.
|
||||
if field == RegisterOffset::DR {
|
||||
// ??? Check if transmitter is enabled.
|
||||
let ch: u8 = value as u8;
|
||||
// SAFETY: char_backend is a valid CharBackend instance after it's been
|
||||
// initialized in realize().
|
||||
let ch: [u8; 1] = [value as u8];
|
||||
// XXX this blocks entire thread. Rewrite to use
|
||||
// qemu_chr_fe_write and background I/O callbacks
|
||||
unsafe {
|
||||
qemu_chr_fe_write_all(addr_of!(self.char_backend) as *mut _, &ch, 1);
|
||||
}
|
||||
let _ = self.char_backend.write_all(&ch);
|
||||
}
|
||||
|
||||
update_irq = self.regs.borrow_mut().write(
|
||||
field,
|
||||
value as u32,
|
||||
addr_of!(self.char_backend) as *mut _,
|
||||
);
|
||||
update_irq = self
|
||||
.regs
|
||||
.borrow_mut()
|
||||
.write(field, value as u32, &self.char_backend);
|
||||
} else {
|
||||
eprintln!("write bad offset {offset} value {value}");
|
||||
}
|
||||
|
@ -587,15 +567,19 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn can_receive(&self) -> bool {
|
||||
// trace_pl011_can_receive(s->lcr, s->read_count, r);
|
||||
fn can_receive(&self) -> u32 {
|
||||
let regs = self.regs.borrow();
|
||||
regs.read_count < regs.fifo_depth()
|
||||
// trace_pl011_can_receive(s->lcr, s->read_count, r);
|
||||
u32::from(regs.read_count < regs.fifo_depth())
|
||||
}
|
||||
|
||||
pub fn receive(&self, ch: u32) {
|
||||
fn receive(&self, buf: &[u8]) {
|
||||
if buf.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut regs = self.regs.borrow_mut();
|
||||
let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch);
|
||||
let c: u32 = buf[0].into();
|
||||
let update_irq = !regs.loopback_enabled() && regs.put_fifo(c.into());
|
||||
// Release the BqlRefCell before calling self.update()
|
||||
drop(regs);
|
||||
|
||||
|
@ -604,11 +588,11 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn event(&self, event: QEMUChrEvent) {
|
||||
fn event(&self, event: Event) {
|
||||
let mut update_irq = false;
|
||||
let mut regs = self.regs.borrow_mut();
|
||||
if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() {
|
||||
update_irq = regs.put_fifo(registers::Data::BREAK.into());
|
||||
if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() {
|
||||
update_irq = regs.put_fifo(registers::Data::BREAK);
|
||||
}
|
||||
// Release the BqlRefCell before calling self.update()
|
||||
drop(regs);
|
||||
|
@ -618,28 +602,16 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn realize(&self) {
|
||||
// SAFETY: self.char_backend has the correct size and alignment for a
|
||||
// CharBackend object, and its callbacks are of the correct types.
|
||||
unsafe {
|
||||
qemu_chr_fe_set_handlers(
|
||||
addr_of!(self.char_backend) as *mut CharBackend,
|
||||
Some(pl011_can_receive),
|
||||
Some(pl011_receive),
|
||||
Some(pl011_event),
|
||||
None,
|
||||
addr_of!(*self).cast::<c_void>() as *mut c_void,
|
||||
core::ptr::null_mut(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
fn realize(&self) {
|
||||
self.char_backend
|
||||
.enable_handlers(self, Self::can_receive, Self::receive, Self::event);
|
||||
}
|
||||
|
||||
pub fn reset_hold(&self, _type: ResetType) {
|
||||
fn reset_hold(&self, _type: ResetType) {
|
||||
self.regs.borrow_mut().reset();
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
fn update(&self) {
|
||||
let regs = self.regs.borrow();
|
||||
let flags = regs.int_level & regs.int_enabled;
|
||||
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
|
||||
|
@ -663,43 +635,6 @@ const IRQMASK: [u32; 6] = [
|
|||
Interrupt::E.0,
|
||||
];
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// 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.
|
||||
pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
|
||||
let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
|
||||
unsafe { state.as_ref().can_receive().into() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// The buffer and size arguments must also be valid.
|
||||
pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) {
|
||||
let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
|
||||
unsafe {
|
||||
if size > 0 {
|
||||
debug_assert!(!buf.is_null());
|
||||
state.as_ref().receive(u32::from(buf.read_volatile()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// 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.
|
||||
pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
|
||||
let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
|
||||
unsafe { state.as_ref().event(event) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// We expect the FFI user of this function to pass a valid pointer for `chr`
|
||||
|
@ -713,10 +648,12 @@ pub unsafe extern "C" fn pl011_create(
|
|||
// SAFETY: The callers promise that they have owned references.
|
||||
// They do not gift them to pl011_create, so use `Owned::from`.
|
||||
let irq = unsafe { Owned::<IRQState>::from(&*irq) };
|
||||
let chr = unsafe { Owned::<Chardev>::from(&*chr) };
|
||||
|
||||
let dev = PL011State::new();
|
||||
if !chr.is_null() {
|
||||
let chr = unsafe { Owned::<Chardev>::from(&*chr) };
|
||||
dev.prop_set_chr("chardev", &chr);
|
||||
}
|
||||
dev.sysbus_realize();
|
||||
dev.mmio_map(0, addr);
|
||||
dev.connect_irq(0, &irq);
|
||||
|
|
|
@ -12,522 +12,13 @@
|
|||
//! See [`PL011State`](crate::device::PL011State) for the device model type and
|
||||
//! the [`registers`] module for register types.
|
||||
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use qemu_api::c_str;
|
||||
|
||||
mod device;
|
||||
mod device_class;
|
||||
mod registers;
|
||||
|
||||
pub use device::pl011_create;
|
||||
|
||||
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.
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G, Table 3-1 p.3-3
|
||||
#[doc(alias = "offset")]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u64)]
|
||||
#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
|
||||
enum RegisterOffset {
|
||||
/// Data Register
|
||||
///
|
||||
/// A write to this register initiates the actual data transmission
|
||||
#[doc(alias = "UARTDR")]
|
||||
DR = 0x000,
|
||||
/// Receive Status Register or Error Clear Register
|
||||
#[doc(alias = "UARTRSR")]
|
||||
#[doc(alias = "UARTECR")]
|
||||
RSR = 0x004,
|
||||
/// Flag Register
|
||||
///
|
||||
/// A read of this register shows if transmission is complete
|
||||
#[doc(alias = "UARTFR")]
|
||||
FR = 0x018,
|
||||
/// Fractional Baud Rate Register
|
||||
///
|
||||
/// responsible for baud rate speed
|
||||
#[doc(alias = "UARTFBRD")]
|
||||
FBRD = 0x028,
|
||||
/// `IrDA` Low-Power Counter Register
|
||||
#[doc(alias = "UARTILPR")]
|
||||
ILPR = 0x020,
|
||||
/// Integer Baud Rate Register
|
||||
///
|
||||
/// Responsible for baud rate speed
|
||||
#[doc(alias = "UARTIBRD")]
|
||||
IBRD = 0x024,
|
||||
/// line control register (data frame format)
|
||||
#[doc(alias = "UARTLCR_H")]
|
||||
LCR_H = 0x02C,
|
||||
/// Toggle UART, transmission or reception
|
||||
#[doc(alias = "UARTCR")]
|
||||
CR = 0x030,
|
||||
/// Interrupt FIFO Level Select Register
|
||||
#[doc(alias = "UARTIFLS")]
|
||||
FLS = 0x034,
|
||||
/// Interrupt Mask Set/Clear Register
|
||||
#[doc(alias = "UARTIMSC")]
|
||||
IMSC = 0x038,
|
||||
/// Raw Interrupt Status Register
|
||||
#[doc(alias = "UARTRIS")]
|
||||
RIS = 0x03C,
|
||||
/// Masked Interrupt Status Register
|
||||
#[doc(alias = "UARTMIS")]
|
||||
MIS = 0x040,
|
||||
/// Interrupt Clear Register
|
||||
#[doc(alias = "UARTICR")]
|
||||
ICR = 0x044,
|
||||
/// DMA control Register
|
||||
#[doc(alias = "UARTDMACR")]
|
||||
DMACR = 0x048,
|
||||
///// Reserved, offsets `0x04C` to `0x07C`.
|
||||
//Reserved = 0x04C,
|
||||
}
|
||||
|
||||
mod registers {
|
||||
//! Device registers exposed as typed structs which are backed by arbitrary
|
||||
//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
|
||||
use bilge::prelude::*;
|
||||
use qemu_api::impl_vmstate_bitsized;
|
||||
|
||||
/// Receive Status Register / Data Register common error bits
|
||||
///
|
||||
/// The `UARTRSR` register is updated only when a read occurs
|
||||
/// from the `UARTDR` register with the same status information
|
||||
/// that can also be obtained by reading the `UARTDR` register
|
||||
#[bitsize(8)]
|
||||
#[derive(Clone, Copy, Default, DebugBits, FromBits)]
|
||||
pub struct Errors {
|
||||
pub framing_error: bool,
|
||||
pub parity_error: bool,
|
||||
pub break_error: bool,
|
||||
pub overrun_error: bool,
|
||||
_reserved_unpredictable: u4,
|
||||
}
|
||||
|
||||
// TODO: FIFO Mode has different semantics
|
||||
/// Data Register, `UARTDR`
|
||||
///
|
||||
/// The `UARTDR` register is the data register.
|
||||
///
|
||||
/// For words to be transmitted:
|
||||
///
|
||||
/// - if the FIFOs are enabled, data written to this location is pushed onto
|
||||
/// the transmit
|
||||
/// FIFO
|
||||
/// - if the FIFOs are not enabled, data is stored in the transmitter
|
||||
/// holding register (the
|
||||
/// bottom word of the transmit FIFO).
|
||||
///
|
||||
/// The write operation initiates transmission from the UART. The data is
|
||||
/// prefixed with a start bit, appended with the appropriate parity bit
|
||||
/// (if parity is enabled), and a stop bit. The resultant word is then
|
||||
/// transmitted.
|
||||
///
|
||||
/// For received words:
|
||||
///
|
||||
/// - if the FIFOs are enabled, the data byte and the 4-bit status (break,
|
||||
/// frame, parity,
|
||||
/// and overrun) is pushed onto the 12-bit wide receive FIFO
|
||||
/// - if the FIFOs are not enabled, the data byte and status are stored in
|
||||
/// the receiving
|
||||
/// holding register (the bottom word of the receive FIFO).
|
||||
///
|
||||
/// The received data byte is read by performing reads from the `UARTDR`
|
||||
/// register along with the corresponding status information. The status
|
||||
/// information can also be read by a read of the `UARTRSR/UARTECR`
|
||||
/// register.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// You must disable the UART before any of the control registers are
|
||||
/// reprogrammed. When the UART is disabled in the middle of
|
||||
/// transmission or reception, it completes the current character before
|
||||
/// stopping.
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G 3.3.1 Data Register, UARTDR
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, Default, DebugBits, FromBits)]
|
||||
#[doc(alias = "UARTDR")]
|
||||
pub struct Data {
|
||||
pub data: u8,
|
||||
pub errors: Errors,
|
||||
_reserved: u16,
|
||||
}
|
||||
impl_vmstate_bitsized!(Data);
|
||||
|
||||
impl Data {
|
||||
// bilge is not very const-friendly, unfortunately
|
||||
pub const BREAK: Self = Self { value: 1 << 10 };
|
||||
}
|
||||
|
||||
// TODO: FIFO Mode has different semantics
|
||||
/// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR`
|
||||
///
|
||||
/// The UARTRSR/UARTECR register is the receive status register/error clear
|
||||
/// register. Receive status can also be read from the `UARTRSR`
|
||||
/// register. If the status is read from this register, then the status
|
||||
/// information for break, framing and parity corresponds to the
|
||||
/// data character read from the [Data register](Data), `UARTDR` prior to
|
||||
/// reading the UARTRSR register. The status information for overrun is
|
||||
/// set immediately when an overrun condition occurs.
|
||||
///
|
||||
///
|
||||
/// # Note
|
||||
/// The received data character must be read first from the [Data
|
||||
/// Register](Data), `UARTDR` before reading the error status associated
|
||||
/// with that data character from the `UARTRSR` register. This read
|
||||
/// sequence cannot be reversed, because the `UARTRSR` register is
|
||||
/// updated only when a read occurs from the `UARTDR` register. However,
|
||||
/// the status information can also be obtained by reading the `UARTDR`
|
||||
/// register
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register,
|
||||
/// UARTRSR/UARTECR
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
pub struct ReceiveStatusErrorClear {
|
||||
pub errors: Errors,
|
||||
_reserved_unpredictable: u24,
|
||||
}
|
||||
impl_vmstate_bitsized!(ReceiveStatusErrorClear);
|
||||
|
||||
impl ReceiveStatusErrorClear {
|
||||
pub fn set_from_data(&mut self, data: Data) {
|
||||
self.set_errors(data.errors());
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
// All the bits are cleared to 0 on reset.
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReceiveStatusErrorClear {
|
||||
fn default() -> Self {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
/// Flag Register, `UARTFR`
|
||||
#[doc(alias = "UARTFR")]
|
||||
pub struct Flags {
|
||||
/// CTS Clear to send. This bit is the complement of the UART clear to
|
||||
/// send, `nUARTCTS`, modem status input. That is, the bit is 1
|
||||
/// when `nUARTCTS` is LOW.
|
||||
pub clear_to_send: bool,
|
||||
/// DSR Data set ready. This bit is the complement of the UART data set
|
||||
/// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when
|
||||
/// `nUARTDSR` is LOW.
|
||||
pub data_set_ready: bool,
|
||||
/// DCD Data carrier detect. This bit is the complement of the UART data
|
||||
/// carrier detect, `nUARTDCD`, modem status input. That is, the bit is
|
||||
/// 1 when `nUARTDCD` is LOW.
|
||||
pub data_carrier_detect: bool,
|
||||
/// BUSY UART busy. If this bit is set to 1, the UART is busy
|
||||
/// transmitting data. This bit remains set until the complete
|
||||
/// byte, including all the stop bits, has been sent from the
|
||||
/// shift register. This bit is set as soon as the transmit FIFO
|
||||
/// becomes non-empty, regardless of whether the UART is enabled
|
||||
/// or not.
|
||||
pub busy: bool,
|
||||
/// RXFE Receive FIFO empty. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the UARTLCR_H register. If the FIFO
|
||||
/// is disabled, this bit is set when the receive holding
|
||||
/// register is empty. If the FIFO is enabled, the RXFE bit is
|
||||
/// set when the receive FIFO is empty.
|
||||
pub receive_fifo_empty: bool,
|
||||
/// TXFF Transmit FIFO full. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the UARTLCR_H register. If the FIFO
|
||||
/// is disabled, this bit is set when the transmit holding
|
||||
/// register is full. If the FIFO is enabled, the TXFF bit is
|
||||
/// set when the transmit FIFO is full.
|
||||
pub transmit_fifo_full: bool,
|
||||
/// RXFF Receive FIFO full. The meaning of this bit depends on the state
|
||||
/// of the FEN bit in the UARTLCR_H register. If the FIFO is
|
||||
/// disabled, this bit is set when the receive holding register
|
||||
/// is full. If the FIFO is enabled, the RXFF bit is set when
|
||||
/// the receive FIFO is full.
|
||||
pub receive_fifo_full: bool,
|
||||
/// Transmit FIFO empty. The meaning of this bit depends on the state of
|
||||
/// the FEN bit in the [Line Control register](LineControl),
|
||||
/// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the
|
||||
/// transmit holding register is empty. If the FIFO is enabled,
|
||||
/// the TXFE bit is set when the transmit FIFO is empty. This
|
||||
/// bit does not indicate if there is data in the transmit shift
|
||||
/// register.
|
||||
pub transmit_fifo_empty: bool,
|
||||
/// `RI`, is `true` when `nUARTRI` is `LOW`.
|
||||
pub ring_indicator: bool,
|
||||
_reserved_zero_no_modify: u23,
|
||||
}
|
||||
impl_vmstate_bitsized!(Flags);
|
||||
|
||||
impl Flags {
|
||||
pub fn reset(&mut self) {
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Flags {
|
||||
fn default() -> Self {
|
||||
let mut ret: Self = 0.into();
|
||||
// After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1
|
||||
ret.set_receive_fifo_empty(true);
|
||||
ret.set_transmit_fifo_empty(true);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
/// Line Control Register, `UARTLCR_H`
|
||||
#[doc(alias = "UARTLCR_H")]
|
||||
pub struct LineControl {
|
||||
/// BRK Send break.
|
||||
///
|
||||
/// If this bit is set to `1`, a low-level is continually output on the
|
||||
/// `UARTTXD` output, after completing transmission of the
|
||||
/// current character. For the proper execution of the break command,
|
||||
/// the software must set this bit for at least two complete
|
||||
/// frames. For normal use, this bit must be cleared to `0`.
|
||||
pub send_break: bool,
|
||||
/// 1 PEN Parity enable:
|
||||
///
|
||||
/// - 0 = parity is disabled and no parity bit added to the data frame
|
||||
/// - 1 = parity checking and generation is enabled.
|
||||
///
|
||||
/// See Table 3-11 on page 3-14 for the parity truth table.
|
||||
pub parity_enabled: bool,
|
||||
/// EPS Even parity select. Controls the type of parity the UART uses
|
||||
/// during transmission and reception:
|
||||
/// - 0 = odd parity. The UART generates or checks for an odd number of
|
||||
/// 1s in the data and parity bits.
|
||||
/// - 1 = even parity. The UART generates or checks for an even number
|
||||
/// of 1s in the data and parity bits.
|
||||
/// This bit has no effect when the `PEN` bit disables parity checking
|
||||
/// and generation. See Table 3-11 on page 3-14 for the parity
|
||||
/// truth table.
|
||||
pub parity: Parity,
|
||||
/// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits
|
||||
/// are transmitted at the end of the frame. The receive
|
||||
/// logic does not check for two stop bits being received.
|
||||
pub two_stops_bits: bool,
|
||||
/// FEN Enable FIFOs:
|
||||
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
|
||||
/// 1-byte-deep holding registers 1 = transmit and receive FIFO
|
||||
/// buffers are enabled (FIFO mode).
|
||||
pub fifos_enabled: Mode,
|
||||
/// WLEN Word length. These bits indicate the number of data bits
|
||||
/// transmitted or received in a frame as follows: b11 = 8 bits
|
||||
/// b10 = 7 bits
|
||||
/// b01 = 6 bits
|
||||
/// b00 = 5 bits.
|
||||
pub word_length: WordLength,
|
||||
/// 7 SPS Stick parity select.
|
||||
/// 0 = stick parity is disabled
|
||||
/// 1 = either:
|
||||
/// • if the EPS bit is 0 then the parity bit is transmitted and checked
|
||||
/// as a 1 • if the EPS bit is 1 then the parity bit is
|
||||
/// transmitted and checked as a 0. This bit has no effect when
|
||||
/// the PEN bit disables parity checking and generation. See Table 3-11
|
||||
/// on page 3-14 for the parity truth table.
|
||||
pub sticky_parity: bool,
|
||||
/// 31:8 - Reserved, do not modify, read as zero.
|
||||
_reserved_zero_no_modify: u24,
|
||||
}
|
||||
impl_vmstate_bitsized!(LineControl);
|
||||
|
||||
impl LineControl {
|
||||
pub fn reset(&mut self) {
|
||||
// All the bits are cleared to 0 when reset.
|
||||
*self = 0.into();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LineControl {
|
||||
fn default() -> Self {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitsize(1)]
|
||||
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
|
||||
/// `EPS` "Even parity select", field of [Line Control
|
||||
/// register](LineControl).
|
||||
pub enum Parity {
|
||||
/// - 0 = odd parity. The UART generates or checks for an odd number of
|
||||
/// 1s in the data and parity bits.
|
||||
Odd = 0,
|
||||
/// - 1 = even parity. The UART generates or checks for an even number
|
||||
/// of 1s in the data and parity bits.
|
||||
Even = 1,
|
||||
}
|
||||
|
||||
#[bitsize(1)]
|
||||
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
|
||||
/// `FEN` "Enable FIFOs" or Device mode, field of [Line Control
|
||||
/// register](LineControl).
|
||||
pub enum Mode {
|
||||
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
|
||||
/// 1-byte-deep holding registers
|
||||
Character = 0,
|
||||
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
|
||||
FIFO = 1,
|
||||
}
|
||||
|
||||
#[bitsize(2)]
|
||||
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
|
||||
/// `WLEN` Word length, field of [Line Control register](LineControl).
|
||||
///
|
||||
/// These bits indicate the number of data bits transmitted or received in a
|
||||
/// frame as follows:
|
||||
pub enum WordLength {
|
||||
/// b11 = 8 bits
|
||||
_8Bits = 0b11,
|
||||
/// b10 = 7 bits
|
||||
_7Bits = 0b10,
|
||||
/// b01 = 6 bits
|
||||
_6Bits = 0b01,
|
||||
/// b00 = 5 bits.
|
||||
_5Bits = 0b00,
|
||||
}
|
||||
|
||||
/// Control Register, `UARTCR`
|
||||
///
|
||||
/// The `UARTCR` register is the control register. All the bits are cleared
|
||||
/// to `0` on reset except for bits `9` and `8` that are set to `1`.
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12
|
||||
#[bitsize(32)]
|
||||
#[doc(alias = "UARTCR")]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
pub struct Control {
|
||||
/// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled
|
||||
/// in the middle of transmission or reception, it completes the current
|
||||
/// character before stopping. 1 = the UART is enabled. Data
|
||||
/// transmission and reception occurs for either UART signals or SIR
|
||||
/// signals depending on the setting of the SIREN bit.
|
||||
pub enable_uart: bool,
|
||||
/// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT`
|
||||
/// remains LOW (no light pulse generated), and signal transitions on
|
||||
/// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is
|
||||
/// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH,
|
||||
/// in the marking state. Signal transitions on UARTRXD or modem status
|
||||
/// inputs have no effect. This bit has no effect if the UARTEN bit
|
||||
/// disables the UART.
|
||||
pub enable_sir: bool,
|
||||
/// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding
|
||||
/// mode. If this bit is cleared to 0, low-level bits are transmitted as
|
||||
/// an active high pulse with a width of 3/ 16th of the bit period. If
|
||||
/// this bit is set to 1, low-level bits are transmitted with a pulse
|
||||
/// width that is 3 times the period of the IrLPBaud16 input signal,
|
||||
/// regardless of the selected bit rate. Setting this bit uses less
|
||||
/// power, but might reduce transmission distances.
|
||||
pub sir_lowpower_irda_mode: u1,
|
||||
/// Reserved, do not modify, read as zero.
|
||||
_reserved_zero_no_modify: u4,
|
||||
/// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is
|
||||
/// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR
|
||||
/// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed
|
||||
/// through to the SIRIN path. The SIRTEST bit in the test register must
|
||||
/// be set to 1 to override the normal half-duplex SIR operation. This
|
||||
/// must be the requirement for accessing the test registers during
|
||||
/// normal operation, and SIRTEST must be cleared to 0 when loopback
|
||||
/// testing is finished. This feature reduces the amount of external
|
||||
/// coupling required during system test. If this bit is set to 1, and
|
||||
/// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the
|
||||
/// UARTRXD path. In either SIR mode or UART mode, when this bit is set,
|
||||
/// the modem outputs are also fed through to the modem inputs. This bit
|
||||
/// is cleared to 0 on reset, to disable loopback.
|
||||
pub enable_loopback: bool,
|
||||
/// `TXE` Transmit enable. If this bit is set to 1, the transmit section
|
||||
/// of the UART is enabled. Data transmission occurs for either UART
|
||||
/// signals, or SIR signals depending on the setting of the SIREN bit.
|
||||
/// When the UART is disabled in the middle of transmission, it
|
||||
/// completes the current character before stopping.
|
||||
pub enable_transmit: bool,
|
||||
/// `RXE` Receive enable. If this bit is set to 1, the receive section
|
||||
/// of the UART is enabled. Data reception occurs for either UART
|
||||
/// signals or SIR signals depending on the setting of the SIREN bit.
|
||||
/// When the UART is disabled in the middle of reception, it completes
|
||||
/// the current character before stopping.
|
||||
pub enable_receive: bool,
|
||||
/// `DTR` Data transmit ready. This bit is the complement of the UART
|
||||
/// data transmit ready, `nUARTDTR`, modem status output. That is, when
|
||||
/// the bit is programmed to a 1 then `nUARTDTR` is LOW.
|
||||
pub data_transmit_ready: bool,
|
||||
/// `RTS` Request to send. This bit is the complement of the UART
|
||||
/// request to send, `nUARTRTS`, modem status output. That is, when the
|
||||
/// bit is programmed to a 1 then `nUARTRTS` is LOW.
|
||||
pub request_to_send: bool,
|
||||
/// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`)
|
||||
/// modem status output. That is, when the bit is programmed to a 1 the
|
||||
/// output is 0. For DTE this can be used as Data Carrier Detect (DCD).
|
||||
pub out_1: bool,
|
||||
/// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`)
|
||||
/// modem status output. That is, when the bit is programmed to a 1, the
|
||||
/// output is 0. For DTE this can be used as Ring Indicator (RI).
|
||||
pub out_2: bool,
|
||||
/// `RTSEn` RTS hardware flow control enable. If this bit is set to 1,
|
||||
/// RTS hardware flow control is enabled. Data is only requested when
|
||||
/// there is space in the receive FIFO for it to be received.
|
||||
pub rts_hardware_flow_control_enable: bool,
|
||||
/// `CTSEn` CTS hardware flow control enable. If this bit is set to 1,
|
||||
/// CTS hardware flow control is enabled. Data is only transmitted when
|
||||
/// the `nUARTCTS` signal is asserted.
|
||||
pub cts_hardware_flow_control_enable: bool,
|
||||
/// 31:16 - Reserved, do not modify, read as zero.
|
||||
_reserved_zero_no_modify2: u16,
|
||||
}
|
||||
impl_vmstate_bitsized!(Control);
|
||||
|
||||
impl Control {
|
||||
pub fn reset(&mut self) {
|
||||
*self = 0.into();
|
||||
self.set_enable_receive(true);
|
||||
self.set_enable_transmit(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Control {
|
||||
fn default() -> Self {
|
||||
let mut ret: Self = 0.into();
|
||||
ret.reset();
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
|
||||
pub struct Interrupt(pub u32);
|
||||
|
||||
impl Interrupt {
|
||||
pub const OE: Self = Self(1 << 10);
|
||||
pub const BE: Self = Self(1 << 9);
|
||||
pub const PE: Self = Self(1 << 8);
|
||||
pub const FE: Self = Self(1 << 7);
|
||||
pub const RT: Self = Self(1 << 6);
|
||||
pub const TX: Self = Self(1 << 5);
|
||||
pub const RX: Self = Self(1 << 4);
|
||||
pub const DSR: Self = Self(1 << 3);
|
||||
pub const DCD: Self = Self(1 << 2);
|
||||
pub const CTS: Self = Self(1 << 1);
|
||||
pub const RI: Self = Self(1 << 0);
|
||||
|
||||
pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0);
|
||||
pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: You must disable the UART before any of the control registers are
|
||||
// reprogrammed. When the UART is disabled in the middle of transmission or
|
||||
// reception, it completes the current character before stopping
|
||||
|
|
506
rust/hw/char/pl011/src/registers.rs
Normal file
506
rust/hw/char/pl011/src/registers.rs
Normal file
|
@ -0,0 +1,506 @@
|
|||
// Copyright 2024, Linaro Limited
|
||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
//! Device registers exposed as typed structs which are backed by arbitrary
|
||||
//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
|
||||
|
||||
use bilge::prelude::*;
|
||||
use qemu_api::impl_vmstate_bitsized;
|
||||
|
||||
/// Offset of each register from the base memory address of the device.
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G, Table 3-1 p.3-3
|
||||
#[doc(alias = "offset")]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u64)]
|
||||
#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
|
||||
pub enum RegisterOffset {
|
||||
/// Data Register
|
||||
///
|
||||
/// A write to this register initiates the actual data transmission
|
||||
#[doc(alias = "UARTDR")]
|
||||
DR = 0x000,
|
||||
/// Receive Status Register or Error Clear Register
|
||||
#[doc(alias = "UARTRSR")]
|
||||
#[doc(alias = "UARTECR")]
|
||||
RSR = 0x004,
|
||||
/// Flag Register
|
||||
///
|
||||
/// A read of this register shows if transmission is complete
|
||||
#[doc(alias = "UARTFR")]
|
||||
FR = 0x018,
|
||||
/// Fractional Baud Rate Register
|
||||
///
|
||||
/// responsible for baud rate speed
|
||||
#[doc(alias = "UARTFBRD")]
|
||||
FBRD = 0x028,
|
||||
/// `IrDA` Low-Power Counter Register
|
||||
#[doc(alias = "UARTILPR")]
|
||||
ILPR = 0x020,
|
||||
/// Integer Baud Rate Register
|
||||
///
|
||||
/// Responsible for baud rate speed
|
||||
#[doc(alias = "UARTIBRD")]
|
||||
IBRD = 0x024,
|
||||
/// line control register (data frame format)
|
||||
#[doc(alias = "UARTLCR_H")]
|
||||
LCR_H = 0x02C,
|
||||
/// Toggle UART, transmission or reception
|
||||
#[doc(alias = "UARTCR")]
|
||||
CR = 0x030,
|
||||
/// Interrupt FIFO Level Select Register
|
||||
#[doc(alias = "UARTIFLS")]
|
||||
FLS = 0x034,
|
||||
/// Interrupt Mask Set/Clear Register
|
||||
#[doc(alias = "UARTIMSC")]
|
||||
IMSC = 0x038,
|
||||
/// Raw Interrupt Status Register
|
||||
#[doc(alias = "UARTRIS")]
|
||||
RIS = 0x03C,
|
||||
/// Masked Interrupt Status Register
|
||||
#[doc(alias = "UARTMIS")]
|
||||
MIS = 0x040,
|
||||
/// Interrupt Clear Register
|
||||
#[doc(alias = "UARTICR")]
|
||||
ICR = 0x044,
|
||||
/// DMA control Register
|
||||
#[doc(alias = "UARTDMACR")]
|
||||
DMACR = 0x048,
|
||||
///// Reserved, offsets `0x04C` to `0x07C`.
|
||||
//Reserved = 0x04C,
|
||||
}
|
||||
|
||||
/// Receive Status Register / Data Register common error bits
|
||||
///
|
||||
/// The `UARTRSR` register is updated only when a read occurs
|
||||
/// from the `UARTDR` register with the same status information
|
||||
/// that can also be obtained by reading the `UARTDR` register
|
||||
#[bitsize(8)]
|
||||
#[derive(Clone, Copy, Default, DebugBits, FromBits)]
|
||||
pub struct Errors {
|
||||
pub framing_error: bool,
|
||||
pub parity_error: bool,
|
||||
pub break_error: bool,
|
||||
pub overrun_error: bool,
|
||||
_reserved_unpredictable: u4,
|
||||
}
|
||||
|
||||
// TODO: FIFO Mode has different semantics
|
||||
/// Data Register, `UARTDR`
|
||||
///
|
||||
/// The `UARTDR` register is the data register.
|
||||
///
|
||||
/// For words to be transmitted:
|
||||
///
|
||||
/// - if the FIFOs are enabled, data written to this location is pushed onto the
|
||||
/// transmit
|
||||
/// FIFO
|
||||
/// - if the FIFOs are not enabled, data is stored in the transmitter holding
|
||||
/// register (the
|
||||
/// bottom word of the transmit FIFO).
|
||||
///
|
||||
/// The write operation initiates transmission from the UART. The data is
|
||||
/// prefixed with a start bit, appended with the appropriate parity bit
|
||||
/// (if parity is enabled), and a stop bit. The resultant word is then
|
||||
/// transmitted.
|
||||
///
|
||||
/// For received words:
|
||||
///
|
||||
/// - if the FIFOs are enabled, the data byte and the 4-bit status (break,
|
||||
/// frame, parity,
|
||||
/// and overrun) is pushed onto the 12-bit wide receive FIFO
|
||||
/// - if the FIFOs are not enabled, the data byte and status are stored in the
|
||||
/// receiving
|
||||
/// holding register (the bottom word of the receive FIFO).
|
||||
///
|
||||
/// The received data byte is read by performing reads from the `UARTDR`
|
||||
/// register along with the corresponding status information. The status
|
||||
/// information can also be read by a read of the `UARTRSR/UARTECR`
|
||||
/// register.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// You must disable the UART before any of the control registers are
|
||||
/// reprogrammed. When the UART is disabled in the middle of
|
||||
/// transmission or reception, it completes the current character before
|
||||
/// stopping.
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G 3.3.1 Data Register, UARTDR
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, Default, DebugBits, FromBits)]
|
||||
#[doc(alias = "UARTDR")]
|
||||
pub struct Data {
|
||||
pub data: u8,
|
||||
pub errors: Errors,
|
||||
_reserved: u16,
|
||||
}
|
||||
impl_vmstate_bitsized!(Data);
|
||||
|
||||
impl Data {
|
||||
// bilge is not very const-friendly, unfortunately
|
||||
pub const BREAK: Self = Self { value: 1 << 10 };
|
||||
}
|
||||
|
||||
// TODO: FIFO Mode has different semantics
|
||||
/// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR`
|
||||
///
|
||||
/// The UARTRSR/UARTECR register is the receive status register/error clear
|
||||
/// register. Receive status can also be read from the `UARTRSR`
|
||||
/// register. If the status is read from this register, then the status
|
||||
/// information for break, framing and parity corresponds to the
|
||||
/// data character read from the [Data register](Data), `UARTDR` prior to
|
||||
/// reading the UARTRSR register. The status information for overrun is
|
||||
/// set immediately when an overrun condition occurs.
|
||||
///
|
||||
///
|
||||
/// # Note
|
||||
/// The received data character must be read first from the [Data
|
||||
/// Register](Data), `UARTDR` before reading the error status associated
|
||||
/// with that data character from the `UARTRSR` register. This read
|
||||
/// sequence cannot be reversed, because the `UARTRSR` register is
|
||||
/// updated only when a read occurs from the `UARTDR` register. However,
|
||||
/// the status information can also be obtained by reading the `UARTDR`
|
||||
/// register
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register,
|
||||
/// UARTRSR/UARTECR
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
pub struct ReceiveStatusErrorClear {
|
||||
pub errors: Errors,
|
||||
_reserved_unpredictable: u24,
|
||||
}
|
||||
impl_vmstate_bitsized!(ReceiveStatusErrorClear);
|
||||
|
||||
impl ReceiveStatusErrorClear {
|
||||
pub fn set_from_data(&mut self, data: Data) {
|
||||
self.set_errors(data.errors());
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
// All the bits are cleared to 0 on reset.
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReceiveStatusErrorClear {
|
||||
fn default() -> Self {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
/// Flag Register, `UARTFR`
|
||||
#[doc(alias = "UARTFR")]
|
||||
pub struct Flags {
|
||||
/// CTS Clear to send. This bit is the complement of the UART clear to
|
||||
/// send, `nUARTCTS`, modem status input. That is, the bit is 1
|
||||
/// when `nUARTCTS` is LOW.
|
||||
pub clear_to_send: bool,
|
||||
/// DSR Data set ready. This bit is the complement of the UART data set
|
||||
/// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when
|
||||
/// `nUARTDSR` is LOW.
|
||||
pub data_set_ready: bool,
|
||||
/// DCD Data carrier detect. This bit is the complement of the UART data
|
||||
/// carrier detect, `nUARTDCD`, modem status input. That is, the bit is
|
||||
/// 1 when `nUARTDCD` is LOW.
|
||||
pub data_carrier_detect: bool,
|
||||
/// BUSY UART busy. If this bit is set to 1, the UART is busy
|
||||
/// transmitting data. This bit remains set until the complete
|
||||
/// byte, including all the stop bits, has been sent from the
|
||||
/// shift register. This bit is set as soon as the transmit FIFO
|
||||
/// becomes non-empty, regardless of whether the UART is enabled
|
||||
/// or not.
|
||||
pub busy: bool,
|
||||
/// RXFE Receive FIFO empty. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the UARTLCR_H register. If the FIFO
|
||||
/// is disabled, this bit is set when the receive holding
|
||||
/// register is empty. If the FIFO is enabled, the RXFE bit is
|
||||
/// set when the receive FIFO is empty.
|
||||
pub receive_fifo_empty: bool,
|
||||
/// TXFF Transmit FIFO full. The meaning of this bit depends on the
|
||||
/// state of the FEN bit in the UARTLCR_H register. If the FIFO
|
||||
/// is disabled, this bit is set when the transmit holding
|
||||
/// register is full. If the FIFO is enabled, the TXFF bit is
|
||||
/// set when the transmit FIFO is full.
|
||||
pub transmit_fifo_full: bool,
|
||||
/// RXFF Receive FIFO full. The meaning of this bit depends on the state
|
||||
/// of the FEN bit in the UARTLCR_H register. If the FIFO is
|
||||
/// disabled, this bit is set when the receive holding register
|
||||
/// is full. If the FIFO is enabled, the RXFF bit is set when
|
||||
/// the receive FIFO is full.
|
||||
pub receive_fifo_full: bool,
|
||||
/// Transmit FIFO empty. The meaning of this bit depends on the state of
|
||||
/// the FEN bit in the [Line Control register](LineControl),
|
||||
/// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the
|
||||
/// transmit holding register is empty. If the FIFO is enabled,
|
||||
/// the TXFE bit is set when the transmit FIFO is empty. This
|
||||
/// bit does not indicate if there is data in the transmit shift
|
||||
/// register.
|
||||
pub transmit_fifo_empty: bool,
|
||||
/// `RI`, is `true` when `nUARTRI` is `LOW`.
|
||||
pub ring_indicator: bool,
|
||||
_reserved_zero_no_modify: u23,
|
||||
}
|
||||
impl_vmstate_bitsized!(Flags);
|
||||
|
||||
impl Flags {
|
||||
pub fn reset(&mut self) {
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Flags {
|
||||
fn default() -> Self {
|
||||
let mut ret: Self = 0.into();
|
||||
// After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1
|
||||
ret.set_receive_fifo_empty(true);
|
||||
ret.set_transmit_fifo_empty(true);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[bitsize(32)]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
/// Line Control Register, `UARTLCR_H`
|
||||
#[doc(alias = "UARTLCR_H")]
|
||||
pub struct LineControl {
|
||||
/// BRK Send break.
|
||||
///
|
||||
/// If this bit is set to `1`, a low-level is continually output on the
|
||||
/// `UARTTXD` output, after completing transmission of the
|
||||
/// current character. For the proper execution of the break command,
|
||||
/// the software must set this bit for at least two complete
|
||||
/// frames. For normal use, this bit must be cleared to `0`.
|
||||
pub send_break: bool,
|
||||
/// 1 PEN Parity enable:
|
||||
///
|
||||
/// - 0 = parity is disabled and no parity bit added to the data frame
|
||||
/// - 1 = parity checking and generation is enabled.
|
||||
///
|
||||
/// See Table 3-11 on page 3-14 for the parity truth table.
|
||||
pub parity_enabled: bool,
|
||||
/// EPS Even parity select. Controls the type of parity the UART uses
|
||||
/// during transmission and reception:
|
||||
/// - 0 = odd parity. The UART generates or checks for an odd number of 1s
|
||||
/// in the data and parity bits.
|
||||
/// - 1 = even parity. The UART generates or checks for an even number of 1s
|
||||
/// in the data and parity bits.
|
||||
/// This bit has no effect when the `PEN` bit disables parity checking
|
||||
/// and generation. See Table 3-11 on page 3-14 for the parity
|
||||
/// truth table.
|
||||
pub parity: Parity,
|
||||
/// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits
|
||||
/// are transmitted at the end of the frame. The receive
|
||||
/// logic does not check for two stop bits being received.
|
||||
pub two_stops_bits: bool,
|
||||
/// FEN Enable FIFOs:
|
||||
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
|
||||
/// 1-byte-deep holding registers 1 = transmit and receive FIFO
|
||||
/// buffers are enabled (FIFO mode).
|
||||
pub fifos_enabled: Mode,
|
||||
/// WLEN Word length. These bits indicate the number of data bits
|
||||
/// transmitted or received in a frame as follows: b11 = 8 bits
|
||||
/// b10 = 7 bits
|
||||
/// b01 = 6 bits
|
||||
/// b00 = 5 bits.
|
||||
pub word_length: WordLength,
|
||||
/// 7 SPS Stick parity select.
|
||||
/// 0 = stick parity is disabled
|
||||
/// 1 = either:
|
||||
/// • if the EPS bit is 0 then the parity bit is transmitted and checked
|
||||
/// as a 1 • if the EPS bit is 1 then the parity bit is
|
||||
/// transmitted and checked as a 0. This bit has no effect when
|
||||
/// the PEN bit disables parity checking and generation. See Table 3-11
|
||||
/// on page 3-14 for the parity truth table.
|
||||
pub sticky_parity: bool,
|
||||
/// 31:8 - Reserved, do not modify, read as zero.
|
||||
_reserved_zero_no_modify: u24,
|
||||
}
|
||||
impl_vmstate_bitsized!(LineControl);
|
||||
|
||||
impl LineControl {
|
||||
pub fn reset(&mut self) {
|
||||
// All the bits are cleared to 0 when reset.
|
||||
*self = 0.into();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LineControl {
|
||||
fn default() -> Self {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitsize(1)]
|
||||
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
|
||||
/// `EPS` "Even parity select", field of [Line Control
|
||||
/// register](LineControl).
|
||||
pub enum Parity {
|
||||
/// - 0 = odd parity. The UART generates or checks for an odd number of 1s
|
||||
/// in the data and parity bits.
|
||||
Odd = 0,
|
||||
/// - 1 = even parity. The UART generates or checks for an even number of 1s
|
||||
/// in the data and parity bits.
|
||||
Even = 1,
|
||||
}
|
||||
|
||||
#[bitsize(1)]
|
||||
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
|
||||
/// `FEN` "Enable FIFOs" or Device mode, field of [Line Control
|
||||
/// register](LineControl).
|
||||
pub enum Mode {
|
||||
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
|
||||
/// 1-byte-deep holding registers
|
||||
Character = 0,
|
||||
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
|
||||
FIFO = 1,
|
||||
}
|
||||
|
||||
#[bitsize(2)]
|
||||
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
|
||||
/// `WLEN` Word length, field of [Line Control register](LineControl).
|
||||
///
|
||||
/// These bits indicate the number of data bits transmitted or received in a
|
||||
/// frame as follows:
|
||||
pub enum WordLength {
|
||||
/// b11 = 8 bits
|
||||
_8Bits = 0b11,
|
||||
/// b10 = 7 bits
|
||||
_7Bits = 0b10,
|
||||
/// b01 = 6 bits
|
||||
_6Bits = 0b01,
|
||||
/// b00 = 5 bits.
|
||||
_5Bits = 0b00,
|
||||
}
|
||||
|
||||
/// Control Register, `UARTCR`
|
||||
///
|
||||
/// The `UARTCR` register is the control register. All the bits are cleared
|
||||
/// to `0` on reset except for bits `9` and `8` that are set to `1`.
|
||||
///
|
||||
/// # Source
|
||||
/// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12
|
||||
#[bitsize(32)]
|
||||
#[doc(alias = "UARTCR")]
|
||||
#[derive(Clone, Copy, DebugBits, FromBits)]
|
||||
pub struct Control {
|
||||
/// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled
|
||||
/// in the middle of transmission or reception, it completes the current
|
||||
/// character before stopping. 1 = the UART is enabled. Data
|
||||
/// transmission and reception occurs for either UART signals or SIR
|
||||
/// signals depending on the setting of the SIREN bit.
|
||||
pub enable_uart: bool,
|
||||
/// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT`
|
||||
/// remains LOW (no light pulse generated), and signal transitions on
|
||||
/// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is
|
||||
/// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH,
|
||||
/// in the marking state. Signal transitions on UARTRXD or modem status
|
||||
/// inputs have no effect. This bit has no effect if the UARTEN bit
|
||||
/// disables the UART.
|
||||
pub enable_sir: bool,
|
||||
/// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding
|
||||
/// mode. If this bit is cleared to 0, low-level bits are transmitted as
|
||||
/// an active high pulse with a width of 3/ 16th of the bit period. If
|
||||
/// this bit is set to 1, low-level bits are transmitted with a pulse
|
||||
/// width that is 3 times the period of the IrLPBaud16 input signal,
|
||||
/// regardless of the selected bit rate. Setting this bit uses less
|
||||
/// power, but might reduce transmission distances.
|
||||
pub sir_lowpower_irda_mode: u1,
|
||||
/// Reserved, do not modify, read as zero.
|
||||
_reserved_zero_no_modify: u4,
|
||||
/// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is
|
||||
/// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR
|
||||
/// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed
|
||||
/// through to the SIRIN path. The SIRTEST bit in the test register must
|
||||
/// be set to 1 to override the normal half-duplex SIR operation. This
|
||||
/// must be the requirement for accessing the test registers during
|
||||
/// normal operation, and SIRTEST must be cleared to 0 when loopback
|
||||
/// testing is finished. This feature reduces the amount of external
|
||||
/// coupling required during system test. If this bit is set to 1, and
|
||||
/// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the
|
||||
/// UARTRXD path. In either SIR mode or UART mode, when this bit is set,
|
||||
/// the modem outputs are also fed through to the modem inputs. This bit
|
||||
/// is cleared to 0 on reset, to disable loopback.
|
||||
pub enable_loopback: bool,
|
||||
/// `TXE` Transmit enable. If this bit is set to 1, the transmit section
|
||||
/// of the UART is enabled. Data transmission occurs for either UART
|
||||
/// signals, or SIR signals depending on the setting of the SIREN bit.
|
||||
/// When the UART is disabled in the middle of transmission, it
|
||||
/// completes the current character before stopping.
|
||||
pub enable_transmit: bool,
|
||||
/// `RXE` Receive enable. If this bit is set to 1, the receive section
|
||||
/// of the UART is enabled. Data reception occurs for either UART
|
||||
/// signals or SIR signals depending on the setting of the SIREN bit.
|
||||
/// When the UART is disabled in the middle of reception, it completes
|
||||
/// the current character before stopping.
|
||||
pub enable_receive: bool,
|
||||
/// `DTR` Data transmit ready. This bit is the complement of the UART
|
||||
/// data transmit ready, `nUARTDTR`, modem status output. That is, when
|
||||
/// the bit is programmed to a 1 then `nUARTDTR` is LOW.
|
||||
pub data_transmit_ready: bool,
|
||||
/// `RTS` Request to send. This bit is the complement of the UART
|
||||
/// request to send, `nUARTRTS`, modem status output. That is, when the
|
||||
/// bit is programmed to a 1 then `nUARTRTS` is LOW.
|
||||
pub request_to_send: bool,
|
||||
/// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`)
|
||||
/// modem status output. That is, when the bit is programmed to a 1 the
|
||||
/// output is 0. For DTE this can be used as Data Carrier Detect (DCD).
|
||||
pub out_1: bool,
|
||||
/// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`)
|
||||
/// modem status output. That is, when the bit is programmed to a 1, the
|
||||
/// output is 0. For DTE this can be used as Ring Indicator (RI).
|
||||
pub out_2: bool,
|
||||
/// `RTSEn` RTS hardware flow control enable. If this bit is set to 1,
|
||||
/// RTS hardware flow control is enabled. Data is only requested when
|
||||
/// there is space in the receive FIFO for it to be received.
|
||||
pub rts_hardware_flow_control_enable: bool,
|
||||
/// `CTSEn` CTS hardware flow control enable. If this bit is set to 1,
|
||||
/// CTS hardware flow control is enabled. Data is only transmitted when
|
||||
/// the `nUARTCTS` signal is asserted.
|
||||
pub cts_hardware_flow_control_enable: bool,
|
||||
/// 31:16 - Reserved, do not modify, read as zero.
|
||||
_reserved_zero_no_modify2: u16,
|
||||
}
|
||||
impl_vmstate_bitsized!(Control);
|
||||
|
||||
impl Control {
|
||||
pub fn reset(&mut self) {
|
||||
*self = 0.into();
|
||||
self.set_enable_receive(true);
|
||||
self.set_enable_transmit(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Control {
|
||||
fn default() -> Self {
|
||||
let mut ret: Self = 0.into();
|
||||
ret.reset();
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
|
||||
pub struct Interrupt(pub u32);
|
||||
|
||||
impl Interrupt {
|
||||
pub const OE: Self = Self(1 << 10);
|
||||
pub const BE: Self = Self(1 << 9);
|
||||
pub const PE: Self = Self(1 << 8);
|
||||
pub const FE: Self = Self(1 << 7);
|
||||
pub const RT: Self = Self(1 << 6);
|
||||
pub const TX: Self = Self(1 << 5);
|
||||
pub const RX: Self = Self(1 << 4);
|
||||
pub const DSR: Self = Self(1 << 3);
|
||||
pub const DCD: Self = Self(1 << 2);
|
||||
pub const CTS: Self = Self(1 << 1);
|
||||
pub const RI: Self = Self(1 << 0);
|
||||
|
||||
pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0);
|
||||
pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0);
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
pin::Pin,
|
||||
ptr::{addr_of_mut, null_mut, NonNull},
|
||||
slice::from_ref,
|
||||
};
|
||||
|
@ -47,8 +48,6 @@ const RTC_ISA_IRQ: usize = 8;
|
|||
const HPET_CLK_PERIOD: u64 = 10; // 10 ns
|
||||
const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns
|
||||
|
||||
/// General Capabilities and ID Register
|
||||
const HPET_CAP_REG: u64 = 0x000;
|
||||
/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec).
|
||||
const HPET_CAP_REV_ID_VALUE: u64 = 0x1;
|
||||
const HPET_CAP_REV_ID_SHIFT: usize = 0;
|
||||
|
@ -64,8 +63,6 @@ const HPET_CAP_VENDER_ID_SHIFT: usize = 16;
|
|||
/// Main Counter Tick Period (bits 32:63)
|
||||
const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32;
|
||||
|
||||
/// General Configuration Register
|
||||
const HPET_CFG_REG: u64 = 0x010;
|
||||
/// Overall Enable (bit 0)
|
||||
const HPET_CFG_ENABLE_SHIFT: usize = 0;
|
||||
/// Legacy Replacement Route (bit 1)
|
||||
|
@ -73,14 +70,6 @@ const HPET_CFG_LEG_RT_SHIFT: usize = 1;
|
|||
/// Other bits are reserved.
|
||||
const HPET_CFG_WRITE_MASK: u64 = 0x003;
|
||||
|
||||
/// General Interrupt Status Register
|
||||
const HPET_INT_STATUS_REG: u64 = 0x020;
|
||||
|
||||
/// Main Counter Value Register
|
||||
const HPET_COUNTER_REG: u64 = 0x0f0;
|
||||
|
||||
/// Timer N Configuration and Capability Register (masked by 0x18)
|
||||
const HPET_TN_CFG_REG: u64 = 0x000;
|
||||
/// bit 0, 7, and bits 16:31 are reserved.
|
||||
/// bit 4, 5, 15, and bits 32:64 are read-only.
|
||||
const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e;
|
||||
|
@ -108,11 +97,51 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15;
|
|||
/// Timer N Interrupt Routing Capability (bits 32:63)
|
||||
const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
|
||||
|
||||
/// Timer N Comparator Value Register (masked by 0x18)
|
||||
const HPET_TN_CMP_REG: u64 = 0x008;
|
||||
#[derive(qemu_api_macros::TryInto)]
|
||||
#[repr(u64)]
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Timer registers, masked by 0x18
|
||||
enum TimerRegister {
|
||||
/// Timer N Configuration and Capability Register
|
||||
CFG = 0,
|
||||
/// Timer N Comparator Value Register
|
||||
CMP = 8,
|
||||
/// Timer N FSB Interrupt Route Register
|
||||
ROUTE = 16,
|
||||
}
|
||||
|
||||
/// Timer N FSB Interrupt Route Register (masked by 0x18)
|
||||
const HPET_TN_FSB_ROUTE_REG: u64 = 0x010;
|
||||
#[derive(qemu_api_macros::TryInto)]
|
||||
#[repr(u64)]
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Global registers
|
||||
enum GlobalRegister {
|
||||
/// General Capabilities and ID Register
|
||||
CAP = 0,
|
||||
/// General Configuration Register
|
||||
CFG = 0x10,
|
||||
/// General Interrupt Status Register
|
||||
INT_STATUS = 0x20,
|
||||
/// Main Counter Value Register
|
||||
COUNTER = 0xF0,
|
||||
}
|
||||
|
||||
enum HPETRegister<'a> {
|
||||
/// Global register in the range from `0` to `0xff`
|
||||
Global(GlobalRegister),
|
||||
|
||||
/// Register in the timer block `0x100`...`0x3ff`
|
||||
Timer(&'a BqlRefCell<HPETTimer>, TimerRegister),
|
||||
|
||||
/// Invalid address
|
||||
#[allow(dead_code)]
|
||||
Unknown(hwaddr),
|
||||
}
|
||||
|
||||
struct HPETAddrDecode<'a> {
|
||||
shift: u32,
|
||||
len: u32,
|
||||
reg: HPETRegister<'a>,
|
||||
}
|
||||
|
||||
const fn hpet_next_wrap(cur_tick: u64) -> u64 {
|
||||
(cur_tick | 0xffffffff) + 1
|
||||
|
@ -151,14 +180,14 @@ fn timer_handler(timer_cell: &BqlRefCell<HPETTimer>) {
|
|||
|
||||
/// HPET Timer Abstraction
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, qemu_api_macros::offsets)]
|
||||
#[derive(Debug, qemu_api_macros::offsets)]
|
||||
pub struct HPETTimer {
|
||||
/// timer N index within the timer block (`HPETState`)
|
||||
#[doc(alias = "tn")]
|
||||
index: usize,
|
||||
qemu_timer: Option<Box<Timer>>,
|
||||
qemu_timer: Timer,
|
||||
/// timer block abstraction containing this timer
|
||||
state: Option<NonNull<HPETState>>,
|
||||
state: NonNull<HPETState>,
|
||||
|
||||
// Memory-mapped, software visible timer registers
|
||||
/// Timer N Configuration and Capability Register
|
||||
|
@ -181,32 +210,39 @@ pub struct HPETTimer {
|
|||
}
|
||||
|
||||
impl HPETTimer {
|
||||
fn init(&mut self, index: usize, state_ptr: *mut HPETState) -> &mut Self {
|
||||
*self = HPETTimer::default();
|
||||
self.index = index;
|
||||
self.state = NonNull::new(state_ptr);
|
||||
self
|
||||
}
|
||||
fn init(&mut self, index: usize, state: &HPETState) {
|
||||
*self = HPETTimer {
|
||||
index,
|
||||
// SAFETY: the HPETTimer will only be used after the timer
|
||||
// is initialized below.
|
||||
qemu_timer: unsafe { Timer::new() },
|
||||
state: NonNull::new(state as *const _ as *mut _).unwrap(),
|
||||
config: 0,
|
||||
cmp: 0,
|
||||
fsb: 0,
|
||||
cmp64: 0,
|
||||
period: 0,
|
||||
wrap_flag: 0,
|
||||
last: 0,
|
||||
};
|
||||
|
||||
fn init_timer_with_state(&mut self) {
|
||||
self.qemu_timer = Some(Box::new({
|
||||
let mut t = Timer::new();
|
||||
t.init_full(
|
||||
// SAFETY: HPETTimer is only used as part of HPETState, which is
|
||||
// always pinned.
|
||||
let qemu_timer = unsafe { Pin::new_unchecked(&mut self.qemu_timer) };
|
||||
qemu_timer.init_full(
|
||||
None,
|
||||
CLOCK_VIRTUAL,
|
||||
Timer::NS,
|
||||
0,
|
||||
timer_handler,
|
||||
&self.get_state().timers[self.index],
|
||||
);
|
||||
t
|
||||
}));
|
||||
&state.timers[self.index],
|
||||
)
|
||||
}
|
||||
|
||||
fn get_state(&self) -> &HPETState {
|
||||
// SAFETY:
|
||||
// the pointer is convertible to a reference
|
||||
unsafe { self.state.unwrap().as_ref() }
|
||||
unsafe { self.state.as_ref() }
|
||||
}
|
||||
|
||||
fn is_int_active(&self) -> bool {
|
||||
|
@ -330,7 +366,7 @@ impl HPETTimer {
|
|||
}
|
||||
|
||||
self.last = ns;
|
||||
self.qemu_timer.as_ref().unwrap().modify(self.last);
|
||||
self.qemu_timer.modify(self.last);
|
||||
}
|
||||
|
||||
fn set_timer(&mut self) {
|
||||
|
@ -353,7 +389,7 @@ impl HPETTimer {
|
|||
fn del_timer(&mut self) {
|
||||
// Just remove the timer from the timer_list without destroying
|
||||
// this timer instance.
|
||||
self.qemu_timer.as_ref().unwrap().delete();
|
||||
self.qemu_timer.delete();
|
||||
|
||||
if self.is_int_active() {
|
||||
// For level-triggered interrupt, this leaves interrupt status
|
||||
|
@ -463,33 +499,21 @@ impl HPETTimer {
|
|||
self.update_irq(true);
|
||||
}
|
||||
|
||||
const fn read(&self, addr: hwaddr, _size: u32) -> u64 {
|
||||
let shift: u64 = (addr & 4) * 8;
|
||||
|
||||
match addr & !4 {
|
||||
HPET_TN_CFG_REG => self.config >> shift, // including interrupt capabilities
|
||||
HPET_TN_CMP_REG => self.cmp >> shift, // comparator register
|
||||
HPET_TN_FSB_ROUTE_REG => self.fsb >> shift,
|
||||
_ => {
|
||||
// TODO: Add trace point - trace_hpet_ram_read_invalid()
|
||||
// Reserved.
|
||||
0
|
||||
}
|
||||
const fn read(&self, reg: TimerRegister) -> u64 {
|
||||
use TimerRegister::*;
|
||||
match reg {
|
||||
CFG => self.config, // including interrupt capabilities
|
||||
CMP => self.cmp, // comparator register
|
||||
ROUTE => self.fsb,
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: hwaddr, value: u64, size: u32) {
|
||||
let shift = ((addr & 4) * 8) as u32;
|
||||
let len = std::cmp::min(size * 8, 64 - shift);
|
||||
|
||||
match addr & !4 {
|
||||
HPET_TN_CFG_REG => self.set_tn_cfg_reg(shift, len, value),
|
||||
HPET_TN_CMP_REG => self.set_tn_cmp_reg(shift, len, value),
|
||||
HPET_TN_FSB_ROUTE_REG => self.set_tn_fsb_route_reg(shift, len, value),
|
||||
_ => {
|
||||
// TODO: Add trace point - trace_hpet_ram_write_invalid()
|
||||
// Reserved.
|
||||
}
|
||||
fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) {
|
||||
use TimerRegister::*;
|
||||
match reg {
|
||||
CFG => self.set_tn_cfg_reg(shift, len, value),
|
||||
CMP => self.set_tn_cmp_reg(shift, len, value),
|
||||
ROUTE => self.set_tn_fsb_route_reg(shift, len, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -581,13 +605,8 @@ impl HPETState {
|
|||
}
|
||||
|
||||
fn init_timer(&self) {
|
||||
let raw_ptr: *mut HPETState = self as *const HPETState as *mut HPETState;
|
||||
|
||||
for (index, timer) in self.timers.iter().enumerate() {
|
||||
timer
|
||||
.borrow_mut()
|
||||
.init(index, raw_ptr)
|
||||
.init_timer_with_state();
|
||||
timer.borrow_mut().init(index, self);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -727,8 +746,6 @@ impl HPETState {
|
|||
}
|
||||
|
||||
fn reset_hold(&self, _type: ResetType) {
|
||||
let sbd = self.upcast::<SysBusDevice>();
|
||||
|
||||
for timer in self.timers.iter().take(self.num_timers.get()) {
|
||||
timer.borrow_mut().reset();
|
||||
}
|
||||
|
@ -741,83 +758,79 @@ impl HPETState {
|
|||
HPETFwConfig::update_hpet_cfg(
|
||||
self.hpet_id.get(),
|
||||
self.capability.get() as u32,
|
||||
sbd.mmio[0].addr,
|
||||
self.mmio_addr(0).unwrap(),
|
||||
);
|
||||
|
||||
// to document that the RTC lowers its output on reset as well
|
||||
self.rtc_irq_level.set(0);
|
||||
}
|
||||
|
||||
fn timer_and_addr(&self, addr: hwaddr) -> Option<(&BqlRefCell<HPETTimer>, hwaddr)> {
|
||||
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
|
||||
|
||||
// TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
|
||||
if timer_id > self.num_timers.get() {
|
||||
// TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
|
||||
None
|
||||
} else {
|
||||
// Keep the complete address so that HPETTimer's read and write could
|
||||
// detect the invalid access.
|
||||
Some((&self.timers[timer_id], addr & 0x1F))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, addr: hwaddr, size: u32) -> u64 {
|
||||
let shift: u64 = (addr & 4) * 8;
|
||||
|
||||
// address range of all TN regs
|
||||
// TODO: Add trace point - trace_hpet_ram_read(addr)
|
||||
if (0x100..=0x3ff).contains(&addr) {
|
||||
match self.timer_and_addr(addr) {
|
||||
None => 0, // Reserved,
|
||||
Some((timer, tn_addr)) => timer.borrow_mut().read(tn_addr, size),
|
||||
}
|
||||
} else {
|
||||
match addr & !4 {
|
||||
HPET_CAP_REG => self.capability.get() >> shift, /* including HPET_PERIOD 0x004 */
|
||||
// (CNT_CLK_PERIOD field)
|
||||
HPET_CFG_REG => self.config.get() >> shift,
|
||||
HPET_COUNTER_REG => {
|
||||
let cur_tick: u64 = if self.is_hpet_enabled() {
|
||||
self.get_ticks()
|
||||
} else {
|
||||
self.counter.get()
|
||||
};
|
||||
|
||||
// TODO: Add trace point - trace_hpet_ram_read_reading_counter(addr & 4,
|
||||
// cur_tick)
|
||||
cur_tick >> shift
|
||||
}
|
||||
HPET_INT_STATUS_REG => self.int_status.get() >> shift,
|
||||
_ => {
|
||||
// TODO: Add trace point- trace_hpet_ram_read_invalid()
|
||||
// Reserved.
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, addr: hwaddr, value: u64, size: u32) {
|
||||
fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode {
|
||||
let shift = ((addr & 4) * 8) as u32;
|
||||
let len = std::cmp::min(size * 8, 64 - shift);
|
||||
|
||||
// TODO: Add trace point - trace_hpet_ram_write(addr, value)
|
||||
if (0x100..=0x3ff).contains(&addr) {
|
||||
match self.timer_and_addr(addr) {
|
||||
None => (), // Reserved.
|
||||
Some((timer, tn_addr)) => timer.borrow_mut().write(tn_addr, value, size),
|
||||
}
|
||||
addr &= !4;
|
||||
let reg = if (0..=0xff).contains(&addr) {
|
||||
GlobalRegister::try_from(addr).map(HPETRegister::Global)
|
||||
} else {
|
||||
match addr & !0x4 {
|
||||
HPET_CAP_REG => {} // General Capabilities and ID Register: Read Only
|
||||
HPET_CFG_REG => self.set_cfg_reg(shift, len, value),
|
||||
HPET_INT_STATUS_REG => self.set_int_status_reg(shift, len, value),
|
||||
HPET_COUNTER_REG => self.set_counter_reg(shift, len, value),
|
||||
_ => {
|
||||
// TODO: Add trace point - trace_hpet_ram_write_invalid()
|
||||
// Reserved.
|
||||
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
|
||||
if timer_id <= self.num_timers.get() {
|
||||
// TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
|
||||
TimerRegister::try_from(addr)
|
||||
.map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
|
||||
} else {
|
||||
// TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
|
||||
Err(addr)
|
||||
}
|
||||
};
|
||||
|
||||
// reg is now a Result<HPETRegister, hwaddr>
|
||||
// convert the Err case into HPETRegister as well
|
||||
let reg = reg.unwrap_or_else(HPETRegister::Unknown);
|
||||
HPETAddrDecode { shift, len, reg }
|
||||
}
|
||||
|
||||
fn read(&self, addr: hwaddr, size: u32) -> u64 {
|
||||
// TODO: Add trace point - trace_hpet_ram_read(addr)
|
||||
let HPETAddrDecode { shift, reg, .. } = self.decode(addr, size);
|
||||
|
||||
use GlobalRegister::*;
|
||||
use HPETRegister::*;
|
||||
(match reg {
|
||||
Timer(timer, tn_reg) => timer.borrow_mut().read(tn_reg),
|
||||
Global(CAP) => self.capability.get(), /* including HPET_PERIOD 0x004 */
|
||||
Global(CFG) => self.config.get(),
|
||||
Global(INT_STATUS) => self.int_status.get(),
|
||||
Global(COUNTER) => {
|
||||
// TODO: Add trace point
|
||||
// trace_hpet_ram_read_reading_counter(addr & 4, cur_tick)
|
||||
if self.is_hpet_enabled() {
|
||||
self.get_ticks()
|
||||
} else {
|
||||
self.counter.get()
|
||||
}
|
||||
}
|
||||
Unknown(_) => {
|
||||
// TODO: Add trace point- trace_hpet_ram_read_invalid()
|
||||
0
|
||||
}
|
||||
}) >> shift
|
||||
}
|
||||
|
||||
fn write(&self, addr: hwaddr, value: u64, size: u32) {
|
||||
let HPETAddrDecode { shift, len, reg } = self.decode(addr, size);
|
||||
|
||||
// TODO: Add trace point - trace_hpet_ram_write(addr, value)
|
||||
use GlobalRegister::*;
|
||||
use HPETRegister::*;
|
||||
match reg {
|
||||
Timer(timer, tn_reg) => timer.borrow_mut().write(tn_reg, value, shift, len),
|
||||
Global(CAP) => {} // General Capabilities and ID Register: Read Only
|
||||
Global(CFG) => self.set_cfg_reg(shift, len, value),
|
||||
Global(INT_STATUS) => self.set_int_status_reg(shift, len, value),
|
||||
Global(COUNTER) => self.set_counter_reg(shift, len, value),
|
||||
Unknown(_) => {
|
||||
// TODO: Add trace point - trace_hpet_ram_write_invalid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use proc_macro::TokenStream;
|
|||
use quote::quote;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
|
||||
DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility,
|
||||
DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Type, Variant, Visibility,
|
||||
};
|
||||
|
||||
mod utils;
|
||||
|
@ -33,6 +33,35 @@ fn get_fields<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> {
|
||||
if let Data::Struct(s) = &input.data {
|
||||
let unnamed = match &s.fields {
|
||||
Fields::Unnamed(FieldsUnnamed {
|
||||
unnamed: ref fields,
|
||||
..
|
||||
}) => fields,
|
||||
_ => {
|
||||
return Err(MacroError::Message(
|
||||
format!("Tuple struct required for {}", msg),
|
||||
s.fields.span(),
|
||||
))
|
||||
}
|
||||
};
|
||||
if unnamed.len() != 1 {
|
||||
return Err(MacroError::Message(
|
||||
format!("A single field is required for {}", msg),
|
||||
s.fields.span(),
|
||||
));
|
||||
}
|
||||
Ok(&unnamed[0])
|
||||
} else {
|
||||
Err(MacroError::Message(
|
||||
format!("Struct required for {}", msg),
|
||||
input.ident.span(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
||||
let expected = parse_quote! { #[repr(C)] };
|
||||
|
||||
|
@ -46,6 +75,19 @@ fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
||||
let expected = parse_quote! { #[repr(transparent)] };
|
||||
|
||||
if input.attrs.iter().any(|attr| attr == &expected) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MacroError::Message(
|
||||
format!("#[repr(transparent)] required for {}", msg),
|
||||
input.ident.span(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||
is_c_repr(&input, "#[derive(Object)]")?;
|
||||
|
||||
|
@ -72,6 +114,52 @@ pub fn derive_object(input: TokenStream) -> TokenStream {
|
|||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||
is_transparent_repr(&input, "#[derive(Wrapper)]")?;
|
||||
|
||||
let name = &input.ident;
|
||||
let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?;
|
||||
let typ = &field.ty;
|
||||
|
||||
// TODO: how to add "::qemu_api"? For now, this is only used in the
|
||||
// qemu_api crate so it's not a problem.
|
||||
Ok(quote! {
|
||||
unsafe impl crate::cell::Wrapper for #name {
|
||||
type Wrapped = <#typ as crate::cell::Wrapper>::Wrapped;
|
||||
}
|
||||
impl #name {
|
||||
pub unsafe fn from_raw<'a>(ptr: *mut <Self as crate::cell::Wrapper>::Wrapped) -> &'a Self {
|
||||
let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>();
|
||||
unsafe { ptr.as_ref() }
|
||||
}
|
||||
|
||||
pub const fn as_mut_ptr(&self) -> *mut <Self as crate::cell::Wrapper>::Wrapped {
|
||||
self.0.as_mut_ptr()
|
||||
}
|
||||
|
||||
pub const fn as_ptr(&self) -> *const <Self as crate::cell::Wrapper>::Wrapped {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void {
|
||||
self.0.as_void_ptr()
|
||||
}
|
||||
|
||||
pub const fn raw_get(slot: *mut Self) -> *mut <Self as crate::cell::Wrapper>::Wrapped {
|
||||
slot.cast()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Wrapper)]
|
||||
pub fn derive_opaque(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into);
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
#[rustfmt::skip::macros(quote)]
|
||||
fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||
is_c_repr(&input, "#[derive(offsets)]")?;
|
||||
|
|
|
@ -42,22 +42,31 @@ _qemu_api_rs = static_library(
|
|||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||
rust_abi: 'rust',
|
||||
rust_args: _qemu_api_cfg,
|
||||
dependencies: libc_dep,
|
||||
dependencies: [libc_dep, qemu_api_macros],
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
qemu_api = declare_dependency(link_with: _qemu_api_rs)
|
||||
|
||||
# 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)])
|
||||
libhwcore.extract_all_objects(recursive: false),
|
||||
libchardev.extract_all_objects(recursive: false),
|
||||
libcrypto.extract_all_objects(recursive: false),
|
||||
libauthz.extract_all_objects(recursive: false),
|
||||
libio.extract_all_objects(recursive: false)])
|
||||
rust_qemu_api_deps = declare_dependency(
|
||||
dependencies: [
|
||||
qom_ss.dependencies(),
|
||||
chardev_ss.dependencies(),
|
||||
crypto_ss.dependencies(),
|
||||
authz_ss.dependencies(),
|
||||
io_ss.dependencies()],
|
||||
link_whole: [rust_qemu_api_objs, libqemuutil])
|
||||
|
||||
test('rust-qemu-api-integration',
|
||||
executable(
|
||||
|
@ -66,8 +75,7 @@ test('rust-qemu-api-integration',
|
|||
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]),
|
||||
dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]),
|
||||
args: [
|
||||
'--test', '--test-threads', '1',
|
||||
'--format', 'pretty',
|
||||
|
|
|
@ -25,33 +25,11 @@ include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
|
|||
|
||||
// SAFETY: these are implemented in C; the bindings need to assert that the
|
||||
// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
|
||||
unsafe impl Send for BusState {}
|
||||
unsafe impl Sync for BusState {}
|
||||
|
||||
// When bindings for character devices are introduced, this can be
|
||||
// moved to the Opaque<> wrapper in src/chardev.rs.
|
||||
unsafe impl Send for CharBackend {}
|
||||
unsafe impl Sync for CharBackend {}
|
||||
|
||||
unsafe impl Send for Chardev {}
|
||||
unsafe impl Sync for Chardev {}
|
||||
|
||||
unsafe impl Send for Clock {}
|
||||
unsafe impl Sync for Clock {}
|
||||
|
||||
unsafe impl Send for DeviceState {}
|
||||
unsafe impl Sync for DeviceState {}
|
||||
|
||||
unsafe impl Send for MemoryRegion {}
|
||||
unsafe impl Sync for MemoryRegion {}
|
||||
|
||||
unsafe impl Send for ObjectClass {}
|
||||
unsafe impl Sync for ObjectClass {}
|
||||
|
||||
unsafe impl Send for Object {}
|
||||
unsafe impl Sync for Object {}
|
||||
|
||||
unsafe impl Send for SysBusDevice {}
|
||||
unsafe impl Sync for SysBusDevice {}
|
||||
|
||||
// SAFETY: this is a pure data struct
|
||||
unsafe impl Send for CoalescedMemoryRange {}
|
||||
unsafe impl Sync for CoalescedMemoryRange {}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! BQL-protected mutable containers.
|
||||
//! QEMU-specific mutable containers
|
||||
//!
|
||||
//! Rust memory safety is based on this rule: Given an object `T`, it is only
|
||||
//! possible to have one of the following:
|
||||
|
@ -43,8 +43,10 @@
|
|||
//! usually have their pointer shared with the "outside world very early in
|
||||
//! their lifetime", for example when they create their
|
||||
//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual
|
||||
//! parts of a device must be made mutable in a controlled manner through the
|
||||
//! use of cell types.
|
||||
//! parts of a device must be made mutable in a controlled manner; this module
|
||||
//! provides the tools to do so.
|
||||
//!
|
||||
//! ## Cell types
|
||||
//!
|
||||
//! [`BqlCell<T>`] and [`BqlRefCell<T>`] allow doing this via the Big QEMU Lock.
|
||||
//! While they are essentially the same single-threaded primitives that are
|
||||
|
@ -71,7 +73,35 @@
|
|||
//! QEMU device implementations is usually incorrect and can lead to
|
||||
//! thread-safety issues.
|
||||
//!
|
||||
//! ## `BqlCell<T>`
|
||||
//! ### Example
|
||||
//!
|
||||
//! ```
|
||||
//! # use qemu_api::prelude::*;
|
||||
//! # use qemu_api::{c_str, cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
|
||||
//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
|
||||
//! # const N_GPIOS: usize = 8;
|
||||
//! # struct PL061Registers { /* ... */ }
|
||||
//! # unsafe impl ObjectType for PL061State {
|
||||
//! # type Class = <SysBusDevice as ObjectType>::Class;
|
||||
//! # const TYPE_NAME: &'static std::ffi::CStr = c_str!("pl061");
|
||||
//! # }
|
||||
//! struct PL061State {
|
||||
//! parent_obj: ParentField<SysBusDevice>,
|
||||
//!
|
||||
//! // Configuration is read-only after initialization
|
||||
//! pullups: u32,
|
||||
//! pulldowns: u32,
|
||||
//!
|
||||
//! // Single values shared with C code use BqlCell, in this case via InterruptSource
|
||||
//! out: [InterruptSource; N_GPIOS],
|
||||
//! interrupt: InterruptSource,
|
||||
//!
|
||||
//! // Larger state accessed by device methods uses BqlRefCell or Mutex
|
||||
//! registers: BqlRefCell<PL061Registers>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `BqlCell<T>`
|
||||
//!
|
||||
//! [`BqlCell<T>`] implements interior mutability by moving values in and out of
|
||||
//! the cell. That is, an `&mut T` to the inner value can never be obtained as
|
||||
|
@ -91,7 +121,7 @@
|
|||
//! - [`set`](BqlCell::set): this method replaces the interior value,
|
||||
//! dropping the replaced value.
|
||||
//!
|
||||
//! ## `BqlRefCell<T>`
|
||||
//! ### `BqlRefCell<T>`
|
||||
//!
|
||||
//! [`BqlRefCell<T>`] uses Rust's lifetimes to implement "dynamic borrowing", a
|
||||
//! process whereby one can claim temporary, exclusive, mutable access to the
|
||||
|
@ -111,13 +141,82 @@
|
|||
//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
|
||||
//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The
|
||||
//! thread will panic if these rules are violated or if the BQL is not held.
|
||||
//!
|
||||
//! ## Opaque wrappers
|
||||
//!
|
||||
//! The cell types from the previous section are useful at the boundaries
|
||||
//! of code that requires interior mutability. When writing glue code that
|
||||
//! interacts directly with C structs, however, it is useful to operate
|
||||
//! at a lower level.
|
||||
//!
|
||||
//! C functions often violate Rust's fundamental assumptions about memory
|
||||
//! safety by modifying memory even if it is shared. Furthermore, C structs
|
||||
//! often start their life uninitialized and may be populated lazily.
|
||||
//!
|
||||
//! For this reason, this module provides the [`Opaque<T>`] type to opt out
|
||||
//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
|
||||
//! value is always through raw pointers, obtained via methods like
|
||||
//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
|
||||
//! pointers can then be passed to C functions or dereferenced; both actions
|
||||
//! require `unsafe` blocks, making it clear where safety guarantees must be
|
||||
//! manually verified. For example
|
||||
//!
|
||||
//! ```ignore
|
||||
//! unsafe {
|
||||
//! let state = Opaque::<MyStruct>::uninit();
|
||||
//! qemu_struct_init(state.as_mut_ptr());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`Opaque<T>`] will usually be wrapped one level further, so that
|
||||
//! bridge methods can be added to the wrapper:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! pub struct MyStruct(Opaque<bindings::MyStruct>);
|
||||
//!
|
||||
//! impl MyStruct {
|
||||
//! fn new() -> Pin<Box<MyStruct>> {
|
||||
//! let result = Box::pin(unsafe { Opaque::uninit() });
|
||||
//! unsafe { qemu_struct_init(result.as_mut_ptr()) };
|
||||
//! result
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
|
||||
//! several advantages:
|
||||
//!
|
||||
//! * The choice of traits to be implemented is not limited by the
|
||||
//! bindgen-generated code. For example, [`Drop`] can be added without
|
||||
//! disabling [`Copy`] on the underlying bindgen type
|
||||
//!
|
||||
//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
|
||||
//! type rather than being automatically derived from the C struct's layout
|
||||
//!
|
||||
//! * Methods can be implemented in a separate crate from the bindgen-generated
|
||||
//! bindings
|
||||
//!
|
||||
//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
|
||||
//! implementations can be customized to be more readable than the raw C
|
||||
//! struct representation
|
||||
//!
|
||||
//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
|
||||
//! assert in the code that the right lock is taken, to use it together
|
||||
//! with a custom lock guard type, or to let C code take the lock, as
|
||||
//! appropriate. It is also possible to use it with non-thread-safe
|
||||
//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
|
||||
//! it is neither `Sync` nor `Send`.
|
||||
//!
|
||||
//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
|
||||
//! and only at FFI boundaries. For QEMU-specific types that need interior
|
||||
//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
|
||||
|
||||
use std::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
mem::{self, MaybeUninit},
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
@ -840,3 +939,167 @@ impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
|
|||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores an opaque value that is shared with C code.
|
||||
///
|
||||
/// Often, C structs can changed when calling a C function even if they are
|
||||
/// behind a shared Rust reference, or they can be initialized lazily and have
|
||||
/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's
|
||||
/// strict aliasing rules, which normally prevent mutation through shared
|
||||
/// references.
|
||||
///
|
||||
/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
|
||||
/// assume the usual constraints that Rust structs require, and allows using
|
||||
/// shared references on the Rust side.
|
||||
///
|
||||
/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
|
||||
/// of `T`.
|
||||
#[repr(transparent)]
|
||||
pub struct Opaque<T> {
|
||||
value: UnsafeCell<MaybeUninit<T>>,
|
||||
// PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
|
||||
// one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
|
||||
// see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
|
||||
impl<T> Opaque<T> {
|
||||
/// Creates a new shared reference from a C pointer
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The pointer must be valid, though it need not point to a valid value.
|
||||
pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
|
||||
let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
|
||||
// SAFETY: Self is a transparent wrapper over T
|
||||
unsafe { ptr.as_ref() }
|
||||
}
|
||||
|
||||
/// Creates a new opaque object with uninitialized contents.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Ultimately the pointer to the returned value will be dereferenced
|
||||
/// in another `unsafe` block, for example when passing it to a C function,
|
||||
/// but the functions containing the dereference are usually safe. The
|
||||
/// value returned from `uninit()` must be initialized and pinned before
|
||||
/// calling them.
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub unsafe fn uninit() -> Self {
|
||||
Self {
|
||||
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new opaque object with zeroed contents.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Ultimately the pointer to the returned value will be dereferenced
|
||||
/// in another `unsafe` block, for example when passing it to a C function,
|
||||
/// but the functions containing the dereference are usually safe. The
|
||||
/// value returned from `uninit()` must be pinned (and possibly initialized)
|
||||
/// before calling them.
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub unsafe fn zeroed() -> Self {
|
||||
Self {
|
||||
value: UnsafeCell::new(MaybeUninit::zeroed()),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a raw mutable pointer to the opaque data.
|
||||
pub const fn as_mut_ptr(&self) -> *mut T {
|
||||
UnsafeCell::get(&self.value).cast()
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the opaque data.
|
||||
pub const fn as_ptr(&self) -> *const T {
|
||||
self.as_mut_ptr() as *const _
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the opaque data that can be passed to a
|
||||
/// C function as `void *`.
|
||||
pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
|
||||
UnsafeCell::get(&self.value).cast()
|
||||
}
|
||||
|
||||
/// Converts a raw pointer to the wrapped type.
|
||||
pub const fn raw_get(slot: *mut Self) -> *mut T {
|
||||
// Compare with Linux's raw_get method, which goes through an UnsafeCell
|
||||
// because it takes a *const Self instead.
|
||||
slot.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Opaque<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut name: String = "Opaque<".to_string();
|
||||
name += std::any::type_name::<T>();
|
||||
name += ">";
|
||||
f.debug_tuple(&name).field(&self.as_ptr()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Opaque<T> {
|
||||
/// Creates a new opaque object with default contents.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Ultimately the pointer to the returned value will be dereferenced
|
||||
/// in another `unsafe` block, for example when passing it to a C function,
|
||||
/// but the functions containing the dereference are usually safe. The
|
||||
/// value returned from `uninit()` must be pinned before calling them.
|
||||
pub unsafe fn new() -> Self {
|
||||
Self {
|
||||
value: UnsafeCell::new(MaybeUninit::new(T::default())),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Annotates [`Self`] as a transparent wrapper for another type.
|
||||
///
|
||||
/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::mem::ManuallyDrop;
|
||||
/// # use qemu_api::cell::Wrapper;
|
||||
/// #[repr(transparent)]
|
||||
/// pub struct Example {
|
||||
/// inner: ManuallyDrop<String>,
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl Wrapper for Example {
|
||||
/// type Wrapped = String;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
|
||||
/// whether directly or indirectly.
|
||||
///
|
||||
/// # Methods
|
||||
///
|
||||
/// By convention, types that implement Wrapper also implement the following
|
||||
/// methods:
|
||||
///
|
||||
/// ```ignore
|
||||
/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
|
||||
/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
|
||||
/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
|
||||
/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
|
||||
/// ```
|
||||
///
|
||||
/// They are not defined here to allow them to be `const`.
|
||||
pub unsafe trait Wrapper {
|
||||
type Wrapped;
|
||||
}
|
||||
|
||||
unsafe impl<T> Wrapper for Opaque<T> {
|
||||
type Wrapped = T;
|
||||
}
|
||||
|
|
|
@ -3,13 +3,255 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
//! Bindings for character devices
|
||||
//!
|
||||
//! Character devices in QEMU can run under the big QEMU lock or in a separate
|
||||
//! `GMainContext`. Here we only support the former, because the bindings
|
||||
//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are
|
||||
//! called.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
fmt::{self, Debug},
|
||||
io::{self, ErrorKind, Write},
|
||||
marker::PhantomPinned,
|
||||
os::raw::{c_int, c_void},
|
||||
ptr::addr_of_mut,
|
||||
slice,
|
||||
};
|
||||
|
||||
use crate::{bindings, prelude::*};
|
||||
use crate::{
|
||||
bindings,
|
||||
callbacks::FnCall,
|
||||
cell::{BqlRefMut, Opaque},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// A safe wrapper around [`bindings::Chardev`].
|
||||
#[repr(transparent)]
|
||||
#[derive(qemu_api_macros::Wrapper)]
|
||||
pub struct Chardev(Opaque<bindings::Chardev>);
|
||||
|
||||
pub type Chardev = bindings::Chardev;
|
||||
pub type ChardevClass = bindings::ChardevClass;
|
||||
pub type Event = bindings::QEMUChrEvent;
|
||||
|
||||
/// A safe wrapper around [`bindings::CharBackend`], denoting the character
|
||||
/// back-end that is used for example by a device. Compared to the
|
||||
/// underlying C struct it adds BQL protection, and is marked as pinned
|
||||
/// because the QOM object ([`bindings::Chardev`]) contains a pointer to
|
||||
/// the `CharBackend`.
|
||||
pub struct CharBackend {
|
||||
inner: BqlRefCell<bindings::CharBackend>,
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
|
||||
impl Write for BqlRefMut<'_, bindings::CharBackend> {
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let chr: &mut bindings::CharBackend = self;
|
||||
|
||||
let len = buf.len().try_into().unwrap();
|
||||
let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
|
||||
errno::into_io_result(r).map(|cnt| cnt as usize)
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
let chr: &mut bindings::CharBackend = self;
|
||||
|
||||
let len = buf.len().try_into().unwrap();
|
||||
let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
|
||||
errno::into_io_result(r).and_then(|cnt| {
|
||||
if cnt as usize == buf.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::WriteZero.into())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CharBackend {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// SAFETY: accessed just to print the values
|
||||
let chr = self.inner.as_ptr();
|
||||
Debug::fmt(unsafe { &*chr }, f)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use something like PinnedDrop from the pinned_init crate
|
||||
impl Drop for CharBackend {
|
||||
fn drop(&mut self) {
|
||||
self.disable_handlers();
|
||||
}
|
||||
}
|
||||
|
||||
impl CharBackend {
|
||||
/// Enable the front-end's character device handlers, if there is an
|
||||
/// associated `Chardev`.
|
||||
pub fn enable_handlers<
|
||||
'chardev,
|
||||
'owner: 'chardev,
|
||||
T,
|
||||
CanReceiveFn: for<'a> FnCall<(&'a T,), u32>,
|
||||
ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>,
|
||||
EventFn: for<'a> FnCall<(&'a T, Event)>,
|
||||
>(
|
||||
// When "self" is dropped, the handlers are automatically disabled.
|
||||
// However, this is not necessarily true if the owner is dropped.
|
||||
// So require the owner to outlive the character device.
|
||||
&'chardev self,
|
||||
owner: &'owner T,
|
||||
_can_receive: CanReceiveFn,
|
||||
_receive: ReceiveFn,
|
||||
_event: EventFn,
|
||||
) {
|
||||
unsafe extern "C" fn rust_can_receive_cb<T, F: for<'a> FnCall<(&'a T,), u32>>(
|
||||
opaque: *mut c_void,
|
||||
) -> c_int {
|
||||
// SAFETY: the values are safe according to the contract of
|
||||
// enable_handlers() and qemu_chr_fe_set_handlers()
|
||||
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
|
||||
let r = F::call((owner,));
|
||||
r.try_into().unwrap()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn rust_receive_cb<T, F: for<'a, 'b> FnCall<(&'a T, &'b [u8])>>(
|
||||
opaque: *mut c_void,
|
||||
buf: *const u8,
|
||||
size: c_int,
|
||||
) {
|
||||
// SAFETY: the values are safe according to the contract of
|
||||
// enable_handlers() and qemu_chr_fe_set_handlers()
|
||||
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
|
||||
let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) };
|
||||
F::call((owner, buf))
|
||||
}
|
||||
|
||||
unsafe extern "C" fn rust_event_cb<T, F: for<'a> FnCall<(&'a T, Event)>>(
|
||||
opaque: *mut c_void,
|
||||
event: Event,
|
||||
) {
|
||||
// SAFETY: the values are safe according to the contract of
|
||||
// enable_handlers() and qemu_chr_fe_set_handlers()
|
||||
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
|
||||
F::call((owner, event))
|
||||
}
|
||||
|
||||
let _: () = CanReceiveFn::ASSERT_IS_SOME;
|
||||
let receive_cb: Option<unsafe extern "C" fn(*mut c_void, *const u8, c_int)> =
|
||||
if ReceiveFn::is_some() {
|
||||
Some(rust_receive_cb::<T, ReceiveFn>)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let event_cb: Option<unsafe extern "C" fn(*mut c_void, Event)> = if EventFn::is_some() {
|
||||
Some(rust_event_cb::<T, EventFn>)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut chr = self.inner.borrow_mut();
|
||||
// SAFETY: the borrow promises that the BQL is taken
|
||||
unsafe {
|
||||
bindings::qemu_chr_fe_set_handlers(
|
||||
addr_of_mut!(*chr),
|
||||
Some(rust_can_receive_cb::<T, CanReceiveFn>),
|
||||
receive_cb,
|
||||
event_cb,
|
||||
None,
|
||||
(owner as *const T as *mut T).cast::<c_void>(),
|
||||
core::ptr::null_mut(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the front-end's character device handlers.
|
||||
pub fn disable_handlers(&self) {
|
||||
let mut chr = self.inner.borrow_mut();
|
||||
// SAFETY: the borrow promises that the BQL is taken
|
||||
unsafe {
|
||||
bindings::qemu_chr_fe_set_handlers(
|
||||
addr_of_mut!(*chr),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
core::ptr::null_mut(),
|
||||
core::ptr::null_mut(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify that the frontend is ready to receive data.
|
||||
pub fn accept_input(&self) {
|
||||
let mut chr = self.inner.borrow_mut();
|
||||
// SAFETY: the borrow promises that the BQL is taken
|
||||
unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) }
|
||||
}
|
||||
|
||||
/// Temporarily borrow the character device, allowing it to be used
|
||||
/// as an implementor of `Write`. Note that it is not valid to drop
|
||||
/// the big QEMU lock while the character device is borrowed, as
|
||||
/// that might cause C code to write to the character device.
|
||||
pub fn borrow_mut(&self) -> impl Write + '_ {
|
||||
self.inner.borrow_mut()
|
||||
}
|
||||
|
||||
/// Send a continuous stream of zero bits on the line if `enabled` is
|
||||
/// true, or a short stream if `enabled` is false.
|
||||
pub fn send_break(&self, long: bool) -> io::Result<()> {
|
||||
let mut chr = self.inner.borrow_mut();
|
||||
let mut duration: c_int = long.into();
|
||||
// SAFETY: the borrow promises that the BQL is taken
|
||||
let r = unsafe {
|
||||
bindings::qemu_chr_fe_ioctl(
|
||||
addr_of_mut!(*chr),
|
||||
bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32,
|
||||
addr_of_mut!(duration).cast::<c_void>(),
|
||||
)
|
||||
};
|
||||
|
||||
errno::into_io_result(r).map(|_| ())
|
||||
}
|
||||
|
||||
/// Write data to a character backend from the front end. This function
|
||||
/// will send data from the front end to the back end. Unlike
|
||||
/// `write`, this function will block if the back end cannot
|
||||
/// consume all of the data attempted to be written.
|
||||
///
|
||||
/// Returns the number of bytes consumed (0 if no associated Chardev) or an
|
||||
/// error.
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let len = buf.len().try_into().unwrap();
|
||||
// SAFETY: qemu_chr_fe_write is thread-safe
|
||||
let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) };
|
||||
errno::into_io_result(r).map(|cnt| cnt as usize)
|
||||
}
|
||||
|
||||
/// Write data to a character backend from the front end. This function
|
||||
/// will send data from the front end to the back end. Unlike
|
||||
/// `write`, this function will block if the back end cannot
|
||||
/// consume all of the data attempted to be written.
|
||||
///
|
||||
/// Returns the number of bytes consumed (0 if no associated Chardev) or an
|
||||
/// error.
|
||||
pub fn write_all(&self, buf: &[u8]) -> io::Result<()> {
|
||||
let len = buf.len().try_into().unwrap();
|
||||
// SAFETY: qemu_chr_fe_write_all is thread-safe
|
||||
let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) };
|
||||
errno::into_io_result(r).and_then(|cnt| {
|
||||
if cnt as usize == buf.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::WriteZero.into())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ObjectType for Chardev {
|
||||
type Class = ChardevClass;
|
||||
|
|
|
@ -8,10 +8,16 @@ use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr};
|
|||
|
||||
use crate::{
|
||||
bindings::{self, qemu_set_irq},
|
||||
cell::Opaque,
|
||||
prelude::*,
|
||||
qom::ObjectClass,
|
||||
};
|
||||
|
||||
/// An opaque wrapper around [`bindings::IRQState`].
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct IRQState(Opaque<bindings::IRQState>);
|
||||
|
||||
/// Interrupt sources are used by devices to pass changes to a value (typically
|
||||
/// a boolean). The interrupt sink is usually an interrupt controller or
|
||||
/// GPIO controller.
|
||||
|
@ -21,8 +27,7 @@ use crate::{
|
|||
/// method sends a `true` value to the sink. If the guest has to see a
|
||||
/// different polarity, that change is performed by the board between the
|
||||
/// device and the interrupt controller.
|
||||
pub type IRQState = bindings::IRQState;
|
||||
|
||||
///
|
||||
/// Interrupts are implemented as a pointer to the interrupt "sink", which has
|
||||
/// type [`IRQState`]. A device exposes its source as a QOM link property using
|
||||
/// a function such as [`SysBusDeviceMethods::init_irq`], and
|
||||
|
@ -40,7 +45,7 @@ pub struct InterruptSource<T = bool>
|
|||
where
|
||||
c_int: From<T>,
|
||||
{
|
||||
cell: BqlCell<*mut IRQState>,
|
||||
cell: BqlCell<*mut bindings::IRQState>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
|
@ -79,11 +84,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState {
|
||||
pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState {
|
||||
self.cell.as_ptr()
|
||||
}
|
||||
|
||||
pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut IRQState {
|
||||
pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState {
|
||||
assert!(!slice.is_empty());
|
||||
slice[0].as_ptr()
|
||||
}
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
marker::PhantomData,
|
||||
os::raw::{c_uint, c_void},
|
||||
ptr::addr_of,
|
||||
};
|
||||
|
||||
pub use bindings::{hwaddr, MemTxAttrs};
|
||||
|
@ -16,6 +15,7 @@ pub use bindings::{hwaddr, MemTxAttrs};
|
|||
use crate::{
|
||||
bindings::{self, device_endian, memory_region_init_io},
|
||||
callbacks::FnCall,
|
||||
cell::Opaque,
|
||||
prelude::*,
|
||||
zeroable::Zeroable,
|
||||
};
|
||||
|
@ -132,13 +132,13 @@ impl<T> Default for MemoryRegionOpsBuilder<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A safe wrapper around [`bindings::MemoryRegion`]. Compared to the
|
||||
/// underlying C struct it is marked as pinned because the QOM tree
|
||||
/// contains a pointer to it.
|
||||
pub struct MemoryRegion {
|
||||
inner: bindings::MemoryRegion,
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
/// A safe wrapper around [`bindings::MemoryRegion`].
|
||||
#[repr(transparent)]
|
||||
#[derive(qemu_api_macros::Wrapper)]
|
||||
pub struct MemoryRegion(Opaque<bindings::MemoryRegion>);
|
||||
|
||||
unsafe impl Send for MemoryRegion {}
|
||||
unsafe impl Sync for MemoryRegion {}
|
||||
|
||||
impl MemoryRegion {
|
||||
// inline to ensure that it is not included in tests, which only
|
||||
|
@ -157,7 +157,7 @@ impl MemoryRegion {
|
|||
let cstr = CString::new(name).unwrap();
|
||||
memory_region_init_io(
|
||||
slot,
|
||||
owner.cast::<Object>(),
|
||||
owner.cast::<bindings::Object>(),
|
||||
ops,
|
||||
owner.cast::<c_void>(),
|
||||
cstr.as_ptr(),
|
||||
|
@ -174,13 +174,15 @@ impl MemoryRegion {
|
|||
size: u64,
|
||||
) {
|
||||
unsafe {
|
||||
Self::do_init_io(&mut self.inner, owner.cast::<Object>(), &ops.0, name, size);
|
||||
Self::do_init_io(
|
||||
self.0.as_mut_ptr(),
|
||||
owner.cast::<Object>(),
|
||||
&ops.0,
|
||||
name,
|
||||
size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn as_mut_ptr(&self) -> *mut bindings::MemoryRegion {
|
||||
addr_of!(self.inner) as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ObjectType for MemoryRegion {
|
||||
|
|
|
@ -17,7 +17,6 @@ pub use crate::qom::InterfaceType;
|
|||
pub use crate::qom::IsA;
|
||||
pub use crate::qom::Object;
|
||||
pub use crate::qom::ObjectCast;
|
||||
pub use crate::qom::ObjectCastMut;
|
||||
pub use crate::qom::ObjectDeref;
|
||||
pub use crate::qom::ObjectClassMethods;
|
||||
pub use crate::qom::ObjectMethods;
|
||||
|
|
|
@ -10,12 +10,12 @@ use std::{
|
|||
ptr::NonNull,
|
||||
};
|
||||
|
||||
pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType};
|
||||
pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
|
||||
|
||||
use crate::{
|
||||
bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass},
|
||||
callbacks::FnCall,
|
||||
cell::bql_locked,
|
||||
cell::{bql_locked, Opaque},
|
||||
chardev::Chardev,
|
||||
irq::InterruptSource,
|
||||
prelude::*,
|
||||
|
@ -23,6 +23,22 @@ use crate::{
|
|||
vmstate::VMStateDescription,
|
||||
};
|
||||
|
||||
/// A safe wrapper around [`bindings::Clock`].
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct Clock(Opaque<bindings::Clock>);
|
||||
|
||||
unsafe impl Send for Clock {}
|
||||
unsafe impl Sync for Clock {}
|
||||
|
||||
/// A safe wrapper around [`bindings::DeviceState`].
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct DeviceState(Opaque<bindings::DeviceState>);
|
||||
|
||||
unsafe impl Send for DeviceState {}
|
||||
unsafe impl Sync for DeviceState {}
|
||||
|
||||
/// Trait providing the contents of the `ResettablePhases` struct,
|
||||
/// which is part of the QOM `Resettable` interface.
|
||||
pub trait ResettablePhasesImpl {
|
||||
|
@ -52,7 +68,7 @@ pub trait ResettablePhasesImpl {
|
|||
/// can be downcasted to type `T`. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
unsafe extern "C" fn rust_resettable_enter_fn<T: ResettablePhasesImpl>(
|
||||
obj: *mut Object,
|
||||
obj: *mut bindings::Object,
|
||||
typ: ResetType,
|
||||
) {
|
||||
let state = NonNull::new(obj).unwrap().cast::<T>();
|
||||
|
@ -65,7 +81,7 @@ unsafe extern "C" fn rust_resettable_enter_fn<T: ResettablePhasesImpl>(
|
|||
/// can be downcasted to type `T`. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
unsafe extern "C" fn rust_resettable_hold_fn<T: ResettablePhasesImpl>(
|
||||
obj: *mut Object,
|
||||
obj: *mut bindings::Object,
|
||||
typ: ResetType,
|
||||
) {
|
||||
let state = NonNull::new(obj).unwrap().cast::<T>();
|
||||
|
@ -78,7 +94,7 @@ unsafe extern "C" fn rust_resettable_hold_fn<T: ResettablePhasesImpl>(
|
|||
/// can be downcasted to type `T`. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>(
|
||||
obj: *mut Object,
|
||||
obj: *mut bindings::Object,
|
||||
typ: ResetType,
|
||||
) {
|
||||
let state = NonNull::new(obj).unwrap().cast::<T>();
|
||||
|
@ -117,7 +133,10 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
|
|||
/// We expect the FFI user of this function to pass a valid pointer that
|
||||
/// can be downcasted to type `T`. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) {
|
||||
unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(
|
||||
dev: *mut bindings::DeviceState,
|
||||
_errp: *mut *mut Error,
|
||||
) {
|
||||
let state = NonNull::new(dev).unwrap().cast::<T>();
|
||||
T::REALIZE.unwrap()(unsafe { state.as_ref() });
|
||||
}
|
||||
|
@ -251,7 +270,7 @@ where
|
|||
events: ClockEvent,
|
||||
) -> Owned<Clock> {
|
||||
fn do_init_clock_in(
|
||||
dev: *mut DeviceState,
|
||||
dev: &DeviceState,
|
||||
name: &str,
|
||||
cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
|
||||
events: ClockEvent,
|
||||
|
@ -265,14 +284,15 @@ where
|
|||
unsafe {
|
||||
let cstr = CString::new(name).unwrap();
|
||||
let clk = bindings::qdev_init_clock_in(
|
||||
dev,
|
||||
dev.as_mut_ptr(),
|
||||
cstr.as_ptr(),
|
||||
cb,
|
||||
dev.cast::<c_void>(),
|
||||
dev.as_void_ptr(),
|
||||
events.0,
|
||||
);
|
||||
|
||||
Owned::from(&*clk)
|
||||
let clk: &Clock = Clock::from_raw(clk);
|
||||
Owned::from(clk)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,7 +309,7 @@ where
|
|||
None
|
||||
};
|
||||
|
||||
do_init_clock_in(self.as_mut_ptr(), name, cb, events)
|
||||
do_init_clock_in(self.upcast(), name, cb, events)
|
||||
}
|
||||
|
||||
/// Add an output clock named `name`.
|
||||
|
@ -304,17 +324,23 @@ where
|
|||
fn init_clock_out(&self, name: &str) -> Owned<Clock> {
|
||||
unsafe {
|
||||
let cstr = CString::new(name).unwrap();
|
||||
let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr());
|
||||
let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr());
|
||||
|
||||
Owned::from(&*clk)
|
||||
let clk: &Clock = Clock::from_raw(clk);
|
||||
Owned::from(clk)
|
||||
}
|
||||
}
|
||||
|
||||
fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
|
||||
assert!(bql_locked());
|
||||
let c_propname = CString::new(propname).unwrap();
|
||||
let chr: &Chardev = chr;
|
||||
unsafe {
|
||||
bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr());
|
||||
bindings::qdev_prop_set_chr(
|
||||
self.upcast().as_mut_ptr(),
|
||||
c_propname.as_ptr(),
|
||||
chr.as_mut_ptr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,8 +349,17 @@ where
|
|||
num_lines: u32,
|
||||
_cb: F,
|
||||
) {
|
||||
let _: () = F::ASSERT_IS_SOME;
|
||||
fn do_init_gpio_in(
|
||||
dev: &DeviceState,
|
||||
num_lines: u32,
|
||||
gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int),
|
||||
) {
|
||||
unsafe {
|
||||
qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int);
|
||||
}
|
||||
}
|
||||
|
||||
let _: () = F::ASSERT_IS_SOME;
|
||||
unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>(
|
||||
opaque: *mut c_void,
|
||||
line: c_int,
|
||||
|
@ -337,19 +372,13 @@ where
|
|||
let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) =
|
||||
rust_irq_handler::<Self::Target, F>;
|
||||
|
||||
unsafe {
|
||||
qdev_init_gpio_in(
|
||||
self.as_mut_ptr::<DeviceState>(),
|
||||
Some(gpio_in_cb),
|
||||
num_lines as c_int,
|
||||
);
|
||||
}
|
||||
do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb);
|
||||
}
|
||||
|
||||
fn init_gpio_out(&self, pins: &[InterruptSource]) {
|
||||
unsafe {
|
||||
qdev_init_gpio_out(
|
||||
self.as_mut_ptr::<DeviceState>(),
|
||||
self.upcast().as_mut_ptr(),
|
||||
InterruptSource::slice_as_ptr(pins),
|
||||
pins.len() as c_int,
|
||||
);
|
||||
|
|
|
@ -101,16 +101,24 @@ use std::{
|
|||
ptr::NonNull,
|
||||
};
|
||||
|
||||
pub use bindings::{Object, ObjectClass};
|
||||
pub use bindings::ObjectClass;
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
self, object_class_dynamic_cast, object_dynamic_cast, object_get_class,
|
||||
object_get_typename, object_new, object_ref, object_unref, TypeInfo,
|
||||
},
|
||||
cell::bql_locked,
|
||||
cell::{bql_locked, Opaque},
|
||||
};
|
||||
|
||||
/// A safe wrapper around [`bindings::Object`].
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct Object(Opaque<bindings::Object>);
|
||||
|
||||
unsafe impl Send for Object {}
|
||||
unsafe impl Sync for Object {}
|
||||
|
||||
/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct
|
||||
/// or indirect parent of `Self`).
|
||||
///
|
||||
|
@ -199,7 +207,7 @@ impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
|
||||
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut bindings::Object) {
|
||||
let mut state = NonNull::new(obj).unwrap().cast::<T>();
|
||||
// SAFETY: obj is an instance of T, since rust_instance_init<T>
|
||||
// is called from QOM core as the instance_init function
|
||||
|
@ -209,7 +217,7 @@ unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) {
|
||||
unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut bindings::Object) {
|
||||
let state = NonNull::new(obj).unwrap().cast::<T>();
|
||||
// SAFETY: obj is an instance of T, since rust_instance_post_init<T>
|
||||
// is called from QOM core as the instance_post_init function
|
||||
|
@ -230,7 +238,7 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ObjectImpl>(
|
|||
<T as ObjectImpl>::CLASS_INIT(unsafe { klass.as_mut() })
|
||||
}
|
||||
|
||||
unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
|
||||
unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut bindings::Object) {
|
||||
// SAFETY: obj is an instance of T, since drop_object<T> is called
|
||||
// from the QOM core function object_deinit() as the instance_finalize
|
||||
// function for class T. Note that while object_deinit() will drop the
|
||||
|
@ -280,14 +288,14 @@ pub unsafe trait ObjectType: Sized {
|
|||
/// Return the receiver as an Object. This is always safe, even
|
||||
/// if this type represents an interface.
|
||||
fn as_object(&self) -> &Object {
|
||||
unsafe { &*self.as_object_ptr() }
|
||||
unsafe { &*self.as_ptr().cast() }
|
||||
}
|
||||
|
||||
/// Return the receiver as a const raw pointer to Object.
|
||||
/// This is preferrable to `as_object_mut_ptr()` if a C
|
||||
/// function only needs a `const Object *`.
|
||||
fn as_object_ptr(&self) -> *const Object {
|
||||
self.as_ptr().cast()
|
||||
fn as_object_ptr(&self) -> *const bindings::Object {
|
||||
self.as_object().as_ptr()
|
||||
}
|
||||
|
||||
/// Return the receiver as a mutable raw pointer to Object.
|
||||
|
@ -297,8 +305,8 @@ pub unsafe trait ObjectType: Sized {
|
|||
/// This cast is always safe, but because the result is mutable
|
||||
/// and the incoming reference is not, this should only be used
|
||||
/// for calls to C functions, and only if needed.
|
||||
unsafe fn as_object_mut_ptr(&self) -> *mut Object {
|
||||
self.as_object_ptr() as *mut _
|
||||
unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object {
|
||||
self.as_object().as_mut_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,90 +463,7 @@ where
|
|||
impl<T: ObjectType> ObjectDeref for &T {}
|
||||
impl<T: ObjectType> ObjectCast for &T {}
|
||||
|
||||
/// Trait for mutable type casting operations in the QOM hierarchy.
|
||||
///
|
||||
/// This trait provides the mutable counterparts to [`ObjectCast`]'s conversion
|
||||
/// functions. Unlike `ObjectCast`, this trait returns `Result` for fallible
|
||||
/// conversions to preserve the original smart pointer if the cast fails. This
|
||||
/// is necessary because mutable references cannot be copied, so a failed cast
|
||||
/// must return ownership of the original reference. For example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut dev = get_device();
|
||||
/// // If this fails, we need the original `dev` back to try something else
|
||||
/// match dev.dynamic_cast_mut::<FooDevice>() {
|
||||
/// Ok(foodev) => /* use foodev */,
|
||||
/// Err(dev) => /* still have ownership of dev */
|
||||
/// }
|
||||
/// ```
|
||||
pub trait ObjectCastMut: Sized + ObjectDeref + DerefMut
|
||||
where
|
||||
Self::Target: ObjectType,
|
||||
{
|
||||
/// Safely convert from a derived type to one of its parent types.
|
||||
///
|
||||
/// This is always safe; the [`IsA`] trait provides static verification
|
||||
/// that `Self` dereferences to `U` or a child of `U`.
|
||||
fn upcast_mut<'a, U: ObjectType>(self) -> &'a mut U
|
||||
where
|
||||
Self::Target: IsA<U>,
|
||||
Self: 'a,
|
||||
{
|
||||
// SAFETY: soundness is declared via IsA<U>, which is an unsafe trait
|
||||
unsafe { self.unsafe_cast_mut::<U>() }
|
||||
}
|
||||
|
||||
/// Attempt to convert to a derived type.
|
||||
///
|
||||
/// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the
|
||||
/// object if the conversion failed. This is verified at runtime by
|
||||
/// checking the object's type information.
|
||||
fn downcast_mut<'a, U: IsA<Self::Target>>(self) -> Result<&'a mut U, Self>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
self.dynamic_cast_mut::<U>()
|
||||
}
|
||||
|
||||
/// Attempt to convert between any two types in the QOM hierarchy.
|
||||
///
|
||||
/// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the
|
||||
/// object if the conversion failed. This is verified at runtime by
|
||||
/// checking the object's type information.
|
||||
fn dynamic_cast_mut<'a, U: ObjectType>(self) -> Result<&'a mut U, Self>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
unsafe {
|
||||
// SAFETY: upcasting to Object is always valid, and the
|
||||
// return type is either NULL or the argument itself
|
||||
let result: *mut U =
|
||||
object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast();
|
||||
|
||||
result.as_mut().ok_or(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to any QOM type without verification.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// What safety? You need to know yourself that the cast is correct; only
|
||||
/// use when performance is paramount. It is still better than a raw
|
||||
/// pointer `cast()`, which does not even check that you remain in the
|
||||
/// realm of QOM `ObjectType`s.
|
||||
///
|
||||
/// `unsafe_cast::<Object>()` is always safe.
|
||||
unsafe fn unsafe_cast_mut<'a, U: ObjectType>(self) -> &'a mut U
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
unsafe { &mut *self.as_mut_ptr::<Self::Target>().cast::<U>() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectType> ObjectDeref for &mut T {}
|
||||
impl<T: ObjectType> ObjectCastMut for &mut T {}
|
||||
|
||||
/// Trait a type must implement to be registered with QEMU.
|
||||
pub trait ObjectImpl: ObjectType + IsA<Object> {
|
||||
|
@ -621,7 +546,7 @@ pub trait ObjectImpl: ObjectType + IsA<Object> {
|
|||
/// We expect the FFI user of this function to pass a valid pointer that
|
||||
/// can be downcasted to type `T`. We also expect the device is
|
||||
/// readable/writeable from one thread at any time.
|
||||
unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
|
||||
unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut bindings::Object) {
|
||||
let state = NonNull::new(dev).unwrap().cast::<T>();
|
||||
T::UNPARENT.unwrap()(unsafe { state.as_ref() });
|
||||
}
|
||||
|
@ -796,8 +721,9 @@ pub trait ObjectClassMethods: IsA<Object> {
|
|||
// SAFETY: the object created by object_new is allocated on
|
||||
// the heap and has a reference count of 1
|
||||
unsafe {
|
||||
let obj = &*object_new(Self::TYPE_NAME.as_ptr());
|
||||
Owned::from_raw(obj.unsafe_cast::<Self>())
|
||||
let raw_obj = object_new(Self::TYPE_NAME.as_ptr());
|
||||
let obj = Object::from_raw(raw_obj).unsafe_cast::<Self>();
|
||||
Owned::from_raw(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
use std::{ffi::CStr, ptr::addr_of_mut};
|
||||
|
||||
pub use bindings::{SysBusDevice, SysBusDeviceClass};
|
||||
pub use bindings::SysBusDeviceClass;
|
||||
|
||||
use crate::{
|
||||
bindings,
|
||||
cell::bql_locked,
|
||||
cell::{bql_locked, Opaque},
|
||||
irq::{IRQState, InterruptSource},
|
||||
memory::MemoryRegion,
|
||||
prelude::*,
|
||||
|
@ -18,6 +18,14 @@ use crate::{
|
|||
qom::Owned,
|
||||
};
|
||||
|
||||
/// A safe wrapper around [`bindings::SysBusDevice`].
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct SysBusDevice(Opaque<bindings::SysBusDevice>);
|
||||
|
||||
unsafe impl Send for SysBusDevice {}
|
||||
unsafe impl Sync for SysBusDevice {}
|
||||
|
||||
unsafe impl ObjectType for SysBusDevice {
|
||||
type Class = SysBusDeviceClass;
|
||||
const TYPE_NAME: &'static CStr =
|
||||
|
@ -49,7 +57,7 @@ where
|
|||
fn init_mmio(&self, iomem: &MemoryRegion) {
|
||||
assert!(bql_locked());
|
||||
unsafe {
|
||||
bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr());
|
||||
bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +68,21 @@ where
|
|||
fn init_irq(&self, irq: &InterruptSource) {
|
||||
assert!(bql_locked());
|
||||
unsafe {
|
||||
bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
|
||||
bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: do we want a type like GuestAddress here?
|
||||
fn mmio_addr(&self, id: u32) -> Option<u64> {
|
||||
assert!(bql_locked());
|
||||
// SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and
|
||||
// the SysBusDevice must be initialized to get an IsA<SysBusDevice>.
|
||||
let sbd = unsafe { *self.upcast().as_ptr() };
|
||||
let id: usize = id.try_into().unwrap();
|
||||
if sbd.mmio[id].memory.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(sbd.mmio[id].addr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +91,7 @@ where
|
|||
assert!(bql_locked());
|
||||
let id: i32 = id.try_into().unwrap();
|
||||
unsafe {
|
||||
bindings::sysbus_mmio_map(self.as_mut_ptr(), id, addr);
|
||||
bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,8 +101,9 @@ where
|
|||
fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
|
||||
assert!(bql_locked());
|
||||
let id: i32 = id.try_into().unwrap();
|
||||
let irq: &IRQState = irq;
|
||||
unsafe {
|
||||
bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr());
|
||||
bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +111,10 @@ where
|
|||
// TODO: return an Error
|
||||
assert!(bql_locked());
|
||||
unsafe {
|
||||
bindings::sysbus_realize(self.as_mut_ptr(), addr_of_mut!(bindings::error_fatal));
|
||||
bindings::sysbus_realize(
|
||||
self.upcast().as_mut_ptr(),
|
||||
addr_of_mut!(bindings::error_fatal),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,31 +2,51 @@
|
|||
// Author(s): Zhao Liu <zhai1.liu@intel.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{
|
||||
os::raw::{c_int, c_void},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType},
|
||||
callbacks::FnCall,
|
||||
cell::Opaque,
|
||||
};
|
||||
|
||||
pub type Timer = bindings::QEMUTimer;
|
||||
pub type TimerListGroup = bindings::QEMUTimerListGroup;
|
||||
/// A safe wrapper around [`bindings::QEMUTimer`].
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, qemu_api_macros::Wrapper)]
|
||||
pub struct Timer(Opaque<bindings::QEMUTimer>);
|
||||
|
||||
unsafe impl Send for Timer {}
|
||||
unsafe impl Sync for Timer {}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(qemu_api_macros::Wrapper)]
|
||||
pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
|
||||
|
||||
unsafe impl Send for TimerListGroup {}
|
||||
unsafe impl Sync for TimerListGroup {}
|
||||
|
||||
impl Timer {
|
||||
pub const MS: u32 = bindings::SCALE_MS;
|
||||
pub const US: u32 = bindings::SCALE_US;
|
||||
pub const NS: u32 = bindings::SCALE_NS;
|
||||
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
const fn as_mut_ptr(&self) -> *mut Self {
|
||||
self as *const Timer as *mut _
|
||||
/// Create a `Timer` struct without initializing it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The timer must be initialized before it is armed with
|
||||
/// [`modify`](Self::modify).
|
||||
pub unsafe fn new() -> Self {
|
||||
// SAFETY: requirements relayed to callers of Timer::new
|
||||
Self(unsafe { Opaque::zeroed() })
|
||||
}
|
||||
|
||||
/// Create a new timer with the given attributes.
|
||||
pub fn init_full<'timer, 'opaque: 'timer, T, F>(
|
||||
&'timer mut self,
|
||||
self: Pin<&'timer mut Self>,
|
||||
timer_list_group: Option<&TimerListGroup>,
|
||||
clk_type: ClockType,
|
||||
scale: u32,
|
||||
|
@ -51,7 +71,7 @@ impl Timer {
|
|||
// SAFETY: the opaque outlives the timer
|
||||
unsafe {
|
||||
timer_init_full(
|
||||
self,
|
||||
self.as_mut_ptr(),
|
||||
if let Some(g) = timer_list_group {
|
||||
g as *const TimerListGroup as *mut _
|
||||
} else {
|
||||
|
@ -67,14 +87,19 @@ impl Timer {
|
|||
}
|
||||
|
||||
pub fn modify(&self, expire_time: u64) {
|
||||
// SAFETY: the only way to obtain a Timer safely is via methods that
|
||||
// take a Pin<&mut Self>, therefore the timer is pinned
|
||||
unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) }
|
||||
}
|
||||
|
||||
pub fn delete(&self) {
|
||||
// SAFETY: the only way to obtain a Timer safely is via methods that
|
||||
// take a Pin<&mut Self>, therefore the timer is pinned
|
||||
unsafe { timer_del(self.as_mut_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use something like PinnedDrop from the pinned_init crate
|
||||
impl Drop for Timer {
|
||||
fn drop(&mut self) {
|
||||
self.delete()
|
||||
|
|
|
@ -330,6 +330,7 @@ macro_rules! impl_vmstate_transparent {
|
|||
|
||||
impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
|
||||
impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
|
||||
impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
|
||||
impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
|
||||
impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
|
||||
|
||||
|
@ -469,7 +470,7 @@ macro_rules! vmstate_clock {
|
|||
$crate::assert_field_type!(
|
||||
$struct_name,
|
||||
$field_name,
|
||||
$crate::qom::Owned<$crate::bindings::Clock>
|
||||
$crate::qom::Owned<$crate::qdev::Clock>
|
||||
);
|
||||
$crate::offset_of!($struct_name, $field_name)
|
||||
},
|
||||
|
|
|
@ -106,3 +106,4 @@ impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1);
|
|||
impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2);
|
||||
impl_zeroable!(crate::bindings::MemoryRegionOps);
|
||||
impl_zeroable!(crate::bindings::MemTxAttrs);
|
||||
impl_zeroable!(crate::bindings::CharBackend);
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use std::{
|
||||
ffi::{c_void, CStr},
|
||||
ptr::{addr_of, addr_of_mut},
|
||||
};
|
||||
use std::{ffi::CStr, ptr::addr_of};
|
||||
|
||||
use qemu_api::{
|
||||
bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool},
|
||||
bindings::{module_call_init, module_init_type, qdev_prop_bool},
|
||||
c_str,
|
||||
cell::{self, BqlCell},
|
||||
declare_properties, define_property,
|
||||
|
@ -182,30 +179,3 @@ fn test_cast() {
|
|||
assert_eq!(addr_of!(*sbd_ref), p_ptr.cast());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::shadow_unrelated)]
|
||||
/// Test casts on mutable references.
|
||||
fn test_cast_mut() {
|
||||
init_qom();
|
||||
let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
|
||||
|
||||
let p_ref: &mut DummyState = unsafe { &mut *p };
|
||||
let obj_ref: &mut Object = p_ref.upcast_mut();
|
||||
assert_eq!(addr_of_mut!(*obj_ref), p.cast());
|
||||
|
||||
let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut();
|
||||
let obj_ref = sbd_ref.unwrap_err();
|
||||
|
||||
let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut();
|
||||
let dev_ref = dev_ref.unwrap();
|
||||
assert_eq!(addr_of_mut!(*dev_ref), p.cast());
|
||||
|
||||
// SAFETY: the cast is wrong, but the value is only used for comparison
|
||||
unsafe {
|
||||
let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut();
|
||||
assert_eq!(addr_of_mut!(*sbd_ref), p.cast());
|
||||
|
||||
object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,8 +241,14 @@ def print_parse(options):
|
|||
print(" esac")
|
||||
print("}")
|
||||
|
||||
|
||||
options = load_options(json.load(sys.stdin))
|
||||
json_data = sys.stdin.read()
|
||||
try:
|
||||
options = load_options(json.loads(json_data))
|
||||
except:
|
||||
print("Failure in scripts/meson-buildoptions.py parsing stdin as json",
|
||||
file=sys.stderr)
|
||||
print(json_data, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print("# This file is generated by meson-buildoptions.py, do not edit!")
|
||||
print_help(options)
|
||||
print_parse(options)
|
||||
|
|
|
@ -504,6 +504,8 @@ _meson_option_parse() {
|
|||
--disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;;
|
||||
--enable-strip) printf "%s" -Dstrip=true ;;
|
||||
--disable-strip) printf "%s" -Dstrip=false ;;
|
||||
--enable-split-debug) printf "%s" -Dsplit_debug=true ;;
|
||||
--disable-split-debug) printf "%s" -Dsplit_debug=false ;;
|
||||
--sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;;
|
||||
--enable-tcg) printf "%s" -Dtcg=enabled ;;
|
||||
--disable-tcg) printf "%s" -Dtcg=disabled ;;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue