Alioth Code Coverage

board_x86_64.rs2.85%

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