From d3fd8170ff7139263701ff24b2a1544611f62e05 Mon Sep 17 00:00:00 2001 From: Simon Lucido Date: Thu, 20 Jun 2024 17:40:06 +0200 Subject: [PATCH] acpi_tables: Update to latest RQSC specs The table definition for RQSC tables changed[0]. This commits updates the RQSC implementation to match the new specs. The new specs can be confusing to use, since some fields depends on others. Sometime they should be set, sometimes not. To prevent mishaps, enums are used to force the developer to be coherent in what he defines. [0] https://github.com/riscv-non-isa/riscv-rqsc Signed-off-by: Simon Lucido --- src/rqsc.rs | 369 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 334 insertions(+), 35 deletions(-) diff --git a/src/rqsc.rs b/src/rqsc.rs index 374176f..54f1560 100644 --- a/src/rqsc.rs +++ b/src/rqsc.rs @@ -8,10 +8,12 @@ use zerocopy::{byteorder, byteorder::LE, AsBytes}; extern crate alloc; use alloc::vec::Vec; +use core::mem::size_of; + use crate::{aml_as_bytes, assert_same_size, gas, Aml, AmlSink, Checksum, TableHeader}; -type U16 = byteorder::U16; type U32 = byteorder::U32; +type U64 = byteorder::U64; #[repr(u8)] pub enum ControllerType { @@ -20,6 +22,7 @@ pub enum ControllerType { } #[repr(u8)] +#[derive(Copy, Clone, Debug)] pub enum ResourceType { Cache = 0, Memory = 1, @@ -54,9 +57,9 @@ impl RQSC { } } - fn update_header(&mut self, data: &[u8]) { + fn update_header(&mut self, qos_len: usize) { // Fix up the length of the table - let len = data.len() as u32; + let len = qos_len as u32; let old_len = self.header.length.get(); let new_len = len + old_len; self.header.length.set(new_len); @@ -69,8 +72,9 @@ impl RQSC { } pub fn add_controller(&mut self, q: QoSController) { + let len = q.len(); self.structures.push(q); - self.update_header(q.as_bytes()); + self.update_header(len); } } @@ -87,63 +91,310 @@ impl Aml for RQSC { } } -#[repr(C, packed)] -#[derive(Clone, Copy, Debug, Default, AsBytes)] +#[derive(Clone, Debug, Default)] pub struct QoSController { /// Identifies the specific register interface that is supported by this /// controller controller_type: u8, - _reserved0: u8, - length: U16, + + length: u16, /// Register Buffer describing the starting address of the QoS register interface register: gas::GAS, - _reserved1: [u8; 3], - /// Describes the type of resource that this QoS controller has control over - resource_type: u8, - /// Depends on the Resource Type field. If Cache (0), this represents the unique Cache ID - /// from the PPTT table's Cache Type structure (Table 5.159 in ACPI Spec 6.5) that this - /// controller is associated with. If Memory, then this represents the proximity domain from - /// the SRAT table that this specific controller is associated with. If SRAT is not - /// implemented, then this shall be 0, indicating a UMA memory configuration. - resource_id: U32, + /// Non-zero number indicates that the controller supports allocation capability and the /// number of Resource Control IDs (RCID) supported by the controller. If 0, then no /// allocation control is available. - rcid_count: U32, + rcid_count: u32, /// Non-zero number indicates that the controller supports usage monitoring capability and /// the number of Monitoring Control IDs (MCID) supported by the controller. If 0, then no /// usage monitoring is available. - mcid_count: U32, + mcid_count: u32, + + /// Controller Specific flags. + /// - Bit 0: When set, indicates the controller supports zero (0) + /// reservations/allocations. + /// - Bit 1-7: Reserved + /// - Bit 8-15: Vendor Specific + controller_flags: u16, + + /// Number of Resource structures associated with this specific QoS + /// controller. + number_of_resources: u16, + + /// List of Resource Structures asssociated with this specific QoS + /// controller. + resource_structure: Vec, } impl QoSController { pub fn new( controller_type: ControllerType, register_interface_address: gas::GAS, - resource_type: ResourceType, - resource_id: u32, rcid_count: u32, mcid_count: u32, + controller_flags: u16, ) -> Self { Self { controller_type: controller_type as u8, - _reserved0: 0, - length: 32u16.into(), + length: 28u16, register: register_interface_address, - _reserved1: [0, 0, 0], - resource_type: resource_type as u8, - resource_id: resource_id.into(), - rcid_count: rcid_count.into(), - mcid_count: mcid_count.into(), + rcid_count, + mcid_count, + controller_flags, + number_of_resources: 0, + resource_structure: Vec::new(), + } + } + + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.length as usize + } + + pub fn add_resource(&mut self, resource: ResourceStructure) { + self.number_of_resources += 1; + self.length += resource.len() as u16; + self.resource_structure.push(resource); + } +} + +impl Aml for QoSController { + fn to_aml_bytes(&self, sink: &mut dyn AmlSink) { + sink.byte(self.controller_type); + sink.byte(0); + sink.word(self.length); + self.register.to_aml_bytes(sink); + sink.dword(self.rcid_count); + sink.dword(self.mcid_count); + sink.word(self.controller_flags); + sink.word(self.number_of_resources); + self.resource_structure + .iter() + .for_each(|resource| resource.to_aml_bytes(sink)); + } +} + +/// Resource Structures asssociated with a specific QoS controller +#[derive(Clone, Debug)] +pub struct ResourceStructure { + /// Describes the type of resource that this QoS controller has control over + /// - 0: Cache + /// - 1: Memory + /// - 2-0x7F: Reserved + /// - 0x80-0xFF: Vendor Specific + resource_type: ResourceType, + + /// Length of this specific Resource Structure. Length includes the Resource + /// Specific Data bytes as well. If length is set to 20, then, it indicates + /// there is no resource specific data available for this structure. + length: u16, + + /// Resource Type Specific flags + /// - Bits 0-7: Reserved + /// - Bits 8-15: Vendor Specific + resource_flags: u16, + + /// [ResourceID] + resource_id: ResourceID, +} + +impl ResourceStructure { + pub fn new(resource_type: ResourceType, resource_flags: u16, resource_id: ResourceID) -> Self { + let length = size_of::() * 3 + size_of::() * 2 + resource_id.len(); + + Self { + resource_type, + length: length as u16, + resource_flags, + resource_id, + } + } + + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.length as usize + } +} + +impl Aml for ResourceStructure { + fn to_aml_bytes(&self, sink: &mut dyn AmlSink) { + sink.byte(self.resource_type as u8); + sink.byte(0); + sink.word(self.length); + sink.word(self.resource_flags); + sink.byte(0); + self.resource_id.to_aml_bytes(sink) + } +} + +/// Regroups Resource ID Type, Resource ID 1, Resource ID 2 and Resource +/// Specific Data. +/// Enforces coherency by using enum variants, ensuring a correct layout of Resource ID +/// 1 and 2 for a corresponding Resource ID Type. +#[derive(Clone, Debug)] +pub enum ResourceID { + Cache(CacheResource), + MemoryAffinityStructure(MemoryAffinityStructureResource), + ACPIDevice(ACPIDeviceResource), + PCIDevice(PCIDeviceResource), + + /// The byte must be the Resource ID Type. + /// The Vec must be Resource ID 1, Resource ID 2 and Resource Specific Data + /// serialized as aml_bytes. + VendorSpecific(u8, Vec), +} + +impl ResourceID { + pub fn resource_id_type(&self) -> u8 { + match self { + ResourceID::Cache(_) => 0, + ResourceID::MemoryAffinityStructure(_) => 1, + ResourceID::ACPIDevice(_) => 2, + ResourceID::PCIDevice(_) => 3, + ResourceID::VendorSpecific(r#type, _) => *r#type, } } + + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + size_of::() + + match self { + ResourceID::Cache(_) => size_of::(), + ResourceID::MemoryAffinityStructure(_) => { + size_of::() + } + ResourceID::ACPIDevice(_) => size_of::(), + ResourceID::PCIDevice(_) => size_of::(), + ResourceID::VendorSpecific(_, resource) => resource.len(), + } + } } -aml_as_bytes!(QoSController); -assert_same_size!(QoSController, [u8; 32]); +impl Aml for ResourceID { + fn to_aml_bytes(&self, sink: &mut dyn AmlSink) { + sink.byte(self.resource_id_type()); + + match self { + ResourceID::Cache(resource) => resource.to_aml_bytes(sink), + ResourceID::MemoryAffinityStructure(resource) => resource.to_aml_bytes(sink), + ResourceID::ACPIDevice(resource) => resource.to_aml_bytes(sink), + ResourceID::PCIDevice(resource) => resource.to_aml_bytes(sink), + ResourceID::VendorSpecific(_, resource) => sink.vec(resource.as_bytes()), + } + } +} + +#[derive(Clone, Debug, Default, AsBytes)] +#[repr(C, packed)] +pub struct CacheResource { + // Resource ID 1 + /// Unique Cache ID from the PPTT table’s Cache Type Structure (Table 5.159 + /// in ACPI Specification 6.5) that this controller is associated with. + cache_id: U32, + _reserved_resource_id_1: U32, + + // Resource ID 2 + _reserved_resource_id_2: U32, +} + +impl CacheResource { + pub fn new(cache_id: u32) -> Self { + Self { + cache_id: cache_id.into(), + _reserved_resource_id_1: 0.into(), + _reserved_resource_id_2: 0.into(), + } + } +} + +#[derive(Clone, Debug, Default, AsBytes)] +#[repr(C, packed)] +pub struct MemoryAffinityStructureResource { + // Resource ID 1 + /// Proximity domain from the SRAT table’s Memory Affinity Structure the + /// resource is associated with. If the SRAT table is not implemented, then + /// this value shall be 0 indicating a UMA memory configuration. + proximity_domain: U32, + _reserved_resource_id_1: U32, + + // Resource ID 2 + _reserved_resource_id_2: U32, + + // Resource Specific Data + /// Indicates the actual raw bandwidth that each unit of bandwidth block + /// corresponds to in bytes/seconds for this specific Resource. + raw_bandwidth_per_block: U64, +} + +impl MemoryAffinityStructureResource { + pub fn new(proximity_domain: u32, raw_bandwidth_per_block: u64) -> Self { + Self { + proximity_domain: proximity_domain.into(), + _reserved_resource_id_1: 0.into(), + _reserved_resource_id_2: 0.into(), + raw_bandwidth_per_block: raw_bandwidth_per_block.into(), + } + } +} + +#[derive(Clone, Debug, Default, AsBytes)] +#[repr(C, packed)] +pub struct ACPIDeviceResource { + // Resource ID 1 + /// _HID value of the ACPI Device corresponding to the Resource. + acpi_hardware_id: U64, + + // Resource ID 2 + /// _UID value of the ACPI Device corresponding to the Resource. + acpi_unique_id: U32, +} + +impl ACPIDeviceResource { + pub fn new(acpi_hardware_id: u64, acpi_unique_id: u32) -> Self { + Self { + acpi_hardware_id: acpi_hardware_id.into(), + acpi_unique_id: acpi_unique_id.into(), + } + } +} + +#[derive(Clone, Debug, Default, AsBytes)] +#[repr(C, packed)] +pub struct PCIDeviceResource { + // Resource ID 1 + /// The Segment/Bus/Device/Function data of the resource that this controller + /// is associated with. + bdf: U32, + _reserved_resource_id_1: U32, + + // Resource ID 2 + _reserved_resource_id_2: U32, +} + +impl PCIDeviceResource { + pub fn new(bdf: u32) -> Self { + Self { + bdf: bdf.into(), + _reserved_resource_id_1: 0.into(), + _reserved_resource_id_2: 0.into(), + } + } +} + +aml_as_bytes!(CacheResource); +aml_as_bytes!(MemoryAffinityStructureResource); +aml_as_bytes!(ACPIDeviceResource); +aml_as_bytes!(PCIDeviceResource); + +assert_same_size!(CacheResource, [u8; 12]); +assert_same_size!(MemoryAffinityStructureResource, [u8; 20]); +assert_same_size!(ACPIDeviceResource, [u8; 12]); +assert_same_size!(PCIDeviceResource, [u8; 12]); #[cfg(test)] mod tests { + use alloc::vec; + use super::*; use crate::gas::*; @@ -161,6 +412,8 @@ mod tests { #[test] fn test_structures() { let mut rqsc = RQSC::new(*b"RQSSCC", *b"SOMETHIN", 0xcafe_d00d); + + // Empty QoS Controller, 28 bytes rqsc.add_controller(QoSController::new( ControllerType::Capacity, gas::GAS::new( @@ -170,10 +423,9 @@ mod tests { AccessSize::QwordAccess, 0x0123_4567_89ab_cdef, ), - ResourceType::Memory, 0x4242_4242, 0x3737_3737, - 0x5656_5656, + 0, )); let mut bytes = Vec::new(); @@ -181,10 +433,57 @@ mod tests { let sum = bytes.iter().fold(0u8, |acc, x| acc.wrapping_add(*x)); assert_eq!(sum, 0); - assert_eq!( - bytes.len(), - TableHeader::len() + 4 + core::mem::size_of::() + assert_eq!(bytes.len(), TableHeader::len() + 4 + 28); + assert_eq!(bytes[0..4], *b"RQSC"); + + let mut controller = QoSController::new( + ControllerType::Capacity, + gas::GAS::new( + AddressSpace::SystemMemory, + 64, + 0, + AccessSize::QwordAccess, + 0x0123_4567_89ab_cdef, + ), + 0x4242_4242, + 0x3737_3737, + 0, ); + + // 28 bytes + controller.add_resource(ResourceStructure::new( + ResourceType::Cache, + 0, + ResourceID::MemoryAffinityStructure(MemoryAffinityStructureResource::new( + 0x2468, 0x1357, + )), + )); + + // 20 bytes + controller.add_resource(ResourceStructure::new( + ResourceType::Cache, + 0, + ResourceID::Cache(CacheResource::new(0)), + )); + + // 24 bytes + controller.add_resource(ResourceStructure::new( + ResourceType::Cache, + 0, + ResourceID::VendorSpecific( + 0xFF, + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + ), + )); + + rqsc.add_controller(controller); + + let mut bytes = Vec::new(); + rqsc.to_aml_bytes(&mut bytes); + let sum = bytes.iter().fold(0u8, |acc, x| acc.wrapping_add(*x)); + + assert_eq!(sum, 0); + assert_eq!(bytes.len(), TableHeader::len() + 4 + 28 + 28 + 28 + 20 + 24); assert_eq!(bytes[0..4], *b"RQSC"); } }