1use nom::{
9 bytes::complete::take,
10 combinator::{all_consuming, map, map_res},
11 multi::{count, length_count},
12 number::complete::{le_u16, le_u32, le_u8},
13 sequence::tuple,
14 Finish, IResult,
15};
16use std::io::{BufReader, Read};
17use std::os::unix::fs::FileExt;
18use thiserror::Error;
19use uuid::{uuid, Uuid};
20
21const PKG_UUID_1_0_X: Uuid = uuid!("f018878c-cb7d-4943-9800-a02f059aca02");
22const PKG_UUID_1_1_X: Uuid = uuid!("1244d264-8d7d-4718-a030-fc8a56587d5a");
23
24use crate::{
25 parse_string, parse_string_adjacent, ComponentClassification, Descriptor,
26 DescriptorString, DeviceIdentifiers,
27};
28
29type VResult<I, O> = IResult<I, O>;
30
31#[derive(Error, Debug)]
32pub enum PldmPackageError {
33 #[error("IO error: {0}")]
34 Io(#[from] std::io::Error),
35 #[error("PLDM package format error: {0}")]
38 Format(String),
39}
40
41impl PldmPackageError {
42 fn new_format(s: &str) -> Self {
43 Self::Format(s.into())
44 }
45}
46
47type Result<T> = std::result::Result<T, PldmPackageError>;
48
49#[derive(Debug)]
50pub struct ComponentBitmap {
51 n_bits: usize,
52 bits: Vec<u8>,
53}
54
55impl<'a> ComponentBitmap {
56 pub fn parse(
57 component_bits: u16,
58 ) -> impl FnMut(&'a [u8]) -> VResult<&'a [u8], Self> {
59 let bytes = (component_bits + 7) / 8;
60 map(take(bytes), move |b: &[u8]| ComponentBitmap {
61 n_bits: component_bits as usize,
62 bits: b.to_vec(),
63 })
64 }
65
66 pub fn bit(&self, i: usize) -> bool {
67 let idx = i / 8;
68 let offt = i % 8;
69 self.bits[idx] & (1 << offt) != 0
70 }
71
72 pub fn as_index_str(&self) -> String {
73 let mut s = String::new();
74 let mut first = true;
75 for i in 0usize..self.n_bits {
76 if self.bit(i) {
77 s.push_str(&format!("{}{}", if first { "" } else { ", " }, i));
78 first = false;
79 }
80 }
81 s
82 }
83
84 pub fn as_index_vec(&self) -> Vec<usize> {
85 let mut v = Vec::new();
86 for i in 0usize..self.n_bits {
87 if self.bit(i) {
88 v.push(i)
89 }
90 }
91 v
92 }
93}
94
95#[derive(Debug)]
96pub struct PackageDevice {
97 pub ids: DeviceIdentifiers,
98 pub option_flags: u32,
99 pub version: DescriptorString,
100 pub components: ComponentBitmap,
101}
102
103impl PackageDevice {
104 pub fn parse(buf: &[u8], component_bits: u16) -> VResult<&[u8], Self> {
105 let (
106 r,
107 (len, desc_count, flags, set_ver_type, set_ver_len, pkg_data_len),
108 ) = tuple((le_u16, le_u8, le_u32, le_u8, le_u8, le_u16))(buf)?;
109
110 let (rest, r) = take(len - 11)(r)?;
112
113 let (r, components) = ComponentBitmap::parse(component_bits)(r)?;
114 let (r, set_ver) = parse_string(set_ver_type, set_ver_len)(r)?;
115 let (r, ids) = count(Descriptor::parse, desc_count as usize)(r)?;
116 let (_, _pkg_data) = all_consuming(take(pkg_data_len))(r)?;
117
118 let pkgdev = PackageDevice {
119 ids: DeviceIdentifiers { ids },
120 option_flags: flags,
121 version: set_ver,
122 components,
123 };
124
125 Ok((rest, pkgdev))
126 }
127}
128
129#[derive(Debug)]
130pub struct PackageComponent {
131 pub classification: ComponentClassification,
132 pub identifier: u16,
133 pub comparison_stamp: u32,
134 pub options: u16,
135 pub activation_method: u16,
136 pub file_offset: usize,
137 pub file_size: usize,
138 pub version: DescriptorString,
139}
140
141impl PackageComponent {
142 pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
143 let (
144 r,
145 (
146 classification,
147 identifier,
148 comparison_stamp,
149 options,
150 activation_method,
151 file_offset,
152 file_size,
153 version,
154 ),
155 ) = tuple((
156 le_u16,
157 le_u16,
158 le_u32,
159 le_u16,
160 le_u16,
161 le_u32,
162 le_u32,
163 parse_string_adjacent,
164 ))(buf)?;
165
166 let c = PackageComponent {
167 classification: classification.into(),
168 identifier,
169 comparison_stamp,
170 options,
171 activation_method,
172 file_offset: file_offset as usize,
173 file_size: file_size as usize,
174 version,
175 };
176 Ok((r, c))
177 }
178}
179
180#[derive(Debug)]
181pub struct Package {
182 pub identifier: Uuid,
183 pub version: DescriptorString,
184 pub devices: Vec<PackageDevice>,
185 pub components: Vec<PackageComponent>,
186 file: std::fs::File,
187}
188
189impl Package {
190 pub fn parse(file: std::fs::File) -> Result<Self> {
191 const HDR_INIT_SIZE: usize = 16 + 1 + 2;
194
195 let mut reader = BufReader::new(&file);
196 let mut init = [0u8; HDR_INIT_SIZE];
197 reader.read_exact(&mut init)?;
198
199 let (_, (identifier, _hdr_format, hdr_size)) = all_consuming(tuple((
200 map_res(
201 take::<_, _, nom::error::Error<_>>(16usize),
202 Uuid::from_slice,
203 ),
204 le_u8,
205 le_u16,
206 )))(&init)
207 .map_err(|_| PldmPackageError::new_format("can't parse header"))?;
208
209 let mut hdr_usize = hdr_size as usize;
210 if hdr_usize < HDR_INIT_SIZE {
211 return Err(PldmPackageError::new_format("invalid header size"));
212 }
213
214 hdr_usize -= HDR_INIT_SIZE;
215
216 let mut buf = vec![0; hdr_usize];
217 reader.read_exact(&mut buf).map_err(|_| {
218 PldmPackageError::new_format(
219 "reported header size is larger than file",
220 )
221 })?;
222
223 let (r, (_release_date_time, component_bitmap_length, version)) =
224 tuple((take(13usize), le_u16, parse_string_adjacent))(&buf)
225 .finish()
226 .map_err(|_| {
227 PldmPackageError::new_format("can't parse header")
228 })?;
229
230 let f = |d| PackageDevice::parse(d, component_bitmap_length);
231 let (r, devices) = length_count(le_u8, f)(r)
232 .finish()
233 .map_err(|_| PldmPackageError::new_format("can't parse devices"))?;
234
235 let r = match identifier {
239 PKG_UUID_1_0_X => r,
240 PKG_UUID_1_1_X => {
241 let (r, _downstream_devices) =
242 length_count(le_u8, f)(r).finish().map_err(|_| {
243 PldmPackageError::new_format(
244 "can't parse downstream devices",
245 )
246 })?;
247 r
248 }
249 _ => {
250 return Err(PldmPackageError::new_format(&format!(
251 "unknown package UUID {}",
252 identifier
253 )))
254 }
255 };
256
257 let f = |d| PackageComponent::parse(d);
258 let (_, components) =
259 length_count(le_u16, f)(r).finish().map_err(|_| {
260 PldmPackageError::new_format("can't parse components")
261 })?;
262
263 let mut whole_header = Vec::new();
264 whole_header.extend_from_slice(&init);
265 whole_header.extend_from_slice(&buf);
266 let (cs_payload, checksum) =
267 whole_header.split_at(whole_header.len() - 4);
268 let checksum = u32::from_le_bytes(checksum.try_into().unwrap());
270 let crc32 = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
271 let cs_calc = crc32.checksum(cs_payload);
272 if cs_calc != checksum {
273 return Err(PldmPackageError::new_format(
274 "Incorrect header checksum",
275 ));
276 }
277
278 Ok(Package {
279 identifier,
280 version,
281 devices,
282 components,
283 file,
284 })
285 }
286
287 pub fn new_virtual(
288 classification: ComponentClassification,
289 identifier: u16,
290 payload_file: std::fs::File,
291 ) -> Result<Self> {
292 let metadata = payload_file.metadata()?;
293 let payload_len = metadata
294 .len()
295 .try_into()
296 .map_err(|_| PldmPackageError::new_format("invalid file size?"))?;
297
298 let comp = PackageComponent {
299 classification,
300 identifier,
301 comparison_stamp: 0,
302 options: 0,
303 activation_method: 0,
304 file_offset: 0,
305 file_size: payload_len,
306 version: DescriptorString::String("0000".into()),
307 };
308 Ok(Self {
309 identifier: PKG_UUID_1_1_X,
310 version: DescriptorString::String("0000".into()),
311 components: vec![comp],
312 devices: vec![],
313 file: payload_file,
314 })
315 }
316
317 pub fn read_component(
318 &self,
319 component: &PackageComponent,
320 offset: u32,
321 buf: &mut [u8],
322 ) -> Result<usize> {
323 let file_offset = offset as u64 + component.file_offset as u64;
324 Ok(self.file.read_at(buf, file_offset)?)
325 }
326}