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..061d93c0c --- /dev/null +++ b/uefi-raw/src/protocol/pci/resource.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use bitflags::bitflags; + +/// Descriptor for current PCI root bridge's configuration space. +/// Specification: +/// +#[repr(C, packed)] +#[derive(Debug)] +pub struct QWordAddressSpaceDescriptor { + pub tag: u8, + pub 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, +} + +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 a1b66872f..45c43a56d 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,29 @@ newtype_enum! { } } +bitflags! { + /// Describes PCI I/O Protocol Attribute bitflags specified in UEFI specification. + /// + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + #[repr(transparent)] + pub struct PciRootBridgeIoProtocolAttribute: u64 { + 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; + } +} + #[derive(Debug)] #[repr(C)] pub struct PciRootBridgeIoAccess { @@ -130,3 +154,16 @@ pub struct PciRootBridgeIoProtocol { impl PciRootBridgeIoProtocol { pub const GUID: Guid = guid!("2f707ebb-4a1a-11d4-9a38-0090273fc14d"); } + +impl PciRootBridgeIoProtocolWidth { + #[must_use] + 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/src/proto/pci/mod.rs b/uefi-test-runner/src/proto/pci/mod.rs index 75ecbc0ad..8e02685c9 100644 --- a/uefi-test-runner/src/proto/pci/mod.rs +++ b/uefi-test-runner/src/proto/pci/mod.rs @@ -3,5 +3,11 @@ pub mod root_bridge; pub fn test() { - root_bridge::test(); + root_bridge::test_io(); + root_bridge::test_buffer(); + 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 0847b3e8f..9e3607eb1 100644 --- a/uefi-test-runner/src/proto/pci/root_bridge.rs +++ b/uefi-test-runner/src/proto/pci/root_bridge.rs @@ -1,19 +1,25 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use core::mem; +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::PciRootBridgeIo; +use uefi::proto::pci::root_bridge::{AttributeReport, PciRootBridgeIo}; +use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor; +use uefi_raw::protocol::pci::root_bridge::{ + PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, +}; +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() { +pub fn test_io() { let pci_handles = uefi::boot::find_handles::().unwrap(); let mut red_hat_dev_cnt = 0; @@ -21,8 +27,7 @@ pub fn test() { let mut sata_ctrl_cnt = 0; for pci_handle in pci_handles { - let mut pci_proto = get_open_protocol::(pci_handle); - + let pci_proto = get_open_protocol::(pci_handle); for bus in 0..=255 { for dev in 0..32 { for fun in 0..8 { @@ -67,6 +72,177 @@ 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::MEMORY_WRITE_COMBINE, + ) + .unwrap(); + + let buffer = unsafe { + buffer.assume_init_mut().fill(0); + buffer.assume_init() + }; + + assert_eq!(buffer.as_ptr().addr() % 4096, 0); + } +} + +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::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"); + } + } +} + +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); + 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; + + 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()); + } +} + +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(); + } +} + +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, diff --git a/uefi/src/proto/pci/buffer.rs b/uefi/src/proto/pci/buffer.rs new file mode 100644 index 000000000..cc0964fcb --- /dev/null +++ b/uefi/src/proto/pci/buffer.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Defines wrapper allocated by PCI Root Bridge protocol. + +use core::fmt::Debug; +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> { + base: NonNull, + pages: NonZeroUsize, + proto: &'p 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 } + } + + /// Assumes the contents of this buffer have been initialized. + /// + /// # Safety + /// Callers of this function must guarantee that value stored is valid. + #[must_use] + 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 AsRef for PciBuffer<'_, T> { + fn as_ref(&self) -> &T { + unsafe { self.base.as_ref() } + } +} + +impl AsMut for PciBuffer<'_, T> { + fn as_mut(&mut self) -> &mut T { + unsafe { self.base.as_mut() } + } +} + +impl Deref for PciBuffer<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl DerefMut for PciBuffer<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut() + } +} + +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()) + }; + 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..3ee129e38 100644 --- a/uefi/src/proto/pci/mod.rs +++ b/uefi/src/proto/pci/mod.rs @@ -3,9 +3,12 @@ //! PCI Bus specific protocols. use core::cmp::Ordering; - +use core::fmt::Debug; 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 @@ -92,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 {} +pub trait PciIoUnit: Sized + Default + Into + Debug {} impl PciIoUnit for u8 {} impl PciIoUnit for u16 {} impl PciIoUnit for u32 {} @@ -106,7 +109,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 new file mode 100644 index 000000000..74c7fd60e --- /dev/null +++ b/uefi/src/proto/pci/region.rs @@ -0,0 +1,113 @@ +// 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::fmt::Debug; +use core::marker::PhantomData; +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, +{ + region: PciRegion, + _lifetime_holder: PhantomData<&'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, + length: usize, + key: *const c_void, + _to_map: &'r T, + proto: &'p PciRootBridgeIoProtocol, + ) -> Self { + let end = device_address + length as u64; + debug!("Mapped new region [0x{device_address:X}..0x{end:X}]"); + Self { + region: PciRegion { + device_address, + length, + }, + _lifetime_holder: PhantomData, + 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) -> PciRegion { + self.region + } +} + +impl Drop for PciMappedRegion<'_, '_> { + fn drop(&mut self) { + 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 + ); + } + 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!(), + } + } +} + +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/resource.rs b/uefi/src/proto/pci/resource.rs new file mode 100644 index 000000000..df87de360 --- /dev/null +++ b/uefi/src/proto/pci/resource.rs @@ -0,0 +1,279 @@ +// 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: +/// +#[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: +/// +#[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: +/// +#[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: +/// +#[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. + #[must_use] + 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. + #[must_use] + 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. + #[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 57135279a..c755c15fc 100644 --- a/uefi/src/proto/pci/root_bridge.rs +++ b/uefi/src/proto/pci/root_bridge.rs @@ -2,24 +2,37 @@ //! PCI Root Bridge protocol. +use super::{PciIoUnit, encode_io_mode_and_unit}; +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; +use core::mem::MaybeUninit; +use core::num::NonZeroUsize; use core::ptr; - -use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; -use crate::StatusExt; +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::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol}; - -#[cfg(doc)] -use crate::Status; +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}; /// Protocol that provides access to the PCI Root Bridge I/O protocol. /// /// # 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. -#[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(PciRootBridgeIoProtocol::GUID)] +#[derive(Debug)] pub struct PciRootBridgeIo(PciRootBridgeIoProtocol); impl PciRootBridgeIo { @@ -29,11 +42,33 @@ impl PciRootBridgeIo { self.0.segment_number } - /// Access PCI I/O operations on this root bridge. - pub const fn pci(&mut self) -> PciIoAccessPci<'_> { + /// Access PCI operations on this root bridge. + #[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, + _type: PhantomData, + } + } + + /// Access I/O operations on this root bridge. + #[must_use] + pub const fn io(&self) -> PciIoAccessPci<'_, io_access::Io> { PciIoAccessPci { - proto: &mut self.0, - io_access: &mut self.0.pci, + proto: ptr::from_ref(&self.0).cast_mut(), + io_access: &self.0.io, + _type: PhantomData, + } + } + + /// Access memory operations on this root bridge. + #[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, + _type: PhantomData, } } @@ -42,27 +77,425 @@ 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) -> 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. + /// + /// # Errors + /// - [`Status::INVALID_PARAMETER`] MemoryType is invalid. + /// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: + /// - [`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( + &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::new(base, pages, &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 + #[cfg(feature = "alloc")] + pub fn map<'p, 'r, T>( + &'p self, + operation: PciRootBridgeIoProtocolOperation, + to_map: &'r T, + ) -> 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 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 => Ok(PciMappedRegion::new( + mapped_address, + bytes, + mapping, + to_map, + &self.0, + )), + e => Err(e.into()), + } + } + + /// 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. + /// + /// # Returns + /// [`Ok`] on successful copy. + /// + /// [`Err`] otherwise. + /// - [`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")] + pub fn copy(&self, destination: &[U], source: &[U]) -> crate::Result<()> { + assert_eq!(destination.len(), source.len()); + let width = encode_io_mode_and_unit::(PciIoMode::Normal); + + let status = unsafe { + (self.0.copy_mem)( + ptr::from_ref::(&self.0).cast_mut(), + width, + destination.as_ptr().addr() as u64, + source.as_ptr().addr() as u64, + destination.len(), + ) + }; + + 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. + /// - 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<&[QWordAddressSpaceDescriptor]> { + let mut configuration_address = 0u64; + let configuration_status = unsafe { + (self.0.configuration)( + &self.0, + ptr::from_mut::(&mut configuration_address).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 { ptr::slice_from_raw_parts(head, count).as_ref().unwrap() }; + Ok(list) + } + e => Err(e.into()), + } } - // TODO: poll I/O - // TODO: mem I/O access - // TODO: io I/O access - // TODO: map & unmap & copy memory - // TODO: buffer management - // TODO: get/set attributes - // TODO: configuration / resource settings + /// 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::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<(), u64> { + let mut result = 0u64; + 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.into(), + delay, + &mut result, + ) + }; + + 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::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<(), u64> { + let mut result = 0u64; + 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.into(), + delay, + &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`] + #[must_use] + 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 contains one of: + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE`] + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_CACHED`] + /// - [`PciRootBridgeIoProtocolAttribute::MEMORY_DISABLE`] + /// + /// [`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 => { + 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()), + } + } } /// 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, + 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 { + 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<'_> { +impl PciIoAccessPci<'_, T> { /// Reads a single value of type `U` from the specified PCI address. /// /// # Arguments @@ -74,8 +507,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)( @@ -98,8 +531,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, @@ -121,7 +554,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)( @@ -144,7 +577,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)( @@ -170,7 +603,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<()> { @@ -200,7 +633,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)( @@ -227,7 +660,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)( @@ -241,3 +674,15 @@ impl PciIoAccessPci<'_> { } } } + +/// 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, +} 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")?;