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

google / alioth / 20496833044

25 Dec 2025 01:27AM UTC coverage: 26.222% (+0.009%) from 26.213%
20496833044

Pull #346

github

web-flow
Merge 2d5ae0b57 into b1883779e
Pull Request #346: refactor(vm): separate argument parsing and device creation

404 of 658 branches covered (61.4%)

Branch coverage included in aggregate %.

0 of 140 new or added lines in 3 files covered. (0.0%)

1 existing line in 1 file now uncovered.

3443 of 14013 relevant lines covered (24.57%)

21.87 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::mem;
18
use std::path::{Path, PathBuf};
19

20
use alioth::board::{BoardConfig, CpuConfig};
21
#[cfg(target_arch = "x86_64")]
22
use alioth::device::fw_cfg::FwCfgItemParam;
23
use alioth::errors::{DebugTrace, trace_error};
24
#[cfg(target_os = "macos")]
25
use alioth::hv::Hvf;
26
#[cfg(target_os = "linux")]
27
use alioth::hv::Kvm;
28
use alioth::hv::{Coco, HvConfig};
29
use alioth::loader::{Executable, Payload};
30
use alioth::mem::{MemBackend, MemConfig};
31
#[cfg(target_os = "linux")]
32
use alioth::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam};
33
use alioth::virtio::dev::balloon::BalloonParam;
34
use alioth::virtio::dev::blk::BlkFileParam;
35
use alioth::virtio::dev::entropy::EntropyParam;
36
use alioth::virtio::worker::WorkerApi;
37
use alioth::vm::Machine;
38
use alioth::vm::config::{BlkParam, Config, FsParam, NetParam, VsockParam};
39
use clap::Args;
40
use serde_aco::help_text;
41
use snafu::{ResultExt, Snafu};
42

43
use crate::objects::{DOC_OBJECTS, parse_objects};
44

45
#[trace_error]
46
#[derive(Snafu, DebugTrace)]
×
47
#[snafu(module, context(suffix(false)))]
48
pub enum Error {
49
    #[snafu(display("Failed to parse {arg}"))]
50
    ParseArg {
51
        arg: String,
52
        error: serde_aco::Error,
53
    },
54
    #[snafu(display("Failed to parse objects"), context(false))]
55
    ParseObjects { source: crate::objects::Error },
56
    #[cfg(target_os = "linux")]
57
    #[snafu(display("Failed to access system hypervisor"))]
58
    Hypervisor { source: alioth::hv::Error },
59
    #[snafu(display("Failed to create a VM"))]
60
    CreateVm { source: alioth::vm::Error },
61
    #[snafu(display("Failed to boot a VM"))]
62
    BootVm { source: alioth::vm::Error },
63
    #[snafu(display("VM did not shutdown peacefully"))]
64
    WaitVm { source: alioth::vm::Error },
65
}
66

67
#[derive(Args, Debug, Clone)]
68
#[command(arg_required_else_help = true, alias("run"))]
69
pub struct BootArgs {
70
    #[arg(long, help(
71
        help_text::<HvConfig>("Specify the Hypervisor to run on.")
72
    ), value_name = "HV")]
73
    hypervisor: Option<String>,
74

75
    /// Path to a Linux kernel image.
76
    #[arg(short, long, value_name = "PATH")]
77
    kernel: Option<Box<Path>>,
78

79
    /// Path to an ELF kernel with PVH note.
80
    #[cfg(target_arch = "x86_64")]
81
    #[arg(long, value_name = "PATH")]
82
    pvh: Option<Box<Path>>,
83

84
    /// Path to a firmware image.
85
    #[arg(long, short, value_name = "PATH")]
86
    firmware: Option<Box<Path>>,
87

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

92
    /// Path to an initramfs image.
93
    #[arg(short, long, value_name = "PATH")]
94
    initramfs: Option<Box<Path>>,
95

96
    /// DEPRECATED: Use --cpu instead.
97
    #[arg(long, default_value_t = 1)]
98
    num_cpu: u16,
99

100
    #[arg(short('p'), long, help(
101
        help_text::<CpuConfig>("Configure the VCPUs of the guest.")
102
    ))]
103
    cpu: Option<Box<str>>,
104

105
    /// DEPRECATED: Use --memory instead.
106
    #[arg(long, default_value = "1G")]
107
    mem_size: String,
108

109
    #[arg(short, long, help(
110
        help_text::<MemConfig>("Specify the memory of the guest.")
111
    ))]
112
    memory: Option<String>,
113

114
    /// Add a pvpanic device.
115
    #[arg(long)]
116
    pvpanic: bool,
117

118
    #[cfg(target_arch = "x86_64")]
119
    #[arg(long, help(
120
        help_text::<FwCfgItemParam>("Add an extra item to the fw_cfg device.")
121
    ), value_name = "ITEM")]
122
    fw_cfg: Vec<String>,
123

124
    /// Add a VirtIO entropy device.
125
    #[arg(long)]
126
    entropy: bool,
127

128
    #[arg(long, help(
129
        help_text::<NetParam>("Add a VirtIO net device.")
130
    ))]
131
    net: Vec<String>,
132

133
    #[arg(long, help(
134
        help_text::<BlkParam>("Add a VirtIO block device.")
135
    ))]
136
    blk: Vec<String>,
137

138
    #[arg(long, help(
139
        help_text::<Coco>("Enable confidential compute supported by host platform.")
140
    ))]
141
    coco: Option<String>,
142

143
    #[arg(long, help(
144
        help_text::<FsParam>("Add a VirtIO filesystem device.")
145
    ))]
146
    fs: Vec<String>,
147

148
    #[arg(long, help(
149
        help_text::<VsockParam>("Add a VirtIO vsock device.")
150
    ))]
151
    vsock: Option<String>,
152

153
    #[cfg(target_os = "linux")]
154
    #[arg(long, help(help_text::<CdevParam>(
155
        "Assign a host PCI device to the guest using IOMMUFD API."
156
    ) ))]
157
    vfio_cdev: Vec<String>,
158

159
    #[cfg(target_os = "linux")]
160
    #[arg(long, help(help_text::<IoasParam>("Create a new IO address space.")))]
161
    vfio_ioas: Vec<String>,
162

163
    #[cfg(target_os = "linux")]
164
    #[arg(long, help(help_text::<GroupParam>(
165
        "Assign a host PCI device to the guest using legacy VFIO API."
166
    )))]
167
    vfio_group: Vec<String>,
168

169
    #[cfg(target_os = "linux")]
170
    #[arg(long, help(help_text::<ContainerParam>("Add a new VFIO container.")))]
171
    vfio_container: Vec<String>,
172

173
    #[arg(long)]
174
    #[arg(long, help(help_text::<BalloonParam>("Add a VirtIO balloon device.")))]
175
    balloon: Option<String>,
176

177
    #[arg(short, long("object"), help = DOC_OBJECTS, value_name = "OBJECT")]
178
    objects: Vec<String>,
179
}
180

NEW
181
fn parse_config(args: BootArgs, objects: HashMap<&str, &str>) -> Result<Config, Error> {
×
NEW
182
    let mut board_config = BoardConfig::default();
×
183

NEW
184
    if let Some(arg) = args.coco {
×
NEW
185
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
186
        board_config.coco = Some(param);
×
187
    };
×
NEW
188
    board_config.mem = if let Some(arg) = args.memory {
×
NEW
189
        serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?
×
190
    } else {
191
        #[cfg(target_os = "linux")]
192
        eprintln!(
×
193
            "Please update the cmd line to --memory size={},backend=memfd",
194
            args.mem_size
195
        );
196
        let size = serde_aco::from_args(&args.mem_size, &objects)
×
197
            .context(error::ParseArg { arg: args.mem_size })?;
×
198
        MemConfig {
×
199
            size,
×
200
            #[cfg(target_os = "linux")]
×
201
            backend: MemBackend::Memfd,
×
202
            #[cfg(not(target_os = "linux"))]
×
203
            backend: MemBackend::Anonymous,
×
204
            ..Default::default()
×
205
        }
×
206
    };
NEW
207
    board_config.cpu = if let Some(arg) = args.cpu {
×
NEW
208
        serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?
×
209
    } else {
210
        eprintln!("Please update the cmd line to --cpu count={}", args.num_cpu);
×
211
        CpuConfig {
×
212
            count: args.num_cpu,
×
213
            ..Default::default()
×
214
        }
×
215
    };
216

NEW
217
    let mut config = Config {
×
NEW
218
        board: board_config,
×
NEW
219
        pvpanic: args.pvpanic,
×
NEW
220
        ..Default::default()
×
221
    };
×
222

223
    #[cfg(target_arch = "x86_64")]
NEW
224
    for arg in args.fw_cfg {
×
NEW
225
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
226
        config.fw_cfg.push(param);
×
227
    }
228

229
    if args.entropy {
×
NEW
230
        config.entropy = Some(EntropyParam::default());
×
231
    }
×
232

NEW
233
    for arg in args.net {
×
234
        #[cfg(target_os = "linux")]
NEW
235
        let param = if let Ok(param) = serde_aco::from_args(&arg, &objects) {
×
NEW
236
            param
×
237
        } else {
NEW
238
            let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
239
            NetParam::Tap(param)
×
240
        };
241
        #[cfg(target_os = "macos")]
NEW
242
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
243

NEW
244
        config.net.push(param);
×
245
    }
246

NEW
247
    for arg in args.blk {
×
NEW
248
        if let Ok(param) = serde_aco::from_args(&arg, &objects) {
×
NEW
249
            config.blk.push(param);
×
NEW
250
        } else if let Ok(param) = serde_aco::from_args(&arg, &objects) {
×
NEW
251
            config.blk.push(BlkParam::File(param));
×
NEW
252
        } else {
×
NEW
253
            eprintln!("Please update the cmd line to --blk file,path={arg}");
×
NEW
254
            config.blk.push(BlkParam::File(BlkFileParam {
×
NEW
255
                path: PathBuf::from(arg).into(),
×
NEW
256
                readonly: false,
×
NEW
257
                api: WorkerApi::Mio,
×
NEW
258
            }));
×
259
        }
×
260
    }
261

NEW
262
    for arg in args.fs {
×
NEW
263
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
264
        config.fs.push(param);
×
265
    }
266

NEW
267
    if let Some(arg) = args.vsock {
×
NEW
268
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
269
        config.vsock = Some(param);
×
NEW
270
    }
×
271

NEW
272
    if let Some(arg) = args.balloon {
×
NEW
273
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
274
        config.balloon = Some(param);
×
UNCOV
275
    }
×
276

277
    #[cfg(target_os = "linux")]
NEW
278
    for arg in args.vfio_ioas {
×
NEW
279
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
280
        config.vfio_ioas.push(param);
×
281
    }
282
    #[cfg(target_os = "linux")]
NEW
283
    for arg in args.vfio_cdev {
×
NEW
284
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
285
        config.vfio_cdev.push(param);
×
286
    }
287

288
    #[cfg(target_os = "linux")]
NEW
289
    for arg in args.vfio_container {
×
NEW
290
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
291
        config.vfio_container.push(param);
×
292
    }
293
    #[cfg(target_os = "linux")]
NEW
294
    for arg in args.vfio_group {
×
NEW
295
        let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
×
NEW
296
        config.vfio_group.push(param);
×
297
    }
298

299
    let mut payload = Payload {
×
300
        firmware: args.firmware,
×
301
        initramfs: args.initramfs,
×
302
        cmdline: args.cmdline,
×
303
        ..Default::default()
×
304
    };
×
305
    payload.executable = args.kernel.map(Executable::Linux);
×
306
    #[cfg(target_arch = "x86_64")]
307
    if payload.executable.is_none() {
×
308
        payload.executable = args.pvh.map(Executable::Pvh);
×
309
    }
×
NEW
310
    config.payload = payload;
×
311

NEW
312
    Ok(config)
×
NEW
313
}
×
314

NEW
315
pub fn boot(mut args: BootArgs) -> Result<(), Error> {
×
NEW
316
    let object_args = mem::take(&mut args.objects);
×
NEW
317
    let objects = parse_objects(&object_args)?;
×
318

NEW
319
    let hv_config = if let Some(arg) = args.hypervisor.take() {
×
NEW
320
        serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?
×
321
    } else {
NEW
322
        HvConfig::default()
×
323
    };
NEW
324
    let hypervisor = match hv_config {
×
325
        #[cfg(target_os = "linux")]
NEW
326
        HvConfig::Kvm(kvm_config) => Kvm::new(kvm_config).context(error::Hypervisor)?,
×
327
        #[cfg(target_os = "macos")]
NEW
328
        HvConfig::Hvf => Hvf {},
×
329
    };
330

NEW
331
    let config = parse_config(args, objects)?;
×
332

NEW
333
    let vm = Machine::new(hypervisor, config).context(error::CreateVm)?;
×
334

335
    vm.boot().context(error::BootVm)?;
×
336
    vm.wait().context(error::WaitVm)?;
×
337
    Ok(())
×
338
}
×
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