1use crate::errors::FSError;
8use bytes::{BufMut, Bytes, BytesMut};
9
10const MIN_FILE_SIZE: usize = 32_usize;
11const MAX_FILE_SIZE: usize = 1_048_576_usize;
12const BOOTOS_BOOT_BLOCK_MAGIC: u32 = 0xFD9B_5B7A_u32;
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct BootSystemFile {
17 header: BootOSBootBlockHeader,
18 data_info: Vec<BootOSDataInfo>,
19}
20
21impl BootSystemFile {
22 #[must_use]
23 pub const fn header(&self) -> &BootOSBootBlockHeader {
24 &self.header
25 }
26 #[must_use]
27 pub const fn data_info(&self) -> &Vec<BootOSDataInfo> {
28 &self.data_info
29 }
30}
31
32impl Default for BootSystemFile {
33 fn default() -> Self {
34 Self {
35 header: BootOSBootBlockHeader {
36 platform_info_physical_address: 0x2000_8000,
37 package_info_physical_address: 0x2000_8800,
38 boot1_flags: 0x8000_0000,
39 sticky_register_bits: 0,
40 },
41 data_info: Vec::with_capacity(0),
42 }
43 }
44}
45
46impl From<BootSystemFile> for Bytes {
47 fn from(value: BootSystemFile) -> Self {
48 let mut buff = BytesMut::with_capacity(32 + (value.data_info.len() * 8));
49
50 buff.put_u32(BOOTOS_BOOT_BLOCK_MAGIC);
51 let actual_len = std::cmp::min(
53 u32::try_from(value.data_info().len()).unwrap_or(131_068),
54 131_068,
55 );
56 buff.put_u32(actual_len);
57 buff.put_u32(value.header().platform_info_physical_address());
58 buff.put_u32(value.header().package_info_physical_address());
59 buff.put_u32(value.header().boot1_flags());
60 buff.put_u32(value.header().sticky_register_bits());
61 buff.put_u32(0x0);
63 buff.put_u32(0x0);
64 for idx in 0..usize::try_from(actual_len).unwrap_or(usize::MAX) {
65 buff.put_u32(value.data_info[idx].address());
66 buff.put_u32(value.data_info[idx].length());
67 }
68
69 buff.freeze()
70 }
71}
72
73impl TryFrom<Bytes> for BootSystemFile {
74 type Error = FSError;
75
76 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
77 if value.len() < 32 {
78 return Err(FSError::TooSmall(MIN_FILE_SIZE, value.len()));
79 }
80 if value.len() > MAX_FILE_SIZE {
81 return Err(FSError::TooLarge(MAX_FILE_SIZE, value.len()));
82 }
83
84 let actual_magic = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
85 if actual_magic != BOOTOS_BOOT_BLOCK_MAGIC {
86 return Err(FSError::InvalidFileMagic(
87 BOOTOS_BOOT_BLOCK_MAGIC,
88 actual_magic,
89 ));
90 }
91
92 let data_info_count = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
93 let expected_file_size =
94 32_usize + (usize::try_from(data_info_count).unwrap_or(usize::MAX) * 8_usize);
95 if value.len() != expected_file_size {
96 return Err(FSError::InvalidFileSize(expected_file_size, value.len()));
97 }
98
99 let platform_info_addr = u32::from_be_bytes([value[8], value[9], value[10], value[11]]);
100 let package_info_addr = u32::from_be_bytes([value[12], value[13], value[14], value[15]]);
101 let boot1_flags = u32::from_be_bytes([value[16], value[17], value[18], value[19]]);
102 let sticky_register_bits = u32::from_be_bytes([value[20], value[21], value[22], value[23]]);
103
104 let mut index = 32_usize;
107 let mut data_infos =
108 Vec::with_capacity(usize::try_from(data_info_count).unwrap_or(usize::MAX));
109 for _ in 0..data_info_count {
110 let address = u32::from_be_bytes([
111 value[index],
112 value[index + 1],
113 value[index + 2],
114 value[index + 3],
115 ]);
116 let length = u32::from_be_bytes([
117 value[index + 4],
118 value[index + 5],
119 value[index + 6],
120 value[index + 7],
121 ]);
122
123 data_infos.push(BootOSDataInfo { address, length });
124 index += 8;
125 }
126
127 Ok(Self {
128 header: BootOSBootBlockHeader {
129 platform_info_physical_address: platform_info_addr,
130 package_info_physical_address: package_info_addr,
131 boot1_flags,
132 sticky_register_bits,
133 },
134 data_info: data_infos,
135 })
136 }
137}
138
139#[derive(Clone, Debug, PartialEq, Eq)]
140pub struct BootOSBootBlockHeader {
141 platform_info_physical_address: u32,
142 package_info_physical_address: u32,
143 boot1_flags: u32,
144 sticky_register_bits: u32,
145}
146impl BootOSBootBlockHeader {
147 #[must_use]
148 pub const fn platform_info_physical_address(&self) -> u32 {
149 self.platform_info_physical_address
150 }
151 #[must_use]
152 pub const fn package_info_physical_address(&self) -> u32 {
153 self.package_info_physical_address
154 }
155 #[must_use]
156 pub const fn boot1_flags(&self) -> u32 {
157 self.boot1_flags
158 }
159 #[must_use]
160 pub const fn sticky_register_bits(&self) -> u32 {
161 self.sticky_register_bits
162 }
163}
164
165#[derive(Clone, Debug, PartialEq, Eq)]
166pub struct BootOSDataInfo {
167 address: u32,
168 length: u32,
169}
170impl BootOSDataInfo {
171 #[must_use]
172 pub const fn address(&self) -> u32 {
173 self.address
174 }
175 #[must_use]
176 pub const fn length(&self) -> u32 {
177 self.length
178 }
179}
180
181#[cfg(test)]
182mod unit_tests {
183 use super::*;
184 use std::path::PathBuf;
185
186 #[test]
187 pub fn can_parse_boot_system_file() {
188 let mut test_data_dir = PathBuf::from(
189 std::env::var("CARGO_MANIFEST_DIR")
190 .expect("Failed to read `CARGO_MANIFEST_DIR` to locate test files!"),
191 );
192 test_data_dir.push("src");
193 test_data_dir.push("fsemul");
194 test_data_dir.push("test-data");
195
196 {
197 let mut bsf_file = test_data_dir.clone();
199 bsf_file.push("ppc.bsf");
200 let file_contents =
201 std::fs::read(&bsf_file).expect("Failed to read real life bsf file!");
202 let file = BootSystemFile::try_from(Bytes::from(file_contents))
203 .expect("Failed to parse real life ppc.bsf");
204
205 assert!(
206 file.data_info().is_empty(),
207 "Expected an empty data info list for Boot System File."
208 );
209
210 assert_eq!(
211 file.header().platform_info_physical_address(),
212 0x20008000,
213 "Invalid Platform Info Physical Address from Boot System File.",
214 );
215 assert_eq!(
216 file.header().package_info_physical_address(),
217 0x20008800,
218 "Invalid Package Info Physical Address from Boot System File.",
219 );
220 assert_eq!(
221 file.header().boot1_flags(),
222 0x80000000,
223 "Invalid Boot1 Flags from Boot System File.",
224 );
225 assert_eq!(
226 file.header().sticky_register_bits(),
227 0,
228 "Invalid Sticky Register Bits for Boot System File."
229 );
230 }
231 }
232}