sim_lib_auto_core/
flash.rs1use sim_kernel::{Error, Expr, Result, Symbol};
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct StockMapBackup {
8 pub ecu: String,
10 pub content_key: String,
12 pub bytes: Vec<u8>,
14}
15
16impl StockMapBackup {
17 pub fn new(ecu: impl Into<String>, bytes: Vec<u8>) -> Self {
19 let ecu = ecu.into();
20 let content_key = stock_content_key(&ecu, &bytes);
21 Self {
22 ecu,
23 content_key,
24 bytes,
25 }
26 }
27
28 pub fn reversal_artifact(&self) -> Expr {
30 Expr::Map(vec![
31 entry(
32 "kind",
33 Expr::Symbol(Symbol::qualified("auto", "StockMapBackup")),
34 ),
35 string_entry("ecu", &self.ecu),
36 string_entry("content-key", &self.content_key),
37 entry("bytes", Expr::Bytes(self.bytes.clone())),
38 ])
39 }
40}
41
42#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct ModeledFlashSession {
45 ecu: String,
46 stock_bytes: Vec<u8>,
47 current_bytes: Vec<u8>,
48}
49
50impl ModeledFlashSession {
51 pub fn new(ecu: impl Into<String>, stock_bytes: Vec<u8>) -> Self {
53 Self {
54 ecu: ecu.into(),
55 current_bytes: stock_bytes.clone(),
56 stock_bytes,
57 }
58 }
59
60 pub fn read_ecu(&self) -> &[u8] {
62 &self.current_bytes
63 }
64
65 pub fn backup_stock(&self) -> StockMapBackup {
67 StockMapBackup::new(self.ecu.clone(), self.stock_bytes.clone())
68 }
69
70 pub fn flash(&mut self, tuned_bytes: Vec<u8>, backup: &StockMapBackup) -> Result<()> {
72 self.validate_backup(backup)?;
73 self.current_bytes = tuned_bytes;
74 Ok(())
75 }
76
77 pub fn restore(&mut self, backup: &StockMapBackup) -> Result<Vec<u8>> {
79 self.validate_backup(backup)?;
80 self.current_bytes = backup.bytes.clone();
81 Ok(self.current_bytes.clone())
82 }
83
84 fn validate_backup(&self, backup: &StockMapBackup) -> Result<()> {
85 if backup.ecu != self.ecu {
86 return Err(Error::Eval(format!(
87 "stock-map backup for ECU {} cannot restore ECU {}",
88 backup.ecu, self.ecu
89 )));
90 }
91 let expected = stock_content_key(&backup.ecu, &backup.bytes);
92 if backup.content_key != expected {
93 return Err(Error::Eval(
94 "stock-map backup content key does not match bytes".to_owned(),
95 ));
96 }
97 if backup.bytes != self.stock_bytes {
98 return Err(Error::Eval(
99 "stock-map backup bytes do not match modeled stock".to_owned(),
100 ));
101 }
102 Ok(())
103 }
104}
105
106pub fn stock_content_key(ecu: &str, bytes: &[u8]) -> String {
108 let mut hash = 0xcbf29ce484222325u64;
109 for byte in ecu
110 .as_bytes()
111 .iter()
112 .copied()
113 .chain(std::iter::once(0xff))
114 .chain(bytes.iter().copied())
115 {
116 hash ^= u64::from(byte);
117 hash = hash.wrapping_mul(0x100000001b3);
118 }
119 format!("auto-stock-fnv1a64-{hash:016x}")
120}
121
122fn string_entry(name: &str, value: &str) -> (Expr, Expr) {
123 entry(name, Expr::String(value.to_owned()))
124}
125
126fn entry(name: &str, value: Expr) -> (Expr, Expr) {
127 (Expr::Symbol(Symbol::new(name.to_owned())), value)
128}