mcumgr_toolkit/mcuboot/image/
mod.rs1use std::io;
2
3#[derive(Debug, Clone, Copy, Eq, PartialEq)]
5pub struct ImageVersion {
6 pub major: u8,
8 pub minor: u8,
10 pub revision: u16,
12 pub build_num: u32,
14}
15impl std::fmt::Display for ImageVersion {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "{}.{}.{}", self.major, self.minor, self.revision)?;
18 if self.build_num != 0 {
19 write!(f, ".{}", self.build_num)?;
20 }
21 Ok(())
22 }
23}
24
25#[derive(Debug, Clone, Copy, Eq, PartialEq)]
27pub struct ImageInfo {
28 pub version: ImageVersion,
30 pub hash: [u8; SHA256_LEN],
36}
37
38#[derive(thiserror::Error, Debug, miette::Diagnostic)]
40pub enum ImageParseError {
41 #[error("Image is not an MCUboot image")]
43 #[diagnostic(code(mcumgr_toolkit::mcuboot::image::unknown_type))]
44 UnknownImageType,
45 #[error("Image does not contain TLV entries")]
47 #[diagnostic(code(mcumgr_toolkit::mcuboot::image::tlv_missing))]
48 TlvMissing,
49 #[error("Image does not contain an SHA256 id hash")]
51 #[diagnostic(code(mcumgr_toolkit::mcuboot::image::id_hash_missing))]
52 IdHashMissing,
53 #[error("Image read failed")]
55 #[diagnostic(code(mcumgr_toolkit::mcuboot::image::read))]
56 ReadFailed(#[from] std::io::Error),
57}
58
59fn read_u32(data: &mut dyn std::io::Read) -> Result<u32, std::io::Error> {
60 let mut bytes = [0u8; 4];
61 data.read_exact(&mut bytes)?;
62 Ok(u32::from_le_bytes(bytes))
63}
64
65fn read_u16(data: &mut dyn std::io::Read) -> Result<u16, std::io::Error> {
66 let mut bytes = [0u8; 2];
67 data.read_exact(&mut bytes)?;
68 Ok(u16::from_le_bytes(bytes))
69}
70
71fn read_u8(data: &mut dyn std::io::Read) -> Result<u8, std::io::Error> {
72 let mut byte = 0u8;
73 data.read_exact(std::slice::from_mut(&mut byte))?;
74 Ok(byte)
75}
76
77const IMAGE_MAGIC: u32 = 0x96f3b83d;
79const IMAGE_TLV_INFO_MAGIC: u16 = 0x6907;
80const IMAGE_TLV_SHA256: u8 = 0x10;
81const SHA256_LEN: usize = 32;
82const TLV_INFO_HEADER_SIZE: u32 = 4;
83const TLV_ELEMENT_HEADER_SIZE: u32 = 4;
84
85pub fn get_image_info(
87 mut image_data: impl io::Read + io::Seek,
88) -> Result<ImageInfo, ImageParseError> {
89 let image_data = &mut image_data;
90
91 let ih_magic = read_u32(image_data)?;
92 log::debug!("ih_magic: 0x{ih_magic:08x}");
93 if ih_magic != IMAGE_MAGIC {
94 return Err(ImageParseError::UnknownImageType);
95 }
96
97 let ih_load_addr = read_u32(image_data)?;
98 log::debug!("ih_load_addr: 0x{ih_load_addr:08x}");
99
100 let ih_hdr_size = read_u16(image_data)?;
101 log::debug!("ih_hdr_size: 0x{ih_hdr_size:04x}");
102
103 let ih_protect_tlv_size = read_u16(image_data)?;
104 log::debug!("ih_protect_tlv_size: 0x{ih_protect_tlv_size:04x}");
105
106 let ih_img_size = read_u32(image_data)?;
107 log::debug!("ih_img_size: 0x{ih_img_size:08x}");
108
109 let ih_flags = read_u32(image_data)?;
110 log::debug!("ih_flags: 0x{ih_flags:08x}");
111
112 let ih_ver = ImageVersion {
113 major: read_u8(image_data)?,
114 minor: read_u8(image_data)?,
115 revision: read_u16(image_data)?,
116 build_num: read_u32(image_data)?,
117 };
118 log::debug!("ih_ver: {ih_ver:?}");
119
120 image_data.seek(io::SeekFrom::Start(
121 u64::from(ih_hdr_size) + u64::from(ih_protect_tlv_size) + u64::from(ih_img_size),
122 ))?;
123
124 let it_magic = read_u16(image_data)?;
125 log::debug!("it_magic: 0x{it_magic:04x}");
126 if it_magic != IMAGE_TLV_INFO_MAGIC {
127 return Err(ImageParseError::TlvMissing);
128 }
129
130 let it_tlv_tot = read_u16(image_data)?;
131 log::debug!("it_tlv_tot: 0x{it_tlv_tot:04x}");
132
133 let mut id_hash = None;
134 {
135 let mut tlv_read: u32 = 0;
136 while tlv_read + TLV_INFO_HEADER_SIZE + TLV_ELEMENT_HEADER_SIZE <= u32::from(it_tlv_tot) {
138 let it_type = read_u8(image_data)?;
139 read_u8(image_data)?;
140 let it_len = read_u16(image_data)?;
141
142 if it_type == IMAGE_TLV_SHA256 && usize::from(it_len) == SHA256_LEN {
143 let mut sha256_hash = [0u8; SHA256_LEN];
144 image_data.read_exact(&mut sha256_hash)?;
145 id_hash = Some(sha256_hash);
146 } else {
147 image_data.seek_relative(it_len.into())?;
148 }
149
150 log::debug!("- it_type: 0x{it_type:02x}, it_len: 0x{it_len:02x}");
151 tlv_read += u32::from(it_len) + 4;
152 }
153 }
154
155 if let Some(id_hash) = id_hash {
156 Ok(ImageInfo {
157 version: ih_ver,
158 hash: id_hash,
159 })
160 } else {
161 Err(ImageParseError::IdHashMissing)
162 }
163}