linux_bzimage_builder/lib.rs
1// SPDX-License-Identifier: MPL-2.0
2
3//! The linux bzImage builder.
4//!
5//! This crate is responsible for building the bzImage. It contains methods to build
6//! the setup binary (with source provided in another crate) and methods to build the
7//! bzImage from the setup binary and the kernel ELF.
8//!
9//! We should build the asterinas kernel as an ELF file, and feed it to the builder to
10//! generate the bzImage. The builder will generate the PE/COFF header for the setup
11//! code and concatenate it to the ELF file to make the bzImage.
12//!
13//! The setup code should be built into the ELF target and we convert it to a flat binary
14//! in the builder.
15
16pub mod encoder;
17mod mapping;
18mod pe_header;
19
20use std::{
21 fs::File,
22 io::{Read, Seek, SeekFrom, Write},
23 path::Path,
24};
25
26use align_ext::AlignExt;
27pub use encoder::{encode_kernel, PayloadEncoding};
28use mapping::{SetupFileOffset, SetupVA};
29use xmas_elf::program::SegmentData;
30
31/// The type of the bzImage that we are building through `make_bzimage`.
32///
33/// Currently, Legacy32 and Efi64 are mutually exclusive.
34pub enum BzImageType {
35 Legacy32,
36 Efi64,
37}
38
39/// Making a bzImage given the kernel ELF and setup source.
40///
41/// Explanations for the arguments:
42/// - `target_image_path`: The path to the target bzImage;
43/// - `image_type`: The type of the bzImage that we are building;
44/// - `setup_elf_path`: The path to the setup ELF;
45///
46pub fn make_bzimage(target_image_path: &Path, image_type: BzImageType, setup_elf_path: &Path) {
47 let mut setup_elf = Vec::new();
48 File::open(setup_elf_path)
49 .unwrap()
50 .read_to_end(&mut setup_elf)
51 .unwrap();
52 let mut setup = to_flat_binary(&setup_elf);
53 // Align the flat binary to `SECTION_ALIGNMENT`.
54 setup.resize(setup.len().align_up(pe_header::SECTION_ALIGNMENT), 0x00);
55
56 let mut kernel_image = File::create(target_image_path).unwrap();
57 kernel_image.write_all(&setup).unwrap();
58
59 if matches!(image_type, BzImageType::Efi64) {
60 // Write the PE/COFF header to the start of the file.
61 // Since the Linux boot header starts at 0x1f1, we can write the PE/COFF header directly to the
62 // start of the file without overwriting the Linux boot header.
63 let pe_header = pe_header::make_pe_coff_header(&setup_elf);
64 assert!(pe_header.len() <= 0x1f1, "PE/COFF header is too large");
65
66 kernel_image.seek(SeekFrom::Start(0)).unwrap();
67 kernel_image.write_all(&pe_header).unwrap();
68 }
69}
70
71/// To build the legacy32 bzImage setup header, the OSDK should use this target.
72pub fn legacy32_rust_target_json() -> &'static str {
73 include_str!("x86_64-i386_pm-none.json")
74}
75
76/// We need a flat binary which satisfies PA delta == File offset delta,
77/// and objcopy does not satisfy us well, so we should parse the ELF and
78/// do our own objcopy job.
79///
80/// Interestingly, the resulting binary should be the same as the memory
81/// dump of the kernel setup header when it's loaded by the bootloader.
82fn to_flat_binary(elf_file: &[u8]) -> Vec<u8> {
83 let elf = xmas_elf::ElfFile::new(elf_file).unwrap();
84 let mut bin = Vec::<u8>::new();
85
86 for program in elf.program_iter() {
87 if program.get_type().unwrap() == xmas_elf::program::Type::Load {
88 let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else {
89 panic!("Unexpected segment data type");
90 };
91 let dst_file_offset = usize::from(SetupFileOffset::from(SetupVA::from(
92 program.virtual_addr() as usize,
93 )));
94
95 // Note that `mem_size` can be greater than `file_size`. The remaining part must be
96 // filled with zeros.
97 let mem_length = program.mem_size() as usize;
98 if bin.len() < dst_file_offset + mem_length {
99 bin.resize(dst_file_offset + mem_length, 0);
100 }
101
102 // Copy the bytes in the `file_size` part.
103 let file_length = program.file_size() as usize;
104 let dest_slice = bin[dst_file_offset..dst_file_offset + file_length].as_mut();
105 dest_slice.copy_from_slice(header_data);
106 }
107 }
108
109 bin
110}