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