linux_bzimage_builder/
lib.rs1pub 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 encoder::encode_kernel;
27pub use encoder::PayloadEncoding;
28use mapping::{SetupFileOffset, SetupVA};
29use xmas_elf::program::SegmentData;
30
31pub enum BzImageType {
35 Legacy32,
36 Efi64,
37}
38
39pub fn make_bzimage(
49 target_image_path: &Path,
50 image_type: BzImageType,
51 kernel_path: &Path,
52 setup_elf_path: &Path,
53 encoding: PayloadEncoding,
54) {
55 let mut setup_elf = Vec::new();
56 File::open(setup_elf_path)
57 .unwrap()
58 .read_to_end(&mut setup_elf)
59 .unwrap();
60 let mut setup = to_flat_binary(&setup_elf);
61 setup.resize((setup.len() + 7) & !7, 0x00);
63
64 let mut kernel = Vec::new();
65 File::open(kernel_path)
66 .unwrap()
67 .read_to_end(&mut kernel)
68 .unwrap();
69 let payload = match image_type {
70 BzImageType::Legacy32 => kernel,
71 BzImageType::Efi64 => encode_kernel(kernel, encoding),
72 };
73
74 let setup_len = setup.len();
75 let payload_len = payload.len();
76 let payload_offset = SetupFileOffset::from(setup_len);
77 fill_legacy_header_fields(&mut setup, payload_len, setup_len, payload_offset.into());
78
79 let mut kernel_image = File::create(target_image_path).unwrap();
80 kernel_image.write_all(&setup).unwrap();
81 kernel_image.write_all(&payload).unwrap();
82
83 let image_size = setup_len + payload_len;
84
85 if matches!(image_type, BzImageType::Efi64) {
86 let pe_header = pe_header::make_pe_coff_header(&setup_elf, image_size);
90 assert!(
91 pe_header.header_at_zero.len() <= 0x1f1,
92 "PE/COFF header is too large"
93 );
94
95 kernel_image.seek(SeekFrom::Start(0)).unwrap();
96 kernel_image.write_all(&pe_header.header_at_zero).unwrap();
97 kernel_image
98 .seek(SeekFrom::Start(usize::from(pe_header.relocs.0) as u64))
99 .unwrap();
100 kernel_image.write_all(&pe_header.relocs.1).unwrap();
101 }
102}
103
104pub fn legacy32_rust_target_json() -> &'static str {
106 include_str!("x86_64-i386_pm-none.json")
107}
108
109fn to_flat_binary(elf_file: &[u8]) -> Vec<u8> {
116 let elf = xmas_elf::ElfFile::new(elf_file).unwrap();
117 let mut bin = Vec::<u8>::new();
118
119 for program in elf.program_iter() {
120 if program.get_type().unwrap() == xmas_elf::program::Type::Load {
121 let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else {
122 panic!("Unexpected segment data type");
123 };
124 let dst_file_offset = usize::from(SetupFileOffset::from(SetupVA::from(
125 program.virtual_addr() as usize,
126 )));
127 let dst_file_length = program.file_size() as usize;
128 if bin.len() < dst_file_offset + dst_file_length {
129 bin.resize(dst_file_offset + dst_file_length, 0);
130 }
131 let dest_slice = bin[dst_file_offset..dst_file_offset + dst_file_length].as_mut();
132 dest_slice.copy_from_slice(header_data);
133 }
134 }
135
136 bin
137}
138
139fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
145 let size = value.len();
146 assert_eq!(
147 &header[offset..offset + size],
148 vec![0xABu8; size].as_slice(),
149 "The field {:#x} to be filled must be marked with 0xAB",
150 offset
151 );
152 header[offset..offset + size].copy_from_slice(value);
153}
154
155fn fill_legacy_header_fields(
156 header: &mut [u8],
157 kernel_len: usize,
158 setup_len: usize,
159 payload_offset: SetupVA,
160) {
161 fill_header_field(
162 header,
163 0x248, &(usize::from(payload_offset) as u32).to_le_bytes(),
165 );
166
167 fill_header_field(
168 header,
169 0x24C, &(kernel_len as u32).to_le_bytes(),
171 );
172
173 fill_header_field(
174 header,
175 0x260, &((setup_len + kernel_len) as u32).to_le_bytes(),
177 );
178}