sftool_lib/
stub_config.rs

1//! Stub/driver config block handling for AXF/ELF files.
2//!
3//! This module focuses only on locating, reading, and overwriting the
4//! fixed-size T_EXT_DRIVER_CFG block inside a file. It does not handle
5//! encrypted ram_patch images (imgtool) or any CLI parsing concerns.
6
7use crate::{Error, Result};
8use std::path::Path;
9
10const MAGIC_FLAG: u32 = 0xABCDDBCA;
11const VERSION_FLAG: u32 = 0xFFFF0003;
12const PIN_CFG_COUNT: usize = 12;
13const FLASH_CFG_COUNT: usize = 12;
14const PMIC_CHANNEL_COUNT: usize = 10;
15
16pub const DRIVER_CONFIG_SIZE: usize = 236;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct StubConfig {
20    pub pins: Vec<PinConfig>,
21    pub flash: Vec<FlashConfig>,
22    pub pmic: Option<PmicConfig>,
23    pub sd0: Option<Sd0Config>,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum PinPort {
28    Pa,
29    Pb,
30    Pbr,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum PinLevel {
35    Low,
36    High,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct PinConfig {
41    pub port: PinPort,
42    pub number: u8,
43    pub level: PinLevel,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum FlashMedia {
48    Nor,
49    Nand,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct FlashConfig {
54    pub media: FlashMedia,
55    pub driver_index: u8,
56    pub manufacturer_id: u8,
57    pub device_type: u8,
58    pub density_id: u8,
59    pub flags: u8,
60    pub capacity_bytes: u32,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum PmicChannel {
65    LvSw1001,
66    LvSw1002,
67    LvSw1003,
68    LvSw1004,
69    LvSw1005,
70    HvSw1501,
71    HvSw1502,
72    Ldo33,
73    Ldo30,
74    Ldo28,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct PmicConfig {
79    pub disabled: bool,
80    pub scl_port: PinPort,
81    pub scl_pin: u8,
82    pub sda_port: PinPort,
83    pub sda_pin: u8,
84    pub channels: Vec<PmicChannel>,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum Sd0Pinmux {
89    ClkPa34OrPa09,
90    ClkPa60OrPa39,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum Sd0InitSequence {
95    EmmcThenSd,
96    SdThenEmmc,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct Sd0Config {
101    pub base_address: u32,
102    pub pinmux: Sd0Pinmux,
103    pub init_sequence: Sd0InitSequence,
104}
105
106/// Scan for the first valid driver config block and return its offset.
107pub fn find_stub_config_offset(data: &[u8]) -> Option<usize> {
108    if data.len() < DRIVER_CONFIG_SIZE {
109        return None;
110    }
111    let last = data.len() - DRIVER_CONFIG_SIZE;
112    for offset in 0..=last {
113        let header = read_u32_le(data, offset)?;
114        if header != MAGIC_FLAG {
115            continue;
116        }
117        let version = read_u32_le(data, offset + 4)?;
118        if version != VERSION_FLAG {
119            continue;
120        }
121        let tail = read_u32_le(data, offset + DRIVER_CONFIG_SIZE - 4)?;
122        if tail == MAGIC_FLAG {
123            return Some(offset);
124        }
125    }
126    None
127}
128
129/// Locate and parse a driver config block from raw bytes.
130pub fn read_stub_config_from_bytes(data: &[u8]) -> Result<StubConfig> {
131    let offset = find_stub_config_offset(data)
132        .ok_or_else(|| Error::invalid_input("driver config block not found"))?;
133    read_stub_config_at(data, offset)
134}
135
136/// Locate and overwrite a driver config block inside a byte buffer.
137pub fn write_stub_config_to_bytes(data: &mut [u8], config: &StubConfig) -> Result<()> {
138    let offset = find_stub_config_offset(data)
139        .ok_or_else(|| Error::invalid_input("driver config block not found"))?;
140    write_stub_config_at(data, offset, config)
141}
142
143/// Clear the config by writing an empty block (masks set to zero).
144pub fn clear_stub_config_in_bytes(data: &mut [u8]) -> Result<()> {
145    let empty = StubConfig {
146        pins: Vec::new(),
147        flash: Vec::new(),
148        pmic: None,
149        sd0: None,
150    };
151    write_stub_config_to_bytes(data, &empty)
152}
153
154/// Read and parse a driver config block from a file.
155pub fn read_stub_config_from_file<P: AsRef<Path>>(path: P) -> Result<StubConfig> {
156    let data = std::fs::read(path)?;
157    read_stub_config_from_bytes(&data)
158}
159
160/// Overwrite a driver config block in a file.
161pub fn write_stub_config_to_file<P: AsRef<Path>>(path: P, config: &StubConfig) -> Result<()> {
162    let path = path.as_ref();
163    let mut data = std::fs::read(path)?;
164    write_stub_config_to_bytes(&mut data, config)?;
165    std::fs::write(path, data)?;
166    Ok(())
167}
168
169/// Clear the config block in a file by writing an empty block.
170pub fn clear_stub_config_in_file<P: AsRef<Path>>(path: P) -> Result<()> {
171    let path = path.as_ref();
172    let mut data = std::fs::read(path)?;
173    clear_stub_config_in_bytes(&mut data)?;
174    std::fs::write(path, data)?;
175    Ok(())
176}
177
178/// Parse a driver config block at the given offset.
179pub fn read_stub_config_at(data: &[u8], offset: usize) -> Result<StubConfig> {
180    if data.len() < offset + DRIVER_CONFIG_SIZE {
181        return Err(Error::invalid_input("buffer too small for driver config"));
182    }
183
184    let pin_mask = read_u16_le(data, offset + 8)
185        .ok_or_else(|| Error::invalid_input("failed to read pin_mask from driver config"))?;
186    let flash_mask = read_u16_le(data, offset + 10)
187        .ok_or_else(|| Error::invalid_input("failed to read flash_mask from driver config"))?;
188    let pmic_mask = read_u8_required(data, offset + 12, "pmic_mask from driver config")?;
189    let sd0_mask = read_u8_required(data, offset + 13, "sd0_mask from driver config")?;
190
191    let mut pins = Vec::new();
192    let pin_base = offset + 16;
193    for index in 0..PIN_CFG_COUNT {
194        if (pin_mask & (1 << index)) == 0 {
195            continue;
196        }
197        let entry_offset = pin_base + index * 4;
198        let port = PinPort::try_from(read_u8_required(data, entry_offset, "pin port")?)?;
199        let number = read_u8_required(data, entry_offset + 1, "pin number")?;
200        let level = PinLevel::try_from(read_u8_required(data, entry_offset + 2, "pin level")?)?;
201        pins.push(PinConfig {
202            port,
203            number,
204            level,
205        });
206    }
207
208    let mut flash = Vec::new();
209    let flash_base = pin_base + PIN_CFG_COUNT * 4;
210    for index in 0..FLASH_CFG_COUNT {
211        if (flash_mask & (1 << index)) == 0 {
212            continue;
213        }
214        let entry_offset = flash_base + index * 12;
215        let media = FlashMedia::try_from(read_u8_required(data, entry_offset, "flash media")?)?;
216        let driver_index = read_u8_required(data, entry_offset + 1, "flash driver index")?;
217        let manufacturer_id = read_u8_required(data, entry_offset + 4, "flash manufacturer_id")?;
218        let device_type = read_u8_required(data, entry_offset + 5, "flash device_type")?;
219        let density_id = read_u8_required(data, entry_offset + 6, "flash density_id")?;
220        let flags = read_u8_required(data, entry_offset + 7, "flash flags")?;
221        let capacity_bytes = read_u32_le(data, entry_offset + 8).ok_or_else(|| {
222            Error::invalid_input("failed to read flash capacity from driver config")
223        })?;
224        flash.push(FlashConfig {
225            media,
226            driver_index,
227            manufacturer_id,
228            device_type,
229            density_id,
230            flags,
231            capacity_bytes,
232        });
233    }
234
235    let pmic_offset = flash_base + FLASH_CFG_COUNT * 12;
236    let pmic = if pmic_mask != 0 {
237        let disabled = read_u8_required(data, pmic_offset, "pmic disabled")? != 0;
238        let scl_port =
239            PinPort::try_from(read_u8_required(data, pmic_offset + 1, "pmic scl_port")?)?;
240        let scl_pin = read_u8_required(data, pmic_offset + 2, "pmic scl_pin")?;
241        let sda_port =
242            PinPort::try_from(read_u8_required(data, pmic_offset + 3, "pmic sda_port")?)?;
243        let sda_pin = read_u8_required(data, pmic_offset + 4, "pmic sda_pin")?;
244        let mut channels = Vec::new();
245        for idx in 0..PMIC_CHANNEL_COUNT {
246            let value = read_u8_required(data, pmic_offset + 6 + idx, "pmic channel")?;
247            if value != 0 {
248                channels.push(PmicChannel::try_from(idx as u8)?);
249            }
250        }
251        Some(PmicConfig {
252            disabled,
253            scl_port,
254            scl_pin,
255            sda_port,
256            sda_pin,
257            channels,
258        })
259    } else {
260        None
261    };
262
263    let sd0_offset = pmic_offset + 16;
264    let sd0 = if sd0_mask != 0 {
265        let base_address = read_u32_le(data, sd0_offset).ok_or_else(|| {
266            Error::invalid_input("failed to read sd0 base address from driver config")
267        })?;
268        let pinmux = Sd0Pinmux::try_from(read_u8_required(data, sd0_offset + 4, "sd0 pinmux")?)?;
269        let init_sequence = Sd0InitSequence::try_from(read_u8_required(
270            data,
271            sd0_offset + 5,
272            "sd0 init sequence",
273        )?)?;
274        Some(Sd0Config {
275            base_address,
276            pinmux,
277            init_sequence,
278        })
279    } else {
280        None
281    };
282
283    Ok(StubConfig {
284        pins,
285        flash,
286        pmic,
287        sd0,
288    })
289}
290
291/// Overwrite a driver config block at the given offset.
292pub fn write_stub_config_at(data: &mut [u8], offset: usize, config: &StubConfig) -> Result<()> {
293    if data.len() < offset + DRIVER_CONFIG_SIZE {
294        return Err(Error::invalid_input("buffer too small for driver config"));
295    }
296
297    if config.pins.len() > PIN_CFG_COUNT {
298        return Err(Error::invalid_input("pin entries exceed 12"));
299    }
300    if config.flash.len() > FLASH_CFG_COUNT {
301        return Err(Error::invalid_input("flash entries exceed 12"));
302    }
303
304    let block = build_stub_config_block(config)?;
305    let target = &mut data[offset..offset + DRIVER_CONFIG_SIZE];
306    target.copy_from_slice(&block);
307    Ok(())
308}
309
310// Build a serialized driver config block with fixed size and masks.
311fn build_stub_config_block(config: &StubConfig) -> Result<Vec<u8>> {
312    let pin_mask: u16 = if config.pins.is_empty() {
313        0
314    } else {
315        (1u16 << config.pins.len()) - 1
316    };
317    let flash_mask: u16 = if config.flash.is_empty() {
318        0
319    } else {
320        (1u16 << config.flash.len()) - 1
321    };
322    let pmic_mask: u8 = if config.pmic.is_some() { 1 } else { 0 };
323    let sd0_mask: u8 = if config.sd0.is_some() { 1 } else { 0 };
324
325    let mut buf = Vec::with_capacity(DRIVER_CONFIG_SIZE);
326    push_u32(&mut buf, MAGIC_FLAG);
327    push_u32(&mut buf, VERSION_FLAG);
328    push_u16(&mut buf, pin_mask);
329    push_u16(&mut buf, flash_mask);
330    push_u8(&mut buf, pmic_mask);
331    push_u8(&mut buf, sd0_mask);
332    push_u8(&mut buf, 0);
333    push_u8(&mut buf, 0);
334
335    for i in 0..PIN_CFG_COUNT {
336        if let Some(entry) = config.pins.get(i) {
337            push_u8(&mut buf, u8::from(entry.port));
338            push_u8(&mut buf, entry.number);
339            push_u8(&mut buf, u8::from(entry.level));
340            push_u8(&mut buf, 0);
341        } else {
342            buf.extend_from_slice(&[0u8; 4]);
343        }
344    }
345
346    for i in 0..FLASH_CFG_COUNT {
347        if let Some(entry) = config.flash.get(i) {
348            push_u8(&mut buf, u8::from(entry.media));
349            push_u8(&mut buf, entry.driver_index);
350            push_u8(&mut buf, 0);
351            push_u8(&mut buf, 0);
352            push_u8(&mut buf, entry.manufacturer_id);
353            push_u8(&mut buf, entry.device_type);
354            push_u8(&mut buf, entry.density_id);
355            push_u8(&mut buf, entry.flags);
356            push_u32(&mut buf, entry.capacity_bytes);
357        } else {
358            buf.extend_from_slice(&[0u8; 12]);
359        }
360    }
361
362    if let Some(pmic) = &config.pmic {
363        push_u8(&mut buf, if pmic.disabled { 1 } else { 0 });
364        push_u8(&mut buf, u8::from(pmic.scl_port));
365        push_u8(&mut buf, pmic.scl_pin);
366        push_u8(&mut buf, u8::from(pmic.sda_port));
367        push_u8(&mut buf, pmic.sda_pin);
368        push_u8(&mut buf, 0);
369
370        let mut channel_bytes = [0u8; PMIC_CHANNEL_COUNT];
371        for channel in &pmic.channels {
372            let index = channel.index();
373            if channel_bytes[index] != 0 {
374                return Err(Error::invalid_input("duplicate PMIC channel"));
375            }
376            channel_bytes[index] = 1;
377        }
378        buf.extend_from_slice(&channel_bytes);
379    } else {
380        buf.extend_from_slice(&[0u8; 16]);
381    }
382
383    if let Some(sd0) = &config.sd0 {
384        push_u32(&mut buf, sd0.base_address);
385        push_u8(&mut buf, u8::from(sd0.pinmux));
386        push_u8(&mut buf, u8::from(sd0.init_sequence));
387        push_u8(&mut buf, 0);
388        push_u8(&mut buf, 0);
389    } else {
390        buf.extend_from_slice(&[0u8; 8]);
391    }
392
393    push_u32(&mut buf, MAGIC_FLAG);
394
395    if buf.len() != DRIVER_CONFIG_SIZE {
396        return Err(Error::invalid_input("driver config block size mismatch"));
397    }
398    Ok(buf)
399}
400
401// Append a u8 to the buffer.
402fn push_u8(buf: &mut Vec<u8>, value: u8) {
403    buf.push(value);
404}
405
406// Append a little-endian u16 to the buffer.
407fn push_u16(buf: &mut Vec<u8>, value: u16) {
408    buf.extend_from_slice(&value.to_le_bytes());
409}
410
411// Append a little-endian u32 to the buffer.
412fn push_u32(buf: &mut Vec<u8>, value: u32) {
413    buf.extend_from_slice(&value.to_le_bytes());
414}
415
416// Read a u8 at the given offset.
417fn read_u8(data: &[u8], offset: usize) -> Option<u8> {
418    data.get(offset).copied()
419}
420
421// Read a u8 and return a labeled error if missing.
422fn read_u8_required(data: &[u8], offset: usize, label: &str) -> Result<u8> {
423    read_u8(data, offset).ok_or_else(|| Error::invalid_input(format!("failed to read {}", label)))
424}
425
426// Read a little-endian u16 at the given offset.
427fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
428    let bytes = data.get(offset..offset + 2)?;
429    Some(u16::from_le_bytes([bytes[0], bytes[1]]))
430}
431
432// Read a little-endian u32 at the given offset.
433fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
434    let bytes = data.get(offset..offset + 4)?;
435    Some(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
436}
437
438impl PinPort {
439    // Convert the port enum to its on-wire value.
440    fn to_u8(self) -> u8 {
441        match self {
442            PinPort::Pa => 0,
443            PinPort::Pb => 1,
444            PinPort::Pbr => 2,
445        }
446    }
447}
448
449impl From<PinPort> for u8 {
450    // Convert the port enum to u8 for serialization.
451    fn from(value: PinPort) -> Self {
452        value.to_u8()
453    }
454}
455
456impl TryFrom<u8> for PinPort {
457    type Error = Error;
458
459    // Parse the port enum from its on-wire value.
460    fn try_from(value: u8) -> Result<Self> {
461        match value {
462            0 => Ok(PinPort::Pa),
463            1 => Ok(PinPort::Pb),
464            2 => Ok(PinPort::Pbr),
465            _ => Err(Error::invalid_input("invalid pin port value")),
466        }
467    }
468}
469
470impl PinLevel {
471    // Convert the level enum to its on-wire value.
472    fn to_u8(self) -> u8 {
473        match self {
474            PinLevel::Low => 0,
475            PinLevel::High => 1,
476        }
477    }
478}
479
480impl From<PinLevel> for u8 {
481    // Convert the level enum to u8 for serialization.
482    fn from(value: PinLevel) -> Self {
483        value.to_u8()
484    }
485}
486
487impl TryFrom<u8> for PinLevel {
488    type Error = Error;
489
490    // Parse the level enum from its on-wire value.
491    fn try_from(value: u8) -> Result<Self> {
492        match value {
493            0 => Ok(PinLevel::Low),
494            1 => Ok(PinLevel::High),
495            _ => Err(Error::invalid_input("invalid pin level value")),
496        }
497    }
498}
499
500impl FlashMedia {
501    // Convert the media enum to its on-wire value.
502    fn to_u8(self) -> u8 {
503        match self {
504            FlashMedia::Nor => 0,
505            FlashMedia::Nand => 1,
506        }
507    }
508}
509
510impl From<FlashMedia> for u8 {
511    // Convert the media enum to u8 for serialization.
512    fn from(value: FlashMedia) -> Self {
513        value.to_u8()
514    }
515}
516
517impl TryFrom<u8> for FlashMedia {
518    type Error = Error;
519
520    // Parse the media enum from its on-wire value.
521    fn try_from(value: u8) -> Result<Self> {
522        match value {
523            0 => Ok(FlashMedia::Nor),
524            1 => Ok(FlashMedia::Nand),
525            _ => Err(Error::invalid_input("invalid flash media value")),
526        }
527    }
528}
529
530impl PmicChannel {
531    // Map a channel to its fixed array index.
532    fn index(self) -> usize {
533        match self {
534            PmicChannel::LvSw1001 => 0,
535            PmicChannel::LvSw1002 => 1,
536            PmicChannel::LvSw1003 => 2,
537            PmicChannel::LvSw1004 => 3,
538            PmicChannel::LvSw1005 => 4,
539            PmicChannel::HvSw1501 => 5,
540            PmicChannel::HvSw1502 => 6,
541            PmicChannel::Ldo33 => 7,
542            PmicChannel::Ldo30 => 8,
543            PmicChannel::Ldo28 => 9,
544        }
545    }
546}
547
548impl TryFrom<u8> for PmicChannel {
549    type Error = Error;
550
551    // Parse the channel from its index.
552    fn try_from(value: u8) -> Result<Self> {
553        match value {
554            0 => Ok(PmicChannel::LvSw1001),
555            1 => Ok(PmicChannel::LvSw1002),
556            2 => Ok(PmicChannel::LvSw1003),
557            3 => Ok(PmicChannel::LvSw1004),
558            4 => Ok(PmicChannel::LvSw1005),
559            5 => Ok(PmicChannel::HvSw1501),
560            6 => Ok(PmicChannel::HvSw1502),
561            7 => Ok(PmicChannel::Ldo33),
562            8 => Ok(PmicChannel::Ldo30),
563            9 => Ok(PmicChannel::Ldo28),
564            _ => Err(Error::invalid_input("invalid PMIC channel index")),
565        }
566    }
567}
568
569impl Sd0Pinmux {
570    // Convert the pinmux enum to its on-wire value.
571    fn to_u8(self) -> u8 {
572        match self {
573            Sd0Pinmux::ClkPa34OrPa09 => 0,
574            Sd0Pinmux::ClkPa60OrPa39 => 1,
575        }
576    }
577}
578
579impl From<Sd0Pinmux> for u8 {
580    // Convert the pinmux enum to u8 for serialization.
581    fn from(value: Sd0Pinmux) -> Self {
582        value.to_u8()
583    }
584}
585
586impl TryFrom<u8> for Sd0Pinmux {
587    type Error = Error;
588
589    // Parse the pinmux enum from its on-wire value.
590    fn try_from(value: u8) -> Result<Self> {
591        match value {
592            0 => Ok(Sd0Pinmux::ClkPa34OrPa09),
593            1 => Ok(Sd0Pinmux::ClkPa60OrPa39),
594            _ => Err(Error::invalid_input("invalid SD0 pinmux value")),
595        }
596    }
597}
598
599impl Sd0InitSequence {
600    // Convert the init sequence enum to its on-wire value.
601    fn to_u8(self) -> u8 {
602        match self {
603            Sd0InitSequence::EmmcThenSd => 0,
604            Sd0InitSequence::SdThenEmmc => 1,
605        }
606    }
607}
608
609impl From<Sd0InitSequence> for u8 {
610    // Convert the init sequence enum to u8 for serialization.
611    fn from(value: Sd0InitSequence) -> Self {
612        value.to_u8()
613    }
614}
615
616impl TryFrom<u8> for Sd0InitSequence {
617    type Error = Error;
618
619    // Parse the init sequence enum from its on-wire value.
620    fn try_from(value: u8) -> Result<Self> {
621        match value {
622            0 => Ok(Sd0InitSequence::EmmcThenSd),
623            1 => Ok(Sd0InitSequence::SdThenEmmc),
624            _ => Err(Error::invalid_input("invalid SD0 init sequence value")),
625        }
626    }
627}