xref: /cloud-hypervisor/vmm/src/pci_segment.rs (revision ea4693a09123234951ae1516f112c5cfce5032ca)
1 // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 //
3 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE-BSD-3-Clause file.
6 //
7 // Copyright © 2019 - 2021 Intel Corporation
8 //
9 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
10 //
11 
12 use std::sync::{Arc, Mutex};
13 
14 use acpi_tables::{aml, Aml};
15 use arch::layout;
16 use pci::{DeviceRelocation, PciBdf, PciBus, PciConfigMmio, PciRoot};
17 #[cfg(target_arch = "x86_64")]
18 use pci::{PciConfigIo, PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE};
19 use uuid::Uuid;
20 use vm_allocator::AddressAllocator;
21 use vm_device::BusDeviceSync;
22 
23 use crate::device_manager::{AddressManager, DeviceManagerError, DeviceManagerResult};
24 
25 pub(crate) struct PciSegment {
26     pub(crate) id: u16,
27     pub(crate) pci_bus: Arc<Mutex<PciBus>>,
28     pub(crate) pci_config_mmio: Arc<Mutex<PciConfigMmio>>,
29     pub(crate) mmio_config_address: u64,
30     pub(crate) proximity_domain: u32,
31 
32     #[cfg(target_arch = "x86_64")]
33     pub(crate) pci_config_io: Option<Arc<Mutex<PciConfigIo>>>,
34 
35     // Bitmap of PCI devices to hotplug.
36     pub(crate) pci_devices_up: u32,
37     // Bitmap of PCI devices to hotunplug.
38     pub(crate) pci_devices_down: u32,
39     // List of allocated IRQs for each PCI slot.
40     pub(crate) pci_irq_slots: [u8; 32],
41 
42     // Device memory covered by this segment
43     pub(crate) start_of_mem32_area: u64,
44     pub(crate) end_of_mem32_area: u64,
45 
46     pub(crate) start_of_mem64_area: u64,
47     pub(crate) end_of_mem64_area: u64,
48 
49     pub(crate) mem32_allocator: Arc<Mutex<AddressAllocator>>,
50     pub(crate) mem64_allocator: Arc<Mutex<AddressAllocator>>,
51 }
52 
53 impl PciSegment {
54     pub(crate) fn new(
55         id: u16,
56         numa_node: u32,
57         address_manager: &Arc<AddressManager>,
58         mem32_allocator: Arc<Mutex<AddressAllocator>>,
59         mem64_allocator: Arc<Mutex<AddressAllocator>>,
60         pci_irq_slots: &[u8; 32],
61     ) -> DeviceManagerResult<PciSegment> {
62         let pci_root = PciRoot::new(None);
63         let pci_bus = Arc::new(Mutex::new(PciBus::new(
64             pci_root,
65             Arc::clone(address_manager) as Arc<dyn DeviceRelocation>,
66         )));
67 
68         let pci_config_mmio = Arc::new(Mutex::new(PciConfigMmio::new(Arc::clone(&pci_bus))));
69         let mmio_config_address =
70             layout::PCI_MMCONFIG_START.0 + layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT * id as u64;
71 
72         address_manager
73             .mmio_bus
74             .insert(
75                 Arc::clone(&pci_config_mmio) as Arc<dyn BusDeviceSync>,
76                 mmio_config_address,
77                 layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
78             )
79             .map_err(DeviceManagerError::BusError)?;
80 
81         let start_of_mem32_area = mem32_allocator.lock().unwrap().base().0;
82         let end_of_mem32_area = mem32_allocator.lock().unwrap().end().0;
83 
84         let start_of_mem64_area = mem64_allocator.lock().unwrap().base().0;
85         let end_of_mem64_area = mem64_allocator.lock().unwrap().end().0;
86 
87         let segment = PciSegment {
88             id,
89             pci_bus,
90             pci_config_mmio,
91             mmio_config_address,
92             proximity_domain: numa_node,
93             pci_devices_up: 0,
94             pci_devices_down: 0,
95             #[cfg(target_arch = "x86_64")]
96             pci_config_io: None,
97             mem32_allocator,
98             mem64_allocator,
99             start_of_mem32_area,
100             end_of_mem32_area,
101             start_of_mem64_area,
102             end_of_mem64_area,
103             pci_irq_slots: *pci_irq_slots,
104         };
105 
106         info!(
107             "Adding PCI segment: id={}, PCI MMIO config address: 0x{:x}, mem32 area [0x{:x}-0x{:x}, mem64 area [0x{:x}-0x{:x}",
108             segment.id, segment.mmio_config_address, segment.start_of_mem32_area, segment.end_of_mem32_area, segment.start_of_mem64_area, segment.end_of_mem64_area
109         );
110         Ok(segment)
111     }
112 
113     #[cfg(target_arch = "x86_64")]
114     pub(crate) fn new_default_segment(
115         address_manager: &Arc<AddressManager>,
116         mem32_allocator: Arc<Mutex<AddressAllocator>>,
117         mem64_allocator: Arc<Mutex<AddressAllocator>>,
118         pci_irq_slots: &[u8; 32],
119     ) -> DeviceManagerResult<PciSegment> {
120         let mut segment = Self::new(
121             0,
122             0,
123             address_manager,
124             mem32_allocator,
125             mem64_allocator,
126             pci_irq_slots,
127         )?;
128         let pci_config_io = Arc::new(Mutex::new(PciConfigIo::new(Arc::clone(&segment.pci_bus))));
129 
130         address_manager
131             .io_bus
132             .insert(
133                 pci_config_io.clone(),
134                 PCI_CONFIG_IO_PORT,
135                 PCI_CONFIG_IO_PORT_SIZE,
136             )
137             .map_err(DeviceManagerError::BusError)?;
138 
139         segment.pci_config_io = Some(pci_config_io);
140 
141         Ok(segment)
142     }
143 
144     #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
145     pub(crate) fn new_default_segment(
146         address_manager: &Arc<AddressManager>,
147         mem32_allocator: Arc<Mutex<AddressAllocator>>,
148         mem64_allocator: Arc<Mutex<AddressAllocator>>,
149         pci_irq_slots: &[u8; 32],
150     ) -> DeviceManagerResult<PciSegment> {
151         Self::new(
152             0,
153             0,
154             address_manager,
155             mem32_allocator,
156             mem64_allocator,
157             pci_irq_slots,
158         )
159     }
160 
161     pub(crate) fn next_device_bdf(&self) -> DeviceManagerResult<PciBdf> {
162         Ok(PciBdf::new(
163             self.id,
164             0,
165             self.pci_bus
166                 .lock()
167                 .unwrap()
168                 .next_device_id()
169                 .map_err(DeviceManagerError::NextPciDeviceId)? as u8,
170             0,
171         ))
172     }
173 
174     pub fn reserve_legacy_interrupts_for_pci_devices(
175         address_manager: &Arc<AddressManager>,
176         pci_irq_slots: &mut [u8; 32],
177     ) -> DeviceManagerResult<()> {
178         // Reserve 8 IRQs which will be shared across all PCI devices.
179         let num_irqs = 8;
180         let mut irqs: Vec<u8> = Vec::new();
181         for _ in 0..num_irqs {
182             irqs.push(
183                 address_manager
184                     .allocator
185                     .lock()
186                     .unwrap()
187                     .allocate_irq()
188                     .ok_or(DeviceManagerError::AllocateIrq)? as u8,
189             );
190         }
191 
192         // There are 32 devices on the PCI bus, let's assign them an IRQ.
193         for i in 0..32 {
194             pci_irq_slots[i] = irqs[i % num_irqs];
195         }
196 
197         Ok(())
198     }
199 }
200 
201 struct PciDevSlot {
202     device_id: u8,
203 }
204 
205 impl Aml for PciDevSlot {
206     fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
207         let sun = self.device_id;
208         let adr: u32 = (self.device_id as u32) << 16;
209         aml::Device::new(
210             format!("S{:03}", self.device_id).as_str().into(),
211             vec![
212                 &aml::Name::new("_SUN".into(), &sun),
213                 &aml::Name::new("_ADR".into(), &adr),
214                 &aml::Method::new(
215                     "_EJ0".into(),
216                     1,
217                     true,
218                     vec![&aml::MethodCall::new(
219                         "\\_SB_.PHPR.PCEJ".into(),
220                         vec![&aml::Path::new("_SUN"), &aml::Path::new("_SEG")],
221                     )],
222                 ),
223             ],
224         )
225         .to_aml_bytes(sink)
226     }
227 }
228 
229 struct PciDevSlotNotify {
230     device_id: u8,
231 }
232 
233 impl Aml for PciDevSlotNotify {
234     fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
235         let device_id_mask: u32 = 1 << self.device_id;
236         let object = aml::Path::new(&format!("S{:03}", self.device_id));
237         aml::And::new(&aml::Local(0), &aml::Arg(0), &device_id_mask).to_aml_bytes(sink);
238         aml::If::new(
239             &aml::Equal::new(&aml::Local(0), &device_id_mask),
240             vec![&aml::Notify::new(&object, &aml::Arg(1))],
241         )
242         .to_aml_bytes(sink);
243     }
244 }
245 
246 struct PciDevSlotMethods {}
247 
248 impl Aml for PciDevSlotMethods {
249     fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
250         let mut device_notifies = Vec::new();
251         for device_id in 0..32 {
252             device_notifies.push(PciDevSlotNotify { device_id });
253         }
254 
255         let mut device_notifies_refs: Vec<&dyn Aml> = Vec::new();
256         for device_notify in device_notifies.iter() {
257             device_notifies_refs.push(device_notify);
258         }
259 
260         aml::Method::new("DVNT".into(), 2, true, device_notifies_refs).to_aml_bytes(sink);
261         aml::Method::new(
262             "PCNT".into(),
263             0,
264             true,
265             vec![
266                 &aml::Acquire::new("\\_SB_.PHPR.BLCK".into(), 0xffff),
267                 &aml::Store::new(&aml::Path::new("\\_SB_.PHPR.PSEG"), &aml::Path::new("_SEG")),
268                 &aml::MethodCall::new(
269                     "DVNT".into(),
270                     vec![&aml::Path::new("\\_SB_.PHPR.PCIU"), &aml::ONE],
271                 ),
272                 &aml::MethodCall::new(
273                     "DVNT".into(),
274                     vec![&aml::Path::new("\\_SB_.PHPR.PCID"), &3usize],
275                 ),
276                 &aml::Release::new("\\_SB_.PHPR.BLCK".into()),
277             ],
278         )
279         .to_aml_bytes(sink)
280     }
281 }
282 
283 struct PciDsmMethod {}
284 
285 impl Aml for PciDsmMethod {
286     fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
287         // Refer to ACPI spec v6.3 Ch 9.1.1 and PCI Firmware spec v3.3 Ch 4.6.1
288         // _DSM (Device Specific Method), the following is the implementation in ASL.
289         /*
290         Method (_DSM, 4, NotSerialized)  // _DSM: Device-Specific Method
291         {
292               If ((Arg0 == ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */))
293               {
294                   If ((Arg2 == Zero))
295                   {
296                       Return (Buffer (One) { 0x21 })
297                   }
298                   If ((Arg2 == 0x05))
299                   {
300                       Return (Zero)
301                   }
302               }
303 
304               Return (Buffer (One) { 0x00 })
305         }
306          */
307         /*
308          * As per ACPI v6.3 Ch 19.6.142, the UUID is required to be in mixed endian:
309          * Among the fields of a UUID:
310          *   {d1 (8 digits)} - {d2 (4 digits)} - {d3 (4 digits)} - {d4 (16 digits)}
311          * d1 ~ d3 need to be little endian, d4 be big endian.
312          * See https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding .
313          */
314         let uuid = Uuid::parse_str("E5C937D0-3553-4D7A-9117-EA4D19C3434D").unwrap();
315         let (uuid_d1, uuid_d2, uuid_d3, uuid_d4) = uuid.as_fields();
316         let mut uuid_buf = vec![];
317         uuid_buf.extend(uuid_d1.to_le_bytes());
318         uuid_buf.extend(uuid_d2.to_le_bytes());
319         uuid_buf.extend(uuid_d3.to_le_bytes());
320         uuid_buf.extend(uuid_d4);
321         aml::Method::new(
322             "_DSM".into(),
323             4,
324             false,
325             vec![
326                 &aml::If::new(
327                     &aml::Equal::new(&aml::Arg(0), &aml::BufferData::new(uuid_buf)),
328                     vec![
329                         &aml::If::new(
330                             &aml::Equal::new(&aml::Arg(2), &aml::ZERO),
331                             vec![&aml::Return::new(&aml::BufferData::new(vec![0x21]))],
332                         ),
333                         &aml::If::new(
334                             &aml::Equal::new(&aml::Arg(2), &0x05u8),
335                             vec![&aml::Return::new(&aml::ZERO)],
336                         ),
337                     ],
338                 ),
339                 &aml::Return::new(&aml::BufferData::new(vec![0])),
340             ],
341         )
342         .to_aml_bytes(sink)
343     }
344 }
345 
346 impl Aml for PciSegment {
347     fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
348         let mut pci_dsdt_inner_data: Vec<&dyn Aml> = Vec::new();
349         let hid = aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08"));
350         pci_dsdt_inner_data.push(&hid);
351         let cid = aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0A03"));
352         pci_dsdt_inner_data.push(&cid);
353         let adr = aml::Name::new("_ADR".into(), &aml::ZERO);
354         pci_dsdt_inner_data.push(&adr);
355         let seg = aml::Name::new("_SEG".into(), &self.id);
356         pci_dsdt_inner_data.push(&seg);
357         let uid = aml::Name::new("_UID".into(), &aml::ZERO);
358         pci_dsdt_inner_data.push(&uid);
359         let cca = aml::Name::new("_CCA".into(), &aml::ONE);
360         pci_dsdt_inner_data.push(&cca);
361         let supp = aml::Name::new("SUPP".into(), &aml::ZERO);
362         pci_dsdt_inner_data.push(&supp);
363 
364         let proximity_domain = self.proximity_domain;
365         let pxm_return = aml::Return::new(&proximity_domain);
366         let pxm = aml::Method::new("_PXM".into(), 0, false, vec![&pxm_return]);
367         pci_dsdt_inner_data.push(&pxm);
368 
369         let pci_dsm = PciDsmMethod {};
370         pci_dsdt_inner_data.push(&pci_dsm);
371 
372         #[allow(clippy::if_same_then_else)]
373         let crs = if self.id == 0 {
374             aml::Name::new(
375                 "_CRS".into(),
376                 &aml::ResourceTemplate::new(vec![
377                     &aml::AddressSpace::new_bus_number(0x0u16, 0x0u16),
378                     #[cfg(target_arch = "x86_64")]
379                     &aml::IO::new(0xcf8, 0xcf8, 1, 0x8),
380                     &aml::Memory32Fixed::new(
381                         true,
382                         self.mmio_config_address as u32,
383                         layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT as u32,
384                     ),
385                     &aml::AddressSpace::new_memory(
386                         aml::AddressSpaceCacheable::NotCacheable,
387                         true,
388                         self.start_of_mem32_area,
389                         self.end_of_mem32_area,
390                         None,
391                     ),
392                     &aml::AddressSpace::new_memory(
393                         aml::AddressSpaceCacheable::NotCacheable,
394                         true,
395                         self.start_of_mem64_area,
396                         self.end_of_mem64_area,
397                         None,
398                     ),
399                     #[cfg(target_arch = "x86_64")]
400                     &aml::AddressSpace::new_io(0u16, 0x0cf7u16, None),
401                     #[cfg(target_arch = "x86_64")]
402                     &aml::AddressSpace::new_io(0x0d00u16, 0xffffu16, None),
403                 ]),
404             )
405         } else {
406             aml::Name::new(
407                 "_CRS".into(),
408                 &aml::ResourceTemplate::new(vec![
409                     &aml::AddressSpace::new_bus_number(0x0u16, 0x0u16),
410                     &aml::Memory32Fixed::new(
411                         true,
412                         self.mmio_config_address as u32,
413                         layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT as u32,
414                     ),
415                     &aml::AddressSpace::new_memory(
416                         aml::AddressSpaceCacheable::NotCacheable,
417                         true,
418                         self.start_of_mem32_area,
419                         self.end_of_mem32_area,
420                         None,
421                     ),
422                     &aml::AddressSpace::new_memory(
423                         aml::AddressSpaceCacheable::NotCacheable,
424                         true,
425                         self.start_of_mem64_area,
426                         self.end_of_mem64_area,
427                         None,
428                     ),
429                 ]),
430             )
431         };
432         pci_dsdt_inner_data.push(&crs);
433 
434         let mut pci_devices = Vec::new();
435         for device_id in 0..32 {
436             let pci_device = PciDevSlot { device_id };
437             pci_devices.push(pci_device);
438         }
439         for pci_device in pci_devices.iter() {
440             pci_dsdt_inner_data.push(pci_device);
441         }
442 
443         let pci_device_methods = PciDevSlotMethods {};
444         pci_dsdt_inner_data.push(&pci_device_methods);
445 
446         // Build PCI routing table, listing IRQs assigned to PCI devices.
447         let prt_package_list: Vec<(u32, u32)> = self
448             .pci_irq_slots
449             .iter()
450             .enumerate()
451             .map(|(i, irq)| (((((i as u32) & 0x1fu32) << 16) | 0xffffu32), *irq as u32))
452             .collect();
453         let prt_package_list: Vec<aml::Package> = prt_package_list
454             .iter()
455             .map(|(bdf, irq)| aml::Package::new(vec![bdf, &0u8, &0u8, irq]))
456             .collect();
457         let prt_package_list: Vec<&dyn Aml> = prt_package_list
458             .iter()
459             .map(|item| item as &dyn Aml)
460             .collect();
461         let prt = aml::Name::new("_PRT".into(), &aml::Package::new(prt_package_list));
462         pci_dsdt_inner_data.push(&prt);
463 
464         aml::Device::new(
465             format!("_SB_.PC{:02X}", self.id).as_str().into(),
466             pci_dsdt_inner_data,
467         )
468         .to_aml_bytes(sink)
469     }
470 }
471