1use std::arch::x86_64::CpuidResult;
6use std::arch::x86_64::__cpuid;
7use std::arch::x86_64::__cpuid_count;
8use std::collections::BTreeMap;
9use std::sync::Arc;
10
11use acpi_tables::aml;
12use acpi_tables::facs::FACS;
13use acpi_tables::rsdp::RSDP;
14use acpi_tables::sdt::SDT;
15use arch::CpuSet;
16use arch::VcpuAffinity;
17use base::error;
18use base::warn;
19use devices::ACPIPMResource;
20use devices::PciAddress;
21use devices::PciInterruptPin;
22use devices::PciRoot;
23use sync::Mutex;
24use vm_memory::GuestAddress;
25use vm_memory::GuestMemory;
26use zerocopy::FromBytes;
27use zerocopy::Immutable;
28use zerocopy::IntoBytes;
29use zerocopy::KnownLayout;
30
31pub struct AcpiDevResource {
32 pub amls: Vec<u8>,
33 pub pm_iobase: u64,
34 pub pm: Arc<Mutex<ACPIPMResource>>,
35 pub sdts: Vec<SDT>,
37}
38
39#[repr(C, packed)]
40#[derive(Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
41struct GenericAddress {
42 _space_id: u8,
43 _bit_width: u8,
44 _bit_offset: u8,
45 _access_width: u8,
46 _address: u64,
47}
48
49#[repr(C)]
50#[derive(Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
51struct LocalApic {
52 _type: u8,
53 _length: u8,
54 _processor_id: u8,
55 _apic_id: u8,
56 _flags: u32,
57}
58
59#[repr(C)]
60#[derive(Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
61struct Ioapic {
62 _type: u8,
63 _length: u8,
64 _ioapic_id: u8,
65 _reserved: u8,
66 _apic_address: u32,
67 _gsi_base: u32,
68}
69
70#[repr(C, packed)]
71#[derive(Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
72struct IoapicInterruptSourceOverride {
73 _type: u8,
74 _length: u8,
75 _bus: u8,
76 _source: u8,
77 _gsi: u32,
78 _flags: u16,
79}
80
81#[repr(C)]
82#[derive(Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
83struct Localx2Apic {
84 _type: u8,
85 _length: u8,
86 _reserved: u16,
87 _x2apic_id: u32,
88 _flags: u32,
89 _processor_id: u32,
90}
91
92const ADR_SPACE_SYSTEM_IO: u8 = 1;
94
95const OEM_REVISION: u32 = 1;
96const DSDT_REVISION: u8 = 6;
98const FADT_LEN: u32 = 276;
100const FADT_REVISION: u8 = 6;
101const FADT_MINOR_REVISION: u8 = 3;
102const FADT_POWER_BUTTON: u32 = 1 << 4;
104const _FADT_SLEEP_BUTTON: u32 = 1 << 5;
105const FADT_RESET_REGISTER: u32 = 1 << 10;
106const FADT_LOW_POWER_S2IDLE: u32 = 1 << 21;
107const FADT_FIELD_FACS_ADDR32: usize = 36;
109const FADT_FIELD_DSDT_ADDR32: usize = 40;
110pub const FADT_FIELD_SCI_INTERRUPT: usize = 46;
111const FADT_FIELD_SMI_COMMAND: usize = 48;
112const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
113const FADT_FIELD_PM1B_EVENT_BLK_ADDR: usize = 60;
114const FADT_FIELD_PM1A_CONTROL_BLK_ADDR: usize = 64;
115const FADT_FIELD_PM1B_CONTROL_BLK_ADDR: usize = 68;
116const FADT_FIELD_PM2_CONTROL_BLK_ADDR: usize = 72;
117const FADT_FIELD_PM_TMR_BLK_ADDR: usize = 76;
118const FADT_FIELD_GPE0_BLK_ADDR: usize = 80;
119const FADT_FIELD_GPE1_BLK_ADDR: usize = 84;
120const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
121const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
122const FADT_FIELD_PM2_CONTROL_BLK_LEN: usize = 90;
123const FADT_FIELD_PM_TMR_LEN: usize = 91;
124const FADT_FIELD_GPE0_BLK_LEN: usize = 92;
125const FADT_FIELD_GPE1_BLK_LEN: usize = 93;
126const FADT_FIELD_GPE1_BASE: usize = 94;
127const FADT_FIELD_RTC_DAY_ALARM: usize = 106;
128const FADT_FIELD_RTC_MONTH_ALARM: usize = 107;
129const FADT_FIELD_RTC_CENTURY: usize = 108;
130const FADT_FIELD_FLAGS: usize = 112;
131const FADT_FIELD_RESET_REGISTER: usize = 116;
132const FADT_FIELD_RESET_VALUE: usize = 128;
133const FADT_FIELD_MINOR_REVISION: usize = 131;
134const FADT_FIELD_FACS_ADDR: usize = 132;
135const FADT_FIELD_DSDT_ADDR: usize = 140;
136const FADT_FIELD_X_PM1A_EVENT_BLK_ADDR: usize = 148;
137const FADT_FIELD_X_PM1B_EVENT_BLK_ADDR: usize = 160;
138const FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR: usize = 172;
139const FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR: usize = 184;
140const FADT_FIELD_X_PM2_CONTROL_BLK_ADDR: usize = 196;
141const FADT_FIELD_X_PM_TMR_BLK_ADDR: usize = 208;
142const FADT_FIELD_X_GPE0_BLK_ADDR: usize = 220;
143const FADT_FIELD_X_GPE1_BLK_ADDR: usize = 232;
144const FADT_FIELD_HYPERVISOR_ID: usize = 268;
145const MADT_LEN: u32 = 44;
147const MADT_REVISION: u8 = 5;
148const MADT_FIELD_LAPIC_ADDR: usize = 36;
150const MADT_FIELD_FLAGS: usize = 40;
151const MADT_FLAG_PCAT_COMPAT: u32 = 1 << 0;
153const MADT_STRUCTURE_TYPE: usize = 0;
155const MADT_STRUCTURE_LEN: usize = 1;
156const MADT_TYPE_LOCAL_APIC: u8 = 0;
158const MADT_TYPE_IO_APIC: u8 = 1;
159const MADT_TYPE_INTERRUPT_SOURCE_OVERRIDE: u8 = 2;
160const MADT_TYPE_LOCAL_X2APIC: u8 = 9;
161const MADT_ENABLED: u32 = 1;
163const MADT_INT_POLARITY_ACTIVE_LOW: u16 = 0b11;
164const MADT_INT_TRIGGER_LEVEL: u16 = 0b11 << 2;
165const MADT_MIN_LOCAL_APIC_ID: u32 = 255;
167const XSDT_REVISION: u8 = 1;
169
170const CPUID_LEAF0_EBX_CPUID_SHIFT: u32 = 24; const MCFG_LEN: u32 = 60;
174const MCFG_REVISION: u8 = 1;
175const MCFG_FIELD_BASE_ADDRESS: usize = 44;
176const MCFG_FIELD_START_BUS_NUMBER: usize = 54;
177const MCFG_FIELD_END_BUS_NUMBER: usize = 55;
178
179const SSDT_REVISION: u8 = 2;
180pub fn create_customize_ssdt(
181 pci_root: Arc<Mutex<PciRoot>>,
182 amls: BTreeMap<PciAddress, Vec<u8>>,
183 gpe_scope_amls: BTreeMap<PciAddress, Vec<u8>>,
184) -> Option<SDT> {
185 if amls.is_empty() {
186 return None;
187 }
188
189 let mut ssdt = SDT::new(
190 *b"SSDT",
191 acpi_tables::HEADER_LEN,
192 SSDT_REVISION,
193 *b"CROSVM",
194 *b"CROSVMDT",
195 OEM_REVISION,
196 );
197
198 for (address, children) in amls {
199 if let Some(path) = pci_root.lock().acpi_path(&address) {
200 ssdt.append_slice(&aml::Scope::raw((*path).into(), children));
201 }
202 }
203
204 for children in gpe_scope_amls.values() {
205 ssdt.append_slice(children);
206 }
207
208 Some(ssdt)
209}
210
211fn create_dsdt_table(amls: &[u8]) -> SDT {
212 let mut dsdt = SDT::new(
213 *b"DSDT",
214 acpi_tables::HEADER_LEN,
215 DSDT_REVISION,
216 *b"CROSVM",
217 *b"CROSVMDT",
218 OEM_REVISION,
219 );
220
221 if !amls.is_empty() {
222 dsdt.append_slice(amls);
223 }
224
225 dsdt
226}
227
228fn create_facp_table(sci_irq: u16, force_s2idle: bool) -> SDT {
229 let mut facp = SDT::new(
230 *b"FACP",
231 FADT_LEN,
232 FADT_REVISION,
233 *b"CROSVM",
234 *b"CROSVMDT",
235 OEM_REVISION,
236 );
237
238 let mut fadt_flags: u32 = 0;
239
240 if force_s2idle {
241 fadt_flags |= FADT_LOW_POWER_S2IDLE;
242 }
243
244 facp.write(FADT_FIELD_FLAGS, fadt_flags);
245
246 facp.write(FADT_FIELD_SCI_INTERRUPT, sci_irq);
248
249 facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); facp.write::<u8>(FADT_FIELD_RTC_CENTURY, devices::cmos::RTC_REG_CENTURY);
253 facp.write::<u8>(FADT_FIELD_RTC_DAY_ALARM, devices::cmos::RTC_REG_ALARM_DAY);
254 facp.write::<u8>(
255 FADT_FIELD_RTC_MONTH_ALARM,
256 devices::cmos::RTC_REG_ALARM_MONTH,
257 );
258
259 facp
260}
261
262fn write_facp_overrides(
264 facp: &mut SDT,
265 facs_offset: GuestAddress,
266 dsdt_offset: GuestAddress,
267 pm_iobase: u32,
268 reset_port: u32,
269 reset_value: u8,
270) {
271 let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
272 facp.write(FADT_FIELD_FLAGS, fadt_flags | FADT_RESET_REGISTER);
274
275 facp.write(FADT_FIELD_SMI_COMMAND, 0u32);
276 facp.write(FADT_FIELD_FACS_ADDR32, 0u32);
277 facp.write(FADT_FIELD_DSDT_ADDR32, 0u32);
278 facp.write(FADT_FIELD_FACS_ADDR, facs_offset.0);
279 facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0);
280
281 facp.write(FADT_FIELD_PM1A_EVENT_BLK_ADDR, pm_iobase);
283
284 facp.write(FADT_FIELD_PM1B_EVENT_BLK_ADDR, 0u32);
286
287 facp.write(
289 FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
290 pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
291 );
292
293 facp.write(FADT_FIELD_PM1B_CONTROL_BLK_ADDR, 0u32);
295
296 facp.write(FADT_FIELD_PM2_CONTROL_BLK_ADDR, 0u32);
298
299 facp.write(FADT_FIELD_PM_TMR_BLK_ADDR, 0u32);
301
302 facp.write(
304 FADT_FIELD_GPE0_BLK_ADDR,
305 pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32 + 4,
306 );
307
308 facp.write(FADT_FIELD_GPE1_BLK_ADDR, 0u32);
310
311 facp.write(
313 FADT_FIELD_PM1A_EVENT_BLK_LEN,
314 devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN,
315 );
316
317 facp.write(
319 FADT_FIELD_PM1A_CONTROL_BLK_LEN,
320 devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN,
321 );
322
323 facp.write(FADT_FIELD_PM2_CONTROL_BLK_LEN, 0u8);
325
326 facp.write(FADT_FIELD_PM_TMR_LEN, 0u8);
328
329 facp.write(
331 FADT_FIELD_GPE0_BLK_LEN,
332 devices::acpi::ACPIPM_RESOURCE_GPE0_BLK_LEN,
333 );
334
335 facp.write(FADT_FIELD_GPE1_BLK_LEN, 0u8);
337
338 facp.write(FADT_FIELD_GPE1_BASE, 0u8);
340
341 facp.write(
343 FADT_FIELD_X_PM1A_EVENT_BLK_ADDR,
344 GenericAddress {
345 ..Default::default()
346 },
347 );
348
349 facp.write(
351 FADT_FIELD_X_PM1B_EVENT_BLK_ADDR,
352 GenericAddress {
353 ..Default::default()
354 },
355 );
356
357 facp.write(
359 FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR,
360 GenericAddress {
361 ..Default::default()
362 },
363 );
364
365 facp.write(
367 FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR,
368 GenericAddress {
369 ..Default::default()
370 },
371 );
372
373 facp.write(
375 FADT_FIELD_X_PM2_CONTROL_BLK_ADDR,
376 GenericAddress {
377 ..Default::default()
378 },
379 );
380
381 facp.write(
383 FADT_FIELD_X_PM_TMR_BLK_ADDR,
384 GenericAddress {
385 ..Default::default()
386 },
387 );
388
389 facp.write(
391 FADT_FIELD_X_GPE0_BLK_ADDR,
392 GenericAddress {
393 ..Default::default()
394 },
395 );
396
397 facp.write(
399 FADT_FIELD_X_GPE1_BLK_ADDR,
400 GenericAddress {
401 ..Default::default()
402 },
403 );
404
405 facp.write(
407 FADT_FIELD_RESET_REGISTER,
408 GenericAddress {
409 _space_id: ADR_SPACE_SYSTEM_IO,
410 _bit_width: 8,
411 _bit_offset: 0,
412 _access_width: 1,
413 _address: reset_port.into(),
414 },
415 );
416 facp.write(FADT_FIELD_RESET_VALUE, reset_value);
417}
418
419fn next_offset(offset: GuestAddress, len: u64) -> Option<GuestAddress> {
420 match len % 64 {
422 0 => offset.checked_add(len),
423 x => offset.checked_add(len.checked_add(64 - x)?),
424 }
425}
426
427fn sync_acpi_id_from_cpuid(
428 madt: &mut SDT,
429 cpus: BTreeMap<usize, CpuSet>,
430 apic_ids: &mut Vec<usize>,
431) -> base::Result<()> {
432 let cpu_set = match base::get_cpu_affinity() {
433 Err(e) => {
434 error!("Failed to get CPU affinity: {} when create MADT", e);
435 return Err(e);
436 }
437 Ok(c) => c,
438 };
439
440 for (vcpu, pcpu) in cpus {
441 let mut has_leafb = false;
442 let mut get_apic_id = false;
443 let mut apic_id: u8 = 0;
444
445 if let Err(e) = base::set_cpu_affinity(pcpu) {
446 error!("Failed to set CPU affinity: {} when create MADT", e);
447 return Err(e);
448 }
449
450 let mut cpuid_entry: CpuidResult = unsafe { __cpuid_count(0, 0) };
454
455 if cpuid_entry.eax >= 0xB {
456 cpuid_entry = unsafe { __cpuid_count(0xB, 0) };
460
461 if cpuid_entry.ebx != 0 {
462 if cpuid_entry.edx < MADT_MIN_LOCAL_APIC_ID {
466 apic_id = cpuid_entry.edx as u8;
467 get_apic_id = true;
468 } else {
469 has_leafb = true;
477
478 let x2apic = Localx2Apic {
479 _type: MADT_TYPE_LOCAL_X2APIC,
480 _length: std::mem::size_of::<Localx2Apic>() as u8,
481 _x2apic_id: cpuid_entry.edx,
482 _flags: MADT_ENABLED,
483 _processor_id: (vcpu + 1) as u32,
484 ..Default::default()
485 };
486 madt.append(x2apic);
487 apic_ids.push(cpuid_entry.edx as usize);
488 }
489 }
490 }
491
492 if !has_leafb {
493 if !get_apic_id {
494 cpuid_entry = unsafe { __cpuid(1) };
498 apic_id = (cpuid_entry.ebx >> CPUID_LEAF0_EBX_CPUID_SHIFT & 0xff) as u8;
499 }
500
501 let apic = LocalApic {
502 _type: MADT_TYPE_LOCAL_APIC,
503 _length: std::mem::size_of::<LocalApic>() as u8,
504 _processor_id: vcpu as u8,
505 _apic_id: apic_id,
506 _flags: MADT_ENABLED,
507 };
508 madt.append(apic);
509 apic_ids.push(apic_id as usize);
510 }
511 }
512
513 if let Err(e) = base::set_cpu_affinity(cpu_set) {
514 error!("Failed to reset CPU affinity: {} when create MADT", e);
515 return Err(e);
516 }
517
518 Ok(())
519}
520
521pub fn create_acpi_tables(
538 guest_mem: &GuestMemory,
539 num_cpus: u8,
540 sci_irq: u32,
541 reset_port: u32,
542 reset_value: u8,
543 acpi_dev_resource: &AcpiDevResource,
544 host_cpus: Option<VcpuAffinity>,
545 apic_ids: &mut Vec<usize>,
546 pci_irqs: &[(PciAddress, u32, PciInterruptPin)],
547 pcie_cfg_mmio: u64,
548 max_bus: u8,
549 force_s2idle: bool,
550) -> Option<GuestAddress> {
551 let rsdp_offset = GuestAddress(super::ACPI_HI_RSDP_WINDOW_BASE);
553 let facs_offset = next_offset(rsdp_offset, RSDP::len() as u64)?;
554 let mut offset = next_offset(facs_offset, FACS::len() as u64)?;
555 let mut dsdt_offset: Option<GuestAddress> = None;
556 let mut tables: Vec<u64> = Vec::new();
557 let mut facp: Option<SDT> = None;
558 let mut host_madt: Option<SDT> = None;
559
560 for sdt in acpi_dev_resource.sdts.iter() {
562 if sdt.is_signature(b"FACP") {
563 facp = Some(sdt.clone());
564 continue;
565 }
566 if sdt.is_signature(b"APIC") {
567 host_madt = Some(sdt.clone());
568 continue;
569 }
570 guest_mem.write_at_addr(sdt.as_slice(), offset).ok()?;
571 if sdt.is_signature(b"DSDT") {
572 dsdt_offset = Some(offset);
573 } else {
574 tables.push(offset.0);
575 }
576 offset = next_offset(offset, sdt.len() as u64)?;
577 }
578
579 let facs = FACS::new();
581 guest_mem.write_at_addr(facs.as_bytes(), facs_offset).ok()?;
582
583 let dsdt_offset = match dsdt_offset {
585 Some(dsdt_offset) => dsdt_offset,
586 None => {
587 let dsdt_offset = offset;
588 let dsdt = create_dsdt_table(&acpi_dev_resource.amls);
589 guest_mem.write_at_addr(dsdt.as_slice(), offset).ok()?;
590 offset = next_offset(offset, dsdt.len() as u64)?;
591 dsdt_offset
592 }
593 };
594
595 let mut facp = facp.map_or_else(
597 || create_facp_table(sci_irq as u16, force_s2idle),
598 |facp| {
599 let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
600 if fadt_flags & FADT_POWER_BUTTON != 0 {
601 warn!(
602 "Control Method Power Button is not supported. FADT flags = 0x{:x}",
603 fadt_flags
604 );
605 }
606 facp
607 },
608 );
609
610 write_facp_overrides(
611 &mut facp,
612 facs_offset,
613 dsdt_offset,
614 acpi_dev_resource.pm_iobase as u32,
615 reset_port,
616 reset_value,
617 );
618
619 guest_mem.write_at_addr(facp.as_slice(), offset).ok()?;
620 tables.push(offset.0);
621 offset = next_offset(offset, facp.len() as u64)?;
622
623 let mut madt = SDT::new(
625 *b"APIC",
626 MADT_LEN,
627 MADT_REVISION,
628 *b"CROSVM",
629 *b"CROSVMDT",
630 OEM_REVISION,
631 );
632 madt.write(
633 MADT_FIELD_LAPIC_ADDR,
634 super::mptable::APIC_DEFAULT_PHYS_BASE,
635 );
636 madt.write(MADT_FIELD_FLAGS, MADT_FLAG_PCAT_COMPAT);
639
640 match host_cpus {
641 Some(VcpuAffinity::PerVcpu(cpus)) => {
642 sync_acpi_id_from_cpuid(&mut madt, cpus, apic_ids).ok()?;
643 }
644 _ => {
645 for cpu in 0..num_cpus {
646 let apic = LocalApic {
647 _type: MADT_TYPE_LOCAL_APIC,
648 _length: std::mem::size_of::<LocalApic>() as u8,
649 _processor_id: cpu,
650 _apic_id: cpu,
651 _flags: MADT_ENABLED,
652 };
653 madt.append(apic);
654 apic_ids.push(cpu as usize);
655 }
656 }
657 }
658
659 madt.append(Ioapic {
660 _type: MADT_TYPE_IO_APIC,
661 _length: std::mem::size_of::<Ioapic>() as u8,
662 _apic_address: super::mptable::IO_APIC_DEFAULT_PHYS_BASE,
663 ..Default::default()
664 });
665
666 let mut unique_pci_irqs: Vec<u32> = pci_irqs.iter().map(|(_, irq_num, _)| *irq_num).collect();
670 unique_pci_irqs.sort_unstable();
671 unique_pci_irqs.dedup();
672 for irq_num in unique_pci_irqs {
673 madt.append(IoapicInterruptSourceOverride {
674 _type: MADT_TYPE_INTERRUPT_SOURCE_OVERRIDE,
675 _length: std::mem::size_of::<IoapicInterruptSourceOverride>() as u8,
676 _bus: 0, _source: irq_num as u8,
678 _gsi: irq_num,
679 _flags: MADT_INT_POLARITY_ACTIVE_LOW | MADT_INT_TRIGGER_LEVEL,
680 });
681 }
682
683 if let Some(host_madt) = host_madt {
684 let mut idx = MADT_LEN as usize;
685 while idx + MADT_STRUCTURE_LEN < host_madt.len() {
686 let struct_type = host_madt.as_slice()[idx + MADT_STRUCTURE_TYPE];
687 let struct_len = host_madt.as_slice()[idx + MADT_STRUCTURE_LEN] as usize;
688 if struct_type == MADT_TYPE_INTERRUPT_SOURCE_OVERRIDE {
689 if idx + struct_len <= host_madt.len() {
690 madt.append_slice(&host_madt.as_slice()[idx..(idx + struct_len)]);
691 } else {
692 error!("Malformed host MADT");
693 }
694 }
695 idx += struct_len;
696 }
697 }
698
699 guest_mem.write_at_addr(madt.as_slice(), offset).ok()?;
700 tables.push(offset.0);
701 offset = next_offset(offset, madt.len() as u64)?;
702
703 let mut mcfg = SDT::new(
705 *b"MCFG",
706 MCFG_LEN,
707 MCFG_REVISION,
708 *b"CROSVM",
709 *b"CROSVMDT",
710 OEM_REVISION,
711 );
712 mcfg.write(MCFG_FIELD_BASE_ADDRESS, pcie_cfg_mmio);
713 mcfg.write(MCFG_FIELD_START_BUS_NUMBER, 0_u8);
714 mcfg.write(MCFG_FIELD_END_BUS_NUMBER, max_bus);
715
716 guest_mem.write_at_addr(mcfg.as_slice(), offset).ok()?;
717 tables.push(offset.0);
718 offset = next_offset(offset, madt.len() as u64)?;
719
720 let mut xsdt = SDT::new(
722 *b"XSDT",
723 acpi_tables::HEADER_LEN,
724 XSDT_REVISION,
725 *b"CROSVM",
726 *b"CROSVMDT",
727 OEM_REVISION,
728 );
729 for table in tables {
730 xsdt.append(table);
731 }
732
733 guest_mem.write_at_addr(xsdt.as_slice(), offset).ok()?;
734
735 let rsdp = RSDP::new(*b"CROSVM", offset.0);
737 guest_mem.write_at_addr(rsdp.as_bytes(), rsdp_offset).ok()?;
738
739 Some(rsdp_offset)
740}
741
742#[cfg(test)]
743mod tests {
744 use crate::acpi::*;
745
746 #[test]
747 fn facp_table_creation() {
748 let sci_irq: u16 = 5;
749 let force_s2idle = true;
750 let facp = create_facp_table(sci_irq, force_s2idle);
751
752 assert_eq!(facp.read::<u32>(FADT_FIELD_FLAGS), FADT_LOW_POWER_S2IDLE);
753 assert_eq!(facp.read::<u16>(FADT_FIELD_SCI_INTERRUPT), sci_irq);
754 assert_eq!(
755 facp.read::<u8>(FADT_FIELD_MINOR_REVISION),
756 FADT_MINOR_REVISION
757 );
758 assert_eq!(facp.read::<[u8; 6]>(FADT_FIELD_HYPERVISOR_ID), *b"CROSVM");
759 assert_eq!(
760 facp.read::<u8>(FADT_FIELD_RTC_CENTURY),
761 devices::cmos::RTC_REG_CENTURY
762 );
763 assert_eq!(
764 facp.read::<u8>(FADT_FIELD_RTC_DAY_ALARM),
765 devices::cmos::RTC_REG_ALARM_DAY
766 );
767 assert_eq!(
768 facp.read::<u8>(FADT_FIELD_RTC_MONTH_ALARM),
769 devices::cmos::RTC_REG_ALARM_MONTH
770 );
771 }
772}