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 = Some(Box::new(power_monitor::powerd::DBusMonitor::connect)
63 as Box<dyn power_monitor::CreatePowerMonitorFn>);
64
65 #[cfg(not(feature = "power-monitor-powerd"))]
66 let create_monitor = None;
67
68 let irq_evt = devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
69
70 let goldfish_bat = devices::GoldfishBattery::new(
71 mmio_base,
72 irq_num,
73 irq_evt
74 .try_clone()
75 .map_err(DeviceRegistrationError::EventClone)?,
76 response_tube,
77 create_monitor,
78 )
79 .map_err(DeviceRegistrationError::RegisterBattery)?;
80 goldfish_bat.to_aml_bytes(amls);
81
82 irq_chip
83 .register_level_irq_event(
84 irq_num,
85 &irq_evt,
86 IrqEventSource::from_device(&goldfish_bat),
87 )
88 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
89
90 match battery_jail {
91 #[cfg(not(windows))]
92 Some(jail) => {
93 let mut keep_rds = goldfish_bat.keep_rds();
94 syslog::push_descriptors(&mut keep_rds);
95 cros_tracing::push_descriptors!(&mut keep_rds);
96 metrics::push_descriptors(&mut keep_rds);
97 mmio_bus
98 .insert(
99 Arc::new(Mutex::new(
100 ProxyDevice::new(
101 goldfish_bat,
102 jail,
103 keep_rds,
104 #[cfg(feature = "swap")]
105 swap_controller,
106 )
107 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
108 )),
109 mmio_base,
110 devices::bat::GOLDFISHBAT_MMIO_LEN,
111 )
112 .map_err(DeviceRegistrationError::MmioInsert)?;
113 }
114 #[cfg(windows)]
115 Some(_) => {}
116 None => {
117 mmio_bus
118 .insert(
119 Arc::new(Mutex::new(goldfish_bat)),
120 mmio_base,
121 devices::bat::GOLDFISHBAT_MMIO_LEN,
122 )
123 .map_err(DeviceRegistrationError::MmioInsert)?;
124 }
125 }
126
127 Ok((control_tube, mmio_base))
128 }
129
130 pub struct PlatformBusResources {
131 pub dt_symbol: String, // DT symbol (label) assigned to the device
132 pub regions: Vec<(u64, u64)>, // (start address, size)
133 pub irqs: Vec<(u32, u32)>, // (IRQ number, flags)
134 pub iommus: Vec<(IommuDevType, Option<u32>, Vec<u32>)>, // (IOMMU type, IOMMU identifier, IDs)
135 }
136
137 impl PlatformBusResources {
138 const IRQ_TRIGGER_EDGE: u32 = 1;
139 const IRQ_TRIGGER_LEVEL: u32 = 4;
140
new(symbol: String) -> Self141 fn new(symbol: String) -> Self {
142 Self {
143 dt_symbol: symbol,
144 regions: vec![],
145 irqs: vec![],
146 iommus: vec![],
147 }
148 }
149 }
150
151 /// Creates a platform device for use by this Vm.
152 #[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, >153 pub fn generate_platform_bus(
154 devices: Vec<(VfioPlatformDevice, Option<Minijail>)>,
155 irq_chip: &mut dyn IrqChip,
156 mmio_bus: &Bus,
157 resources: &mut SystemAllocator,
158 vm: &mut impl Vm,
159 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
160 protection_type: ProtectionType,
161 ) -> Result<
162 (
163 Vec<Arc<Mutex<dyn BusDevice>>>,
164 BTreeMap<u32, String>,
165 Vec<PlatformBusResources>,
166 ),
167 DeviceRegistrationError,
168 > {
169 let mut platform_devices = Vec::new();
170 let mut pid_labels = BTreeMap::new();
171 let mut bus_dev_resources = vec![];
172
173 // Allocate ranges that may need to be in the Platform MMIO region (MmioType::Platform).
174 for (mut device, jail) in devices.into_iter() {
175 let dt_symbol = device
176 .dt_symbol()
177 .ok_or(DeviceRegistrationError::MissingDeviceTreeSymbol)?
178 .to_owned();
179 let mut device_resources = PlatformBusResources::new(dt_symbol);
180 let ranges = device
181 .allocate_regions(resources)
182 .map_err(DeviceRegistrationError::AllocateIoResource)?;
183
184 // If guest memory is private, don't wait for the first access to mmap the device.
185 if protection_type.isolates_memory() {
186 device.regions_mmap_early(vm);
187 }
188
189 let mut keep_rds = device.keep_rds();
190 syslog::push_descriptors(&mut keep_rds);
191 cros_tracing::push_descriptors!(&mut keep_rds);
192 metrics::push_descriptors(&mut keep_rds);
193
194 let irqs = device
195 .get_platform_irqs()
196 .map_err(DeviceRegistrationError::AllocateIrqResource)?;
197 for irq in irqs.into_iter() {
198 let irq_num = resources
199 .allocate_irq()
200 .ok_or(DeviceRegistrationError::AllocateIrq)?;
201
202 if device.irq_is_automask(&irq) {
203 let irq_evt =
204 devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
205 irq_chip
206 .register_level_irq_event(
207 irq_num,
208 &irq_evt,
209 IrqEventSource::from_device(&device),
210 )
211 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
212 device
213 .assign_level_platform_irq(&irq_evt, irq.index)
214 .map_err(DeviceRegistrationError::SetupVfioPlatformIrq)?;
215 keep_rds.extend(irq_evt.as_raw_descriptors());
216 device_resources
217 .irqs
218 .push((irq_num, PlatformBusResources::IRQ_TRIGGER_LEVEL));
219 } else {
220 let irq_evt =
221 devices::IrqEdgeEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
222 irq_chip
223 .register_edge_irq_event(
224 irq_num,
225 &irq_evt,
226 IrqEventSource::from_device(&device),
227 )
228 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
229 device
230 .assign_edge_platform_irq(&irq_evt, irq.index)
231 .map_err(DeviceRegistrationError::SetupVfioPlatformIrq)?;
232 keep_rds.extend(irq_evt.as_raw_descriptors());
233 device_resources
234 .irqs
235 .push((irq_num, PlatformBusResources::IRQ_TRIGGER_EDGE));
236 }
237 }
238
239 if let Some((iommu_type, id, vsids)) = device.iommu() {
240 // We currently only support one IOMMU per VFIO device.
241 device_resources
242 .iommus
243 .push((iommu_type, id, vsids.to_vec()));
244 }
245
246 let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
247 let proxy = ProxyDevice::new(
248 device,
249 jail,
250 keep_rds,
251 #[cfg(feature = "swap")]
252 swap_controller,
253 )
254 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
255 pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
256 Arc::new(Mutex::new(proxy))
257 } else {
258 device.on_sandboxed();
259 Arc::new(Mutex::new(device))
260 };
261 platform_devices.push(arced_dev.clone());
262 for range in &ranges {
263 mmio_bus
264 .insert(arced_dev.clone(), range.0, range.1)
265 .map_err(DeviceRegistrationError::MmioInsert)?;
266 device_resources.regions.push((range.0, range.1));
267 }
268 bus_dev_resources.push(device_resources);
269 }
270 Ok((platform_devices, pid_labels, bus_dev_resources))
271 }
272