Alioth Code Coverage

board_x86_64.rs2.78%

1// Copyright 2024 Google LLC
2//
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 at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15mod sev;
16mod tdx;
17
18use std::arch::x86_64::{__cpuid, CpuidResult};
19use std::collections::HashMap;
20use std::mem::{offset_of, size_of, size_of_val};
21use std::path::Path;
22use std::sync::Arc;
23use std::sync::atomic::{AtomicU32, AtomicU64};
24
25use parking_lot::Mutex;
26use snafu::ResultExt;
27use zerocopy::{FromZeros, IntoBytes};
28
29use crate::arch::cpuid::CpuidIn;
30use 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};
34use crate::arch::msr::{IA32_MISC_ENABLE, MiscEnable};
35use crate::board::{Board, BoardConfig, CpuTopology, PCIE_MMIO_64_SIZE, Result, VcpuGuard, error};
36use crate::device::ioapic::IoApic;
37use crate::firmware::acpi::bindings::{
38 AcpiTableFadt, AcpiTableHeader, AcpiTableRsdp, AcpiTableXsdt3,
39};
40use crate::firmware::acpi::reg::{FadtReset, FadtSleepControl};
41use crate::firmware::acpi::{
42 AcpiTable, create_fadt, create_madt, create_mcfg, create_rsdp, create_xsdt,
43};
44use crate::hv::{Coco, Hypervisor, Vcpu, Vm};
45use crate::loader::{Executable, InitState, Payload, firmware};
46use crate::mem::mapped::ArcMemPages;
47use crate::mem::{MemRange, MemRegion, MemRegionEntry, MemRegionType};
48use crate::utils::wrapping_sum;
49
50pub struct ArchBoard<V>
51where
52 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
60fn 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
76impl<V: Vm> ArchBoard<V> {
77 pub fn new<H>(hv: &H, vm: &V, config: &BoardConfig) -> Result<Self>
78 where
79 H: Hypervisor<Vm = V>,
80 {
81 let mut cpuids = hv.get_supported_cpuids()?;
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 name
137 // 0x8000_0005: L1 cache/LTB
138 // 0x8000_0006: L2 cache/TLB and L3 cache
139 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 1
158 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
177fn encode_x2apic_id(topology: &CpuTopology, index: u16) -> u32 {9x
178 let (socket_id, core_id, thread_id) = topology.encode(index);9x
179
180 let thread_width = topology.smt as u32;9x
181 let cores_per_socket = topology.cores as u32;9x
182 let core_width = cores_per_socket.next_power_of_two().trailing_zeros();9x
183
184 (socket_id as u32) << (core_width + thread_width)9x
185 | (core_id as u32) << thread_width9x
186 | (thread_id as u32)9x
187}9x
188
189impl<V> Board<V>
190where
191 V: Vm,
192{
193 pub fn encode_cpu_identity(&self, index: u16) -> u64 {
194 encode_x2apic_id(&self.config.cpu.topology, index) as u64
195 }
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
464const 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
489const DSDT_OFFSET_PCI_QWORD_MEM: usize = 0x12b;
490
491#[cfg(test)]
492#[path = "board_x86_64_test.rs"]
493mod tests;
494