acpi/
spcr.rs

1use crate::{
2    address::{GenericAddress, RawGenericAddress},
3    AcpiResult,
4    AcpiTable,
5    SdtHeader,
6    Signature,
7};
8use core::{
9    num::{NonZeroU32, NonZeroU8},
10    ptr,
11    slice,
12    str::{self, Utf8Error},
13};
14
15/// Serial Port Console Redirection (SPCR) Table.
16///
17/// The table provides information about the configuration and use of the
18/// serial port or non-legacy UART interface. On a system where the BIOS or
19/// system firmware uses the serial port for console input/output, this table
20/// should be used to convey information about the settings.
21///
22/// For more information, see [the official documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table).
23#[repr(C, packed)]
24#[derive(Debug)]
25pub struct Spcr {
26    pub header: SdtHeader,
27    interface_type: u8,
28    _reserved: [u8; 3],
29    base_address: RawGenericAddress,
30    interrupt_type: u8,
31    irq: u8,
32    global_system_interrupt: u32,
33    /// The baud rate the BIOS used for redirection.
34    configured_baud_rate: u8,
35    pub parity: u8,
36    pub stop_bits: u8,
37    flow_control: u8,
38    terminal_type: u8,
39    /// Language which the BIOS was redirecting. Must be 0.
40    pub language: u8,
41    pci_device_id: u16,
42    pci_vendor_id: u16,
43    pci_bus_number: u8,
44    pci_device_number: u8,
45    pci_function_number: u8,
46    pub pci_flags: u32,
47    /// PCI segment number. systems with fewer than 255 PCI buses, this number
48    /// will be 0.
49    pub pci_segment: u8,
50    uart_clock_freq: u32,
51    precise_baud_rate: u32,
52    namespace_string_length: u16,
53    namespace_string_offset: u16,
54}
55
56unsafe impl AcpiTable for Spcr {
57    const SIGNATURE: Signature = Signature::SPCR;
58
59    fn header(&self) -> &SdtHeader {
60        &self.header
61    }
62}
63
64impl Spcr {
65    /// Gets the type of the register interface.
66    pub fn interface_type(&self) -> SpcrInterfaceType {
67        SpcrInterfaceType::from(self.interface_type)
68    }
69
70    /// The base address of the Serial Port register set, if if console
71    /// redirection is enabled.
72    pub fn base_address(&self) -> Option<AcpiResult<GenericAddress>> {
73        (!self.base_address.is_empty()).then(|| GenericAddress::from_raw(self.base_address))
74    }
75
76    fn configured_baud_rate(&self) -> Option<NonZeroU32> {
77        match self.configured_baud_rate {
78            3 => unsafe { Some(NonZeroU32::new_unchecked(9600)) },
79            4 => unsafe { Some(NonZeroU32::new_unchecked(19200)) },
80            6 => unsafe { Some(NonZeroU32::new_unchecked(57600)) },
81            7 => unsafe { Some(NonZeroU32::new_unchecked(115200)) },
82            _ => None,
83        }
84    }
85
86    /// The baud rate the BIOS used for redirection, if configured.
87    pub fn baud_rate(&self) -> Option<NonZeroU32> {
88        NonZeroU32::new(self.precise_baud_rate).or_else(|| self.configured_baud_rate())
89    }
90
91    /// Flow control flags for the UART.
92    pub fn flow_control(&self) -> SpcrFlowControl {
93        SpcrFlowControl::from_bits_truncate(self.flow_control)
94    }
95
96    /// Interrupt type(s) used by the UART.
97    pub fn interrupt_type(&self) -> SpcrInterruptType {
98        SpcrInterruptType::from_bits_truncate(self.interrupt_type)
99    }
100
101    /// The PC-AT-compatible IRQ used by the UART, if the UART supports it.
102    /// Support is indicated by the [`interrupt_type`](Self::interrupt_type).
103    pub fn irq(&self) -> Option<u8> {
104        self.interrupt_type().contains(SpcrInterruptType::DUAL_8259).then_some(self.irq)
105    }
106
107    /// The Global System Interrupt (GSIV) used by the UART, if the UART
108    /// supports it. Support is indicated by the
109    /// [`interrupt_type`](Self::interrupt_type).
110    pub fn global_system_interrupt(&self) -> Option<u32> {
111        if self.interrupt_type().difference(SpcrInterruptType::DUAL_8259).is_empty() {
112            return None;
113        }
114        Some(self.global_system_interrupt)
115    }
116
117    /// The terminal protocol the BIOS was using for console redirection.
118    pub fn terminal_type(&self) -> SpcrTerminalType {
119        SpcrTerminalType::from_bits_truncate(self.terminal_type)
120    }
121
122    /// If the UART is a PCI device, returns its Device ID.
123    pub fn pci_device_id(&self) -> Option<u16> {
124        (self.pci_device_id != 0xffff).then_some(self.pci_device_id)
125    }
126
127    /// If the UART is a PCI device, returns its Vendor ID.
128    pub fn pci_vendor_id(&self) -> Option<u16> {
129        (self.pci_vendor_id != 0xffff).then_some(self.pci_vendor_id)
130    }
131
132    /// If the UART is a PCI device, returns its bus number.
133    pub fn pci_bus_number(&self) -> Option<NonZeroU8> {
134        NonZeroU8::new(self.pci_bus_number)
135    }
136
137    /// If the UART is a PCI device, returns its device number.
138    pub fn pci_device_number(&self) -> Option<NonZeroU8> {
139        NonZeroU8::new(self.pci_device_number)
140    }
141
142    /// If the UART is a PCI device, returns its function number.
143    pub fn pci_function_number(&self) -> Option<NonZeroU8> {
144        NonZeroU8::new(self.pci_function_number)
145    }
146
147    /// The UART clock frequency in Hz, if it can be determined.
148    pub const fn uart_clock_frequency(&self) -> Option<NonZeroU32> {
149        if self.header.revision <= 2 {
150            return None;
151        }
152        NonZeroU32::new(self.uart_clock_freq)
153    }
154
155    /// An ASCII string to uniquely identify this device. This string consists
156    /// of a fully qualified reference to the object that represents this
157    /// device in the ACPI namespace. If no namespace device exists,
158    /// the namespace string must only contain a single '.'.
159    pub fn namespace_string(&self) -> Result<&str, Utf8Error> {
160        let start = ptr::from_ref(self).cast::<u8>();
161        let bytes = unsafe {
162            let str_start = start.add(self.namespace_string_offset as usize);
163            slice::from_raw_parts(str_start, self.namespace_string_length as usize)
164        };
165        str::from_utf8(bytes)
166    }
167}
168
169bitflags::bitflags! {
170    /// Interrupt type(s) used by an UART.
171    #[derive(Clone, Copy, Debug)]
172    pub struct SpcrInterruptType: u8 {
173        /// PC-AT-compatible dual-8259 IRQ interrupt.
174        const DUAL_8259 = 1 << 0;
175        /// I/O APIC interrupt (Global System Interrupt).
176        const IO_APIC = 1 << 1;
177        /// I/O SAPIC interrupt (Global System Interrupt).
178        const IO_SAPIC = 1 << 2;
179        /// ARMH GIC interrupt (Global System Interrupt).
180        const ARMH_GIC = 1 << 3;
181        /// RISC-V PLIC/APLIC interrupt (Global System Interrupt).
182        const RISCV_PLIC = 1 << 4;
183    }
184}
185
186bitflags::bitflags! {
187    /// The terminal protocol the BIOS uses for console redirection.
188    #[derive(Clone, Copy, Debug)]
189    pub struct SpcrTerminalType: u8 {
190        const VT1000 = 1 << 0;
191        const EXTENDED_VT1000 = 1 << 1;
192        const VT_UTF8 = 1 << 2;
193        const ANSI = 1 << 3;
194    }
195}
196
197bitflags::bitflags! {
198    /// Flow control flags for the UART.
199    #[derive(Clone, Copy, Debug)]
200    pub struct SpcrFlowControl: u8 {
201        /// DCD required for transmit
202        const DCD = 1 << 0;
203        /// RTS/CTS hardware flow control
204        const RTS_CTS = 1 << 1;
205        /// XON/XOFF software control
206        const XON_XOFF = 1 << 2;
207    }
208}
209
210#[repr(u8)]
211#[derive(Clone, Copy, Debug)]
212pub enum SpcrInterfaceType {
213    /// Full 16550 interface
214    Full16550,
215    /// Full 16450 interface (must also accept writing to the 16550 FCR register).
216    Full16450,
217    /// MAX311xE SPI UART
218    MAX311xE,
219    /// Arm PL011 UART
220    ArmPL011,
221    /// MSM8x60 (e.g. 8960)
222    MSM8x60,
223    /// Nvidia 16550
224    Nvidia16550,
225    /// TI OMAP
226    TiOmap,
227    /// APM88xxxx
228    APM88xxxx,
229    /// MSM8974
230    Msm8974,
231    /// SAM5250
232    Sam5250,
233    /// Intel USIF
234    IntelUSIF,
235    /// i.MX 6
236    Imx6,
237    /// (deprecated) Arm SBSA (2.x only) Generic UART supporting only 32-bit accesses
238    ArmSBSAGeneric32bit,
239    /// Arm SBSA Generic UART
240    ArmSBSAGeneric,
241    /// Arm DCC
242    ArmDCC,
243    /// VCM2835
244    Bcm2835,
245    /// SDM845 with clock rate of 1.8432 MHz
246    Sdm845_18432,
247    /// 16550-compatible with parameters defined in Generic Address Structure
248    Generic16550,
249    /// SDM845 with clock rate of 7.372 MHz
250    Sdm845_7372,
251    /// Intel LPSS
252    IntelLPSS,
253    /// RISC-V SBI console (any supported SBI mechanism)
254    RiscVSbi,
255    /// Unknown interface
256    Unknown(u8),
257}
258
259impl From<u8> for SpcrInterfaceType {
260    fn from(val: u8) -> Self {
261        match val {
262            0x00 => Self::Full16550,
263            0x01 => Self::Full16450,
264            0x02 => Self::MAX311xE,
265            0x03 => Self::ArmPL011,
266            0x04 => Self::MSM8x60,
267            0x05 => Self::Nvidia16550,
268            0x06 => Self::TiOmap,
269            0x08 => Self::APM88xxxx,
270            0x09 => Self::Msm8974,
271            0x0A => Self::Sam5250,
272            0x0B => Self::IntelUSIF,
273            0x0C => Self::Imx6,
274            0x0D => Self::ArmSBSAGeneric32bit,
275            0x0E => Self::ArmSBSAGeneric,
276            0x0F => Self::ArmDCC,
277            0x10 => Self::Bcm2835,
278            0x11 => Self::Sdm845_18432,
279            0x12 => Self::Generic16550,
280            0x13 => Self::Sdm845_7372,
281            0x14 => Self::IntelLPSS,
282            0x15 => Self::RiscVSbi,
283            _ => Self::Unknown(val),
284        }
285    }
286}