mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* rust: more qdev bindings * rust: HPET device model with timer and GPIO bindings * rust: small cleanups and fixes; run doctests during CI * ui/sdl2: reenable the SDL2 Windows keyboard hook procedure # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmet6qkUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroO4yQgAjSpJ8DChoEVrm6xgCUGPkC7VlI0A # 3WimcgiTUCUVqiywvLmObHRv9ld/b9mJ+2v/actDy39qioN3i3+RGpyeSRcysITd # 2AWQVOe6JuVfEyN+ihYq3yS3v1meDhzZbOzRNHgbTX20rMy/HWJFIvQbK4abQaVI # j8zaPYIjcfcH/ScEmmha88l6PJDMPy7fCEzQWx41oHKkQ8w4rhmarn9f3WcXB/SN # bCvm2NmkJFPsU/TCynWz7YSjrLWCsWjiDgxoDD1295QoeEvfcuD8Z6vPIA9BttGx # MUgcrXi4KnJI8W9gm5jAiKq+DSxFX6f7AwUDfb2l+Vrkq84s7bu9UVNQqA== # =/vpW # -----END PGP SIGNATURE----- # gpg: Signature made Thu 13 Feb 2025 07:50:49 EST # 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: (27 commits) ui/sdl2: reenable the SDL2 Windows keyboard hook procedure rust: fix doctests rust: vmstate: remove redundant link targets rust: qemu_api: add a documentation header for all modules i386: enable rust hpet for pc when rust is enabled rust/timer/hpet: add qom and qdev APIs support rust/timer/hpet: add basic HPET timer and HPETState rust/timer/hpet: define hpet_fw_cfg rust: add bindings for timer rust: add bindings for memattrs rust: add bindings for gpio_{in|out} initialization rust/irq: Add a helper to convert [InterruptSource] to pointer rust/qdev: add the macro to define bit property i386/fw_cfg: move hpet_cfg definition to hpet.c rust: pl011: convert pl011_create to safe Rust rust: chardev, qdev: add bindings to qdev_prop_set_chr rust: irq: define ObjectType for IRQState rust: bindings for MemoryRegionOps rust: bindings: add Send and Sync markers for types that have bindings rust: qdev: switch from legacy reset to Resettable ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
@@ -131,6 +131,12 @@ build-system-fedora-rust-nightly:
|
||||
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
|
||||
TARGETS: aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
after_script:
|
||||
- source scripts/ci/gitlab-ci-section
|
||||
- section_start test "Running Rust doctests"
|
||||
- cd build
|
||||
- pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api
|
||||
|
||||
allow_failure: true
|
||||
|
||||
check-system-fedora:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#CONFIG_APPLESMC=n
|
||||
#CONFIG_FDC=n
|
||||
#CONFIG_HPET=n
|
||||
#CONFIG_X_HPET_RUST=n
|
||||
#CONFIG_HYPERV=n
|
||||
#CONFIG_ISA_DEBUG=n
|
||||
#CONFIG_ISA_IPMI_BT=n
|
||||
|
||||
@@ -180,11 +180,13 @@ module status
|
||||
``cell`` stable
|
||||
``c_str`` complete
|
||||
``irq`` complete
|
||||
``memory`` stable
|
||||
``module`` complete
|
||||
``offset_of`` stable
|
||||
``qdev`` stable
|
||||
``qom`` stable
|
||||
``sysbus`` stable
|
||||
``timer`` stable
|
||||
``vmstate`` proof of concept
|
||||
``zeroable`` stable
|
||||
================ ======================
|
||||
@@ -194,6 +196,50 @@ module status
|
||||
interface either. Also, ``unsafe`` interfaces may be replaced by safe interfaces
|
||||
later.
|
||||
|
||||
Naming convention
|
||||
'''''''''''''''''
|
||||
|
||||
C function names usually are prefixed according to the data type that they
|
||||
apply to, for example ``timer_mod`` or ``sysbus_connect_irq``. Furthermore,
|
||||
both function and structs sometimes have a ``qemu_`` or ``QEMU`` prefix.
|
||||
Generally speaking, these are all removed in the corresponding Rust functions:
|
||||
``QEMUTimer`` becomes ``timer::Timer``, ``timer_mod`` becomes ``Timer::modify``,
|
||||
``sysbus_connect_irq`` becomes ``SysBusDeviceMethods::connect_irq``.
|
||||
|
||||
Sometimes however a name appears multiple times in the QOM class hierarchy,
|
||||
and the only difference is in the prefix. An example is ``qdev_realize`` and
|
||||
``sysbus_realize``. In such cases, whenever a name is not unique in
|
||||
the hierarchy, always add the prefix to the classes that are lower in
|
||||
the hierarchy; for the top class, decide on a case by case basis.
|
||||
|
||||
For example:
|
||||
|
||||
========================== =========================================
|
||||
``device_cold_reset()`` ``DeviceMethods::cold_reset()``
|
||||
``pci_device_reset()`` ``PciDeviceMethods::pci_device_reset()``
|
||||
``pci_bridge_reset()`` ``PciBridgeMethods::pci_bridge_reset()``
|
||||
========================== =========================================
|
||||
|
||||
Here, the name is not exactly the same, but nevertheless ``PciDeviceMethods``
|
||||
adds the prefix to avoid confusion, because the functionality of
|
||||
``device_cold_reset()`` and ``pci_device_reset()`` is subtly different.
|
||||
|
||||
In this case, however, no prefix is needed:
|
||||
|
||||
========================== =========================================
|
||||
``device_realize()`` ``DeviceMethods::realize()``
|
||||
``sysbus_realize()`` ``SysbusDeviceMethods::sysbus_realize()``
|
||||
``pci_realize()`` ``PciDeviceMethods::pci_realize()``
|
||||
========================== =========================================
|
||||
|
||||
Here, the lower classes do not add any functionality, and mostly
|
||||
provide extra compile-time checking; the basic *realize* functionality
|
||||
is the same for all devices. Therefore, ``DeviceMethods`` does not
|
||||
add the prefix.
|
||||
|
||||
Whenever a name is unique in the hierarchy, instead, you should
|
||||
always remove the class name prefix.
|
||||
|
||||
Common pitfalls
|
||||
'''''''''''''''
|
||||
|
||||
|
||||
+4
-2
@@ -26,7 +26,9 @@
|
||||
#include CONFIG_DEVICES
|
||||
#include "target/i386/cpu.h"
|
||||
|
||||
struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
|
||||
#if !defined(CONFIG_HPET) && !defined(CONFIG_X_HPET_RUST)
|
||||
struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX};
|
||||
#endif
|
||||
|
||||
const char *fw_cfg_arch_key_name(uint16_t key)
|
||||
{
|
||||
@@ -149,7 +151,7 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms,
|
||||
#endif
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1);
|
||||
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg));
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_fw_cfg, sizeof(hpet_fw_cfg));
|
||||
/* allocate memory for the NUMA channel: one (64bit) word for the number
|
||||
* of nodes, one word for each VCPU->node and one word for each node to
|
||||
* hold the amount of memory.
|
||||
|
||||
+1
-1
@@ -1701,7 +1701,7 @@ static void pc_machine_initfn(Object *obj)
|
||||
pcms->sata_enabled = true;
|
||||
pcms->i8042_enabled = true;
|
||||
pcms->max_fw_size = 8 * MiB;
|
||||
#ifdef CONFIG_HPET
|
||||
#if defined(CONFIG_HPET) || defined(CONFIG_X_HPET_RUST)
|
||||
pcms->hpet_enabled = true;
|
||||
#endif
|
||||
pcms->fd_bootchk = true;
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ config A9_GTIMER
|
||||
|
||||
config HPET
|
||||
bool
|
||||
default y if PC
|
||||
default y if PC && !HAVE_RUST
|
||||
|
||||
config I8254
|
||||
bool
|
||||
|
||||
+9
-7
@@ -40,6 +40,8 @@
|
||||
#include "qom/object.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX};
|
||||
|
||||
#define HPET_MSI_SUPPORT 0
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(HPETState, HPET)
|
||||
@@ -278,7 +280,7 @@ static int hpet_post_load(void *opaque, int version_id)
|
||||
/* Push number of timers into capability returned via HPET_ID */
|
||||
s->capability &= ~HPET_ID_NUM_TIM_MASK;
|
||||
s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
|
||||
hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
|
||||
hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
|
||||
|
||||
/* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
|
||||
s->flags &= ~(1 << HPET_MSI_SUPPORT);
|
||||
@@ -665,8 +667,8 @@ static void hpet_reset(DeviceState *d)
|
||||
s->hpet_counter = 0ULL;
|
||||
s->hpet_offset = 0ULL;
|
||||
s->config = 0ULL;
|
||||
hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
|
||||
hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr;
|
||||
hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
|
||||
hpet_fw_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr;
|
||||
|
||||
/* to document that the RTC lowers its output on reset as well */
|
||||
s->rtc_irq_level = 0;
|
||||
@@ -708,17 +710,17 @@ static void hpet_realize(DeviceState *dev, Error **errp)
|
||||
if (!s->intcap) {
|
||||
warn_report("Hpet's intcap not initialized");
|
||||
}
|
||||
if (hpet_cfg.count == UINT8_MAX) {
|
||||
if (hpet_fw_cfg.count == UINT8_MAX) {
|
||||
/* first instance */
|
||||
hpet_cfg.count = 0;
|
||||
hpet_fw_cfg.count = 0;
|
||||
}
|
||||
|
||||
if (hpet_cfg.count == 8) {
|
||||
if (hpet_fw_cfg.count == 8) {
|
||||
error_setg(errp, "Only 8 instances of HPET is allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
s->hpet_id = hpet_cfg.count++;
|
||||
s->hpet_id = hpet_fw_cfg.count++;
|
||||
|
||||
for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
|
||||
sysbus_init_irq(sbd, &s->irqs[i]);
|
||||
|
||||
@@ -73,7 +73,7 @@ struct hpet_fw_config
|
||||
struct hpet_fw_entry hpet[8];
|
||||
} QEMU_PACKED;
|
||||
|
||||
extern struct hpet_fw_config hpet_cfg;
|
||||
extern struct hpet_fw_config hpet_fw_cfg;
|
||||
|
||||
#define TYPE_HPET "hpet"
|
||||
|
||||
|
||||
@@ -4073,6 +4073,7 @@ if have_rust
|
||||
'MigrationPriority',
|
||||
'QEMUChrEvent',
|
||||
'QEMUClockType',
|
||||
'ResetType',
|
||||
'device_endian',
|
||||
'module_init_type',
|
||||
]
|
||||
@@ -4086,6 +4087,13 @@ 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.
|
||||
#
|
||||
|
||||
Generated
+8
@@ -37,6 +37,14 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "hpet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"qemu_api",
|
||||
"qemu_api_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
|
||||
@@ -4,6 +4,7 @@ members = [
|
||||
"qemu-api-macros",
|
||||
"qemu-api",
|
||||
"hw/char/pl011",
|
||||
"hw/timer/hpet",
|
||||
]
|
||||
|
||||
[workspace.lints.rust]
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# devices Kconfig
|
||||
source char/Kconfig
|
||||
source timer/Kconfig
|
||||
|
||||
@@ -10,24 +10,22 @@ use std::{
|
||||
|
||||
use qemu_api::{
|
||||
bindings::{
|
||||
error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_new,
|
||||
qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers,
|
||||
qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map,
|
||||
sysbus_realize_and_unref, CharBackend, Chardev, Clock, ClockEvent, MemoryRegion,
|
||||
QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK,
|
||||
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,
|
||||
},
|
||||
c_str, impl_vmstate_forward,
|
||||
irq::InterruptSource,
|
||||
chardev::Chardev,
|
||||
impl_vmstate_forward,
|
||||
irq::{IRQState, InterruptSource},
|
||||
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
|
||||
prelude::*,
|
||||
qdev::{DeviceImpl, DeviceState, Property},
|
||||
qom::{ClassInitImpl, ObjectImpl, ParentField},
|
||||
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
||||
qom::{ClassInitImpl, ObjectImpl, Owned, ParentField},
|
||||
sysbus::{SysBusDevice, SysBusDeviceClass},
|
||||
vmstate::VMStateDescription,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
device_class,
|
||||
memory_ops::PL011_OPS,
|
||||
registers::{self, Interrupt},
|
||||
RegisterOffset,
|
||||
};
|
||||
@@ -131,7 +129,7 @@ pub struct PL011State {
|
||||
#[doc(alias = "irq")]
|
||||
pub interrupts: [InterruptSource; IRQMASK.len()],
|
||||
#[doc(alias = "clk")]
|
||||
pub clock: NonNull<Clock>,
|
||||
pub clock: Owned<Clock>,
|
||||
#[doc(alias = "migrate_clk")]
|
||||
pub migrate_clock: bool,
|
||||
}
|
||||
@@ -172,7 +170,10 @@ impl DeviceImpl for PL011State {
|
||||
Some(&device_class::VMSTATE_PL011)
|
||||
}
|
||||
const REALIZE: Option<fn(&Self)> = Some(Self::realize);
|
||||
const RESET: Option<fn(&Self)> = Some(Self::reset);
|
||||
}
|
||||
|
||||
impl ResettablePhasesImpl for PL011State {
|
||||
const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
|
||||
}
|
||||
|
||||
impl PL011Registers {
|
||||
@@ -485,43 +486,39 @@ impl PL011State {
|
||||
/// location/instance. All its fields are expected to hold unitialized
|
||||
/// values with the sole exception of `parent_obj`.
|
||||
unsafe fn init(&mut self) {
|
||||
const CLK_NAME: &CStr = c_str!("clk");
|
||||
static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
|
||||
.read(&PL011State::read)
|
||||
.write(&PL011State::write)
|
||||
.native_endian()
|
||||
.impl_sizes(4, 4)
|
||||
.build();
|
||||
|
||||
// SAFETY:
|
||||
//
|
||||
// self and self.iomem are guaranteed to be valid at this point since callers
|
||||
// must make sure the `self` reference is valid.
|
||||
unsafe {
|
||||
memory_region_init_io(
|
||||
addr_of_mut!(self.iomem),
|
||||
addr_of_mut!(*self).cast::<Object>(),
|
||||
&PL011_OPS,
|
||||
addr_of_mut!(*self).cast::<c_void>(),
|
||||
Self::TYPE_NAME.as_ptr(),
|
||||
0x1000,
|
||||
);
|
||||
}
|
||||
MemoryRegion::init_io(
|
||||
unsafe { &mut *addr_of_mut!(self.iomem) },
|
||||
addr_of_mut!(*self),
|
||||
&PL011_OPS,
|
||||
"pl011",
|
||||
0x1000,
|
||||
);
|
||||
|
||||
self.regs = Default::default();
|
||||
|
||||
// SAFETY:
|
||||
//
|
||||
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
|
||||
// we can overwrite the undefined value without side effects. This is
|
||||
// safe since all PL011State instances are created by QOM code which
|
||||
// calls this function to initialize the fields; therefore no code is
|
||||
// able to access an invalid self.clock value.
|
||||
unsafe {
|
||||
let dev: &mut DeviceState = self.upcast_mut();
|
||||
self.clock = NonNull::new(qdev_init_clock_in(
|
||||
dev,
|
||||
CLK_NAME.as_ptr(),
|
||||
None, /* pl011_clock_update */
|
||||
addr_of_mut!(*self).cast::<c_void>(),
|
||||
ClockEvent::ClockUpdate.0,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
// self.clock is not initialized at this point; but since `Owned<_>` is
|
||||
// not Drop, we can overwrite the undefined value without side effects;
|
||||
// it's not sound but, because for all PL011State instances are created
|
||||
// by QOM code which calls this function to initialize the fields, at
|
||||
// leastno code is able to access an invalid self.clock value.
|
||||
self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate);
|
||||
}
|
||||
|
||||
const fn clock_update(&self, _event: ClockEvent) {
|
||||
/* pl011_trace_baudrate_change(s); */
|
||||
}
|
||||
|
||||
fn post_init(&self) {
|
||||
@@ -531,7 +528,7 @@ impl PL011State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 {
|
||||
pub 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;
|
||||
@@ -546,7 +543,7 @@ impl PL011State {
|
||||
if update_irq {
|
||||
self.update();
|
||||
unsafe {
|
||||
qemu_chr_fe_accept_input(&mut self.char_backend);
|
||||
qemu_chr_fe_accept_input(addr_of!(self.char_backend) as *mut _);
|
||||
}
|
||||
}
|
||||
result.into()
|
||||
@@ -554,7 +551,7 @@ impl PL011State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, offset: hwaddr, value: u64) {
|
||||
pub 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
|
||||
@@ -567,14 +564,15 @@ impl PL011State {
|
||||
// XXX this blocks entire thread. Rewrite to use
|
||||
// qemu_chr_fe_write and background I/O callbacks
|
||||
unsafe {
|
||||
qemu_chr_fe_write_all(&mut self.char_backend, &ch, 1);
|
||||
qemu_chr_fe_write_all(addr_of!(self.char_backend) as *mut _, &ch, 1);
|
||||
}
|
||||
}
|
||||
|
||||
update_irq = self
|
||||
.regs
|
||||
.borrow_mut()
|
||||
.write(field, value as u32, &mut self.char_backend);
|
||||
update_irq = self.regs.borrow_mut().write(
|
||||
field,
|
||||
value as u32,
|
||||
addr_of!(self.char_backend) as *mut _,
|
||||
);
|
||||
} else {
|
||||
eprintln!("write bad offset {offset} value {value}");
|
||||
}
|
||||
@@ -631,7 +629,7 @@ impl PL011State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
pub fn reset_hold(&self, _type: ResetType) {
|
||||
self.regs.borrow_mut().reset();
|
||||
}
|
||||
|
||||
@@ -698,23 +696,27 @@ pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// We expect the FFI user of this function to pass a valid pointer for `chr`.
|
||||
/// We expect the FFI user of this function to pass a valid pointer for `chr`
|
||||
/// and `irq`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_create(
|
||||
addr: u64,
|
||||
irq: qemu_irq,
|
||||
irq: *mut IRQState,
|
||||
chr: *mut Chardev,
|
||||
) -> *mut DeviceState {
|
||||
unsafe {
|
||||
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_NAME.as_ptr());
|
||||
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
|
||||
// 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) };
|
||||
|
||||
qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr);
|
||||
sysbus_realize_and_unref(sysbus, addr_of_mut!(error_fatal));
|
||||
sysbus_mmio_map(sysbus, 0, addr);
|
||||
sysbus_connect_irq(sysbus, 0, irq);
|
||||
dev
|
||||
}
|
||||
let dev = PL011State::new();
|
||||
dev.prop_set_chr("chardev", &chr);
|
||||
dev.sysbus_realize();
|
||||
dev.mmio_map(0, addr);
|
||||
dev.connect_irq(0, &irq);
|
||||
|
||||
// The pointer is kept alive by the QOM tree; drop the owned ref
|
||||
dev.as_mut_ptr()
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -743,3 +745,4 @@ impl ObjectImpl for PL011Luminary {
|
||||
}
|
||||
|
||||
impl DeviceImpl for PL011Luminary {}
|
||||
impl ResettablePhasesImpl for PL011Luminary {}
|
||||
|
||||
@@ -18,7 +18,6 @@ use qemu_api::c_str;
|
||||
|
||||
mod device;
|
||||
mod device_class;
|
||||
mod memory_ops;
|
||||
|
||||
pub use device::pl011_create;
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2024, Linaro Limited
|
||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use core::ptr::NonNull;
|
||||
use std::os::raw::{c_uint, c_void};
|
||||
|
||||
use qemu_api::{bindings::*, zeroable::Zeroable};
|
||||
|
||||
use crate::device::PL011State;
|
||||
|
||||
pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps {
|
||||
read: Some(pl011_read),
|
||||
write: Some(pl011_write),
|
||||
read_with_attrs: None,
|
||||
write_with_attrs: None,
|
||||
endianness: device_endian::DEVICE_NATIVE_ENDIAN,
|
||||
valid: Zeroable::ZERO,
|
||||
impl_: MemoryRegionOps__bindgen_ty_2 {
|
||||
min_access_size: 4,
|
||||
max_access_size: 4,
|
||||
..Zeroable::ZERO
|
||||
},
|
||||
};
|
||||
|
||||
unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 {
|
||||
let mut state = NonNull::new(opaque).unwrap().cast::<PL011State>();
|
||||
unsafe { state.as_mut() }.read(addr, size)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) {
|
||||
let mut state = NonNull::new(opaque).unwrap().cast::<PL011State>();
|
||||
unsafe { state.as_mut() }.write(addr, data);
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
subdir('char')
|
||||
subdir('timer')
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
config X_HPET_RUST
|
||||
bool
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "hpet"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Zhao Liu <zhao1.liu@intel.com>"]
|
||||
license = "GPL-2.0-or-later"
|
||||
description = "IA-PC High Precision Event Timer emulation in Rust"
|
||||
rust-version = "1.63.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
qemu_api = { path = "../../../qemu-api" }
|
||||
qemu_api_macros = { path = "../../../qemu-api-macros" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,18 @@
|
||||
_libhpet_rs = static_library(
|
||||
'hpet',
|
||||
files('src/lib.rs'),
|
||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||
rust_abi: 'rust',
|
||||
dependencies: [
|
||||
qemu_api,
|
||||
qemu_api_macros,
|
||||
],
|
||||
)
|
||||
|
||||
rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
|
||||
link_whole: [_libhpet_rs],
|
||||
# Putting proc macro crates in `dependencies` is necessary for Meson to find
|
||||
# them when compiling the root per-target static rust lib.
|
||||
dependencies: [qemu_api_macros],
|
||||
variables: {'crate': 'hpet'},
|
||||
)])
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (C) 2024 Intel Corporation.
|
||||
// Author(s): Zhao Liu <zhai1.liu@intel.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
use qemu_api::{cell::bql_locked, impl_zeroable, zeroable::Zeroable};
|
||||
|
||||
/// Each `HPETState` represents a Event Timer Block. The v1 spec supports
|
||||
/// up to 8 blocks. QEMU only uses 1 block (in PC machine).
|
||||
const HPET_MAX_NUM_EVENT_TIMER_BLOCK: usize = 8;
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct HPETFwEntry {
|
||||
pub event_timer_block_id: u32,
|
||||
pub address: u64,
|
||||
pub min_tick: u16,
|
||||
pub page_prot: u8,
|
||||
}
|
||||
impl_zeroable!(HPETFwEntry);
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct HPETFwConfig {
|
||||
pub count: u8,
|
||||
pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK],
|
||||
}
|
||||
impl_zeroable!(HPETFwConfig);
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[no_mangle]
|
||||
pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig {
|
||||
count: u8::MAX,
|
||||
..Zeroable::ZERO
|
||||
};
|
||||
|
||||
impl HPETFwConfig {
|
||||
pub(crate) fn assign_hpet_id() -> usize {
|
||||
assert!(bql_locked());
|
||||
// SAFETY: all accesses go through these methods, which guarantee
|
||||
// that the accesses are protected by the BQL.
|
||||
let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
|
||||
|
||||
if fw_cfg.count == u8::MAX {
|
||||
// first instance
|
||||
fw_cfg.count = 0;
|
||||
}
|
||||
|
||||
if fw_cfg.count == 8 {
|
||||
// TODO: Add error binding: error_setg()
|
||||
panic!("Only 8 instances of HPET is allowed");
|
||||
}
|
||||
|
||||
let id: usize = fw_cfg.count.into();
|
||||
fw_cfg.count += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) {
|
||||
assert!(bql_locked());
|
||||
// SAFETY: all accesses go through these methods, which guarantee
|
||||
// that the accesses are protected by the BQL.
|
||||
let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
|
||||
|
||||
fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id;
|
||||
fw_cfg.hpet[hpet_id].address = address;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user