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