1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Drivers for Realtek controllers
16 
17 use nom::{bytes, combinator, error, multi, number, sequence};
18 
19 /// Realtek firmware file contents
20 pub struct Firmware {
21     version: u32,
22     project_id: u8,
23     patches: Vec<Patch>,
24 }
25 
26 impl Firmware {
27     /// Parse a `*_fw.bin` file
parse(input: &[u8]) -> Result<Self, nom::Err<error::Error<&[u8]>>>28     pub fn parse(input: &[u8]) -> Result<Self, nom::Err<error::Error<&[u8]>>> {
29         let extension_sig = [0x51, 0x04, 0xFD, 0x77];
30 
31         let (_rem, (_tag, fw_version, patch_count, payload)) =
32             combinator::all_consuming(combinator::map_parser(
33                 // ignore the sig suffix
34                 sequence::terminated(
35                     bytes::complete::take(
36                         // underflow will show up as parse failure
37                         input.len().saturating_sub(extension_sig.len()),
38                     ),
39                     bytes::complete::tag(extension_sig.as_slice()),
40                 ),
41                 sequence::tuple((
42                     bytes::complete::tag(b"Realtech"),
43                     // version
44                     number::complete::le_u32,
45                     // patch count
46                     combinator::map(number::complete::le_u16, |c| c as usize),
47                     // everything else except suffix
48                     combinator::rest,
49                 )),
50             ))(input)?;
51 
52         // ignore remaining input, since patch offsets are relative to the complete input
53         let (_rem, (chip_ids, patch_lengths, patch_offsets)) = sequence::tuple((
54             // chip id
55             multi::many_m_n(patch_count, patch_count, number::complete::le_u16),
56             // patch length
57             multi::many_m_n(patch_count, patch_count, number::complete::le_u16),
58             // patch offset
59             multi::many_m_n(patch_count, patch_count, number::complete::le_u32),
60         ))(payload)?;
61 
62         let patches = chip_ids
63             .into_iter()
64             .zip(patch_lengths.into_iter())
65             .zip(patch_offsets.into_iter())
66             .map(|((chip_id, patch_length), patch_offset)| {
67                 combinator::map(
68                     sequence::preceded(
69                         bytes::complete::take(patch_offset),
70                         // ignore trailing 4-byte suffix
71                         sequence::terminated(
72                             // patch including svn version, but not suffix
73                             combinator::consumed(sequence::preceded(
74                                 // patch before svn version or version suffix
75                                 // prefix length underflow will show up as parse failure
76                                 bytes::complete::take(patch_length.saturating_sub(8)),
77                                 // svn version
78                                 number::complete::le_u32,
79                             )),
80                             // dummy suffix, overwritten with firmware version
81                             bytes::complete::take(4_usize),
82                         ),
83                     ),
84                     |(patch_contents_before_version, svn_version): (&[u8], u32)| {
85                         let mut contents = patch_contents_before_version.to_vec();
86                         // replace what would have been the trailing dummy suffix with fw version
87                         contents.extend_from_slice(&fw_version.to_le_bytes());
88 
89                         Patch {
90                             contents,
91                             svn_version,
92                             chip_id,
93                         }
94                     },
95                 )(input)
96                 .map(|(_rem, output)| output)
97             })
98             .collect::<Result<Vec<_>, _>>()?;
99 
100         // look for project id from the end
101         let mut offset = payload.len();
102         let mut project_id: Option<u8> = None;
103         while offset >= 2 {
104             // Won't panic, since offset >= 2
105             let chunk = &payload[offset - 2..offset];
106             let length: usize = chunk[0].into();
107             let opcode = chunk[1];
108             offset -= 2;
109 
110             if opcode == 0xFF {
111                 break;
112             }
113             if length == 0 {
114                 // report what nom likely would have done, if nom was good at parsing backwards
115                 return Err(nom::Err::Error(error::Error::new(
116                     chunk,
117                     error::ErrorKind::Verify,
118                 )));
119             }
120             if opcode == 0 && length == 1 {
121                 project_id = offset
122                     .checked_sub(1)
123                     .and_then(|index| payload.get(index))
124                     .copied();
125                 break;
126             }
127 
128             offset -= length;
129         }
130 
131         match project_id {
132             Some(project_id) => Ok(Firmware {
133                 project_id,
134                 version: fw_version,
135                 patches,
136             }),
137             None => {
138                 // we ran out of file without finding a project id
139                 Err(nom::Err::Error(error::Error::new(
140                     payload,
141                     error::ErrorKind::Eof,
142                 )))
143             }
144         }
145     }
146 
147     /// Patch version
version(&self) -> u32148     pub fn version(&self) -> u32 {
149         self.version
150     }
151 
152     /// Project id
project_id(&self) -> u8153     pub fn project_id(&self) -> u8 {
154         self.project_id
155     }
156 
157     /// Patches
patches(&self) -> &[Patch]158     pub fn patches(&self) -> &[Patch] {
159         &self.patches
160     }
161 }
162 
163 /// Patch in a [Firmware}
164 pub struct Patch {
165     chip_id: u16,
166     contents: Vec<u8>,
167     svn_version: u32,
168 }
169 
170 impl Patch {
171     /// Chip id
chip_id(&self) -> u16172     pub fn chip_id(&self) -> u16 {
173         self.chip_id
174     }
175     /// Contents of the patch, including the 4-byte firmware version suffix
contents(&self) -> &[u8]176     pub fn contents(&self) -> &[u8] {
177         &self.contents
178     }
179     /// SVN version
svn_version(&self) -> u32180     pub fn svn_version(&self) -> u32 {
181         self.svn_version
182     }
183 }
184 
185 #[cfg(test)]
186 mod tests {
187     use super::*;
188     use anyhow::anyhow;
189     use std::{fs, io, path};
190 
191     #[test]
parse_firmware_rtl8723b() -> anyhow::Result<()>192     fn parse_firmware_rtl8723b() -> anyhow::Result<()> {
193         let fw = Firmware::parse(&firmware_contents("rtl8723b_fw_structure.bin")?)
194             .map_err(|e| anyhow!("{:?}", e))?;
195 
196         let fw_version = 0x0E2F9F73;
197         assert_eq!(fw_version, fw.version());
198         assert_eq!(0x0001, fw.project_id());
199         assert_eq!(
200             vec![(0x0001, 0x00002BBF, 22368,), (0x0002, 0x00002BBF, 22496,),],
201             patch_summaries(fw, fw_version)
202         );
203 
204         Ok(())
205     }
206 
207     #[test]
parse_firmware_rtl8761bu() -> anyhow::Result<()>208     fn parse_firmware_rtl8761bu() -> anyhow::Result<()> {
209         let fw = Firmware::parse(&firmware_contents("rtl8761bu_fw_structure.bin")?)
210             .map_err(|e| anyhow!("{:?}", e))?;
211 
212         let fw_version = 0xDFC6D922;
213         assert_eq!(fw_version, fw.version());
214         assert_eq!(0x000E, fw.project_id());
215         assert_eq!(
216             vec![(0x0001, 0x00005060, 14048,), (0x0002, 0xD6D525A4, 30204,),],
217             patch_summaries(fw, fw_version)
218         );
219 
220         Ok(())
221     }
222 
firmware_contents(filename: &str) -> io::Result<Vec<u8>>223     fn firmware_contents(filename: &str) -> io::Result<Vec<u8>> {
224         fs::read(
225             path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
226                 .join("resources/test/firmware/realtek")
227                 .join(filename),
228         )
229     }
230 
231     /// Return a tuple of (chip id, svn version, contents len, contents sha256)
patch_summaries(fw: Firmware, fw_version: u32) -> Vec<(u16, u32, usize)>232     fn patch_summaries(fw: Firmware, fw_version: u32) -> Vec<(u16, u32, usize)> {
233         fw.patches()
234             .iter()
235             .map(|p| {
236                 let contents = p.contents();
237                 let mut dummy_contents = dummy_contents(contents.len());
238                 dummy_contents.extend_from_slice(&p.svn_version().to_le_bytes());
239                 dummy_contents.extend_from_slice(&fw_version.to_le_bytes());
240                 assert_eq!(&dummy_contents, contents);
241                 (p.chip_id(), p.svn_version(), contents.len())
242             })
243             .collect::<Vec<_>>()
244     }
245 
dummy_contents(len: usize) -> Vec<u8>246     fn dummy_contents(len: usize) -> Vec<u8> {
247         let mut vec = (len as u32).to_le_bytes().as_slice().repeat(len / 4 + 1);
248         assert!(vec.len() >= len);
249         // leave room for svn version and firmware version
250         vec.truncate(len - 8);
251         vec
252     }
253 }
254