1use anyhow::{Context, Result, bail, ensure};
6use clap::Args;
7use serde::ser::{Serialize, SerializeStruct, Serializer};
8use std::time::{Duration, Instant};
9
10use crate::app::TransportWrapper;
11use crate::debug::dmi::{Dmi, OpenOcdDmi};
12use crate::io::jtag::{JtagChain, JtagParams, JtagTap};
13use crate::transport::Capability;
14use crate::util::vmem::Word;
15
16pub mod regs {
20
21 pub const STATUS_REG_OFFSET: usize = 0x0;
23 pub const STATUS_ERROR_BIT: u32 = 0;
24 pub const STATUS_CLEAR_IDLE_BIT: u32 = 1;
25
26 pub const CONTROL_REG_OFFSET: usize = 0x4;
28 pub const CONTROL_DONE_BIT: u32 = 0;
29 pub const CONTROL_WRITE_ENA_BIT: u32 = 1;
30 pub const CONTROL_CLEAR_START_BIT: u32 = 2;
31 pub const CONTROL_AUTO_INCR_BIT: u32 = 3;
32 pub const CONTROL_TARGET_IDX_MASK: u32 = 0xff;
33 pub const CONTROL_TARGET_IDX_OFFSET: usize = 8;
34
35 pub const NUM_BKDR_TARGETS_REG_OFFSET: usize = 0x8;
37 pub const MISSION_MODE_SWITCH_DELAY_REG_OFFSET: usize = 0xc;
38 pub const USR_ACCESS_TIMESTAMP_REG_OFFSET: usize = 0x10;
39 pub const TARGET_INFO_0_REG_OFFSET: usize = 0x100;
40 pub const WIDTH_INFO_0_REG_OFFSET: usize = 0x200;
41 pub const DEPTH_INFO_0_REG_OFFSET: usize = 0x300;
42 pub const READ_DATA_0_REG_OFFSET: usize = 0x400;
43 pub const WRITE_DATA_0_REG_OFFSET: usize = 0x500;
44 pub const INDEX_REG_OFFSET: usize = 0x600;
45 pub const HASH_LAST_LOADED_0_REG_OFFSET: usize = 0x700;
46}
47
48pub mod consts {
49 pub const RESET_PULSE_MS: u64 = 50;
51
52 pub const HOLD_TAP_STRAPS_MS: u64 = 50;
54
55 pub const CLEAR_TIMEOUT_SECS: u64 = 5;
57
58 pub const JTAG_DONE_CYCLES: u64 = 10000;
61
62 pub const CW340_MAIN_CLOCK_FREQ_HZ: u64 = 24 * 1000 * 1000; pub const DATA_REGS_PER_WORD: usize = 8; }
70
71use consts::*;
72
73pub fn enter_backdoor_loader(transport: &TransportWrapper) -> Result<()> {
75 transport.capabilities()?.request(Capability::GPIO).ok()?;
76 let pinmux_tap_backdoor = transport.pin_strapping("PINMUX_TAP_FPGA_BACKDOOR")?;
77 let reset = transport.pin_strapping("RESET")?;
78 let trst = transport.optional_pin_strapping("TRST")?;
82
83 log::info!(
84 "Resetting with PINMUX_TAP_FPGA_BACKDOOR (== DFT) strapping applied to enter the backdoor loader"
85 );
86 pinmux_tap_backdoor.apply()?;
87 if let Some(trst) = &trst {
88 log::info!("Asserting TRST strapping");
89 trst.apply()?;
90 }
91 reset.apply()?;
92 std::thread::sleep(Duration::from_millis(RESET_PULSE_MS));
93 reset.remove()?;
96 if let Some(trst) = &trst {
97 log::info!("Deasserting TRST strapping");
98 trst.remove()?;
99 }
100 std::thread::sleep(Duration::from_millis(HOLD_TAP_STRAPS_MS));
101 pinmux_tap_backdoor.remove()?;
102 log::info!("Reset complete, backdoor TAP strapping released");
103 Ok(())
104}
105
106pub struct BackdoorTap<'a> {
111 jtag: Box<dyn JtagChain + 'a>,
112 jtag_speed_khz: u64,
113}
114
115impl BackdoorTap<'_> {
116 pub fn connect(self, enumerate: bool) -> Result<Backdoor> {
118 let openocd = self.jtag.connect(JtagTap::BackdoorTap)?.into_raw()?;
119 Backdoor::new(
120 OpenOcdDmi::new(openocd, "fpga_backdoor.tap")?,
121 self.jtag_speed_khz,
122 enumerate,
123 )
124 }
125}
126
127#[derive(Debug, Args, Clone)]
128pub struct BackdoorParams {
129 #[command(flatten)]
131 pub jtag: JtagParams,
132}
133
134impl BackdoorParams {
135 pub fn create<'a>(&self, transport: &'a TransportWrapper) -> Result<BackdoorTap<'a>> {
136 Ok(BackdoorTap {
137 jtag: self.jtag.create(transport)?,
138 jtag_speed_khz: self.jtag.adapter_speed_khz,
139 })
140 }
141}
142
143#[derive(Debug, Clone, Copy)]
145pub struct BackdoorTargetInfo {
146 pub id: u32,
148 pub width: u32,
150 pub depth: u32,
152}
153
154impl BackdoorTargetInfo {
155 pub fn id_str(&self) -> String {
157 let bytes = self.id.to_be_bytes();
158
159 String::from_utf8_lossy(&bytes).trim_end().to_owned()
160 }
161
162 pub fn id_from_str(id: &str) -> Result<u32> {
164 let mut bytes = [32u8; 4];
165 let src = id.as_bytes();
166 let len = id.len().min(4);
167 bytes[..len].copy_from_slice(&src[..len]);
168
169 Ok(u32::from_be_bytes(bytes))
170 }
171}
172
173impl Serialize for BackdoorTargetInfo {
174 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
175 where
176 S: Serializer,
177 {
178 let mut s = serializer.serialize_struct("BackdoorTargetInfo", 4)?;
179 s.serialize_field("id", &self.id)?;
180 s.serialize_field("id_str", &self.id_str())?;
181 s.serialize_field("width", &self.width)?;
182 s.serialize_field("depth", &self.depth)?;
183 s.end()
184 }
185}
186
187impl std::fmt::Display for BackdoorTargetInfo {
188 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189 write!(f, "{} {} x {}", self.id_str(), self.width, self.depth)
190 }
191}
192
193impl Word {
194 fn to_u32_chunks(&self) -> Result<[u32; DATA_REGS_PER_WORD]> {
196 ensure!(
197 self.bytes.len() <= DATA_REGS_PER_WORD * 4,
198 "Word '{}' with {} bytes will not fit into {} 32-bit registers.",
199 hex::encode(self.bytes.clone()),
200 self.bytes.len(),
201 DATA_REGS_PER_WORD
202 );
203 let mut chunks = [0u32; DATA_REGS_PER_WORD];
204
205 for (i, &b) in self.bytes.iter().rev().enumerate() {
208 let chunk_idx = i / 4;
210 let byte_pos = i % 4;
211 chunks[chunk_idx] |= (b as u32) << (byte_pos * 8);
212 }
213
214 Ok(chunks)
215 }
216
217 fn from_u32_chunks(chunks: &[u32; DATA_REGS_PER_WORD], bytes_per_word: usize) -> Self {
219 let num_chunks = bytes_per_word.div_ceil(size_of::<u32>());
220 let padding_bytes = (num_chunks * size_of::<u32>()) - bytes_per_word;
221
222 Self {
223 bytes: chunks
224 .iter()
225 .take(num_chunks)
226 .rev()
227 .flat_map(|chunk| chunk.to_be_bytes())
228 .skip(padding_bytes)
229 .collect(),
230 }
231 }
232}
233
234pub struct BackdoorTarget<'a> {
236 backdoor: &'a mut Backdoor,
237 index: u8,
238 pub info: BackdoorTargetInfo,
240}
241
242impl<'a> BackdoorTarget<'a> {
243 pub fn write(
250 &mut self,
251 start: u32,
252 words: &[Word],
253 write_all: bool,
254 check_status: bool,
255 ) -> Result<()> {
256 ensure!(
257 start + words.len() as u32 <= self.info.depth,
258 "fpga bkdr_loader write of len {:#x} to word {:#x} of {} is out of bounds (depth: {:#x})",
259 words.len(),
260 start,
261 self.info.id_str(),
262 self.info.depth,
263 );
264 self.backdoor
265 .write_target(self.index, start, words, write_all, check_status)
266 }
267
268 pub fn read(&mut self, start: u32, count: u32, check_status: bool) -> Result<Vec<Word>> {
273 ensure!(
274 start + count <= self.info.depth,
275 "fpga bkdr_loader read of len {:#x} to word {:#x} of {} is out of bounds (depth: {:#x})",
276 count,
277 start,
278 self.info.id_str(),
279 self.info.depth,
280 );
281 self.backdoor
282 .read_target(self.index, start, count, check_status)
283 }
284
285 pub fn write_word(&mut self, index: u32, word: &Word, check_status: bool) -> Result<()> {
288 ensure!(
289 index < self.info.depth,
290 "fpga bkdr_loader write to word {:#x} of {} is out of bounds (depth: {:#x})",
291 index,
292 self.info.id_str(),
293 self.info.depth,
294 );
295 self.backdoor
296 .write_target_word(self.index, index, word, check_status)
297 }
298
299 pub fn read_word(&mut self, index: u32, check_status: bool) -> Result<Word> {
302 ensure!(
303 index < self.info.depth,
304 "fpga bkdr_loader read from word {:#x} of {} is out of bounds (depth: {:#x})",
305 index,
306 self.info.id_str(),
307 self.info.depth,
308 );
309 self.backdoor
310 .read_target_word(self.index, index, check_status)
311 }
312
313 pub fn clear(&mut self, word: &Word, check_status: bool) -> Result<()> {
319 self.backdoor.clear_target(self.index, word, check_status)
320 }
321
322 pub fn read_hash(&mut self) -> Result<u32> {
326 self.backdoor.read_target_hash(self.index)
327 }
328
329 pub fn write_hash(&mut self, hash: u32) -> Result<()> {
331 self.backdoor.write_target_hash(self.index, hash)
332 }
333}
334
335pub struct Backdoor {
337 dmi: OpenOcdDmi,
338 jtag_speed_khz: u64,
339 targets: Vec<BackdoorTargetInfo>,
340}
341
342impl Backdoor {
343 pub fn new(dmi: OpenOcdDmi, jtag_speed_khz: u64, enumerate: bool) -> Result<Self> {
346 let mut fpga_backdoor = Self {
347 dmi,
348 jtag_speed_khz,
349 targets: Vec::new(),
350 };
351 if enumerate {
352 fpga_backdoor.enumerate()?;
353 }
354
355 Ok(fpga_backdoor)
356 }
357
358 fn dmi_read(&mut self, byte_addr: usize) -> Result<u32> {
361 self.dmi.dmi_read(byte_addr as u32 >> 2)
362 }
363
364 fn dmi_write(&mut self, byte_addr: usize, data: u32) -> Result<()> {
367 self.dmi.dmi_write(byte_addr as u32 >> 2, data)
368 }
369
370 pub fn enumerate(&mut self) -> Result<()> {
372 self.targets.clear();
373
374 let num_targets = self
375 .dmi_read(regs::NUM_BKDR_TARGETS_REG_OFFSET)
376 .context("cannot read number of targets")? as usize;
377 log::info!("Number of FPGA bkdr_loader targets: {num_targets:?}");
378 for idx in 0..num_targets {
379 let addr_offset = idx * 4;
380 let target_info = BackdoorTargetInfo {
381 id: self
382 .dmi_read(regs::TARGET_INFO_0_REG_OFFSET + addr_offset)
383 .context("cannot read target info")?,
384 width: self
385 .dmi_read(regs::WIDTH_INFO_0_REG_OFFSET + addr_offset)
386 .context("cannot read width info")?,
387 depth: self
388 .dmi_read(regs::DEPTH_INFO_0_REG_OFFSET + addr_offset)
389 .context("cannot read depth info")?,
390 };
391 self.targets.push(target_info);
392 }
393
394 Ok(())
395 }
396
397 pub fn set_done(mut self) -> Result<()> {
402 log::debug!("Finished using backdoor loader until next reset");
403
404 let jtag_freq_hz: u64 = self.jtag_speed_khz * 1000;
411 let soc_clk_wait_cycles =
412 CW340_MAIN_CLOCK_FREQ_HZ.div_ceil(jtag_freq_hz) * JTAG_DONE_CYCLES;
413 let soc_clk_wait_cycles: u32 = soc_clk_wait_cycles.try_into().unwrap_or_else(|_| {
414 log::warn!(
415 "Configured JTAG speed ({} kHz) may overflow bkdr_loader wait time.",
416 self.jtag_speed_khz
417 );
418 log::warn!("Configuring maximum wait time.");
419 u32::MAX
420 });
421 self.dmi_write(
422 regs::MISSION_MODE_SWITCH_DELAY_REG_OFFSET,
423 soc_clk_wait_cycles,
424 )
425 .context("cannot write FPGA bkdr_loader mission_mode_switch_delay register")?;
426
427 if let Err(e) = self
428 .dmi_write(regs::CONTROL_REG_OFFSET, 0b1 << regs::CONTROL_DONE_BIT)
429 .context("cannot write done to FPGA bkdr_loader control reg")
430 {
431 log::error!("Error received when writing to `CONTROL.DONE`: {:?}", e);
432 log::error!("Trying to continue anyway...");
433 }
434
435 drop(self);
438
439 let done_wait_millis = (JTAG_DONE_CYCLES * 1000).div_ceil(jtag_freq_hz);
443 std::thread::sleep(Duration::from_millis(done_wait_millis));
444
445 Ok(())
446 }
447
448 pub fn targets(&self) -> &[BackdoorTargetInfo] {
450 &self.targets
451 }
452
453 pub fn target_by_id(&mut self, id: u32) -> Option<BackdoorTarget<'_>> {
455 let (index, info) = self.targets.iter().enumerate().find(|&(_, t)| t.id == id)?;
456 let (index, info) = (index as u8, *info);
457
458 Some(BackdoorTarget {
459 backdoor: self,
460 index,
461 info,
462 })
463 }
464
465 pub fn target_by_id_str(&mut self, id: &str) -> Result<Option<BackdoorTarget<'_>>> {
467 let encoded_id = BackdoorTargetInfo::id_from_str(id)?;
468
469 Ok(self.target_by_id(encoded_id))
470 }
471
472 pub fn write_target(
486 &mut self,
487 target_index: u8,
488 start: u32,
489 words: &[Word],
490 write_all: bool,
491 check_status: bool,
492 ) -> Result<()> {
493 ensure!(
494 usize::from(target_index) < self.targets.len(),
495 "Target index {} is out of range for {} targets",
496 target_index,
497 self.targets.len()
498 );
499 let info = self.targets[target_index as usize];
500 let width = info.width as usize;
501 let regs_used = width.div_ceil(u32::BITS as usize);
502 ensure!(
503 regs_used <= DATA_REGS_PER_WORD,
504 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
505 width,
506 regs_used,
507 DATA_REGS_PER_WORD
508 );
509
510 if words.is_empty() {
511 return Ok(());
512 }
513
514 let top_reg_idx = regs_used - 1;
517
518 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
519 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
520 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
521
522 let mut prev_regs = [0u32; DATA_REGS_PER_WORD];
524 let mut first = true;
525
526 let mut writes = vec![
533 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
534 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
535 ];
536
537 for word in words {
538 let regs = word.to_u32_chunks()?;
539 for idx in 0..regs_used {
540 if idx == top_reg_idx || write_all || first || regs[idx] != prev_regs[idx] {
545 let addr_offset = idx * 4;
546 writes.push((
547 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
548 regs[idx],
549 ));
550 prev_regs[idx] = regs[idx];
551 }
552 }
553 first = false;
554 }
555
556 self.dmi
557 .batched_dmi_writes(&writes)
558 .context("failed to perform DMI writes")?;
559
560 if check_status {
561 let end_index = self
565 .dmi_read(regs::INDEX_REG_OFFSET)
566 .context("cannot read back index")?;
567 ensure!(
568 end_index == start + words.len() as u32,
569 "fpga bkdr_loader index is {:#x} after writing {:#x} words at {:#x} of target {} (expected {:#x})",
570 end_index,
571 words.len(),
572 start,
573 info.id_str(),
574 start + words.len() as u32
575 );
576
577 let status = self
578 .dmi_read(regs::STATUS_REG_OFFSET)
579 .context("cannot read status")?;
580 ensure!(
581 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
582 "fpga bkdr_loader reported an error writing to target {}",
583 info.id_str()
584 );
585 }
586
587 Ok(())
588 }
589
590 pub fn write_target_word(
599 &mut self,
600 target_index: u8,
601 index: u32,
602 word: &Word,
603 check_status: bool,
604 ) -> Result<()> {
605 ensure!(
606 usize::from(target_index) < self.targets.len(),
607 "Target index {} is out of range for {} targets",
608 target_index,
609 self.targets.len()
610 );
611 let info = self.targets[target_index as usize];
612 let width = info.width as usize;
613 let regs_used = width.div_ceil(u32::BITS as usize);
614 ensure!(
615 regs_used <= DATA_REGS_PER_WORD,
616 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
617 width,
618 regs_used,
619 DATA_REGS_PER_WORD
620 );
621
622 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
623 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
624
625 let regs = word.to_u32_chunks()?;
628 let writes: Vec<(u32, u32)> =
629 std::iter::once(((regs::CONTROL_REG_OFFSET >> 2) as u32, control))
630 .chain(regs[..regs_used].iter().enumerate().map(|(idx, ®)| {
631 let addr_offset = idx * 4;
632 (
633 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
634 reg,
635 )
636 }))
637 .chain(std::iter::once((
638 (regs::INDEX_REG_OFFSET >> 2) as u32,
639 index,
640 )))
641 .collect();
642
643 self.dmi
644 .batched_dmi_writes(&writes)
645 .context("failed to perform DMI writes")?;
646
647 if check_status {
648 let status = self
649 .dmi_read(regs::STATUS_REG_OFFSET)
650 .context("cannot read status")?;
651 ensure!(
652 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
653 "fpga bkdr_loader reported an error writing to target {}",
654 info.id_str()
655 );
656 }
657
658 Ok(())
659 }
660
661 pub fn read_target(
674 &mut self,
675 target_index: u8,
676 start: u32,
677 count: u32,
678 check_status: bool,
679 ) -> Result<Vec<Word>> {
680 ensure!(
681 usize::from(target_index) < self.targets.len(),
682 "Target index {} is out of range for {} targets",
683 target_index,
684 self.targets.len()
685 );
686 let info = self.targets[target_index as usize];
687 let width = info.width as usize;
688 let bytes_per_word = width.div_ceil(u8::BITS as usize);
689 let regs_used = width.div_ceil(u32::BITS as usize);
690 ensure!(
691 regs_used <= DATA_REGS_PER_WORD,
692 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
693 width,
694 regs_used,
695 DATA_REGS_PER_WORD
696 );
697
698 if count == 0 {
699 return Ok(Vec::new());
700 }
701
702 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
703 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
704 self.dmi
709 .batched_dmi_writes(&[
710 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
711 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
712 ])
713 .context("cannot set up control and index registers")?;
714
715 let addrs: Vec<u32> = (0..count)
719 .flat_map(|_| {
720 (0..regs_used).map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
721 })
722 .collect();
723 let values = self
724 .dmi
725 .batched_dmi_reads(&addrs)
726 .context("cannot read from read_data registers")?;
727
728 let words = values
729 .chunks_exact(regs_used)
730 .map(|chunk| {
731 let mut regs = [0u32; DATA_REGS_PER_WORD];
732 regs[..regs_used].copy_from_slice(chunk);
733 Word::from_u32_chunks(®s, bytes_per_word)
734 })
735 .collect::<Vec<_>>();
736
737 if check_status {
738 let end_index = self
741 .dmi_read(regs::INDEX_REG_OFFSET)
742 .context("cannot read back index")?;
743 ensure!(
744 end_index == start + count,
745 "fpga bkdr_loader index is {:#x} after reading {:#x} words at {:#x} of target {} (expected {:#x})",
746 end_index,
747 count,
748 start,
749 info.id_str(),
750 start + count
751 );
752
753 let status = self
754 .dmi_read(regs::STATUS_REG_OFFSET)
755 .context("cannot read status")?;
756 ensure!(
757 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
758 "fpga bkdr_loader reported an error reading from target {} starting at word {}",
759 info.id_str(),
760 start
761 );
762 }
763
764 Ok(words)
765 }
766
767 pub fn read_target_word(
776 &mut self,
777 target_index: u8,
778 index: u32,
779 check_status: bool,
780 ) -> Result<Word> {
781 ensure!(
782 usize::from(target_index) < self.targets.len(),
783 "Target index {} is out of range for {} targets",
784 target_index,
785 self.targets.len()
786 );
787 let info = self.targets[target_index as usize];
788 let width = info.width as usize;
789 let bytes_per_word = width.div_ceil(u8::BITS as usize);
790 let regs_used = width.div_ceil(u32::BITS as usize);
791 ensure!(
792 regs_used <= DATA_REGS_PER_WORD,
793 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
794 width,
795 regs_used,
796 DATA_REGS_PER_WORD
797 );
798
799 let control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
802 self.dmi
803 .batched_dmi_writes(&[
804 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
805 ((regs::INDEX_REG_OFFSET >> 2) as u32, index),
806 ])
807 .context("cannot set up control and index registers")?;
808
809 if check_status {
810 let status = self
811 .dmi_read(regs::STATUS_REG_OFFSET)
812 .context("cannot read status")?;
813 ensure!(
814 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
815 "fpga bkdr_loader reported an error reading from word idx {} of target {}",
816 index,
817 info.id_str()
818 );
819 }
820
821 let addrs: Vec<u32> = (0..regs_used)
822 .map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
823 .collect();
824 let values = self
825 .dmi
826 .batched_dmi_reads(&addrs)
827 .context("cannot read from read_data registers")?;
828
829 let mut regs = [0u32; DATA_REGS_PER_WORD];
830 regs[..regs_used].copy_from_slice(&values);
831
832 Ok(Word::from_u32_chunks(®s, bytes_per_word))
833 }
834
835 pub fn clear_target(
841 &mut self,
842 target_index: u8,
843 word: &Word,
844 check_status: bool,
845 ) -> Result<()> {
846 ensure!(
847 usize::from(target_index) < self.targets.len(),
848 "Target index {} is out of range for {} targets",
849 target_index,
850 self.targets.len()
851 );
852 let info = self.targets[target_index as usize];
853
854 self.dmi
855 .batched_dmi_writes(
856 &word
857 .to_u32_chunks()?
858 .into_iter()
859 .enumerate()
860 .map(|(idx, reg)| {
861 let addr_offset = idx * 4;
862 (
863 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
864 reg,
865 )
866 })
867 .collect::<Vec<_>>(),
868 )
869 .context("failed to perform DMI writes")?;
870
871 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
872 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
873 control |= 0b1 << regs::CONTROL_CLEAR_START_BIT;
874 self.dmi_write(regs::CONTROL_REG_OFFSET, control)
875 .context("cannot write to control register")?;
876
877 let timeout = Instant::now() + Duration::from_secs(CLEAR_TIMEOUT_SECS);
879 let mut status: u32;
880 loop {
881 status = self
882 .dmi_read(regs::STATUS_REG_OFFSET)
883 .context("cannot read status")?;
884 if status & (0b1 << regs::STATUS_CLEAR_IDLE_BIT) != 0 {
885 break;
886 }
887
888 if Instant::now() > timeout {
889 bail!(
890 "Timed out after {} seconds waiting for {} clear to complete",
891 CLEAR_TIMEOUT_SECS,
892 info.id_str()
893 );
894 }
895 }
896
897 if check_status {
898 ensure!(
899 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
900 "fpga bkdr_loader reported an error writing to target {}",
901 info.id_str()
902 );
903 }
904
905 Ok(())
906 }
907
908 pub fn read_target_hash(&mut self, target_index: u8) -> Result<u32> {
916 ensure!(
917 usize::from(target_index) < self.targets.len(),
918 "Target index {} is out of range for {} targets",
919 target_index,
920 self.targets.len()
921 );
922 self.dmi_read(regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4)
923 .context("cannot read target hash register")
924 }
925
926 pub fn write_target_hash(&mut self, target_index: u8, hash: u32) -> Result<()> {
928 ensure!(
929 usize::from(target_index) < self.targets.len(),
930 "Target index {} is out of range for {} targets",
931 target_index,
932 self.targets.len()
933 );
934 self.dmi_write(
935 regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4,
936 hash,
937 )
938 .context("cannot write target hash register")
939 }
940
941 pub fn read_usr_access_timestamp(&mut self) -> Result<u32> {
946 self.dmi_read(regs::USR_ACCESS_TIMESTAMP_REG_OFFSET)
947 .context("cannot read USR_ACCESS_TIMESTAMP register")
948 }
949}
950
951#[cfg(test)]
952mod tests {
953 use super::*;
954
955 #[test]
956 fn identifer_str_encoding() {
957 let (width, depth) = (1, 1);
958 for (id, id_str) in [
959 (0x4f545020, "OTP"),
960 (0x5352414d, "SRAM"),
961 (0x46493031, "FI01"),
962 ] {
963 assert_eq!(BackdoorTargetInfo { id, width, depth }.id_str(), id_str);
964 assert_eq!(BackdoorTargetInfo::id_from_str(id_str).unwrap(), id);
965 }
966 }
967
968 #[test]
969 fn byte_u32_conversion() {
970 let word = Word::new(vec![
972 0x5a, 0xa5, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xbe, 0xef, 0xca, 0xfe,
973 ]);
974 let mut expected = [0x0; DATA_REGS_PER_WORD];
977 expected[0] = 0xbeefcafe;
978 expected[1] = 0x89abcdef;
979 expected[2] = 0x01234567;
980 expected[3] = 0x00005aa5;
981
982 let chunks = word.to_u32_chunks().unwrap();
983 assert_eq!(chunks, expected);
984 assert_eq!(Word::from_u32_chunks(&chunks, word.bytes.len()), word);
985 }
986}