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

google / alioth / 18638570188

20 Oct 2025 12:38AM UTC coverage: 20.202% (-0.01%) from 20.213%
18638570188

Pull #308

github

web-flow
Merge 73a1640e9 into 416357998
Pull Request #308: Add tests for PciSegment

0 of 59 new or added lines in 5 files covered. (0.0%)

1163 existing lines in 25 files now uncovered.

1578 of 7811 relevant lines covered (20.2%)

19.85 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, PL031_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
#[cfg(target_arch = "aarch64")]
36
use crate::device::pl031::Pl031;
37
use crate::device::pvpanic::PvPanic;
38
#[cfg(target_arch = "x86_64")]
39
use crate::device::serial::Serial;
40
use crate::errors::{DebugTrace, trace_error};
41
#[cfg(target_os = "linux")]
42
use crate::hv::Kvm;
43
use crate::hv::{Hypervisor, IoeventFdRegistry, Vm, VmConfig};
44
use crate::loader::Payload;
45
use crate::mem::Memory;
46
#[cfg(target_arch = "aarch64")]
47
use crate::mem::{MemRegion, MemRegionType};
48
use crate::pci::{Bdf, PciDevice};
49
#[cfg(target_os = "linux")]
50
use crate::sys::vfio::VfioIommu;
51
#[cfg(target_os = "linux")]
52
use crate::vfio::cdev::Cdev;
53
#[cfg(target_os = "linux")]
54
use crate::vfio::container::{Container, UpdateContainerMapping};
55
#[cfg(target_os = "linux")]
56
use crate::vfio::group::{DevFd, Group};
57
#[cfg(target_os = "linux")]
58
use crate::vfio::iommu::UpdateIommuIoas;
59
#[cfg(target_os = "linux")]
60
use crate::vfio::iommu::{Ioas, Iommu};
61
#[cfg(target_os = "linux")]
62
use crate::vfio::pci::VfioPciDev;
63
#[cfg(target_os = "linux")]
64
use crate::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam};
65
use crate::virtio::dev::{DevParam, Virtio, VirtioDevice};
66
use crate::virtio::pci::VirtioPciDevice;
67

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

104
type Result<T, E = Error> = std::result::Result<T, E>;
105

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

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

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

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

137
        let (event_tx, event_rx) = mpsc::channel();
×
138

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

156
        board.arch_init()?;
×
157

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

166
        Ok(machine)
×
167
    }
168

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

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

191
    #[cfg(target_arch = "aarch64")]
192
    pub fn add_pl031(&self) {
×
193
        let pl031_dev = Pl031::new(PL031_START);
×
194
        self.board.mmio_devs.write().push((
×
195
            PL031_START,
×
196
            Arc::new(MemRegion::with_emulated(
×
197
                Arc::new(pl031_dev),
×
198
                MemRegionType::Hidden,
×
199
            )),
200
        ));
201
    }
202

203
    pub fn add_pci_dev(&self, bdf: Option<Bdf>, dev: PciDevice) -> Result<(), Error> {
×
204
        let name = dev.name.clone();
×
205
        let bdf = if let Some(bdf) = bdf {
×
206
            bdf
×
207
        } else {
NEW
208
            self.board.pci_bus.reserve(None).unwrap()
×
209
        };
210
        dev.dev.config().get_header().set_bdf(bdf);
×
211
        self.board.pci_bus.add(bdf, dev);
×
212
        log::info!("{bdf}: device: {name}");
×
213
        Ok(())
×
214
    }
215

216
    pub fn add_pvpanic(&self) -> Result<(), Error> {
×
217
        let dev = PvPanic::new();
×
218
        let pci_dev = PciDevice::new("pvpanic", Arc::new(dev));
×
219
        self.add_pci_dev(None, pci_dev)
×
220
    }
221

222
    #[cfg(target_arch = "x86_64")]
223
    pub fn add_fw_cfg(
224
        &self,
225
        params: impl Iterator<Item = FwCfgItemParam>,
226
    ) -> Result<Arc<Mutex<FwCfg>>, Error> {
227
        let items = params
×
228
            .map(|p| p.build())
×
229
            .collect::<Result<Vec<_>, _>>()
230
            .context(error::FwCfg)?;
×
231
        let fw_cfg = Arc::new(Mutex::new(
×
232
            FwCfg::new(self.board.memory.ram_bus(), items).context(error::FwCfg)?,
×
233
        ));
234
        let mut io_devs = self.board.io_devs.write();
×
235
        io_devs.push((PORT_FW_CFG_SELECTOR, fw_cfg.clone()));
×
236
        *self.board.fw_cfg.lock() = Some(fw_cfg.clone());
×
237
        Ok(fw_cfg)
×
238
    }
239

240
    pub fn add_virtio_dev<D, P>(
×
241
        &self,
242
        name: impl Into<Arc<str>>,
243
        param: P,
244
    ) -> Result<Arc<VirtioPciDev<H>>, Error>
245
    where
246
        P: DevParam<Device = D>,
247
        D: Virtio,
248
    {
249
        if param.needs_mem_shared_fd() && !self.board.config.mem.has_shared_fd() {
×
250
            return error::MemNotSharedFd.fail();
×
251
        }
252
        let name = name.into();
×
NEW
253
        let bdf = self.board.pci_bus.reserve(None).unwrap();
×
254
        let dev = param.build(name.clone())?;
×
255
        if let Some(callback) = dev.mem_update_callback() {
×
256
            self.board.memory.register_update_callback(callback)?;
×
257
        }
258
        if let Some(callback) = dev.mem_change_callback() {
×
259
            self.board.memory.register_change_callback(callback)?;
×
260
        }
261
        let registry = self.board.vm.create_ioeventfd_registry()?;
×
262
        let virtio_dev = VirtioDevice::new(
263
            name.clone(),
×
264
            dev,
×
265
            self.board.memory.ram_bus(),
×
266
            self.board.config.coco.is_some(),
×
267
        )?;
268
        let msi_sender = self.board.vm.create_msi_sender(
×
269
            #[cfg(target_arch = "aarch64")]
×
270
            u32::from(bdf.0),
×
271
        )?;
272
        let dev = VirtioPciDevice::new(virtio_dev, msi_sender, registry)?;
×
273
        let dev = Arc::new(dev);
×
274
        let pci_dev = PciDevice::new(name.clone(), dev.clone());
×
275
        self.add_pci_dev(Some(bdf), pci_dev)?;
×
276
        Ok(dev)
×
277
    }
278

279
    pub fn add_payload(&self, payload: Payload) {
×
280
        *self.board.payload.write() = Some(payload)
×
281
    }
282

283
    pub fn boot(&self) -> Result<(), Error> {
×
284
        self.board.boot()?;
×
285
        Ok(())
×
286
    }
287

288
    pub fn wait(&self) -> Result<()> {
×
289
        self.event_rx.recv().unwrap();
×
290
        let vcpus = self.board.vcpus.read();
×
291
        for _ in 1..vcpus.len() {
×
292
            self.event_rx.recv().unwrap();
×
293
        }
294
        drop(vcpus);
×
295
        let mut vcpus = self.board.vcpus.write();
×
296
        let mut ret = Ok(());
×
297
        for (id, (handle, _)) in vcpus.drain(..).enumerate() {
×
298
            let Ok(r) = handle.join() else {
×
299
                log::error!("Cannot join VCPU-{id}");
×
300
                continue;
×
301
            };
302
            if r.is_err() && ret.is_ok() {
×
303
                ret = r.context(error::Vcpu { id: id as u32 });
×
304
            }
305
        }
306
        ret
×
307
    }
308
}
309

310
#[cfg(target_os = "linux")]
311
impl Machine<Kvm> {
312
    const DEFAULT_NAME: &str = "default";
313

UNCOV
314
    pub fn add_vfio_ioas(&self, param: IoasParam) -> Result<Arc<Ioas>, Error> {
UNCOV
315
        let mut ioases = self.board.vfio_ioases.lock();
UNCOV
316
        if ioases.contains_key(&param.name) {
UNCOV
317
            return error::AlreadyExists { name: param.name }.fail();
318
        }
UNCOV
319
        let maybe_iommu = &mut *self.iommu.lock();
UNCOV
320
        let iommu = if let Some(iommu) = maybe_iommu {
UNCOV
321
            iommu.clone()
322
        } else {
UNCOV
323
            let iommu_path = if let Some(dev_iommu) = &param.dev_iommu {
UNCOV
324
                dev_iommu
325
            } else {
UNCOV
326
                Path::new("/dev/iommu")
327
            };
UNCOV
328
            let iommu = Arc::new(Iommu::new(iommu_path)?);
UNCOV
329
            maybe_iommu.replace(iommu.clone());
UNCOV
330
            iommu
331
        };
UNCOV
332
        let ioas = Arc::new(Ioas::alloc_on(iommu)?);
UNCOV
333
        let update = Box::new(UpdateIommuIoas { ioas: ioas.clone() });
UNCOV
334
        self.board.memory.register_change_callback(update)?;
UNCOV
335
        ioases.insert(param.name, ioas.clone());
UNCOV
336
        Ok(ioas)
337
    }
338

UNCOV
339
    fn get_ioas(&self, name: Option<&str>) -> Result<Arc<Ioas>> {
UNCOV
340
        let ioas_name = name.unwrap_or(Self::DEFAULT_NAME);
UNCOV
341
        if let Some(ioas) = self.board.vfio_ioases.lock().get(ioas_name) {
UNCOV
342
            return Ok(ioas.clone());
343
        };
UNCOV
344
        if name.is_none() {
UNCOV
345
            self.add_vfio_ioas(IoasParam {
UNCOV
346
                name: Self::DEFAULT_NAME.into(),
UNCOV
347
                dev_iommu: None,
348
            })
349
        } else {
350
            error::NotExist { name: ioas_name }.fail()
351
        }
352
    }
353

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

UNCOV
357
        let mut cdev = Cdev::new(&param.path)?;
UNCOV
358
        cdev.attach_iommu_ioas(ioas.clone())?;
359

NEW
360
        let bdf = self.board.pci_bus.reserve(None).unwrap();
UNCOV
361
        let msi_sender = self.board.vm.create_msi_sender(
362
            #[cfg(target_arch = "aarch64")]
UNCOV
363
            u32::from(bdf.0),
364
        )?;
UNCOV
365
        let dev = VfioPciDev::new(name.clone(), cdev, msi_sender)?;
UNCOV
366
        let pci_dev = PciDevice::new(name, Arc::new(dev));
UNCOV
367
        self.add_pci_dev(Some(bdf), pci_dev)?;
UNCOV
368
        Ok(())
369
    }
370

UNCOV
371
    pub fn add_vfio_container(&self, param: ContainerParam) -> Result<Arc<Container>, Error> {
UNCOV
372
        let mut containers = self.board.vfio_containers.lock();
UNCOV
373
        if containers.contains_key(&param.name) {
UNCOV
374
            return error::AlreadyExists { name: param.name }.fail();
375
        }
UNCOV
376
        let vfio_path = if let Some(dev_vfio) = &param.dev_vfio {
UNCOV
377
            dev_vfio
378
        } else {
UNCOV
379
            Path::new("/dev/vfio/vfio")
380
        };
UNCOV
381
        let container = Arc::new(Container::new(vfio_path)?);
UNCOV
382
        let update = Box::new(UpdateContainerMapping {
UNCOV
383
            container: container.clone(),
384
        });
UNCOV
385
        self.board.memory.register_change_callback(update)?;
UNCOV
386
        containers.insert(param.name, container.clone());
UNCOV
387
        Ok(container)
388
    }
389

UNCOV
390
    fn get_container(&self, name: Option<&str>) -> Result<Arc<Container>> {
UNCOV
391
        let container_name = name.unwrap_or(Self::DEFAULT_NAME);
UNCOV
392
        if let Some(container) = self.board.vfio_containers.lock().get(container_name) {
UNCOV
393
            return Ok(container.clone());
394
        }
UNCOV
395
        if name.is_none() {
UNCOV
396
            self.add_vfio_container(ContainerParam {
UNCOV
397
                name: Self::DEFAULT_NAME.into(),
UNCOV
398
                dev_vfio: None,
399
            })
400
        } else {
401
            error::NotExist {
402
                name: container_name,
403
            }
404
            .fail()
405
        }
406
    }
407

UNCOV
408
    pub fn add_vfio_devs_in_group(&self, name: &str, param: GroupParam) -> Result<()> {
UNCOV
409
        let container = self.get_container(param.container.as_deref())?;
UNCOV
410
        let mut group = Group::new(&param.path)?;
UNCOV
411
        group.attach(container, VfioIommu::TYPE1_V2)?;
412

UNCOV
413
        let group = Arc::new(group);
UNCOV
414
        for device in param.devices {
UNCOV
415
            let devfd = DevFd::new(group.clone(), &device)?;
UNCOV
416
            let name = format!("{name}-{device}");
UNCOV
417
            self.add_vfio_devfd(name.into(), devfd)?;
418
        }
419

UNCOV
420
        Ok(())
421
    }
422

UNCOV
423
    fn add_vfio_devfd(&self, name: Arc<str>, devfd: DevFd) -> Result<()> {
NEW
424
        let bdf = self.board.pci_bus.reserve(None).unwrap();
UNCOV
425
        let msi_sender = self.board.vm.create_msi_sender(
426
            #[cfg(target_arch = "aarch64")]
UNCOV
427
            u32::from(bdf.0),
428
        )?;
UNCOV
429
        let dev = VfioPciDev::new(name.clone(), devfd, msi_sender)?;
UNCOV
430
        let pci_dev = PciDevice::new(name, Arc::new(dev));
UNCOV
431
        self.add_pci_dev(Some(bdf), pci_dev)
432
    }
433
}
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