1pub use crate::aml::*;
5use memory_range::MemoryRange;
6use zerocopy::FromBytes;
7use zerocopy::Immutable;
8use zerocopy::IntoBytes;
9use zerocopy::KnownLayout;
10
11#[repr(C, packed)]
12#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
13pub struct DescriptionHeader {
14 pub signature: u32,
15 _length: u32, pub revision: u8,
17 _checksum: u8, pub oem_id: [u8; 6],
19 pub oem_table_id: u64,
20 pub oem_revision: u32,
21 pub creator_id: u32,
22 pub creator_rev: u32,
23}
24
25fn encode_pcie_name(mut pcie_index: u32) -> Vec<u8> {
26 assert!(pcie_index < 1000);
27 let mut temp = "PCI0".as_bytes().to_vec();
28 let mut i = temp.len() - 1;
29 while pcie_index > 0 {
30 temp[i] = b'0' + (pcie_index % 10) as u8;
31 pcie_index /= 10;
32 i -= 1;
33 }
34 temp
35}
36
37pub struct Ssdt {
38 description_header: DescriptionHeader,
39 objects: Vec<u8>,
40 pcie_ecam_ranges: Vec<MemoryRange>,
41}
42
43pub struct PcieHostBridgeEntry {
45 pub index: u32,
47 pub segment: u16,
49 pub start_bus: u8,
51 pub end_bus: u8,
53 pub ecam_range: MemoryRange,
55 pub low_mmio: MemoryRange,
57 pub high_mmio: MemoryRange,
59 pub cxl: bool,
61 pub vnode: Option<u32>,
63 pub preserve_boot_config: bool,
68}
69
70impl Ssdt {
71 pub fn new() -> Self {
72 Self {
73 description_header: DescriptionHeader {
74 signature: u32::from_le_bytes(*b"SSDT"),
75 _length: 0,
76 revision: 2,
77 _checksum: 0,
78 oem_id: *b"MSFTVM",
79 oem_table_id: 0x313054445353, oem_revision: 1,
81 creator_id: u32::from_le_bytes(*b"MSFT"),
82 creator_rev: 0x01000000,
83 },
84 objects: vec![],
85 pcie_ecam_ranges: vec![],
86 }
87 }
88
89 pub fn to_bytes(&self) -> Vec<u8> {
90 let mut byte_stream = Vec::new();
91 byte_stream.extend_from_slice(self.description_header.as_bytes());
92 byte_stream.extend_from_slice(&self.objects);
93
94 if !self.pcie_ecam_ranges.is_empty() {
97 let mut vmod = Device::new(b"VMOD");
98 vmod.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0C02")));
99
100 let mut crs = CurrentResourceSettings::new();
101 for ecam_range in &self.pcie_ecam_ranges {
102 crs.add_resource(&QwordMemory::new(
103 ecam_range.start(),
104 ecam_range.end() - ecam_range.start(),
105 ));
106 }
107 vmod.add_object(&crs);
108 vmod.append_to_vec(&mut byte_stream);
109 }
110
111 let length = byte_stream.len();
112 byte_stream[4..8].copy_from_slice(&u32::try_from(length).unwrap().to_le_bytes());
113 let mut checksum: u8 = 0;
114 for byte in &byte_stream {
115 checksum = checksum.wrapping_add(*byte);
116 }
117
118 byte_stream[9] = (!checksum).wrapping_add(1);
119 byte_stream
120 }
121
122 pub fn add_object(&mut self, obj: &impl AmlObject) {
123 obj.append_to_vec(&mut self.objects);
124 }
125
126 pub fn add_pcie(&mut self, entry: PcieHostBridgeEntry) {
146 let PcieHostBridgeEntry {
147 index,
148 segment,
149 start_bus,
150 end_bus,
151 ecam_range,
152 low_mmio,
153 high_mmio,
154 cxl,
155 vnode,
156 preserve_boot_config,
157 } = entry;
158 let mut pcie = Device::new(encode_pcie_name(index).as_slice());
159 if cxl {
160 pcie.add_object(&NamedString::new(b"_HID", b"ACPI0016"));
163 let mut cid_data = Vec::new();
164 cid_data.extend_from_slice(&EisaId(*b"PNP0A03").to_bytes());
165 cid_data.extend_from_slice(&EisaId(*b"PNP0A08").to_bytes());
166 pcie.add_object(&NamedObject::new(
167 b"_CID",
168 &StructuredPackage {
169 elem_count: 2,
170 elem_data: cid_data,
171 },
172 ));
173 } else {
174 pcie.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0A08")));
175 pcie.add_object(&NamedObject::new(b"_CID", &EisaId(*b"PNP0A03")));
176 }
177 pcie.add_object(&NamedInteger::new(b"_UID", index.into()));
178 pcie.add_object(&NamedInteger::new(b"_SEG", segment.into()));
179 pcie.add_object(&NamedInteger::new(b"_BBN", start_bus.into()));
180 pcie.add_object(&NamedInteger::new(b"_CCA", 1));
181 if let Some(vnode) = vnode {
182 pcie.add_object(&NamedInteger::new(b"_PXM", vnode.into()));
183 }
184
185 let mut osc_method = Method::new(b"_OSC");
206 osc_method.set_arg_count(4);
207
208 osc_method.add_operation(&CreateDWordFieldOp {
210 source_buffer: encode_arg(3),
211 byte_index: encode_integer(0),
212 field_name: *b"STS0",
213 });
214
215 let pcie_osc_uuid = guid::guid!("33DB4D5B-1FF7-401C-9657-7441C03DD766");
217 let uuid_buffer = Buffer(pcie_osc_uuid.as_bytes()).to_bytes();
218 let lequal = LEqualOp {
219 left: encode_arg(0),
220 right: uuid_buffer,
221 };
222
223 let else_body = if cxl {
224 let cxl_osc_uuid = guid::guid!("68f2d50b-c469-4d8a-bd3d-941a103fd3fc");
227 let cxl_uuid_buffer = Buffer(cxl_osc_uuid.as_bytes()).to_bytes();
228 let cxl_uuid_match = LEqualOp {
229 left: encode_arg(0),
230 right: cxl_uuid_buffer,
231 };
232
233 let cxl_revision_match = LEqualOp {
234 left: encode_arg(1),
235 right: encode_integer(1),
236 };
237
238 let cxl_store_zero = StoreOp {
239 source: encode_integer(0),
240 destination: b"STS0".to_vec(),
241 };
242 let cxl_rev_if_op = IfOp {
243 predicate: cxl_revision_match.to_bytes(),
244 body: cxl_store_zero.to_bytes(),
245 };
246
247 let cxl_revision_or = OrOp {
249 operand1: b"STS0".to_vec(),
250 operand2: encode_integer(0x02),
251 target_name: b"STS0".to_vec(),
252 };
253 let cxl_revision_else = ElseOp {
254 body: cxl_revision_or.to_bytes(),
255 };
256
257 let uuid_or = OrOp {
259 operand1: b"STS0".to_vec(),
260 operand2: encode_integer(0x04),
261 target_name: b"STS0".to_vec(),
262 };
263 let unknown_uuid_else = ElseOp {
264 body: uuid_or.to_bytes(),
265 };
266
267 let cxl_if_op = IfOp {
268 predicate: cxl_uuid_match.to_bytes(),
269 body: {
270 let mut body = Vec::new();
271 cxl_rev_if_op.append_to_vec(&mut body);
272 cxl_revision_else.append_to_vec(&mut body);
273 body
274 },
275 };
276
277 ElseOp {
278 body: {
279 let mut body = Vec::new();
280 cxl_if_op.append_to_vec(&mut body);
281 unknown_uuid_else.append_to_vec(&mut body);
282 body
283 },
284 }
285 } else {
286 let uuid_or = OrOp {
288 operand1: b"STS0".to_vec(),
289 operand2: encode_integer(0x04),
290 target_name: b"STS0".to_vec(),
291 };
292 ElseOp {
293 body: uuid_or.to_bytes(),
294 }
295 };
296
297 let store_zero = StoreOp {
299 source: encode_integer(0),
300 destination: b"STS0".to_vec(),
301 };
302 let if_op = IfOp {
303 predicate: lequal.to_bytes(),
304 body: store_zero.to_bytes(),
305 };
306 osc_method.add_operation(&if_op);
307 osc_method.add_operation(&else_body);
308
309 osc_method.add_operation(&ReturnOp {
311 result: encode_arg(3),
312 });
313
314 pcie.add_object(&osc_method);
315
316 if preserve_boot_config {
329 let mut dsm_method = Method::new(b"_DSM");
330 dsm_method.set_arg_count(4);
331
332 let dsm_uuid = guid::guid!("E5C937D0-3553-4D7A-9117-EA4D19C3434D");
333 let dsm_uuid_buffer = Buffer(dsm_uuid.as_bytes()).to_bytes();
334
335 let uuid_match = LEqualOp {
337 left: encode_arg(0),
338 right: dsm_uuid_buffer,
339 };
340
341 let fn0_match = LEqualOp {
344 left: encode_arg(2),
345 right: encode_integer(0),
346 };
347 let fn0_return = ReturnOp {
348 result: Buffer(&[0x21u8]).to_bytes(),
349 };
350 let fn0_if = IfOp {
351 predicate: fn0_match.to_bytes(),
352 body: fn0_return.to_bytes(),
353 };
354
355 let fn5_match = LEqualOp {
357 left: encode_arg(2),
358 right: encode_integer(5),
359 };
360 let fn5_return = ReturnOp {
361 result: encode_integer(0),
362 };
363 let fn5_if = IfOp {
364 predicate: fn5_match.to_bytes(),
365 body: fn5_return.to_bytes(),
366 };
367
368 let uuid_if = IfOp {
369 predicate: uuid_match.to_bytes(),
370 body: {
371 let mut body = Vec::new();
372 fn0_if.append_to_vec(&mut body);
373 fn5_if.append_to_vec(&mut body);
374 body
375 },
376 };
377
378 dsm_method.add_operation(&uuid_if);
379
380 dsm_method.add_operation(&ReturnOp {
382 result: Buffer(&[] as &[u8]).to_bytes(),
383 });
384
385 pcie.add_object(&dsm_method);
386 }
387
388 let mut crs = CurrentResourceSettings::new();
389 crs.add_resource(&BusNumber::new(
390 start_bus.into(),
391 (end_bus as u16) - (start_bus as u16) + 1,
392 ));
393 crs.add_resource(&QwordMemory::new(
394 low_mmio.start(),
395 low_mmio.end() - low_mmio.start(),
396 ));
397 crs.add_resource(&QwordMemory::new(
398 high_mmio.start(),
399 high_mmio.end() - high_mmio.start(),
400 ));
401 pcie.add_object(&crs);
402
403 self.add_object(&pcie);
404 self.pcie_ecam_ranges.push(ecam_range);
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411 use crate::aml::test_helpers::verify_expected_bytes;
412
413 fn verify_header(bytes: &[u8]) {
414 assert!(bytes.len() >= 36);
415
416 assert_eq!(bytes[0], b'S');
418 assert_eq!(bytes[1], b'S');
419 assert_eq!(bytes[2], b'D');
420 assert_eq!(bytes[3], b'T');
421
422 let ssdt_len = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
424 assert_eq!(ssdt_len as usize, bytes.len());
425
426 assert_eq!(bytes[8], 2);
428
429 let mut checksum: u8 = 0;
431 for byte in bytes.iter() {
432 checksum = checksum.wrapping_add(*byte);
433 }
434 assert_eq!(checksum, 0);
435
436 assert_eq!(bytes[10], b'M');
438 assert_eq!(bytes[11], b'S');
439 assert_eq!(bytes[12], b'F');
440 assert_eq!(bytes[13], b'T');
441 assert_eq!(bytes[14], b'V');
442 assert_eq!(bytes[15], b'M');
443
444 assert_eq!(bytes[16], b'S');
446 assert_eq!(bytes[17], b'S');
447 assert_eq!(bytes[18], b'D');
448 assert_eq!(bytes[19], b'T');
449 assert_eq!(bytes[20], b'0');
450 assert_eq!(bytes[21], b'1');
451 assert_eq!(bytes[22], 0);
452 assert_eq!(bytes[23], 0);
453
454 let oem_revision = u32::from_le_bytes(bytes[24..28].try_into().unwrap());
456 assert_eq!(oem_revision, 1);
457
458 assert_eq!(bytes[28], b'M');
460 assert_eq!(bytes[29], b'S');
461 assert_eq!(bytes[30], b'F');
462 assert_eq!(bytes[31], b'T');
463
464 let creator_rev = u32::from_le_bytes(bytes[32..36].try_into().unwrap());
466 assert_eq!(creator_rev, 0x01000000);
467 }
468
469 #[test]
470 fn verify_pcie_name_encoding() {
471 assert_eq!(encode_pcie_name(0), b"PCI0".to_vec());
472 assert_eq!(encode_pcie_name(1), b"PCI1".to_vec());
473 assert_eq!(encode_pcie_name(2), b"PCI2".to_vec());
474 assert_eq!(encode_pcie_name(54), b"PC54".to_vec());
475 assert_eq!(encode_pcie_name(294), b"P294".to_vec());
476 }
477
478 #[test]
479 fn verify_simple_table() {
480 let mut ssdt = Ssdt::new();
481 let nobj = NamedObject::new(b"_S0", &Package(vec![0, 0]));
482 ssdt.add_object(&nobj);
483 let bytes = ssdt.to_bytes();
484 verify_header(&bytes);
485 verify_expected_bytes(&bytes[36..], &[8, b'_', b'S', b'0', b'_', 0x12, 4, 2, 0, 0]);
486 }
487
488 fn test_pcie_entry(vnode: Option<u32>) -> PcieHostBridgeEntry {
489 PcieHostBridgeEntry {
490 index: 0,
491 segment: 0,
492 start_bus: 0,
493 end_bus: 255,
494 ecam_range: MemoryRange::new(0x1000_0000..0x2000_0000),
495 low_mmio: MemoryRange::new(0xdc00_0000..0xe000_0000),
496 high_mmio: MemoryRange::new(0x10_0000_0000..0x10_4000_0000),
497 cxl: false,
498 vnode,
499 preserve_boot_config: false,
500 }
501 }
502
503 fn contains_name(bytes: &[u8], name: &[u8; 4]) -> bool {
504 bytes.windows(4).any(|w| w == name)
505 }
506
507 fn contains_bytes(bytes: &[u8], needle: &[u8]) -> bool {
508 bytes.windows(needle.len()).any(|w| w == needle)
509 }
510
511 #[test]
512 fn pcie_includes_cca() {
513 let mut ssdt = Ssdt::new();
514 ssdt.add_pcie(test_pcie_entry(None));
515
516 let bytes = ssdt.to_bytes();
517 verify_header(&bytes);
518 assert!(
519 bytes
520 .windows(6)
521 .any(|window| window == [8, b'_', b'C', b'C', b'A', 1,])
522 );
523 }
524
525 #[test]
526 fn pcie_no_pxm_when_vnode_none() {
527 let mut ssdt = Ssdt::new();
528 ssdt.add_pcie(test_pcie_entry(None));
529
530 let bytes = ssdt.to_bytes();
531 verify_header(&bytes);
532 assert!(
534 !bytes.windows(4).any(|window| window == *b"_PXM"),
535 "_PXM should not be emitted when vnode is None"
536 );
537 }
538
539 #[test]
540 fn pcie_includes_pxm() {
541 let mut ssdt = Ssdt::new();
542 ssdt.add_pcie(test_pcie_entry(Some(0)));
543
544 let bytes = ssdt.to_bytes();
545 verify_header(&bytes);
546 assert!(
548 bytes
549 .windows(6)
550 .any(|window| window == [8, b'_', b'P', b'X', b'M', 0,])
551 );
552 }
553
554 #[test]
555 fn pcie_pxm_nonzero_node() {
556 let mut ssdt = Ssdt::new();
557 ssdt.add_pcie(test_pcie_entry(Some(3)));
558
559 let bytes = ssdt.to_bytes();
560 verify_header(&bytes);
561 assert!(
563 bytes
564 .windows(7)
565 .any(|window| window == [8, b'_', b'P', b'X', b'M', 0x0a, 3,])
566 );
567 }
568
569 #[test]
570 fn pcie_dsm_present_when_preserve_boot_config() {
571 let mut ssdt = Ssdt::new();
572 ssdt.add_pcie(PcieHostBridgeEntry {
573 preserve_boot_config: true,
574 ..test_pcie_entry(None)
575 });
576
577 let bytes = ssdt.to_bytes();
578 verify_header(&bytes);
579
580 assert!(contains_name(&bytes, b"_DSM"));
582
583 let uuid = guid::guid!("E5C937D0-3553-4D7A-9117-EA4D19C3434D");
586 assert!(contains_bytes(&bytes, uuid.as_bytes()));
587
588 assert!(contains_bytes(&bytes, &[0x21]));
591 }
592
593 #[test]
594 fn pcie_dsm_absent_without_preserve_boot_config() {
595 let mut ssdt = Ssdt::new();
596 ssdt.add_pcie(PcieHostBridgeEntry {
597 preserve_boot_config: false,
598 ..test_pcie_entry(None)
599 });
600
601 let bytes = ssdt.to_bytes();
602 verify_header(&bytes);
603
604 assert!(!contains_name(&bytes, b"_DSM"));
606
607 let uuid = guid::guid!("E5C937D0-3553-4D7A-9117-EA4D19C3434D");
609 assert!(!contains_bytes(&bytes, uuid.as_bytes()));
610 }
611}