1use std::sync::{Arc, Mutex};
13
14#[cfg(target_arch = "x86_64")]
15use acpi_tables::{Aml, aml};
16use log::info;
17use pci::PciBdf;
18#[cfg(target_arch = "x86_64")]
19use uuid::Uuid;
20use vm_allocator::AddressAllocator;
21
22use crate::arch::{ArchVm as Vm, PCI_MMCONFIG_START, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT};
23#[cfg(target_arch = "x86_64")]
24use crate::pci::bus::{PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE};
25use crate::pci::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
26use crate::vstate::bus::{BusDeviceSync, BusError};
27use crate::vstate::resources::ResourceAllocator;
28
29pub struct PciSegment {
30 pub(crate) id: u16,
31 pub(crate) pci_bus: Arc<Mutex<PciBus>>,
32 pub(crate) pci_config_mmio: Arc<Mutex<PciConfigMmio>>,
33 pub(crate) mmio_config_address: u64,
34 pub(crate) proximity_domain: u32,
35
36 #[cfg(target_arch = "x86_64")]
37 pub(crate) pci_config_io: Option<Arc<Mutex<PciConfigIo>>>,
38
39 pub(crate) pci_devices_up: u32,
41 pub(crate) pci_devices_down: u32,
43 pub(crate) pci_irq_slots: [u8; 32],
45
46 pub(crate) start_of_mem32_area: u64,
48 pub(crate) end_of_mem32_area: u64,
49
50 pub(crate) start_of_mem64_area: u64,
51 pub(crate) end_of_mem64_area: u64,
52}
53
54impl std::fmt::Debug for PciSegment {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.debug_struct("PciSegment")
57 .field("id", &self.id)
58 .field("mmio_config_address", &self.mmio_config_address)
59 .field("proximity_domain", &self.proximity_domain)
60 .field("pci_devices_up", &self.pci_devices_up)
61 .field("pci_devices_down", &self.pci_devices_down)
62 .field("pci_irq_slots", &self.pci_irq_slots)
63 .field("start_of_mem32_area", &self.start_of_mem32_area)
64 .field("end_of_mem32_area", &self.end_of_mem32_area)
65 .field("start_of_mem64_area", &self.start_of_mem64_area)
66 .field("end_of_mem64_area", &self.end_of_mem64_area)
67 .finish()
68 }
69}
70
71impl PciSegment {
72 fn build(id: u16, vm: &Arc<Vm>, pci_irq_slots: &[u8; 32]) -> Result<PciSegment, BusError> {
73 let pci_root = PciRoot::new(None);
74 let pci_bus = Arc::new(Mutex::new(PciBus::new(pci_root, vm.clone())));
75
76 let pci_config_mmio = Arc::new(Mutex::new(PciConfigMmio::new(Arc::clone(&pci_bus))));
77 let mmio_config_address = PCI_MMCONFIG_START + PCI_MMIO_CONFIG_SIZE_PER_SEGMENT * id as u64;
78
79 vm.common.mmio_bus.insert(
80 Arc::clone(&pci_config_mmio) as Arc<dyn BusDeviceSync>,
81 mmio_config_address,
82 PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
83 )?;
84
85 let resource_allocator = vm.resource_allocator();
86
87 let start_of_mem32_area = resource_allocator.mmio32_memory.base();
88 let end_of_mem32_area = resource_allocator.mmio32_memory.end();
89
90 let start_of_mem64_area = resource_allocator.mmio64_memory.base();
91 let end_of_mem64_area = resource_allocator.mmio64_memory.end();
92
93 let segment = PciSegment {
94 id,
95 pci_bus,
96 pci_config_mmio,
97 mmio_config_address,
98 proximity_domain: 0,
99 pci_devices_up: 0,
100 pci_devices_down: 0,
101 #[cfg(target_arch = "x86_64")]
102 pci_config_io: None,
103 start_of_mem32_area,
104 end_of_mem32_area,
105 start_of_mem64_area,
106 end_of_mem64_area,
107 pci_irq_slots: *pci_irq_slots,
108 };
109
110 Ok(segment)
111 }
112
113 #[cfg(target_arch = "x86_64")]
114 pub(crate) fn new(
115 id: u16,
116 vm: &Arc<Vm>,
117 pci_irq_slots: &[u8; 32],
118 ) -> Result<PciSegment, BusError> {
119 use crate::Vm;
120
121 let mut segment = Self::build(id, vm, pci_irq_slots)?;
122 let pci_config_io = Arc::new(Mutex::new(PciConfigIo::new(Arc::clone(&segment.pci_bus))));
123
124 vm.pio_bus.insert(
125 pci_config_io.clone(),
126 PCI_CONFIG_IO_PORT,
127 PCI_CONFIG_IO_PORT_SIZE,
128 )?;
129
130 segment.pci_config_io = Some(pci_config_io);
131
132 info!(
133 "pci: adding PCI segment: id={:#x}, PCI MMIO config address: {:#x}, mem32 area: \
134 [{:#x}-{:#x}], mem64 area: [{:#x}-{:#x}] IO area: [{PCI_CONFIG_IO_PORT:#x}-{:#x}]",
135 segment.id,
136 segment.mmio_config_address,
137 segment.start_of_mem32_area,
138 segment.end_of_mem32_area,
139 segment.start_of_mem64_area,
140 segment.end_of_mem64_area,
141 PCI_CONFIG_IO_PORT + PCI_CONFIG_IO_PORT_SIZE - 1
142 );
143
144 Ok(segment)
145 }
146
147 #[cfg(target_arch = "aarch64")]
148 pub(crate) fn new(
149 id: u16,
150 vm: &Arc<Vm>,
151 pci_irq_slots: &[u8; 32],
152 ) -> Result<PciSegment, BusError> {
153 let segment = Self::build(id, vm, pci_irq_slots)?;
154 info!(
155 "pci: adding PCI segment: id={:#x}, PCI MMIO config address: {:#x}, mem32 area: \
156 [{:#x}-{:#x}], mem64 area: [{:#x}-{:#x}]",
157 segment.id,
158 segment.mmio_config_address,
159 segment.start_of_mem32_area,
160 segment.end_of_mem32_area,
161 segment.start_of_mem64_area,
162 segment.end_of_mem64_area,
163 );
164
165 Ok(segment)
166 }
167
168 pub(crate) fn next_device_bdf(&self) -> Result<PciBdf, PciRootError> {
169 Ok(PciBdf::new(
170 self.id,
171 0,
172 self.pci_bus
173 .lock()
174 .unwrap()
175 .next_device_id()?
176 .try_into()
177 .unwrap(),
178 0,
179 ))
180 }
181}
182
183#[cfg(target_arch = "x86_64")]
184struct PciDevSlot {
185 device_id: u8,
186}
187
188#[cfg(target_arch = "x86_64")]
189impl Aml for PciDevSlot {
190 fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
191 let sun = self.device_id;
192 let adr: u32 = (self.device_id as u32) << 16;
193 aml::Device::new(
194 format!("S{:03}", self.device_id).as_str().try_into()?,
195 vec![
196 &aml::Name::new("_SUN".try_into()?, &sun)?,
197 &aml::Name::new("_ADR".try_into()?, &adr)?,
198 &aml::Method::new(
199 "_EJ0".try_into()?,
200 1,
201 true,
202 vec![&aml::MethodCall::new(
203 "\\_SB_.PHPR.PCEJ".try_into()?,
204 vec![&aml::Path::new("_SUN")?, &aml::Path::new("_SEG")?],
205 )],
206 ),
207 ],
208 )
209 .append_aml_bytes(v)
210 }
211}
212
213#[cfg(target_arch = "x86_64")]
214struct PciDevSlotNotify {
215 device_id: u8,
216}
217
218#[cfg(target_arch = "x86_64")]
219impl Aml for PciDevSlotNotify {
220 fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
221 let device_id_mask: u32 = 1 << self.device_id;
222 let object = aml::Path::new(&format!("S{:03}", self.device_id))?;
223 aml::And::new(&aml::Local(0), &aml::Arg(0), &device_id_mask).append_aml_bytes(v)?;
224 aml::If::new(
225 &aml::Equal::new(&aml::Local(0), &device_id_mask),
226 vec![&aml::Notify::new(&object, &aml::Arg(1))],
227 )
228 .append_aml_bytes(v)
229 }
230}
231
232#[cfg(target_arch = "x86_64")]
233struct PciDevSlotMethods {}
234
235#[cfg(target_arch = "x86_64")]
236impl Aml for PciDevSlotMethods {
237 fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
238 let mut device_notifies = Vec::new();
239 for device_id in 0..32 {
240 device_notifies.push(PciDevSlotNotify { device_id });
241 }
242
243 let mut device_notifies_refs: Vec<&dyn Aml> = Vec::new();
244 for device_notify in device_notifies.iter() {
245 device_notifies_refs.push(device_notify);
246 }
247
248 aml::Method::new("DVNT".try_into()?, 2, true, device_notifies_refs).append_aml_bytes(v)?;
249 aml::Method::new(
250 "PCNT".try_into()?,
251 0,
252 true,
253 vec![
254 &aml::Acquire::new("\\_SB_.PHPR.BLCK".try_into()?, 0xffff),
255 &aml::Store::new(
256 &aml::Path::new("\\_SB_.PHPR.PSEG")?,
257 &aml::Path::new("_SEG")?,
258 ),
259 &aml::MethodCall::new(
260 "DVNT".try_into()?,
261 vec![&aml::Path::new("\\_SB_.PHPR.PCIU")?, &aml::ONE],
262 ),
263 &aml::MethodCall::new(
264 "DVNT".try_into()?,
265 vec![&aml::Path::new("\\_SB_.PHPR.PCID")?, &3usize],
266 ),
267 &aml::Release::new("\\_SB_.PHPR.BLCK".try_into()?),
268 ],
269 )
270 .append_aml_bytes(v)
271 }
272}
273
274#[cfg(target_arch = "x86_64")]
275struct PciDsmMethod {}
276
277#[cfg(target_arch = "x86_64")]
278impl Aml for PciDsmMethod {
279 fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
280 let uuid = Uuid::parse_str("E5C937D0-3553-4D7A-9117-EA4D19C3434D").unwrap();
306 let (uuid_d1, uuid_d2, uuid_d3, uuid_d4) = uuid.as_fields();
307 let mut uuid_buf = vec![];
308 uuid_buf.extend(uuid_d1.to_le_bytes());
309 uuid_buf.extend(uuid_d2.to_le_bytes());
310 uuid_buf.extend(uuid_d3.to_le_bytes());
311 uuid_buf.extend(uuid_d4);
312 aml::Method::new(
313 "_DSM".try_into()?,
314 4,
315 false,
316 vec![
317 &aml::If::new(
318 &aml::Equal::new(&aml::Arg(0), &aml::Buffer::new(uuid_buf)),
319 vec![
320 &aml::If::new(
321 &aml::Equal::new(&aml::Arg(2), &aml::ZERO),
322 vec![&aml::Return::new(&aml::Buffer::new(vec![0x21]))],
323 ),
324 &aml::If::new(
325 &aml::Equal::new(&aml::Arg(2), &0x05u8),
326 vec![&aml::Return::new(&aml::ZERO)],
327 ),
328 ],
329 ),
330 &aml::Return::new(&aml::Buffer::new(vec![0])),
331 ],
332 )
333 .append_aml_bytes(v)
334 }
335}
336
337#[cfg(target_arch = "x86_64")]
338impl Aml for PciSegment {
339 fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
340 let mut pci_dsdt_inner_data: Vec<&dyn Aml> = Vec::new();
341 let hid = aml::Name::new("_HID".try_into()?, &aml::EisaName::new("PNP0A08")?)?;
342 pci_dsdt_inner_data.push(&hid);
343 let cid = aml::Name::new("_CID".try_into()?, &aml::EisaName::new("PNP0A03")?)?;
344 pci_dsdt_inner_data.push(&cid);
345 let adr = aml::Name::new("_ADR".try_into()?, &aml::ZERO)?;
346 pci_dsdt_inner_data.push(&adr);
347 let seg = aml::Name::new("_SEG".try_into()?, &self.id)?;
348 pci_dsdt_inner_data.push(&seg);
349 let uid = aml::Name::new("_UID".try_into()?, &aml::ZERO)?;
350 pci_dsdt_inner_data.push(&uid);
351 let cca = aml::Name::new("_CCA".try_into()?, &aml::ONE)?;
352 pci_dsdt_inner_data.push(&cca);
353 let supp = aml::Name::new("SUPP".try_into()?, &aml::ZERO)?;
354 pci_dsdt_inner_data.push(&supp);
355
356 let proximity_domain = self.proximity_domain;
357 let pxm_return = aml::Return::new(&proximity_domain);
358 let pxm = aml::Method::new("_PXM".try_into()?, 0, false, vec![&pxm_return]);
359 pci_dsdt_inner_data.push(&pxm);
360
361 let pci_dsm = PciDsmMethod {};
362 pci_dsdt_inner_data.push(&pci_dsm);
363
364 #[allow(clippy::if_same_then_else)]
365 let crs = if self.id == 0 {
366 aml::Name::new(
367 "_CRS".try_into()?,
368 &aml::ResourceTemplate::new(vec![
369 &aml::AddressSpace::new_bus_number(0x0u16, 0x0u16)?,
370 &aml::Io::new(0xcf8, 0xcf8, 1, 0x8),
371 &aml::Memory32Fixed::new(
372 true,
373 self.mmio_config_address.try_into().unwrap(),
374 PCI_MMIO_CONFIG_SIZE_PER_SEGMENT.try_into().unwrap(),
375 ),
376 &aml::AddressSpace::new_memory(
377 aml::AddressSpaceCacheable::NotCacheable,
378 true,
379 self.start_of_mem32_area,
380 self.end_of_mem32_area,
381 )?,
382 &aml::AddressSpace::new_memory(
383 aml::AddressSpaceCacheable::NotCacheable,
384 true,
385 self.start_of_mem64_area,
386 self.end_of_mem64_area,
387 )?,
388 &aml::AddressSpace::new_io(0u16, 0x0cf7u16)?,
389 &aml::AddressSpace::new_io(0x0d00u16, 0xffffu16)?,
390 ]),
391 )?
392 } else {
393 aml::Name::new(
394 "_CRS".try_into()?,
395 &aml::ResourceTemplate::new(vec![
396 &aml::AddressSpace::new_bus_number(0x0u16, 0x0u16)?,
397 &aml::Memory32Fixed::new(
398 true,
399 self.mmio_config_address.try_into().unwrap(),
400 PCI_MMIO_CONFIG_SIZE_PER_SEGMENT.try_into().unwrap(),
401 ),
402 &aml::AddressSpace::new_memory(
403 aml::AddressSpaceCacheable::NotCacheable,
404 true,
405 self.start_of_mem32_area,
406 self.end_of_mem32_area,
407 )?,
408 &aml::AddressSpace::new_memory(
409 aml::AddressSpaceCacheable::NotCacheable,
410 true,
411 self.start_of_mem64_area,
412 self.end_of_mem64_area,
413 )?,
414 ]),
415 )?
416 };
417 pci_dsdt_inner_data.push(&crs);
418
419 let mut pci_devices = Vec::new();
420 for device_id in 0..32 {
421 let pci_device = PciDevSlot { device_id };
422 pci_devices.push(pci_device);
423 }
424 for pci_device in pci_devices.iter() {
425 pci_dsdt_inner_data.push(pci_device);
426 }
427
428 let pci_device_methods = PciDevSlotMethods {};
429 pci_dsdt_inner_data.push(&pci_device_methods);
430
431 let prt_package_list: Vec<(u32, u32)> = self
433 .pci_irq_slots
434 .iter()
435 .enumerate()
436 .map(|(i, irq)| {
437 (
438 ((((u32::try_from(i).unwrap()) & 0x1fu32) << 16) | 0xffffu32),
439 *irq as u32,
440 )
441 })
442 .collect();
443 let prt_package_list: Vec<aml::Package> = prt_package_list
444 .iter()
445 .map(|(bdf, irq)| aml::Package::new(vec![bdf, &0u8, &0u8, irq]))
446 .collect();
447 let prt_package_list: Vec<&dyn Aml> = prt_package_list
448 .iter()
449 .map(|item| item as &dyn Aml)
450 .collect();
451 let prt = aml::Name::new("_PRT".try_into()?, &aml::Package::new(prt_package_list))?;
452 pci_dsdt_inner_data.push(&prt);
453
454 aml::Device::new(
455 format!("_SB_.PC{:02X}", self.id).as_str().try_into()?,
456 pci_dsdt_inner_data,
457 )
458 .append_aml_bytes(v)
459 }
460}
461
462#[cfg(test)]
463mod tests {
464
465 use super::*;
466 use crate::arch;
467 use crate::builder::tests::default_vmm;
468 use crate::utils::u64_to_usize;
469
470 #[test]
471 fn test_pci_segment_build() {
472 let vmm = default_vmm();
473 let pci_irq_slots = &[0u8; 32];
474 let pci_segment = PciSegment::new(0, &vmm.vm, pci_irq_slots).unwrap();
475
476 assert_eq!(pci_segment.id, 0);
477 assert_eq!(
478 pci_segment.start_of_mem32_area,
479 arch::MEM_32BIT_DEVICES_START
480 );
481 assert_eq!(
482 pci_segment.end_of_mem32_area,
483 arch::MEM_32BIT_DEVICES_START + arch::MEM_32BIT_DEVICES_SIZE - 1
484 );
485 assert_eq!(
486 pci_segment.start_of_mem64_area,
487 arch::MEM_64BIT_DEVICES_START
488 );
489 assert_eq!(
490 pci_segment.end_of_mem64_area,
491 arch::MEM_64BIT_DEVICES_START + arch::MEM_64BIT_DEVICES_SIZE - 1
492 );
493 assert_eq!(pci_segment.mmio_config_address, arch::PCI_MMCONFIG_START);
494 assert_eq!(pci_segment.proximity_domain, 0);
495 assert_eq!(pci_segment.pci_devices_up, 0);
496 assert_eq!(pci_segment.pci_devices_down, 0);
497 assert_eq!(pci_segment.pci_irq_slots, [0u8; 32]);
498 }
499
500 #[cfg(target_arch = "x86_64")]
501 #[test]
502 fn test_io_bus() {
503 let vmm = default_vmm();
504 let pci_irq_slots = &[0u8; 32];
505 let pci_segment = PciSegment::new(0, &vmm.vm, pci_irq_slots).unwrap();
506
507 let mut data = [0u8; u64_to_usize(PCI_CONFIG_IO_PORT_SIZE)];
508 vmm.vm.pio_bus.read(PCI_CONFIG_IO_PORT, &mut data).unwrap();
509
510 vmm.vm
511 .pio_bus
512 .read(PCI_CONFIG_IO_PORT + PCI_CONFIG_IO_PORT_SIZE, &mut data)
513 .unwrap_err();
514 }
515
516 #[test]
517 fn test_mmio_bus() {
518 let vmm = default_vmm();
519 let pci_irq_slots = &[0u8; 32];
520 let pci_segment = PciSegment::new(0, &vmm.vm, pci_irq_slots).unwrap();
521
522 let mut data = [0u8; u64_to_usize(PCI_MMIO_CONFIG_SIZE_PER_SEGMENT)];
523
524 vmm.vm
525 .common
526 .mmio_bus
527 .read(pci_segment.mmio_config_address, &mut data)
528 .unwrap();
529 vmm.vm
530 .common
531 .mmio_bus
532 .read(
533 pci_segment.mmio_config_address + PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
534 &mut data,
535 )
536 .unwrap_err();
537 }
538
539 #[test]
540 fn test_next_device_bdf() {
541 let vmm = default_vmm();
542 let pci_irq_slots = &[0u8; 32];
543 let pci_segment = PciSegment::new(0, &vmm.vm, pci_irq_slots).unwrap();
544
545 for dev_id in 1..32 {
547 let bdf = pci_segment.next_device_bdf().unwrap();
548 assert_eq!(bdf, PciBdf::new(0, 0, dev_id, 0));
552 }
553
554 pci_segment.next_device_bdf().unwrap_err();
556 }
557}