From 29683ffca40b29cc2c5a8eb4f2adab64d9cc50be Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Thu, 5 Jun 2025 11:34:19 +0900 Subject: [PATCH 01/14] uefi: Implement PciRootBridgeIo::allocate_buffer --- uefi-raw/src/protocol/pci/root_bridge.rs | 23 ++++++ uefi-test-runner/src/proto/pci/mod.rs | 3 +- uefi-test-runner/src/proto/pci/root_bridge.rs | 25 +++++- uefi/src/proto/pci/buffer.rs | 82 +++++++++++++++++++ uefi/src/proto/pci/mod.rs | 1 + 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 uefi/src/proto/pci/buffer.rs diff --git a/uefi-raw/src/protocol/pci/root_bridge.rs b/uefi-raw/src/protocol/pci/root_bridge.rs index a1b66872f..0b21b17ed 100644 --- a/uefi-raw/src/protocol/pci/root_bridge.rs +++ b/uefi-raw/src/protocol/pci/root_bridge.rs @@ -2,6 +2,7 @@ use crate::table::boot::{AllocateType, MemoryType}; use crate::{Handle, PhysicalAddress, Status}; +use bitflags::bitflags; use core::ffi::c_void; use uguid::{Guid, guid}; @@ -37,6 +38,28 @@ newtype_enum! { } } +bitflags! { + /// Describes PCI I/O Protocol Attribute bitflags specified in UEFI specification. + ///. https://uefi.org/specs/UEFI/2.10_A/14_Protocols_PCI_Bus_Support.html + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub struct PciRootBridgeIoProtocolAttribute: u64 { + const PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO = 0x0001; + const PCI_ATTRIBUTE_ISA_IO = 0x0002; + const PCI_ATTRIBUTE_VGA_PALETTE_IO = 0x0004; + const PCI_ATTRIBUTE_VGA_MEMORY = 0x0008; + const PCI_ATTRIBUTE_VGA_IO = 0x0010; + const PCI_ATTRIBUTE_IDE_PRIMARY_IO = 0x0020; + const PCI_ATTRIBUTE_IDE_SECONDARY_IO = 0x0040; + const PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE = 0x0080; + const PCI_ATTRIBUTE_MEMORY_CACHED = 0x0800; + const PCI_ATTRIBUTE_MEMORY_DISABLE = 0x1000; + const PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE = 0x8000; + const PCI_ATTRIBUTE_ISA_IO_16 = 0x10000; + const PCI_ATTRIBUTE_VGA_PALETTE_IO_16 = 0x20000; + const PCI_ATTRIBUTE_VGA_IO_16 = 0x40000; + } +} + #[derive(Debug)] #[repr(C)] pub struct PciRootBridgeIoAccess { diff --git a/uefi-test-runner/src/proto/pci/mod.rs b/uefi-test-runner/src/proto/pci/mod.rs index 75ecbc0ad..5a4732c5a 100644 --- a/uefi-test-runner/src/proto/pci/mod.rs +++ b/uefi-test-runner/src/proto/pci/mod.rs @@ -3,5 +3,6 @@ pub mod root_bridge; pub fn test() { - root_bridge::test(); + root_bridge::test_io(); + root_bridge::test_buffer(); } diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 0847b3e8f..1d1ddd553 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -6,6 +6,8 @@ use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, ima use uefi::proto::ProtocolPointer; use uefi::proto::pci::PciIoAddress; use uefi::proto::pci::root_bridge::PciRootBridgeIo; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolAttribute; +use uefi_raw::table::boot::MemoryType; const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4; const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1; @@ -13,7 +15,7 @@ const SATA_CTRL_SUBCLASS_CODE: u8 = 0x6; const REG_SIZE: u8 = mem::size_of::() as u8; -pub fn test() { +pub fn test_io() { let pci_handles = uefi::boot::find_handles::().unwrap(); let mut red_hat_dev_cnt = 0; @@ -67,6 +69,27 @@ pub fn test() { assert!(sata_ctrl_cnt > 0); } +pub fn test_buffer() { + let pci_handles = uefi::boot::find_handles::().unwrap(); + + for pci_handle in pci_handles { + let pci_proto = get_open_protocol::(pci_handle); + + let mut buffer = pci_proto + .allocate_buffer::<[u8; 4096]>( + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; + assert_eq!(buffer.as_ptr().addr() % 4096, 0); + } +} + fn get_open_protocol(handle: Handle) -> ScopedProtocol

{ let open_opts = OpenProtocolParams { handle, diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs new file mode 100644 index 000000000..554a1a7d6 --- /dev/null +++ b/uefi/src/proto/pci/buffer.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Defines wrapper allocated by PCI Root Bridge protocol. + +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::num::NonZeroUsize; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; +use log::debug; +use uefi_raw::Status; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; + +/// Smart pointer for wrapping owned buffer allocated by PCI Root Bridge protocol. +#[derive(Debug)] +pub struct PciBuffer<'p, T> { + pub(crate) base: NonNull, + pub(crate) pages: NonZeroUsize, + pub(crate) proto: &'p PciRootBridgeIoProtocol, +} + +impl<'p, T> PciBuffer<'p, MaybeUninit> { + /// Assumes the contents of this buffer have been initialized. + /// + /// # Safety + /// Callers of this function must guarantee that value stored is valid. + #[must_use] + pub unsafe fn assume_init(self) -> PciBuffer<'p, T> { + let old = ManuallyDrop::new(self); + PciBuffer { + base: old.base.cast(), + pages: old.pages, + proto: old.proto, + } + } +} + +impl<'p, T> AsRef for PciBuffer<'p, T> { + fn as_ref(&self) -> &T { + unsafe { self.base.as_ref() } + } +} + +impl<'p, T> AsMut for PciBuffer<'p, T> { + fn as_mut(&mut self) -> &mut T { + unsafe { self.base.as_mut() } + } +} + +impl<'p, T> Deref for PciBuffer<'p, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl<'p, T> DerefMut for PciBuffer<'p, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut() + } +} + +impl<'p, T> Drop for PciBuffer<'p, T> { + fn drop(&mut self) { + let status = unsafe { + (self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast()) + }; + match status { + Status::SUCCESS => { + debug!( + "Freed {} pages at 0x{:X}", + self.pages.get(), + self.base.as_ptr().addr() + ); + } + Status::INVALID_PARAMETER => { + panic!("PciBuffer was not created through valid protocol usage!") + } + _ => unreachable!(), + } + } +} diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index 98cbaf7c2..ab41c7a6a 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -6,6 +6,7 @@ use core::cmp::Ordering; use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth; +pub mod buffer; pub mod root_bridge; /// IO Address for PCI/register IO operations From f5b6b95ccfcc99020542984e17a8bf9f6c731fc7 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Sat, 7 Jun 2025 20:40:25 +0900 Subject: [PATCH 02/14] uefi: Implement PciRootBridgeIo::map --- uefi-test-runner/src/proto/pci/mod.rs | 1 + uefi-test-runner/src/proto/pci/root_bridge.rs | 28 +++- uefi/src/proto/pci/mapped_region.rs | 83 ++++++++++++ uefi/src/proto/pci/mod.rs | 1 + uefi/src/proto/pci/root_bridge.rs | 121 +++++++++++++++++- 5 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 uefi/src/proto/pci/mapped_region.rs diff --git a/uefi-test-runner/src/proto/pci/mod.rs b/uefi-test-runner/src/proto/pci/mod.rs index 5a4732c5a..f7c78e0e6 100644 --- a/uefi-test-runner/src/proto/pci/mod.rs +++ b/uefi-test-runner/src/proto/pci/mod.rs @@ -5,4 +5,5 @@ pub mod root_bridge; pub fn test() { root_bridge::test_io(); root_bridge::test_buffer(); + root_bridge::test_mapping(); } diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 1d1ddd553..e56ad1f38 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -6,7 +6,7 @@ use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, ima use uefi::proto::ProtocolPointer; use uefi::proto::pci::PciIoAddress; use uefi::proto::pci::root_bridge::PciRootBridgeIo; -use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolAttribute; +use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation}; use uefi_raw::table::boot::MemoryType; const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4; @@ -90,6 +90,32 @@ pub fn test_buffer() { } } +pub fn test_mapping() { + let pci_handles = uefi::boot::find_handles::().unwrap(); + + for pci_handle in pci_handles { + let pci_proto = get_open_protocol::(pci_handle); + + let mut buffer = pci_proto + .allocate_buffer::<[u8; 4096]>( + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; + let mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref()); + if mapped.region().0 == buffer.as_ptr().addr() as u64 { + info!("This PCI device uses identity mapping"); + } else { + info!("This PCI device uses different mapping from CPU"); + } + } +} + fn get_open_protocol(handle: Handle) -> ScopedProtocol

{ let open_opts = OpenProtocolParams { handle, diff --git a/uefi/src/proto/pci/mapped_region.rs b/uefi/src/proto/pci/mapped_region.rs new file mode 100644 index 000000000..907ac529e --- /dev/null +++ b/uefi/src/proto/pci/mapped_region.rs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Defines wrapper for region mapped by PCI Root Bridge I/O protocol. + +use core::ffi::c_void; +use core::ptr; +use log::debug; +use uefi_raw::Status; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; + +/// Represents a region of memory mapped by PCI Root Bridge I/O protocol. +/// The region will be unmapped automatically when it is dropped. +/// +/// # Lifetime +/// `'p` is the lifetime for Protocol. +/// `'r` is the lifetime for Mapped Region. +/// Protocol must outlive the mapped region +/// as unmap function can only be accessed through the protocol. +#[derive(Debug)] +pub struct PciMappedRegion<'p, 'r> +where + 'p: 'r, +{ + device_address: u64, + length: usize, + _lifetime_holder: &'r (), + key: *const c_void, + proto: &'p PciRootBridgeIoProtocol, +} + +impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { + pub(crate) fn new( + device_address: u64, + length: usize, + key: *const c_void, + to_map: &'r T, + proto: &'p PciRootBridgeIoProtocol, + ) -> Self { + let _lifetime_holder: &'r () = unsafe { + let ptr = ptr::from_ref(to_map); + ptr.cast::<()>().as_ref().unwrap() + }; + + let end = device_address + length as u64; + debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end); + Self { + device_address, + length, + _lifetime_holder, + key, + proto, + } + } + + /// Returns mapped address and length of mapped region. + /// + /// # Warning + /// **Returned address cannot be used to reference memory from CPU!** + /// **Do not cast it back to pointer or reference** + #[must_use] + pub const fn region(&self) -> (u64, usize) { + (self.device_address, self.length) + } +} + +impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { + fn drop(&mut self) { + let status = unsafe { (self.proto.unmap)(self.proto, self.key) }; + match status { + Status::SUCCESS => { + let end = self.device_address + self.length as u64; + debug!("Region [0x{:X}..0x{:X}] was unmapped", self.device_address, end); + } + Status::INVALID_PARAMETER => { + panic!("This region was not mapped using PciRootBridgeIo::map"); + } + Status::DEVICE_ERROR => { + panic!("The data was not committed to the target system memory."); + } + _ => unreachable!(), + } + } +} diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index ab41c7a6a..7d7a9b11e 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -7,6 +7,7 @@ use core::cmp::Ordering; use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth; pub mod buffer; +pub mod mapped_region; pub mod root_bridge; /// IO Address for PCI/register IO operations diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index 57135279a..ae3483bd9 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -2,12 +2,23 @@ //! PCI Root Bridge protocol. -use core::ptr; - use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; use crate::StatusExt; +use crate::proto::pci::buffer::PciBuffer; +use crate::proto::pci::mapped_region::PciMappedRegion; +use core::ffi::c_void; +use core::mem::MaybeUninit; +use core::num::NonZeroUsize; +use core::ptr; +use core::ptr::NonNull; +use log::debug; use uefi_macros::unsafe_protocol; -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol}; +use uefi_raw::Status; +use uefi_raw::protocol::pci::root_bridge::{ + PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, + PciRootBridgeIoProtocolOperation, +}; +use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; #[cfg(doc)] use crate::Status; @@ -46,11 +57,111 @@ impl PciRootBridgeIo { unsafe { (self.0.flush)(&mut self.0).to_result() } } + /// Allocates pages suitable for communicating with PCI devices. + /// + /// # Errors + /// - [`crate::Status::INVALID_PARAMETER`] MemoryType is invalid. + /// - [`crate::Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`] + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] + /// - [`crate::Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. + pub fn allocate_buffer( + &self, + memory_type: MemoryType, + pages: Option, + attributes: PciRootBridgeIoProtocolAttribute, + ) -> crate::Result>> { + let mut address = 0usize; + let original_alignment = align_of::(); + assert_ne!(original_alignment, 0); + assert!(PAGE_SIZE >= original_alignment); + assert_eq!(PAGE_SIZE % original_alignment, 0); + + let alignment = PAGE_SIZE; + + let pages = if let Some(pages) = pages { + pages + } else { + let size = size_of::(); + assert_ne!(size, 0); + + NonZeroUsize::new(size.div_ceil(alignment)).unwrap() + }; + + let status = unsafe { + (self.0.allocate_buffer)( + &self.0, + AllocateType(0), + memory_type, + pages.get(), + ptr::from_mut(&mut address).cast(), + attributes.bits(), + ) + }; + + match status { + Status::SUCCESS => { + let base = NonNull::new(address as *mut MaybeUninit).unwrap(); + debug!("Allocated {} pages at 0x{:X}", pages.get(), address); + Ok(PciBuffer { + base, + pages, + proto: &self.0, + }) + } + error + @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { + Err(error.into()) + } + _ => unreachable!(), + } + } + + /// Map given variable's address into PCI Controller-specific address + /// required to access it from a DMA bus master. + /// # Arguments + /// - `operation` - Indicates if bus master is going to read, write or do both to given variable. + /// - `to_map` - Variable to map. + /// + /// # Returns + /// - PciMappedRegion capturing lifetime of passed variable + pub fn map<'p, 'r, T>( + &'p self, + operation: PciRootBridgeIoProtocolOperation, + to_map: &'r T, + ) -> PciMappedRegion<'p, 'r> + where + 'p: 'r, + { + let host_address = ptr::from_ref(to_map); + let mut bytes = size_of_val(to_map); + let mut mapped_address = 0u64; + let mut mapping: *mut c_void = ptr::null_mut(); + + let status = unsafe { + (self.0.map)( + &self.0, + operation, + host_address.cast(), + ptr::from_mut(&mut bytes), + ptr::from_mut(&mut mapped_address).cast(), + ptr::from_mut(&mut mapping), + ) + }; + + match status { + Status::SUCCESS => { + PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0) + } + _ => unreachable!(), + } + } + // TODO: poll I/O // TODO: mem I/O access // TODO: io I/O access - // TODO: map & unmap & copy memory - // TODO: buffer management + // TODO: copy memory // TODO: get/set attributes // TODO: configuration / resource settings } From 4c76f96229ec7d29fb3198907f73055d5a88dc74 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Sat, 21 Jun 2025 18:58:39 +0900 Subject: [PATCH 03/14] uefi: Implement PciRootBridgeIo::copy --- uefi-test-runner/src/proto/pci/root_bridge.rs | 47 ++++++++++++++++-- uefi/src/proto/pci/buffer.rs | 26 ++++++++-- uefi/src/proto/pci/mod.rs | 2 +- .../proto/pci/{mapped_region.rs => region.rs} | 44 +++++++++++++---- uefi/src/proto/pci/root_bridge.rs | 48 +++++++++++++------ 5 files changed, 137 insertions(+), 30 deletions(-) rename uefi/src/proto/pci/{mapped_region.rs => region.rs} (65%) diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index e56ad1f38..3fb72d6a0 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use core::mem; +use qcell::{QCell, QCellOwner}; use uefi::Handle; use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle}; use uefi::proto::ProtocolPointer; use uefi::proto::pci::PciIoAddress; use uefi::proto::pci::root_bridge::PciRootBridgeIo; -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation}; -use uefi_raw::table::boot::MemoryType; +use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; +use uefi_raw::table::boot::{MemoryType, PAGE_SIZE}; const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4; const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1; @@ -108,7 +109,7 @@ pub fn test_mapping() { buffer.assume_init() }; let mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref()); - if mapped.region().0 == buffer.as_ptr().addr() as u64 { + if mapped.region().device_address == buffer.as_ptr().addr() as u64 { info!("This PCI device uses identity mapping"); } else { info!("This PCI device uses different mapping from CPU"); @@ -116,6 +117,46 @@ pub fn test_mapping() { } } +pub fn test_copy() { + let pci_handles = uefi::boot::find_handles::().unwrap(); + + for pci_handle in pci_handles { + let mut owner = QCellOwner::new(); + let item = QCell::new(&owner, get_open_protocol::(pci_handle)); + let pci_proto = owner.rw(&item); + + let mut src = pci_proto + .allocate_buffer::<[u32; 4096 / 4]>( + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); + assert_eq!(size_of_val(src.as_ref()), size_of::<[u8; PAGE_SIZE]>()); + let src = unsafe { + src.assume_init_mut().fill(0xDEADBEEF); + src.assume_init() + }; + let src_mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, src.as_ref()); + + let dst = pci_proto + .allocate_buffer::<[u32; 4096 / 4]>( + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); + assert_eq!(size_of_val(dst.as_ref()), size_of::<[u8; PAGE_SIZE]>()); + let dst_mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, dst.as_ref()); + + pci_proto.copy(PciRootBridgeIoProtocolWidth::UINT32, dst_mapped.region(), src_mapped.region()).unwrap(); + drop(dst_mapped); + let dst = unsafe { dst.assume_init() }; + + assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); + } +} + fn get_open_protocol(handle: Handle) -> ScopedProtocol

{ let open_opts = OpenProtocolParams { handle, diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs index 554a1a7d6..37121d1a5 100644 --- a/uefi/src/proto/pci/buffer.rs +++ b/uefi/src/proto/pci/buffer.rs @@ -5,6 +5,7 @@ use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZeroUsize; use core::ops::{Deref, DerefMut}; +use core::ptr; use core::ptr::NonNull; use log::debug; use uefi_raw::Status; @@ -13,12 +14,27 @@ use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; /// Smart pointer for wrapping owned buffer allocated by PCI Root Bridge protocol. #[derive(Debug)] pub struct PciBuffer<'p, T> { - pub(crate) base: NonNull, - pub(crate) pages: NonZeroUsize, - pub(crate) proto: &'p PciRootBridgeIoProtocol, + base: NonNull, + pages: NonZeroUsize, + proto_lifetime: &'p (), + proto: *const PciRootBridgeIoProtocol, } impl<'p, T> PciBuffer<'p, MaybeUninit> { + + /// Creates wrapper for buffer allocated by PCI Root Bridge protocol. + /// Passed protocol is stored as a pointer along with its lifetime so that it doesn't + /// block others from using its mutable functions. + #[must_use] + pub const fn new(base: NonNull>, pages: NonZeroUsize, proto: &'p PciRootBridgeIoProtocol) -> Self { + Self { + base, + pages, + proto_lifetime: &(), + proto: ptr::from_ref(proto), + } + } + /// Assumes the contents of this buffer have been initialized. /// /// # Safety @@ -29,6 +45,7 @@ impl<'p, T> PciBuffer<'p, MaybeUninit> { PciBuffer { base: old.base.cast(), pages: old.pages, + proto_lifetime: old.proto_lifetime, proto: old.proto, } } @@ -63,7 +80,8 @@ impl<'p, T> DerefMut for PciBuffer<'p, T> { impl<'p, T> Drop for PciBuffer<'p, T> { fn drop(&mut self) { let status = unsafe { - (self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast()) + let proto = self.proto.as_ref().unwrap(); + (proto.free_buffer)(proto, self.pages.get(), self.base.as_ptr().cast()) }; match status { Status::SUCCESS => { diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index 7d7a9b11e..cb7ca5626 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -7,7 +7,7 @@ use core::cmp::Ordering; use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth; pub mod buffer; -pub mod mapped_region; +pub mod region; pub mod root_bridge; /// IO Address for PCI/register IO operations diff --git a/uefi/src/proto/pci/mapped_region.rs b/uefi/src/proto/pci/region.rs similarity index 65% rename from uefi/src/proto/pci/mapped_region.rs rename to uefi/src/proto/pci/region.rs index 907ac529e..7501c7270 100644 --- a/uefi/src/proto/pci/mapped_region.rs +++ b/uefi/src/proto/pci/region.rs @@ -21,13 +21,25 @@ pub struct PciMappedRegion<'p, 'r> where 'p: 'r, { - device_address: u64, - length: usize, + region: PciRegion, _lifetime_holder: &'r (), key: *const c_void, proto: &'p PciRootBridgeIoProtocol, } +/// Represents a region of memory in PCI root bridge memory space. +/// CPU cannot use address in this struct to deference memory. +/// This is effectively the same as rust's slice type. +/// This type only exists to prevent users from accidentally dereferencing it. +#[derive(Debug, Copy, Clone)] +pub struct PciRegion { + /// Starting address of the memory region + pub device_address: u64, + + /// Byte length of the memory region. + pub length: usize +} + impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { pub(crate) fn new( device_address: u64, @@ -44,8 +56,10 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { let end = device_address + length as u64; debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end); Self { - device_address, - length, + region: PciRegion { + device_address, + length, + }, _lifetime_holder, key, proto, @@ -58,8 +72,8 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { /// **Returned address cannot be used to reference memory from CPU!** /// **Do not cast it back to pointer or reference** #[must_use] - pub const fn region(&self) -> (u64, usize) { - (self.device_address, self.length) + pub const fn region(&self) -> PciRegion { + self.region } } @@ -68,8 +82,8 @@ impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { let status = unsafe { (self.proto.unmap)(self.proto, self.key) }; match status { Status::SUCCESS => { - let end = self.device_address + self.length as u64; - debug!("Region [0x{:X}..0x{:X}] was unmapped", self.device_address, end); + let end = self.region.device_address + self.region.length as u64; + debug!("Region [0x{:X}..0x{:X}] was unmapped", self.region.device_address, end); } Status::INVALID_PARAMETER => { panic!("This region was not mapped using PciRootBridgeIo::map"); @@ -81,3 +95,17 @@ impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { } } } + +impl PciRegion { + /// Creates a new region of memory with different length. + /// The new region must have shorter length to ensure + /// it won't contain invalid memory address. + #[must_use] + pub fn with_length(self, new_length: usize) -> Self { + assert!(new_length <= self.length); + Self { + device_address: self.device_address, + length: new_length, + } + } +} diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index ae3483bd9..fc59f9299 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -5,7 +5,7 @@ use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; -use crate::proto::pci::mapped_region::PciMappedRegion; +use crate::proto::pci::region::{PciMappedRegion, PciRegion}; use core::ffi::c_void; use core::mem::MaybeUninit; use core::num::NonZeroUsize; @@ -14,10 +14,7 @@ use core::ptr::NonNull; use log::debug; use uefi_macros::unsafe_protocol; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::{ - PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, - PciRootBridgeIoProtocolOperation, -}; +use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; #[cfg(doc)] @@ -60,12 +57,12 @@ impl PciRootBridgeIo { /// Allocates pages suitable for communicating with PCI devices. /// /// # Errors - /// - [`crate::Status::INVALID_PARAMETER`] MemoryType is invalid. - /// - [`crate::Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: + /// - [`Status::INVALID_PARAMETER`] MemoryType is invalid. + /// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`] /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] - /// - [`crate::Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. + /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. pub fn allocate_buffer( &self, memory_type: MemoryType, @@ -104,11 +101,7 @@ impl PciRootBridgeIo { Status::SUCCESS => { let base = NonNull::new(address as *mut MaybeUninit).unwrap(); debug!("Allocated {} pages at 0x{:X}", pages.get(), address); - Ok(PciBuffer { - base, - pages, - proto: &self.0, - }) + Ok(PciBuffer::new(base, pages, &self.0)) } error @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { @@ -158,10 +151,37 @@ impl PciRootBridgeIo { } } + /// Copies a region in PCI root bridge memory space onto the other. + /// Two regions must have same length. Functionally, this is the same as + /// `<[T]>::copy_from_slice` which is effectively memcpy. + /// And the same safety requirements as the above method apply. + pub fn copy( + &mut self, + width: PciRootBridgeIoProtocolWidth, + destination: PciRegion, + source: PciRegion, + ) -> crate::Result<()> { + assert_eq!(destination.length, source.length); + + let status = unsafe { + (self.0.copy_mem)( + &mut self.0, + width, + destination.device_address, + source.device_address, + destination.length, + ) + }; + + match status { + Status::SUCCESS => Ok(()), + error => Err(error.into()), + } + } + // TODO: poll I/O // TODO: mem I/O access // TODO: io I/O access - // TODO: copy memory // TODO: get/set attributes // TODO: configuration / resource settings } From 0dbfcfa9e37504f47caeb265d9ef23c4c85065aa Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Sun, 22 Jun 2025 04:26:45 +0900 Subject: [PATCH 04/14] uefi: Use RefCell for allocate_buffer and map --- uefi-test-runner/src/proto/pci/mod.rs | 1 + uefi-test-runner/src/proto/pci/root_bridge.rs | 115 +++++++++++------- uefi/src/proto/pci/buffer.rs | 19 ++- uefi/src/proto/pci/region.rs | 22 ++-- uefi/src/proto/pci/root_bridge.rs | 69 ++++++++--- 5 files changed, 140 insertions(+), 86 deletions(-) diff --git a/uefi-test-runner/src/proto/pci/mod.rs b/uefi-test-runner/src/proto/pci/mod.rs index f7c78e0e6..4b863de3a 100644 --- a/uefi-test-runner/src/proto/pci/mod.rs +++ b/uefi-test-runner/src/proto/pci/mod.rs @@ -6,4 +6,5 @@ pub fn test() { root_bridge::test_io(); root_bridge::test_buffer(); root_bridge::test_mapping(); + root_bridge::test_copy(); } diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 3fb72d6a0..27041cffb 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,13 +1,16 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +use core::cell::RefCell; use core::mem; -use qcell::{QCell, QCellOwner}; -use uefi::Handle; -use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle}; -use uefi::proto::ProtocolPointer; -use uefi::proto::pci::PciIoAddress; +use uefi::boot::{image_handle, OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; use uefi::proto::pci::root_bridge::PciRootBridgeIo; -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; +use uefi::proto::pci::PciIoAddress; +use uefi::proto::ProtocolPointer; +use uefi::Handle; +use uefi_raw::protocol::pci::root_bridge::{ + PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, + PciRootBridgeIoProtocolWidth, +}; use uefi_raw::table::boot::{MemoryType, PAGE_SIZE}; const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4; @@ -75,14 +78,15 @@ pub fn test_buffer() { for pci_handle in pci_handles { let pci_proto = get_open_protocol::(pci_handle); - - let mut buffer = pci_proto - .allocate_buffer::<[u8; 4096]>( - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); + let pci_proto: RefCell<_> = pci_proto.into(); + + let mut buffer = PciRootBridgeIo::allocate_buffer::<[u8; 4096]>( + &pci_proto, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); let buffer = unsafe { buffer.assume_init_mut().fill(0); buffer.assume_init() @@ -96,19 +100,25 @@ pub fn test_mapping() { for pci_handle in pci_handles { let pci_proto = get_open_protocol::(pci_handle); - - let mut buffer = pci_proto - .allocate_buffer::<[u8; 4096]>( - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); + let pci_proto: RefCell<_> = pci_proto.into(); + + let mut buffer = PciRootBridgeIo::allocate_buffer::<[u8; 4096]>( + &pci_proto, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); let buffer = unsafe { buffer.assume_init_mut().fill(0); buffer.assume_init() }; - let mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref()); + + let mapped = PciRootBridgeIo::map( + &pci_proto, + PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, + buffer.as_ref(), + ); if mapped.region().device_address == buffer.as_ptr().addr() as u64 { info!("This PCI device uses identity mapping"); } else { @@ -121,35 +131,48 @@ pub fn test_copy() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - let mut owner = QCellOwner::new(); - let item = QCell::new(&owner, get_open_protocol::(pci_handle)); - let pci_proto = owner.rw(&item); - - let mut src = pci_proto - .allocate_buffer::<[u32; 4096 / 4]>( - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); + let pci_proto = get_open_protocol::(pci_handle); + let pci_proto: RefCell<_> = pci_proto.into(); + + let mut src = PciRootBridgeIo::allocate_buffer::<[u32; 4096 / 4]>( + &pci_proto, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); assert_eq!(size_of_val(src.as_ref()), size_of::<[u8; PAGE_SIZE]>()); let src = unsafe { src.assume_init_mut().fill(0xDEADBEEF); src.assume_init() }; - let src_mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, src.as_ref()); - - let dst = pci_proto - .allocate_buffer::<[u32; 4096 / 4]>( - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); + let src_mapped = PciRootBridgeIo::map( + &pci_proto, + PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, + src.as_ref(), + ); + + let dst = PciRootBridgeIo::allocate_buffer::<[u32; 4096 / 4]>( + &pci_proto, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ) + .unwrap(); assert_eq!(size_of_val(dst.as_ref()), size_of::<[u8; PAGE_SIZE]>()); - let dst_mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, dst.as_ref()); - - pci_proto.copy(PciRootBridgeIoProtocolWidth::UINT32, dst_mapped.region(), src_mapped.region()).unwrap(); + let dst_mapped = PciRootBridgeIo::map( + &pci_proto, + PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, + dst.as_ref(), + ); + + PciRootBridgeIo::copy( + &pci_proto, + PciRootBridgeIoProtocolWidth::UINT32, + dst_mapped.region(), + src_mapped.region(), + ) + .unwrap(); drop(dst_mapped); let dst = unsafe { dst.assume_init() }; diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs index 37121d1a5..f0036a5ff 100644 --- a/uefi/src/proto/pci/buffer.rs +++ b/uefi/src/proto/pci/buffer.rs @@ -2,22 +2,22 @@ //! Defines wrapper allocated by PCI Root Bridge protocol. +use core::cell::RefCell; use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZeroUsize; use core::ops::{Deref, DerefMut}; -use core::ptr; use core::ptr::NonNull; use log::debug; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; +use crate::boot::ScopedProtocol; +use crate::proto::pci::root_bridge::PciRootBridgeIo; /// Smart pointer for wrapping owned buffer allocated by PCI Root Bridge protocol. #[derive(Debug)] pub struct PciBuffer<'p, T> { base: NonNull, pages: NonZeroUsize, - proto_lifetime: &'p (), - proto: *const PciRootBridgeIoProtocol, + proto: &'p RefCell>, } impl<'p, T> PciBuffer<'p, MaybeUninit> { @@ -26,12 +26,11 @@ impl<'p, T> PciBuffer<'p, MaybeUninit> { /// Passed protocol is stored as a pointer along with its lifetime so that it doesn't /// block others from using its mutable functions. #[must_use] - pub const fn new(base: NonNull>, pages: NonZeroUsize, proto: &'p PciRootBridgeIoProtocol) -> Self { + pub const fn new(base: NonNull>, pages: NonZeroUsize, proto: &'p RefCell>) -> Self { Self { base, pages, - proto_lifetime: &(), - proto: ptr::from_ref(proto), + proto, } } @@ -45,7 +44,6 @@ impl<'p, T> PciBuffer<'p, MaybeUninit> { PciBuffer { base: old.base.cast(), pages: old.pages, - proto_lifetime: old.proto_lifetime, proto: old.proto, } } @@ -79,10 +77,7 @@ impl<'p, T> DerefMut for PciBuffer<'p, T> { impl<'p, T> Drop for PciBuffer<'p, T> { fn drop(&mut self) { - let status = unsafe { - let proto = self.proto.as_ref().unwrap(); - (proto.free_buffer)(proto, self.pages.get(), self.base.as_ptr().cast()) - }; + let status = PciRootBridgeIo::free_buffer(&self.proto, self.pages, self.base); match status { Status::SUCCESS => { debug!( diff --git a/uefi/src/proto/pci/region.rs b/uefi/src/proto/pci/region.rs index 7501c7270..00217f904 100644 --- a/uefi/src/proto/pci/region.rs +++ b/uefi/src/proto/pci/region.rs @@ -2,11 +2,14 @@ //! Defines wrapper for region mapped by PCI Root Bridge I/O protocol. +use core::cell::RefCell; use core::ffi::c_void; +use core::marker::PhantomData; use core::ptr; use log::debug; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; +use crate::boot::ScopedProtocol; +use crate::proto::pci::root_bridge::PciRootBridgeIo; /// Represents a region of memory mapped by PCI Root Bridge I/O protocol. /// The region will be unmapped automatically when it is dropped. @@ -22,9 +25,9 @@ where 'p: 'r, { region: PciRegion, - _lifetime_holder: &'r (), + _lifetime_holder: PhantomData<&'r ()>, key: *const c_void, - proto: &'p PciRootBridgeIoProtocol, + proto: &'p RefCell>, } /// Represents a region of memory in PCI root bridge memory space. @@ -45,14 +48,9 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { device_address: u64, length: usize, key: *const c_void, - to_map: &'r T, - proto: &'p PciRootBridgeIoProtocol, + _to_map: &'r T, + proto: &'p RefCell>, ) -> Self { - let _lifetime_holder: &'r () = unsafe { - let ptr = ptr::from_ref(to_map); - ptr.cast::<()>().as_ref().unwrap() - }; - let end = device_address + length as u64; debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end); Self { @@ -60,7 +58,7 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { device_address, length, }, - _lifetime_holder, + _lifetime_holder: PhantomData, key, proto, } @@ -79,7 +77,7 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { fn drop(&mut self) { - let status = unsafe { (self.proto.unmap)(self.proto, self.key) }; + let status = PciRootBridgeIo::unmap(self.proto, self.key); match status { Status::SUCCESS => { let end = self.region.device_address + self.region.length as u64; diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index fc59f9299..70a94b0b0 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -2,11 +2,12 @@ //! PCI Root Bridge protocol. +use core::cell::RefCell; +use core::ffi::c_void; use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; use crate::proto::pci::region::{PciMappedRegion, PciRegion}; -use core::ffi::c_void; use core::mem::MaybeUninit; use core::num::NonZeroUsize; use core::ptr; @@ -16,6 +17,7 @@ use uefi_macros::unsafe_protocol; use uefi_raw::Status; use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; +use crate::boot::ScopedProtocol; #[cfg(doc)] use crate::Status; @@ -25,6 +27,14 @@ use crate::Status; /// # UEFI Spec Description /// Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are /// used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller. +/// +/// # RefCell Usage +/// Types such as [`PciMappedRegion`] holds references to this protocol +/// so that it can free themselves in drop. However it prevents others calling mutable +/// method of this protocol. To avoid this we +/// +/// TODO Find a way to hide above implementation detail. Even if we still use +/// RefCell, we should use it internally instead of requiring users to do so themselves. #[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(PciRootBridgeIoProtocol::GUID)] @@ -64,7 +74,7 @@ impl PciRootBridgeIo { /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. pub fn allocate_buffer( - &self, + protocol: &RefCell>, memory_type: MemoryType, pages: Option, attributes: PciRootBridgeIoProtocolAttribute, @@ -86,9 +96,11 @@ impl PciRootBridgeIo { NonZeroUsize::new(size.div_ceil(alignment)).unwrap() }; + let borrow = protocol.borrow(); + let raw = &borrow.0; let status = unsafe { - (self.0.allocate_buffer)( - &self.0, + (raw.allocate_buffer)( + raw, AllocateType(0), memory_type, pages.get(), @@ -101,7 +113,8 @@ impl PciRootBridgeIo { Status::SUCCESS => { let base = NonNull::new(address as *mut MaybeUninit).unwrap(); debug!("Allocated {} pages at 0x{:X}", pages.get(), address); - Ok(PciBuffer::new(base, pages, &self.0)) + drop(borrow); + Ok(PciBuffer::new(base, pages, protocol)) } error @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { @@ -111,6 +124,18 @@ impl PciRootBridgeIo { } } + pub(crate) fn free_buffer( + protocol: &RefCell>, + pages: NonZeroUsize, + base: NonNull, + ) -> Status { + let borrow = protocol.borrow(); + let raw = &borrow.0; + unsafe { + (raw.free_buffer)(raw, pages.get(), base.as_ptr().cast()) + } + } + /// Map given variable's address into PCI Controller-specific address /// required to access it from a DMA bus master. /// # Arguments @@ -120,7 +145,7 @@ impl PciRootBridgeIo { /// # Returns /// - PciMappedRegion capturing lifetime of passed variable pub fn map<'p, 'r, T>( - &'p self, + protocol: &'p RefCell>, operation: PciRootBridgeIoProtocolOperation, to_map: &'r T, ) -> PciMappedRegion<'p, 'r> @@ -133,8 +158,10 @@ impl PciRootBridgeIo { let mut mapping: *mut c_void = ptr::null_mut(); let status = unsafe { - (self.0.map)( - &self.0, + let borrow = protocol.borrow(); + let raw = &borrow.0; + (raw.map)( + raw, operation, host_address.cast(), ptr::from_mut(&mut bytes), @@ -145,27 +172,40 @@ impl PciRootBridgeIo { match status { Status::SUCCESS => { - PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0) + PciMappedRegion::new(mapped_address, bytes, mapping, to_map, protocol) } _ => unreachable!(), } } + pub(crate) fn unmap( + protocol: &RefCell>, + key: *const c_void, + ) -> Status { + let borrow = protocol.borrow(); + let raw = &borrow.0; + unsafe { + (raw.unmap)(raw, key) + } + } + /// Copies a region in PCI root bridge memory space onto the other. /// Two regions must have same length. Functionally, this is the same as /// `<[T]>::copy_from_slice` which is effectively memcpy. /// And the same safety requirements as the above method apply. pub fn copy( - &mut self, + protocol: &RefCell>, width: PciRootBridgeIoProtocolWidth, destination: PciRegion, source: PciRegion, ) -> crate::Result<()> { assert_eq!(destination.length, source.length); + let mut borrow = protocol.borrow_mut(); let status = unsafe { - (self.0.copy_mem)( - &mut self.0, + let raw = &mut borrow.0; + (raw.copy_mem)( + raw, width, destination.device_address, source.device_address, @@ -173,10 +213,7 @@ impl PciRootBridgeIo { ) }; - match status { - Status::SUCCESS => Ok(()), - error => Err(error.into()), - } + status.to_result() } // TODO: poll I/O From ea659420dfd0ac63785b95ef49358458004f7421 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Sat, 28 Jun 2025 09:17:12 +0900 Subject: [PATCH 05/14] uefi: Switch to GhostCell This allows PciRootBridgeIo to be used as `protocol.copy_mem` instead of `PciRootBridgeIo::copy_mem(protocol, ...)` --- Cargo.lock | 8 + Cargo.toml | 1 + uefi-raw/src/protocol/pci/root_bridge.rs | 20 ++ uefi-test-runner/Cargo.toml | 1 + uefi-test-runner/src/proto/pci/root_bridge.rs | 242 +++++++++--------- uefi/Cargo.toml | 1 + uefi/src/proto/pci/buffer.rs | 57 ++++- uefi/src/proto/pci/region.rs | 42 ++- uefi/src/proto/pci/root_bridge.rs | 113 ++++---- 9 files changed, 288 insertions(+), 197 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f3727fb2..513a4f471 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,6 +339,12 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] +[[package]] +name = "ghost-cell" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" + [[package]] name = "glob" version = "0.3.2" @@ -953,6 +959,7 @@ version = "0.35.0" dependencies = [ "bitflags 2.9.1", "cfg-if", + "ghost-cell", "log", "ptr_meta", "qemu-exit", @@ -992,6 +999,7 @@ dependencies = [ name = "uefi-test-runner" version = "0.2.0" dependencies = [ + "ghost-cell", "log", "qemu-exit", "smoltcp", diff --git a/Cargo.toml b/Cargo.toml index df43bbca0..4aaff49a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ bitflags = "2.0.0" log = { version = "0.4.5", default-features = false } ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] } uguid = "2.2.1" +ghost-cell = "0.2.6" [patch.crates-io] uefi = { path = "uefi" } diff --git a/uefi-raw/src/protocol/pci/root_bridge.rs b/uefi-raw/src/protocol/pci/root_bridge.rs index 0b21b17ed..83a74db36 100644 --- a/uefi-raw/src/protocol/pci/root_bridge.rs +++ b/uefi-raw/src/protocol/pci/root_bridge.rs @@ -153,3 +153,23 @@ pub struct PciRootBridgeIoProtocol { impl PciRootBridgeIoProtocol { pub const GUID: Guid = guid!("2f707ebb-4a1a-11d4-9a38-0090273fc14d"); } + +impl PciRootBridgeIoProtocolWidth { + pub fn size(self) -> usize { + match self { + Self::UINT8 | + Self::FIFO_UINT8 | + Self::FILL_UINT8 => 1, + Self::UINT16 | + Self::FIFO_UINT16 | + Self::FILL_UINT16 => 2, + Self::UINT32 | + Self::FIFO_UINT32 | + Self::FILL_UINT32 => 4, + Self::UINT64 | + Self::FIFO_UINT64 | + Self::FILL_UINT64 => 8, + _ => unreachable!(), + } + } +} diff --git a/uefi-test-runner/Cargo.toml b/uefi-test-runner/Cargo.toml index fe534ac92..93b359f00 100644 --- a/uefi-test-runner/Cargo.toml +++ b/uefi-test-runner/Cargo.toml @@ -9,6 +9,7 @@ edition = "2024" uefi-raw = { path = "../uefi-raw" } uefi = { path = "../uefi", features = ["alloc", "global_allocator", "panic_handler", "logger", "qemu", "log-debugcon"] } smoltcp = { version = "0.12.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "socket-udp"] } +ghost-cell.workspace = true log.workspace = true diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 27041cffb..3cd8f546e 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -2,6 +2,8 @@ use core::cell::RefCell; use core::mem; +use core::ops::DerefMut; +use ghost_cell::GhostToken; use uefi::boot::{image_handle, OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; use uefi::proto::pci::root_bridge::PciRootBridgeIo; use uefi::proto::pci::PciIoAddress; @@ -27,45 +29,46 @@ pub fn test_io() { let mut sata_ctrl_cnt = 0; for pci_handle in pci_handles { - let mut pci_proto = get_open_protocol::(pci_handle); - - for bus in 0..=255 { - for dev in 0..32 { - for fun in 0..8 { - let addr = PciIoAddress::new(bus, dev, fun); - let Ok(reg0) = pci_proto.pci().read_one::(addr.with_register(0)) else { - continue; - }; - if reg0 == 0xFFFFFFFF { - continue; // not a valid device - } - let reg1 = pci_proto - .pci() - .read_one::(addr.with_register(2 * REG_SIZE)) - .unwrap(); - - let vendor_id = (reg0 & 0xFFFF) as u16; - let device_id = (reg0 >> 16) as u16; - if vendor_id == RED_HAT_PCI_VENDOR_ID { - red_hat_dev_cnt += 1; - } + GhostToken::new(|mut token| { + let pci_proto = get_open_protocol::(pci_handle); + for bus in 0..=255 { + for dev in 0..32 { + for fun in 0..8 { + let addr = PciIoAddress::new(bus, dev, fun); + let Ok(reg0) = pci_proto.pci(&mut token).read_one::(addr.with_register(0)) else { + continue; + }; + if reg0 == 0xFFFFFFFF { + continue; // not a valid device + } + let reg1 = pci_proto + .pci(&mut token) + .read_one::(addr.with_register(2 * REG_SIZE)) + .unwrap(); + + let vendor_id = (reg0 & 0xFFFF) as u16; + let device_id = (reg0 >> 16) as u16; + if vendor_id == RED_HAT_PCI_VENDOR_ID { + red_hat_dev_cnt += 1; + } - let class_code = (reg1 >> 24) as u8; - let subclass_code = ((reg1 >> 16) & 0xFF) as u8; - if class_code == MASS_STORAGE_CTRL_CLASS_CODE { - mass_storage_ctrl_cnt += 1; + let class_code = (reg1 >> 24) as u8; + let subclass_code = ((reg1 >> 16) & 0xFF) as u8; + if class_code == MASS_STORAGE_CTRL_CLASS_CODE { + mass_storage_ctrl_cnt += 1; - if subclass_code == SATA_CTRL_SUBCLASS_CODE { - sata_ctrl_cnt += 1; + if subclass_code == SATA_CTRL_SUBCLASS_CODE { + sata_ctrl_cnt += 1; + } } - } - log::info!( + log::info!( "PCI Device: [{bus}, {dev}, {fun}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X}" ); + } } } - } + }); } assert!(red_hat_dev_cnt > 0); @@ -77,21 +80,23 @@ pub fn test_buffer() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - let pci_proto = get_open_protocol::(pci_handle); - let pci_proto: RefCell<_> = pci_proto.into(); - - let mut buffer = PciRootBridgeIo::allocate_buffer::<[u8; 4096]>( - &pci_proto, - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); - let buffer = unsafe { - buffer.assume_init_mut().fill(0); - buffer.assume_init() - }; - assert_eq!(buffer.as_ptr().addr() % 4096, 0); + GhostToken::new(|token| { + let pci_proto = get_open_protocol::(pci_handle); + let token: RefCell<_> = token.into(); + let mut buffer = pci_proto.allocate_buffer::<[u8; 4096]>( + &token, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ).unwrap(); + + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; + + assert_eq!(buffer.as_ptr().addr() % 4096, 0); + }); } } @@ -99,31 +104,32 @@ pub fn test_mapping() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - let pci_proto = get_open_protocol::(pci_handle); - let pci_proto: RefCell<_> = pci_proto.into(); - - let mut buffer = PciRootBridgeIo::allocate_buffer::<[u8; 4096]>( - &pci_proto, - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); - let buffer = unsafe { - buffer.assume_init_mut().fill(0); - buffer.assume_init() - }; - - let mapped = PciRootBridgeIo::map( - &pci_proto, - PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, - buffer.as_ref(), - ); - if mapped.region().device_address == buffer.as_ptr().addr() as u64 { - info!("This PCI device uses identity mapping"); - } else { - info!("This PCI device uses different mapping from CPU"); - } + GhostToken::new(|token| { + let pci_proto = get_open_protocol::(pci_handle); + let token: RefCell<_> = token.into(); + + let mut buffer = pci_proto.allocate_buffer::<[u8; 4096]>( + &token, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ).unwrap(); + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; + + let mapped = pci_proto.map( + &token, + PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, + buffer.as_ref(), + ); + if mapped.region().device_address == buffer.as_ptr().addr() as u64 { + info!("This PCI device uses identity mapping"); + } else { + info!("This PCI device uses different mapping from CPU"); + } + }); } } @@ -131,52 +137,54 @@ pub fn test_copy() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - let pci_proto = get_open_protocol::(pci_handle); - let pci_proto: RefCell<_> = pci_proto.into(); - - let mut src = PciRootBridgeIo::allocate_buffer::<[u32; 4096 / 4]>( - &pci_proto, - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); - assert_eq!(size_of_val(src.as_ref()), size_of::<[u8; PAGE_SIZE]>()); - let src = unsafe { - src.assume_init_mut().fill(0xDEADBEEF); - src.assume_init() - }; - let src_mapped = PciRootBridgeIo::map( - &pci_proto, - PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, - src.as_ref(), - ); - - let dst = PciRootBridgeIo::allocate_buffer::<[u32; 4096 / 4]>( - &pci_proto, - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ) - .unwrap(); - assert_eq!(size_of_val(dst.as_ref()), size_of::<[u8; PAGE_SIZE]>()); - let dst_mapped = PciRootBridgeIo::map( - &pci_proto, - PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, - dst.as_ref(), - ); - - PciRootBridgeIo::copy( - &pci_proto, - PciRootBridgeIoProtocolWidth::UINT32, - dst_mapped.region(), - src_mapped.region(), - ) - .unwrap(); - drop(dst_mapped); - let dst = unsafe { dst.assume_init() }; - - assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); + GhostToken::new(|token| { + let pci_proto = get_open_protocol::(pci_handle); + let token: RefCell<_> = token.into(); + + let mut src = pci_proto.allocate_buffer::<[u32; 4096 / 4]>( + &token, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ).unwrap(); + assert_eq!(size_of_val(src.as_ref()), size_of::<[u8; PAGE_SIZE]>()); + let src = unsafe { + src.assume_init_mut().fill(0xDEADBEEF); + src.assume_init() + }; + let src_mapped = pci_proto.map( + &token, + PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, + src.as_ref(), + ); + + let dst = pci_proto.allocate_buffer::<[u32; 4096 / 4]>( + &token, + MemoryType::BOOT_SERVICES_DATA, + None, + PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, + ).unwrap(); + assert_eq!(size_of_val(dst.as_ref()), size_of::<[u8; PAGE_SIZE]>()); + let dst_mapped = pci_proto.map( + &token, + PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, + dst.as_ref(), + ); + + let width = PciRootBridgeIoProtocolWidth::UINT32; + assert_eq!(width.size(), 4); + + pci_proto.copy( + token.borrow_mut().deref_mut(), + width, + dst_mapped.region(), + src_mapped.region(), + ).unwrap(); + drop(dst_mapped); + let dst = unsafe { dst.assume_init() }; + + assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); + }); } } diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index 1048c1ab4..a67866b1a 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -45,6 +45,7 @@ ucs2 = "0.3.3" uefi-macros = "0.18.1" uefi-raw = "0.11.0" qemu-exit = { version = "3.0.2", optional = true } +ghost-cell.workspace = true [package.metadata.docs.rs] all-features = true diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs index f0036a5ff..831858e39 100644 --- a/uefi/src/proto/pci/buffer.rs +++ b/uefi/src/proto/pci/buffer.rs @@ -3,34 +3,41 @@ //! Defines wrapper allocated by PCI Root Bridge protocol. use core::cell::RefCell; +use core::fmt::{Debug, Formatter}; use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZeroUsize; use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; +use ghost_cell::{GhostCell, GhostToken}; use log::debug; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; use uefi_raw::Status; -use crate::boot::ScopedProtocol; -use crate::proto::pci::root_bridge::PciRootBridgeIo; /// Smart pointer for wrapping owned buffer allocated by PCI Root Bridge protocol. -#[derive(Debug)] -pub struct PciBuffer<'p, T> { +pub struct PciBuffer<'b, 'id, T> { base: NonNull, pages: NonZeroUsize, - proto: &'p RefCell>, + proto: &'b GhostCell<'id, PciRootBridgeIoProtocol>, + token: &'b RefCell> } -impl<'p, T> PciBuffer<'p, MaybeUninit> { +impl<'b, 'id, T> PciBuffer<'b, 'id, MaybeUninit> { /// Creates wrapper for buffer allocated by PCI Root Bridge protocol. /// Passed protocol is stored as a pointer along with its lifetime so that it doesn't /// block others from using its mutable functions. #[must_use] - pub const fn new(base: NonNull>, pages: NonZeroUsize, proto: &'p RefCell>) -> Self { + pub const fn new( + base: NonNull>, + pages: NonZeroUsize, + proto: &'b GhostCell<'id, PciRootBridgeIoProtocol>, + token: &'b RefCell> + ) -> Self { Self { base, pages, proto, + token, } } @@ -39,29 +46,30 @@ impl<'p, T> PciBuffer<'p, MaybeUninit> { /// # Safety /// Callers of this function must guarantee that value stored is valid. #[must_use] - pub unsafe fn assume_init(self) -> PciBuffer<'p, T> { + pub unsafe fn assume_init(self) -> PciBuffer<'b, 'id, T> { let old = ManuallyDrop::new(self); PciBuffer { base: old.base.cast(), pages: old.pages, proto: old.proto, + token: old.token, } } } -impl<'p, T> AsRef for PciBuffer<'p, T> { +impl<'b, 'id, T> AsRef for PciBuffer<'b, 'id, T> { fn as_ref(&self) -> &T { unsafe { self.base.as_ref() } } } -impl<'p, T> AsMut for PciBuffer<'p, T> { +impl<'b, 'id, T> AsMut for PciBuffer<'b, 'id, T> { fn as_mut(&mut self) -> &mut T { unsafe { self.base.as_mut() } } } -impl<'p, T> Deref for PciBuffer<'p, T> { +impl<'b, 'id, T> Deref for PciBuffer<'b, 'id, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -69,15 +77,19 @@ impl<'p, T> Deref for PciBuffer<'p, T> { } } -impl<'p, T> DerefMut for PciBuffer<'p, T> { +impl<'b, 'id, T> DerefMut for PciBuffer<'b, 'id, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut() } } -impl<'p, T> Drop for PciBuffer<'p, T> { +impl<'b, 'id, T> Drop for PciBuffer<'b, 'id, T> { fn drop(&mut self) { - let status = PciRootBridgeIo::free_buffer(&self.proto, self.pages, self.base); + let token = self.token.borrow(); + let protocol = self.proto.borrow(token.deref()); + let status = unsafe { + (protocol.free_buffer)(protocol, self.pages.get(), self.base.as_ptr().cast()) + }; match status { Status::SUCCESS => { debug!( @@ -93,3 +105,20 @@ impl<'p, T> Drop for PciBuffer<'p, T> { } } } + +impl<'b, 'id, T> Debug for PciBuffer<'b, 'id, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let mut debug = f.debug_struct("PciBuffer"); + debug.field("base", &self.base); + debug.field("pages", &self.pages); + + if let Ok(token) = self.token.try_borrow() { + let protocol = self.proto.borrow(token.deref()); + debug.field("proto", protocol); + } else { + debug.field("proto", &"unavailable"); + }; + + debug.finish() + } +} \ No newline at end of file diff --git a/uefi/src/proto/pci/region.rs b/uefi/src/proto/pci/region.rs index 00217f904..7bbcfc5c2 100644 --- a/uefi/src/proto/pci/region.rs +++ b/uefi/src/proto/pci/region.rs @@ -4,12 +4,13 @@ use core::cell::RefCell; use core::ffi::c_void; +use core::fmt::{Debug, Formatter}; use core::marker::PhantomData; -use core::ptr; +use core::ops::Deref; +use ghost_cell::{GhostCell, GhostToken}; use log::debug; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; use uefi_raw::Status; -use crate::boot::ScopedProtocol; -use crate::proto::pci::root_bridge::PciRootBridgeIo; /// Represents a region of memory mapped by PCI Root Bridge I/O protocol. /// The region will be unmapped automatically when it is dropped. @@ -19,15 +20,15 @@ use crate::proto::pci::root_bridge::PciRootBridgeIo; /// `'r` is the lifetime for Mapped Region. /// Protocol must outlive the mapped region /// as unmap function can only be accessed through the protocol. -#[derive(Debug)] -pub struct PciMappedRegion<'p, 'r> +pub struct PciMappedRegion<'p, 'r, 'id> where 'p: 'r, { region: PciRegion, _lifetime_holder: PhantomData<&'r ()>, key: *const c_void, - proto: &'p RefCell>, + proto: &'p GhostCell<'id, PciRootBridgeIoProtocol>, + token: &'p RefCell>, } /// Represents a region of memory in PCI root bridge memory space. @@ -43,13 +44,14 @@ pub struct PciRegion { pub length: usize } -impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { +impl<'p, 'r, 'id> PciMappedRegion<'p, 'r, 'id> where 'p: 'r { pub(crate) fn new( device_address: u64, length: usize, key: *const c_void, _to_map: &'r T, - proto: &'p RefCell>, + proto: &'p GhostCell<'id, PciRootBridgeIoProtocol>, + token: &'p RefCell>, ) -> Self { let end = device_address + length as u64; debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end); @@ -61,6 +63,7 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { _lifetime_holder: PhantomData, key, proto, + token, } } @@ -75,9 +78,13 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r { } } -impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { +impl<'p, 'r, 'id> Drop for PciMappedRegion<'p, 'r, 'id> { fn drop(&mut self) { - let status = PciRootBridgeIo::unmap(self.proto, self.key); + let token = self.token.borrow(); + let protocol = self.proto.borrow(token.deref()); + let status = unsafe { + (protocol.unmap)(protocol, self.key) + }; match status { Status::SUCCESS => { let end = self.region.device_address + self.region.length as u64; @@ -94,6 +101,21 @@ impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { } } +impl<'p, 'r, 'id> Debug for PciMappedRegion<'p, 'r, 'id> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let mut debug = f.debug_struct("PciMappedRegion"); + debug.field("region", &self.region); + debug.field("key", &self.key); + + if let Ok(token) = self.token.try_borrow() { + debug.field("proto", self.proto.borrow(token.deref())); + } else { + debug.field("proto", &"unavailable"); + } + debug.finish() + } +} + impl PciRegion { /// Creates a new region of memory with different length. /// The new region must have shorter length to ensure diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index 70a94b0b0..1e49043f4 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -4,20 +4,22 @@ use core::cell::RefCell; use core::ffi::c_void; +use core::fmt::{Debug, Formatter}; use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; use crate::proto::pci::region::{PciMappedRegion, PciRegion}; use core::mem::MaybeUninit; use core::num::NonZeroUsize; +use core::ops::Deref; use core::ptr; use core::ptr::NonNull; +use ghost_cell::{GhostCell, GhostToken}; use log::debug; use uefi_macros::unsafe_protocol; use uefi_raw::Status; use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; -use crate::boot::ScopedProtocol; #[cfg(doc)] use crate::Status; @@ -35,23 +37,24 @@ use crate::Status; /// /// TODO Find a way to hide above implementation detail. Even if we still use /// RefCell, we should use it internally instead of requiring users to do so themselves. -#[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(PciRootBridgeIoProtocol::GUID)] -pub struct PciRootBridgeIo(PciRootBridgeIoProtocol); +pub struct PciRootBridgeIo<'id>(GhostCell<'id, PciRootBridgeIoProtocol>); -impl PciRootBridgeIo { +impl<'id> PciRootBridgeIo<'id> { /// Get the segment number where this PCI root bridge resides. #[must_use] - pub const fn segment_nr(&self) -> u32 { - self.0.segment_number + pub fn segment_nr(&self, token: &GhostToken<'id>) -> u32 { + self.0.borrow(token).segment_number } /// Access PCI I/O operations on this root bridge. - pub const fn pci(&mut self) -> PciIoAccessPci<'_> { + pub fn pci<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a> where 'p: 'a + { + let proto = self.0.borrow_mut(token); PciIoAccessPci { - proto: &mut self.0, - io_access: &mut self.0.pci, + proto, + io_access: &mut proto.pci, } } @@ -60,8 +63,9 @@ impl PciRootBridgeIo { /// # Errors /// - [`Status::DEVICE_ERROR`] The PCI posted write transactions were not flushed from the PCI host bridge /// due to a hardware error. - pub fn flush(&mut self) -> crate::Result<()> { - unsafe { (self.0.flush)(&mut self.0).to_result() } + pub fn flush(&self, token: &mut GhostToken<'id>) -> crate::Result<()> { + let proto = self.0.borrow_mut(token); + unsafe { (proto.flush)(proto).to_result() } } /// Allocates pages suitable for communicating with PCI devices. @@ -73,12 +77,14 @@ impl PciRootBridgeIo { /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. - pub fn allocate_buffer( - protocol: &RefCell>, + #[cfg(feature = "alloc")] + pub fn allocate_buffer<'p, 'b, T>( + &'p self, + token: &'b RefCell>, memory_type: MemoryType, pages: Option, attributes: PciRootBridgeIoProtocolAttribute, - ) -> crate::Result>> { + ) -> crate::Result>> where 'p: 'b { let mut address = 0usize; let original_alignment = align_of::(); assert_ne!(original_alignment, 0); @@ -96,11 +102,11 @@ impl PciRootBridgeIo { NonZeroUsize::new(size.div_ceil(alignment)).unwrap() }; - let borrow = protocol.borrow(); - let raw = &borrow.0; + let token_borrow = token.borrow(); + let protocol = self.0.borrow(token_borrow.deref()); let status = unsafe { - (raw.allocate_buffer)( - raw, + (protocol.allocate_buffer)( + protocol, AllocateType(0), memory_type, pages.get(), @@ -111,10 +117,10 @@ impl PciRootBridgeIo { match status { Status::SUCCESS => { + drop(token_borrow); let base = NonNull::new(address as *mut MaybeUninit).unwrap(); debug!("Allocated {} pages at 0x{:X}", pages.get(), address); - drop(borrow); - Ok(PciBuffer::new(base, pages, protocol)) + Ok(PciBuffer::new(base, pages, &self.0, token)) } error @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { @@ -124,18 +130,6 @@ impl PciRootBridgeIo { } } - pub(crate) fn free_buffer( - protocol: &RefCell>, - pages: NonZeroUsize, - base: NonNull, - ) -> Status { - let borrow = protocol.borrow(); - let raw = &borrow.0; - unsafe { - (raw.free_buffer)(raw, pages.get(), base.as_ptr().cast()) - } - } - /// Map given variable's address into PCI Controller-specific address /// required to access it from a DMA bus master. /// # Arguments @@ -144,11 +138,13 @@ impl PciRootBridgeIo { /// /// # Returns /// - PciMappedRegion capturing lifetime of passed variable + #[cfg(feature = "alloc")] pub fn map<'p, 'r, T>( - protocol: &'p RefCell>, + &'p self, + token: &'p RefCell>, operation: PciRootBridgeIoProtocolOperation, to_map: &'r T, - ) -> PciMappedRegion<'p, 'r> + ) -> PciMappedRegion<'p, 'r, 'id> where 'p: 'r, { @@ -156,12 +152,12 @@ impl PciRootBridgeIo { let mut bytes = size_of_val(to_map); let mut mapped_address = 0u64; let mut mapping: *mut c_void = ptr::null_mut(); + let token_borrow = token.borrow(); + let protocol = self.0.borrow(token_borrow.deref()); let status = unsafe { - let borrow = protocol.borrow(); - let raw = &borrow.0; - (raw.map)( - raw, + (protocol.map)( + protocol, operation, host_address.cast(), ptr::from_mut(&mut bytes), @@ -172,44 +168,41 @@ impl PciRootBridgeIo { match status { Status::SUCCESS => { - PciMappedRegion::new(mapped_address, bytes, mapping, to_map, protocol) + drop(token_borrow); + PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0, token) } _ => unreachable!(), } } - pub(crate) fn unmap( - protocol: &RefCell>, - key: *const c_void, - ) -> Status { - let borrow = protocol.borrow(); - let raw = &borrow.0; - unsafe { - (raw.unmap)(raw, key) - } - } - /// Copies a region in PCI root bridge memory space onto the other. /// Two regions must have same length. Functionally, this is the same as /// `<[T]>::copy_from_slice` which is effectively memcpy. /// And the same safety requirements as the above method apply. + #[cfg(feature = "alloc")] pub fn copy( - protocol: &RefCell>, + &self, + token: &mut GhostToken<'id>, width: PciRootBridgeIoProtocolWidth, destination: PciRegion, source: PciRegion, ) -> crate::Result<()> { assert_eq!(destination.length, source.length); - let mut borrow = protocol.borrow_mut(); + let protocol = self.0.borrow_mut(token); + debug!("Width: {:?}", width); + debug!("Destination Address: 0x{:X}", destination.device_address); + debug!("Destination Length: 0x{:X}", destination.length); + debug!("Source Address: 0x{:X}", source.device_address); + debug!("Source Address: 0x{:X}", source.length); + debug!("Count: 0x{:X}", destination.length / width.size()); let status = unsafe { - let raw = &mut borrow.0; - (raw.copy_mem)( - raw, + (protocol.copy_mem)( + protocol, width, destination.device_address, source.device_address, - destination.length, + destination.length / width.size(), ) }; @@ -223,6 +216,14 @@ impl PciRootBridgeIo { // TODO: configuration / resource settings } +impl<'id> Debug for PciRootBridgeIo<'id> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let mut debug = f.debug_tuple("PciRootBridgeIo"); + debug.field(&"unavailable"); + debug.finish() + } +} + /// Struct for performing PCI I/O operations on a root bridge. #[derive(Debug)] pub struct PciIoAccessPci<'a> { From e31b2135b1bfde6468592689b2a7c33616f4a441 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Mon, 30 Jun 2025 02:31:17 +0900 Subject: [PATCH 06/14] uefi: Fix copy_mem test and implement access for io and memory space --- uefi-test-runner/src/proto/pci/root_bridge.rs | 115 +++++++++------- uefi/src/proto/pci/mod.rs | 6 +- uefi/src/proto/pci/region.rs | 2 +- uefi/src/proto/pci/root_bridge.rs | 129 +++++++++++++----- xtask/src/qemu.rs | 7 + 5 files changed, 175 insertions(+), 84 deletions(-) diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 3cd8f546e..77f0d744a 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use core::cell::RefCell; -use core::mem; use core::ops::DerefMut; +use core::ptr; use ghost_cell::GhostToken; use uefi::boot::{image_handle, OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; use uefi::proto::pci::root_bridge::PciRootBridgeIo; @@ -11,15 +11,15 @@ use uefi::proto::ProtocolPointer; use uefi::Handle; use uefi_raw::protocol::pci::root_bridge::{ PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, - PciRootBridgeIoProtocolWidth, }; -use uefi_raw::table::boot::{MemoryType, PAGE_SIZE}; +use uefi_raw::table::boot::MemoryType; const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4; const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1; const SATA_CTRL_SUBCLASS_CODE: u8 = 0x6; +const DEVICE_IVSHMEM: u16 = 0x1110; -const REG_SIZE: u8 = mem::size_of::() as u8; +const REG_SIZE: u8 = size_of::() as u8; pub fn test_io() { let pci_handles = uefi::boot::find_handles::().unwrap(); @@ -123,7 +123,7 @@ pub fn test_mapping() { &token, PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref(), - ); + ).unwrap(); if mapped.region().device_address == buffer.as_ptr().addr() as u64 { info!("This PCI device uses identity mapping"); } else { @@ -138,52 +138,73 @@ pub fn test_copy() { for pci_handle in pci_handles { GhostToken::new(|token| { - let pci_proto = get_open_protocol::(pci_handle); let token: RefCell<_> = token.into(); + let pci_proto = get_open_protocol::(pci_handle); + for bus in 0..=255 { + for dev in 0..32 { + for fun in 0..8 { + let addr = PciIoAddress::new(bus, dev, fun); + let mut token_mut = token.borrow_mut(); + let pci_access = pci_proto.pci(token_mut.deref_mut()); + let Ok(reg0) = pci_access.read_one::(addr.with_register(0)) else { + continue; + }; + if reg0 == 0xFFFFFFFF { + continue; // not a valid device + } - let mut src = pci_proto.allocate_buffer::<[u32; 4096 / 4]>( - &token, - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ).unwrap(); - assert_eq!(size_of_val(src.as_ref()), size_of::<[u8; PAGE_SIZE]>()); - let src = unsafe { - src.assume_init_mut().fill(0xDEADBEEF); - src.assume_init() - }; - let src_mapped = pci_proto.map( - &token, - PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, - src.as_ref(), - ); + let vendor_id = (reg0 & 0xFFFF) as u16; + let device_id = (reg0 >> 16) as u16; - let dst = pci_proto.allocate_buffer::<[u32; 4096 / 4]>( - &token, - MemoryType::BOOT_SERVICES_DATA, - None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ).unwrap(); - assert_eq!(size_of_val(dst.as_ref()), size_of::<[u8; PAGE_SIZE]>()); - let dst_mapped = pci_proto.map( - &token, - PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, - dst.as_ref(), - ); - - let width = PciRootBridgeIoProtocolWidth::UINT32; - assert_eq!(width.size(), 4); - - pci_proto.copy( - token.borrow_mut().deref_mut(), - width, - dst_mapped.region(), - src_mapped.region(), - ).unwrap(); - drop(dst_mapped); - let dst = unsafe { dst.assume_init() }; + if vendor_id != RED_HAT_PCI_VENDOR_ID { + continue; + } + if device_id != DEVICE_IVSHMEM { + continue; + } + + let header_type: u8 = pci_access.read_one(addr.with_register(0xE)).unwrap(); + assert_eq!(header_type, 0); - assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); + let command_value = pci_access.read_one::(addr.with_register(4)).unwrap(); + pci_access.write_one::(addr.with_register(4), command_value & !0x11).unwrap(); + + let bar2 = pci_access.read_one::(addr.with_register(0x18)).unwrap(); // reads both bar2 and bar3 since it's 64bit + assert_eq!(bar2 & 0b1, 0); + assert_eq!((bar2 & 0b110) >> 1, 2); // make sure it's actually 64bit + + let bar2_value = bar2 & 0xFFFFFFFFFFFFFFF0; + let bar2_size = { + pci_access.write_one(addr.with_register(0x18), u32::MAX).unwrap(); + let value: u32 = pci_access.read_one(addr.with_register(0x18)).unwrap(); + let size = (!value).wrapping_add(1); + pci_access.write_one(addr.with_register(0x18), bar2 as u32).unwrap(); + size + }; + assert!(bar2_size >= 0x1000 * 2); + + pci_access.write_one::(addr.with_register(4), command_value | 0b10).unwrap(); + drop(pci_access); + drop(token_mut); + + let (src, dst) = unsafe { + let src = ptr::slice_from_raw_parts_mut((bar2_value as usize) as *mut u32, 0x1000 / size_of::()).as_mut().unwrap(); + let dst = ptr::slice_from_raw_parts((bar2_value as usize + 0x1000) as *mut u32, 0x1000 / size_of::()).as_ref().unwrap(); + (src, dst) + }; + src.fill(0xDEADBEEF); + + pci_proto.copy::( + token.borrow_mut().deref_mut(), + dst, + src, + ).unwrap(); + + assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); + break; + } + } + } }); } } diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index cb7ca5626..0b2598015 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -99,6 +99,10 @@ impl PciIoUnit for u8 {} impl PciIoUnit for u16 {} impl PciIoUnit for u32 {} impl PciIoUnit for u64 {} +impl PciIoUnit for i8 {} +impl PciIoUnit for i16 {} +impl PciIoUnit for i32 {} +impl PciIoUnit for i64 {} #[allow(dead_code)] enum PciIoMode { @@ -108,7 +112,7 @@ enum PciIoMode { } fn encode_io_mode_and_unit(mode: PciIoMode) -> PciRootBridgeIoProtocolWidth { - match (mode, core::mem::size_of::()) { + match (mode, size_of::()) { (PciIoMode::Normal, 1) => PciRootBridgeIoProtocolWidth::UINT8, (PciIoMode::Normal, 2) => PciRootBridgeIoProtocolWidth::UINT16, (PciIoMode::Normal, 4) => PciRootBridgeIoProtocolWidth::UINT32, diff --git a/uefi/src/proto/pci/region.rs b/uefi/src/proto/pci/region.rs index 7bbcfc5c2..fd3fa6cf7 100644 --- a/uefi/src/proto/pci/region.rs +++ b/uefi/src/proto/pci/region.rs @@ -45,7 +45,7 @@ pub struct PciRegion { } impl<'p, 'r, 'id> PciMappedRegion<'p, 'r, 'id> where 'p: 'r { - pub(crate) fn new( + pub(crate) fn new( device_address: u64, length: usize, key: *const c_void, diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index 1e49043f4..d0de29243 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -5,10 +5,11 @@ use core::cell::RefCell; use core::ffi::c_void; use core::fmt::{Debug, Formatter}; -use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; +use core::marker::PhantomData; +use super::{PciIoUnit, encode_io_mode_and_unit}; use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; -use crate::proto::pci::region::{PciMappedRegion, PciRegion}; +use crate::proto::pci::region::PciMappedRegion; use core::mem::MaybeUninit; use core::num::NonZeroUsize; use core::ops::Deref; @@ -16,9 +17,11 @@ use core::ptr; use core::ptr::NonNull; use ghost_cell::{GhostCell, GhostToken}; use log::debug; +use uefi::proto::pci::PciIoMode; +use uefi::proto::pci::root_bridge::io_access::IoAccessType; use uefi_macros::unsafe_protocol; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; +use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; #[cfg(doc)] @@ -48,13 +51,36 @@ impl<'id> PciRootBridgeIo<'id> { self.0.borrow(token).segment_number } - /// Access PCI I/O operations on this root bridge. - pub fn pci<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a> where 'p: 'a + /// Access PCI operations on this root bridge. + pub fn pci<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a, io_access::Pci> where 'p: 'a { let proto = self.0.borrow_mut(token); PciIoAccessPci { proto, io_access: &mut proto.pci, + _type: PhantomData, + } + } + + /// Access I/O operations on this root bridge. + pub fn io<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a, io_access::Io> where 'p: 'a + { + let proto = self.0.borrow_mut(token); + PciIoAccessPci { + proto, + io_access: &mut proto.io, + _type: PhantomData, + } + } + + /// Access memory operations on this root bridge. + pub fn mem<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a, io_access::Mem> where 'p: 'a + { + let proto = self.0.borrow_mut(token); + PciIoAccessPci { + proto, + io_access: &mut proto.mem, + _type: PhantomData, } } @@ -144,9 +170,8 @@ impl<'id> PciRootBridgeIo<'id> { token: &'p RefCell>, operation: PciRootBridgeIoProtocolOperation, to_map: &'r T, - ) -> PciMappedRegion<'p, 'r, 'id> - where - 'p: 'r, + ) -> crate::Result> + where 'p: 'r, T: ?Sized { let host_address = ptr::from_ref(to_map); let mut bytes = size_of_val(to_map); @@ -169,9 +194,11 @@ impl<'id> PciRootBridgeIo<'id> { match status { Status::SUCCESS => { drop(token_borrow); - PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0, token) + Ok(PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0, token)) + } + e => { + Err(e.into()) } - _ => unreachable!(), } } @@ -179,30 +206,27 @@ impl<'id> PciRootBridgeIo<'id> { /// Two regions must have same length. Functionally, this is the same as /// `<[T]>::copy_from_slice` which is effectively memcpy. /// And the same safety requirements as the above method apply. + /// + /// # Question + /// Should this support other types than just primitives? #[cfg(feature = "alloc")] - pub fn copy( + pub fn copy( &self, token: &mut GhostToken<'id>, - width: PciRootBridgeIoProtocolWidth, - destination: PciRegion, - source: PciRegion, + destination: &[U], + source: &[U], ) -> crate::Result<()> { - assert_eq!(destination.length, source.length); + assert_eq!(destination.len(), source.len()); let protocol = self.0.borrow_mut(token); + let width = encode_io_mode_and_unit::(PciIoMode::Normal); - debug!("Width: {:?}", width); - debug!("Destination Address: 0x{:X}", destination.device_address); - debug!("Destination Length: 0x{:X}", destination.length); - debug!("Source Address: 0x{:X}", source.device_address); - debug!("Source Address: 0x{:X}", source.length); - debug!("Count: 0x{:X}", destination.length / width.size()); let status = unsafe { (protocol.copy_mem)( protocol, width, - destination.device_address, - source.device_address, - destination.length / width.size(), + destination.as_ptr().addr() as u64, + source.as_ptr().addr() as u64, + destination.len(), ) }; @@ -226,12 +250,47 @@ impl<'id> Debug for PciRootBridgeIo<'id> { /// Struct for performing PCI I/O operations on a root bridge. #[derive(Debug)] -pub struct PciIoAccessPci<'a> { +pub struct PciIoAccessPci<'a, T: IoAccessType> { proto: *mut PciRootBridgeIoProtocol, io_access: &'a mut PciRootBridgeIoAccess, + _type: PhantomData, } -impl PciIoAccessPci<'_> { + +/// Defines all 3 PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS types according to UEFI protocol 2.10 +/// Currently there are 3: Mem, Io, Pci +pub mod io_access { + use uefi::proto::pci::PciIoAddress; + + /// One of PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS types that provides PCI configurations space access. + #[derive(Debug)] + pub struct Pci; + impl IoAccessType for Pci { + type Address = PciIoAddress; + } + + /// One of PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS types that provides I/O space access. + #[derive(Debug)] + pub struct Io; + impl IoAccessType for Io { + type Address = u64; + } + + /// One of PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS types that provides memory mapped I/O space access. + #[derive(Debug)] + pub struct Mem; + impl IoAccessType for Mem { + type Address = u64; + } + + /// Defines base trait for all PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS types + pub trait IoAccessType { + /// Specify what to use as address. This is needed as Pci IO Access uses special address format. + type Address: Into; + } +} + +impl PciIoAccessPci<'_, T> { /// Reads a single value of type `U` from the specified PCI address. /// /// # Arguments @@ -243,8 +302,8 @@ impl PciIoAccessPci<'_> { /// # Errors /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. /// - [`Status::OUT_OF_RESOURCES`] The read request could not be completed due to a lack of resources. - pub fn read_one(&self, addr: PciIoAddress) -> crate::Result { - let width_mode = encode_io_mode_and_unit::(super::PciIoMode::Normal); + pub fn read_one(&self, addr: T::Address) -> crate::Result { + let width_mode = encode_io_mode_and_unit::(PciIoMode::Normal); let mut result = U::default(); unsafe { (self.io_access.read)( @@ -267,8 +326,8 @@ impl PciIoAccessPci<'_> { /// # Errors /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. /// - [`Status::OUT_OF_RESOURCES`] The write request could not be completed due to a lack of resources. - pub fn write_one(&self, addr: PciIoAddress, data: U) -> crate::Result<()> { - let width_mode = encode_io_mode_and_unit::(super::PciIoMode::Normal); + pub fn write_one(&self, addr: T::Address, data: U) -> crate::Result<()> { + let width_mode = encode_io_mode_and_unit::(PciIoMode::Normal); unsafe { (self.io_access.write)( self.proto, @@ -290,7 +349,7 @@ impl PciIoAccessPci<'_> { /// # Errors /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. /// - [`Status::OUT_OF_RESOURCES`] The read operation could not be completed due to a lack of resources. - pub fn read(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> { + pub fn read(&self, addr: T::Address, data: &mut [U]) -> crate::Result<()> { let width_mode = encode_io_mode_and_unit::(super::PciIoMode::Normal); unsafe { (self.io_access.read)( @@ -313,7 +372,7 @@ impl PciIoAccessPci<'_> { /// # Errors /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. /// - [`Status::OUT_OF_RESOURCES`] The write operation could not be completed due to a lack of resources. - pub fn write(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> { + pub fn write(&self, addr: T::Address, data: &[U]) -> crate::Result<()> { let width_mode = encode_io_mode_and_unit::(super::PciIoMode::Normal); unsafe { (self.io_access.write)( @@ -339,7 +398,7 @@ impl PciIoAccessPci<'_> { /// - [`Status::OUT_OF_RESOURCES`] The operation could not be completed due to a lack of resources. pub fn fill_write( &self, - addr: PciIoAddress, + addr: T::Address, count: usize, data: U, ) -> crate::Result<()> { @@ -369,7 +428,7 @@ impl PciIoAccessPci<'_> { /// # Errors /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. /// - [`Status::OUT_OF_RESOURCES`] The read operation could not be completed due to a lack of resources. - pub fn fifo_read(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> { + pub fn fifo_read(&self, addr: T::Address, data: &mut [U]) -> crate::Result<()> { let width_mode = encode_io_mode_and_unit::(super::PciIoMode::Fifo); unsafe { (self.io_access.read)( @@ -396,7 +455,7 @@ impl PciIoAccessPci<'_> { /// # Errors /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. /// - [`Status::OUT_OF_RESOURCES`] The write operation could not be completed due to a lack of resources. - pub fn fifo_write(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> { + pub fn fifo_write(&self, addr: T::Address, data: &[U]) -> crate::Result<()> { let width_mode = encode_io_mode_and_unit::(super::PciIoMode::Fifo); unsafe { (self.io_access.write)( diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index b8aed7f05..17d1f704c 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -481,6 +481,13 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> { cmd.arg("-device"); cmd.arg("ide-hd,drive=satadisk0,bus=ide.2,serial=AtaPassThru,model=AtaPassThru"); + // Sixth shared memory device used for testing Pci Root Bridge I/O's Copy function. + // This is to provide PCI device with large enough memory. + cmd.arg("-device"); + cmd.arg("ivshmem-plain,memdev=hostmem,id=hostmem"); + cmd.arg("-object"); + cmd.arg("memory-backend-file,size=1M,share,mem-path=/dev/shm/ivshmem,id=hostmem"); + let qemu_monitor_pipe = Pipe::new(tmp_dir, "qemu-monitor")?; let serial_pipe = Pipe::new(tmp_dir, "serial")?; From 173115f705900b22ba12769eeb7ba8dcad08adc5 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Mon, 30 Jun 2025 02:38:25 +0900 Subject: [PATCH 07/14] uefi: Remove achieved TODO comments --- uefi/src/proto/pci/root_bridge.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index d0de29243..4c8137d02 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -234,8 +234,6 @@ impl<'id> PciRootBridgeIo<'id> { } // TODO: poll I/O - // TODO: mem I/O access - // TODO: io I/O access // TODO: get/set attributes // TODO: configuration / resource settings } From eaee087a12df56762d1498daba1f159b57c6459d Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Mon, 30 Jun 2025 08:27:43 +0900 Subject: [PATCH 08/14] uefi: Remove GhostCell and implement PciRootBridgeIo::configuration --- Cargo.lock | 15 +- Cargo.toml | 2 +- uefi-raw/Cargo.toml | 1 + uefi-raw/src/protocol/pci/mod.rs | 1 + uefi-raw/src/protocol/pci/resource.rs | 99 ++++++ uefi-raw/src/protocol/pci/root_bridge.rs | 45 ++- uefi-test-runner/Cargo.toml | 1 - uefi-test-runner/src/proto/pci/mod.rs | 1 + uefi-test-runner/src/proto/pci/root_bridge.rs | 288 +++++++++--------- uefi/Cargo.toml | 1 - uefi/src/proto/pci/buffer.rs | 59 +--- uefi/src/proto/pci/mod.rs | 1 + uefi/src/proto/pci/region.rs | 52 ++-- uefi/src/proto/pci/resource.rs | 276 +++++++++++++++++ uefi/src/proto/pci/root_bridge.rs | 201 +++++++----- 15 files changed, 708 insertions(+), 335 deletions(-) create mode 100644 uefi-raw/src/protocol/pci/resource.rs create mode 100644 uefi/src/proto/pci/resource.rs diff --git a/Cargo.lock b/Cargo.lock index 513a4f471..8b2e8ec8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,12 +339,6 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] -[[package]] -name = "ghost-cell" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" - [[package]] name = "glob" version = "0.3.2" @@ -800,6 +794,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.6.1" @@ -959,7 +959,6 @@ version = "0.35.0" dependencies = [ "bitflags 2.9.1", "cfg-if", - "ghost-cell", "log", "ptr_meta", "qemu-exit", @@ -985,6 +984,7 @@ name = "uefi-raw" version = "0.11.0" dependencies = [ "bitflags 2.9.1", + "static_assertions", "uguid", ] @@ -999,7 +999,6 @@ dependencies = [ name = "uefi-test-runner" version = "0.2.0" dependencies = [ - "ghost-cell", "log", "qemu-exit", "smoltcp", diff --git a/Cargo.toml b/Cargo.toml index 4aaff49a3..bb2ed350d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ bitflags = "2.0.0" log = { version = "0.4.5", default-features = false } ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] } uguid = "2.2.1" -ghost-cell = "0.2.6" +static_assertions = "1.1.0" [patch.crates-io] uefi = { path = "uefi" } diff --git a/uefi-raw/Cargo.toml b/uefi-raw/Cargo.toml index b77b5530f..4b30ad2f7 100644 --- a/uefi-raw/Cargo.toml +++ b/uefi-raw/Cargo.toml @@ -21,6 +21,7 @@ rust-version = "1.85.1" [dependencies] bitflags.workspace = true uguid.workspace = true +static_assertions.workspace = true [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/uefi-raw/src/protocol/pci/mod.rs b/uefi-raw/src/protocol/pci/mod.rs index 814d95217..4522d72be 100644 --- a/uefi-raw/src/protocol/pci/mod.rs +++ b/uefi-raw/src/protocol/pci/mod.rs @@ -1,3 +1,4 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +pub mod resource; pub mod root_bridge; diff --git a/uefi-raw/src/protocol/pci/resource.rs b/uefi-raw/src/protocol/pci/resource.rs new file mode 100644 index 000000000..083f49331 --- /dev/null +++ b/uefi-raw/src/protocol/pci/resource.rs @@ -0,0 +1,99 @@ +use bitflags::bitflags; +use static_assertions::assert_eq_size; + +/// Descriptor for current PCI root bridge's configuration space. +/// Specification: +/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#qword-address-space-descriptor +#[repr(C, packed)] +#[derive(Debug)] +pub struct QWordAddressSpaceDescriptor { + tag: u8, + descriptor_length: u16, + pub resource_type: ResourceType, + pub flags: GeneralFlags, + pub type_flags: u8, + pub address_granularity: u64, + pub range_min: u64, + pub range_max: u64, // inclusive + pub translation_offset: u64, + pub address_length: u64, +} +assert_eq_size!(QWordAddressSpaceDescriptor, [u8; 0x2E]); + +newtype_enum! { + /// Indicates which type of resource this descriptor describes. + pub enum ResourceType: u8 => { + /// This resource describes range of memory. + MEMORY = 0, + + /// This resource describes range of I/O ports. + IO = 1, + + /// This resource describes range of Bus numbers. + BUS = 2, + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Copy, Clone)] + pub struct GeneralFlags: u8 { + /// Indicates maximum address is fixed. + const MAX_ADDRESS_FIXED = 0b1000; + + /// Indicates minimum address is fixed. + const MIN_ADDRESS_FIXED = 0b0100; + + /// Indicates if this bridge would subtract or positively decode address. + /// 1 This bridge subtractively decodes this address (top level bridges only) + /// 0 This bridge positively decodes this address + const DECODE_TYPE = 0b0010; + } +} + +impl QWordAddressSpaceDescriptor { + /// Verifies if given descriptor is valid according to specification. + /// This also checks if all reserved bit fields which are supposed to be 0 are actually 0. + pub fn verify(&self) { + let tag = self.tag; + if tag != 0x8A { + panic!("Tag value for QWordAddressSpaceDescriptor should be 0x8A, not {}", tag); + } + + let length = self.descriptor_length; + if self.descriptor_length != 0x2B { + panic!("Length value for QWordAddressSpaceDescriptor should be 0x2B, not {}", length); + } + + if self.flags.bits() & 0b11110000 != 0 { + panic!("Reserved bits for GeneralFlags are 1") + } + + let type_flags = self.type_flags; + match self.resource_type { + ResourceType::MEMORY => { + if type_flags & 0b11000000 != 0 { + panic!("Reserved bits for Memory Type Flags are 1"); + } + } + ResourceType::IO => { + if type_flags & 0b11001100 != 0 { + panic!("Reserved bits for IO Type Flags are 1"); + } + } + ResourceType::BUS => { + if type_flags != 0 { + panic!("Bus type flags should be 0, not {}", type_flags); + } + } + ResourceType(3..=191) => panic!("Invalid resource type: {}", self.resource_type.0), + ResourceType(192..) => {} // Hardware defined range + } + + let min = self.range_min; + let max = self.range_max; + if max < min { + panic!("Address range is invalid. Max(0x{:X}) is smaller than Min(0x{:X}).", max, min); + } + } +} diff --git a/uefi-raw/src/protocol/pci/root_bridge.rs b/uefi-raw/src/protocol/pci/root_bridge.rs index 83a74db36..79c8d1902 100644 --- a/uefi-raw/src/protocol/pci/root_bridge.rs +++ b/uefi-raw/src/protocol/pci/root_bridge.rs @@ -42,21 +42,22 @@ bitflags! { /// Describes PCI I/O Protocol Attribute bitflags specified in UEFI specification. ///. https://uefi.org/specs/UEFI/2.10_A/14_Protocols_PCI_Bus_Support.html #[derive(Copy, Clone, Debug, Eq, PartialEq)] + #[repr(transparent)] pub struct PciRootBridgeIoProtocolAttribute: u64 { - const PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO = 0x0001; - const PCI_ATTRIBUTE_ISA_IO = 0x0002; - const PCI_ATTRIBUTE_VGA_PALETTE_IO = 0x0004; - const PCI_ATTRIBUTE_VGA_MEMORY = 0x0008; - const PCI_ATTRIBUTE_VGA_IO = 0x0010; - const PCI_ATTRIBUTE_IDE_PRIMARY_IO = 0x0020; - const PCI_ATTRIBUTE_IDE_SECONDARY_IO = 0x0040; - const PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE = 0x0080; - const PCI_ATTRIBUTE_MEMORY_CACHED = 0x0800; - const PCI_ATTRIBUTE_MEMORY_DISABLE = 0x1000; - const PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE = 0x8000; - const PCI_ATTRIBUTE_ISA_IO_16 = 0x10000; - const PCI_ATTRIBUTE_VGA_PALETTE_IO_16 = 0x20000; - const PCI_ATTRIBUTE_VGA_IO_16 = 0x40000; + const ISA_MOTHERBOARD_IO = 0x0001; + const ISA_IO = 0x0002; + const VGA_PALETTE_IO = 0x0004; + const VGA_MEMORY = 0x0008; + const VGA_IO = 0x0010; + const IDE_PRIMARY_IO = 0x0020; + const IDE_SECONDARY_IO = 0x0040; + const MEMORY_WRITE_COMBINE = 0x0080; + const MEMORY_CACHED = 0x0800; + const MEMORY_DISABLE = 0x1000; + const DUAL_ADDRESS_CYCLE = 0x8000; + const ISA_IO_16 = 0x10000; + const VGA_PALETTE_IO_16 = 0x20000; + const VGA_IO_16 = 0x40000; } } @@ -157,18 +158,10 @@ impl PciRootBridgeIoProtocol { impl PciRootBridgeIoProtocolWidth { pub fn size(self) -> usize { match self { - Self::UINT8 | - Self::FIFO_UINT8 | - Self::FILL_UINT8 => 1, - Self::UINT16 | - Self::FIFO_UINT16 | - Self::FILL_UINT16 => 2, - Self::UINT32 | - Self::FIFO_UINT32 | - Self::FILL_UINT32 => 4, - Self::UINT64 | - Self::FIFO_UINT64 | - Self::FILL_UINT64 => 8, + Self::UINT8 | Self::FIFO_UINT8 | Self::FILL_UINT8 => 1, + Self::UINT16 | Self::FIFO_UINT16 | Self::FILL_UINT16 => 2, + Self::UINT32 | Self::FIFO_UINT32 | Self::FILL_UINT32 => 4, + Self::UINT64 | Self::FIFO_UINT64 | Self::FILL_UINT64 => 8, _ => unreachable!(), } } diff --git a/uefi-test-runner/Cargo.toml b/uefi-test-runner/Cargo.toml index 93b359f00..fe534ac92 100644 --- a/uefi-test-runner/Cargo.toml +++ b/uefi-test-runner/Cargo.toml @@ -9,7 +9,6 @@ edition = "2024" uefi-raw = { path = "../uefi-raw" } uefi = { path = "../uefi", features = ["alloc", "global_allocator", "panic_handler", "logger", "qemu", "log-debugcon"] } smoltcp = { version = "0.12.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "socket-udp"] } -ghost-cell.workspace = true log.workspace = true diff --git a/uefi-test-runner/src/proto/pci/mod.rs b/uefi-test-runner/src/proto/pci/mod.rs index 4b863de3a..05f733c07 100644 --- a/uefi-test-runner/src/proto/pci/mod.rs +++ b/uefi-test-runner/src/proto/pci/mod.rs @@ -7,4 +7,5 @@ pub fn test() { root_bridge::test_buffer(); root_bridge::test_mapping(); root_bridge::test_copy(); + root_bridge::test_config(); } diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 77f0d744a..591fbdb49 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use core::cell::RefCell; -use core::ops::DerefMut; use core::ptr; -use ghost_cell::GhostToken; -use uefi::boot::{image_handle, OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; -use uefi::proto::pci::root_bridge::PciRootBridgeIo; -use uefi::proto::pci::PciIoAddress; +use uefi::{println, Handle}; +use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle}; use uefi::proto::ProtocolPointer; -use uefi::Handle; +use uefi::proto::pci::PciIoAddress; +use uefi::proto::pci::root_bridge::PciRootBridgeIo; use uefi_raw::protocol::pci::root_bridge::{ PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, }; @@ -29,46 +26,44 @@ pub fn test_io() { let mut sata_ctrl_cnt = 0; for pci_handle in pci_handles { - GhostToken::new(|mut token| { - let pci_proto = get_open_protocol::(pci_handle); - for bus in 0..=255 { - for dev in 0..32 { - for fun in 0..8 { - let addr = PciIoAddress::new(bus, dev, fun); - let Ok(reg0) = pci_proto.pci(&mut token).read_one::(addr.with_register(0)) else { - continue; - }; - if reg0 == 0xFFFFFFFF { - continue; // not a valid device - } - let reg1 = pci_proto - .pci(&mut token) - .read_one::(addr.with_register(2 * REG_SIZE)) - .unwrap(); - - let vendor_id = (reg0 & 0xFFFF) as u16; - let device_id = (reg0 >> 16) as u16; - if vendor_id == RED_HAT_PCI_VENDOR_ID { - red_hat_dev_cnt += 1; - } + let pci_proto = get_open_protocol::(pci_handle); + for bus in 0..=255 { + for dev in 0..32 { + for fun in 0..8 { + let addr = PciIoAddress::new(bus, dev, fun); + let Ok(reg0) = pci_proto.pci().read_one::(addr.with_register(0)) else { + continue; + }; + if reg0 == 0xFFFFFFFF { + continue; // not a valid device + } + let reg1 = pci_proto + .pci() + .read_one::(addr.with_register(2 * REG_SIZE)) + .unwrap(); + + let vendor_id = (reg0 & 0xFFFF) as u16; + let device_id = (reg0 >> 16) as u16; + if vendor_id == RED_HAT_PCI_VENDOR_ID { + red_hat_dev_cnt += 1; + } - let class_code = (reg1 >> 24) as u8; - let subclass_code = ((reg1 >> 16) & 0xFF) as u8; - if class_code == MASS_STORAGE_CTRL_CLASS_CODE { - mass_storage_ctrl_cnt += 1; + let class_code = (reg1 >> 24) as u8; + let subclass_code = ((reg1 >> 16) & 0xFF) as u8; + if class_code == MASS_STORAGE_CTRL_CLASS_CODE { + mass_storage_ctrl_cnt += 1; - if subclass_code == SATA_CTRL_SUBCLASS_CODE { - sata_ctrl_cnt += 1; - } + if subclass_code == SATA_CTRL_SUBCLASS_CODE { + sata_ctrl_cnt += 1; } + } - log::info!( + log::info!( "PCI Device: [{bus}, {dev}, {fun}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X}" ); - } } } - }); + } } assert!(red_hat_dev_cnt > 0); @@ -80,23 +75,21 @@ pub fn test_buffer() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - GhostToken::new(|token| { - let pci_proto = get_open_protocol::(pci_handle); - let token: RefCell<_> = token.into(); - let mut buffer = pci_proto.allocate_buffer::<[u8; 4096]>( - &token, + let pci_proto = get_open_protocol::(pci_handle); + let mut buffer = pci_proto + .allocate_buffer::<[u8; 4096]>( MemoryType::BOOT_SERVICES_DATA, None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ).unwrap(); + PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE, + ) + .unwrap(); - let buffer = unsafe { - buffer.assume_init_mut().fill(0); - buffer.assume_init() - }; + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; - assert_eq!(buffer.as_ptr().addr() % 4096, 0); - }); + assert_eq!(buffer.as_ptr().addr() % 4096, 0); } } @@ -104,32 +97,31 @@ pub fn test_mapping() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - GhostToken::new(|token| { - let pci_proto = get_open_protocol::(pci_handle); - let token: RefCell<_> = token.into(); + let pci_proto = get_open_protocol::(pci_handle); - let mut buffer = pci_proto.allocate_buffer::<[u8; 4096]>( - &token, + let mut buffer = pci_proto + .allocate_buffer::<[u8; 4096]>( MemoryType::BOOT_SERVICES_DATA, None, - PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE, - ).unwrap(); - let buffer = unsafe { - buffer.assume_init_mut().fill(0); - buffer.assume_init() - }; - - let mapped = pci_proto.map( - &token, + PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE, + ) + .unwrap(); + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; + + let mapped = pci_proto + .map( PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref(), - ).unwrap(); - if mapped.region().device_address == buffer.as_ptr().addr() as u64 { - info!("This PCI device uses identity mapping"); - } else { - info!("This PCI device uses different mapping from CPU"); - } - }); + ) + .unwrap(); + if mapped.region().device_address == buffer.as_ptr().addr() as u64 { + info!("This PCI device uses identity mapping"); + } else { + info!("This PCI device uses different mapping from CPU"); + } } } @@ -137,75 +129,97 @@ pub fn test_copy() { let pci_handles = uefi::boot::find_handles::().unwrap(); for pci_handle in pci_handles { - GhostToken::new(|token| { - let token: RefCell<_> = token.into(); - let pci_proto = get_open_protocol::(pci_handle); - for bus in 0..=255 { - for dev in 0..32 { - for fun in 0..8 { - let addr = PciIoAddress::new(bus, dev, fun); - let mut token_mut = token.borrow_mut(); - let pci_access = pci_proto.pci(token_mut.deref_mut()); - let Ok(reg0) = pci_access.read_one::(addr.with_register(0)) else { - continue; - }; - if reg0 == 0xFFFFFFFF { - continue; // not a valid device - } + let pci_proto = get_open_protocol::(pci_handle); + for bus in 0..=255 { + for dev in 0..32 { + for fun in 0..8 { + let addr = PciIoAddress::new(bus, dev, fun); + let pci_access = pci_proto.pci(); + let Ok(reg0) = pci_access.read_one::(addr.with_register(0)) else { + continue; + }; + if reg0 == 0xFFFFFFFF { + continue; // not a valid device + } - let vendor_id = (reg0 & 0xFFFF) as u16; - let device_id = (reg0 >> 16) as u16; + let vendor_id = (reg0 & 0xFFFF) as u16; + let device_id = (reg0 >> 16) as u16; - if vendor_id != RED_HAT_PCI_VENDOR_ID { - continue; - } - if device_id != DEVICE_IVSHMEM { - continue; - } - - let header_type: u8 = pci_access.read_one(addr.with_register(0xE)).unwrap(); - assert_eq!(header_type, 0); - - let command_value = pci_access.read_one::(addr.with_register(4)).unwrap(); - pci_access.write_one::(addr.with_register(4), command_value & !0x11).unwrap(); - - let bar2 = pci_access.read_one::(addr.with_register(0x18)).unwrap(); // reads both bar2 and bar3 since it's 64bit - assert_eq!(bar2 & 0b1, 0); - assert_eq!((bar2 & 0b110) >> 1, 2); // make sure it's actually 64bit - - let bar2_value = bar2 & 0xFFFFFFFFFFFFFFF0; - let bar2_size = { - pci_access.write_one(addr.with_register(0x18), u32::MAX).unwrap(); - let value: u32 = pci_access.read_one(addr.with_register(0x18)).unwrap(); - let size = (!value).wrapping_add(1); - pci_access.write_one(addr.with_register(0x18), bar2 as u32).unwrap(); - size - }; - assert!(bar2_size >= 0x1000 * 2); - - pci_access.write_one::(addr.with_register(4), command_value | 0b10).unwrap(); - drop(pci_access); - drop(token_mut); - - let (src, dst) = unsafe { - let src = ptr::slice_from_raw_parts_mut((bar2_value as usize) as *mut u32, 0x1000 / size_of::()).as_mut().unwrap(); - let dst = ptr::slice_from_raw_parts((bar2_value as usize + 0x1000) as *mut u32, 0x1000 / size_of::()).as_ref().unwrap(); - (src, dst) - }; - src.fill(0xDEADBEEF); - - pci_proto.copy::( - token.borrow_mut().deref_mut(), - dst, - src, - ).unwrap(); - - assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); - break; + if vendor_id != RED_HAT_PCI_VENDOR_ID { + continue; } + if device_id != DEVICE_IVSHMEM { + continue; + } + + let header_type: u8 = pci_access.read_one(addr.with_register(0xE)).unwrap(); + assert_eq!(header_type, 0); + + let command_value = pci_access.read_one::(addr.with_register(4)).unwrap(); + pci_access + .write_one::(addr.with_register(4), command_value & !0x11) + .unwrap(); + + let bar2 = pci_access + .read_one::(addr.with_register(0x18)) + .unwrap(); // reads both bar2 and bar3 since it's 64bit + assert_eq!(bar2 & 0b1, 0); + assert_eq!((bar2 & 0b110) >> 1, 2); // make sure it's actually 64bit + + let bar2_value = bar2 & 0xFFFFFFFFFFFFFFF0; + let bar2_size = { + pci_access + .write_one(addr.with_register(0x18), u32::MAX) + .unwrap(); + let value: u32 = pci_access.read_one(addr.with_register(0x18)).unwrap(); + let size = (!value).wrapping_add(1); + pci_access + .write_one(addr.with_register(0x18), bar2 as u32) + .unwrap(); + size + }; + assert!(bar2_size >= 0x1000 * 2); + + pci_access + .write_one::(addr.with_register(4), command_value | 0b10) + .unwrap(); + + let (src, dst) = unsafe { + let src = ptr::slice_from_raw_parts_mut( + (bar2_value as usize) as *mut u32, + 0x1000 / size_of::(), + ) + .as_mut() + .unwrap(); + let dst = ptr::slice_from_raw_parts( + (bar2_value as usize + 0x1000) as *mut u32, + 0x1000 / size_of::(), + ) + .as_ref() + .unwrap(); + (src, dst) + }; + src.fill(0xDEADBEEF); + + pci_proto.copy::(dst, src).unwrap(); + + assert!(dst.iter().all(|&b| b == 0xDEADBEEF)); + break; } } - }); + } + } +} + +pub fn test_config() { + let pci_handles = uefi::boot::find_handles::().unwrap(); + + for pci_handle in pci_handles { + let pci_proto = get_open_protocol::(pci_handle); + let Ok(configuration) = pci_proto.configuration() else { + continue; + }; + info!("Found {} configurations", configuration.len()); } } diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index a67866b1a..1048c1ab4 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -45,7 +45,6 @@ ucs2 = "0.3.3" uefi-macros = "0.18.1" uefi-raw = "0.11.0" qemu-exit = { version = "3.0.2", optional = true } -ghost-cell.workspace = true [package.metadata.docs.rs] all-features = true diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs index 831858e39..5b043e95c 100644 --- a/uefi/src/proto/pci/buffer.rs +++ b/uefi/src/proto/pci/buffer.rs @@ -2,27 +2,24 @@ //! Defines wrapper allocated by PCI Root Bridge protocol. -use core::cell::RefCell; -use core::fmt::{Debug, Formatter}; +use core::fmt::Debug; use core::mem::{ManuallyDrop, MaybeUninit}; use core::num::NonZeroUsize; use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; -use ghost_cell::{GhostCell, GhostToken}; use log::debug; -use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; use uefi_raw::Status; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; /// Smart pointer for wrapping owned buffer allocated by PCI Root Bridge protocol. -pub struct PciBuffer<'b, 'id, T> { +#[derive(Debug)] +pub struct PciBuffer<'p, T> { base: NonNull, pages: NonZeroUsize, - proto: &'b GhostCell<'id, PciRootBridgeIoProtocol>, - token: &'b RefCell> + proto: &'p PciRootBridgeIoProtocol, } -impl<'b, 'id, T> PciBuffer<'b, 'id, MaybeUninit> { - +impl<'p, T> PciBuffer<'p, MaybeUninit> { /// Creates wrapper for buffer allocated by PCI Root Bridge protocol. /// Passed protocol is stored as a pointer along with its lifetime so that it doesn't /// block others from using its mutable functions. @@ -30,15 +27,9 @@ impl<'b, 'id, T> PciBuffer<'b, 'id, MaybeUninit> { pub const fn new( base: NonNull>, pages: NonZeroUsize, - proto: &'b GhostCell<'id, PciRootBridgeIoProtocol>, - token: &'b RefCell> + proto: &'p PciRootBridgeIoProtocol, ) -> Self { - Self { - base, - pages, - proto, - token, - } + Self { base, pages, proto } } /// Assumes the contents of this buffer have been initialized. @@ -46,30 +37,29 @@ impl<'b, 'id, T> PciBuffer<'b, 'id, MaybeUninit> { /// # Safety /// Callers of this function must guarantee that value stored is valid. #[must_use] - pub unsafe fn assume_init(self) -> PciBuffer<'b, 'id, T> { + pub unsafe fn assume_init(self) -> PciBuffer<'p, T> { let old = ManuallyDrop::new(self); PciBuffer { base: old.base.cast(), pages: old.pages, proto: old.proto, - token: old.token, } } } -impl<'b, 'id, T> AsRef for PciBuffer<'b, 'id, T> { +impl<'p, T> AsRef for PciBuffer<'p, T> { fn as_ref(&self) -> &T { unsafe { self.base.as_ref() } } } -impl<'b, 'id, T> AsMut for PciBuffer<'b, 'id, T> { +impl<'p, T> AsMut for PciBuffer<'p, T> { fn as_mut(&mut self) -> &mut T { unsafe { self.base.as_mut() } } } -impl<'b, 'id, T> Deref for PciBuffer<'b, 'id, T> { +impl<'p, T> Deref for PciBuffer<'p, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -77,18 +67,16 @@ impl<'b, 'id, T> Deref for PciBuffer<'b, 'id, T> { } } -impl<'b, 'id, T> DerefMut for PciBuffer<'b, 'id, T> { +impl<'p, T> DerefMut for PciBuffer<'p, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut() } } -impl<'b, 'id, T> Drop for PciBuffer<'b, 'id, T> { +impl<'p, T> Drop for PciBuffer<'p, T> { fn drop(&mut self) { - let token = self.token.borrow(); - let protocol = self.proto.borrow(token.deref()); let status = unsafe { - (protocol.free_buffer)(protocol, self.pages.get(), self.base.as_ptr().cast()) + (self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast()) }; match status { Status::SUCCESS => { @@ -105,20 +93,3 @@ impl<'b, 'id, T> Drop for PciBuffer<'b, 'id, T> { } } } - -impl<'b, 'id, T> Debug for PciBuffer<'b, 'id, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let mut debug = f.debug_struct("PciBuffer"); - debug.field("base", &self.base); - debug.field("pages", &self.pages); - - if let Ok(token) = self.token.try_borrow() { - let protocol = self.proto.borrow(token.deref()); - debug.field("proto", protocol); - } else { - debug.field("proto", &"unavailable"); - }; - - debug.finish() - } -} \ No newline at end of file diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index 0b2598015..8a0a3b3ea 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -8,6 +8,7 @@ use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth; pub mod buffer; pub mod region; +pub mod resource; pub mod root_bridge; /// IO Address for PCI/register IO operations diff --git a/uefi/src/proto/pci/region.rs b/uefi/src/proto/pci/region.rs index fd3fa6cf7..e4cfa3f16 100644 --- a/uefi/src/proto/pci/region.rs +++ b/uefi/src/proto/pci/region.rs @@ -2,15 +2,12 @@ //! Defines wrapper for region mapped by PCI Root Bridge I/O protocol. -use core::cell::RefCell; use core::ffi::c_void; -use core::fmt::{Debug, Formatter}; +use core::fmt::Debug; use core::marker::PhantomData; -use core::ops::Deref; -use ghost_cell::{GhostCell, GhostToken}; use log::debug; -use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; use uefi_raw::Status; +use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol; /// Represents a region of memory mapped by PCI Root Bridge I/O protocol. /// The region will be unmapped automatically when it is dropped. @@ -20,15 +17,15 @@ use uefi_raw::Status; /// `'r` is the lifetime for Mapped Region. /// Protocol must outlive the mapped region /// as unmap function can only be accessed through the protocol. -pub struct PciMappedRegion<'p, 'r, 'id> +#[derive(Debug)] +pub struct PciMappedRegion<'p, 'r> where 'p: 'r, { region: PciRegion, _lifetime_holder: PhantomData<&'r ()>, key: *const c_void, - proto: &'p GhostCell<'id, PciRootBridgeIoProtocol>, - token: &'p RefCell>, + proto: &'p PciRootBridgeIoProtocol, } /// Represents a region of memory in PCI root bridge memory space. @@ -41,17 +38,19 @@ pub struct PciRegion { pub device_address: u64, /// Byte length of the memory region. - pub length: usize + pub length: usize, } -impl<'p, 'r, 'id> PciMappedRegion<'p, 'r, 'id> where 'p: 'r { +impl<'p, 'r> PciMappedRegion<'p, 'r> +where + 'p: 'r, +{ pub(crate) fn new( device_address: u64, length: usize, key: *const c_void, _to_map: &'r T, - proto: &'p GhostCell<'id, PciRootBridgeIoProtocol>, - token: &'p RefCell>, + proto: &'p PciRootBridgeIoProtocol, ) -> Self { let end = device_address + length as u64; debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end); @@ -63,7 +62,6 @@ impl<'p, 'r, 'id> PciMappedRegion<'p, 'r, 'id> where 'p: 'r { _lifetime_holder: PhantomData, key, proto, - token, } } @@ -78,17 +76,16 @@ impl<'p, 'r, 'id> PciMappedRegion<'p, 'r, 'id> where 'p: 'r { } } -impl<'p, 'r, 'id> Drop for PciMappedRegion<'p, 'r, 'id> { +impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { fn drop(&mut self) { - let token = self.token.borrow(); - let protocol = self.proto.borrow(token.deref()); - let status = unsafe { - (protocol.unmap)(protocol, self.key) - }; + let status = unsafe { (self.proto.unmap)(self.proto, self.key) }; match status { Status::SUCCESS => { let end = self.region.device_address + self.region.length as u64; - debug!("Region [0x{:X}..0x{:X}] was unmapped", self.region.device_address, end); + debug!( + "Region [0x{:X}..0x{:X}] was unmapped", + self.region.device_address, end + ); } Status::INVALID_PARAMETER => { panic!("This region was not mapped using PciRootBridgeIo::map"); @@ -101,21 +98,6 @@ impl<'p, 'r, 'id> Drop for PciMappedRegion<'p, 'r, 'id> { } } -impl<'p, 'r, 'id> Debug for PciMappedRegion<'p, 'r, 'id> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let mut debug = f.debug_struct("PciMappedRegion"); - debug.field("region", &self.region); - debug.field("key", &self.key); - - if let Ok(token) = self.token.try_borrow() { - debug.field("proto", self.proto.borrow(token.deref())); - } else { - debug.field("proto", &"unavailable"); - } - debug.finish() - } -} - impl PciRegion { /// Creates a new region of memory with different length. /// The new region must have shorter length to ensure diff --git a/uefi/src/proto/pci/resource.rs b/uefi/src/proto/pci/resource.rs new file mode 100644 index 000000000..9acd6cb33 --- /dev/null +++ b/uefi/src/proto/pci/resource.rs @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Defines additional features for [`QWordAddressSpaceDescriptor`]. + +use core::ops::RangeInclusive; +use uefi_raw::protocol::pci::resource::{QWordAddressSpaceDescriptor, ResourceType}; + +/// Describes resource type specific flags. +/// ACPI Specification: +/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#type-specific-attributes +#[derive(Debug)] +pub enum TypeFlag { + /// Flags for Memory type resource. + Memory(MemoryFlag), + /// Flags for Io type resource. + Io(IoFlags), + /// Flags for Bus type resource. + Bus(BusFlags), +} + +/// Flags for Memory type resource. +/// ACPI Specification: +/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#memory-resource-flag-resource-type-0-definitions +#[derive(Debug)] +pub struct MemoryFlag { + /// Specifies if this resource is I/O on primary side of the bridge. + /// [`TranslationType::TRANSLATION]` means it's memory on secondary bridge, I/O on primary bridge. + /// [`TranslationType::STATIC]` means it's memory on both primary and secondary bridge. + pub translation_type: TranslationType, + + /// Specifies properties of address range from this resource. + /// It's only defined when this resource describes system RAM. + pub mtp_attribute: MtpAttribute, + + /// Specifies cache properties of this resource + /// Note: OSPM ignores this field in the Extended address space descriptor. + /// Instead, it uses the Type Specific Attributes field to determine memory attributes. + pub mem_attribute: MemAttribute, + + /// Specifies write-ability of this resource. + pub write_status: WriteStatus, +} + +/// Flags for Io type resource. +/// ACP Specification: +/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#io-resource-flag-resource-type-1-definitions +#[derive(Debug)] +pub struct IoFlags { + /// Specifies sparsity of address translation. + /// It's only meaningful when translation_type below is [`TranslationType::TRANSLATION`]. + pub translation_sparsity: TranslationSparsity, + + /// Specifies if this resource is I/O on primary side of the bridge. + /// [`TranslationType::TRANSLATION]` means it's I/O on secondary bridge, memory on primary bridge. + /// [`TranslationType::STATIC]` means it's I/O on both secondary and primary bridge. + pub translation_type: TranslationType, + + /// Specifies window range of Rng. + pub rng_range: RngRange, +} + +/// Flags for Bus type resource. +/// Currently, it's unused and all bits should be 0. +/// ACPI Specification: +/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#bus-number-range-resource-flag-resource-type-2-definitions +#[derive(Debug)] +pub struct BusFlags { + _reserved: u8, +} + +newtype_enum! { + /// Defines translation type flag. + pub enum TranslationType: u8 => { + /// Type of this resource is different on primary side of the bridge. + TRANSLATION = 1, + + /// Type of this resource is same on primary side of the bridge. + STATIC = 0, + } +} + +newtype_enum! { + /// Defines memory range attribute flag. + pub enum MtpAttribute: u8 => { + /// This range is available RAM usable by the operating system. + MEMORY = 0x0, + + /// This range of addresses is in use or reserved by the system + /// and is not to be included in the allocatable memory pool + /// of the operating system’s memory manager. + RESERVED = 0x1, + + /// ACPI Reclaim Memory. + /// This range is available RAM usable by the OS after it reads the ACPI tables. + ACPI = 0x2, + + /// ACPI NVS Memory. + /// This range of addresses is in use or reserved by the system + /// and must not be used by the operating system. + /// This range is required to be saved and restored across an NVS sleep. + NVS = 0x3, + } +} + +newtype_enum! { + /// Defines memory cache attribute flag. + pub enum MemAttribute: u8 => { + /// Memory is non-cacheable. + NON_CACHEABLE = 0x0, + + /// Memory is cacheable. + CACHEABLE = 0x1, + + /// Memory is cacheable and supports write combining. + WRITE_COMBINE = 0x2, + + /// The memory is cacheable and prefetchable. + PREFETCH = 0x3, + } +} + +newtype_enum! { + /// Defines write status flag. + pub enum WriteStatus: u8 => { + /// This memory range is read-write. + READ_WRITE = 1, + + /// This memory range is read-only. + READ_ONLY = 0, + } +} + +newtype_enum! { + /// Defines address translation sparsity flag. + pub enum TranslationSparsity: u8 => { + /// The primary-side memory address of any specific I/O port within + /// the secondary-side range can be found using the following function. + /// address = (((port & 0xFFFc) << 10) || (port & 0xFFF)) + [`QWordAddressSpaceDescriptor#translation_offset`] + /// In the address used to access the I/O port, bits[11:2] must be identical to bits[21:12], + /// this gives four bytes of I/O ports on each 4 KB page. + SPARSE = 1, + + /// The primary-side memory address of any specific I/O port within + /// the secondary-side range can be found using the following function. + /// address = port + [`QWordAddressSpaceDescriptor#translation_offset`] + DENSE = 0, + } +} + +newtype_enum! { + /// Defines rng window range flag. + pub enum RngRange: u8 => { + /// Memory window covers the entire range + ALL = 3, + + /// ISARangesOnly. + /// This flag is for bridges on systems with multiple bridges. + /// Setting this bit means the memory window specified in this descriptor is + /// limited to the ISA I/O addresses that fall within the specified window. + /// The ISA I/O ranges are: n000-n0FF, n400-n4FF, n800-n8FF, nC00-nCFF. + /// This bit can only be set for bridges entirely configured throughACPI namespace. + ISA_ONLY = 2, + + /// NonISARangesOnly. + /// This flag is for bridges on systems with multiple bridges. + /// Setting this bit means the memory window specified in this descriptor is + /// limited to the non-ISA I/O addresses that fall within the specified window. + /// The non-ISA I/O ranges are: n100-n3FF, n500-n7FF, n900-nBFF, nD00-nFFF. + /// This bit can only be set for bridges entirely configured through ACPI namespace. + NON_ISA_ONLY = 1, + } +} + +/// Extension trait for [`QWordAddressSpaceDescriptor`]. +pub trait QWordAddressSpaceDescriptorExt { + /// Returns type-specific flags of this descriptor + fn type_flags(&self) -> TypeFlag { + match self.descriptor().resource_type { + ResourceType::MEMORY => TypeFlag::Memory(MemoryFlag::new(self.descriptor().type_flags)), + ResourceType::IO => TypeFlag::Io(IoFlags::new(self.descriptor().type_flags)), + ResourceType::BUS => TypeFlag::Bus(BusFlags::new(self.descriptor().type_flags)), + _ => unreachable!(), + } + } + + /// Returns if this descriptor is currently 64 bit or 32 bit. + /// + /// # Returns + /// None: It's unspecified + /// true: This descriptor is 64 bit + /// false: This descriptor is 32 bit + fn is_64bit(&self) -> Option { + let granularity = self.descriptor().address_granularity; + match granularity { + 32 => Some(false), + 64 => Some(true), + _ => None, + } + } + + /// Returns address range of this descriptor. + fn address_range(&self) -> RangeInclusive { + let descriptor = self.descriptor(); + let offset_min = descriptor.range_min + descriptor.translation_offset; + let offset_max = descriptor.range_max + descriptor.translation_offset; + let length = descriptor.address_length; + let range = offset_min..=offset_max; + debug_assert_eq!(range.clone().count() as u64, length); + range + } + + #[allow(missing_docs)] + fn descriptor(&self) -> &QWordAddressSpaceDescriptor; +} + +impl QWordAddressSpaceDescriptorExt for QWordAddressSpaceDescriptor { + fn descriptor(&self) -> &QWordAddressSpaceDescriptor { + self + } +} + +impl MemoryFlag { + /// Constructs new [`MemoryFlag`] from raw byte. + /// + /// # Panic + /// Panics when reserved bits are not 0. + pub fn new(flags: u8) -> Self { + let write_status = WriteStatus(flags & 0b1); + let mem_attribute = MemAttribute((flags >> 1) & 0b11); + let mtp_attribute = MtpAttribute((flags >> 3) & 0b11); + let translation_type = TranslationType((flags >> 5) & 0b1); + assert_eq!((flags >> 6) & 0b11, 0b00); + + Self { + translation_type, + mtp_attribute, + mem_attribute, + write_status, + } + } +} + +impl IoFlags { + /// Constructs new [`IoFlags`] from raw byte. + /// + /// # Panic + /// Panics when reserved bits are not 0. + pub fn new(flags: u8) -> Self { + assert_ne!(flags & 0b11, 0); + let rng_range = RngRange(flags & 0b11); + + let translation_type = TranslationType((flags >> 4) & 0b1); + let translation_sparsity = TranslationSparsity((flags >> 5) & 0b1); + + assert_eq!((flags >> 2) & 0b11, 0b00); + assert_eq!((flags >> 6) & 0b11, 0b00); + + Self { + translation_sparsity, + translation_type, + rng_range, + } + } +} + +impl BusFlags { + /// Constructs new [`BusFlags`] from raw byte. + /// The byte must be 0. + /// + /// # Panic + /// Panics when byte is not 0. + pub fn new(flags: u8) -> Self { + assert_eq!(flags, 0); + Self { _reserved: 0 } + } +} diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index 4c8137d02..b2b892b8d 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -2,26 +2,27 @@ //! PCI Root Bridge protocol. -use core::cell::RefCell; -use core::ffi::c_void; -use core::fmt::{Debug, Formatter}; -use core::marker::PhantomData; use super::{PciIoUnit, encode_io_mode_and_unit}; use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; use crate::proto::pci::region::PciMappedRegion; +use core::ffi::c_void; +use core::fmt::Debug; +use core::marker::PhantomData; use core::mem::MaybeUninit; use core::num::NonZeroUsize; -use core::ops::Deref; use core::ptr; -use core::ptr::NonNull; -use ghost_cell::{GhostCell, GhostToken}; +use core::ptr::{NonNull, slice_from_raw_parts}; use log::debug; use uefi::proto::pci::PciIoMode; use uefi::proto::pci::root_bridge::io_access::IoAccessType; use uefi_macros::unsafe_protocol; +use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation}; +use uefi_raw::protocol::pci::root_bridge::{ + PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, + PciRootBridgeIoProtocolOperation, +}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; #[cfg(doc)] @@ -32,54 +33,41 @@ use crate::Status; /// # UEFI Spec Description /// Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are /// used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller. -/// -/// # RefCell Usage -/// Types such as [`PciMappedRegion`] holds references to this protocol -/// so that it can free themselves in drop. However it prevents others calling mutable -/// method of this protocol. To avoid this we -/// -/// TODO Find a way to hide above implementation detail. Even if we still use -/// RefCell, we should use it internally instead of requiring users to do so themselves. #[repr(transparent)] #[unsafe_protocol(PciRootBridgeIoProtocol::GUID)] -pub struct PciRootBridgeIo<'id>(GhostCell<'id, PciRootBridgeIoProtocol>); +#[derive(Debug)] +pub struct PciRootBridgeIo(PciRootBridgeIoProtocol); -impl<'id> PciRootBridgeIo<'id> { +impl PciRootBridgeIo { /// Get the segment number where this PCI root bridge resides. #[must_use] - pub fn segment_nr(&self, token: &GhostToken<'id>) -> u32 { - self.0.borrow(token).segment_number + pub fn segment_nr(&self) -> u32 { + self.0.segment_number } /// Access PCI operations on this root bridge. - pub fn pci<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a, io_access::Pci> where 'p: 'a - { - let proto = self.0.borrow_mut(token); + pub fn pci(&self) -> PciIoAccessPci<'_, io_access::Pci> { PciIoAccessPci { - proto, - io_access: &mut proto.pci, + proto: ptr::from_ref(&self.0).cast_mut(), + io_access: &self.0.pci, _type: PhantomData, } } /// Access I/O operations on this root bridge. - pub fn io<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a, io_access::Io> where 'p: 'a - { - let proto = self.0.borrow_mut(token); + pub fn io(&self) -> PciIoAccessPci<'_, io_access::Io> { PciIoAccessPci { - proto, - io_access: &mut proto.io, + proto: ptr::from_ref(&self.0).cast_mut(), + io_access: &self.0.io, _type: PhantomData, } } /// Access memory operations on this root bridge. - pub fn mem<'p, 'a>(&'p self, token: &'a mut GhostToken<'id>) -> PciIoAccessPci<'a, io_access::Mem> where 'p: 'a - { - let proto = self.0.borrow_mut(token); + pub fn mem(&self) -> PciIoAccessPci<'_, io_access::Mem> { PciIoAccessPci { - proto, - io_access: &mut proto.mem, + proto: ptr::from_ref(&self.0).cast_mut(), + io_access: &self.0.mem, _type: PhantomData, } } @@ -89,9 +77,9 @@ impl<'id> PciRootBridgeIo<'id> { /// # Errors /// - [`Status::DEVICE_ERROR`] The PCI posted write transactions were not flushed from the PCI host bridge /// due to a hardware error. - pub fn flush(&self, token: &mut GhostToken<'id>) -> crate::Result<()> { - let proto = self.0.borrow_mut(token); - unsafe { (proto.flush)(proto).to_result() } + pub fn flush(&self) -> crate::Result<()> { + let this = (&self.0) as *const PciRootBridgeIoProtocol; + unsafe { (self.0.flush)(this.cast_mut()).to_result() } } /// Allocates pages suitable for communicating with PCI devices. @@ -104,13 +92,12 @@ impl<'id> PciRootBridgeIo<'id> { /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. #[cfg(feature = "alloc")] - pub fn allocate_buffer<'p, 'b, T>( - &'p self, - token: &'b RefCell>, + pub fn allocate_buffer( + &self, memory_type: MemoryType, pages: Option, attributes: PciRootBridgeIoProtocolAttribute, - ) -> crate::Result>> where 'p: 'b { + ) -> crate::Result>> { let mut address = 0usize; let original_alignment = align_of::(); assert_ne!(original_alignment, 0); @@ -128,11 +115,9 @@ impl<'id> PciRootBridgeIo<'id> { NonZeroUsize::new(size.div_ceil(alignment)).unwrap() }; - let token_borrow = token.borrow(); - let protocol = self.0.borrow(token_borrow.deref()); let status = unsafe { - (protocol.allocate_buffer)( - protocol, + (self.0.allocate_buffer)( + &self.0, AllocateType(0), memory_type, pages.get(), @@ -143,10 +128,9 @@ impl<'id> PciRootBridgeIo<'id> { match status { Status::SUCCESS => { - drop(token_borrow); let base = NonNull::new(address as *mut MaybeUninit).unwrap(); debug!("Allocated {} pages at 0x{:X}", pages.get(), address); - Ok(PciBuffer::new(base, pages, &self.0, token)) + Ok(PciBuffer::new(base, pages, &self.0)) } error @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { @@ -167,22 +151,21 @@ impl<'id> PciRootBridgeIo<'id> { #[cfg(feature = "alloc")] pub fn map<'p, 'r, T>( &'p self, - token: &'p RefCell>, operation: PciRootBridgeIoProtocolOperation, to_map: &'r T, - ) -> crate::Result> - where 'p: 'r, T: ?Sized + ) -> crate::Result> + where + 'p: 'r, + T: ?Sized, { let host_address = ptr::from_ref(to_map); let mut bytes = size_of_val(to_map); let mut mapped_address = 0u64; let mut mapping: *mut c_void = ptr::null_mut(); - let token_borrow = token.borrow(); - let protocol = self.0.borrow(token_borrow.deref()); let status = unsafe { - (protocol.map)( - protocol, + (self.0.map)( + &self.0, operation, host_address.cast(), ptr::from_mut(&mut bytes), @@ -192,13 +175,14 @@ impl<'id> PciRootBridgeIo<'id> { }; match status { - Status::SUCCESS => { - drop(token_borrow); - Ok(PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0, token)) - } - e => { - Err(e.into()) - } + Status::SUCCESS => Ok(PciMappedRegion::new( + mapped_address, + bytes, + mapping, + to_map, + &self.0, + )), + e => Err(e.into()), } } @@ -207,22 +191,21 @@ impl<'id> PciRootBridgeIo<'id> { /// `<[T]>::copy_from_slice` which is effectively memcpy. /// And the same safety requirements as the above method apply. /// + /// # Returns + /// [`Ok`] on successful copy. + /// [`Err`] otherwise. + /// * [`Status::INVALID_PARAMETER`]: Width is invalid for this PCI root bridge. + /// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. /// # Question /// Should this support other types than just primitives? #[cfg(feature = "alloc")] - pub fn copy( - &self, - token: &mut GhostToken<'id>, - destination: &[U], - source: &[U], - ) -> crate::Result<()> { + pub fn copy(&self, destination: &[U], source: &[U]) -> crate::Result<()> { assert_eq!(destination.len(), source.len()); - let protocol = self.0.borrow_mut(token); let width = encode_io_mode_and_unit::(PciIoMode::Normal); let status = unsafe { - (protocol.copy_mem)( - protocol, + (self.0.copy_mem)( + ((&self.0) as *const PciRootBridgeIoProtocol).cast_mut(), width, destination.as_ptr().addr() as u64, source.as_ptr().addr() as u64, @@ -233,28 +216,82 @@ impl<'id> PciRootBridgeIo<'id> { status.to_result() } + /// Retrieves the current resource settings of this PCI root bridge + /// in the form of a set of ACPI resource descriptors. + /// + /// # Returns + /// [`Ok`] when it successfully retrieved current configuration. + /// [`Err`] when it failed to retrieve current configuration. + /// * Status value will be [`Status::UNSUPPORTED`] + /// + /// # Panic + /// It may panic if pci devices or drivers for those provided by boot service misbehave. + /// There are multiple verifications put in place, and they will panic if invariants + /// are broken, such as when invalid enum variant value was received + /// or reserved bits are not 0 + pub fn configuration(&self) -> crate::Result<&'static [QWordAddressSpaceDescriptor]> { + let mut configuration_address = 0u64; + let configuration_status = unsafe { + (self.0.configuration)( + &self.0, + ((&mut configuration_address) as *mut u64).cast::<*const c_void>(), + ) + }; + match configuration_status { + Status::SUCCESS => { + let head = configuration_address as *const QWordAddressSpaceDescriptor; + let mut count = 0; + + unsafe { + loop { + let cursor = head.add(count); + match cursor.cast::().read() { + 0x8A => { + let cursor_ref = cursor.as_ref().unwrap(); + cursor_ref.verify(); + count += 1; + if count >= 1024 { + panic!("Timed out while fetching configurations:\ + There are more than 1024 configuration spaces"); + } + } + 0x79 => { + let checksum_ptr = cursor.cast::().byte_add(1); + if checksum_ptr.read() != 0 { + panic!( + "Checksum failed for QWordAddressSpaceDescriptor list starting at 0x{:X} with size {}", + configuration_address, count + ); + } + break; + } + _ => panic!( + "Invalid Tag value for entry in QWordAddressSpaceDescriptor list starting at 0x{:X} with index {}", + configuration_address, count + ), + } + } + }; + let list: &[QWordAddressSpaceDescriptor] = + unsafe { slice_from_raw_parts(head, count).as_ref().unwrap() }; + Ok(list) + } + e => Err(e.into()), + } + } + // TODO: poll I/O // TODO: get/set attributes - // TODO: configuration / resource settings -} - -impl<'id> Debug for PciRootBridgeIo<'id> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let mut debug = f.debug_tuple("PciRootBridgeIo"); - debug.field(&"unavailable"); - debug.finish() - } } /// Struct for performing PCI I/O operations on a root bridge. #[derive(Debug)] pub struct PciIoAccessPci<'a, T: IoAccessType> { proto: *mut PciRootBridgeIoProtocol, - io_access: &'a mut PciRootBridgeIoAccess, + io_access: &'a PciRootBridgeIoAccess, _type: PhantomData, } - /// Defines all 3 PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS types according to UEFI protocol 2.10 /// Currently there are 3: Mem, Io, Pci pub mod io_access { From cec0f5029a8e026a8ff5eea36be1bda1683f5674 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Mon, 30 Jun 2025 08:58:21 +0900 Subject: [PATCH 09/14] uefi: Implement polling for both IO and memory --- uefi/src/proto/pci/mod.rs | 6 +-- uefi/src/proto/pci/resource.rs | 6 +-- uefi/src/proto/pci/root_bridge.rs | 81 +++++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index 8a0a3b3ea..d3d025f85 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -95,15 +95,11 @@ impl Ord for PciIoAddress { /// Trait implemented by all data types that can natively be read from a PCI device. /// Note: Not all of them have to actually be supported by the hardware at hand. -pub trait PciIoUnit: Sized + Default {} +pub trait PciIoUnit: Sized + Default + Into {} impl PciIoUnit for u8 {} impl PciIoUnit for u16 {} impl PciIoUnit for u32 {} impl PciIoUnit for u64 {} -impl PciIoUnit for i8 {} -impl PciIoUnit for i16 {} -impl PciIoUnit for i32 {} -impl PciIoUnit for i64 {} #[allow(dead_code)] enum PciIoMode { diff --git a/uefi/src/proto/pci/resource.rs b/uefi/src/proto/pci/resource.rs index 9acd6cb33..7b1dd99ec 100644 --- a/uefi/src/proto/pci/resource.rs +++ b/uefi/src/proto/pci/resource.rs @@ -186,9 +186,9 @@ pub trait QWordAddressSpaceDescriptorExt { /// Returns if this descriptor is currently 64 bit or 32 bit. /// /// # Returns - /// None: It's unspecified - /// true: This descriptor is 64 bit - /// false: This descriptor is 32 bit + /// [`None`]: It's unspecified + /// [`true`]: This descriptor is 64 bit + /// [`false`]: This descriptor is 32 bit fn is_64bit(&self) -> Option { let granularity = self.descriptor().address_granularity; match granularity { diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index b2b892b8d..f7dd1408b 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -13,16 +13,14 @@ use core::mem::MaybeUninit; use core::num::NonZeroUsize; use core::ptr; use core::ptr::{NonNull, slice_from_raw_parts}; +use core::time::Duration; use log::debug; use uefi::proto::pci::PciIoMode; use uefi::proto::pci::root_bridge::io_access::IoAccessType; use uefi_macros::unsafe_protocol; use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::{ - PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, - PciRootBridgeIoProtocolOperation, -}; +use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; #[cfg(doc)] @@ -280,7 +278,80 @@ impl PciRootBridgeIo { } } - // TODO: poll I/O + /// Polls a same memory location until criteria is met. + /// The criteria in question is met when value read from provided reference + /// equals to provided value when masked: + /// `(*to_poll) & mask == value` + /// /// Refer to [`Self::poll_io`] for polling io port instead. + /// + /// # Returns + /// [`Ok`]: Criteria was met before timeout. + /// [`Err`]: One of below error happened: + /// * [`Status::TIMEOUT`]: Delay expired before a match occurred. + /// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. + /// + /// # Panic + /// Panics when delay is too large (longer than 58494 years). + pub fn poll_mem(&self, to_poll: &U, mask: U, value: U, delay: Duration) -> crate::Result<(), U> { + let mut result = U::default(); + let delay = delay.as_nanos().div_ceil(100).try_into().unwrap(); + let status = unsafe { + (self.0.poll_mem)( + ptr::from_ref(&self.0).cast_mut(), + encode_io_mode_and_unit::(PciIoMode::Normal), + ptr::from_ref(to_poll).addr() as u64, + mask.into(), + value, + delay, + &mut result + ) + }; + + match status { + Status::SUCCESS => { + Ok(()) + } + e => Err(e.into()), + } + } + + /// Polls a same io port until criteria is met. + /// The criteria in question is met when value read from provided reference + /// equals to provided value when masked: + /// `(*to_poll) & mask == value` + /// Refer to [`Self::poll_mem`] for polling memory instead. + /// + /// # Returns + /// [`Ok`]: Criteria was met before timeout. + /// [`Err`]: One of below error happened: + /// * [`Status::TIMEOUT`]: Delay expired before a match occurred. + /// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. + /// + /// # Panic + /// Panics when delay is too large (longer than 58494 years). + pub fn poll_io(&self, to_poll: &U, mask: U, value: U, delay: Duration) -> crate::Result<(), U> { + let mut result = U::default(); + let delay = delay.as_nanos().div_ceil(100).try_into().unwrap(); + let status = unsafe { + (self.0.poll_io)( + ptr::from_ref(&self.0).cast_mut(), + encode_io_mode_and_unit::(PciIoMode::Normal), + ptr::from_ref(to_poll).addr() as u64, + mask.into(), + value, + delay, + &mut result + ) + }; + + match status { + Status::SUCCESS => { + Ok(()) + } + e => Err(e.into()), + } + } + // TODO: get/set attributes } From a6b9f85ea5e38de07e47c59ffdf2b02f39c56236 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Mon, 30 Jun 2025 09:55:40 +0900 Subject: [PATCH 10/14] uefi: Implement get/set attributes --- uefi-raw/src/protocol/pci/resource.rs | 19 +- uefi-test-runner/src/proto/pci/root_bridge.rs | 18 +- uefi/src/proto/pci/mod.rs | 4 +- uefi/src/proto/pci/root_bridge.rs | 176 ++++++++++++++---- 4 files changed, 176 insertions(+), 41 deletions(-) diff --git a/uefi-raw/src/protocol/pci/resource.rs b/uefi-raw/src/protocol/pci/resource.rs index 083f49331..e806f162f 100644 --- a/uefi-raw/src/protocol/pci/resource.rs +++ b/uefi-raw/src/protocol/pci/resource.rs @@ -7,8 +7,8 @@ use static_assertions::assert_eq_size; #[repr(C, packed)] #[derive(Debug)] pub struct QWordAddressSpaceDescriptor { - tag: u8, - descriptor_length: u16, + pub tag: u8, + pub descriptor_length: u16, pub resource_type: ResourceType, pub flags: GeneralFlags, pub type_flags: u8, @@ -57,12 +57,18 @@ impl QWordAddressSpaceDescriptor { pub fn verify(&self) { let tag = self.tag; if tag != 0x8A { - panic!("Tag value for QWordAddressSpaceDescriptor should be 0x8A, not {}", tag); + panic!( + "Tag value for QWordAddressSpaceDescriptor should be 0x8A, not {}", + tag + ); } let length = self.descriptor_length; if self.descriptor_length != 0x2B { - panic!("Length value for QWordAddressSpaceDescriptor should be 0x2B, not {}", length); + panic!( + "Length value for QWordAddressSpaceDescriptor should be 0x2B, not {}", + length + ); } if self.flags.bits() & 0b11110000 != 0 { @@ -93,7 +99,10 @@ impl QWordAddressSpaceDescriptor { let min = self.range_min; let max = self.range_max; if max < min { - panic!("Address range is invalid. Max(0x{:X}) is smaller than Min(0x{:X}).", max, min); + panic!( + "Address range is invalid. Max(0x{:X}) is smaller than Min(0x{:X}).", + max, min + ); } } } diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 591fbdb49..3b49fb2fb 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use core::ptr; -use uefi::{println, Handle}; use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle}; use uefi::proto::ProtocolPointer; use uefi::proto::pci::PciIoAddress; -use uefi::proto::pci::root_bridge::PciRootBridgeIo; +use uefi::proto::pci::root_bridge::{AttributeReport, PciRootBridgeIo}; +use uefi::{Handle, println}; use uefi_raw::protocol::pci::root_bridge::{ PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, }; @@ -223,6 +223,20 @@ pub fn test_config() { } } +pub fn test_attributes() { + let pci_handles = uefi::boot::find_handles::().unwrap(); + + for pci_handle in pci_handles { + let pci_proto = get_open_protocol::(pci_handle); + let AttributeReport { supported, .. } = pci_proto.get_attributes(); + + pci_proto + .set_attributes(PciRootBridgeIoProtocolAttribute::empty(), None) + .unwrap(); + pci_proto.set_attributes(supported, None).unwrap(); + } +} + fn get_open_protocol(handle: Handle) -> ScopedProtocol

{ let open_opts = OpenProtocolParams { handle, diff --git a/uefi/src/proto/pci/mod.rs b/uefi/src/proto/pci/mod.rs index d3d025f85..3ee129e38 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -3,7 +3,7 @@ //! PCI Bus specific protocols. use core::cmp::Ordering; - +use core::fmt::Debug; use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth; pub mod buffer; @@ -95,7 +95,7 @@ impl Ord for PciIoAddress { /// Trait implemented by all data types that can natively be read from a PCI device. /// Note: Not all of them have to actually be supported by the hardware at hand. -pub trait PciIoUnit: Sized + Default + Into {} +pub trait PciIoUnit: Sized + Default + Into + Debug {} impl PciIoUnit for u8 {} impl PciIoUnit for u16 {} impl PciIoUnit for u32 {} diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index f7dd1408b..3cf554826 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -12,15 +12,18 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::num::NonZeroUsize; use core::ptr; -use core::ptr::{NonNull, slice_from_raw_parts}; +use core::ptr::NonNull; use core::time::Duration; use log::debug; use uefi::proto::pci::PciIoMode; use uefi::proto::pci::root_bridge::io_access::IoAccessType; use uefi_macros::unsafe_protocol; -use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; use uefi_raw::Status; -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth}; +use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; +use uefi_raw::protocol::pci::root_bridge::{ + PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, + PciRootBridgeIoProtocolOperation, +}; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; #[cfg(doc)] @@ -191,9 +194,10 @@ impl PciRootBridgeIo { /// /// # Returns /// [`Ok`] on successful copy. + /// /// [`Err`] otherwise. - /// * [`Status::INVALID_PARAMETER`]: Width is invalid for this PCI root bridge. - /// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. + /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. + /// - [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. /// # Question /// Should this support other types than just primitives? #[cfg(feature = "alloc")] @@ -219,15 +223,16 @@ impl PciRootBridgeIo { /// /// # Returns /// [`Ok`] when it successfully retrieved current configuration. + /// /// [`Err`] when it failed to retrieve current configuration. - /// * Status value will be [`Status::UNSUPPORTED`] + /// - Its Status value will be [`Status::UNSUPPORTED`] /// /// # Panic /// It may panic if pci devices or drivers for those provided by boot service misbehave. /// There are multiple verifications put in place, and they will panic if invariants /// are broken, such as when invalid enum variant value was received /// or reserved bits are not 0 - pub fn configuration(&self) -> crate::Result<&'static [QWordAddressSpaceDescriptor]> { + pub fn configuration(&self) -> crate::Result<&[QWordAddressSpaceDescriptor]> { let mut configuration_address = 0u64; let configuration_status = unsafe { (self.0.configuration)( @@ -249,8 +254,10 @@ impl PciRootBridgeIo { cursor_ref.verify(); count += 1; if count >= 1024 { - panic!("Timed out while fetching configurations:\ - There are more than 1024 configuration spaces"); + panic!( + "Timed out while fetching configurations:\ + There are more than 1024 configuration spaces" + ); } } 0x79 => { @@ -271,7 +278,7 @@ impl PciRootBridgeIo { } }; let list: &[QWordAddressSpaceDescriptor] = - unsafe { slice_from_raw_parts(head, count).as_ref().unwrap() }; + unsafe { ptr::slice_from_raw_parts(head, count).as_ref().unwrap() }; Ok(list) } e => Err(e.into()), @@ -282,18 +289,27 @@ impl PciRootBridgeIo { /// The criteria in question is met when value read from provided reference /// equals to provided value when masked: /// `(*to_poll) & mask == value` - /// /// Refer to [`Self::poll_io`] for polling io port instead. + /// + /// Refer to [`Self::poll_io`] for polling io port instead. /// /// # Returns /// [`Ok`]: Criteria was met before timeout. + /// /// [`Err`]: One of below error happened: - /// * [`Status::TIMEOUT`]: Delay expired before a match occurred. - /// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. + /// - [`Status::TIMEOUT`]: Delay expired before a match occurred. + /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. + /// - [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. /// /// # Panic /// Panics when delay is too large (longer than 58494 years). - pub fn poll_mem(&self, to_poll: &U, mask: U, value: U, delay: Duration) -> crate::Result<(), U> { - let mut result = U::default(); + pub fn poll_mem( + &self, + to_poll: &U, + mask: U, + value: U, + delay: Duration, + ) -> crate::Result<(), u64> { + let mut result = 0u64; let delay = delay.as_nanos().div_ceil(100).try_into().unwrap(); let status = unsafe { (self.0.poll_mem)( @@ -301,36 +317,40 @@ impl PciRootBridgeIo { encode_io_mode_and_unit::(PciIoMode::Normal), ptr::from_ref(to_poll).addr() as u64, mask.into(), - value, + value.into(), delay, - &mut result + &mut result, ) }; - match status { - Status::SUCCESS => { - Ok(()) - } - e => Err(e.into()), - } + status.to_result_with_err(|_| result) } /// Polls a same io port until criteria is met. /// The criteria in question is met when value read from provided reference /// equals to provided value when masked: /// `(*to_poll) & mask == value` + /// /// Refer to [`Self::poll_mem`] for polling memory instead. /// /// # Returns /// [`Ok`]: Criteria was met before timeout. + /// /// [`Err`]: One of below error happened: - /// * [`Status::TIMEOUT`]: Delay expired before a match occurred. - /// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. + /// - [`Status::TIMEOUT`]: Delay expired before a match occurred. + /// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge. + /// - [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources. /// /// # Panic /// Panics when delay is too large (longer than 58494 years). - pub fn poll_io(&self, to_poll: &U, mask: U, value: U, delay: Duration) -> crate::Result<(), U> { - let mut result = U::default(); + pub fn poll_io( + &self, + to_poll: &U, + mask: U, + value: U, + delay: Duration, + ) -> crate::Result<(), u64> { + let mut result = 0u64; let delay = delay.as_nanos().div_ceil(100).try_into().unwrap(); let status = unsafe { (self.0.poll_io)( @@ -338,21 +358,101 @@ impl PciRootBridgeIo { encode_io_mode_and_unit::(PciIoMode::Normal), ptr::from_ref(to_poll).addr() as u64, mask.into(), - value, + value.into(), delay, - &mut result + &mut result, + ) + }; + + status.to_result_with_err(|_| result) + } + + /// Returns available and used attributes of this root bridge. + /// + /// # Returns + /// Both supported and used attribute will be returned in struct [`AttributeReport`] + pub fn get_attributes(&self) -> AttributeReport { + let mut supports = PciRootBridgeIoProtocolAttribute::empty(); + let mut attributes = PciRootBridgeIoProtocolAttribute::empty(); + let status = unsafe { + (self.0.get_attributes)( + &self.0, + ptr::from_mut(&mut supports).cast(), + ptr::from_mut(&mut attributes).cast(), + ) + }; + + match status { + Status::SUCCESS => AttributeReport { + supported: supports, + used: attributes, + }, + Status::INVALID_PARAMETER => unreachable!(), + e => panic!("Unexpected error occurred: {:?}", e), + } + } + + /// Sets attributes to use for this root bridge. + /// Specified attributes must be supported. Otherwise, it will return error. + /// Supported attributes can be requested with [`Self::get_attributes`] + /// + /// # Returns + /// [`Ok`]: Optional resource range. It will only be available when resource + /// parameter is Some and one of: + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE`] + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_CACHED`] + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_DISABLE`] + /// is set. + /// + /// [`Err`]: Possible error cases: + /// - [`Status::UNSUPPORTED`]: A bit is set in Attributes that is not supported by the PCI Root Bridge. + /// The supported attribute bits are reported by [`Self::get_attributes`] + /// - [`Status::INVALID_PARAMETER`]: More than one attribute bit is set in Attributes that requires a resource parameter. + /// - [`Status::OUT_OF_RESOURCES`]: There are not enough resources to set the attributes on the resource range specified by resource parameter. + pub fn set_attributes<'a, 'p>( + &'p self, + attributes: PciRootBridgeIoProtocolAttribute, + resource: Option<&'a [u64]>, + ) -> crate::Result> + where + 'p: 'a, + { + let (mut base, mut length) = match resource { + Some(v) => { + let ptr: *const [u64] = v; + let base = ptr.addr() as u64; + let length = ptr.len() as u64; + (base, length) + } + None => (0, 0), + }; + let status = unsafe { + (self.0.set_attributes)( + ptr::from_ref(&self.0).cast_mut(), + attributes.bits(), + &mut base, + &mut length, ) }; match status { Status::SUCCESS => { - Ok(()) + let to_return = if length != 0 { + unsafe { + Some( + ptr::slice_from_raw_parts(base as *const u64, length as usize) + .as_ref() + .unwrap(), + ) + } + } else { + None + }; + Ok(to_return) } e => Err(e.into()), } } - - // TODO: get/set attributes } /// Struct for performing PCI I/O operations on a root bridge. @@ -575,3 +675,15 @@ impl PciIoAccessPci<'_, T> { } } } + +/// Struct containing return value for [`PciRootBridgeIo::get_attributes`] +/// This is to minimize confusion by giving both of them names. +#[derive(Debug)] +pub struct AttributeReport { + /// Attributes supported by this bridge. + /// Only attributes in this set can be used as parameter for [`PciRootBridgeIo::set_attributes`] + pub supported: PciRootBridgeIoProtocolAttribute, + + /// Attributes currently being used. + pub used: PciRootBridgeIoProtocolAttribute, +} From 5752e90a5098e3cafc46c45fa9ee483b73592f43 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Wed, 2 Jul 2025 10:01:47 +0900 Subject: [PATCH 11/14] uefi-test-runner: Switch from static assertion to test function for consistency --- Cargo.lock | 7 ------- Cargo.toml | 1 - uefi-raw/Cargo.toml | 1 - uefi-test-runner/src/proto/pci/mod.rs | 2 ++ uefi-test-runner/src/proto/pci/root_bridge.rs | 8 +++++++- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b2e8ec8d..9f3727fb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,12 +794,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "subtle" version = "2.6.1" @@ -984,7 +978,6 @@ name = "uefi-raw" version = "0.11.0" dependencies = [ "bitflags 2.9.1", - "static_assertions", "uguid", ] diff --git a/Cargo.toml b/Cargo.toml index bb2ed350d..df43bbca0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ bitflags = "2.0.0" log = { version = "0.4.5", default-features = false } ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] } uguid = "2.2.1" -static_assertions = "1.1.0" [patch.crates-io] uefi = { path = "uefi" } diff --git a/uefi-raw/Cargo.toml b/uefi-raw/Cargo.toml index 4b30ad2f7..b77b5530f 100644 --- a/uefi-raw/Cargo.toml +++ b/uefi-raw/Cargo.toml @@ -21,7 +21,6 @@ rust-version = "1.85.1" [dependencies] bitflags.workspace = true uguid.workspace = true -static_assertions.workspace = true [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/uefi-test-runner/src/proto/pci/mod.rs b/uefi-test-runner/src/proto/pci/mod.rs index 05f733c07..8e02685c9 100644 --- a/uefi-test-runner/src/proto/pci/mod.rs +++ b/uefi-test-runner/src/proto/pci/mod.rs @@ -8,4 +8,6 @@ pub fn test() { root_bridge::test_mapping(); root_bridge::test_copy(); root_bridge::test_config(); + root_bridge::test_attributes(); + root_bridge::test_sizes(); } diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index 3b49fb2fb..eb0cef9e3 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -5,7 +5,8 @@ use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, ima use uefi::proto::ProtocolPointer; use uefi::proto::pci::PciIoAddress; use uefi::proto::pci::root_bridge::{AttributeReport, PciRootBridgeIo}; -use uefi::{Handle, println}; +use uefi::Handle; +use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; use uefi_raw::protocol::pci::root_bridge::{ PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, }; @@ -237,6 +238,11 @@ pub fn test_attributes() { } } +pub fn test_sizes() { + assert_eq!(size_of::(), 0x2E); + assert_eq!(size_of::(), size_of::()); +} + fn get_open_protocol(handle: Handle) -> ScopedProtocol

{ let open_opts = OpenProtocolParams { handle, From 36ec2065c330bf6897be3dd95c256bec23f13a01 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Wed, 2 Jul 2025 10:02:20 +0900 Subject: [PATCH 12/14] uefi: Adjusting documentation and attributes for QA test --- uefi-raw/src/protocol/pci/resource.rs | 6 ++--- uefi-raw/src/protocol/pci/root_bridge.rs | 3 ++- uefi/src/proto/pci/buffer.rs | 25 ++++++++++---------- uefi/src/proto/pci/region.rs | 2 +- uefi/src/proto/pci/resource.rs | 11 +++++---- uefi/src/proto/pci/root_bridge.rs | 30 ++++++++++++------------ 6 files changed, 41 insertions(+), 36 deletions(-) diff --git a/uefi-raw/src/protocol/pci/resource.rs b/uefi-raw/src/protocol/pci/resource.rs index e806f162f..061d93c0c 100644 --- a/uefi-raw/src/protocol/pci/resource.rs +++ b/uefi-raw/src/protocol/pci/resource.rs @@ -1,9 +1,10 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + use bitflags::bitflags; -use static_assertions::assert_eq_size; /// Descriptor for current PCI root bridge's configuration space. /// Specification: -/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#qword-address-space-descriptor +/// #[repr(C, packed)] #[derive(Debug)] pub struct QWordAddressSpaceDescriptor { @@ -18,7 +19,6 @@ pub struct QWordAddressSpaceDescriptor { pub translation_offset: u64, pub address_length: u64, } -assert_eq_size!(QWordAddressSpaceDescriptor, [u8; 0x2E]); newtype_enum! { /// Indicates which type of resource this descriptor describes. diff --git a/uefi-raw/src/protocol/pci/root_bridge.rs b/uefi-raw/src/protocol/pci/root_bridge.rs index 79c8d1902..45c43a56d 100644 --- a/uefi-raw/src/protocol/pci/root_bridge.rs +++ b/uefi-raw/src/protocol/pci/root_bridge.rs @@ -40,7 +40,7 @@ newtype_enum! { bitflags! { /// Describes PCI I/O Protocol Attribute bitflags specified in UEFI specification. - ///. https://uefi.org/specs/UEFI/2.10_A/14_Protocols_PCI_Bus_Support.html + /// #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(transparent)] pub struct PciRootBridgeIoProtocolAttribute: u64 { @@ -156,6 +156,7 @@ impl PciRootBridgeIoProtocol { } impl PciRootBridgeIoProtocolWidth { + #[must_use] pub fn size(self) -> usize { match self { Self::UINT8 | Self::FIFO_UINT8 | Self::FILL_UINT8 => 1, diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs index 5b043e95c..cc0964fcb 100644 --- a/uefi/src/proto/pci/buffer.rs +++ b/uefi/src/proto/pci/buffer.rs @@ -37,29 +37,30 @@ impl<'p, T> PciBuffer<'p, MaybeUninit> { /// # Safety /// Callers of this function must guarantee that value stored is valid. #[must_use] - pub unsafe fn assume_init(self) -> PciBuffer<'p, T> { - let old = ManuallyDrop::new(self); - PciBuffer { - base: old.base.cast(), - pages: old.pages, - proto: old.proto, - } + pub const unsafe fn assume_init(self) -> PciBuffer<'p, T> { + let initialized = PciBuffer { + base: self.base.cast(), + pages: self.pages, + proto: self.proto, + }; + let _ = ManuallyDrop::new(self); + initialized } } -impl<'p, T> AsRef for PciBuffer<'p, T> { +impl AsRef for PciBuffer<'_, T> { fn as_ref(&self) -> &T { unsafe { self.base.as_ref() } } } -impl<'p, T> AsMut for PciBuffer<'p, T> { +impl AsMut for PciBuffer<'_, T> { fn as_mut(&mut self) -> &mut T { unsafe { self.base.as_mut() } } } -impl<'p, T> Deref for PciBuffer<'p, T> { +impl Deref for PciBuffer<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -67,13 +68,13 @@ impl<'p, T> Deref for PciBuffer<'p, T> { } } -impl<'p, T> DerefMut for PciBuffer<'p, T> { +impl DerefMut for PciBuffer<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut() } } -impl<'p, T> Drop for PciBuffer<'p, T> { +impl Drop for PciBuffer<'_, T> { fn drop(&mut self) { let status = unsafe { (self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast()) diff --git a/uefi/src/proto/pci/region.rs b/uefi/src/proto/pci/region.rs index e4cfa3f16..ae8bc3f98 100644 --- a/uefi/src/proto/pci/region.rs +++ b/uefi/src/proto/pci/region.rs @@ -76,7 +76,7 @@ where } } -impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> { +impl Drop for PciMappedRegion<'_, '_> { fn drop(&mut self) { let status = unsafe { (self.proto.unmap)(self.proto, self.key) }; match status { diff --git a/uefi/src/proto/pci/resource.rs b/uefi/src/proto/pci/resource.rs index 7b1dd99ec..df87de360 100644 --- a/uefi/src/proto/pci/resource.rs +++ b/uefi/src/proto/pci/resource.rs @@ -7,7 +7,7 @@ use uefi_raw::protocol::pci::resource::{QWordAddressSpaceDescriptor, ResourceTyp /// Describes resource type specific flags. /// ACPI Specification: -/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#type-specific-attributes +/// #[derive(Debug)] pub enum TypeFlag { /// Flags for Memory type resource. @@ -20,7 +20,7 @@ pub enum TypeFlag { /// Flags for Memory type resource. /// ACPI Specification: -/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#memory-resource-flag-resource-type-0-definitions +/// #[derive(Debug)] pub struct MemoryFlag { /// Specifies if this resource is I/O on primary side of the bridge. @@ -43,7 +43,7 @@ pub struct MemoryFlag { /// Flags for Io type resource. /// ACP Specification: -/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#io-resource-flag-resource-type-1-definitions +/// #[derive(Debug)] pub struct IoFlags { /// Specifies sparsity of address translation. @@ -62,7 +62,7 @@ pub struct IoFlags { /// Flags for Bus type resource. /// Currently, it's unused and all bits should be 0. /// ACPI Specification: -/// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/06_Device_Configuration/Device_Configuration.html#bus-number-range-resource-flag-resource-type-2-definitions +/// #[derive(Debug)] pub struct BusFlags { _reserved: u8, @@ -224,6 +224,7 @@ impl MemoryFlag { /// /// # Panic /// Panics when reserved bits are not 0. + #[must_use] pub fn new(flags: u8) -> Self { let write_status = WriteStatus(flags & 0b1); let mem_attribute = MemAttribute((flags >> 1) & 0b11); @@ -245,6 +246,7 @@ impl IoFlags { /// /// # Panic /// Panics when reserved bits are not 0. + #[must_use] pub fn new(flags: u8) -> Self { assert_ne!(flags & 0b11, 0); let rng_range = RngRange(flags & 0b11); @@ -269,6 +271,7 @@ impl BusFlags { /// /// # Panic /// Panics when byte is not 0. + #[must_use] pub fn new(flags: u8) -> Self { assert_eq!(flags, 0); Self { _reserved: 0 } diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index 3cf554826..52e39319e 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -3,6 +3,7 @@ //! PCI Root Bridge protocol. use super::{PciIoUnit, encode_io_mode_and_unit}; +use crate::Status; use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; use crate::proto::pci::region::PciMappedRegion; @@ -18,7 +19,6 @@ use log::debug; use uefi::proto::pci::PciIoMode; use uefi::proto::pci::root_bridge::io_access::IoAccessType; use uefi_macros::unsafe_protocol; -use uefi_raw::Status; use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; use uefi_raw::protocol::pci::root_bridge::{ PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, @@ -26,9 +26,6 @@ use uefi_raw::protocol::pci::root_bridge::{ }; use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; -#[cfg(doc)] -use crate::Status; - /// Protocol that provides access to the PCI Root Bridge I/O protocol. /// /// # UEFI Spec Description @@ -42,12 +39,13 @@ pub struct PciRootBridgeIo(PciRootBridgeIoProtocol); impl PciRootBridgeIo { /// Get the segment number where this PCI root bridge resides. #[must_use] - pub fn segment_nr(&self) -> u32 { + pub const fn segment_nr(&self) -> u32 { self.0.segment_number } /// Access PCI operations on this root bridge. - pub fn pci(&self) -> PciIoAccessPci<'_, io_access::Pci> { + #[must_use] + pub const fn pci(&self) -> PciIoAccessPci<'_, io_access::Pci> { PciIoAccessPci { proto: ptr::from_ref(&self.0).cast_mut(), io_access: &self.0.pci, @@ -56,7 +54,8 @@ impl PciRootBridgeIo { } /// Access I/O operations on this root bridge. - pub fn io(&self) -> PciIoAccessPci<'_, io_access::Io> { + #[must_use] + pub const fn io(&self) -> PciIoAccessPci<'_, io_access::Io> { PciIoAccessPci { proto: ptr::from_ref(&self.0).cast_mut(), io_access: &self.0.io, @@ -65,7 +64,8 @@ impl PciRootBridgeIo { } /// Access memory operations on this root bridge. - pub fn mem(&self) -> PciIoAccessPci<'_, io_access::Mem> { + #[must_use] + pub const fn mem(&self) -> PciIoAccessPci<'_, io_access::Mem> { PciIoAccessPci { proto: ptr::from_ref(&self.0).cast_mut(), io_access: &self.0.mem, @@ -88,9 +88,9 @@ impl PciRootBridgeIo { /// # Errors /// - [`Status::INVALID_PARAMETER`] MemoryType is invalid. /// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: - /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`] - /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] - /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE`] + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_CACHED`] + /// - [`PciRootBridgeIoProtocolAttribute::DUAL_ADDRESS_CYCLE`] /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. #[cfg(feature = "alloc")] pub fn allocate_buffer( @@ -207,7 +207,7 @@ impl PciRootBridgeIo { let status = unsafe { (self.0.copy_mem)( - ((&self.0) as *const PciRootBridgeIoProtocol).cast_mut(), + ptr::from_ref::(&self.0).cast_mut(), width, destination.as_ptr().addr() as u64, source.as_ptr().addr() as u64, @@ -237,7 +237,7 @@ impl PciRootBridgeIo { let configuration_status = unsafe { (self.0.configuration)( &self.0, - ((&mut configuration_address) as *mut u64).cast::<*const c_void>(), + ptr::from_mut::(&mut configuration_address).cast::<*const c_void>(), ) }; match configuration_status { @@ -371,6 +371,7 @@ impl PciRootBridgeIo { /// /// # Returns /// Both supported and used attribute will be returned in struct [`AttributeReport`] + #[must_use] pub fn get_attributes(&self) -> AttributeReport { let mut supports = PciRootBridgeIoProtocolAttribute::empty(); let mut attributes = PciRootBridgeIoProtocolAttribute::empty(); @@ -398,11 +399,10 @@ impl PciRootBridgeIo { /// /// # Returns /// [`Ok`]: Optional resource range. It will only be available when resource - /// parameter is Some and one of: + /// parameter is Some and contains one of: /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE`] /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_CACHED`] /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_DISABLE`] - /// is set. /// /// [`Err`]: Possible error cases: /// - [`Status::UNSUPPORTED`]: A bit is set in Attributes that is not supported by the PCI Root Bridge. From 7ba95c3977766c31f10b9fe64f049621c464fb26 Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Wed, 2 Jul 2025 10:07:28 +0900 Subject: [PATCH 13/14] uefi: Adjust import order for QA test --- uefi-test-runner/src/proto/pci/root_bridge.rs | 2 +- uefi/src/proto/pci/root_bridge.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/uefi-test-runner/src/proto/pci/root_bridge.rs b/uefi-test-runner/src/proto/pci/root_bridge.rs index eb0cef9e3..9e3607eb1 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use core::ptr; +use uefi::Handle; use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle}; use uefi::proto::ProtocolPointer; use uefi::proto::pci::PciIoAddress; use uefi::proto::pci::root_bridge::{AttributeReport, PciRootBridgeIo}; -use uefi::Handle; use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; use uefi_raw::protocol::pci::root_bridge::{ PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, diff --git a/uefi/src/proto/pci/root_bridge.rs b/uefi/src/proto/pci/root_bridge.rs index 52e39319e..c755c15fc 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -3,10 +3,9 @@ //! PCI Root Bridge protocol. use super::{PciIoUnit, encode_io_mode_and_unit}; -use crate::Status; -use crate::StatusExt; use crate::proto::pci::buffer::PciBuffer; use crate::proto::pci::region::PciMappedRegion; +use crate::{Status, StatusExt}; use core::ffi::c_void; use core::fmt::Debug; use core::marker::PhantomData; From e62fbf3affc5561359133ebe8c7963f94ac03aed Mon Sep 17 00:00:00 2001 From: tmvkrpxl0 Date: Wed, 2 Jul 2025 10:13:16 +0900 Subject: [PATCH 14/14] uefi: Simplify debug! macro --- uefi/src/proto/pci/region.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uefi/src/proto/pci/region.rs b/uefi/src/proto/pci/region.rs index ae8bc3f98..74c7fd60e 100644 --- a/uefi/src/proto/pci/region.rs +++ b/uefi/src/proto/pci/region.rs @@ -53,7 +53,7 @@ where proto: &'p PciRootBridgeIoProtocol, ) -> Self { let end = device_address + length as u64; - debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end); + debug!("Mapped new region [0x{device_address:X}..0x{end:X}]"); Self { region: PciRegion { device_address,