Skip to content

Commit fa3ee6d

Browse files
Merge pull request #27 from FrameworkComputer/zephyr-ec-bin-info
framework_lib: Parse zephyr EC binary
2 parents 5e4989f + 8eae1fb commit fa3ee6d

File tree

5 files changed

+129
-25
lines changed

5 files changed

+129
-25
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ see the [Support Matrices](support-matrices.md).
2525
- [x] ESRT table (UEFI and Linux only) (`--esrt`)
2626
- [x] SMBIOS
2727
- [x] Get firmware version from binary file
28-
- [x] MCHP EC on Intel Platform (`--ec-bin`)
29-
- [ ] NPC EC on Framework 16
28+
- [x] Legacy EC (Intel 13th Gen and earlier) (`--ec-bin`)
29+
- [x] Zephyr EC (AMD) (`--ec-bin`)
3030
- [x] CCG5 PD (11th Gen TigerLake) (`--pd-bin`)
3131
- [x] CCG6 PD (12th Gen AlderLake) (`--pd-bin`)
3232
- [x] CCG8 PD (Framework 16) (`--pd-bin`)

framework_lib/src/commandline/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,8 +807,15 @@ fn analyze_ccgx_pd_fw(data: &[u8]) {
807807
}
808808

809809
pub fn analyze_ec_fw(data: &[u8]) {
810-
if let Some(ver) = ec_binary::read_ec_version(data) {
811-
ec_binary::print_ec_version(&ver);
810+
// Readonly firmware
811+
if let Some(ver) = ec_binary::read_ec_version(data, true) {
812+
ec_binary::print_ec_version(&ver, true);
813+
} else {
814+
println!("Failed to read version")
815+
}
816+
// Readwrite firmware
817+
if let Some(ver) = ec_binary::read_ec_version(data, false) {
818+
ec_binary::print_ec_version(&ver, false);
812819
} else {
813820
println!("Failed to read version")
814821
}

framework_lib/src/ec_binary.rs

Lines changed: 118 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ use alloc::string::ToString;
66
const CROS_EC_IMAGE_DATA_COOKIE1: u32 = 0xce778899;
77
const CROS_EC_IMAGE_DATA_COOKIE2: u32 = 0xceaabbdd;
88
// Absolute offset of the version struct inside the entire EC binary
9-
const EC_VERSION_OFFSET: usize = 0x1158;
9+
// Legacy
10+
// const EC_VERSION_OFFSET: usize = 0x1158; // Bootloader?
11+
const EC_RO_VER_OFFSET: usize = 0x2430;
12+
const EC_RW_VER_OFFSET: usize = 0x402f0;
13+
// Zephyr
14+
const EC_RO_VER_OFFSET_ZEPHYR: usize = 0x00180;
15+
const EC_RW_VER_OFFSET_ZEPHYR: usize = 0x40140;
1016
pub const EC_LEN: usize = 0x8_0000;
1117

1218
#[cfg(not(feature = "uefi"))]
@@ -16,6 +22,7 @@ use regex;
1622
#[cfg(feature = "uefi")]
1723
use core::prelude::rust_2021::derive;
1824

25+
// Defined in EC code as `struct image_data` in include/cros_version.h
1926
#[derive(Clone, Copy, Debug)]
2027
#[repr(C, packed)]
2128
struct _ImageVersionData {
@@ -52,8 +59,9 @@ pub struct ImageVersionDetails {
5259
}
5360

5461
/// Print pretty information about the EC version
55-
pub fn print_ec_version(ver: &ImageVersionData) {
62+
pub fn print_ec_version(ver: &ImageVersionData, ro: bool) {
5663
println!("EC");
64+
println!(" Type: {:>20}", if ro { "RO" } else { "RW" });
5765
println!(" Version: {:>20}", ver.version);
5866
println!(" RollbackVer:{:>20}", ver.rollback_version);
5967
println!(" Platform: {:>20}", ver.details.platform);
@@ -102,13 +110,13 @@ fn parse_ec_version(data: &_ImageVersionData) -> Option<ImageVersionData> {
102110
})
103111
}
104112

105-
//#[cfg(not(feature = "uefi"))]
106113
/// Parse the EC version string into its components
107114
///
108115
/// # Examples
109116
///
110117
/// ```
111118
/// use framework_lib::ec_binary::*;
119+
/// // Legacy EC
112120
/// let ver = parse_ec_version_str("hx30_v0.0.1-7a61a89");
113121
/// assert_eq!(ver, Some(ImageVersionDetails {
114122
/// platform: "hx30".to_string(),
@@ -117,16 +125,40 @@ fn parse_ec_version(data: &_ImageVersionData) -> Option<ImageVersionData> {
117125
/// patch: 1,
118126
/// commit: "7a61a89".to_string(),
119127
/// }));
128+
///
129+
/// // Zephyr based EC 2023
130+
/// let ver = parse_ec_version_str("lotus_v3.2.103876-ec:a3a7cb,os:");
131+
/// assert_eq!(ver, Some(ImageVersionDetails {
132+
/// platform: "lotus".to_string(),
133+
/// major: 3,
134+
/// minor: 2,
135+
/// patch: 103876,
136+
/// commit: "a3a7cb".to_string(),
137+
/// }));
138+
///
139+
/// // Zephyr based EC 2024
140+
/// let ver = parse_ec_version_str("lotus-0.0.0-c6c7ac3");
141+
/// assert_eq!(ver, Some(ImageVersionDetails {
142+
/// platform: "lotus".to_string(),
143+
/// major: 0,
144+
/// minor: 0,
145+
/// patch: 0,
146+
/// commit: "c6c7ac3".to_string(),
147+
/// }));
120148
/// ```
121149
#[cfg(not(feature = "uefi"))]
122150
pub fn parse_ec_version_str(version: &str) -> Option<ImageVersionDetails> {
123-
let re = regex::Regex::new(r"([a-z0-9]+)_v([0-9])\.([0-9])\.([0-9])-([0-9a-f]+)").unwrap();
124-
let caps = re.captures(version).unwrap();
151+
debug!("Trying to parse version: {:?}", version);
152+
let re = regex::Regex::new(r"([a-z0-9]+)(_v|-)([0-9])\.([0-9])\.([0-9]+)-(ec:)?([0-9a-f]+)")
153+
.unwrap();
154+
let caps = re.captures(version)?;
125155
let platform = caps.get(1)?.as_str().to_string();
126-
let major = caps.get(2)?.as_str().parse::<u32>().ok()?;
127-
let minor = caps.get(3)?.as_str().parse::<u32>().ok()?;
128-
let patch = caps.get(4)?.as_str().parse::<u32>().ok()?;
129-
let commit = caps.get(5)?.as_str().to_string();
156+
// Skipping second
157+
let major = caps.get(3)?.as_str().parse::<u32>().ok()?;
158+
let minor = caps.get(4)?.as_str().parse::<u32>().ok()?;
159+
let patch = caps.get(5)?.as_str().parse::<u32>().ok()?;
160+
// Skipping sixth
161+
let commit = caps.get(7)?.as_str().to_string();
130162

131163
Some(ImageVersionDetails {
132164
platform,
@@ -138,19 +170,38 @@ pub fn parse_ec_version_str(version: &str) -> Option<ImageVersionDetails> {
138170
}
139171

140172
/// Parse version information from EC FW image buffer
141-
pub fn read_ec_version(data: &[u8]) -> Option<ImageVersionData> {
142-
let v: _ImageVersionData =
143-
unsafe { std::ptr::read(data[EC_VERSION_OFFSET..].as_ptr() as *const _) };
173+
pub fn read_ec_version(data: &[u8], ro: bool) -> Option<ImageVersionData> {
174+
let offset = if ro {
175+
EC_RO_VER_OFFSET
176+
} else {
177+
EC_RW_VER_OFFSET
178+
};
179+
let offset_zephyr = if ro {
180+
EC_RO_VER_OFFSET_ZEPHYR
181+
} else {
182+
EC_RW_VER_OFFSET_ZEPHYR
183+
};
184+
185+
let v: _ImageVersionData = unsafe { std::ptr::read(data[offset..].as_ptr() as *const _) };
144186
if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 {
145-
error!("Failed to find Cookie 1");
146-
return None;
187+
debug!("Failed to find Cookie 1. Found: {:X?}", { v.cookie1 });
188+
} else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 {
189+
debug!("Failed to find Cookie 2. Found: {:X?}", { v.cookie2 });
190+
} else {
191+
return parse_ec_version(&v);
147192
}
148-
if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 {
149-
error!("Failed to find Cookie 2");
150-
return None;
193+
194+
let v: _ImageVersionData =
195+
unsafe { std::ptr::read(data[offset_zephyr..].as_ptr() as *const _) };
196+
if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 {
197+
debug!("Failed to find Cookie 1. Found: {:X?}", { v.cookie1 });
198+
} else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 {
199+
debug!("Failed to find Cookie 2. Found: {:X?}", { v.cookie2 });
200+
} else {
201+
return parse_ec_version(&v);
151202
}
152203

153-
parse_ec_version(&v)
204+
None
154205
}
155206

156207
#[cfg(test)]
@@ -188,11 +239,11 @@ mod tests {
188239
}
189240

190241
#[test]
191-
fn can_parse_binary() {
242+
fn can_parse_adl_ec() {
192243
let mut ec_bin_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
193244
ec_bin_path.push("test_bins/adl-ec-0.0.1.bin");
194245
let data = fs::read(ec_bin_path).unwrap();
195-
let ver = read_ec_version(&data);
246+
let ver = read_ec_version(&data, false);
196247
assert_eq!(
197248
ver,
198249
Some({
@@ -205,10 +256,56 @@ mod tests {
205256
patch: 1,
206257
commit: "7a61a89".to_string(),
207258
},
208-
size: 2868,
259+
size: 136900,
209260
rollback_version: 0,
210261
}
211262
})
212263
);
213264
}
265+
266+
#[test]
267+
fn can_parse_amd_fl13_ec() {
268+
let mut ec_bin_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
269+
ec_bin_path.push("test_bins/amd-fl13-ec-3.05.bin");
270+
let data = fs::read(ec_bin_path).unwrap();
271+
let expected = Some({
272+
ImageVersionData {
273+
version: "azalea_v3.4.113353-ec:b4c1fb,os".to_string(),
274+
details: ImageVersionDetails {
275+
platform: "azalea".to_string(),
276+
major: 3,
277+
minor: 4,
278+
patch: 113353,
279+
commit: "b4c1fb".to_string(),
280+
},
281+
size: 258048,
282+
rollback_version: 0,
283+
}
284+
});
285+
assert_eq!(expected, read_ec_version(&data, false));
286+
assert_eq!(expected, read_ec_version(&data, true));
287+
}
288+
289+
#[test]
290+
fn can_parse_amd_fl16_ec() {
291+
let mut ec_bin_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
292+
ec_bin_path.push("test_bins/amd-fl16-ec-3.03.bin");
293+
let data = fs::read(ec_bin_path).unwrap();
294+
let expected = Some({
295+
ImageVersionData {
296+
version: "lotus_v3.4.113353-ec:b4c1fb,os:".to_string(),
297+
details: ImageVersionDetails {
298+
platform: "lotus".to_string(),
299+
major: 3,
300+
minor: 4,
301+
patch: 113353,
302+
commit: "b4c1fb".to_string(),
303+
},
304+
size: 258048,
305+
rollback_version: 0,
306+
}
307+
});
308+
assert_eq!(expected, read_ec_version(&data, false));
309+
assert_eq!(expected, read_ec_version(&data, true));
310+
}
214311
}
512 KB
Binary file not shown.
512 KB
Binary file not shown.

0 commit comments

Comments
 (0)