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

google / alioth / 19414098802

16 Nov 2025 11:34PM UTC coverage: 26.521% (+0.02%) from 26.498%
19414098802

push

github

Lencerf
refactor: add kernel data to fw_cfg automatically

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

396 of 648 branches covered (61.11%)

Branch coverage included in aggregate %.

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

105 existing lines in 5 files now uncovered.

3585 of 14363 relevant lines covered (24.96%)

21.79 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
use std::ffi::CString;
17
use std::path::{Path, PathBuf};
18

19
use alioth::board::BoardConfig;
20
#[cfg(target_arch = "x86_64")]
21
use alioth::device::fw_cfg::FwCfgItemParam;
22
use alioth::errors::{DebugTrace, trace_error};
23
#[cfg(target_os = "macos")]
24
use alioth::hv::Hvf;
25
use alioth::hv::{self, Coco};
26
#[cfg(target_os = "linux")]
27
use alioth::hv::{Kvm, KvmConfig};
28
use alioth::loader::{Executable, Payload};
29
use alioth::mem::{MemBackend, MemConfig};
30
#[cfg(target_os = "linux")]
31
use alioth::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam};
32
#[cfg(target_os = "linux")]
33
use alioth::virtio::DeviceId;
34
use alioth::virtio::dev::balloon::BalloonParam;
35
use alioth::virtio::dev::blk::BlkFileParam;
36
use alioth::virtio::dev::entropy::EntropyParam;
37
use alioth::virtio::dev::fs::shared_dir::SharedDirParam;
38
#[cfg(target_os = "linux")]
39
use alioth::virtio::dev::fs::vu::VuFsParam;
40
#[cfg(target_os = "linux")]
41
use alioth::virtio::dev::net::tap::NetTapParam;
42
#[cfg(target_os = "macos")]
43
use alioth::virtio::dev::net::vmnet::NetVmnetParam;
44
use alioth::virtio::dev::vsock::UdsVsockParam;
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::virtio::worker::WorkerApi;
50
use alioth::vm::Machine;
51
use clap::Args;
52
use serde::Deserialize;
53
use serde_aco::{Help, help_text};
54
use snafu::{ResultExt, Snafu};
55

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

58
#[trace_error]
59
#[derive(Snafu, DebugTrace)]
×
60
#[snafu(module, context(suffix(false)))]
61
pub enum Error {
62
    #[snafu(display("Failed to parse {arg}"))]
63
    ParseArg {
64
        arg: String,
65
        error: serde_aco::Error,
66
    },
67
    #[snafu(display("Failed to parse objects"), context(false))]
68
    ParseObjects { source: crate::objects::Error },
69
    #[cfg(target_os = "linux")]
70
    #[snafu(display("Failed to access system hypervisor"))]
71
    Hypervisor { source: alioth::hv::Error },
72
    #[snafu(display("Failed to create a VM"))]
73
    CreateVm { source: alioth::vm::Error },
74
    #[snafu(display("Failed to create a device"))]
75
    CreateDevice { source: alioth::vm::Error },
76
    #[snafu(display("Failed to boot a VM"))]
77
    BootVm { source: alioth::vm::Error },
78
    #[snafu(display("VM did not shutdown peacefully"))]
79
    WaitVm { source: alioth::vm::Error },
80
}
81

82
#[derive(Debug, Deserialize, Clone, Help)]
83
#[cfg_attr(target_os = "macos", derive(Default))]
84
enum Hypervisor {
85
    /// KVM backed by the Linux kernel.
86
    #[cfg(target_os = "linux")]
87
    #[serde(alias = "kvm")]
88
    Kvm(KvmConfig),
89
    /// macOS Hypervisor Framework.
90
    #[cfg(target_os = "macos")]
91
    #[serde(alias = "hvf")]
92
    #[default]
93
    Hvf,
94
}
95

96
#[cfg(target_os = "linux")]
97
impl Default for Hypervisor {
UNCOV
98
    fn default() -> Self {
×
UNCOV
99
        Hypervisor::Kvm(KvmConfig::default())
×
UNCOV
100
    }
×
101
}
102

103
#[derive(Debug, Deserialize, Clone, Help)]
104
enum FsParam {
105
    /// VirtIO FS device backed by a shared directory.
106
    #[serde(alias = "dir")]
107
    Dir(SharedDirParam),
108
    #[cfg(target_os = "linux")]
109
    /// VirtIO FS device backed by a vhost-user process, e.g. virtiofsd.
110
    #[serde(alias = "vu")]
111
    Vu(VuFsParam),
112
}
113

114
#[derive(Debug, Deserialize, Clone, Help)]
115
enum VsockParam {
116
    #[cfg(target_os = "linux")]
117
    /// Vsock device backed by host kernel vhost-vsock module.
118
    #[serde(alias = "vhost")]
119
    Vhost(VhostVsockParam),
120
    /// Vsock device mapped to a Unix domain socket.
121
    #[serde(alias = "uds")]
122
    Uds(UdsVsockParam),
123
}
124

125
#[cfg(target_os = "linux")]
126
#[derive(Deserialize, Help)]
127
struct VuSocket {
128
    socket: Box<Path>,
129
}
130

131
#[derive(Deserialize, Help)]
132
enum NetParam {
133
    /// VirtIO net device backed by TUN/TAP, MacVTap, or IPVTap.
134
    #[cfg(target_os = "linux")]
135
    #[serde(alias = "tap")]
136
    Tap(NetTapParam),
137
    /// VirtIO net device backed by vmnet framework.
138
    #[cfg(target_os = "macos")]
139
    #[serde(alias = "vmnet")]
140
    Vmnet(NetVmnetParam),
141
    /// vhost-user net device over a Unix domain socket.
142
    #[cfg(target_os = "linux")]
143
    #[serde(alias = "vu")]
144
    Vu(VuSocket),
145
}
146

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

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

166
    /// Path to a Linux kernel image.
167
    #[arg(short, long, value_name = "PATH")]
168
    kernel: Option<Box<Path>>,
169

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

175
    /// Path to a firmware image.
176
    #[arg(long, short, value_name = "PATH")]
177
    firmware: Option<Box<Path>>,
178

179
    /// Command line to pass to the kernel, e.g. `console=ttyS0`.
180
    #[arg(short, long, alias = "cmd-line", value_name = "ARGS")]
181
    cmdline: Option<CString>,
182

183
    /// Path to an initramfs image.
184
    #[arg(short, long, value_name = "PATH")]
185
    initramfs: Option<Box<Path>>,
186

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

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

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

200
    /// Add a pvpanic device.
201
    #[arg(long)]
202
    pvpanic: bool,
203

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

210
    /// Add a VirtIO entropy device.
211
    #[arg(long)]
212
    entropy: bool,
213

214
    #[arg(long, help(
215
        help_text::<NetParam>("Add a VirtIO net device.")
216
    ))]
217
    net: Vec<String>,
218

219
    #[arg(long, help(
220
        help_text::<BlkParam>("Add a VirtIO block device.")
221
    ))]
222
    blk: Vec<String>,
223

224
    #[arg(long, help(
225
        help_text::<Coco>("Enable confidential compute supported by host platform.")
226
    ))]
227
    coco: Option<String>,
228

229
    #[arg(long, help(
230
        help_text::<FsParam>("Add a VirtIO filesystem device.")
231
    ))]
232
    fs: Vec<String>,
233

234
    #[arg(long, help(
235
        help_text::<VsockParam>("Add a VirtIO vsock device.")
236
    ))]
237
    vsock: Option<String>,
238

239
    #[cfg(target_os = "linux")]
240
    #[arg(long, help(help_text::<CdevParam>(
241
        "Assign a host PCI device to the guest using IOMMUFD API."
242
    ) ))]
243
    vfio_cdev: Vec<String>,
244

245
    #[cfg(target_os = "linux")]
246
    #[arg(long, help(help_text::<IoasParam>("Create a new IO address space.")))]
247
    vfio_ioas: Vec<String>,
248

249
    #[cfg(target_os = "linux")]
250
    #[arg(long, help(help_text::<GroupParam>(
251
        "Assign a host PCI device to the guest using legacy VFIO API."
252
    )))]
253
    vfio_group: Vec<String>,
254

255
    #[cfg(target_os = "linux")]
256
    #[arg(long, help(help_text::<ContainerParam>("Add a new VFIO container.")))]
257
    vfio_container: Vec<String>,
258

259
    #[arg(long)]
260
    #[arg(long, help(help_text::<BalloonParam>("Add a VirtIO balloon device.")))]
261
    balloon: Option<String>,
262

263
    #[arg(short, long("object"), help = DOC_OBJECTS, value_name = "OBJECT")]
264
    objects: Vec<String>,
265
}
266

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

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

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

395
    if args.pvpanic {
×
UNCOV
396
        vm.add_pvpanic().context(error::CreateDevice)?;
×
397
    }
×
398

399
    #[cfg(target_arch = "x86_64")]
UNCOV
400
    if args.firmware.is_some() || !args.fw_cfgs.is_empty() {
×
UNCOV
401
        let params = args
×
402
            .fw_cfgs
×
403
            .into_iter()
×
404
            .map(|s| serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s }))
×
405
            .collect::<Result<Vec<_>, _>>()?;
×
NEW
406
        vm.add_fw_cfg(params.into_iter())
×
407
            .context(error::CreateDevice)?;
×
408
    };
×
409

410
    if args.entropy {
×
UNCOV
411
        vm.add_virtio_dev("virtio-entropy", EntropyParam::default())
×
412
            .context(error::CreateDevice)?;
×
413
    }
×
414
    add_net(&vm, args.net, &objects)?;
×
UNCOV
415
    add_blk(&vm, args.blk, &objects)?;
×
416
    for (index, fs) in args.fs.into_iter().enumerate() {
×
417
        let param: FsParam =
×
418
            serde_aco::from_args(&fs, &objects).context(error::ParseArg { arg: fs })?;
×
419
        match param {
×
420
            FsParam::Dir(p) => vm.add_virtio_dev(format!("virtio-fs-{index}"), p),
×
421
            #[cfg(target_os = "linux")]
422
            FsParam::Vu(p) => vm.add_virtio_dev(format!("vu-fs-{index}"), p),
×
423
        }
424
        .context(error::CreateDevice)?;
×
425
    }
426
    if let Some(vsock) = args.vsock {
×
UNCOV
427
        let param =
×
428
            serde_aco::from_args(&vsock, &objects).context(error::ParseArg { arg: vsock })?;
×
UNCOV
429
        match param {
×
430
            #[cfg(target_os = "linux")]
UNCOV
431
            VsockParam::Vhost(p) => vm
×
432
                .add_virtio_dev("vhost-vsock", p)
×
433
                .context(error::CreateDevice)?,
×
434
            VsockParam::Uds(p) => vm
×
435
                .add_virtio_dev("uds-vsock", p)
×
UNCOV
436
                .context(error::CreateDevice)?,
×
437
        };
438
    }
×
439
    if let Some(balloon) = args.balloon {
×
440
        let param: BalloonParam =
×
441
            serde_aco::from_args(&balloon, &objects).context(error::ParseArg { arg: balloon })?;
×
442
        vm.add_virtio_dev("virtio-balloon", param)
×
UNCOV
443
            .context(error::CreateDevice)?;
×
444
    }
×
445

446
    #[cfg(target_os = "linux")]
447
    for ioas in args.vfio_ioas.into_iter() {
×
448
        let param: IoasParam =
×
449
            serde_aco::from_args(&ioas, &objects).context(error::ParseArg { arg: ioas })?;
×
450
        vm.add_vfio_ioas(param).context(error::CreateDevice)?;
×
451
    }
452
    #[cfg(target_os = "linux")]
453
    for (index, vfio) in args.vfio_cdev.into_iter().enumerate() {
×
454
        let param: CdevParam =
×
455
            serde_aco::from_args(&vfio, &objects).context(error::ParseArg { arg: vfio })?;
×
456
        vm.add_vfio_cdev(format!("vfio-{index}").into(), param)
×
UNCOV
457
            .context(error::CreateDevice)?;
×
458
    }
459

460
    #[cfg(target_os = "linux")]
461
    for container in args.vfio_container.into_iter() {
×
462
        let param: ContainerParam = serde_aco::from_args(&container, &objects)
×
463
            .context(error::ParseArg { arg: container })?;
×
UNCOV
464
        vm.add_vfio_container(param).context(error::CreateDevice)?;
×
465
    }
466
    #[cfg(target_os = "linux")]
467
    for (index, group) in args.vfio_group.into_iter().enumerate() {
×
468
        let param: GroupParam =
×
469
            serde_aco::from_args(&group, &objects).context(error::ParseArg { arg: group })?;
×
470
        vm.add_vfio_devs_in_group(&index.to_string(), param)
×
UNCOV
471
            .context(error::CreateDevice)?;
×
472
    }
473

NEW
474
    let mut payload = Payload {
×
NEW
475
        firmware: args.firmware,
×
NEW
476
        initramfs: args.initramfs,
×
NEW
477
        cmdline: args.cmdline,
×
NEW
478
        ..Default::default()
×
UNCOV
479
    };
×
NEW
480
    payload.executable = args.kernel.map(Executable::Linux);
×
481
    #[cfg(target_arch = "x86_64")]
NEW
482
    if payload.executable.is_none() {
×
NEW
483
        payload.executable = args.pvh.map(Executable::Pvh);
×
UNCOV
484
    }
×
NEW
485
    vm.add_payload(payload);
×
486

487
    vm.boot().context(error::BootVm)?;
×
UNCOV
488
    vm.wait().context(error::WaitVm)?;
×
489
    Ok(())
×
490
}
×
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