Skip to content

Commit 8e6c0df

Browse files
committed
acpi: define x86 devices in ACPI DSDT table
Use AML bytecode in DSDT ACPI table to define VirtIO devices as well as passing them as kernel command line arguments. Also, pass AML for legacy devices, as the kernel doesn't pick up the console or i8042 unless we do. For the time being, we are keeping the old way of booting, i.e. MPTable plus VirtIO devices via command line arguments to allow for some sort of backwards compatibility with kernels that are not built with ACPI support. Signed-off-by: Babis Chalios <[email protected]>
1 parent 7330aa2 commit 8e6c0df

File tree

5 files changed

+141
-8
lines changed

5 files changed

+141
-8
lines changed

src/vmm/src/acpi/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use vm_allocator::AllocPolicy;
99
use crate::acpi::x86_64::{
1010
apic_addr, rsdp_addr, setup_arch_dsdt, setup_arch_fadt, setup_interrupt_controllers,
1111
};
12+
use crate::device_manager::mmio::MMIODeviceManager;
1213
use crate::device_manager::resources::ResourceAllocator;
1314
use crate::vstate::memory::{GuestAddress, GuestMemoryMmap};
1415
use crate::Vcpu;
@@ -73,9 +74,12 @@ impl<'a> AcpiTableWriter<'a> {
7374
}
7475

7576
/// Build the DSDT table for the guest
76-
fn build_dsdt(&mut self) -> Result<u64, AcpiError> {
77+
fn build_dsdt(&mut self, mmio_device_manager: &MMIODeviceManager) -> Result<u64, AcpiError> {
7778
let mut dsdt_data = Vec::new();
7879

80+
// Virtio-devices DSDT data
81+
dsdt_data.extend_from_slice(&mmio_device_manager.dsdt_data);
82+
7983
// Architecture specific DSDT data
8084
setup_arch_dsdt(&mut dsdt_data);
8185

@@ -150,14 +154,15 @@ impl<'a> AcpiTableWriter<'a> {
150154
pub(crate) fn create_acpi_tables(
151155
mem: &GuestMemoryMmap,
152156
resource_allocator: &mut ResourceAllocator,
157+
mmio_device_manager: &MMIODeviceManager,
153158
vcpus: &[Vcpu],
154159
) -> Result<(), AcpiError> {
155160
let mut writer = AcpiTableWriter {
156161
mem,
157162
resource_allocator,
158163
};
159164

160-
let dsdt_addr = writer.build_dsdt()?;
165+
let dsdt_addr = writer.build_dsdt(mmio_device_manager)?;
161166
let fadt_addr = writer.build_fadt(dsdt_addr)?;
162167
let madt_addr = writer.build_madt(vcpus.len().try_into().unwrap())?;
163168
let xsdt_addr = writer.build_xsdt(fadt_addr, madt_addr)?;

src/vmm/src/acpi/x86_64.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use vm_memory::GuestAddress;
1313
use zerocopy::AsBytes;
1414

1515
use crate::arch::x86_64::layout;
16+
use crate::device_manager::legacy::PortIODeviceManager;
1617

1718
#[inline(always)]
1819
pub(crate) fn setup_interrupt_controllers(nr_vcpus: u8) -> Vec<u8> {
@@ -39,9 +40,10 @@ pub(crate) fn setup_arch_fadt(fadt: &mut Fadt) {
3940
);
4041
}
4142

42-
#[allow(clippy::ptr_arg)]
4343
#[inline(always)]
44-
pub(crate) fn setup_arch_dsdt(_dsdt_data: &mut Vec<u8>) {}
44+
pub(crate) fn setup_arch_dsdt(dsdt_data: &mut Vec<u8>) {
45+
PortIODeviceManager::append_aml_bytes(dsdt_data)
46+
}
4547

4648
pub(crate) const fn apic_addr() -> u32 {
4749
layout::APIC_ADDR

src/vmm/src/builder.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,12 @@ pub fn configure_system_for_boot(
799799

800800
// Create ACPI tables and write them in guest memory
801801
// For the time being we only support ACPI in x86_64
802-
acpi::create_acpi_tables(&vmm.guest_memory, &mut vmm.resource_allocator, vcpus)?;
802+
acpi::create_acpi_tables(
803+
&vmm.guest_memory,
804+
&mut vmm.resource_allocator,
805+
&vmm.mmio_device_manager,
806+
vcpus,
807+
)?;
803808
}
804809
#[cfg(target_arch = "aarch64")]
805810
{

src/vmm/src/device_manager/legacy.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use std::fmt::Debug;
1010
use std::sync::{Arc, Mutex};
1111

12+
use acpi_tables::{aml, Aml};
1213
use kvm_ioctls::VmFd;
1314
use libc::EFD_NONBLOCK;
1415
use utils::eventfd::EventFd;
@@ -167,6 +168,71 @@ impl PortIODeviceManager {
167168

168169
Ok(())
169170
}
171+
172+
pub(crate) fn append_aml_bytes(bytes: &mut Vec<u8>) {
173+
// Set up COM devices
174+
let gsi = [
175+
Self::COM_EVT_1_3_GSI,
176+
Self::COM_EVT_2_4_GSI,
177+
Self::COM_EVT_1_3_GSI,
178+
Self::COM_EVT_2_4_GSI,
179+
];
180+
for com in 0u8..4 {
181+
// COM1
182+
aml::Device::new(
183+
format!("_SB_.COM{}", com + 1).as_str().into(),
184+
vec![
185+
&aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0501")),
186+
&aml::Name::new("_UID".into(), &com),
187+
&aml::Name::new("_DDN".into(), &format!("COM{}", com + 1)),
188+
&aml::Name::new(
189+
"_CRS".into(),
190+
&aml::ResourceTemplate::new(vec![
191+
&aml::Interrupt::new(true, true, false, false, gsi[com as usize]),
192+
&aml::Io::new(
193+
PortIODeviceManager::SERIAL_PORT_ADDRESSES[com as usize]
194+
.try_into()
195+
.unwrap(),
196+
PortIODeviceManager::SERIAL_PORT_ADDRESSES[com as usize]
197+
.try_into()
198+
.unwrap(),
199+
1,
200+
PortIODeviceManager::SERIAL_PORT_SIZE.try_into().unwrap(),
201+
),
202+
]),
203+
),
204+
],
205+
)
206+
.append_aml_bytes(bytes);
207+
}
208+
// Setup i8042
209+
aml::Device::new(
210+
"_SB_.PS2_".into(),
211+
vec![
212+
&aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0303")),
213+
&aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0x0fu8)]),
214+
&aml::Name::new(
215+
"_CRS".into(),
216+
&aml::ResourceTemplate::new(vec![
217+
&aml::Io::new(
218+
PortIODeviceManager::I8042_KDB_DATA_REGISTER_ADDRESS
219+
.try_into()
220+
.unwrap(),
221+
PortIODeviceManager::I8042_KDB_DATA_REGISTER_ADDRESS
222+
.try_into()
223+
.unwrap(),
224+
1u8,
225+
1u8,
226+
),
227+
// Fake a command port so Linux stops complaining
228+
&aml::Io::new(0x0064, 0x0064, 1u8, 1u8),
229+
&aml::Interrupt::new(true, true, false, false, Self::KBD_EVT_GSI),
230+
]),
231+
),
232+
],
233+
)
234+
.append_aml_bytes(bytes);
235+
}
170236
}
171237

172238
#[cfg(test)]

src/vmm/src/device_manager/mmio.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ use std::collections::HashMap;
99
use std::fmt::Debug;
1010
use std::sync::{Arc, Mutex};
1111

12+
#[cfg(target_arch = "x86_64")]
13+
use acpi_tables::{aml, Aml};
1214
use kvm_ioctls::{IoEventAddress, VmFd};
1315
use linux_loader::cmdline as kernel_cmdline;
16+
#[cfg(target_arch = "x86_64")]
17+
use log::debug;
1418
use log::info;
1519
use serde::{Deserialize, Serialize};
1620
use vm_allocator::AllocPolicy;
@@ -58,7 +62,8 @@ pub enum MmioError {
5862
RegisterIrqFd(kvm_ioctls::Error),
5963
}
6064

61-
/// This represents the size of the mmio device specified to the kernel as a cmdline option
65+
/// This represents the size of the mmio device specified to the kernel through ACPI and as a
66+
/// command line option.
6267
/// It has to be larger than 0x100 (the offset where the configuration space starts from
6368
/// the beginning of the memory mapped device registers) + the size of the configuration space
6469
/// Currently hardcoded to 4K.
@@ -75,11 +80,49 @@ pub struct MMIODeviceInfo {
7580
pub irqs: Vec<u32>,
7681
}
7782

83+
#[cfg(target_arch = "x86_64")]
84+
fn add_virtio_aml(dsdt_data: &mut Vec<u8>, addr: u64, len: u64, irq: u32) {
85+
let dev_id = irq - crate::arch::IRQ_BASE;
86+
debug!(
87+
"acpi: Building AML for VirtIO device _SB_.V{:03}. memory range: {:#010x}:{} irq: {}",
88+
dev_id, addr, len, irq
89+
);
90+
aml::Device::new(
91+
format!("V{:03}", dev_id).as_str().into(),
92+
vec![
93+
&aml::Name::new("_HID".into(), &"LNRO0005"),
94+
&aml::Name::new("_UID".into(), &dev_id),
95+
&aml::Name::new("_CCA".into(), &aml::ONE),
96+
&aml::Name::new(
97+
"_CRS".into(),
98+
&aml::ResourceTemplate::new(vec![
99+
&aml::Memory32Fixed::new(
100+
true,
101+
addr.try_into().unwrap(),
102+
len.try_into().unwrap(),
103+
),
104+
&aml::Interrupt::new(true, true, false, false, irq),
105+
]),
106+
),
107+
],
108+
)
109+
.append_aml_bytes(dsdt_data);
110+
}
111+
78112
/// Manages the complexities of registering a MMIO device.
79113
#[derive(Debug)]
80114
pub struct MMIODeviceManager {
81115
pub(crate) bus: crate::devices::Bus,
82116
pub(crate) id_to_dev_info: HashMap<(DeviceType, String), MMIODeviceInfo>,
117+
// We create the AML byte code for every VirtIO device in the order we build
118+
// it, so that we ensure the root block device is appears first in the DSDT.
119+
// This is needed, so that the root device appears as `/dev/vda` in the guest
120+
// filesystem.
121+
// The alternative would be that we iterate the bus to get the data after all
122+
// of the devices are build. However, iterating the bus won't give us the
123+
// devices in the order they were added.
124+
#[cfg(target_arch = "x86_64")]
125+
pub(crate) dsdt_data: Vec<u8>,
83126
}
84127

85128
impl MMIODeviceManager {
@@ -88,6 +131,8 @@ impl MMIODeviceManager {
88131
MMIODeviceManager {
89132
bus: crate::devices::Bus::new(),
90133
id_to_dev_info: HashMap::new(),
134+
#[cfg(target_arch = "x86_64")]
135+
dsdt_data: vec![],
91136
}
92137
}
93138

@@ -166,7 +211,7 @@ impl MMIODeviceManager {
166211
device_info: &MMIODeviceInfo,
167212
) -> Result<(), MmioError> {
168213
// as per doc, [virtio_mmio.]device=<size>@<baseaddr>:<irq> needs to be appended
169-
// to kernel commandline for virtio mmio devices to get recognized
214+
// to kernel command line for virtio mmio devices to get recongnized
170215
// the size parameter has to be transformed to KiB, so dividing hexadecimal value in
171216
// bytes to 1024; further, the '{}' formatting rust construct will automatically
172217
// transform it to decimal
@@ -193,7 +238,17 @@ impl MMIODeviceManager {
193238
let device_info = self.allocate_mmio_resources(resource_allocator, 1)?;
194239
self.register_mmio_virtio(vm, device_id, mmio_device, &device_info)?;
195240
#[cfg(target_arch = "x86_64")]
196-
Self::add_virtio_device_to_cmdline(_cmdline, &device_info)?;
241+
{
242+
Self::add_virtio_device_to_cmdline(_cmdline, &device_info)?;
243+
add_virtio_aml(
244+
&mut self.dsdt_data,
245+
device_info.addr,
246+
device_info.len,
247+
// We are sure that `irqs` has at least one element; allocate_mmio_resources makes
248+
// sure of it.
249+
device_info.irqs[0],
250+
);
251+
}
197252
Ok(device_info)
198253
}
199254

0 commit comments

Comments
 (0)