Skip to main content

acpi_lite/
debug_port.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use crate::acpi_lite::printf;
8use crate::binary_reader::{BinaryReader, Unaligned};
9use crate::structures::{
10    ACPI_ADDR_SPACE_IO, ACPI_ADDR_SPACE_MEMORY, ACPI_DBG2_SUBTYPE_16550_COMPATIBLE,
11    ACPI_DBG2_TYPE_SERIAL_PORT, AcpiDbg2Device, AcpiDbg2Table, AcpiGenericAddress,
12};
13use crate::{AcpiParserInterface, get_table_by_type};
14use zx_status::Status;
15
16#[derive(Copy, Clone, Debug, Eq, PartialEq)]
17pub enum AcpiDebugPortType {
18    Mmio,
19    Pio,
20}
21
22#[derive(Copy, Clone, Debug, Eq, PartialEq)]
23// Describes a dedicated system debug port suitable for low-level
24// debugging and diagnostics.
25//
26// Currently, we only support a 16550-compatible UART using MMIO or PIO.
27pub struct AcpiDebugPortDescriptor {
28    pub r#type: AcpiDebugPortType,
29
30    // Physical address of the 16550 MMIO registers for Type::Mmio.
31    // IO port base for Type::Pio.
32    pub address: usize,
33    pub length: u32,
34}
35
36// Parse an AcpiDbg2Table ACPI structure.
37pub fn parse_acpi_dbg2_table(
38    debug_table: &AcpiDbg2Table,
39) -> Result<AcpiDebugPortDescriptor, Status> {
40    let num_entries = debug_table.num_entries;
41    if num_entries < 1 {
42        unsafe {
43            printf(b"acpi_lite: DBG2 table contains no debug ports.\n\0".as_ptr()
44                as *const core::ffi::c_char);
45        }
46        return Err(Status::NOT_FOUND);
47    }
48
49    let offset = debug_table.offset as usize;
50    let mut reader = BinaryReader::from_variable_sized(debug_table);
51    if !reader.skip_bytes(offset) {
52        return Err(Status::INTERNAL);
53    }
54
55    let device = reader.read::<AcpiDbg2Device>().ok_or(Status::INTERNAL)?;
56
57    let port_type = device.port_type;
58    let port_subtype = device.port_subtype;
59    if port_type != ACPI_DBG2_TYPE_SERIAL_PORT || port_subtype != ACPI_DBG2_SUBTYPE_16550_COMPATIBLE
60    {
61        unsafe {
62            printf(
63                b"acpi_lite: DBG2 debug port unsupported. (type=%x, subtype=%x)\n\0".as_ptr()
64                    as *const core::ffi::c_char,
65                port_type as core::ffi::c_uint,
66                port_subtype as core::ffi::c_uint,
67            );
68        }
69        return Err(Status::NOT_SUPPORTED);
70    }
71
72    if device.register_count < 1 {
73        unsafe {
74            printf(b"acpi_lite: DBG2 debug port doesn't have any registers defined.\n\0".as_ptr()
75                as *const core::ffi::c_char);
76        }
77        return Err(Status::NOT_SUPPORTED);
78    }
79
80    let base_address_offset = device.base_address_offset as usize;
81    let mut reader = BinaryReader::from_variable_sized(device);
82    if !reader.skip_bytes(base_address_offset) {
83        return Err(Status::INTERNAL);
84    }
85    let address = reader.read_fixed_length::<AcpiGenericAddress>().ok_or(Status::INTERNAL)?;
86
87    let address_size_offset = device.address_size_offset as usize;
88    let mut reader = BinaryReader::from_variable_sized(device);
89    if !reader.skip_bytes(address_size_offset) {
90        return Err(Status::INTERNAL);
91    }
92    let length = reader.read_fixed_length::<Unaligned<u32>>().ok_or(Status::INTERNAL)?;
93
94    let addr = address.address as usize;
95    let mut result = AcpiDebugPortDescriptor {
96        r#type: AcpiDebugPortType::Mmio,
97        address: addr,
98        length: length.0,
99    };
100
101    match address.address_space_id {
102        ACPI_ADDR_SPACE_MEMORY => {
103            result.r#type = AcpiDebugPortType::Mmio;
104        }
105        ACPI_ADDR_SPACE_IO => {
106            result.r#type = AcpiDebugPortType::Pio;
107        }
108        _ => {
109            unsafe {
110                printf(
111                    b"acpi_lite: Address space unsupported (space_id=%x)\n\0".as_ptr()
112                        as *const core::ffi::c_char,
113                    address.address_space_id as core::ffi::c_uint,
114                );
115            }
116            return Err(Status::NOT_SUPPORTED);
117        }
118    }
119
120    Ok(result)
121}
122
123// Lookup low-level debug port information.
124pub fn get_debug_port(parser: &dyn AcpiParserInterface) -> Result<AcpiDebugPortDescriptor, Status> {
125    let debug_table = get_table_by_type::<AcpiDbg2Table>(parser).ok_or_else(|| {
126        unsafe {
127            printf(b"acpi_lite: could not find debug port (v2) ACPI entry\n\0".as_ptr()
128                as *const core::ffi::c_char);
129        }
130        Status::NOT_FOUND
131    })?;
132
133    parse_acpi_dbg2_table(debug_table)
134}