Skip to content

acpi: add support for SPCR table #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions acpi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ edition = "2021"

[dependencies]
bit_field = "0.10.2"
bitflags = "2.5.0"
log = "0.4.20"

[features]
Expand Down
10 changes: 10 additions & 0 deletions acpi/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions acpi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
286 changes: 286 additions & 0 deletions acpi/src/spcr.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
Loading