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

google / alioth / 20386865102

20 Dec 2025 01:04AM UTC coverage: 26.213% (+0.01%) from 26.199%
20386865102

push

github

Lencerf
feat(loader): boot guest with x2APIC enabled

When booted in x2APC mode, Linux is able to bring up CPUs with x2APIC ID
> 254.

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

404 of 658 branches covered (61.4%)

Branch coverage included in aggregate %.

0 of 10 new or added lines in 2 files covered. (0.0%)

639 existing lines in 11 files now uncovered.

3443 of 14018 relevant lines covered (24.56%)

21.72 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, CpuConfig};
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 {
98
    fn default() -> Self {
×
99
        Hypervisor::Kvm(KvmConfig::default())
×
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
    /// DEPRECATED: Use --cpu instead.
188
    #[arg(long, default_value_t = 1)]
189
    num_cpu: u16,
190

191
    #[arg(short('p'), long, help(
192
        help_text::<CpuConfig>("Configure the VCPUs of the guest.")
193
    ))]
194
    cpu: Option<Box<str>>,
195

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
409
    if args.pvpanic {
×
410
        vm.add_pvpanic().context(error::CreateDevice)?;
×
411
    }
×
412

413
    #[cfg(target_arch = "x86_64")]
414
    if args.firmware.is_some() || !args.fw_cfgs.is_empty() {
×
415
        let params = args
×
416
            .fw_cfgs
×
417
            .into_iter()
×
418
            .map(|s| serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s }))
×
419
            .collect::<Result<Vec<_>, _>>()?;
×
420
        vm.add_fw_cfg(params.into_iter())
×
UNCOV
421
            .context(error::CreateDevice)?;
×
422
    };
×
423

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

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

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

488
    let mut payload = Payload {
×
489
        firmware: args.firmware,
×
490
        initramfs: args.initramfs,
×
UNCOV
491
        cmdline: args.cmdline,
×
UNCOV
492
        ..Default::default()
×
UNCOV
493
    };
×
UNCOV
494
    payload.executable = args.kernel.map(Executable::Linux);
×
495
    #[cfg(target_arch = "x86_64")]
UNCOV
496
    if payload.executable.is_none() {
×
UNCOV
497
        payload.executable = args.pvh.map(Executable::Pvh);
×
UNCOV
498
    }
×
UNCOV
499
    vm.add_payload(payload);
×
500

UNCOV
501
    vm.boot().context(error::BootVm)?;
×
UNCOV
502
    vm.wait().context(error::WaitVm)?;
×
UNCOV
503
    Ok(())
×
UNCOV
504
}
×
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