arch/sys/
linux.rs

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