• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / alioth / 17385686062

01 Sep 2025 07:20PM UTC coverage: 18.016% (-0.1%) from 18.149%
17385686062

Pull #281

github

web-flow
Merge f6f978f6a into 6ec9a6d6b
Pull Request #281: Port to Apple Hypervisor framework

0 of 152 new or added lines in 11 files covered. (0.0%)

1323 existing lines in 30 files now uncovered.

1362 of 7560 relevant lines covered (18.02%)

18.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/alioth/src/vm.rs
1
// Copyright 2024 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
#[cfg(target_os = "linux")]
16
use std::path::Path;
17
use std::sync::Arc;
18
use std::sync::mpsc::{self, Receiver, Sender};
19
use std::thread;
20
use std::time::Duration;
21

22
#[cfg(target_os = "linux")]
23
use parking_lot::Mutex;
24
use snafu::{ResultExt, Snafu};
25

26
#[cfg(target_arch = "aarch64")]
27
use crate::arch::layout::PL011_START;
28
#[cfg(target_arch = "x86_64")]
29
use crate::arch::layout::{PORT_COM1, PORT_FW_CFG_SELECTOR};
30
use crate::board::{ArchBoard, Board, BoardConfig};
31
#[cfg(target_arch = "x86_64")]
32
use crate::device::fw_cfg::{FwCfg, FwCfgItemParam};
33
#[cfg(target_arch = "aarch64")]
34
use crate::device::pl011::Pl011;
35
use crate::device::pvpanic::PvPanic;
36
#[cfg(target_arch = "x86_64")]
37
use crate::device::serial::Serial;
38
use crate::errors::{DebugTrace, trace_error};
39
#[cfg(target_os = "linux")]
40
use crate::hv::Kvm;
41
use crate::hv::{Hypervisor, IoeventFdRegistry, Vm, VmConfig};
42
use crate::loader::Payload;
43
use crate::mem::Memory;
44
#[cfg(target_arch = "aarch64")]
45
use crate::mem::{MemRegion, MemRegionType};
46
use crate::pci::{Bdf, PciDevice};
47
#[cfg(target_os = "linux")]
48
use crate::vfio::bindings::VfioIommu;
49
#[cfg(target_os = "linux")]
50
use crate::vfio::cdev::Cdev;
51
#[cfg(target_os = "linux")]
52
use crate::vfio::container::{Container, UpdateContainerMapping};
53
#[cfg(target_os = "linux")]
54
use crate::vfio::group::{DevFd, Group};
55
#[cfg(target_os = "linux")]
56
use crate::vfio::iommu::UpdateIommuIoas;
57
#[cfg(target_os = "linux")]
58
use crate::vfio::iommu::{Ioas, Iommu};
59
#[cfg(target_os = "linux")]
60
use crate::vfio::pci::VfioPciDev;
61
#[cfg(target_os = "linux")]
62
use crate::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam};
63
use crate::virtio::dev::{DevParam, Virtio, VirtioDevice};
64
use crate::virtio::pci::VirtioPciDevice;
65

66
#[trace_error]
67
#[derive(Snafu, DebugTrace)]
68
#[snafu(module, context(suffix(false)))]
69
pub enum Error {
70
    #[snafu(display("Hypervisor internal error"), context(false))]
71
    HvError { source: Box<crate::hv::Error> },
72
    #[snafu(display("Failed to create board"), context(false))]
73
    CreateBoard { source: Box<crate::board::Error> },
74
    #[snafu(display("Failed to create VCPU-{id} thread"))]
75
    VcpuThread { id: u32, error: std::io::Error },
76
    #[snafu(display("Failed to create a console"))]
77
    CreateConsole { error: std::io::Error },
78
    #[snafu(display("Failed to create fw-cfg device"))]
79
    FwCfg { error: std::io::Error },
80
    #[snafu(display("Failed to create a VirtIO device"), context(false))]
81
    CreateVirtio { source: Box<crate::virtio::Error> },
82
    #[snafu(display("Guest memory is not backed by sharable file descriptors"))]
83
    MemNotSharedFd,
84
    #[cfg(target_os = "linux")]
85
    #[snafu(display("Failed to create a VFIO device"), context(false))]
86
    CreateVfio { source: Box<crate::vfio::Error> },
87
    #[snafu(display("VCPU-{id} error"))]
88
    VcpuError {
89
        id: u32,
90
        source: Box<crate::board::Error>,
91
    },
92
    #[snafu(display("Failed to configure guest memory"), context(false))]
93
    Memory { source: Box<crate::mem::Error> },
94
    #[cfg(target_os = "linux")]
95
    #[snafu(display("{name:?} already exists"))]
96
    AlreadyExists { name: Box<str> },
97
    #[cfg(target_os = "linux")]
98
    #[snafu(display("{name:?} does not exist"))]
99
    NotExist { name: Box<str> },
100
}
101

102
type Result<T, E = Error> = std::result::Result<T, E>;
103

104
pub struct Machine<H>
105
where
106
    H: Hypervisor,
107
{
108
    board: Arc<Board<H::Vm>>,
109
    #[cfg(target_os = "linux")]
110
    iommu: Mutex<Option<Arc<Iommu>>>,
111
    event_rx: Receiver<u32>,
112
    _event_tx: Sender<u32>,
113
}
114

115
pub type VirtioPciDev<H> = VirtioPciDevice<
116
    <<H as Hypervisor>::Vm as Vm>::MsiSender,
117
    <<<H as Hypervisor>::Vm as Vm>::IoeventFdRegistry as IoeventFdRegistry>::IoeventFd,
118
>;
119

120
impl<H> Machine<H>
121
where
122
    H: Hypervisor + 'static,
123
{
124
    pub fn new(hv: H, config: BoardConfig) -> Result<Self> {
×
125
        let vm_config = VmConfig {
126
            coco: config.coco.clone(),
×
127
        };
128
        let mut vm = hv.create_vm(&vm_config)?;
×
129
        let vm_memory = vm.create_vm_memory()?;
×
130
        let memory = Memory::new(vm_memory);
×
131
        let arch = ArchBoard::new(&hv, &vm, &config)?;
×
132

133
        let board = Arc::new(Board::new(vm, memory, arch, config));
×
134

135
        let (event_tx, event_rx) = mpsc::channel();
×
136

137
        let mut vcpus = board.vcpus.write();
×
138
        for vcpu_id in 0..board.config.num_cpu {
×
139
            let (boot_tx, boot_rx) = mpsc::channel();
×
140
            let event_tx = event_tx.clone();
×
141
            let board = board.clone();
×
142
            let handle = thread::Builder::new()
×
143
                .name(format!("vcpu_{vcpu_id}"))
×
144
                .spawn(move || board.run_vcpu(vcpu_id, event_tx, boot_rx))
×
145
                .context(error::VcpuThread { id: vcpu_id })?;
×
146
            if event_rx.recv_timeout(Duration::from_secs(2)).is_err() {
×
147
                let err = std::io::ErrorKind::TimedOut.into();
×
148
                Err(err).context(error::VcpuThread { id: vcpu_id })?;
×
149
            }
150
            vcpus.push((handle, boot_tx));
×
151
        }
152
        drop(vcpus);
×
153

154
        board.arch_init()?;
×
155

156
        let machine = Machine {
157
            board,
158
            event_rx,
159
            _event_tx: event_tx,
160
            #[cfg(target_os = "linux")]
161
            iommu: Mutex::new(None),
×
162
        };
163

164
        Ok(machine)
×
165
    }
166

167
    #[cfg(target_arch = "x86_64")]
168
    pub fn add_com1(&self) -> Result<(), Error> {
×
169
        let irq_sender = self.board.vm.create_irq_sender(4)?;
×
170
        let com1 = Serial::new(PORT_COM1, irq_sender).context(error::CreateConsole)?;
×
171
        self.board.io_devs.write().push((PORT_COM1, Arc::new(com1)));
×
172
        Ok(())
×
173
    }
174

175
    #[cfg(target_arch = "aarch64")]
176
    pub fn add_pl011(&self) -> Result<(), Error> {
×
177
        let irq_line = self.board.vm.create_irq_sender(1)?;
×
178
        let pl011_dev = Pl011::new(PL011_START, irq_line).context(error::CreateConsole)?;
×
179
        self.board.mmio_devs.write().push((
×
180
            PL011_START,
×
181
            Arc::new(MemRegion::with_emulated(
×
182
                Arc::new(pl011_dev),
×
183
                MemRegionType::Hidden,
×
184
            )),
185
        ));
186
        Ok(())
×
187
    }
188

189
    pub fn add_pci_dev(&self, bdf: Option<Bdf>, dev: PciDevice) -> Result<(), Error> {
×
190
        let name = dev.name.clone();
×
191
        let bdf = if let Some(bdf) = bdf {
×
192
            bdf
×
193
        } else {
194
            self.board.pci_bus.reserve(None, name.clone()).unwrap()
×
195
        };
196
        dev.dev.config().get_header().set_bdf(bdf);
×
197
        self.board.pci_bus.add(bdf, dev);
×
198
        log::info!("{bdf}: device: {name}");
×
199
        Ok(())
×
200
    }
201

202
    pub fn add_pvpanic(&self) -> Result<(), Error> {
×
203
        let dev = PvPanic::new();
×
204
        let pci_dev = PciDevice::new("pvpanic", Arc::new(dev));
×
205
        self.add_pci_dev(None, pci_dev)
×
206
    }
207

208
    #[cfg(target_arch = "x86_64")]
209
    pub fn add_fw_cfg(
210
        &self,
211
        params: impl Iterator<Item = FwCfgItemParam>,
212
    ) -> Result<Arc<Mutex<FwCfg>>, Error> {
213
        let items = params
×
214
            .map(|p| p.build())
×
215
            .collect::<Result<Vec<_>, _>>()
216
            .context(error::FwCfg)?;
×
217
        let fw_cfg = Arc::new(Mutex::new(
×
218
            FwCfg::new(self.board.memory.ram_bus(), items).context(error::FwCfg)?,
×
219
        ));
220
        let mut io_devs = self.board.io_devs.write();
×
221
        io_devs.push((PORT_FW_CFG_SELECTOR, fw_cfg.clone()));
×
222
        *self.board.fw_cfg.lock() = Some(fw_cfg.clone());
×
223
        Ok(fw_cfg)
×
224
    }
225

226
    pub fn add_virtio_dev<D, P>(
×
227
        &self,
228
        name: impl Into<Arc<str>>,
229
        param: P,
230
    ) -> Result<Arc<VirtioPciDev<H>>, Error>
231
    where
232
        P: DevParam<Device = D>,
233
        D: Virtio,
234
    {
235
        if param.needs_mem_shared_fd() && !self.board.config.mem.has_shared_fd() {
×
236
            return error::MemNotSharedFd.fail();
×
237
        }
238
        let name = name.into();
×
239
        let bdf = self.board.pci_bus.reserve(None, name.clone()).unwrap();
×
240
        let dev = param.build(name.clone())?;
×
241
        if let Some(callback) = dev.mem_update_callback() {
×
242
            self.board.memory.register_update_callback(callback)?;
×
243
        }
244
        if let Some(callback) = dev.mem_change_callback() {
×
245
            self.board.memory.register_change_callback(callback)?;
×
246
        }
247
        let registry = self.board.vm.create_ioeventfd_registry()?;
×
248
        let virtio_dev = VirtioDevice::new(
249
            name.clone(),
×
250
            dev,
×
251
            self.board.memory.ram_bus(),
×
252
            self.board.config.coco.is_some(),
×
253
        )?;
254
        let msi_sender = self.board.vm.create_msi_sender(
×
255
            #[cfg(target_arch = "aarch64")]
×
256
            u32::from(bdf.0),
×
257
        )?;
258
        let dev = VirtioPciDevice::new(virtio_dev, msi_sender, registry)?;
×
259
        let dev = Arc::new(dev);
×
260
        let pci_dev = PciDevice::new(name.clone(), dev.clone());
×
261
        self.add_pci_dev(Some(bdf), pci_dev)?;
×
262
        Ok(dev)
×
263
    }
264

265
    pub fn add_payload(&self, payload: Payload) {
×
266
        *self.board.payload.write() = Some(payload)
×
267
    }
268

269
    pub fn boot(&self) -> Result<(), Error> {
×
270
        self.board.boot()?;
×
271
        Ok(())
×
272
    }
273

274
    pub fn wait(&self) -> Result<()> {
×
275
        self.event_rx.recv().unwrap();
×
276
        let vcpus = self.board.vcpus.read();
×
277
        for _ in 1..vcpus.len() {
×
278
            self.event_rx.recv().unwrap();
×
279
        }
280
        drop(vcpus);
×
281
        let mut vcpus = self.board.vcpus.write();
×
282
        let mut ret = Ok(());
×
283
        for (id, (handle, _)) in vcpus.drain(..).enumerate() {
×
284
            let Ok(r) = handle.join() else {
×
285
                log::error!("Cannot join VCPU-{id}");
×
286
                continue;
×
287
            };
288
            if r.is_err() && ret.is_ok() {
×
289
                ret = r.context(error::Vcpu { id: id as u32 });
×
290
            }
291
        }
292
        ret
×
293
    }
294
}
295

296
#[cfg(target_os = "linux")]
297
impl Machine<Kvm> {
298
    const DEFAULT_NAME: &str = "default";
299

UNCOV
300
    pub fn add_vfio_ioas(&self, param: IoasParam) -> Result<Arc<Ioas>, Error> {
UNCOV
301
        let mut ioases = self.board.vfio_ioases.lock();
UNCOV
302
        if ioases.contains_key(&param.name) {
UNCOV
303
            return error::AlreadyExists { name: param.name }.fail();
304
        }
UNCOV
305
        let maybe_iommu = &mut *self.iommu.lock();
UNCOV
306
        let iommu = if let Some(iommu) = maybe_iommu {
UNCOV
307
            iommu.clone()
308
        } else {
UNCOV
309
            let iommu_path = if let Some(dev_iommu) = &param.dev_iommu {
UNCOV
310
                dev_iommu
311
            } else {
UNCOV
312
                Path::new("/dev/iommu")
313
            };
UNCOV
314
            let iommu = Arc::new(Iommu::new(iommu_path)?);
UNCOV
315
            maybe_iommu.replace(iommu.clone());
UNCOV
316
            iommu
317
        };
UNCOV
318
        let ioas = Arc::new(Ioas::alloc_on(iommu)?);
UNCOV
319
        let update = Box::new(UpdateIommuIoas { ioas: ioas.clone() });
UNCOV
320
        self.board.memory.register_change_callback(update)?;
UNCOV
321
        ioases.insert(param.name, ioas.clone());
UNCOV
322
        Ok(ioas)
323
    }
324

UNCOV
325
    fn get_ioas(&self, name: Option<&str>) -> Result<Arc<Ioas>> {
UNCOV
326
        let ioas_name = name.unwrap_or(Self::DEFAULT_NAME);
UNCOV
327
        if let Some(ioas) = self.board.vfio_ioases.lock().get(ioas_name) {
UNCOV
328
            return Ok(ioas.clone());
329
        };
UNCOV
330
        if name.is_none() {
UNCOV
331
            self.add_vfio_ioas(IoasParam {
UNCOV
332
                name: Self::DEFAULT_NAME.into(),
UNCOV
333
                dev_iommu: None,
334
            })
335
        } else {
336
            error::NotExist { name: ioas_name }.fail()
337
        }
338
    }
339

UNCOV
340
    pub fn add_vfio_cdev(&self, name: Arc<str>, param: CdevParam) -> Result<(), Error> {
UNCOV
341
        let ioas = self.get_ioas(param.ioas.as_deref())?;
342

UNCOV
343
        let mut cdev = Cdev::new(&param.path)?;
UNCOV
344
        cdev.attach_iommu_ioas(ioas.clone())?;
345

UNCOV
346
        let bdf = self.board.pci_bus.reserve(None, name.clone()).unwrap();
UNCOV
347
        let msi_sender = self.board.vm.create_msi_sender(
348
            #[cfg(target_arch = "aarch64")]
UNCOV
349
            u32::from(bdf.0),
350
        )?;
UNCOV
351
        let dev = VfioPciDev::new(name.clone(), cdev, msi_sender)?;
UNCOV
352
        let pci_dev = PciDevice::new(name, Arc::new(dev));
UNCOV
353
        self.add_pci_dev(Some(bdf), pci_dev)?;
UNCOV
354
        Ok(())
355
    }
356

UNCOV
357
    pub fn add_vfio_container(&self, param: ContainerParam) -> Result<Arc<Container>, Error> {
UNCOV
358
        let mut containers = self.board.vfio_containers.lock();
UNCOV
359
        if containers.contains_key(&param.name) {
UNCOV
360
            return error::AlreadyExists { name: param.name }.fail();
361
        }
UNCOV
362
        let vfio_path = if let Some(dev_vfio) = &param.dev_vfio {
UNCOV
363
            dev_vfio
364
        } else {
UNCOV
365
            Path::new("/dev/vfio/vfio")
366
        };
UNCOV
367
        let container = Arc::new(Container::new(vfio_path)?);
UNCOV
368
        let update = Box::new(UpdateContainerMapping {
UNCOV
369
            container: container.clone(),
370
        });
UNCOV
371
        self.board.memory.register_change_callback(update)?;
UNCOV
372
        containers.insert(param.name, container.clone());
UNCOV
373
        Ok(container)
374
    }
375

UNCOV
376
    fn get_container(&self, name: Option<&str>) -> Result<Arc<Container>> {
UNCOV
377
        let container_name = name.unwrap_or(Self::DEFAULT_NAME);
UNCOV
378
        if let Some(container) = self.board.vfio_containers.lock().get(container_name) {
UNCOV
379
            return Ok(container.clone());
380
        }
UNCOV
381
        if name.is_none() {
UNCOV
382
            self.add_vfio_container(ContainerParam {
UNCOV
383
                name: Self::DEFAULT_NAME.into(),
UNCOV
384
                dev_vfio: None,
385
            })
386
        } else {
387
            error::NotExist {
388
                name: container_name,
389
            }
390
            .fail()
391
        }
392
    }
393

UNCOV
394
    pub fn add_vfio_devs_in_group(&self, name: &str, param: GroupParam) -> Result<()> {
UNCOV
395
        let container = self.get_container(param.container.as_deref())?;
UNCOV
396
        let mut group = Group::new(&param.path)?;
UNCOV
397
        group.attach(container, VfioIommu::TYPE1_V2)?;
398

UNCOV
399
        let group = Arc::new(group);
UNCOV
400
        for device in param.devices {
UNCOV
401
            let devfd = DevFd::new(group.clone(), &device)?;
UNCOV
402
            let name = format!("{name}-{device}");
UNCOV
403
            self.add_vfio_devfd(name.into(), devfd)?;
404
        }
405

UNCOV
406
        Ok(())
407
    }
408

UNCOV
409
    fn add_vfio_devfd(&self, name: Arc<str>, devfd: DevFd) -> Result<()> {
UNCOV
410
        let bdf = self.board.pci_bus.reserve(None, name.clone()).unwrap();
UNCOV
411
        let msi_sender = self.board.vm.create_msi_sender(
412
            #[cfg(target_arch = "aarch64")]
UNCOV
413
            u32::from(bdf.0),
414
        )?;
UNCOV
415
        let dev = VfioPciDev::new(name.clone(), devfd, msi_sender)?;
UNCOV
416
        let pci_dev = PciDevice::new(name, Arc::new(dev));
UNCOV
417
        self.add_pci_dev(Some(bdf), pci_dev)
418
    }
419
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc