Alioth Code Coverage

board_x86_64.rs2.87%

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