Skip to content

Commit 900b276

Browse files
committed
Add vCPU hotplug controller and extend GED
Define new ACPI devices related to hotplugging, including a hotplug controller, a CPU container, and individual CPU devices. Add hotplugging devices to GED. Signed-off-by: James Curtis <[email protected]>
1 parent b47eb70 commit 900b276

File tree

6 files changed

+481
-51
lines changed

6 files changed

+481
-51
lines changed

src/vmm/src/builder.rs

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use vm_superio::Serial;
2929

3030
#[cfg(target_arch = "x86_64")]
3131
use crate::acpi;
32+
#[cfg(target_arch = "x86_64")]
33+
use crate::arch::DeviceType;
3234
use crate::arch::InitrdConfig;
3335
#[cfg(target_arch = "aarch64")]
3436
use crate::construct_kvm_mpidrs;
@@ -48,6 +50,8 @@ use crate::device_manager::persist::{
4850
};
4951
use crate::device_manager::resources::ResourceAllocator;
5052
#[cfg(target_arch = "x86_64")]
53+
use crate::devices::acpi::cpu_container::{CpuContainer, CpuContainerError};
54+
#[cfg(target_arch = "x86_64")]
5155
use crate::devices::acpi::vmgenid::{VmGenId, VmGenIdError};
5256
use crate::devices::legacy::serial::SerialOut;
5357
#[cfg(target_arch = "aarch64")]
@@ -96,6 +100,9 @@ pub enum StartMicrovmError {
96100
/// Error creating VMGenID device: {0}
97101
#[cfg(target_arch = "x86_64")]
98102
CreateVMGenID(VmGenIdError),
103+
/// Error creating CpuContainer device: {0}
104+
#[cfg(target_arch = "x86_64")]
105+
CreateCpuContainer(#[from] CpuContainerError),
99106
/// Invalid Memory Configuration: {0}
100107
GuestMemory(crate::vstate::memory::MemoryError),
101108
/// Cannot load initrd due to an invalid memory configuration.
@@ -156,6 +163,7 @@ fn create_vmm_and_vcpus(
156163
vcpu_count: u8,
157164
#[cfg(target_arch = "x86_64")] seccomp_filters: BpfThreadMap,
158165
kvm_capabilities: Vec<KvmCapability>,
166+
boot_timer_enabled: bool,
159167
) -> Result<(Vmm, Vec<Vcpu>), StartMicrovmError> {
160168
use self::StartMicrovmError::*;
161169

@@ -172,14 +180,28 @@ fn create_vmm_and_vcpus(
172180
.map_err(VmmError::EventFd)
173181
.map_err(Internal)?;
174182

175-
let resource_allocator = ResourceAllocator::new()?;
183+
let mut resource_allocator = ResourceAllocator::new()?;
176184

177185
// Instantiate the MMIO device manager.
178-
let mmio_device_manager = MMIODeviceManager::new();
186+
let mut mmio_device_manager = MMIODeviceManager::new();
187+
188+
if boot_timer_enabled {
189+
let boot_timer = crate::devices::pseudo::BootTimer::new(TimestampUs::default());
190+
191+
mmio_device_manager
192+
.register_mmio_boot_timer(&mut resource_allocator, boot_timer)
193+
.map_err(RegisterMmioDevice)?;
194+
}
179195

180196
// Instantiate ACPI device manager.
181197
#[cfg(target_arch = "x86_64")]
182-
let acpi_device_manager = ACPIDeviceManager::new();
198+
let acpi_device_manager = {
199+
let cpu_container = Arc::new(Mutex::new(CpuContainer::new(
200+
&mut resource_allocator,
201+
vcpu_count,
202+
)?));
203+
ACPIDeviceManager::new(cpu_container)
204+
};
183205

184206
// For x86_64 we need to create the interrupt controller before calling `KVM_CREATE_VCPUS`
185207
// while on aarch64 we need to do it the other way around.
@@ -317,14 +339,18 @@ pub fn build_microvm_for_boot(
317339
#[cfg(target_arch = "x86_64")]
318340
seccomp_filters.clone(),
319341
cpu_template.kvm_capabilities.clone(),
342+
vm_resources.boot_timer,
320343
)?;
321344

322345
// The boot timer device needs to be the first device attached in order
323346
// to maintain the same MMIO address referenced in the documentation
324347
// and tests.
325-
if vm_resources.boot_timer {
326-
attach_boot_timer_device(&mut vmm, request_ts)?;
327-
}
348+
// if vm_resources.boot_timer {
349+
// attach_boot_timer_device(&mut vmm, request_ts)?;
350+
// }
351+
352+
#[cfg(target_arch = "x86_64")]
353+
attach_cpu_container_device(&mut vmm)?;
328354

329355
if let Some(balloon) = vm_resources.balloon.get() {
330356
attach_balloon_device(&mut vmm, &mut boot_cmdline, balloon, event_manager)?;
@@ -488,6 +514,7 @@ pub fn build_microvm_from_snapshot(
488514
#[cfg(target_arch = "x86_64")]
489515
seccomp_filters.clone(),
490516
microvm_state.vm_state.kvm_cap_modifiers.clone(),
517+
vm_resources.boot_timer,
491518
)?;
492519

493520
#[cfg(target_arch = "x86_64")]
@@ -1048,6 +1075,8 @@ pub mod tests {
10481075
use super::*;
10491076
use crate::arch::DeviceType;
10501077
use crate::device_manager::resources::ResourceAllocator;
1078+
#[cfg(target_arch = "x86_64")]
1079+
use crate::devices::acpi::cpu_container::CpuContainer;
10511080
use crate::devices::virtio::block::CacheType;
10521081
use crate::devices::virtio::rng::device::ENTROPY_DEV_ID;
10531082
use crate::devices::virtio::vsock::{TYPE_VSOCK, VSOCK_DEV_ID};
@@ -1126,9 +1155,27 @@ pub mod tests {
11261155

11271156
let mut vm = Vm::new(vec![]).unwrap();
11281157
vm.memory_init(&guest_memory, false).unwrap();
1158+
1159+
#[cfg(target_arch = "x86_64")]
1160+
let mut resource_allocator = ResourceAllocator::new().unwrap();
1161+
#[cfg(target_arch = "aarch64")]
1162+
let resource_allocator = ResourceAllocator::new().unwrap();
1163+
11291164
let mmio_device_manager = MMIODeviceManager::new();
1165+
1166+
#[cfg(target_arch = "x86_64")]
1167+
setup_interrupt_controller(&mut vm).unwrap();
1168+
11301169
#[cfg(target_arch = "x86_64")]
1131-
let acpi_device_manager = ACPIDeviceManager::new();
1170+
let acpi_device_manager = {
1171+
let cpu_container = Arc::new(Mutex::new(
1172+
CpuContainer::new(&mut resource_allocator, 1)
1173+
.map_err(StartMicrovmError::CreateCpuContainer)
1174+
.unwrap(),
1175+
));
1176+
ACPIDeviceManager::new(cpu_container)
1177+
};
1178+
11321179
#[cfg(target_arch = "x86_64")]
11331180
let pio_device_manager = PortIODeviceManager::new(
11341181
Arc::new(Mutex::new(SerialWrapper {
@@ -1145,9 +1192,6 @@ pub mod tests {
11451192
)
11461193
.unwrap();
11471194

1148-
#[cfg(target_arch = "x86_64")]
1149-
setup_interrupt_controller(&mut vm).unwrap();
1150-
11511195
#[cfg(target_arch = "aarch64")]
11521196
{
11531197
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
@@ -1166,7 +1210,7 @@ pub mod tests {
11661210
vcpus_exit_evt,
11671211
#[cfg(target_arch = "x86_64")]
11681212
seccomp_filters: crate::seccomp_filters::get_empty_filters(),
1169-
resource_allocator: ResourceAllocator::new().unwrap(),
1213+
resource_allocator,
11701214
mmio_device_manager,
11711215
#[cfg(target_arch = "x86_64")]
11721216
pio_device_manager,

src/vmm/src/device_manager/acpi.rs

Lines changed: 98 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use std::sync::{Arc, Mutex};
5+
46
use acpi_tables::{aml, Aml};
57
use kvm_ioctls::VmFd;
68

9+
use crate::devices::acpi::cpu_container::CpuContainer;
710
use crate::devices::acpi::vmgenid::VmGenId;
811

912
#[derive(Debug)]
1013
pub struct ACPIDeviceManager {
1114
/// VMGenID device
1215
pub vmgenid: Option<VmGenId>,
16+
pub cpu_container: Arc<Mutex<CpuContainer>>,
1317
}
1418

1519
impl ACPIDeviceManager {
1620
/// Create a new ACPIDeviceManager object
17-
pub fn new() -> Self {
18-
Self { vmgenid: None }
21+
pub fn new(cpu_container: Arc<Mutex<CpuContainer>>) -> Self {
22+
Self {
23+
vmgenid: None,
24+
cpu_container,
25+
}
1926
}
2027

2128
/// Attach a new VMGenID device to the microVM
@@ -31,6 +38,14 @@ impl ACPIDeviceManager {
3138
Ok(())
3239
}
3340

41+
pub fn notify_cpu_container(&mut self) -> Result<(), std::io::Error> {
42+
self.cpu_container
43+
.lock()
44+
.expect("Poisoned lock")
45+
.notify_guest()?;
46+
Ok(())
47+
}
48+
3449
/// If it exists, notify guest VMGenID device that we have resumed from a snapshot.
3550
pub fn notify_vmgenid(&mut self) -> Result<(), std::io::Error> {
3651
if let Some(vmgenid) = &mut self.vmgenid {
@@ -42,44 +57,89 @@ impl ACPIDeviceManager {
4257

4358
impl Aml for ACPIDeviceManager {
4459
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
45-
// If we have a VMGenID device, create the AML for the device and GED interrupt handler
46-
self.vmgenid.as_ref().inspect(|vmgenid| {
47-
// AML for GED
48-
aml::Device::new(
49-
"_SB_.GED_".into(),
50-
vec![
51-
&aml::Name::new("_HID".into(), &"ACPI0013"),
52-
&aml::Name::new(
53-
"_CRS".into(),
54-
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
60+
// Depending on what devices are available, generate AML for GED.
61+
let locked_container = self.cpu_container.lock().expect("Poisoned lock");
62+
match self.vmgenid.as_ref() {
63+
Some(vmgenid) => {
64+
aml::Device::new(
65+
"_SB_.GED_".into(),
66+
vec![
67+
&aml::Name::new("_HID".into(), &"ACPI0013"),
68+
&aml::Name::new(
69+
"_CRS".into(),
70+
&aml::ResourceTemplate::new(vec![
71+
&aml::Interrupt::new(
72+
true,
73+
true,
74+
false,
75+
false,
76+
locked_container.gsi,
77+
),
78+
&aml::Interrupt::new(true, true, false, false, vmgenid.gsi),
79+
]),
80+
),
81+
&aml::Method::new(
82+
"_EVT".into(),
83+
1,
5584
true,
85+
vec![
86+
&aml::If::new(
87+
#[allow(clippy::cast_possible_truncation)]
88+
&aml::Equal::new(&aml::Arg(0), &(locked_container.gsi as u8)),
89+
vec![&aml::MethodCall::new(
90+
aml::Path::new("\\_SB_.CPUS.CSCN"),
91+
vec![],
92+
)],
93+
),
94+
&aml::If::new(
95+
#[allow(clippy::cast_possible_truncation)]
96+
&aml::Equal::new(&aml::Arg(0), &(vmgenid.gsi as u8)),
97+
vec![&aml::Notify::new(
98+
&aml::Path::new("\\_SB_.VGEN"),
99+
&0x80usize,
100+
)],
101+
),
102+
],
103+
),
104+
],
105+
)
106+
.append_aml_bytes(v);
107+
locked_container.append_aml_bytes(v);
108+
vmgenid.append_aml_bytes(v);
109+
}
110+
None => {
111+
aml::Device::new(
112+
"_SB_.GED_".into(),
113+
vec![
114+
&aml::Name::new("_HID".into(), &"ACPI0013"),
115+
&aml::Name::new(
116+
"_CRS".into(),
117+
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
118+
true,
119+
true,
120+
false,
121+
false,
122+
locked_container.gsi,
123+
)]),
124+
),
125+
&aml::Method::new(
126+
"_EVT".into(),
127+
1,
56128
true,
57-
false,
58-
false,
59-
vmgenid.gsi,
60-
)]),
61-
),
62-
&aml::Method::new(
63-
"_EVT".into(),
64-
1,
65-
true,
66-
vec![&aml::If::new(
67-
// We know that the maximum IRQ number fits in a u8. We have up to 32
68-
// IRQs in x86 and up to 128 in ARM (look into
69-
// `vmm::crate::arch::layout::IRQ_MAX`)
70-
#[allow(clippy::cast_possible_truncation)]
71-
&aml::Equal::new(&aml::Arg(0), &(vmgenid.gsi as u8)),
72-
vec![&aml::Notify::new(
73-
&aml::Path::new("\\_SB_.VGEN"),
74-
&0x80usize,
129+
vec![&aml::If::new(
130+
#[allow(clippy::cast_possible_truncation)]
131+
&aml::Equal::new(&aml::Arg(0), &(locked_container.gsi as u8)),
132+
vec![&aml::MethodCall::new(
133+
aml::Path::new("\\_SB_.CPUS.CSCN"),
134+
vec![],
135+
)],
75136
)],
76-
)],
77-
),
78-
],
79-
)
80-
.append_aml_bytes(v);
81-
// AML for VMGenID itself.
82-
vmgenid.append_aml_bytes(v);
83-
});
137+
),
138+
],
139+
)
140+
.append_aml_bytes(v);
141+
locked_container.append_aml_bytes(v);
142+
}
143+
}
84144
}
85145
}

src/vmm/src/device_manager/persist.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use super::resources::ResourceAllocator;
1919
#[cfg(target_arch = "aarch64")]
2020
use crate::arch::DeviceType;
2121
#[cfg(target_arch = "x86_64")]
22+
use crate::devices::acpi::cpu_container::CpuContainer;
23+
#[cfg(target_arch = "x86_64")]
2224
use crate::devices::acpi::vmgenid::{VMGenIDState, VMGenIdConstructorArgs, VmGenId, VmGenIdError};
2325
use crate::devices::virtio::balloon::persist::{BalloonConstructorArgs, BalloonState};
2426
use crate::devices::virtio::balloon::{Balloon, BalloonError};
@@ -241,6 +243,7 @@ pub struct ACPIDeviceManagerConstructorArgs<'a> {
241243
pub mem: &'a GuestMemoryMmap,
242244
pub resource_allocator: &'a mut ResourceAllocator,
243245
pub vm: &'a VmFd,
246+
pub cpu_container: Arc<Mutex<CpuContainer>>,
244247
}
245248

246249
#[cfg(target_arch = "x86_64")]
@@ -268,7 +271,7 @@ impl<'a> Persist<'a> for ACPIDeviceManager {
268271
constructor_args: Self::ConstructorArgs,
269272
state: &Self::State,
270273
) -> std::result::Result<Self, Self::Error> {
271-
let mut dev_manager = ACPIDeviceManager::new();
274+
let mut dev_manager = ACPIDeviceManager::new(constructor_args.cpu_container.clone());
272275
if let Some(vmgenid_args) = &state.vmgenid {
273276
let vmgenid = VmGenId::restore(
274277
VMGenIdConstructorArgs {

0 commit comments

Comments
 (0)