acpi: Implement SPCR on AArch64

This commit implements an AArch64-required ACPI table: Serial
Port Console Redirection Table (SPCR). The table provides
information about the configuration and use of the serial port
or non-legacy UART interface.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
This commit is contained in:
Henry Wang 2021-01-31 14:27:01 +08:00 committed by Sebastien Boeuf
parent 9c5528490e
commit 213da7d862
2 changed files with 81 additions and 6 deletions

View File

@ -22,6 +22,15 @@ impl GenericAddress {
address: u64::from(address),
}
}
pub fn mmio_address<T>(address: u64) -> Self {
GenericAddress {
address_space_id: 0,
register_bit_width: 8 * std::mem::size_of::<T>() as u8,
register_bit_offset: 0,
access_size: std::mem::size_of::<T>() as u8,
address,
}
}
}
pub struct Sdt {

View File

@ -6,9 +6,12 @@ use crate::cpu::CpuManager;
use crate::device_manager::DeviceManager;
use crate::memory_manager::MemoryManager;
use crate::vm::NumaNodes;
#[cfg(target_arch = "x86_64")]
use acpi_tables::sdt::GenericAddress;
use acpi_tables::{aml::Aml, rsdp::Rsdp, sdt::Sdt};
#[cfg(target_arch = "aarch64")]
use arch::aarch64::DeviceInfoForFdt;
#[cfg(target_arch = "aarch64")]
use arch::DeviceType;
use bitflags::bitflags;
use std::sync::{Arc, Mutex};
@ -282,6 +285,34 @@ fn create_gtdt_table() -> Sdt {
gtdt
}
#[cfg(target_arch = "aarch64")]
fn create_spcr_table(base_address: u64, gsi: u32) -> Sdt {
// SPCR
let mut spcr = Sdt::new(*b"SPCR", 80, 2, *b"CLOUDH", *b"CHSPCR ", 1);
// Interface Type
spcr.write(36, 3u8);
// Base Address in format ACPI Generic Address Structure
spcr.write(40, GenericAddress::mmio_address::<u8>(base_address));
// Interrupt Type: Bit[3] ARMH GIC interrupt
spcr.write(52, (1 << 3) as u8);
// Global System Interrupt used by the UART
spcr.write(54, (gsi as u32).to_le());
// Baud Rate: 3 = 9600
spcr.write(58, 3u8);
// Stop Bits: 1 Stop bit
spcr.write(60, 1u8);
// Flow Control: Bit[1] = RTS/CTS hardware flow control
spcr.write(61, (1 << 1) as u8);
// PCI Device ID: Not a PCI device
spcr.write(64, 0xffff_u16);
// PCI Vendor ID: Not a PCI device
spcr.write(66, 0xffff_u16);
spcr.update_checksum();
spcr
}
pub fn create_acpi_tables(
guest_mem: &GuestMemoryMmap,
device_manager: &Arc<Mutex<DeviceManager>>,
@ -339,15 +370,49 @@ pub fn create_acpi_tables(
.write_slice(mcfg.as_slice(), mcfg_offset)
.expect("Error writing MCFG table");
tables.push(mcfg_offset.0);
prev_tbl_len = mcfg.len() as u64;
prev_tbl_off = mcfg_offset;
// SPCR
#[cfg(target_arch = "aarch64")]
{
let is_serial_on = device_manager
.lock()
.unwrap()
.get_device_info()
.clone()
.get(&(DeviceType::Serial, DeviceType::Serial.to_string()))
.is_some();
let serial_device_addr = arch::layout::LEGACY_SERIAL_MAPPED_IO_START;
let serial_device_irq = if is_serial_on {
device_manager
.lock()
.unwrap()
.get_device_info()
.clone()
.get(&(DeviceType::Serial, DeviceType::Serial.to_string()))
.unwrap()
.irq()
} else {
// If serial is turned off, add a fake device with invalid irq.
31
};
let spcr = create_spcr_table(serial_device_addr, serial_device_irq);
let spcr_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(spcr.as_slice(), spcr_offset)
.expect("Error writing SPCR table");
tables.push(spcr_offset.0);
prev_tbl_len = spcr.len() as u64;
prev_tbl_off = spcr_offset;
}
// SRAT and SLIT
// Only created if the NUMA nodes list is not empty.
let (prev_tbl_len, prev_tbl_off) = if numa_nodes.is_empty() {
(mcfg.len(), mcfg_offset)
} else {
if !numa_nodes.is_empty() {
// SRAT
let srat = create_srat_table(numa_nodes);
let srat_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap();
let srat_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(srat.as_slice(), srat_offset)
.expect("Error writing SRAT table");
@ -361,7 +426,8 @@ pub fn create_acpi_tables(
.expect("Error writing SRAT table");
tables.push(slit_offset.0);
(slit.len(), slit_offset)
prev_tbl_len = slit.len() as u64;
prev_tbl_off = slit_offset;
};
// XSDT