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