Skip to content

Commit ea74b2b

Browse files
Alexandra Iordachealexandruag
authored andcommitted
cmdline: add virtio MMIO devices
Signed-off-by: Alexandra Iordache <aghecen@amazon.com>
1 parent 3cf96c5 commit ea74b2b

File tree

2 files changed

+135
-10
lines changed

2 files changed

+135
-10
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Added
44

5+
- Added functions for specifying virtio MMIO devices when building the kernel
6+
command line.
7+
8+
# [v0.2.0]
9+
10+
## Added
11+
512
- Added traits and structs for loading ELF (`vmlinux`), big zImage (`bzImage`)
613
and PE (`Image`) kernels into guest memory.
714
- Added traits and structs for writing boot parameters to guest memory.

src/cmdline/mod.rs

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,42 @@
1111
use std::fmt;
1212
use std::result;
1313

14+
use vm_memory::{Address, GuestAddress, GuestUsize};
15+
1416
/// The error type for command line building operations.
1517
#[derive(Debug, PartialEq)]
1618
pub enum Error {
1719
/// Operation would have resulted in a non-printable ASCII character.
1820
InvalidAscii,
21+
/// Invalid device passed to the kernel command line builder.
22+
InvalidDevice(String),
1923
/// Key/Value Operation would have had a space in it.
2024
HasSpace,
2125
/// Key/Value Operation would have had an equals sign in it.
2226
HasEquals,
27+
/// 0-sized virtio MMIO device passed to the kernel command line builder.
28+
MmioSize,
2329
/// Operation would have made the command line too large.
2430
TooLarge,
2531
}
2632

2733
impl fmt::Display for Error {
2834
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29-
write!(
30-
f,
31-
"{}",
32-
match *self {
33-
Error::InvalidAscii => "String contains a non-printable ASCII character.",
34-
Error::HasSpace => "String contains a space.",
35-
Error::HasEquals => "String contains an equals sign.",
36-
Error::TooLarge => "Inserting string would make command line too long.",
37-
}
38-
)
35+
match *self {
36+
Error::InvalidAscii => write!(f, "String contains a non-printable ASCII character."),
37+
Error::InvalidDevice(ref dev) => write!(
38+
f,
39+
"Invalid device passed to the kernel command line builder: {}.",
40+
dev
41+
),
42+
Error::HasSpace => write!(f, "String contains a space."),
43+
Error::HasEquals => write!(f, "String contains an equals sign."),
44+
Error::MmioSize => write!(
45+
f,
46+
"0-sized virtio MMIO device passed to the kernel command line builder."
47+
),
48+
Error::TooLarge => write!(f, "Inserting string would make command line too long."),
49+
}
3950
}
4051
}
4152

@@ -209,6 +220,75 @@ impl Cmdline {
209220
pub fn as_str(&self) -> &str {
210221
self.line.as_str()
211222
}
223+
224+
/// Adds a virtio MMIO device to the kernel command line.
225+
///
226+
/// Multiple devices can be specified, with multiple `virtio_mmio.device=` options. This
227+
/// function must be called once per device.
228+
/// The function appends a string of the following format to the kernel command line:
229+
/// `<size>@<baseaddr>:<irq>[:<id>]`.
230+
/// For more details see the [documentation] (section `virtio_mmio.device=`).
231+
///
232+
/// # Arguments
233+
///
234+
/// * `size` - size of the slot the device occupies on the MMIO bus.
235+
/// * `baseaddr` - physical base address of the device.
236+
/// * `irq` - interrupt number to be used by the device.
237+
/// * `id` - optional platform device ID.
238+
///
239+
/// # Examples
240+
///
241+
/// ```rust
242+
/// # use linux_loader::cmdline::*;
243+
/// # use std::ffi::CString;
244+
/// # use vm_memory::{GuestAddress, GuestUsize};
245+
/// let mut cl = Cmdline::new(100);
246+
/// cl.add_virtio_mmio_device(1 << 12, GuestAddress(0x1000), 5, Some(42)).unwrap();
247+
/// let cl_cstring = CString::new(cl).unwrap();
248+
/// assert_eq!(cl_cstring.to_str().unwrap(), "virtio_mmio.device=4K@0x1000:5:42");
249+
/// ```
250+
///
251+
/// [documentation]: https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
252+
pub fn add_virtio_mmio_device(
253+
&mut self,
254+
size: GuestUsize,
255+
baseaddr: GuestAddress,
256+
irq: u32,
257+
id: Option<u32>,
258+
) -> Result<()> {
259+
if size == 0 {
260+
return Err(Error::MmioSize);
261+
}
262+
263+
let mut device_str = format!(
264+
"virtio_mmio.device={}@0x{:x?}:{}",
265+
Self::guestusize_to_str(size),
266+
baseaddr.raw_value(),
267+
irq
268+
);
269+
if let Some(id) = id {
270+
device_str.push_str(format!(":{}", id).as_str());
271+
}
272+
self.insert_str(&device_str)
273+
}
274+
275+
// Converts a `GuestUsize` to a concise string representation, with multiplier suffixes.
276+
fn guestusize_to_str(size: GuestUsize) -> String {
277+
const KB_MULT: u64 = 1 << 10;
278+
const MB_MULT: u64 = KB_MULT << 10;
279+
const GB_MULT: u64 = MB_MULT << 10;
280+
281+
if size % GB_MULT == 0 {
282+
return format!("{}G", size / GB_MULT);
283+
}
284+
if size % MB_MULT == 0 {
285+
return format!("{}M", size / MB_MULT);
286+
}
287+
if size % KB_MULT == 0 {
288+
return format!("{}K", size / KB_MULT);
289+
}
290+
size.to_string()
291+
}
212292
}
213293

214294
impl Into<Vec<u8>> for Cmdline {
@@ -296,4 +376,42 @@ mod tests {
296376
assert_eq!(cl.insert("c", "da"), Err(Error::TooLarge)); // adds 5 (including space) length
297377
assert!(cl.insert("c", "d").is_ok()); // adds 4 (including space) length
298378
}
379+
380+
#[test]
381+
fn test_add_virtio_mmio_device() {
382+
let mut cl = Cmdline::new(5);
383+
assert_eq!(
384+
cl.add_virtio_mmio_device(0, GuestAddress(0), 0, None),
385+
Err(Error::MmioSize)
386+
);
387+
assert_eq!(
388+
cl.add_virtio_mmio_device(1, GuestAddress(0), 0, None),
389+
Err(Error::TooLarge)
390+
);
391+
392+
let mut cl = Cmdline::new(150);
393+
assert!(cl
394+
.add_virtio_mmio_device(1, GuestAddress(0), 1, None)
395+
.is_ok());
396+
let mut expected_str = "virtio_mmio.device=1@0x0:1".to_string();
397+
assert_eq!(cl.as_str(), &expected_str);
398+
399+
assert!(cl
400+
.add_virtio_mmio_device(2 << 10, GuestAddress(0x100), 2, None)
401+
.is_ok());
402+
expected_str.push_str(" virtio_mmio.device=2K@0x100:2");
403+
assert_eq!(cl.as_str(), &expected_str);
404+
405+
assert!(cl
406+
.add_virtio_mmio_device(3 << 20, GuestAddress(0x1000), 3, None)
407+
.is_ok());
408+
expected_str.push_str(" virtio_mmio.device=3M@0x1000:3");
409+
assert_eq!(cl.as_str(), &expected_str);
410+
411+
assert!(cl
412+
.add_virtio_mmio_device(4 << 30, GuestAddress(0x0001_0000), 4, Some(42))
413+
.is_ok());
414+
expected_str.push_str(" virtio_mmio.device=4G@0x10000:4:42");
415+
assert_eq!(cl.as_str(), &expected_str);
416+
}
299417
}

0 commit comments

Comments
 (0)