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