mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-08 02:03:56 -06:00
rust: pl011: extract PL011Registers
Pull all the mutable fields of PL011State into a separate struct. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
ab6b6a8a55
commit
49bfe63f29
2 changed files with 166 additions and 127 deletions
|
@ -85,11 +85,8 @@ impl std::ops::Index<u32> for Fifo {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
|
||||
/// PL011 Device Model in QEMU
|
||||
pub struct PL011State {
|
||||
pub parent_obj: ParentField<SysBusDevice>,
|
||||
pub iomem: MemoryRegion,
|
||||
#[derive(Debug, Default, qemu_api_macros::offsets)]
|
||||
pub struct PL011Registers {
|
||||
#[doc(alias = "fr")]
|
||||
pub flags: registers::Flags,
|
||||
#[doc(alias = "lcr")]
|
||||
|
@ -109,8 +106,17 @@ pub struct PL011State {
|
|||
pub read_pos: u32,
|
||||
pub read_count: u32,
|
||||
pub read_trigger: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
|
||||
/// PL011 Device Model in QEMU
|
||||
pub struct PL011State {
|
||||
pub parent_obj: ParentField<SysBusDevice>,
|
||||
pub iomem: MemoryRegion,
|
||||
#[doc(alias = "chr")]
|
||||
pub char_backend: CharBackend,
|
||||
pub regs: PL011Registers,
|
||||
/// QEMU interrupts
|
||||
///
|
||||
/// ```text
|
||||
|
@ -169,61 +175,8 @@ impl DeviceImpl for PL011State {
|
|||
const RESET: Option<fn(&mut Self)> = Some(Self::reset);
|
||||
}
|
||||
|
||||
impl PL011State {
|
||||
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `self` must point to a correctly sized and aligned location for the
|
||||
/// `PL011State` type. It must not be called more than once on the same
|
||||
/// location/instance. All its fields are expected to hold unitialized
|
||||
/// values with the sole exception of `parent_obj`.
|
||||
unsafe fn init(&mut self) {
|
||||
const CLK_NAME: &CStr = c_str!("clk");
|
||||
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
fn post_init(&self) {
|
||||
self.init_mmio(&self.iomem);
|
||||
for irq in self.interrupts.iter() {
|
||||
self.init_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
fn regs_read(&mut self, offset: RegisterOffset) -> ControlFlow<u32, u32> {
|
||||
impl PL011Registers {
|
||||
pub(self) fn read(&mut self, offset: RegisterOffset) -> ControlFlow<u32, u32> {
|
||||
use RegisterOffset::*;
|
||||
|
||||
ControlFlow::Break(match offset {
|
||||
|
@ -265,7 +218,12 @@ impl PL011State {
|
|||
})
|
||||
}
|
||||
|
||||
fn regs_write(&mut self, offset: RegisterOffset, value: u32) -> bool {
|
||||
pub(self) fn write(
|
||||
&mut self,
|
||||
offset: RegisterOffset,
|
||||
value: u32,
|
||||
char_backend: *mut CharBackend,
|
||||
) -> bool {
|
||||
// eprintln!("write offset {offset} value {value}");
|
||||
use RegisterOffset::*;
|
||||
match offset {
|
||||
|
@ -303,7 +261,7 @@ impl PL011State {
|
|||
// initialized in realize().
|
||||
unsafe {
|
||||
qemu_chr_fe_ioctl(
|
||||
addr_of_mut!(self.char_backend),
|
||||
char_backend,
|
||||
CHR_IOCTL_SERIAL_SET_BREAK as i32,
|
||||
addr_of_mut!(break_enable).cast::<c_void>(),
|
||||
);
|
||||
|
@ -422,23 +380,6 @@ impl PL011State {
|
|||
self.read_trigger = 1;
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.line_control.reset();
|
||||
self.receive_status_error_clear.reset();
|
||||
|
@ -471,26 +412,6 @@ impl PL011State {
|
|||
self.flags.set_transmit_fifo_empty(true);
|
||||
}
|
||||
|
||||
pub fn can_receive(&self) -> bool {
|
||||
// trace_pl011_can_receive(s->lcr, s->read_count, r);
|
||||
self.read_count < self.fifo_depth()
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, ch: u32) {
|
||||
if !self.loopback_enabled() && self.put_fifo(ch) {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&mut self, event: QEMUChrEvent) {
|
||||
if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() {
|
||||
let update = self.put_fifo(registers::Data::BREAK.into());
|
||||
if update {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fifo_enabled(&self) -> bool {
|
||||
self.line_control.fifos_enabled() == registers::Mode::FIFO
|
||||
|
@ -529,14 +450,7 @@ impl PL011State {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
let flags = self.int_level & self.int_enabled;
|
||||
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
|
||||
irq.set(flags & i != 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
|
||||
pub fn post_load(&mut self) -> Result<(), ()> {
|
||||
/* Sanity-check input state */
|
||||
if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
|
||||
return Err(());
|
||||
|
@ -556,6 +470,63 @@ impl PL011State {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PL011State {
|
||||
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `self` must point to a correctly sized and aligned location for the
|
||||
/// `PL011State` type. It must not be called more than once on the same
|
||||
/// location/instance. All its fields are expected to hold unitialized
|
||||
/// values with the sole exception of `parent_obj`.
|
||||
unsafe fn init(&mut self) {
|
||||
const CLK_NAME: &CStr = c_str!("clk");
|
||||
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
fn post_init(&self) {
|
||||
self.init_mmio(&self.iomem);
|
||||
for irq in self.interrupts.iter() {
|
||||
self.init_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow<u64, u64> {
|
||||
let mut update_irq = false;
|
||||
|
@ -568,7 +539,7 @@ impl PL011State {
|
|||
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
||||
ControlFlow::Break(0)
|
||||
}
|
||||
Ok(field) => match self.regs_read(field) {
|
||||
Ok(field) => match self.regs.read(field) {
|
||||
ControlFlow::Break(value) => ControlFlow::Break(value.into()),
|
||||
ControlFlow::Continue(value) => {
|
||||
update_irq = true;
|
||||
|
@ -599,7 +570,7 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
update_irq = self.regs_write(field, value as u32);
|
||||
update_irq = self.regs.write(field, value as u32, &mut self.char_backend);
|
||||
} else {
|
||||
eprintln!("write bad offset {offset} value {value}");
|
||||
}
|
||||
|
@ -607,6 +578,64 @@ impl PL011State {
|
|||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_receive(&self) -> bool {
|
||||
// trace_pl011_can_receive(s->lcr, s->read_count, r);
|
||||
let regs = &self.regs;
|
||||
regs.read_count < regs.fifo_depth()
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, ch: u32) {
|
||||
let regs = &mut self.regs;
|
||||
let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch);
|
||||
if update_irq {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&mut self, event: QEMUChrEvent) {
|
||||
let mut update_irq = false;
|
||||
let regs = &mut self.regs;
|
||||
if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() {
|
||||
update_irq = regs.put_fifo(registers::Data::BREAK.into());
|
||||
}
|
||||
if update_irq {
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.regs.reset();
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
let regs = &self.regs;
|
||||
let flags = regs.int_level & regs.int_enabled;
|
||||
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
|
||||
irq.set(flags & i != 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
|
||||
self.regs.post_load()
|
||||
}
|
||||
}
|
||||
|
||||
/// Which bits in the interrupt status matter for each outbound IRQ line ?
|
||||
|
|
|
@ -6,11 +6,11 @@ use core::ptr::NonNull;
|
|||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use qemu_api::{
|
||||
bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_of, vmstate_subsections,
|
||||
vmstate_unused, zeroable::Zeroable,
|
||||
bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct,
|
||||
vmstate_subsections, vmstate_unused, zeroable::Zeroable,
|
||||
};
|
||||
|
||||
use crate::device::PL011State;
|
||||
use crate::device::{PL011Registers, PL011State};
|
||||
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
|
||||
|
@ -40,6 +40,30 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
|
|||
}
|
||||
}
|
||||
|
||||
static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
|
||||
name: c_str!("pl011/regs").as_ptr(),
|
||||
version_id: 2,
|
||||
minimum_version_id: 2,
|
||||
fields: vmstate_fields! {
|
||||
vmstate_of!(PL011Registers, flags),
|
||||
vmstate_of!(PL011Registers, line_control),
|
||||
vmstate_of!(PL011Registers, receive_status_error_clear),
|
||||
vmstate_of!(PL011Registers, control),
|
||||
vmstate_of!(PL011Registers, dmacr),
|
||||
vmstate_of!(PL011Registers, int_enabled),
|
||||
vmstate_of!(PL011Registers, int_level),
|
||||
vmstate_of!(PL011Registers, read_fifo),
|
||||
vmstate_of!(PL011Registers, ilpr),
|
||||
vmstate_of!(PL011Registers, ibrd),
|
||||
vmstate_of!(PL011Registers, fbrd),
|
||||
vmstate_of!(PL011Registers, ifl),
|
||||
vmstate_of!(PL011Registers, read_pos),
|
||||
vmstate_of!(PL011Registers, read_count),
|
||||
vmstate_of!(PL011Registers, read_trigger),
|
||||
},
|
||||
..Zeroable::ZERO
|
||||
};
|
||||
|
||||
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
||||
name: c_str!("pl011").as_ptr(),
|
||||
version_id: 2,
|
||||
|
@ -47,21 +71,7 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
|||
post_load: Some(pl011_post_load),
|
||||
fields: vmstate_fields! {
|
||||
vmstate_unused!(core::mem::size_of::<u32>()),
|
||||
vmstate_of!(PL011State, flags),
|
||||
vmstate_of!(PL011State, line_control),
|
||||
vmstate_of!(PL011State, receive_status_error_clear),
|
||||
vmstate_of!(PL011State, control),
|
||||
vmstate_of!(PL011State, dmacr),
|
||||
vmstate_of!(PL011State, int_enabled),
|
||||
vmstate_of!(PL011State, int_level),
|
||||
vmstate_of!(PL011State, read_fifo),
|
||||
vmstate_of!(PL011State, ilpr),
|
||||
vmstate_of!(PL011State, ibrd),
|
||||
vmstate_of!(PL011State, fbrd),
|
||||
vmstate_of!(PL011State, ifl),
|
||||
vmstate_of!(PL011State, read_pos),
|
||||
vmstate_of!(PL011State, read_count),
|
||||
vmstate_of!(PL011State, read_trigger),
|
||||
vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, PL011Registers),
|
||||
},
|
||||
subsections: vmstate_subsections! {
|
||||
VMSTATE_PL011_CLOCK
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue