xref: /aosp_15_r20/external/crosvm/arch/src/sys/linux.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap;
6 use std::sync::Arc;
7 
8 use acpi_tables::aml::Aml;
9 use base::syslog;
10 use base::AsRawDescriptors;
11 use base::Tube;
12 use devices::Bus;
13 use devices::BusDevice;
14 use devices::IommuDevType;
15 use devices::IrqChip;
16 use devices::IrqEventSource;
17 use devices::ProxyDevice;
18 use devices::VfioPlatformDevice;
19 use hypervisor::ProtectionType;
20 use hypervisor::Vm;
21 use minijail::Minijail;
22 use resources::AllocOptions;
23 use resources::SystemAllocator;
24 use sync::Mutex;
25 
26 use crate::DeviceRegistrationError;
27 
28 /// Adds goldfish battery and returns the platform needed resources including
29 /// its AML data and mmio base address
30 ///
31 /// # Arguments
32 ///
33 /// * `amls` - the vector to put the goldfish battery AML
34 /// * `battery_jail` - used when sandbox is enabled
35 /// * `mmio_bus` - bus to add the devices to
36 /// * `irq_chip` - the IrqChip object for registering irq events
37 /// * `irq_num` - assigned interrupt to use
38 /// * `resources` - the SystemAllocator to allocate IO and MMIO for acpi
add_goldfish_battery( amls: &mut Vec<u8>, battery_jail: Option<Minijail>, mmio_bus: &Bus, irq_chip: &mut dyn IrqChip, irq_num: u32, resources: &mut SystemAllocator, #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>, ) -> Result<(Tube, u64), DeviceRegistrationError>39 pub fn add_goldfish_battery(
40     amls: &mut Vec<u8>,
41     battery_jail: Option<Minijail>,
42     mmio_bus: &Bus,
43     irq_chip: &mut dyn IrqChip,
44     irq_num: u32,
45     resources: &mut SystemAllocator,
46     #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
47 ) -> Result<(Tube, u64), DeviceRegistrationError> {
48     let alloc = resources.get_anon_alloc();
49     let mmio_base = resources
50         .allocate_mmio(
51             devices::bat::GOLDFISHBAT_MMIO_LEN,
52             alloc,
53             "GoldfishBattery".to_string(),
54             AllocOptions::new().align(devices::bat::GOLDFISHBAT_MMIO_LEN),
55         )
56         .map_err(DeviceRegistrationError::AllocateIoResource)?;
57 
58     let (control_tube, response_tube) =
59         Tube::pair().map_err(DeviceRegistrationError::CreateTube)?;
60 
61     #[cfg(feature = "power-monitor-powerd")]
62     let (create_monitor, create_client) = (
63         Some(
64             Box::new(power_monitor::powerd::monitor::DBusMonitor::connect)
65                 as Box<dyn power_monitor::CreatePowerMonitorFn>,
66         ),
67         Some(Box::new(power_monitor::powerd::client::DBusClient::connect)
68             as Box<dyn power_monitor::CreatePowerClientFn>),
69     );
70 
71     #[cfg(not(feature = "power-monitor-powerd"))]
72     let (create_monitor, create_client) = (None, None);
73 
74     let irq_evt = devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
75 
76     let goldfish_bat = devices::GoldfishBattery::new(
77         mmio_base,
78         irq_num,
79         irq_evt
80             .try_clone()
81             .map_err(DeviceRegistrationError::EventClone)?,
82         response_tube,
83         create_monitor,
84         create_client,
85     )
86     .map_err(DeviceRegistrationError::RegisterBattery)?;
87     goldfish_bat.to_aml_bytes(amls);
88 
89     irq_chip
90         .register_level_irq_event(
91             irq_num,
92             &irq_evt,
93             IrqEventSource::from_device(&goldfish_bat),
94         )
95         .map_err(DeviceRegistrationError::RegisterIrqfd)?;
96 
97     match battery_jail {
98         #[cfg(not(windows))]
99         Some(jail) => {
100             let mut keep_rds = goldfish_bat.keep_rds();
101             syslog::push_descriptors(&mut keep_rds);
102             cros_tracing::push_descriptors!(&mut keep_rds);
103             metrics::push_descriptors(&mut keep_rds);
104             mmio_bus
105                 .insert(
106                     Arc::new(Mutex::new(
107                         ProxyDevice::new(
108                             goldfish_bat,
109                             jail,
110                             keep_rds,
111                             #[cfg(feature = "swap")]
112                             swap_controller,
113                         )
114                         .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
115                     )),
116                     mmio_base,
117                     devices::bat::GOLDFISHBAT_MMIO_LEN,
118                 )
119                 .map_err(DeviceRegistrationError::MmioInsert)?;
120         }
121         #[cfg(windows)]
122         Some(_) => {}
123         None => {
124             mmio_bus
125                 .insert(
126                     Arc::new(Mutex::new(goldfish_bat)),
127                     mmio_base,
128                     devices::bat::GOLDFISHBAT_MMIO_LEN,
129                 )
130                 .map_err(DeviceRegistrationError::MmioInsert)?;
131         }
132     }
133 
134     Ok((control_tube, mmio_base))
135 }
136 
137 pub struct PlatformBusResources {
138     pub dt_symbol: String,        // DT symbol (label) assigned to the device
139     pub regions: Vec<(u64, u64)>, // (start address, size)
140     pub irqs: Vec<(u32, u32)>,    // (IRQ number, flags)
141     pub iommus: Vec<(IommuDevType, Option<u32>, Vec<u32>)>, // (IOMMU type, IOMMU identifier, IDs)
142 }
143 
144 impl PlatformBusResources {
145     const IRQ_TRIGGER_EDGE: u32 = 1;
146     const IRQ_TRIGGER_LEVEL: u32 = 4;
147 
new(symbol: String) -> Self148     fn new(symbol: String) -> Self {
149         Self {
150             dt_symbol: symbol,
151             regions: vec![],
152             irqs: vec![],
153             iommus: vec![],
154         }
155     }
156 }
157 
158 /// Creates a platform device for use by this Vm.
159 #[cfg(any(target_os = "android", target_os = "linux"))]
generate_platform_bus( devices: Vec<(VfioPlatformDevice, Option<Minijail>)>, irq_chip: &mut dyn IrqChip, mmio_bus: &Bus, resources: &mut SystemAllocator, vm: &mut impl Vm, #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>, protection_type: ProtectionType, ) -> Result< ( Vec<Arc<Mutex<dyn BusDevice>>>, BTreeMap<u32, String>, Vec<PlatformBusResources>, ), DeviceRegistrationError, >160 pub fn generate_platform_bus(
161     devices: Vec<(VfioPlatformDevice, Option<Minijail>)>,
162     irq_chip: &mut dyn IrqChip,
163     mmio_bus: &Bus,
164     resources: &mut SystemAllocator,
165     vm: &mut impl Vm,
166     #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
167     protection_type: ProtectionType,
168 ) -> Result<
169     (
170         Vec<Arc<Mutex<dyn BusDevice>>>,
171         BTreeMap<u32, String>,
172         Vec<PlatformBusResources>,
173     ),
174     DeviceRegistrationError,
175 > {
176     let mut platform_devices = Vec::new();
177     let mut pid_labels = BTreeMap::new();
178     let mut bus_dev_resources = vec![];
179 
180     // Allocate ranges that may need to be in the Platform MMIO region (MmioType::Platform).
181     for (mut device, jail) in devices.into_iter() {
182         let dt_symbol = device
183             .dt_symbol()
184             .ok_or(DeviceRegistrationError::MissingDeviceTreeSymbol)?
185             .to_owned();
186         let mut device_resources = PlatformBusResources::new(dt_symbol);
187         let ranges = device
188             .allocate_regions(resources)
189             .map_err(DeviceRegistrationError::AllocateIoResource)?;
190 
191         // If guest memory is private, don't wait for the first access to mmap the device.
192         if protection_type.isolates_memory() {
193             device.regions_mmap_early(vm);
194         }
195 
196         let mut keep_rds = device.keep_rds();
197         syslog::push_descriptors(&mut keep_rds);
198         cros_tracing::push_descriptors!(&mut keep_rds);
199         metrics::push_descriptors(&mut keep_rds);
200 
201         let irqs = device
202             .get_platform_irqs()
203             .map_err(DeviceRegistrationError::AllocateIrqResource)?;
204         for irq in irqs.into_iter() {
205             let irq_num = resources
206                 .allocate_irq()
207                 .ok_or(DeviceRegistrationError::AllocateIrq)?;
208 
209             if device.irq_is_automask(&irq) {
210                 let irq_evt =
211                     devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
212                 irq_chip
213                     .register_level_irq_event(
214                         irq_num,
215                         &irq_evt,
216                         IrqEventSource::from_device(&device),
217                     )
218                     .map_err(DeviceRegistrationError::RegisterIrqfd)?;
219                 device
220                     .assign_level_platform_irq(&irq_evt, irq.index)
221                     .map_err(DeviceRegistrationError::SetupVfioPlatformIrq)?;
222                 keep_rds.extend(irq_evt.as_raw_descriptors());
223                 device_resources
224                     .irqs
225                     .push((irq_num, PlatformBusResources::IRQ_TRIGGER_LEVEL));
226             } else {
227                 let irq_evt =
228                     devices::IrqEdgeEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
229                 irq_chip
230                     .register_edge_irq_event(
231                         irq_num,
232                         &irq_evt,
233                         IrqEventSource::from_device(&device),
234                     )
235                     .map_err(DeviceRegistrationError::RegisterIrqfd)?;
236                 device
237                     .assign_edge_platform_irq(&irq_evt, irq.index)
238                     .map_err(DeviceRegistrationError::SetupVfioPlatformIrq)?;
239                 keep_rds.extend(irq_evt.as_raw_descriptors());
240                 device_resources
241                     .irqs
242                     .push((irq_num, PlatformBusResources::IRQ_TRIGGER_EDGE));
243             }
244         }
245 
246         if let Some((iommu_type, id, vsids)) = device.iommu() {
247             // We currently only support one IOMMU per VFIO device.
248             device_resources
249                 .iommus
250                 .push((iommu_type, id, vsids.to_vec()));
251         }
252 
253         let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
254             let proxy = ProxyDevice::new(
255                 device,
256                 jail,
257                 keep_rds,
258                 #[cfg(feature = "swap")]
259                 swap_controller,
260             )
261             .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
262             pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
263             Arc::new(Mutex::new(proxy))
264         } else {
265             device.on_sandboxed();
266             Arc::new(Mutex::new(device))
267         };
268         platform_devices.push(arced_dev.clone());
269         for range in &ranges {
270             mmio_bus
271                 .insert(arced_dev.clone(), range.0, range.1)
272                 .map_err(DeviceRegistrationError::MmioInsert)?;
273             device_resources.regions.push((range.0, range.1));
274         }
275         bus_dev_resources.push(device_resources);
276     }
277     Ok((platform_devices, pid_labels, bus_dev_resources))
278 }
279