Parcourir la source

acpi: add support for SPCR table (#216)

Add support for the Serial Port Console Redirection (SPCR). The table
provides information about the configuration and use of the serial
port or non-legacy UART interface.
Carlos López il y a 9 mois
Parent
commit
20486f85ab
4 fichiers modifiés avec 298 ajouts et 0 suppressions
  1. 1 0
      acpi/Cargo.toml
  2. 10 0
      acpi/src/address.rs
  3. 1 0
      acpi/src/lib.rs
  4. 286 0
      acpi/src/spcr.rs

+ 1 - 0
acpi/Cargo.toml

@@ -11,6 +11,7 @@ edition = "2021"
 
 [dependencies]
 bit_field = "0.10.2"
+bitflags = "2.5.0"
 log = "0.4.20"
 
 [features]

+ 10 - 0
acpi/src/address.rs

@@ -16,6 +16,16 @@ pub(crate) struct RawGenericAddress {
     pub address: u64,
 }
 
+impl RawGenericAddress {
+    pub(crate) const fn is_empty(&self) -> bool {
+        self.address_space == 0
+            && self.bit_width == 0
+            && self.bit_offset == 0
+            && self.access_size == 0
+            && self.address == 0
+    }
+}
+
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
 pub enum AddressSpace {
     SystemMemory,

+ 1 - 0
acpi/src/lib.rs

@@ -75,6 +75,7 @@ pub mod madt;
 pub mod mcfg;
 pub mod rsdp;
 pub mod sdt;
+pub mod spcr;
 
 #[cfg(feature = "allocator_api")]
 mod managed_slice;

+ 286 - 0
acpi/src/spcr.rs

@@ -0,0 +1,286 @@
+use crate::{
+    address::{GenericAddress, RawGenericAddress},
+    AcpiResult,
+    AcpiTable,
+    SdtHeader,
+    Signature,
+};
+use core::{
+    num::{NonZeroU32, NonZeroU8},
+    ptr,
+    slice,
+    str::{self, Utf8Error},
+};
+
+/// Serial Port Console Redirection (SPCR) Table.
+///
+/// The table provides information about the configuration and use of the
+/// serial port or non-legacy UART interface. On a system where the BIOS or
+/// system firmware uses the serial port for console input/output, this table
+/// should be used to convey information about the settings.
+///
+/// For more information, see [the official documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table).
+#[repr(C, packed)]
+#[derive(Debug)]
+pub struct Spcr {
+    pub header: SdtHeader,
+    interface_type: u8,
+    _reserved: [u8; 3],
+    base_address: RawGenericAddress,
+    interrupt_type: u8,
+    irq: u8,
+    global_system_interrupt: u32,
+    /// The baud rate the BIOS used for redirection.
+    configured_baud_rate: u8,
+    pub parity: u8,
+    pub stop_bits: u8,
+    flow_control: u8,
+    terminal_type: u8,
+    /// Language which the BIOS was redirecting. Must be 0.
+    pub language: u8,
+    pci_device_id: u16,
+    pci_vendor_id: u16,
+    pci_bus_number: u8,
+    pci_device_number: u8,
+    pci_function_number: u8,
+    pub pci_flags: u32,
+    /// PCI segment number. systems with fewer than 255 PCI buses, this number
+    /// will be 0.
+    pub pci_segment: u8,
+    uart_clock_freq: u32,
+    precise_baud_rate: u32,
+    namespace_string_length: u16,
+    namespace_string_offset: u16,
+}
+
+unsafe impl AcpiTable for Spcr {
+    const SIGNATURE: Signature = Signature::SPCR;
+
+    fn header(&self) -> &SdtHeader {
+        &self.header
+    }
+}
+
+impl Spcr {
+    /// Gets the type of the register interface.
+    pub fn interface_type(&self) -> SpcrInteraceType {
+        SpcrInteraceType::from(self.interface_type)
+    }
+
+    /// The base address of the Serial Port register set, if if console
+    /// redirection is enabled.
+    pub fn base_address(&self) -> Option<AcpiResult<GenericAddress>> {
+        (!self.base_address.is_empty()).then(|| GenericAddress::from_raw(self.base_address))
+    }
+
+    fn configured_baud_rate(&self) -> Option<NonZeroU32> {
+        match self.configured_baud_rate {
+            3 => unsafe { Some(NonZeroU32::new_unchecked(9600)) },
+            4 => unsafe { Some(NonZeroU32::new_unchecked(19200)) },
+            6 => unsafe { Some(NonZeroU32::new_unchecked(57600)) },
+            7 => unsafe { Some(NonZeroU32::new_unchecked(115200)) },
+            _ => None,
+        }
+    }
+
+    /// The baud rate the BIOS used for redirection, if configured.
+    pub fn baud_rate(&self) -> Option<NonZeroU32> {
+        NonZeroU32::new(self.precise_baud_rate).or_else(|| self.configured_baud_rate())
+    }
+
+    /// Flow control flags for the UART.
+    pub fn flow_control(&self) -> SpcrFlowControl {
+        SpcrFlowControl::from_bits_truncate(self.flow_control)
+    }
+
+    /// Interrupt type(s) used by the UART.
+    pub fn interrupt_type(&self) -> SpcrInterruptType {
+        SpcrInterruptType::from_bits_truncate(self.interrupt_type)
+    }
+
+    /// The PC-AT-compatible IRQ used by the UART, if the UART supports it.
+    /// Support is indicated by the [`interrupt_type`](Self::interrupt_type).
+    pub fn irq(&self) -> Option<u8> {
+        self.interrupt_type().contains(SpcrInterruptType::DUAL_8259).then_some(self.irq)
+    }
+
+    /// The Global System Interrupt (GSIV) used by the UART, if the UART
+    /// supports it. Support is indicated by the
+    /// [`interrupt_type`](Self::interrupt_type).
+    pub fn global_system_interrupt(&self) -> Option<u32> {
+        if self.interrupt_type().difference(SpcrInterruptType::DUAL_8259).is_empty() {
+            return None;
+        }
+        Some(self.global_system_interrupt)
+    }
+
+    /// The terminal protocol the BIOS was using for console redirection.
+    pub fn terminal_type(&self) -> SpcrTerminalType {
+        SpcrTerminalType::from_bits_truncate(self.terminal_type)
+    }
+
+    /// If the UART is a PCI device, returns its Device ID.
+    pub fn pci_device_id(&self) -> Option<u16> {
+        (self.pci_device_id != 0xffff).then_some(self.pci_device_id)
+    }
+
+    /// If the UART is a PCI device, returns its Vendor ID.
+    pub fn pci_vendor_id(&self) -> Option<u16> {
+        (self.pci_vendor_id != 0xffff).then_some(self.pci_vendor_id)
+    }
+
+    /// If the UART is a PCI device, returns its bus number.
+    pub fn pci_bus_number(&self) -> Option<NonZeroU8> {
+        NonZeroU8::new(self.pci_bus_number)
+    }
+
+    /// If the UART is a PCI device, returns its device number.
+    pub fn pci_device_number(&self) -> Option<NonZeroU8> {
+        NonZeroU8::new(self.pci_device_number)
+    }
+
+    /// If the UART is a PCI device, returns its function number.
+    pub fn pci_function_number(&self) -> Option<NonZeroU8> {
+        NonZeroU8::new(self.pci_function_number)
+    }
+
+    /// The UART clock frequency in Hz, if it can be determined.
+    pub const fn uart_clock_frequency(&self) -> Option<NonZeroU32> {
+        if self.header.revision <= 2 {
+            return None;
+        }
+        NonZeroU32::new(self.uart_clock_freq)
+    }
+
+    /// An ASCII string to uniquely identify this device. This string consists
+    /// of a fully qualified reference to the object that represents this
+    /// device in the ACPI namespace. If no namespace device exists,
+    /// the namespace string must only contain a single '.'.
+    pub fn namespace_string(&self) -> Result<&str, Utf8Error> {
+        let start = ptr::from_ref(self).cast::<u8>();
+        let bytes = unsafe {
+            let str_start = start.add(self.namespace_string_offset as usize);
+            slice::from_raw_parts(str_start, self.namespace_string_length as usize)
+        };
+        str::from_utf8(bytes)
+    }
+}
+
+bitflags::bitflags! {
+    /// Interrupt type(s) used by an UART.
+    #[derive(Clone, Copy, Debug)]
+    pub struct SpcrInterruptType: u8 {
+        /// PC-AT-compatible dual-8259 IRQ interrupt.
+        const DUAL_8259 = 1 << 0;
+        /// I/O APIC interrupt (Global System Interrupt).
+        const IO_APIC = 1 << 1;
+        /// I/O SAPIC interrupt (Global System Interrupt).
+        const IO_SAPIC = 1 << 2;
+        /// ARMH GIC interrupt (Global System Interrupt).
+        const ARMH_GIC = 1 << 3;
+        /// RISC-V PLIC/APLIC interrupt (Global System Interrupt).
+        const RISCV_PLIC = 1 << 4;
+    }
+}
+
+bitflags::bitflags! {
+    /// The terminal protocol the BIOS uses for console redirection.
+    #[derive(Clone, Copy, Debug)]
+    pub struct SpcrTerminalType: u8 {
+        const VT1000 = 1 << 0;
+        const EXTENDED_VT1000 = 1 << 1;
+        const VT_UTF8 = 1 << 2;
+        const ANSI = 1 << 3;
+    }
+}
+
+bitflags::bitflags! {
+    /// Flow control flags for the UART.
+    #[derive(Clone, Copy, Debug)]
+    pub struct SpcrFlowControl: u8 {
+        /// DCD required for transmit
+        const DCD = 1 << 0;
+        /// RTS/CTS hardware flow control
+        const RTS_CTS = 1 << 1;
+        /// XON/XOFF software control
+        const XON_XOFF = 1 << 2;
+    }
+}
+
+#[repr(u8)]
+#[derive(Clone, Copy, Debug)]
+pub enum SpcrInteraceType {
+    /// Full 16550 interface
+    Full16550,
+    /// Full 16450 interface (must also accept writing to the 16550 FCR register).
+    Full16450,
+    /// MAX311xE SPI UART
+    MAX311xE,
+    /// Arm PL011 UART
+    ArmPL011,
+    /// MSM8x60 (e.g. 8960)
+    MSM8x60,
+    /// Nvidia 16550
+    Nvidia16550,
+    /// TI OMAP
+    TiOmap,
+    /// APM88xxxx
+    APM88xxxx,
+    /// MSM8974
+    Msm8974,
+    /// SAM5250
+    Sam5250,
+    /// Intel USIF
+    IntelUSIF,
+    /// i.MX 6
+    Imx6,
+    /// (deprecated) Arm SBSA (2.x only) Generic UART supporting only 32-bit accesses
+    ArmSBSAGeneric32bit,
+    /// Arm SBSA Generic UART
+    ArmSBSAGeneric,
+    /// Arm DCC
+    ArmDCC,
+    /// VCM2835
+    Bcm2835,
+    /// SDM845 with clock rate of 1.8432 MHz
+    Sdm845_18432,
+    /// 16550-compatible with parameters defined in Generic Address Structure
+    Generic16550,
+    /// SDM845 with clock rate of 7.372 MHz
+    Sdm845_7372,
+    /// Intel LPSS
+    IntelLPSS,
+    /// RISC-V SBI console (any supported SBI mechanism)
+    RiscVSbi,
+    /// Unknown interface
+    Unknown(u8),
+}
+
+impl From<u8> for SpcrInteraceType {
+    fn from(val: u8) -> Self {
+        match val {
+            0x00 => Self::Full16550,
+            0x01 => Self::Full16450,
+            0x02 => Self::MAX311xE,
+            0x03 => Self::ArmPL011,
+            0x04 => Self::MSM8x60,
+            0x05 => Self::Nvidia16550,
+            0x06 => Self::TiOmap,
+            0x08 => Self::APM88xxxx,
+            0x09 => Self::Msm8974,
+            0x0A => Self::Sam5250,
+            0x0B => Self::IntelUSIF,
+            0x0C => Self::Imx6,
+            0x0D => Self::ArmSBSAGeneric32bit,
+            0x0E => Self::ArmSBSAGeneric,
+            0x0F => Self::ArmDCC,
+            0x10 => Self::Bcm2835,
+            0x11 => Self::Sdm845_18432,
+            0x12 => Self::Generic16550,
+            0x13 => Self::Sdm845_7372,
+            0x14 => Self::IntelLPSS,
+            0x15 => Self::RiscVSbi,
+            _ => Self::Unknown(val),
+        }
+    }
+}