Skip to content

Commit 682976c

Browse files
bjzhjingrbradford
authored andcommitted
linux-loader: Create linux-loader crate
This crate is derived from upstream project: firecracker project commit 223bdb64d3eb445f55872e63d9ed2518d2235759 The following changes are made after code ported from firecracker: - Return the offset in memory where kernel image loading is finished, add the related logic in load function also, according to upstream project crosvm commit 4133b0120d1e16cafbb373b2ae17a214b594038b - Ported to vm-memory, remove dependency on firecrakcer memory-model - Remove dependency on sys_util, use read_util instead Signed-off-by: Cathy Zhang <cathy.zhang@intel.com>
1 parent cb6ca99 commit 682976c

File tree

9 files changed

+1169
-0
lines changed

9 files changed

+1169
-0
lines changed

Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "linux-loader"
3+
version = "0.1.0"
4+
authors = ["Cathy Zhang <cathy.zhang@intel.com>"]
5+
edition = "2018"
6+
license = "Apache-2.0"
7+
8+
[dependencies.vm-memory]
9+
git = "https://github.com/rust-vmm/vm-memory"
10+
features = ["backend-mmap"]

DESIGN.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ELF Image parsing and loading
2+
3+
The boot process is explained from the following two sides.
4+
5+
## Loader side
6+
7+
It follows ELF standard which is specified in elf.rs.
8+
The entry header and program headers will be inerpreted, and PT_LOAD segments
9+
will be loaded into guest memory.
10+
11+
### Where kernel is loaded
12+
13+
There are two ways on deciding where the program segments will be loaded.
14+
15+
- One way is to provide an option and allow vmm to specify where to load the
16+
image, considering its memory layout.
17+
18+
- The other way is to load image into phdr.p_paddr by default.
19+
20+
## VMM side
21+
22+
### Construct zero page
23+
24+
According to the 64-bit boot protocol, the boot parameters (traditionally known
25+
as "zero page") should be setup, including setup_header, e820 table and other
26+
stuff. However, ELF has no setup_header, nothing returned from ELF loader could
27+
be used to fill boot parameters, vmm is totally responsible for the construction.
28+
29+
### Configure vCPU
30+
31+
- RIP, the start offset of guest memory where kernel is loaded, which is
32+
returned from loader
33+
34+
- 64 bit mode with paging enabled
35+
36+
- GDT must be configured and loaded
37+
38+

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Linux-loader
2+
3+
## Short-description
4+
5+
* Parsing and loading vmlinux (raw ELF image) and bzImage images
6+
* Linux command line parsing and generation
7+
* Definitions and helpers for the Linux boot protocol
8+
9+
## How to build
10+
11+
```
12+
cd linux-loader
13+
cargo build
14+
```
15+
16+
## How to run test
17+
18+
```
19+
cd linux-loader
20+
cargo test
21+
cargo test -- --nocapture
22+
```
23+
24+
## Platform Support
25+
- Arch: x86
26+
- OS: Linux/Unix
27+

src/cmdline/mod.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5+
// Use of this source code is governed by a BSD-style license that can be
6+
// found in the THIRD-PARTY file.
7+
8+
//! Helper for creating valid kernel command line strings.
9+
10+
use std::fmt;
11+
use std::result;
12+
13+
/// The error type for command line building operations.
14+
#[derive(PartialEq, Debug)]
15+
pub enum Error {
16+
/// Operation would have resulted in a non-printable ASCII character.
17+
InvalidAscii,
18+
/// Key/Value Operation would have had a space in it.
19+
HasSpace,
20+
/// Key/Value Operation would have had an equals sign in it.
21+
HasEquals,
22+
/// Operation would have made the command line too large.
23+
TooLarge,
24+
}
25+
26+
impl fmt::Display for Error {
27+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28+
write!(
29+
f,
30+
"{}",
31+
match *self {
32+
Error::InvalidAscii => "string contains non-printable ASCII character",
33+
Error::HasSpace => "string contains a space",
34+
Error::HasEquals => "string contains an equals sign",
35+
Error::TooLarge => "inserting string would make command line too long",
36+
}
37+
)
38+
}
39+
}
40+
41+
/// Specialized Result type for command line operations.
42+
pub type Result<T> = result::Result<T, Error>;
43+
44+
fn valid_char(c: char) -> bool {
45+
match c {
46+
' '...'~' => true,
47+
_ => false,
48+
}
49+
}
50+
51+
fn valid_str(s: &str) -> Result<()> {
52+
if s.chars().all(valid_char) {
53+
Ok(())
54+
} else {
55+
Err(Error::InvalidAscii)
56+
}
57+
}
58+
59+
fn valid_element(s: &str) -> Result<()> {
60+
if !s.chars().all(valid_char) {
61+
Err(Error::InvalidAscii)
62+
} else if s.contains(' ') {
63+
Err(Error::HasSpace)
64+
} else if s.contains('=') {
65+
Err(Error::HasEquals)
66+
} else {
67+
Ok(())
68+
}
69+
}
70+
71+
/// A builder for a kernel command line string that validates the string as its being built. A
72+
/// `CString` can be constructed from this directly using `CString::new`.
73+
#[derive(Clone)]
74+
pub struct Cmdline {
75+
line: String,
76+
capacity: usize,
77+
}
78+
79+
impl Cmdline {
80+
/// Constructs an empty Cmdline with the given capacity, which includes the nul terminator.
81+
/// Capacity must be greater than 0.
82+
pub fn new(capacity: usize) -> Cmdline {
83+
assert_ne!(capacity, 0);
84+
Cmdline {
85+
line: String::with_capacity(capacity),
86+
capacity: capacity,
87+
}
88+
}
89+
90+
fn has_capacity(&self, more: usize) -> Result<()> {
91+
let needs_space = if self.line.is_empty() { 0 } else { 1 };
92+
if self.line.len() + more + needs_space < self.capacity {
93+
Ok(())
94+
} else {
95+
Err(Error::TooLarge)
96+
}
97+
}
98+
99+
fn start_push(&mut self) {
100+
if !self.line.is_empty() {
101+
self.line.push(' ');
102+
}
103+
}
104+
105+
fn end_push(&mut self) {
106+
// This assert is always true because of the `has_capacity` check that each insert method
107+
// uses.
108+
assert!(self.line.len() < self.capacity);
109+
}
110+
111+
/// Validates and inserts a key value pair into this command line
112+
pub fn insert<T: AsRef<str>>(&mut self, key: T, val: T) -> Result<()> {
113+
let k = key.as_ref();
114+
let v = val.as_ref();
115+
116+
valid_element(k)?;
117+
valid_element(v)?;
118+
self.has_capacity(k.len() + v.len() + 1)?;
119+
120+
self.start_push();
121+
self.line.push_str(k);
122+
self.line.push('=');
123+
self.line.push_str(v);
124+
self.end_push();
125+
126+
Ok(())
127+
}
128+
129+
/// Validates and inserts a string to the end of the current command line
130+
pub fn insert_str<T: AsRef<str>>(&mut self, slug: T) -> Result<()> {
131+
let s = slug.as_ref();
132+
valid_str(s)?;
133+
134+
self.has_capacity(s.len())?;
135+
136+
self.start_push();
137+
self.line.push_str(s);
138+
self.end_push();
139+
140+
Ok(())
141+
}
142+
143+
/// Returns the cmdline in progress without nul termination
144+
pub fn as_str(&self) -> &str {
145+
self.line.as_str()
146+
}
147+
}
148+
149+
impl Into<Vec<u8>> for Cmdline {
150+
fn into(self) -> Vec<u8> {
151+
self.line.into_bytes()
152+
}
153+
}
154+
155+
#[cfg(test)]
156+
mod tests {
157+
use super::*;
158+
use std::ffi::CString;
159+
160+
#[test]
161+
fn insert_hello_world() {
162+
let mut cl = Cmdline::new(100);
163+
assert_eq!(cl.as_str(), "");
164+
assert!(cl.insert("hello", "world").is_ok());
165+
assert_eq!(cl.as_str(), "hello=world");
166+
167+
let s = CString::new(cl).expect("failed to create CString from Cmdline");
168+
assert_eq!(s, CString::new("hello=world").unwrap());
169+
}
170+
171+
#[test]
172+
fn insert_multi() {
173+
let mut cl = Cmdline::new(100);
174+
assert!(cl.insert("hello", "world").is_ok());
175+
assert!(cl.insert("foo", "bar").is_ok());
176+
assert_eq!(cl.as_str(), "hello=world foo=bar");
177+
}
178+
179+
#[test]
180+
fn insert_space() {
181+
let mut cl = Cmdline::new(100);
182+
assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace));
183+
assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace));
184+
assert_eq!(cl.insert("a ", "b "), Err(Error::HasSpace));
185+
assert_eq!(cl.insert(" a", "b"), Err(Error::HasSpace));
186+
assert_eq!(cl.as_str(), "");
187+
}
188+
189+
#[test]
190+
fn insert_equals() {
191+
let mut cl = Cmdline::new(100);
192+
assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals));
193+
assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals));
194+
assert_eq!(cl.insert("a=", "b "), Err(Error::HasEquals));
195+
assert_eq!(cl.insert("=a", "b"), Err(Error::HasEquals));
196+
assert_eq!(cl.insert("a", "=b"), Err(Error::HasEquals));
197+
assert_eq!(cl.as_str(), "");
198+
}
199+
200+
#[test]
201+
fn insert_emoji() {
202+
let mut cl = Cmdline::new(100);
203+
assert_eq!(cl.insert("heart", "💖"), Err(Error::InvalidAscii));
204+
assert_eq!(cl.insert("💖", "love"), Err(Error::InvalidAscii));
205+
assert_eq!(cl.as_str(), "");
206+
}
207+
208+
#[test]
209+
fn insert_string() {
210+
let mut cl = Cmdline::new(13);
211+
assert_eq!(cl.as_str(), "");
212+
assert!(cl.insert_str("noapic").is_ok());
213+
assert_eq!(cl.as_str(), "noapic");
214+
assert!(cl.insert_str("nopci").is_ok());
215+
assert_eq!(cl.as_str(), "noapic nopci");
216+
}
217+
218+
#[test]
219+
fn insert_too_large() {
220+
let mut cl = Cmdline::new(4);
221+
assert_eq!(cl.insert("hello", "world"), Err(Error::TooLarge));
222+
assert_eq!(cl.insert("a", "world"), Err(Error::TooLarge));
223+
assert_eq!(cl.insert("hello", "b"), Err(Error::TooLarge));
224+
assert!(cl.insert("a", "b").is_ok());
225+
assert_eq!(cl.insert("a", "b"), Err(Error::TooLarge));
226+
assert_eq!(cl.insert_str("a"), Err(Error::TooLarge));
227+
assert_eq!(cl.as_str(), "a=b");
228+
229+
let mut cl = Cmdline::new(10);
230+
assert!(cl.insert("ab", "ba").is_ok()); // adds 5 length
231+
assert_eq!(cl.insert("c", "da"), Err(Error::TooLarge)); // adds 5 (including space) length
232+
assert!(cl.insert("c", "d").is_ok()); // adds 4 (including space) length
233+
}
234+
}

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) 2019 Intel Corporation. All rights reserved.
2+
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Copyright 2017 The Chromium OS Authors. All rights reserved.
6+
// Use of this source code is governed by a BSD-style license that can be
7+
// found in the LICENSE file.
8+
9+
pub mod cmdline;
10+
pub mod loader;
11+
12+
extern crate vm_memory;
13+

0 commit comments

Comments
 (0)