Alioth Code Coverage

board_x86_64.rs2.84%

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