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