board_x86_64.rs2.73%
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::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 |= (1 << 24) | (1 << 31);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 matches!(147
&config.coco,148
Some(Coco::AmdSev { .. } | Coco::AmdSnp { .. })149
) {150
// AMD Volume 3, section E.4.17.151
let leaf = CpuidIn {152
func: 0x8000_001f,153
index: None,154
};155
let Some(out) = cpuids.get_mut(&leaf) else {156
return error::MissingCpuid { leaf }.fail();157
};158
let host_ebx = __cpuid(leaf.func).ebx;159
// set PhysAddrReduction to 1160
out.ebx = (1 << 6) | (host_ebx & 0x3f);161
out.ecx = 0;162
out.edx = 0;163
if let Some(Coco::AmdSev { policy }) = &config.coco {164
out.eax = if policy.es() { 0x2 | 0x8 } else { 0x2 };165
} else if let Some(Coco::AmdSnp { .. }) = &config.coco {166
out.eax = 0x2 | 0x8 | 0x10;167
}168
}169
170
Ok(Self {171
cpuids,172
sev_ap_eip: AtomicU32::new(0),173
tdx_hob: AtomicU64::new(0),174
io_apic: Arc::new(IoApic::new(vm.create_msi_sender()?)),175
})176
}177
}178
179
fn encode_x2apic_id(topology: &CpuTopology, index: u16) -> u32 {9x180
let (socket_id, core_id, thread_id) = topology.encode(index);9x181
182
let thread_width = topology.smt as u32;9x183
let cores_per_socket = topology.cores as u32;9x184
let core_width = cores_per_socket.next_power_of_two().trailing_zeros();9x185
186
(socket_id as u32) << (core_width + thread_width)9x187
| (core_id as u32) << thread_width9x188
| (thread_id as u32)9x189
}9x190
191
impl<V> Board<V>192
where193
V: Vm,194
{195
pub fn encode_cpu_identity(&self, index: u16) -> u64 {196
encode_x2apic_id(&self.config.cpu.topology, index) as u64197
}198
199
fn setup_fw_cfg(&self, payload: &Payload) -> Result<()> {200
let Some(dev) = &*self.fw_cfg.lock() else {201
return Ok(());202
};203
let mut dev = dev.lock();204
if let Some(Executable::Linux(image)) = &payload.executable {205
dev.add_kernel_data(image).context(error::FwCfg)?;206
};207
if let Some(cmdline) = &payload.cmdline {208
dev.add_kernel_cmdline(cmdline.to_owned());209
};210
if let Some(initramfs) = &payload.initramfs {211
dev.add_initramfs_data(initramfs).context(error::FwCfg)?;212
};213
Ok(())214
}215
216
fn setup_coco(&self, fw: &mut ArcMemPages, vcpu: &V::Vcpu) -> Result<()> {217
let Some(coco) = &self.config.coco else {218
return Ok(());219
};220
match coco {221
Coco::AmdSev { policy } => self.setup_sev(fw, *policy),222
Coco::AmdSnp { .. } => self.setup_snp(fw),223
Coco::IntelTdx { .. } => self.setup_tdx(fw, vcpu),224
}225
}226
227
pub fn setup_firmware(228
&self,229
fw: &Path,230
payload: &Payload,231
vcpu: &V::Vcpu,232
) -> Result<InitState> {233
let (init_state, mut rom) = firmware::load(&self.memory, fw)?;234
self.setup_coco(&mut rom, vcpu)?;235
self.setup_fw_cfg(payload)?;236
Ok(init_state)237
}238
239
pub fn init_ap(&self, index: u16, vcpu: &mut V::Vcpu, vcpus: &VcpuGuard) -> Result<()> {240
let Some(coco) = &self.config.coco else {241
return Ok(());242
};243
self.sync_vcpus(vcpus)?;244
if index == 0 {245
return Ok(());246
}247
match coco {248
Coco::AmdSev { policy } => {249
if policy.es() {250
self.sev_init_ap(vcpu)?;251
}252
}253
Coco::AmdSnp { .. } => self.sev_init_ap(vcpu)?,254
Coco::IntelTdx { .. } => self.tdx_init_ap(vcpu)?,255
}256
Ok(())257
}258
259
pub fn init_boot_vcpu(&self, vcpu: &mut V::Vcpu, init_state: &InitState) -> Result<()> {260
if matches!(self.config.coco, Some(Coco::IntelTdx { .. })) {261
return Ok(());262
}263
vcpu.set_sregs(&init_state.sregs, &init_state.seg_regs, &init_state.dt_regs)?;264
vcpu.set_regs(&init_state.regs)?;265
Ok(())266
}267
268
pub fn init_vcpu(&self, index: u16, vcpu: &mut V::Vcpu) -> Result<()> {269
let mut cpuids = self.arch.cpuids.clone();270
let apic_id = self.encode_cpu_identity(index) as u32;271
for (in_, out) in &mut cpuids {272
if in_.func == 0x1 {273
out.ebx &= 0x00ff_ffff;274
out.ebx |= apic_id << 24;275
} else if in_.func == 0xb || in_.func == 0x1f || in_.func == 0x80000026 {276
out.edx = apic_id;277
}278
}279
vcpu.set_cpuids(cpuids)?;280
vcpu.set_msrs(&[(IA32_MISC_ENABLE, MiscEnable::FAST_STRINGS.bits())])?;281
Ok(())282
}283
284
pub fn reset_vcpu(&self, _index: u16, _vcpu: &mut V::Vcpu) -> Result<()> {285
Ok(())286
}287
288
pub fn create_ram(&self) -> Result<()> {289
let config = &self.config;290
let memory = &self.memory;291
292
let low_mem_size = std::cmp::min(config.mem.size, RAM_32_SIZE);293
let pages_low = self.create_ram_pages(low_mem_size, c"ram-low")?;294
let region_low = MemRegion {295
ranges: vec![MemRange::Ram(pages_low.clone())],296
entries: if self.config.coco.is_none() {297
vec![298
MemRegionEntry {299
size: BIOS_DATA_END,300
type_: MemRegionType::Reserved,301
},302
MemRegionEntry {303
size: EBDA_START - BIOS_DATA_END,304
type_: MemRegionType::Ram,305
},306
MemRegionEntry {307
size: EBDA_END - EBDA_START,308
type_: MemRegionType::Acpi,309
},310
MemRegionEntry {311
size: low_mem_size - EBDA_END,312
type_: MemRegionType::Ram,313
},314
]315
} else {316
vec![MemRegionEntry {317
size: low_mem_size,318
type_: MemRegionType::Ram,319
}]320
},321
callbacks: Mutex::new(vec![]),322
};323
memory.add_region(0, Arc::new(region_low))?;324
325
if config.mem.size > RAM_32_SIZE {326
let mem_hi_size = config.mem.size - RAM_32_SIZE;327
let mem_hi = self.create_ram_pages(mem_hi_size, c"ram-high")?;328
let region_hi = MemRegion::with_ram(mem_hi.clone(), MemRegionType::Ram);329
memory.add_region(MEM_64_START, Arc::new(region_hi))?;330
}331
Ok(())332
}333
334
pub fn coco_init(&self, memory: Arc<V::Memory>) -> Result<()> {335
let Some(coco) = &self.config.coco else {336
return Ok(());337
};338
match coco {339
Coco::AmdSev { policy } => self.sev_init(*policy, memory)?,340
Coco::AmdSnp { policy } => self.snp_init(*policy, memory)?,341
Coco::IntelTdx { attr } => self.tdx_init(*attr, memory)?,342
}343
Ok(())344
}345
346
pub fn coco_finalize(&self, index: u16, vcpus: &VcpuGuard) -> Result<()> {347
let Some(coco) = &self.config.coco else {348
return Ok(());349
};350
self.sync_vcpus(vcpus)?;351
if index != 0 {352
return Ok(());353
};354
match coco {355
Coco::AmdSev { policy } => self.sev_finalize(*policy),356
Coco::AmdSnp { .. } => self.snp_finalize(),357
Coco::IntelTdx { .. } => self.tdx_finalize(),358
}359
}360
361
fn patch_dsdt(&self, data: &mut [u8; 352]) {362
let pcie_mmio_64_start = self.config.pcie_mmio_64_start();363
let pcei_mmio_64_max = pcie_mmio_64_start - 1 + PCIE_MMIO_64_SIZE;364
data[DSDT_OFFSET_PCI_QWORD_MEM..(DSDT_OFFSET_PCI_QWORD_MEM + 8)]365
.copy_from_slice(&pcie_mmio_64_start.to_le_bytes());366
data[(DSDT_OFFSET_PCI_QWORD_MEM + 8)..(DSDT_OFFSET_PCI_QWORD_MEM + 16)]367
.copy_from_slice(&pcei_mmio_64_max.to_le_bytes());368
let sum = wrapping_sum(&*data);369
let checksum = &mut data[offset_of!(AcpiTableHeader, checksum)];370
*checksum = checksum.wrapping_sub(sum);371
}372
373
fn create_acpi(&self) -> AcpiTable {374
let mut table_bytes = Vec::new();375
let mut pointers = vec![];376
let mut checksums = vec![];377
378
let mut xsdt: AcpiTableXsdt3 = AcpiTableXsdt3::new_zeroed();379
let offset_xsdt = 0;380
table_bytes.extend(xsdt.as_bytes());381
382
let offset_dsdt = offset_xsdt + size_of_val(&xsdt);383
let mut dsdt = DSDT_TEMPLATE;384
self.patch_dsdt(&mut dsdt);385
table_bytes.extend(dsdt);386
387
let offset_fadt = offset_dsdt + size_of_val(&DSDT_TEMPLATE);388
debug_assert_eq!(offset_fadt % 4, 0);389
let fadt = create_fadt(offset_dsdt as u64);390
let pointer_fadt_to_dsdt = offset_fadt + offset_of!(AcpiTableFadt, xdsdt);391
table_bytes.extend(fadt.as_bytes());392
pointers.push(pointer_fadt_to_dsdt);393
checksums.push((offset_fadt, size_of_val(&fadt)));394
395
let offset_madt = offset_fadt + size_of_val(&fadt);396
debug_assert_eq!(offset_madt % 4, 0);397
let apic_ids: Vec<u32> = (0..self.config.cpu.count)398
.map(|index| self.encode_cpu_identity(index) as u32)399
.collect();400
let (madt, madt_ioapic, madt_apics) = create_madt(&apic_ids);401
table_bytes.extend(madt.as_bytes());402
table_bytes.extend(madt_ioapic.as_bytes());403
for apic in madt_apics {404
table_bytes.extend(apic.as_bytes());405
}406
407
let offset_mcfg = offset_madt + madt.header.length as usize;408
debug_assert_eq!(offset_mcfg % 4, 0);409
let mcfg = create_mcfg();410
table_bytes.extend(mcfg.as_bytes());411
412
debug_assert_eq!(offset_xsdt % 4, 0);413
let xsdt_entries = [offset_fadt as u64, offset_madt as u64, offset_mcfg as u64];414
xsdt = create_xsdt(xsdt_entries);415
xsdt.write_to_prefix(&mut table_bytes).unwrap();416
for index in 0..xsdt_entries.len() {417
pointers.push(offset_xsdt + offset_of!(AcpiTableXsdt3, entries) + index * 8);418
}419
checksums.push((offset_xsdt, size_of_val(&xsdt)));420
421
let rsdp = create_rsdp(offset_xsdt as u64);422
423
AcpiTable {424
rsdp,425
tables: table_bytes,426
table_checksums: checksums,427
table_pointers: pointers,428
}429
}430
431
pub fn create_firmware_data(&self, _init_state: &InitState) -> Result<()> {432
let mut acpi_table = self.create_acpi();433
let memory = &self.memory;434
memory.add_io_dev(PORT_ACPI_RESET, Arc::new(FadtReset))?;435
memory.add_io_dev(PORT_ACPI_SLEEP_CONTROL, Arc::new(FadtSleepControl))?;436
memory.add_io_dev(PORT_ACPI_TIMER, Arc::new(AcpiPmTimer::new()))?;437
if self.config.coco.is_none() {438
let ram = memory.ram_bus();439
acpi_table.relocate(EBDA_START + size_of::<AcpiTableRsdp>() as u64);440
acpi_table.update_checksums();441
ram.write_range(442
EBDA_START,443
size_of::<AcpiTableRsdp>() as u64,444
acpi_table.rsdp().as_bytes(),445
)?;446
ram.write_range(447
EBDA_START + size_of::<AcpiTableRsdp>() as u64,448
acpi_table.tables().len() as u64,449
acpi_table.tables(),450
)?;451
}452
if let Some(fw_cfg) = &*self.fw_cfg.lock() {453
let mut dev = fw_cfg.lock();454
dev.add_acpi(acpi_table).context(error::FwCfg)?;455
let mem_regions = memory.mem_region_entries();456
dev.add_e820(&mem_regions).context(error::FwCfg)?;457
dev.add_ram_size(self.config.mem.size);458
dev.add_cpu_count(self.config.cpu.count);459
}460
Ok(())461
}462
463
pub fn arch_init(&self) -> Result<()> {464
let io_apic = self.arch.io_apic.clone();465
self.mmio_devs.write().push((IOAPIC_START, io_apic));466
let mut io_devs = self.io_devs.write();467
io_devs.push((PORT_CMOS_REG, Arc::new(Cmos::new(SystemClock))));468
Ok(())469
}470
}471
472
const DSDT_TEMPLATE: [u8; 352] = [473
0x44, 0x53, 0x44, 0x54, 0x5D, 0x01, 0x00, 0x00, 0x02, 0x5D, 0x41, 0x4C, 0x49, 0x4F, 0x54, 0x48,474
0x41, 0x4C, 0x49, 0x4F, 0x54, 0x48, 0x56, 0x4D, 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C,475
0x28, 0x06, 0x23, 0x20, 0x5B, 0x82, 0x37, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31,476
0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x01,477
0x08, 0x5F, 0x53, 0x54, 0x41, 0x0A, 0x0F, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x10, 0x0A, 0x0D,478
0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x22, 0x10, 0x00, 0x79, 0x00, 0x08, 0x5F, 0x53,479
0x35, 0x5F, 0x12, 0x04, 0x01, 0x0A, 0x05, 0x5B, 0x82, 0x44, 0x0F, 0x2E, 0x5F, 0x53, 0x42, 0x5F,480
0x50, 0x43, 0x49, 0x30, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x08, 0x08, 0x5F,481
0x43, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x03, 0x08, 0x5F, 0x53, 0x45, 0x47, 0x00, 0x08, 0x5F,482
0x55, 0x49, 0x44, 0x00, 0x14, 0x32, 0x5F, 0x44, 0x53, 0x4D, 0x04, 0xA0, 0x29, 0x93, 0x68, 0x11,483
0x13, 0x0A, 0x10, 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, 0x91, 0x17, 0xEA, 0x4D, 0x19,484
0xC3, 0x43, 0x4D, 0xA0, 0x09, 0x93, 0x6A, 0x00, 0xA4, 0x11, 0x03, 0x01, 0x21, 0xA0, 0x07, 0x93,485
0x6A, 0x0A, 0x05, 0xA4, 0x00, 0xA4, 0x00, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x40, 0x09, 0x0A,486
0x8C, 0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,487
0x00, 0x47, 0x01, 0xF8, 0x0C, 0xF8, 0x0C, 0x01, 0x08, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x07, 0x00,488
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00,489
0x00, 0x00, 0x20, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,490
0xA0, 0xFF, 0xFF, 0xFF, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x8A, 0x2B, 0x00,491
0x00, 0x0C, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,492
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,493
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x0D, 0x00, 0x01, 0x0C,494
0x03, 0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF0, 0x79, 0x00, 0x00, 0x00, 0x00,495
];496
497
const DSDT_OFFSET_PCI_QWORD_MEM: usize = 0x12b;498
499
#[cfg(test)]500
#[path = "board_x86_64_test.rs"]501
mod tests;502