Skip to content

Functions and types about device access and interrupt injection #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions src/device/device_addr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use core::fmt::LowerHex;

use memory_addr::AddrRange;

use crate::GuestPhysAddr;

use super::{Port, SysRegAddr};

/// An address-like type that can be used to access devices.
pub trait DeviceAddr: Copy + Eq + Ord + core::fmt::Debug {}

/// A range of device addresses. It may be contiguous or not.
pub trait DeviceAddrRange {
/// The address type of the range.
type Addr: DeviceAddr;

/// Returns whether the address range contains the given address.
fn contains(&self, addr: Self::Addr) -> bool;
}

impl DeviceAddr for GuestPhysAddr {}

impl DeviceAddrRange for AddrRange<GuestPhysAddr> {
type Addr = GuestPhysAddr;

fn contains(&self, addr: Self::Addr) -> bool {
Self::contains(*self, addr)
}
}

impl DeviceAddr for SysRegAddr {}

/// A inclusive range of system register addresses.
///
/// Unlike [`AddrRange`], this type is inclusive on both ends.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct SysRegAddrRange {
/// The start address of the range.
pub start: SysRegAddr,
/// The end address of the range.
pub end: SysRegAddr,
}

impl SysRegAddrRange {
/// Creates a new [`SysRegAddrRange`] instance.
pub fn new(start: SysRegAddr, end: SysRegAddr) -> Self {
Self { start, end }
}
}

impl DeviceAddrRange for SysRegAddrRange {
type Addr = SysRegAddr;

fn contains(&self, addr: Self::Addr) -> bool {
addr.0 >= self.start.0 && addr.0 <= self.end.0
}
}

impl LowerHex for SysRegAddrRange {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#x}..={:#x}", self.start.0, self.end.0)
}
}

impl DeviceAddr for Port {}

/// A inclusive range of port numbers.
///
/// Unlike [`AddrRange`], this type is inclusive on both ends.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PortRange {
/// The start port number of the range.
pub start: Port,
/// The end port number of the range.
pub end: Port,
}

impl PortRange {
/// Creates a new [`PortRange`] instance.
pub fn new(start: Port, end: Port) -> Self {
Self { start, end }
}
}

impl DeviceAddrRange for PortRange {
type Addr = Port;

fn contains(&self, addr: Self::Addr) -> bool {
addr.0 >= self.start.0 && addr.0 <= self.end.0
}
}

impl LowerHex for PortRange {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#x}..={:#x}", self.start.0, self.end.0)
}
}
132 changes: 132 additions & 0 deletions src/device/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//! Definitions about device accessing.

use core::fmt::{Debug, LowerHex, UpperHex};

mod device_addr;

pub use device_addr::*;

/// The width of an access.
///
/// Note that the term "word" here refers to 16-bit data, as in the x86 architecture.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AccessWidth {
/// 8-bit access.
Byte,
/// 16-bit access.
Word,
/// 32-bit access.
Dword,
/// 64-bit access.
Qword,
}

impl TryFrom<usize> for AccessWidth {
type Error = ();

fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::Byte),
2 => Ok(Self::Word),
4 => Ok(Self::Dword),
8 => Ok(Self::Qword),
_ => Err(()),
}
}
}

impl From<AccessWidth> for usize {
fn from(width: AccessWidth) -> usize {
match width {
AccessWidth::Byte => 1,
AccessWidth::Word => 2,
AccessWidth::Dword => 4,
AccessWidth::Qword => 8,
}
}
}

impl AccessWidth {
/// Returns the size of the access in bytes.
pub fn size(&self) -> usize {
(*self).into()
}

/// Returns the range of bits that the access covers.
pub fn bits_range(&self) -> core::ops::Range<usize> {
match self {
AccessWidth::Byte => 0..8,
AccessWidth::Word => 0..16,
AccessWidth::Dword => 0..32,
AccessWidth::Qword => 0..64,
}
}
}

/// The port number of an I/O operation.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Port(pub u16);

impl Port {
/// Creates a new `Port` instance.
pub fn new(port: u16) -> Self {
Self(port)
}

/// Returns the port number.
pub fn number(&self) -> u16 {
self.0
}
}

impl LowerHex for Port {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Port({:#x})", self.0)
}
}

impl UpperHex for Port {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Port({:#X})", self.0)
}
}

impl Debug for Port {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Port({})", self.0)
}
}

/// A system register address.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct SysRegAddr(pub usize); // u32 seems to be enough, but we use usize for generality.

impl SysRegAddr {
/// Creates a new `SysRegAddr` instance.
pub const fn new(addr: usize) -> Self {
Self(addr)
}

/// Returns the address.
pub const fn addr(&self) -> usize {
self.0
}
}

impl LowerHex for SysRegAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SysRegAddr({:#x})", self.0)
}
}

impl UpperHex for SysRegAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SysRegAddr({:#X})", self.0)
}
}

impl Debug for SysRegAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SysRegAddr({})", self.0)
}
}
74 changes: 74 additions & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use core::marker::PhantomData;

use axerrno::{AxResult, ax_err_type};

pub(crate) use memory_addr::PAGE_SIZE_4K as PAGE_SIZE;

use crate::{AxMmHal, HostPhysAddr};

/// A physical frame which will be automatically deallocated when dropped.
///
/// The frame is allocated using the [`AxMmHal`] implementation. The size of the frame is likely to
/// be 4 KiB but the actual size is determined by the [`AxMmHal`] implementation.
#[derive(Debug)]
pub struct PhysFrame<H: AxMmHal> {
start_paddr: Option<HostPhysAddr>,
_marker: PhantomData<H>,
}

impl<H: AxMmHal> PhysFrame<H> {
/// Allocate a [`PhysFrame`].
pub fn alloc() -> AxResult<Self> {
let start_paddr = H::alloc_frame()
.ok_or_else(|| ax_err_type!(NoMemory, "allocate physical frame failed"))?;
assert_ne!(start_paddr.as_usize(), 0);
Ok(Self {
start_paddr: Some(start_paddr),
_marker: PhantomData,
})
}

/// Allocate a [`PhysFrame`] and fill it with zeros.
pub fn alloc_zero() -> AxResult<Self> {
let mut f = Self::alloc()?;
f.fill(0);
Ok(f)
}

/// Create an uninitialized [`PhysFrame`].
///
/// # Safety
///
/// The caller must ensure that the [`PhysFrame`] is only used as a placeholder and never
/// accessed.
pub const unsafe fn uninit() -> Self {
Self {
start_paddr: None,
_marker: PhantomData,
}
}

/// Get the starting physical address of the frame.
pub fn start_paddr(&self) -> HostPhysAddr {
self.start_paddr.expect("uninitialized PhysFrame")
}

/// Get a mutable pointer to the frame.
pub fn as_mut_ptr(&self) -> *mut u8 {
H::phys_to_virt(self.start_paddr()).as_mut_ptr()
}

/// Fill the frame with a byte. Works only when the frame is 4 KiB in size.
pub fn fill(&mut self, byte: u8) {
unsafe { core::ptr::write_bytes(self.as_mut_ptr(), byte, PAGE_SIZE) }
}
}

impl<H: AxMmHal> Drop for PhysFrame<H> {
fn drop(&mut self) {
if let Some(start_paddr) = self.start_paddr {
H::dealloc_frame(start_paddr);
debug!("[AxVM] deallocated PhysFrame({:#x})", start_paddr);
}
}
}
40 changes: 40 additions & 0 deletions src/hal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::{HostPhysAddr, HostVirtAddr};

/// Hardware abstraction layer for memory management.
pub trait AxMmHal {
/// Allocates a frame and returns its host physical address. The
///
/// # Returns
///
/// * `Option<HostPhysAddr>` - Some containing the physical address of the allocated frame, or None if allocation fails.
fn alloc_frame() -> Option<HostPhysAddr>;

/// Deallocates a frame given its physical address.
///
/// # Parameters
///
/// * `paddr` - The physical address of the frame to deallocate.
fn dealloc_frame(paddr: HostPhysAddr);

/// Converts a host physical address to a host virtual address.
///
/// # Parameters
///
/// * `paddr` - The physical address to convert.
///
/// # Returns
///
/// * `HostVirtAddr` - The corresponding virtual address.
fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr;

/// Converts a host virtual address to a host physical address.
///
/// # Parameters
///
/// * `vaddr` - The virtual address to convert.
///
/// # Returns
///
/// * `HostPhysAddr` - The corresponding physical address.
fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr;
}
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ extern crate alloc;

mod addr;
mod address_space;
pub mod device;
mod frame;
mod hal;
mod npt;

pub use addr::*;
pub use address_space::*;

pub use frame::PhysFrame;
pub use hal::AxMmHal;

use axerrno::AxError;
use memory_set::MappingError;

Expand Down