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::AsRawDescriptors;
11use base::Tube;
12use devices::Bus;
13use devices::BusDevice;
14use devices::IommuDevType;
15use devices::IrqChip;
16use devices::IrqEventSource;
17use devices::ProxyDevice;
18use devices::VfioPlatformDevice;
19use hypervisor::ProtectionType;
20use hypervisor::Vm;
21use minijail::Minijail;
22use resources::AllocOptions;
23use resources::SystemAllocator;
24use sync::Mutex;
25
26use 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
39pub 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
137pub 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
144impl PlatformBusResources {
145    const IRQ_TRIGGER_EDGE: u32 = 1;
146    const IRQ_TRIGGER_LEVEL: u32 = 4;
147
148    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"))]
160pub 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}