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