|
| 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