Skip to content

Commit 2180261

Browse files
committed
Add builder sub-crate
1 parent 4c393ac commit 2180261

File tree

4 files changed

+304
-0
lines changed

4 files changed

+304
-0
lines changed

builder/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target/
2+
**/*.rs.bk

builder/Cargo.lock

Lines changed: 72 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builder/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "builder"
3+
version = "0.1.0"
4+
authors = ["Philipp Oppermann <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
xmas-elf = "0.6.2"
9+
byteorder = "1.2.7"
10+
args = "2.2.0"
11+
getopts = "0.2.18"

builder/src/main.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use args::Args;
2+
use byteorder::{ByteOrder, LittleEndian};
3+
use std::{
4+
fs::File,
5+
io::{self, Read, Write},
6+
path::Path,
7+
process,
8+
};
9+
10+
const PROGRAM_NAME: &'static str = "builder";
11+
const PROGRAM_DESC: &'static str = "Builds the bootloader crate.";
12+
13+
const BLOCK_SIZE: usize = 512;
14+
type KernelInfoBlock = [u8; BLOCK_SIZE];
15+
16+
fn main() {
17+
let mut args = args();
18+
19+
if help_arg_present() {
20+
println!("{}", args.full_usage());
21+
process::exit(0);
22+
}
23+
24+
if let Err(args_err) = args.parse_from_cli() {
25+
writeln!(io::stderr(), "{}", args_err).expect("Failed to write to stderr");
26+
process::exit(1);
27+
};
28+
29+
// load kernel
30+
31+
let kernel_path: String = args.value_of("kernel").unwrap();
32+
let kernel_path = Path::new(&kernel_path);
33+
let mut kernel_file = match File::open(kernel_path) {
34+
Ok(file) => file,
35+
Err(err) => {
36+
writeln!(io::stderr(), "Failed to open kernel at {:?}: {}", kernel_path, err)
37+
.expect("Failed to write to stderr");
38+
process::exit(1);
39+
}
40+
};
41+
let kernel_size = kernel_file
42+
.metadata()
43+
.map(|m| m.len())
44+
.unwrap_or_else(|err| {
45+
writeln!(io::stderr(), "Failed to read size of kernel: {}", err)
46+
.expect("Failed to write to stderr");
47+
process::exit(1);
48+
});
49+
let kernel_info_block = create_kernel_info_block(kernel_size, None);
50+
51+
// build bootloader
52+
53+
let mut build_args = vec![
54+
"--manifest-path".into(),
55+
"../Cargo.toml".into(),
56+
"--target".into(),
57+
"../x86_64-bootloader.json".into(),
58+
"--release".into(),
59+
];
60+
if args.value_of("no-default-features").unwrap() {
61+
build_args.push("--no-default-features".into());
62+
}
63+
if args.value_of("all-features").unwrap() {
64+
build_args.push("--all-features".into());
65+
}
66+
if let Some(features) = args.optional_value_of("features").unwrap() {
67+
build_args.push("--features".into());
68+
build_args.push(features);
69+
}
70+
71+
let exit_status = run_xbuild(&build_args);
72+
if !exit_status.map(|s| s.success()).unwrap_or(false) {
73+
process::exit(1)
74+
}
75+
76+
let bootloader_elf_path = Path::new("../target/x86_64-bootloader/release/bootloader");
77+
let mut bootloader_elf_bytes = Vec::new();
78+
File::open(bootloader_elf_path)
79+
.and_then(|mut f| f.read_to_end(&mut bootloader_elf_bytes))
80+
.expect("failed to read bootloader ELF file");
81+
82+
// read bootloader section of ELF file
83+
84+
let elf_file = xmas_elf::ElfFile::new(&bootloader_elf_bytes).unwrap();
85+
xmas_elf::header::sanity_check(&elf_file).unwrap();
86+
let bootloader_section = elf_file
87+
.find_section_by_name(".bootloader")
88+
.expect("bootloader must have a .bootloader section");
89+
let bootloader_bytes = bootloader_section.raw_data(&elf_file);
90+
91+
// create output file
92+
93+
let output_file_path = Path::new("../target/x86_64-bootloader/release/bootimage.bin");
94+
95+
let mut output_file = File::create(output_file_path).expect("Failed to create output file");
96+
output_file
97+
.write_all(bootloader_bytes)
98+
.expect("Failed to write bootloader bytes to output file");
99+
output_file
100+
.write_all(&kernel_info_block)
101+
.expect("Failed to write kernel info block to output file");
102+
103+
write_file_to_file(&mut output_file, &mut kernel_file)
104+
.expect("Failed to write kernel to output file");
105+
pad_file(&mut output_file, kernel_size as usize, &[0; 512]).expect("Failed to pad file");
106+
}
107+
108+
fn args() -> Args {
109+
use getopts::Occur;
110+
111+
let mut args = Args::new(PROGRAM_NAME, PROGRAM_DESC);
112+
args.flag("h", "help", "Prints the help message");
113+
args.option(
114+
"",
115+
"kernel",
116+
"Path to the kernel ELF file",
117+
"KERNEL_PATH",
118+
Occur::Req,
119+
None,
120+
);
121+
args.option(
122+
"",
123+
"features",
124+
"Space-separated list of features to activate",
125+
"FEATURES",
126+
Occur::Optional,
127+
None,
128+
);
129+
args.flag("", "all-features", "Activate all available features");
130+
args.flag(
131+
"",
132+
"no-default-features",
133+
"Do not activate the `default` feature",
134+
);
135+
args
136+
}
137+
138+
fn help_arg_present() -> bool {
139+
std::env::args()
140+
.find(|a| a == "--help" || a == "-h")
141+
.is_some()
142+
}
143+
144+
fn run_xbuild(args: &[String]) -> io::Result<process::ExitStatus> {
145+
let mut command = process::Command::new("cargo");
146+
command.arg("xbuild");
147+
command.args(args);
148+
let exit_status = command.status()?;
149+
150+
if !exit_status.success() {
151+
let mut help_command = process::Command::new("cargo");
152+
help_command.arg("xbuild").arg("--help");
153+
help_command.stdout(process::Stdio::null());
154+
help_command.stderr(process::Stdio::null());
155+
if let Ok(help_exit_status) = help_command.status() {
156+
if !help_exit_status.success() {
157+
let mut stderr = io::stderr();
158+
writeln!(
159+
stderr,
160+
"Failed to run `cargo xbuild`. Perhaps it is not installed?"
161+
)?;
162+
writeln!(stderr, "Run `cargo install cargo-xbuild` to install it.")?;
163+
}
164+
}
165+
}
166+
167+
Ok(exit_status)
168+
}
169+
170+
fn create_kernel_info_block(kernel_size: u64, maybe_package_size: Option<u64>) -> KernelInfoBlock {
171+
let kernel_size = if kernel_size <= u64::from(u32::max_value()) {
172+
kernel_size as u32
173+
} else {
174+
panic!("Kernel can't be loaded by BIOS bootloader because is too big")
175+
};
176+
177+
let package_size = if let Some(size) = maybe_package_size {
178+
if size <= u64::from(u32::max_value()) {
179+
size as u32
180+
} else {
181+
panic!("Package can't be loaded by BIOS bootloader because is too big")
182+
}
183+
} else {
184+
0
185+
};
186+
187+
let mut kernel_info_block = [0u8; BLOCK_SIZE];
188+
LittleEndian::write_u32(&mut kernel_info_block[0..4], kernel_size);
189+
LittleEndian::write_u32(&mut kernel_info_block[8..12], package_size);
190+
191+
kernel_info_block
192+
}
193+
194+
fn write_file_to_file(output: &mut File, datafile: &mut File) -> io::Result<()> {
195+
let data_size = datafile.metadata()?.len();
196+
let mut buffer = [0u8; 1024];
197+
let mut acc = 0;
198+
loop {
199+
let (n, interrupted) = match datafile.read(&mut buffer) {
200+
Ok(0) => break,
201+
Ok(n) => (n, false),
202+
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (0, true),
203+
Err(e) => Err(e)?,
204+
};
205+
if !interrupted {
206+
acc += n;
207+
output.write_all(&buffer[..n])?
208+
}
209+
}
210+
211+
assert!(data_size == acc as u64);
212+
213+
Ok(())
214+
}
215+
216+
fn pad_file(output: &mut File, written_size: usize, padding: &[u8]) -> io::Result<()> {
217+
let padding_size = (padding.len() - (written_size % padding.len())) % padding.len();
218+
output.write_all(&padding[..padding_size])
219+
}

0 commit comments

Comments
 (0)