Skip to content

Commit 2f93806

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 2f93806

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

acpi/Cargo.toml

Lines changed: 1 addition & 0 deletions
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

Lines changed: 10 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
use crate::{
2+
address::{GenericAddress, RawGenericAddress},
3+
AcpiResult,
4+
AcpiTable,
5+
SdtHeader,
6+
Signature,
7+
};
8+
use core::{
9+
num::{NonZeroU8, NonZeroU32},
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+
/// The type of the register interface
28+
pub interface_type: u8,
29+
_reserved: [u8; 3],
30+
base_address: RawGenericAddress,
31+
interrupt_type: u8,
32+
irq: u8,
33+
global_system_interrupt: u32,
34+
/// The baud rate the BIOS used for redirection.
35+
configured_baud_rate: u8,
36+
pub parity: u8,
37+
pub stop_bits: u8,
38+
flow_control: u8,
39+
terminal_type: u8,
40+
/// Language which the BIOS was redirecting. Must be 0.
41+
pub language: u8,
42+
pci_device_id: u16,
43+
pci_vendor_id: u16,
44+
pci_bus_number: u8,
45+
pci_device_number: u8,
46+
pci_function_number: u8,
47+
pub pci_flags: u32,
48+
/// PCI segment number. systems with fewer than 255 PCI buses, this number
49+
/// will be 0.
50+
pub pci_segment: u8,
51+
uart_clock_freq: u32,
52+
precise_baud_rate: u32,
53+
namespace_string_length: u16,
54+
namespace_string_offset: u16,
55+
}
56+
57+
unsafe impl AcpiTable for Spcr {
58+
const SIGNATURE: Signature = Signature::SPCR;
59+
60+
fn header(&self) -> &SdtHeader {
61+
&self.header
62+
}
63+
}
64+
65+
impl Spcr {
66+
/// The base address of the Serial Port register set, if if console
67+
/// redirection is enabled.
68+
pub fn base_address(&self) -> Option<AcpiResult<GenericAddress>> {
69+
(!self.base_address.is_empty()).then(|| GenericAddress::from_raw(self.base_address))
70+
}
71+
72+
fn configured_baud_rate(&self) -> Option<NonZeroU32> {
73+
match self.configured_baud_rate {
74+
3 => unsafe { Some(NonZeroU32::new_unchecked(9600)) },
75+
4 => unsafe { Some(NonZeroU32::new_unchecked(19200)) },
76+
6 => unsafe { Some(NonZeroU32::new_unchecked(57600)) },
77+
7 => unsafe { Some(NonZeroU32::new_unchecked(115200)) },
78+
_ => None,
79+
}
80+
}
81+
82+
/// The baud rate the BIOS used for redirection, if configured.
83+
pub fn baud_rate(&self) -> Option<NonZeroU32> {
84+
NonZeroU32::new(self.precise_baud_rate)
85+
.or_else(|| self.configured_baud_rate())
86+
}
87+
88+
/// Flow control flags for the UART.
89+
pub fn flow_control(&self) -> SpcrFlowControl {
90+
SpcrFlowControl::from_bits_truncate(self.flow_control)
91+
}
92+
93+
/// Interrupt type(s) used by the UART.
94+
pub fn interrupt_type(&self) -> SpcrInterruptType {
95+
SpcrInterruptType::from_bits_truncate(self.interrupt_type)
96+
}
97+
98+
/// The PC-AT-compatible IRQ used by the UART, if the UART supports it.
99+
/// Support is indicated by the [`interrupt_type`](Self::interrupt_type).
100+
pub fn irq(&self) -> Option<u8> {
101+
self.interrupt_type().contains(SpcrInterruptType::DUAL_8259).then_some(self.irq)
102+
}
103+
104+
/// The Global System Interrupt (GSIV) used by the UART, if the UART
105+
/// supports it. Support is indicated by the
106+
/// [`interrupt_type`](Self::interrupt_type).
107+
pub fn global_system_interrupt(&self) -> Option<u32> {
108+
if self.interrupt_type().difference(SpcrInterruptType::DUAL_8259).is_empty() {
109+
return None;
110+
}
111+
Some(self.global_system_interrupt)
112+
}
113+
114+
/// The terminal protocol the BIOS was using for console redirection.
115+
pub fn terminal_type(&self) -> SpcrTerminalType {
116+
SpcrTerminalType::from_bits_truncate(self.terminal_type)
117+
}
118+
119+
/// If the UART is a PCI device, returns its Device ID.
120+
pub fn pci_device_id(&self) -> Option<u16> {
121+
(self.pci_device_id != 0xffff).then_some(self.pci_device_id)
122+
}
123+
124+
/// If the UART is a PCI device, returns its Vendor ID.
125+
pub fn pci_vendor_id(&self) -> Option<u16> {
126+
(self.pci_vendor_id != 0xffff).then_some(self.pci_vendor_id)
127+
}
128+
129+
/// If the UART is a PCI device, returns its bus number.
130+
pub fn pci_bus_number(&self) -> Option<NonZeroU8> {
131+
NonZeroU8::new(self.pci_bus_number)
132+
}
133+
134+
/// If the UART is a PCI device, returns its device number.
135+
pub fn pci_device_number(&self) -> Option<NonZeroU8> {
136+
NonZeroU8::new(self.pci_device_number)
137+
}
138+
139+
/// If the UART is a PCI device, returns its function number.
140+
pub fn pci_function_number(&self) -> Option<NonZeroU8> {
141+
NonZeroU8::new(self.pci_function_number)
142+
}
143+
144+
/// The UART clock frequency in Hz, if it can be determined.
145+
pub const fn uart_clock_frequency(&self) -> Option<NonZeroU32> {
146+
if self.header.revision <= 2 {
147+
return None;
148+
}
149+
NonZeroU32::new(self.uart_clock_freq)
150+
}
151+
152+
/// An ASCII string to uniquely identify this device. This string consists
153+
/// of a fully qualified reference to the object that represents this
154+
/// device in the ACPI namespace. If no namespace device exists,
155+
/// the namespace string must only contain a single '.'.
156+
pub fn namespace_string(&self) -> Result<&str, Utf8Error> {
157+
let start = ptr::from_ref(self).cast::<u8>();
158+
let bytes = unsafe {
159+
let str_start = start.add(self.namespace_string_offset as usize);
160+
slice::from_raw_parts(str_start, self.namespace_string_length as usize)
161+
};
162+
str::from_utf8(bytes)
163+
}
164+
}
165+
166+
bitflags::bitflags! {
167+
/// Interrupt type(s) used by an UART.
168+
#[derive(Clone, Copy, Debug)]
169+
pub struct SpcrInterruptType: u8 {
170+
/// PC-AT-compatible dual-8259 IRQ interrupt.
171+
const DUAL_8259 = 1 << 0;
172+
/// I/O APIC interrupt (Global System Interrupt).
173+
const IO_APIC = 1 << 1;
174+
/// I/O SAPIC interrupt (Global System Interrupt).
175+
const IO_SAPIC = 1 << 2;
176+
/// ARMH GIC interrupt (Global System Interrupt).
177+
const ARMH_GIC = 1 << 3;
178+
/// RISC-V PLIC/APLIC interrupt (Global System Interrupt).
179+
const RISCV_PLIC = 1 << 4;
180+
}
181+
}
182+
183+
bitflags::bitflags! {
184+
/// The terminal protocol the BIOS uses for console redirection.
185+
#[derive(Clone, Copy, Debug)]
186+
pub struct SpcrTerminalType: u8 {
187+
const VT1000 = 1 << 0;
188+
const EXTENDED_VT1000 = 1 << 1;
189+
const VT_UTF8 = 1 << 2;
190+
const ANSI = 1 << 3;
191+
}
192+
}
193+
194+
bitflags::bitflags! {
195+
/// Flow control flags for the UART.
196+
#[derive(Clone, Copy, Debug)]
197+
pub struct SpcrFlowControl: u8 {
198+
/// DCD required for transmit
199+
const DCD = 1 << 0;
200+
/// RTS/CTS hardware flow control
201+
const RTS_CTS = 1 << 1;
202+
/// XON/XOFF software control
203+
const XON_XOFF = 1 << 2;
204+
}
205+
}

0 commit comments

Comments
 (0)