board_x86_64.rs2.85%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
mod sev;16
mod tdx;17
18
use std::arch::x86_64::{__cpuid, CpuidResult};19
use std::collections::HashMap;20
use std::mem::{offset_of, size_of, size_of_val};21
use std::path::Path;22
use std::sync::Arc;23
use std::sync::atomic::{AtomicU32, AtomicU64};24
25
use parking_lot::Mutex;26
use snafu::ResultExt;27
use zerocopy::{FromZeros, IntoBytes};28
29
use crate::arch::cpuid::{Cpuid1Ecx, CpuidIn};30
use crate::arch::layout::{31
BIOS_DATA_END, EBDA_END, EBDA_START, IOAPIC_START, MEM_64_START, PORT_ACPI_RESET,32
PORT_ACPI_SLEEP_CONTROL, PORT_ACPI_TIMER, PORT_CMOS_REG, RAM_32_SIZE,33
};34
use crate::arch::msr::{IA32_MISC_ENABLE, MiscEnable};35
use crate::board::{Board, BoardConfig, CpuTopology, PCIE_MMIO_64_SIZE, Result, VcpuGuard, error};36
use crate::device::clock::SystemClock;37
use crate::device::cmos::Cmos;38
use crate::device::ioapic::IoApic;39
use crate::firmware::acpi::bindings::{40
AcpiTableFadt, AcpiTableHeader, AcpiTableRsdp, AcpiTableXsdt3,41
};42
use crate::firmware::acpi::reg::{AcpiPmTimer, FadtReset, FadtSleepControl};43
use crate::firmware::acpi::{44
AcpiTable, create_fadt, create_madt, create_mcfg, create_rsdp, create_xsdt,45
};46
use crate::hv::{Coco, Hypervisor, Vcpu, Vm};47
use crate::loader::{Executable, InitState, Payload, firmware};48
use crate::mem::mapped::ArcMemPages;49
use crate::mem::{MemRange, MemRegion, MemRegionEntry, MemRegionType};50
use crate::utils::wrapping_sum;51
52
pub struct ArchBoard<V>53
where54
V: Vm,55
{56
cpuids: HashMap<CpuidIn, CpuidResult>,57
sev_ap_eip: AtomicU32,58
tdx_hob: AtomicU64,59
pub(crate) io_apic: Arc<IoApic<V::MsiSender>>,60
}61
62
fn add_topology(cpuids: &mut HashMap<CpuidIn, CpuidResult>, func: u32, levels: &[(u8, u16)]) {63
let edx = 0; // patched later in init_vcpu()64
for (index, (level, count)) in levels.iter().chain(&[(0, 0)]).enumerate() {65
let eax = count.next_power_of_two().trailing_zeros();66
let ebx = *count as u32;67
let ecx = ((*level as u32) << 8) | (index as u32);68
cpuids.insert(69
CpuidIn {70
func,71
index: Some(index as u32),72
},73
CpuidResult { eax, ebx, ecx, edx },74
);75
}76
}77
78
impl<V: Vm> ArchBoard<V> {79
pub fn new<H>(hv: &H, vm: &V, config: &BoardConfig) -> Result<Self>80
where81
H: Hypervisor<Vm = V>,82
{83
let mut cpuids = hv.get_supported_cpuids(config.coco.as_ref())?;84
85
let threads_per_core = 1 + config.cpu.topology.smt as u16;86
let threads_per_socket = config.cpu.topology.cores * threads_per_core;87
88
add_topology(89
&mut cpuids,90
0xb,91
&[(1, threads_per_core), (2, threads_per_socket)],92
);93
94
let leaf0 = CpuidIn {95
func: 0,96
index: None,97
};98
let Some(out) = cpuids.get_mut(&leaf0) else {99
return error::MissingCpuid { leaf: leaf0 }.fail();100
};101
let vendor = [out.ebx, out.edx, out.ecx];102
match vendor.as_bytes() {103
b"GenuineIntel" => add_topology(104
&mut cpuids,105
0x1f,106
&[(1, threads_per_core), (2, threads_per_socket)],107
),108
b"AuthenticAMD" => add_topology(109
&mut cpuids,110
0x8000_0026,111
&[112
(1, threads_per_core),113
(2, threads_per_socket),114
(3, threads_per_socket),115
(4, threads_per_socket),116
],117
),118
_ => {}119
}120
121
let leaf1 = CpuidIn {122
func: 0x1,123
index: None,124
};125
let Some(out) = cpuids.get_mut(&leaf1) else {126
return error::MissingCpuid { leaf: leaf1 }.fail();127
};128
out.ecx |= (Cpuid1Ecx::TSC_DEADLINE | Cpuid1Ecx::HYPERVISOR).bits();129
130
let leaf_8000_0000 = __cpuid(0x8000_0000);131
cpuids.insert(132
CpuidIn {133
func: 0x8000_0000,134
index: None,135
},136
leaf_8000_0000,137
);138
// 0x8000_0002 to 0x8000_0004: processor name139
// 0x8000_0005: L1 cache/LTB140
// 0x8000_0006: L2 cache/TLB and L3 cache141
for func in 0x8000_0002..=0x8000_0006 {142
let host_cpuid = __cpuid(func);143
cpuids.insert(CpuidIn { func, index: None }, host_cpuid);144
}145
146
if let Some(coco) = &config.coco147
&& matches!(coco, Coco::AmdSev { .. } | Coco::AmdSnp { .. })148
{149
sev::adjust_cpuid(coco, &mut cpuids)?;150
}151
152
Ok(Self {153
cpuids,154
sev_ap_eip: AtomicU32::new(0),155
tdx_hob: AtomicU64::new(0),156
io_apic: Arc::new(IoApic::new(vm.create_msi_sender()?)),157
})158
}159
}160
161
fn encode_x2apic_id(topology: &CpuTopology, index: u16) -> u32 {9x162
let (socket_id, core_id, thread_id) = topology.encode(index);9x163
164
let thread_width = topology.smt as u32;9x165
let cores_per_socket = topology.cores as u32;9x166
let core_width = cores_per_socket.next_power_of_two().trailing_zeros();9x167
168
(socket_id as u32) << (core_width + thread_width)9x169
| (core_id as u32) << thread_width9x170
| (thread_id as u32)9x171
}9x172
173
impl<V> Board<V>174
where175
V: Vm,176
{177
pub fn encode_cpu_identity(&self, index: u16) -> u64 {178
encode_x2apic_id(&self.config.cpu.topology, index) as u64179
}180
181
fn setup_fw_cfg(&self, payload: &Payload) -> Result<()> {182
let Some(dev) = &*self.fw_cfg.lock() else {183
return Ok(());184
};185
let mut dev = dev.lock();186
if let Some(Executable::Linux(image)) = &payload.executable {187
dev.add_kernel_data(image).context(error::FwCfg)?;188
};189
if let Some(cmdline) = &payload.cmdline {190
dev.add_kernel_cmdline(cmdline.to_owned());191
};192
if let Some(initramfs) = &payload.initramfs {193
dev.add_initramfs_data(initramfs).context(error::FwCfg)?;194
};195
Ok(())196
}197
198
fn setup_coco(&self, fw: &mut ArcMemPages, vcpu: &V::Vcpu) -> Result<()> {199
let Some(coco) = &self.config.coco else {200
return Ok(());201
};202
match coco {203
Coco::AmdSev { policy } => self.setup_sev(fw, *policy),204
Coco::AmdSnp { .. } => self.setup_snp(fw),205
Coco::IntelTdx { .. } => self.setup_tdx(fw, vcpu),206
}207
}208
209
pub fn setup_firmware(210
&self,211
fw: &Path,212
payload: &Payload,213
vcpu: &V::Vcpu,214
) -> Result<InitState> {215
let (init_state, mut rom) = firmware::load(&self.memory, fw)?;216
self.setup_coco(&mut rom, vcpu)?;217
self.setup_fw_cfg(payload)?;218
Ok(init_state)219
}220
221
pub fn init_ap(&self, index: u16, vcpu: &mut V::Vcpu, vcpus: &VcpuGuard) -> Result<()> {222
let Some(coco) = &self.config.coco else {223
return Ok(());224
};225
self.sync_vcpus(vcpus)?;226
if index == 0 {227
return Ok(());228
}229
match coco {230
Coco::AmdSev { policy } => {231
if policy.es() {232
self.sev_init_ap(vcpu)?;233
}234
}235
Coco::AmdSnp { .. } => self.sev_init_ap(vcpu)?,236
Coco::IntelTdx { .. } => self.tdx_init_ap(vcpu)?,237
}238
Ok(())239
}240
241
pub fn init_boot_vcpu(&self, vcpu: &mut V::Vcpu, init_state: &InitState) -> Result<()> {242
if matches!(self.config.coco, Some(Coco::IntelTdx { .. })) {243
return Ok(());244
}245
vcpu.set_sregs(&init_state.sregs, &init_state.seg_regs, &init_state.dt_regs)?;246
vcpu.set_regs(&init_state.regs)?;247
Ok(())248
}249
250
pub fn init_vcpu(&self, index: u16, vcpu: &mut V::Vcpu) -> Result<()> {251
let mut cpuids = self.arch.cpuids.clone();252
let apic_id = self.encode_cpu_identity(index) as u32;253
for (in_, out) in &mut cpuids {254
if in_.func == 0x1 {255
out.ebx &= 0x00ff_ffff;256
out.ebx |= apic_id << 24;257
} else if in_.func == 0xb || in_.func == 0x1f || in_.func == 0x80000026 {258
out.edx = apic_id;259
}260
}261
vcpu.set_cpuids(cpuids)?;262
vcpu.set_msrs(&[(IA32_MISC_ENABLE, MiscEnable::FAST_STRINGS.bits())])?;263
Ok(())264
}265
266
pub fn reset_vcpu(&self, _index: u16, _vcpu: &mut V::Vcpu) -> Result<()> {267
Ok(())268
}269
270
pub fn create_ram(&self) -> Result<()> {271
let config = &self.config;272
let memory = &self.memory;273
274
let low_mem_size = std::cmp::min(config.mem.size, RAM_32_SIZE);275
let pages_low = self.create_ram_pages(low_mem_size, c"ram-low")?;276
let region_low = MemRegion {277
ranges: vec![MemRange::Ram(pages_low.clone())],278
entries: if self.config.coco.is_none() {279
vec![280
MemRegionEntry {281
size: BIOS_DATA_END,282
type_: MemRegionType::Reserved,283
},284
MemRegionEntry {285
size: EBDA_START - BIOS_DATA_END,286
type_: MemRegionType::Ram,287
},288
MemRegionEntry {289
size: EBDA_END - EBDA_START,290
type_: MemRegionType::Acpi,291
},292
MemRegionEntry {293
size: low_mem_size - EBDA_END,294
type_: MemRegionType::Ram,295
},296
]297
} else {298
vec![MemRegionEntry {299
size: low_mem_size,300
type_: MemRegionType::Ram,301
}]302
},303
callbacks: Mutex::new(vec![]),304
};305
memory.add_region(0, Arc::new(region_low))?;306
307
if config.mem.size > RAM_32_SIZE {308
let mem_hi_size = config.mem.size - RAM_32_SIZE;309
let mem_hi = self.create_ram_pages(mem_hi_size, c"ram-high")?;310
let region_hi = MemRegion::with_ram(mem_hi.clone(), MemRegionType::Ram);311
memory.add_region(MEM_64_START, Arc::new(region_hi))?;312
}313
Ok(())314
}315
316
pub fn coco_init(&self, memory: Arc<V::Memory>) -> Result<()> {317
let Some(coco) = &self.config.coco else {318
return Ok(());319
};320
match coco {321
Coco::AmdSev { policy } => self.sev_init(*policy, memory)?,322
Coco::AmdSnp { policy } => self.snp_init(*policy, memory)?,323
Coco::IntelTdx { attr } => self.tdx_init(*attr, memory)?,324
}325
Ok(())326
}327
328
pub fn coco_finalize(&self, index: u16, vcpus: &VcpuGuard) -> Result<()> {329
let Some(coco) = &self.config.coco else {330
return Ok(());331
};332
self.sync_vcpus(vcpus)?;333
if index != 0 {334
return Ok(());335
};336
match coco {337
Coco::AmdSev { policy } => self.sev_finalize(*policy),338
Coco::AmdSnp { .. } => self.snp_finalize(),339
Coco::IntelTdx { .. } => self.tdx_finalize(),340
}341
}342
343
fn patch_dsdt(&self, data: &mut [u8; 352]) {344
let pcie_mmio_64_start = self.config.pcie_mmio_64_start();345
let pcei_mmio_64_max = pcie_mmio_64_start - 1 + PCIE_MMIO_64_SIZE;346
data[DSDT_OFFSET_PCI_QWORD_MEM..(DSDT_OFFSET_PCI_QWORD_MEM + 8)]347
.copy_from_slice(&pcie_mmio_64_start.to_le_bytes());348
data[(DSDT_OFFSET_PCI_QWORD_MEM + 8)..(DSDT_OFFSET_PCI_QWORD_MEM + 16)]349
.copy_from_slice(&pcei_mmio_64_max.to_le_bytes());350
let sum = wrapping_sum(&*data);351
let checksum = &mut data[offset_of!(AcpiTableHeader, checksum)];352
*checksum = checksum.wrapping_sub(sum);353
}354
355
fn create_acpi(&self) -> AcpiTable {356
let mut table_bytes = Vec::new();357
let mut pointers = vec![];358
let mut checksums = vec![];359
360
let mut xsdt: AcpiTableXsdt3 = AcpiTableXsdt3::new_zeroed();361
let offset_xsdt = 0;362
table_bytes.extend(xsdt.as_bytes());363
364
let offset_dsdt = offset_xsdt + size_of_val(&xsdt);365
let mut dsdt = DSDT_TEMPLATE;366
self.patch_dsdt(&mut dsdt);367
table_bytes.extend(dsdt);368
369
let offset_fadt = offset_dsdt + size_of_val(&DSDT_TEMPLATE);370
debug_assert_eq!(offset_fadt % 4, 0);371
let fadt = create_fadt(offset_dsdt as u64);372
let pointer_fadt_to_dsdt = offset_fadt + offset_of!(AcpiTableFadt, xdsdt);373
table_bytes.extend(fadt.as_bytes());374
pointers.push(pointer_fadt_to_dsdt);375
checksums.push((offset_fadt, size_of_val(&fadt)));376
377
let offset_madt = offset_fadt + size_of_val(&fadt);378
debug_assert_eq!(offset_madt % 4, 0);379
let apic_ids: Vec<u32> = (0..self.config.cpu.count)380
.map(|index| self.encode_cpu_identity(index) as u32)381
.collect();382
let (madt, madt_ioapic, madt_apics) = create_madt(&apic_ids);383
table_bytes.extend(madt.as_bytes());384
table_bytes.extend(madt_ioapic.as_bytes());385
for apic in madt_apics {386
table_bytes.extend(apic.as_bytes());387
}388
389
let offset_mcfg = offset_madt + madt.header.length as usize;390
debug_assert_eq!(offset_mcfg % 4, 0);391
let mcfg = create_mcfg();392
table_bytes.extend(mcfg.as_bytes());393
394
debug_assert_eq!(offset_xsdt % 4, 0);395
let xsdt_entries = [offset_fadt as u64, offset_madt as u64, offset_mcfg as u64];396
xsdt = create_xsdt(xsdt_entries);397
xsdt.write_to_prefix(&mut table_bytes).unwrap();398
for index in 0..xsdt_entries.len() {399
pointers.push(offset_xsdt + offset_of!(AcpiTableXsdt3, entries) + index * 8);400
}401
checksums.push((offset_xsdt, size_of_val(&xsdt)));402
403
let rsdp = create_rsdp(offset_xsdt as u64);404
405
AcpiTable {406
rsdp,407
tables: table_bytes,408
table_checksums: checksums,409
table_pointers: pointers,410
}411
}412
413
pub fn create_firmware_data(&self, _init_state: &InitState) -> Result<()> {414
let mut acpi_table = self.create_acpi();415
let memory = &self.memory;416
memory.add_io_dev(PORT_ACPI_RESET, Arc::new(FadtReset))?;417
memory.add_io_dev(PORT_ACPI_SLEEP_CONTROL, Arc::new(FadtSleepControl))?;418
memory.add_io_dev(PORT_ACPI_TIMER, Arc::new(AcpiPmTimer::new()))?;419
if self.config.coco.is_none() {420
let ram = memory.ram_bus();421
acpi_table.relocate(EBDA_START + size_of::<AcpiTableRsdp>() as u64);422
acpi_table.update_checksums();423
ram.write_range(424
EBDA_START,425
size_of::<AcpiTableRsdp>() as u64,426
acpi_table.rsdp().as_bytes(),427
)?;428
ram.write_range(429
EBDA_START + size_of::<AcpiTableRsdp>() as u64,430
acpi_table.tables().len() as u64,431
acpi_table.tables(),432
)?;433
}434
if let Some(fw_cfg) = &*self.fw_cfg.lock() {435
let mut dev = fw_cfg.lock();436
dev.add_acpi(acpi_table).context(error::FwCfg)?;437
let mem_regions = memory.mem_region_entries();438
dev.add_e820(&mem_regions).context(error::FwCfg)?;439
dev.add_ram_size(self.config.mem.size);440
dev.add_cpu_count(self.config.cpu.count);441
}442
Ok(())443
}444
445
pub fn arch_init(&self) -> Result<()> {446
let io_apic = self.arch.io_apic.clone();447
self.mmio_devs.write().push((IOAPIC_START, io_apic));448
let mut io_devs = self.io_devs.write();449
io_devs.push((PORT_CMOS_REG, Arc::new(Cmos::new(SystemClock))));450
Ok(())451
}452
}453
454
const DSDT_TEMPLATE: [u8; 352] = [455
0x44, 0x53, 0x44, 0x54, 0x5D, 0x01, 0x00, 0x00, 0x02, 0x5D, 0x41, 0x4C, 0x49, 0x4F, 0x54, 0x48,456
0x41, 0x4C, 0x49, 0x4F, 0x54, 0x48, 0x56, 0x4D, 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C,457
0x28, 0x06, 0x23, 0x20, 0x5B, 0x82, 0x37, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31,458
0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x01,459
0x08, 0x5F, 0x53, 0x54, 0x41, 0x0A, 0x0F, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x10, 0x0A, 0x0D,460
0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x22, 0x10, 0x00, 0x79, 0x00, 0x08, 0x5F, 0x53,461
0x35, 0x5F, 0x12, 0x04, 0x01, 0x0A, 0x05, 0x5B, 0x82, 0x44, 0x0F, 0x2E, 0x5F, 0x53, 0x42, 0x5F,462
0x50, 0x43, 0x49, 0x30, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x08, 0x08, 0x5F,463
0x43, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x03, 0x08, 0x5F, 0x53, 0x45, 0x47, 0x00, 0x08, 0x5F,464
0x55, 0x49, 0x44, 0x00, 0x14, 0x32, 0x5F, 0x44, 0x53, 0x4D, 0x04, 0xA0, 0x29, 0x93, 0x68, 0x11,465
0x13, 0x0A, 0x10, 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, 0x91, 0x17, 0xEA, 0x4D, 0x19,466
0xC3, 0x43, 0x4D, 0xA0, 0x09, 0x93, 0x6A, 0x00, 0xA4, 0x11, 0x03, 0x01, 0x21, 0xA0, 0x07, 0x93,467
0x6A, 0x0A, 0x05, 0xA4, 0x00, 0xA4, 0x00, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x40, 0x09, 0x0A,468
0x8C, 0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,469
0x00, 0x47, 0x01, 0xF8, 0x0C, 0xF8, 0x0C, 0x01, 0x08, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x07, 0x00,470
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00,471
0x00, 0x00, 0x20, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,472
0xA0, 0xFF, 0xFF, 0xFF, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x8A, 0x2B, 0x00,473
0x00, 0x0C, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,474
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,475
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x0D, 0x00, 0x01, 0x0C,476
0x03, 0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF0, 0x79, 0x00, 0x00, 0x00, 0x00,477
];478
479
const DSDT_OFFSET_PCI_QWORD_MEM: usize = 0x12b;480
481
#[cfg(test)]482
#[path = "board_x86_64_test.rs"]483
mod tests;484