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::IommuDevType;
17use devices::IrqChip;
18use devices::IrqEventSource;
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: &mut 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
139pub struct PlatformBusResources {
140    pub dt_symbol: String,        // DT symbol (label) assigned to the device
141    pub regions: Vec<(u64, u64)>, // (start address, size)
142    pub irqs: Vec<(u32, u32)>,    // (IRQ number, flags)
143    pub iommus: Vec<(IommuDevType, Option<u32>, Vec<u32>)>, // (IOMMU type, IOMMU identifier, IDs)
144    pub requires_power_domain: bool,
145}
146
147impl PlatformBusResources {
148    const IRQ_TRIGGER_EDGE: u32 = 1;
149    const IRQ_TRIGGER_LEVEL: u32 = 4;
150
151    fn new(symbol: String) -> Self {
152        Self {
153            dt_symbol: symbol,
154            regions: vec![],
155            irqs: vec![],
156            iommus: vec![],
157            requires_power_domain: false,
158        }
159    }
160}
161
162/// Creates a platform device for use by this Vm.
163#[cfg(any(target_os = "android", target_os = "linux"))]
164pub fn generate_platform_bus(
165    devices: Vec<(VfioPlatformDevice, Option<Minijail>)>,
166    irq_chip: &mut dyn IrqChip,
167    mmio_bus: &Bus,
168    resources: &mut SystemAllocator,
169    vm: &mut impl Vm,
170    #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
171    dev_pm: &mut Option<DevicePowerManager>,
172    protection_type: ProtectionType,
173) -> Result<
174    (
175        Vec<Arc<Mutex<dyn BusDevice>>>,
176        BTreeMap<u32, String>,
177        Vec<PlatformBusResources>,
178    ),
179    DeviceRegistrationError,
180> {
181    let mut platform_devices = Vec::new();
182    let mut pid_labels = BTreeMap::new();
183    let mut bus_dev_resources = vec![];
184
185    // Allocate ranges that may need to be in the Platform MMIO region (MmioType::Platform).
186    for (mut device, jail) in devices.into_iter() {
187        let dt_symbol = device
188            .dt_symbol()
189            .ok_or(DeviceRegistrationError::MissingDeviceTreeSymbol)?
190            .to_owned();
191        let mut device_resources = PlatformBusResources::new(dt_symbol);
192        let ranges = device
193            .allocate_regions(resources)
194            .map_err(DeviceRegistrationError::AllocateIoResource)?;
195
196        // If guest memory is private, don't wait for the first access to mmap the device.
197        if protection_type.isolates_memory() {
198            device.regions_mmap_early(vm);
199        }
200
201        let mut keep_rds = device.keep_rds();
202        syslog::push_descriptors(&mut keep_rds);
203        cros_tracing::push_descriptors!(&mut keep_rds);
204        metrics::push_descriptors(&mut keep_rds);
205
206        let irqs = device
207            .get_platform_irqs()
208            .map_err(DeviceRegistrationError::AllocateIrqResource)?;
209        for irq in irqs.into_iter() {
210            let irq_num = resources
211                .allocate_irq()
212                .ok_or(DeviceRegistrationError::AllocateIrq)?;
213
214            if device.irq_is_automask(&irq) {
215                let irq_evt =
216                    devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
217                irq_chip
218                    .register_level_irq_event(
219                        irq_num,
220                        &irq_evt,
221                        IrqEventSource::from_device(&device),
222                    )
223                    .map_err(DeviceRegistrationError::RegisterIrqfd)?;
224                device
225                    .assign_level_platform_irq(&irq_evt, irq.index)
226                    .map_err(DeviceRegistrationError::SetupVfioPlatformIrq)?;
227                keep_rds.extend(irq_evt.as_raw_descriptors());
228                device_resources
229                    .irqs
230                    .push((irq_num, PlatformBusResources::IRQ_TRIGGER_LEVEL));
231            } else {
232                let irq_evt =
233                    devices::IrqEdgeEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
234                irq_chip
235                    .register_edge_irq_event(
236                        irq_num,
237                        &irq_evt,
238                        IrqEventSource::from_device(&device),
239                    )
240                    .map_err(DeviceRegistrationError::RegisterIrqfd)?;
241                device
242                    .assign_edge_platform_irq(&irq_evt, irq.index)
243                    .map_err(DeviceRegistrationError::SetupVfioPlatformIrq)?;
244                keep_rds.extend(irq_evt.as_raw_descriptors());
245                device_resources
246                    .irqs
247                    .push((irq_num, PlatformBusResources::IRQ_TRIGGER_EDGE));
248            }
249        }
250
251        if let Some((iommu_type, id, vsids)) = device.iommu() {
252            // We currently only support one IOMMU per VFIO device.
253            device_resources
254                .iommus
255                .push((iommu_type, id, vsids.to_vec()));
256        }
257
258        let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
259            let proxy = ProxyDevice::new(
260                device,
261                jail,
262                keep_rds,
263                #[cfg(feature = "swap")]
264                swap_controller,
265            )
266            .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
267            pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
268            Arc::new(Mutex::new(proxy))
269        } else {
270            device.on_sandboxed();
271            Arc::new(Mutex::new(device))
272        };
273        platform_devices.push(arced_dev.clone());
274        for range in &ranges {
275            mmio_bus
276                .insert(arced_dev.clone(), range.0, range.1)
277                .map_err(DeviceRegistrationError::MmioInsert)?;
278            device_resources.regions.push((range.0, range.1));
279        }
280
281        if let Some(ref mut pm) = dev_pm {
282            let supports_power_management = arced_dev.lock().supports_power_management();
283            match supports_power_management {
284                Err(e) => warn!("Error while detecting PM support, ignoring: {e:#}"),
285                Ok(false) => {}
286                Ok(true) => {
287                    // As we currently restrict one device per vPD and to simplify having a PD ID
288                    // which the VMM, hypervisor, and guest recognize, refer to a PD using the base
289                    // of the MMIO of the device it contains.
290                    let domain_id = ranges.first().unwrap().0.try_into().unwrap();
291                    pm.attach(arced_dev.clone(), domain_id)
292                        .map_err(DeviceRegistrationError::AttachDevicePowerDomain)?;
293                    device_resources.requires_power_domain = true;
294                }
295            }
296        }
297
298        bus_dev_resources.push(device_resources);
299    }
300    Ok((platform_devices, pid_labels, bus_dev_resources))
301}