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:
Stefan Hajnoczi
2025-02-14 08:18:56 -05:00
45 changed files with 2232 additions and 204 deletions
+6
View File
@@ -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:
+1
View File
@@ -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
+46
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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]);
+1 -1
View File
@@ -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"
+8
View File
@@ -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.
#
+8
View File
@@ -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"
+1
View File
@@ -4,6 +4,7 @@ members = [
"qemu-api-macros",
"qemu-api",
"hw/char/pl011",
"hw/timer/hpet",
]
[workspace.lints.rust]
+1
View File
@@ -1,2 +1,3 @@
# devices Kconfig
source char/Kconfig
source timer/Kconfig
+62 -59
View File
@@ -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 {}
-1
View File
@@ -18,7 +18,6 @@ use qemu_api::c_str;
mod device;
mod device_class;
mod memory_ops;
pub use device::pl011_create;
-34
View File
@@ -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
View File
@@ -1 +1,2 @@
subdir('char')
subdir('timer')
+2
View File
@@ -0,0 +1,2 @@
config X_HPET_RUST
bool
+18
View File
@@ -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
+18
View File
@@ -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'},
)])
+69
View File
@@ -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