• 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/board/board.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_arch = "aarch64")]
16
mod aarch64;
17
#[cfg(target_arch = "x86_64")]
18
mod x86_64;
19

20
#[cfg(target_os = "linux")]
21
use std::collections::HashMap;
22
use std::ffi::CStr;
23
use std::sync::Arc;
24
use std::sync::mpsc::{Receiver, Sender};
25
use std::thread::JoinHandle;
26

27
use libc::{MAP_PRIVATE, MAP_SHARED};
28
use parking_lot::{Condvar, Mutex, RwLock, RwLockReadGuard};
29
use snafu::{ResultExt, Snafu};
30

31
#[cfg(target_arch = "x86_64")]
32
use crate::arch::layout::PORT_PCI_ADDRESS;
33
use crate::arch::layout::{
34
    MEM_64_START, PCIE_CONFIG_START, PCIE_MMIO_32_NON_PREFETCHABLE_END,
35
    PCIE_MMIO_32_NON_PREFETCHABLE_START, PCIE_MMIO_32_PREFETCHABLE_END,
36
    PCIE_MMIO_32_PREFETCHABLE_START, RAM_32_SIZE,
37
};
38
#[cfg(target_arch = "x86_64")]
39
use crate::device::fw_cfg::FwCfg;
40
use crate::errors::{DebugTrace, trace_error};
41
use crate::hv::{Coco, Vcpu, Vm, VmEntry, VmExit};
42
#[cfg(target_arch = "x86_64")]
43
use crate::loader::xen;
44
use crate::loader::{ExecType, InitState, Payload, firmware, linux};
45
use crate::mem::emulated::Mmio;
46
use crate::mem::mapped::ArcMemPages;
47
use crate::mem::{MemBackend, MemConfig, MemRegion, MemRegionType, Memory};
48
use crate::pci::bus::PciBus;
49
#[cfg(target_os = "linux")]
50
use crate::vfio::container::Container;
51
#[cfg(target_os = "linux")]
52
use crate::vfio::iommu::Ioas;
53

54
#[cfg(target_arch = "aarch64")]
55
pub(crate) use self::aarch64::ArchBoard;
56
#[cfg(target_arch = "x86_64")]
57
pub(crate) use self::x86_64::ArchBoard;
58

59
#[trace_error]
60
#[derive(Snafu, DebugTrace)]
61
#[snafu(module, context(suffix(false)))]
62
pub enum Error {
63
    #[snafu(display("Hypervisor internal error"), context(false))]
64
    HvError { source: Box<crate::hv::Error> },
65
    #[snafu(display("Failed to access guest memory"), context(false))]
66
    Memory { source: Box<crate::mem::Error> },
67
    #[snafu(display("Failed to load payload"), context(false))]
68
    Loader { source: Box<crate::loader::Error> },
69
    #[snafu(display("Failed to create VCPU-{id}"))]
70
    CreateVcpu {
71
        id: u32,
72
        source: Box<crate::hv::Error>,
73
    },
74
    #[snafu(display("Failed to run VCPU-{id}"))]
75
    RunVcpu {
76
        id: u32,
77
        source: Box<crate::hv::Error>,
78
    },
79
    #[snafu(display("Failed to stop VCPU-{id}"))]
80
    StopVcpu {
81
        id: u32,
82
        source: Box<crate::hv::Error>,
83
    },
84
    #[snafu(display("Failed to reset PCI devices"))]
85
    ResetPci { source: Box<crate::pci::Error> },
86
    #[snafu(display("Failed to configure firmware"))]
87
    Firmware { error: std::io::Error },
88
    #[snafu(display("Failed to notify the VMM thread"))]
89
    NotifyVmm,
90
    #[snafu(display("Another VCPU thread has signaled failure"))]
91
    PeerFailure,
92
}
93

94
type Result<T, E = Error> = std::result::Result<T, E>;
95

96
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97
enum BoardState {
98
    Created,
99
    Running,
100
    Shutdown,
101
    RebootPending,
102
}
103

104
#[derive(Debug)]
105
struct MpSync {
106
    state: BoardState,
107
    fatal: bool,
108
    count: u32,
109
}
110

111
pub const PCIE_MMIO_64_SIZE: u64 = 1 << 40;
112

113
pub struct BoardConfig {
114
    pub mem: MemConfig,
115
    pub num_cpu: u32,
116
    pub coco: Option<Coco>,
117
}
118

119
impl BoardConfig {
120
    pub fn pcie_mmio_64_start(&self) -> u64 {
×
121
        (self.mem.size.saturating_sub(RAM_32_SIZE) + MEM_64_START).next_power_of_two()
×
122
    }
123
}
124

125
type VcpuGuard<'a> = RwLockReadGuard<'a, Vec<(JoinHandle<Result<()>>, Sender<()>)>>;
126
type VcpuHandle = (JoinHandle<Result<()>>, Sender<()>);
127

128
pub struct Board<V>
129
where
130
    V: Vm,
131
{
132
    pub vm: V,
133
    pub memory: Memory,
134
    pub vcpus: Arc<RwLock<Vec<VcpuHandle>>>,
135
    pub arch: ArchBoard<V>,
136
    pub config: BoardConfig,
137
    pub payload: RwLock<Option<Payload>>,
138
    pub io_devs: RwLock<Vec<(u16, Arc<dyn Mmio>)>>,
139
    #[cfg(target_arch = "aarch64")]
140
    pub mmio_devs: RwLock<Vec<(u64, Arc<MemRegion>)>>,
141
    pub pci_bus: PciBus,
142
    #[cfg(target_arch = "x86_64")]
143
    pub fw_cfg: Mutex<Option<Arc<Mutex<FwCfg>>>>,
144
    #[cfg(target_os = "linux")]
145
    pub vfio_ioases: Mutex<HashMap<Box<str>, Arc<Ioas>>>,
146
    #[cfg(target_os = "linux")]
147
    pub vfio_containers: Mutex<HashMap<Box<str>, Arc<Container>>>,
148

149
    mp_sync: Mutex<MpSync>,
150
    cond_var: Condvar,
151
}
152

153
impl<V> Board<V>
154
where
155
    V: Vm,
156
{
157
    pub fn new(vm: V, memory: Memory, arch: ArchBoard<V>, config: BoardConfig) -> Self {
×
158
        Board {
159
            vm,
160
            memory,
161
            arch,
162
            config,
163
            payload: RwLock::new(None),
×
164
            vcpus: Arc::new(RwLock::new(Vec::new())),
×
165
            io_devs: RwLock::new(Vec::new()),
×
166
            #[cfg(target_arch = "aarch64")]
167
            mmio_devs: RwLock::new(Vec::new()),
×
168
            pci_bus: PciBus::new(),
×
169
            #[cfg(target_arch = "x86_64")]
170
            fw_cfg: Mutex::new(None),
×
171
            #[cfg(target_os = "linux")]
172
            vfio_ioases: Mutex::new(HashMap::new()),
×
173
            #[cfg(target_os = "linux")]
174
            vfio_containers: Mutex::new(HashMap::new()),
×
175

176
            mp_sync: Mutex::new(MpSync {
×
177
                state: BoardState::Created,
178
                count: 0,
179
                fatal: false,
180
            }),
181
            cond_var: Condvar::new(),
×
182
        }
183
    }
184

185
    pub fn boot(&self) -> Result<()> {
×
186
        let vcpus = self.vcpus.read();
×
187
        let mut mp_sync = self.mp_sync.lock();
×
188
        mp_sync.state = BoardState::Running;
×
189
        for (_, boot_tx) in vcpus.iter() {
×
190
            boot_tx.send(()).unwrap();
×
191
        }
192
        Ok(())
×
193
    }
194

195
    fn load_payload(&self) -> Result<InitState, Error> {
×
196
        let payload = self.payload.read();
×
197
        let Some(payload) = payload.as_ref() else {
×
198
            return Ok(InitState::default());
×
199
        };
200
        let mem_regions = self.memory.mem_region_entries();
×
201
        let init_state = match payload.exec_type {
×
202
            ExecType::Linux => linux::load(
203
                &self.memory.ram_bus(),
×
204
                &mem_regions,
×
205
                &payload.executable,
×
206
                payload.cmd_line.as_deref(),
×
207
                payload.initramfs.as_ref(),
×
208
            )?,
209
            #[cfg(target_arch = "x86_64")]
210
            ExecType::Pvh => xen::load(
211
                &self.memory.ram_bus(),
×
212
                &mem_regions,
×
213
                &payload.executable,
×
214
                payload.cmd_line.as_deref(),
×
215
                payload.initramfs.as_ref(),
×
216
            )?,
217
            ExecType::Firmware => {
×
218
                let (init_state, mut rom) = firmware::load(&self.memory, &payload.executable)?;
×
219
                self.setup_firmware(&mut rom)?;
×
220
                init_state
×
221
            }
222
        };
223
        Ok(init_state)
×
224
    }
225

226
    fn add_pci_devs(&self) -> Result<()> {
×
227
        #[cfg(target_arch = "x86_64")]
×
228
        self.memory
×
229
            .add_io_dev(PORT_PCI_ADDRESS, self.pci_bus.io_bus.clone())?;
×
230
        self.memory.add_region(
×
231
            PCIE_CONFIG_START,
×
232
            Arc::new(MemRegion::with_emulated(
×
233
                self.pci_bus.segment.clone(),
×
234
                MemRegionType::Reserved,
×
235
            )),
236
        )?;
237
        let pcie_mmio_64_start = self.config.pcie_mmio_64_start();
×
NEW
238
        self.pci_bus.segment.assign_resources(&[
×
239
            (0x1000, 0x10000),
×
240
            (
241
                PCIE_MMIO_32_NON_PREFETCHABLE_START,
×
242
                PCIE_MMIO_32_NON_PREFETCHABLE_END,
×
243
            ),
244
            (
245
                PCIE_MMIO_32_PREFETCHABLE_START,
×
246
                PCIE_MMIO_32_PREFETCHABLE_END,
×
247
            ),
248
            (pcie_mmio_64_start, pcie_mmio_64_start + PCIE_MMIO_64_SIZE),
×
249
        ]);
250
        Ok(())
×
251
    }
252

253
    fn vcpu_loop(&self, vcpu: &mut <V as Vm>::Vcpu, id: u32) -> Result<bool, Error> {
×
254
        let mut vm_entry = VmEntry::None;
×
255
        loop {
×
256
            let vm_exit = vcpu.run(vm_entry).context(error::RunVcpu { id })?;
×
257
            vm_entry = match vm_exit {
×
258
                VmExit::Io { port, write, size } => self.memory.handle_io(port, write, size)?,
×
259
                VmExit::Mmio { addr, write, size } => self.memory.handle_mmio(addr, write, size)?,
×
260
                VmExit::Shutdown => {
×
261
                    log::info!("vcpu {id} requested shutdown");
×
262
                    break Ok(false);
×
263
                }
264
                VmExit::Reboot => {
×
265
                    break Ok(true);
×
266
                }
267
                VmExit::Interrupted => {
×
268
                    let mp_sync = self.mp_sync.lock();
×
269
                    match mp_sync.state {
×
270
                        BoardState::Shutdown => VmEntry::Shutdown,
×
271
                        BoardState::RebootPending => VmEntry::Reboot,
×
272
                        _ => VmEntry::None,
×
273
                    }
274
                }
275
                VmExit::ConvertMemory { gpa, size, private } => {
×
276
                    self.memory.mark_private_memory(gpa, size, private)?;
×
277
                    VmEntry::None
×
278
                }
279
            };
280
        }
281
    }
282

283
    fn sync_vcpus(&self, vcpus: &VcpuGuard) -> Result<()> {
×
284
        let mut mp_sync = self.mp_sync.lock();
×
285
        if mp_sync.fatal {
×
286
            return error::PeerFailure.fail();
×
287
        }
288

289
        mp_sync.count += 1;
×
290
        if mp_sync.count == vcpus.len() as u32 {
×
291
            mp_sync.count = 0;
×
292
            self.cond_var.notify_all();
×
293
        } else {
294
            self.cond_var.wait(&mut mp_sync)
×
295
        }
296

297
        if mp_sync.fatal {
×
298
            return error::PeerFailure.fail();
×
299
        }
300

301
        Ok(())
×
302
    }
303

304
    fn run_vcpu_inner(
×
305
        &self,
306
        id: u32,
307
        vcpu: &mut V::Vcpu,
308
        boot_rx: &Receiver<()>,
309
    ) -> Result<(), Error> {
310
        self.init_vcpu(id, vcpu)?;
×
311
        boot_rx.recv().unwrap();
×
312
        if self.mp_sync.lock().state != BoardState::Running {
×
313
            return Ok(());
×
314
        }
315
        loop {
×
316
            let vcpus = self.vcpus.read();
×
317
            self.coco_init(id)?;
×
318
            if id == 0 {
×
319
                self.create_ram()?;
×
320
                for (port, dev) in self.io_devs.read().iter() {
×
321
                    self.memory.add_io_dev(*port, dev.clone())?;
×
322
                }
323
                #[cfg(target_arch = "aarch64")]
324
                for (addr, dev) in self.mmio_devs.read().iter() {
×
325
                    self.memory.add_region(*addr, dev.clone())?;
×
326
                }
327
                self.add_pci_devs()?;
×
328
                let init_state = self.load_payload()?;
×
329
                self.init_boot_vcpu(vcpu, &init_state)?;
×
330
                self.create_firmware_data(&init_state)?;
×
331
            }
332
            self.init_ap(id, vcpu, &vcpus)?;
×
333
            self.coco_finalize(id, &vcpus)?;
×
334
            self.sync_vcpus(&vcpus)?;
×
335
            drop(vcpus);
×
336

337
            let maybe_reboot = self.vcpu_loop(vcpu, id);
×
338

339
            let vcpus = self.vcpus.read();
×
340
            let mut mp_sync = self.mp_sync.lock();
×
341
            if mp_sync.state == BoardState::Running {
×
342
                mp_sync.state = if matches!(maybe_reboot, Ok(true)) {
×
343
                    BoardState::RebootPending
×
344
                } else {
345
                    BoardState::Shutdown
×
346
                };
347
                for (vcpu_id, (handle, _)) in vcpus.iter().enumerate() {
×
348
                    if id != vcpu_id as u32 {
×
349
                        log::info!("VCPU-{id}: stopping VCPU-{vcpu_id}");
×
350
                        self.vm
×
351
                            .stop_vcpu(vcpu_id as u32, handle)
×
352
                            .context(error::StopVcpu { id })?;
×
353
                    }
354
                }
355
            }
356
            drop(mp_sync);
×
357
            self.sync_vcpus(&vcpus)?;
×
358

359
            if id == 0 {
×
NEW
360
                self.pci_bus.segment.reset().context(error::ResetPci)?;
×
361
                self.memory.reset()?;
×
362
            }
363
            self.reset_vcpu(id, vcpu)?;
×
364

365
            if let Err(e) = maybe_reboot {
×
366
                break Err(e);
×
367
            }
368

369
            let mut mp_sync = self.mp_sync.lock();
×
370
            if mp_sync.state == BoardState::Shutdown {
×
371
                break Ok(());
×
372
            }
373
            mp_sync.state = BoardState::Running;
×
374
        }
375
    }
376

377
    fn create_vcpu(&self, id: u32, event_tx: &Sender<u32>) -> Result<V::Vcpu> {
×
378
        let vcpu = self.vm.create_vcpu(id).context(error::CreateVcpu { id })?;
×
379
        if event_tx.send(id).is_err() {
×
380
            error::NotifyVmm.fail()
×
381
        } else {
382
            Ok(vcpu)
×
383
        }
384
    }
385

386
    pub fn run_vcpu(
×
387
        &self,
388
        id: u32,
389
        event_tx: Sender<u32>,
390
        boot_rx: Receiver<()>,
391
    ) -> Result<(), Error> {
392
        let mut vcpu = self.create_vcpu(id, &event_tx)?;
×
393

394
        let ret = self.run_vcpu_inner(id, &mut vcpu, &boot_rx);
×
395
        event_tx.send(id).unwrap();
×
396

397
        if matches!(ret, Ok(_) | Err(Error::PeerFailure { .. })) {
×
398
            return Ok(());
×
399
        }
400

401
        log::warn!("VCPU-{id} reported error, unblocking other VCPUs...");
×
402
        let mut mp_sync = self.mp_sync.lock();
×
403
        mp_sync.fatal = true;
×
404
        if mp_sync.count > 0 {
×
405
            self.cond_var.notify_all();
×
406
        }
407
        ret
×
408
    }
409

410
    fn create_ram_pages(
×
411
        &self,
412
        size: u64,
413
        #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] name: &CStr,
414
    ) -> Result<ArcMemPages> {
415
        let mmap_flag = if self.config.mem.shared {
×
416
            Some(MAP_SHARED)
×
417
        } else {
418
            Some(MAP_PRIVATE)
×
419
        };
420
        let pages = match self.config.mem.backend {
×
421
            #[cfg(target_os = "linux")]
422
            MemBackend::Memfd => ArcMemPages::from_memfd(name, size as usize, None),
×
423
            MemBackend::Anonymous => ArcMemPages::from_anonymous(size as usize, None, mmap_flag),
×
424
        }?;
425
        #[cfg(target_os = "linux")]
×
426
        if self.config.mem.transparent_hugepage {
×
427
            pages.madvise_hugepage()?;
×
428
        }
429
        Ok(pages)
×
430
    }
431
}
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