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