Alioth Code Coverage

board_x86_64.rs2.73%

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, PORT_ACPI_TIMER, PORT_CMOS_REG, 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::clock::SystemClock;
37use crate::device::cmos::Cmos;
38use crate::device::ioapic::IoApic;
39use crate::firmware::acpi::bindings::{
40 AcpiTableFadt, AcpiTableHeader, AcpiTableRsdp, AcpiTableXsdt3,
41};
42use crate::firmware::acpi::reg::{AcpiPmTimer, FadtReset, FadtSleepControl};
43use crate::firmware::acpi::{
44 AcpiTable, create_fadt, create_madt, create_mcfg, create_rsdp, create_xsdt,
45};
46use crate::hv::{Coco, Hypervisor, Vcpu, Vm};
47use crate::loader::{Executable, InitState, Payload, firmware};
48use crate::mem::mapped::ArcMemPages;
49use crate::mem::{MemRange, MemRegion, MemRegionEntry, MemRegionType};
50use crate::utils::wrapping_sum;
51
52pub struct ArchBoard<V>
53where
54 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
62fn 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
78impl<V: Vm> ArchBoard<V> {
79 pub fn new<H>(hv: &H, vm: &V, config: &BoardConfig) -> Result<Self>
80 where
81 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 name
139 // 0x8000_0005: L1 cache/LTB
140 // 0x8000_0006: L2 cache/TLB and L3 cache
141 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 1
160 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
179fn encode_x2apic_id(topology: &CpuTopology, index: u16) -> u32 {9x
180 let (socket_id, core_id, thread_id) = topology.encode(index);9x
181
182 let thread_width = topology.smt as u32;9x
183 let cores_per_socket = topology.cores as u32;9x
184 let core_width = cores_per_socket.next_power_of_two().trailing_zeros();9x
185
186 (socket_id as u32) << (core_width + thread_width)9x
187 | (core_id as u32) << thread_width9x
188 | (thread_id as u32)9x
189}9x
190
191impl<V> Board<V>
192where
193 V: Vm,
194{
195 pub fn encode_cpu_identity(&self, index: u16) -> u64 {
196 encode_x2apic_id(&self.config.cpu.topology, index) as u64
197 }
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
472const 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
497const DSDT_OFFSET_PCI_QWORD_MEM: usize = 0x12b;
498
499#[cfg(test)]
500#[path = "board_x86_64_test.rs"]
501mod tests;
502