Skip to content

Commit 8fba408

Browse files
committed
acpi: add support for SPCR table
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.
1 parent 4390f04 commit 8fba408

File tree

4 files changed

+298
-0
lines changed

4 files changed

+298
-0
lines changed

acpi/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ edition = "2021"
1111

1212
[dependencies]
1313
bit_field = "0.10.2"
14+
bitflags = "2.5.0"
1415
log = "0.4.20"
1516

1617
[features]

acpi/src/address.rs

+10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ pub(crate) struct RawGenericAddress {
1616
pub address: u64,
1717
}
1818

19+
impl RawGenericAddress {
20+
pub(crate) const fn is_empty(&self) -> bool {
21+
self.address_space == 0
22+
&& self.bit_width == 0
23+
&& self.bit_offset == 0
24+
&& self.access_size == 0
25+
&& self.address == 0
26+
}
27+
}
28+
1929
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
2030
pub enum AddressSpace {
2131
SystemMemory,

acpi/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub mod madt;
7575
pub mod mcfg;
7676
pub mod rsdp;
7777
pub mod sdt;
78+
pub mod spcr;
7879

7980
#[cfg(feature = "allocator_api")]
8081
mod managed_slice;

acpi/src/spcr.rs

+286
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
use crate::{
2+
address::{GenericAddress, RawGenericAddress},
3+
AcpiResult,
4+
AcpiTable,
5+
SdtHeader,
6+
Signature,
7+
};
8+
use 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)]
25+
pub 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+
56+
unsafe impl AcpiTable for Spcr {
57+
const SIGNATURE: Signature = Signature::SPCR;
58+
59+
fn header(&self) -> &SdtHeader {
60+
&self.header
61+
}
62+
}
63+
64+
impl Spcr {
65+
/// Gets the type of the register interface.
66+
pub fn interface_type(&self) -> SpcrInteraceType {
67+
SpcrInteraceType::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+
169+
bitflags::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+
186+
bitflags::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+
197+
bitflags::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)]
212+
pub enum SpcrInteraceType {
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+
259+
impl From<u8> for SpcrInteraceType {
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+
}

0 commit comments

Comments
 (0)