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

google / alioth / 17149713571

22 Aug 2025 07:57AM UTC coverage: 13.887% (+1.9%) from 11.963%
17149713571

push

github

Lencerf
test(entropy): add unit tests

Signed-off-by: Changyuan Lyu <changyuanl@google.com>

9 of 11 new or added lines in 5 files covered. (81.82%)

1168 existing lines in 26 files now uncovered.

975 of 7021 relevant lines covered (13.89%)

17.71 hits per line

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

0.0
/alioth-cli/src/boot.rs
1
// Copyright 2025 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
use std::collections::HashMap;
16
#[cfg(target_arch = "x86_64")]
17
use std::ffi::CString;
18
#[cfg(target_arch = "x86_64")]
19
use std::fs::File;
20
use std::path::PathBuf;
21

22
use alioth::board::BoardConfig;
23
#[cfg(target_arch = "x86_64")]
24
use alioth::device::fw_cfg::FwCfgItemParam;
25
use alioth::errors::{DebugTrace, trace_error};
26
#[cfg(target_os = "macos")]
27
use alioth::hv::Hvf;
28
use alioth::hv::{self, Coco};
29
#[cfg(target_os = "linux")]
30
use alioth::hv::{Kvm, KvmConfig};
31
use alioth::loader::{ExecType, Payload};
32
use alioth::mem::{MemBackend, MemConfig};
33
#[cfg(target_os = "linux")]
34
use alioth::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam};
35
#[cfg(target_os = "linux")]
36
use alioth::virtio::DeviceId;
37
use alioth::virtio::dev::balloon::BalloonParam;
38
use alioth::virtio::dev::blk::BlkFileParam;
39
use alioth::virtio::dev::entropy::EntropyParam;
40
use alioth::virtio::dev::fs::shared_dir::SharedDirParam;
41
#[cfg(target_os = "linux")]
42
use alioth::virtio::dev::fs::vu::VuFsParam;
43
#[cfg(target_os = "linux")]
44
use alioth::virtio::dev::net::NetTapParam;
45
#[cfg(target_os = "linux")]
46
use alioth::virtio::dev::vsock::VhostVsockParam;
47
#[cfg(target_os = "linux")]
48
use alioth::virtio::vu::frontend::VuFrontendParam;
49
use alioth::vm::Machine;
50
use clap::Args;
51
use serde::Deserialize;
52
use serde_aco::{Help, help_text};
53
use snafu::{ResultExt, Snafu};
54

55
use crate::objects::{DOC_OBJECTS, parse_objects};
56

57
#[trace_error]
58
#[derive(Snafu, DebugTrace)]
59
#[snafu(module, context(suffix(false)))]
60
pub enum Error {
61
    #[snafu(display("Failed to parse {arg}"))]
62
    ParseArg {
63
        arg: String,
64
        error: serde_aco::Error,
65
    },
66
    #[snafu(display("Failed to parse objects"), context(false))]
67
    ParseObjects { source: crate::objects::Error },
68
    #[cfg(target_os = "linux")]
69
    #[snafu(display("Failed to access system hypervisor"))]
70
    Hypervisor { source: alioth::hv::Error },
71
    #[snafu(display("Failed to create a VM"))]
72
    CreateVm { source: alioth::vm::Error },
73
    #[snafu(display("Failed to create a device"))]
74
    CreateDevice { source: alioth::vm::Error },
75
    #[cfg(target_arch = "x86_64")]
76
    #[snafu(display("Failed to open {path:?}"))]
77
    OpenFile {
78
        path: PathBuf,
79
        error: std::io::Error,
80
    },
81
    #[cfg(target_arch = "x86_64")]
82
    #[snafu(display("Failed to configure the fw-cfg device"))]
83
    FwCfg { error: std::io::Error },
84
    #[cfg(target_arch = "x86_64")]
85
    #[snafu(display("{s} is not a valid CString"))]
86
    CreateCString { s: String },
87
    #[snafu(display("Failed to boot a VM"))]
88
    BootVm { source: alioth::vm::Error },
89
    #[snafu(display("VM did not shutdown peacefully"))]
90
    WaitVm { source: alioth::vm::Error },
91
}
92

93
#[derive(Debug, Deserialize, Clone, Help)]
94
#[cfg_attr(target_os = "macos", derive(Default))]
95
enum Hypervisor {
96
    /// KVM backed by the Linux kernel.
97
    #[cfg(target_os = "linux")]
98
    #[serde(alias = "kvm")]
99
    Kvm(KvmConfig),
100
    /// macOS Hypervisor Framework.
101
    #[cfg(target_os = "macos")]
102
    #[serde(alias = "hvf")]
103
    #[default]
104
    Hvf,
105
}
106

107
#[cfg(target_os = "linux")]
108
impl Default for Hypervisor {
UNCOV
109
    fn default() -> Self {
UNCOV
110
        Hypervisor::Kvm(KvmConfig::default())
111
    }
112
}
113

114
#[derive(Debug, Deserialize, Clone, Help)]
115
enum FsParam {
116
    /// VirtIO FS device backed by a shared directory.
117
    #[serde(alias = "dir")]
118
    Dir(SharedDirParam),
119
    #[cfg(target_os = "linux")]
120
    /// VirtIO FS device backed by a vhost-user process, e.g. virtiofsd.
121
    #[serde(alias = "vu")]
122
    Vu(VuFsParam),
123
}
124

125
#[cfg(target_os = "linux")]
126
#[derive(Debug, Deserialize, Clone, Help)]
127
enum VsockParam {
128
    /// Vsock device backed by host kernel vhost-vsock module.
129
    #[serde(alias = "vhost")]
130
    Vhost(VhostVsockParam),
131
}
132

133
#[cfg(target_os = "linux")]
134
#[derive(Deserialize, Help)]
135
struct VuSocket {
136
    socket: PathBuf,
137
}
138

139
#[cfg(target_os = "linux")]
140
#[derive(Deserialize, Help)]
141
enum NetParam {
142
    /// VirtIO net device backed by TUN/TAP, MacVTap, or IPVTap.
143
    #[serde(alias = "tap")]
144
    Tap(NetTapParam),
145
    /// vhost-user net device over a Unix domain socket.
146
    #[serde(alias = "vu")]
147
    Vu(VuSocket),
148
}
149

150
#[derive(Deserialize, Help)]
151
enum BlkParam {
152
    /// VirtIO block device backed a disk image file.
153
    #[serde(alias = "file")]
154
    File(BlkFileParam),
155
    #[cfg(target_os = "linux")]
156
    #[serde(alias = "vu")]
157
    /// vhost-user block device over a Unix domain socket.
158
    Vu(VuSocket),
159
}
160

161
#[derive(Args, Debug, Clone)]
162
#[command(arg_required_else_help = true, alias("run"))]
163
pub struct BootArgs {
164
    #[arg(long, help(
165
        help_text::<Hypervisor>("Specify the Hypervisor to run on.")
166
    ), value_name = "HV")]
167
    hypervisor: Option<String>,
168

169
    /// Path to a Linux kernel image.
170
    #[arg(short, long, value_name = "PATH")]
171
    kernel: Option<PathBuf>,
172

173
    /// Path to an ELF kernel with PVH note.
174
    #[cfg(target_arch = "x86_64")]
175
    #[arg(long, value_name = "PATH")]
176
    pvh: Option<PathBuf>,
177

178
    /// Path to a firmware image.
179
    #[arg(long, short, value_name = "PATH")]
180
    firmware: Option<PathBuf>,
181

182
    /// Command line to pass to the kernel, e.g. `console=ttyS0`.
183
    #[arg(short, long, value_name = "ARGS")]
184
    cmd_line: Option<String>,
185

186
    /// Path to an initramfs image.
187
    #[arg(short, long, value_name = "PATH")]
188
    initramfs: Option<PathBuf>,
189

190
    /// Number of VCPUs assigned to the guest.
191
    #[arg(long, default_value_t = 1)]
192
    num_cpu: u32,
193

194
    /// DEPRECATED: Use --memory instead.
195
    #[arg(long, default_value = "1G")]
196
    mem_size: String,
197

198
    #[arg(short, long, help(
199
        help_text::<MemConfig>("Specify the memory of the guest.")
200
    ))]
201
    memory: Option<String>,
202

203
    /// Add a pvpanic device.
204
    #[arg(long)]
205
    pvpanic: bool,
206

207
    #[cfg(target_arch = "x86_64")]
208
    #[arg(long = "fw-cfg", help(
209
        help_text::<FwCfgItemParam>("Add an extra item to the fw_cfg device.")
210
    ), value_name = "ITEM")]
211
    fw_cfgs: Vec<String>,
212

213
    /// Add a VirtIO entropy device.
214
    #[arg(long)]
215
    entropy: bool,
216

217
    #[cfg(target_os = "linux")]
218
    #[arg(long, help(
219
        help_text::<NetParam>("Add a VirtIO net device.")
220
    ))]
221
    net: Vec<String>,
222

223
    #[arg(long, help(
224
        help_text::<BlkParam>("Add a VirtIO block device.")
225
    ))]
226
    blk: Vec<String>,
227

228
    #[arg(long, help(
229
        help_text::<Coco>("Enable confidential compute supported by host platform.")
230
    ))]
231
    coco: Option<String>,
232

233
    #[arg(long, help(
234
        help_text::<FsParam>("Add a VirtIO filesystem device.")
235
    ))]
236
    fs: Vec<String>,
237

238
    #[cfg(target_os = "linux")]
239
    #[arg(long, help(
240
        help_text::<VsockParam>("Add a VirtIO vsock device.")
241
    ))]
242
    vsock: Option<String>,
243

244
    #[cfg(target_os = "linux")]
245
    #[arg(long, help(help_text::<CdevParam>(
246
        "Assign a host PCI device to the guest using IOMMUFD API."
247
    ) ))]
248
    vfio_cdev: Vec<String>,
249

250
    #[cfg(target_os = "linux")]
251
    #[arg(long, help(help_text::<IoasParam>("Create a new IO address space.")))]
252
    vfio_ioas: Vec<String>,
253

254
    #[cfg(target_os = "linux")]
255
    #[arg(long, help(help_text::<GroupParam>(
256
        "Assign a host PCI device to the guest using legacy VFIO API."
257
    )))]
258
    vfio_group: Vec<String>,
259

260
    #[cfg(target_os = "linux")]
261
    #[arg(long, help(help_text::<ContainerParam>("Add a new VFIO container.")))]
262
    vfio_container: Vec<String>,
263

264
    #[arg(long)]
265
    #[arg(long, help(help_text::<BalloonParam>("Add a VirtIO balloon device.")))]
266
    balloon: Option<String>,
267

268
    #[arg(short, long("object"), help = DOC_OBJECTS, value_name = "OBJECT")]
269
    objects: Vec<String>,
270
}
271

272
#[cfg(target_os = "linux")]
UNCOV
273
fn add_net<H>(
274
    vm: &Machine<H>,
275
    args: Vec<String>,
276
    objects: &HashMap<&str, &str>,
277
) -> Result<(), Error>
278
where
279
    H: hv::Hypervisor + 'static,
280
{
281
    for (index, net_opt) in args.into_iter().enumerate() {
×
282
        let net_param: NetParam = match serde_aco::from_args(&net_opt, objects) {
×
283
            Ok(p) => p,
×
284
            Err(_) => {
×
285
                let tap_param = serde_aco::from_args::<NetTapParam>(&net_opt, objects)
×
286
                    .context(error::ParseArg { arg: net_opt })?;
×
287
                NetParam::Tap(tap_param)
×
288
            }
289
        };
290
        match net_param {
×
291
            NetParam::Tap(tap_param) => vm.add_virtio_dev(format!("virtio-net-{index}"), tap_param),
×
292
            #[cfg(target_os = "linux")]
293
            NetParam::Vu(sock) => {
×
294
                let param = VuFrontendParam {
295
                    id: DeviceId::Net,
296
                    socket: sock.socket,
×
297
                };
298
                vm.add_virtio_dev(format!("vu-net-{index}"), param)
×
299
            }
300
        }
301
        .context(error::CreateDevice)?;
×
302
    }
303
    Ok(())
×
304
}
305

306
fn add_blk<H>(
×
307
    vm: &Machine<H>,
308
    args: Vec<String>,
309
    objects: &HashMap<&str, &str>,
310
) -> Result<(), Error>
311
where
312
    H: hv::Hypervisor + 'static,
313
{
314
    for (index, opt) in args.into_iter().enumerate() {
×
315
        let param: BlkParam = match serde_aco::from_args(&opt, objects) {
×
316
            Ok(param) => param,
×
317
            Err(_) => match serde_aco::from_args(&opt, objects) {
×
318
                Ok(param) => BlkParam::File(param),
×
319
                Err(_) => {
×
320
                    eprintln!("Please update the cmd line to --blk file,path={opt}");
×
321
                    BlkParam::File(BlkFileParam {
×
322
                        path: opt.into(),
×
323
                        ..Default::default()
×
324
                    })
325
                }
326
            },
327
        };
328
        match param {
×
329
            BlkParam::File(p) => vm.add_virtio_dev(format!("virtio-blk-{index}"), p),
×
330
            #[cfg(target_os = "linux")]
331
            BlkParam::Vu(s) => {
×
332
                let p = VuFrontendParam {
333
                    id: DeviceId::Block,
334
                    socket: s.socket,
×
335
                };
336
                vm.add_virtio_dev(format!("vu-net-{index}"), p)
×
337
            }
338
        }
339
        .context(error::CreateDevice)?;
×
340
    }
341
    Ok(())
×
342
}
343

344
pub fn boot(args: BootArgs) -> Result<(), Error> {
×
345
    let objects = parse_objects(&args.objects)?;
×
346
    let hv_config = if let Some(hv_cfg_opt) = args.hypervisor {
×
347
        serde_aco::from_args(&hv_cfg_opt, &objects).context(error::ParseArg { arg: hv_cfg_opt })?
×
348
    } else {
349
        Hypervisor::default()
×
350
    };
351
    let hypervisor = match hv_config {
352
        #[cfg(target_os = "linux")]
UNCOV
353
        Hypervisor::Kvm(kvm_config) => Kvm::new(kvm_config).context(error::Hypervisor)?,
354
        #[cfg(target_os = "macos")]
355
        Hypervisor::Hvf => Hvf {},
356
    };
357
    let coco = match args.coco {
×
358
        None => None,
×
359
        Some(c) => Some(serde_aco::from_args(&c, &objects).context(error::ParseArg { arg: c })?),
×
360
    };
361
    let mem_config = if let Some(s) = args.memory {
×
362
        serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s })?
×
363
    } else {
364
        #[cfg(target_os = "linux")]
UNCOV
365
        eprintln!(
UNCOV
366
            "Please update the cmd line to --memory size={},backend=memfd",
367
            args.mem_size
368
        );
369
        let size = serde_aco::from_args(&args.mem_size, &objects)
×
370
            .context(error::ParseArg { arg: args.mem_size })?;
×
371
        MemConfig {
372
            size,
373
            #[cfg(target_os = "linux")]
374
            backend: MemBackend::Memfd,
375
            #[cfg(not(target_os = "linux"))]
376
            backend: MemBackend::Anonymous,
377
            ..Default::default()
378
        }
379
    };
380
    let board_config = BoardConfig {
381
        mem: mem_config,
382
        num_cpu: args.num_cpu,
×
383
        coco,
384
    };
385
    let vm = Machine::new(hypervisor, board_config).context(error::CreateVm)?;
×
386
    #[cfg(target_arch = "x86_64")]
387
    vm.add_com1().context(error::CreateDevice)?;
388
    #[cfg(target_arch = "aarch64")]
389
    vm.add_pl011().context(error::CreateDevice)?;
390

391
    if args.pvpanic {
×
392
        vm.add_pvpanic().context(error::CreateDevice)?;
×
393
    }
394

395
    #[cfg(target_arch = "x86_64")]
396
    if args.firmware.is_some() || !args.fw_cfgs.is_empty() {
397
        let params = args
398
            .fw_cfgs
399
            .into_iter()
400
            .map(|s| serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s }))
401
            .collect::<Result<Vec<_>, _>>()?;
402
        let fw_cfg = vm
403
            .add_fw_cfg(params.into_iter())
404
            .context(error::CreateDevice)?;
405
        let mut dev = fw_cfg.lock();
406

407
        if let Some(kernel) = &args.kernel {
408
            dev.add_kernel_data(File::open(kernel).context(error::OpenFile { path: kernel })?)
409
                .context(error::FwCfg)?
410
        }
411
        if let Some(initramfs) = &args.initramfs {
412
            dev.add_initramfs_data(
413
                File::open(initramfs).context(error::OpenFile { path: initramfs })?,
414
            )
415
            .context(error::FwCfg)?;
416
        }
417
        if let Some(cmdline) = &args.cmd_line {
418
            let Ok(cmdline_c) = CString::new(cmdline.as_str()) else {
419
                return error::CreateCString {
420
                    s: cmdline.to_owned(),
421
                }
422
                .fail();
423
            };
424
            dev.add_kernel_cmdline(cmdline_c);
425
        }
426
    };
427

428
    if args.entropy {
×
NEW
429
        vm.add_virtio_dev("virtio-entropy", EntropyParam::default())
×
430
            .context(error::CreateDevice)?;
×
431
    }
432
    #[cfg(target_os = "linux")]
UNCOV
433
    add_net(&vm, args.net, &objects)?;
434
    add_blk(&vm, args.blk, &objects)?;
×
435
    for (index, fs) in args.fs.into_iter().enumerate() {
×
436
        let param: FsParam =
×
437
            serde_aco::from_args(&fs, &objects).context(error::ParseArg { arg: fs })?;
438
        match param {
×
439
            FsParam::Dir(p) => vm.add_virtio_dev(format!("virtio-fs-{index}"), p),
×
440
            #[cfg(target_os = "linux")]
UNCOV
441
            FsParam::Vu(p) => vm.add_virtio_dev(format!("vu-fs-{index}"), p),
442
        }
443
        .context(error::CreateDevice)?;
×
444
    }
445
    #[cfg(target_os = "linux")]
UNCOV
446
    if let Some(vsock) = args.vsock {
UNCOV
447
        let param =
UNCOV
448
            serde_aco::from_args(&vsock, &objects).context(error::ParseArg { arg: vsock })?;
UNCOV
449
        match param {
UNCOV
450
            VsockParam::Vhost(p) => vm
UNCOV
451
                .add_virtio_dev("vhost-vsock", p)
UNCOV
452
                .context(error::CreateDevice)?,
453
        };
454
    }
455
    if let Some(balloon) = args.balloon {
×
456
        let param: BalloonParam =
×
457
            serde_aco::from_args(&balloon, &objects).context(error::ParseArg { arg: balloon })?;
458
        vm.add_virtio_dev("virtio-balloon", param)
×
459
            .context(error::CreateDevice)?;
×
460
    }
461

462
    #[cfg(target_os = "linux")]
UNCOV
463
    for ioas in args.vfio_ioas.into_iter() {
UNCOV
464
        let param: IoasParam =
UNCOV
465
            serde_aco::from_args(&ioas, &objects).context(error::ParseArg { arg: ioas })?;
UNCOV
466
        vm.add_vfio_ioas(param).context(error::CreateDevice)?;
467
    }
468
    #[cfg(target_os = "linux")]
UNCOV
469
    for (index, vfio) in args.vfio_cdev.into_iter().enumerate() {
UNCOV
470
        let param: CdevParam =
UNCOV
471
            serde_aco::from_args(&vfio, &objects).context(error::ParseArg { arg: vfio })?;
UNCOV
472
        vm.add_vfio_cdev(format!("vfio-{index}").into(), param)
UNCOV
473
            .context(error::CreateDevice)?;
474
    }
475

476
    #[cfg(target_os = "linux")]
UNCOV
477
    for container in args.vfio_container.into_iter() {
UNCOV
478
        let param: ContainerParam = serde_aco::from_args(&container, &objects)
UNCOV
479
            .context(error::ParseArg { arg: container })?;
UNCOV
480
        vm.add_vfio_container(param).context(error::CreateDevice)?;
481
    }
482
    #[cfg(target_os = "linux")]
UNCOV
483
    for (index, group) in args.vfio_group.into_iter().enumerate() {
UNCOV
484
        let param: GroupParam =
UNCOV
485
            serde_aco::from_args(&group, &objects).context(error::ParseArg { arg: group })?;
UNCOV
486
        vm.add_vfio_devs_in_group(&index.to_string(), param)
UNCOV
487
            .context(error::CreateDevice)?;
488
    }
489

490
    let payload = if let Some(fw) = args.firmware {
×
491
        Some(Payload {
×
492
            executable: fw,
493
            exec_type: ExecType::Firmware,
×
494
            initramfs: None,
×
495
            cmd_line: None,
×
496
        })
497
    } else if let Some(kernel) = args.kernel {
×
498
        Some(Payload {
×
499
            exec_type: ExecType::Linux,
×
500
            executable: kernel,
501
            initramfs: args.initramfs,
×
502
            cmd_line: args.cmd_line,
×
503
        })
504
    } else {
505
        #[cfg(target_arch = "x86_64")]
506
        if let Some(pvh_kernel) = args.pvh {
507
            Some(Payload {
508
                executable: pvh_kernel,
509
                exec_type: ExecType::Pvh,
510
                initramfs: args.initramfs,
511
                cmd_line: args.cmd_line,
512
            })
513
        } else {
514
            None
515
        }
516
        #[cfg(not(target_arch = "x86_64"))]
517
        None
518
    };
519
    if let Some(payload) = payload {
×
520
        vm.add_payload(payload);
×
521
    }
522

523
    vm.boot().context(error::BootVm)?;
×
524
    vm.wait().context(error::WaitVm)?;
×
525
    Ok(())
×
526
}
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

© 2026 Coveralls, Inc