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_CLEAR_SEGMENT_START_BIT: u32 = 3;
32 pub const CONTROL_AUTO_INCR_BIT: u32 = 4;
33 pub const CONTROL_TARGET_IDX_MASK: u32 = 0xff;
34 pub const CONTROL_TARGET_IDX_OFFSET: usize = 8;
35
36 pub const NUM_BKDR_TARGETS_REG_OFFSET: usize = 0x8;
38 pub const MISSION_MODE_SWITCH_DELAY_REG_OFFSET: usize = 0xc;
39 pub const CLEAR_INDEX_START_REG_OFFSET: usize = 0x10;
40 pub const CLEAR_INDEX_END_REG_OFFSET: usize = 0x14;
41 pub const USR_ACCESS_TIMESTAMP_REG_OFFSET: usize = 0x18;
42 pub const TARGET_INFO_0_REG_OFFSET: usize = 0x100;
43 pub const WIDTH_INFO_0_REG_OFFSET: usize = 0x200;
44 pub const DEPTH_INFO_0_REG_OFFSET: usize = 0x300;
45 pub const READ_DATA_0_REG_OFFSET: usize = 0x400;
46 pub const WRITE_DATA_0_REG_OFFSET: usize = 0x500;
47 pub const INDEX_REG_OFFSET: usize = 0x600;
48 pub const HASH_LAST_LOADED_0_REG_OFFSET: usize = 0x700;
49}
50
51pub mod consts {
52 pub const RESET_PULSE_MS: u64 = 50;
54
55 pub const HOLD_TAP_STRAPS_MS: u64 = 50;
57
58 pub const CLEAR_TIMEOUT_SECS: u64 = 5;
60
61 pub const JTAG_DONE_CYCLES: u64 = 10000;
64
65 pub const CW340_MAIN_CLOCK_FREQ_HZ: u64 = 24 * 1000 * 1000; pub const DATA_REGS_PER_WORD: usize = 8; }
73
74use consts::*;
75
76pub fn enter_backdoor_loader(transport: &TransportWrapper) -> Result<()> {
78 transport.capabilities()?.request(Capability::GPIO).ok()?;
79 let pinmux_tap_backdoor = transport.pin_strapping("PINMUX_TAP_FPGA_BACKDOOR")?;
80 let reset = transport.pin_strapping("RESET")?;
81 let trst = transport.optional_pin_strapping("TRST")?;
85
86 log::info!(
87 "Resetting with PINMUX_TAP_FPGA_BACKDOOR (== DFT) strapping applied to enter the backdoor loader"
88 );
89 pinmux_tap_backdoor.apply()?;
90 if let Some(trst) = &trst {
91 log::info!("Asserting TRST strapping");
92 trst.apply()?;
93 }
94 reset.apply()?;
95 std::thread::sleep(Duration::from_millis(RESET_PULSE_MS));
96 reset.remove()?;
99 if let Some(trst) = &trst {
100 log::info!("Deasserting TRST strapping");
101 trst.remove()?;
102 }
103 std::thread::sleep(Duration::from_millis(HOLD_TAP_STRAPS_MS));
104 pinmux_tap_backdoor.remove()?;
105 log::info!("Reset complete, backdoor TAP strapping released");
106 Ok(())
107}
108
109pub struct BackdoorTap<'a> {
114 jtag: Box<dyn JtagChain + 'a>,
115 jtag_speed_khz: u64,
116}
117
118impl BackdoorTap<'_> {
119 pub fn connect(self, enumerate: bool) -> Result<Backdoor> {
121 let openocd = self.jtag.connect(JtagTap::BackdoorTap)?.into_raw()?;
122 Backdoor::new(
123 OpenOcdDmi::new(openocd, "fpga_backdoor.tap")?,
124 self.jtag_speed_khz,
125 enumerate,
126 )
127 }
128}
129
130#[derive(Debug, Args, Clone)]
131pub struct BackdoorParams {
132 #[command(flatten)]
134 pub jtag: JtagParams,
135}
136
137impl BackdoorParams {
138 pub fn create<'a>(&self, transport: &'a TransportWrapper) -> Result<BackdoorTap<'a>> {
139 Ok(BackdoorTap {
140 jtag: self.jtag.create(transport)?,
141 jtag_speed_khz: self.jtag.adapter_speed_khz,
142 })
143 }
144}
145
146#[derive(Debug, Clone, Copy)]
148pub struct BackdoorTargetInfo {
149 pub id: u32,
151 pub width: u32,
153 pub depth: u32,
155}
156
157impl BackdoorTargetInfo {
158 pub fn id_str(&self) -> String {
160 let bytes = self.id.to_be_bytes();
161
162 String::from_utf8_lossy(&bytes).trim_end().to_owned()
163 }
164
165 pub fn id_from_str(id: &str) -> Result<u32> {
167 let mut bytes = [32u8; 4];
168 let src = id.as_bytes();
169 let len = id.len().min(4);
170 bytes[..len].copy_from_slice(&src[..len]);
171
172 Ok(u32::from_be_bytes(bytes))
173 }
174}
175
176impl Serialize for BackdoorTargetInfo {
177 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
178 where
179 S: Serializer,
180 {
181 let mut s = serializer.serialize_struct("BackdoorTargetInfo", 4)?;
182 s.serialize_field("id", &self.id)?;
183 s.serialize_field("id_str", &self.id_str())?;
184 s.serialize_field("width", &self.width)?;
185 s.serialize_field("depth", &self.depth)?;
186 s.end()
187 }
188}
189
190impl std::fmt::Display for BackdoorTargetInfo {
191 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
192 write!(f, "{} {} x {}", self.id_str(), self.width, self.depth)
193 }
194}
195
196impl Word {
197 fn to_u32_chunks(&self) -> Result<[u32; DATA_REGS_PER_WORD]> {
199 ensure!(
200 self.bytes.len() <= DATA_REGS_PER_WORD * 4,
201 "Word '{}' with {} bytes will not fit into {} 32-bit registers.",
202 hex::encode(self.bytes.clone()),
203 self.bytes.len(),
204 DATA_REGS_PER_WORD
205 );
206 let mut chunks = [0u32; DATA_REGS_PER_WORD];
207
208 for (i, &b) in self.bytes.iter().rev().enumerate() {
211 let chunk_idx = i / 4;
213 let byte_pos = i % 4;
214 chunks[chunk_idx] |= (b as u32) << (byte_pos * 8);
215 }
216
217 Ok(chunks)
218 }
219
220 fn from_u32_chunks(chunks: &[u32; DATA_REGS_PER_WORD], bytes_per_word: usize) -> Self {
222 let num_chunks = bytes_per_word.div_ceil(size_of::<u32>());
223 let padding_bytes = (num_chunks * size_of::<u32>()) - bytes_per_word;
224
225 Self {
226 bytes: chunks
227 .iter()
228 .take(num_chunks)
229 .rev()
230 .flat_map(|chunk| chunk.to_be_bytes())
231 .skip(padding_bytes)
232 .collect(),
233 }
234 }
235}
236
237pub struct BackdoorTarget<'a> {
239 backdoor: &'a mut Backdoor,
240 index: u8,
241 pub info: BackdoorTargetInfo,
243}
244
245impl<'a> BackdoorTarget<'a> {
246 pub fn write(
253 &mut self,
254 start: u32,
255 words: &[Word],
256 write_all: bool,
257 check_status: bool,
258 ) -> Result<()> {
259 ensure!(
260 start + words.len() as u32 <= self.info.depth,
261 "fpga bkdr_loader write of len {:#x} to word {:#x} of {} is out of bounds (depth: {:#x})",
262 words.len(),
263 start,
264 self.info.id_str(),
265 self.info.depth,
266 );
267 self.backdoor
268 .write_target(self.index, start, words, write_all, check_status)
269 }
270
271 pub fn read(&mut self, start: u32, count: u32, check_status: bool) -> Result<Vec<Word>> {
276 ensure!(
277 start + count <= self.info.depth,
278 "fpga bkdr_loader read of len {:#x} to word {:#x} of {} is out of bounds (depth: {:#x})",
279 count,
280 start,
281 self.info.id_str(),
282 self.info.depth,
283 );
284 self.backdoor
285 .read_target(self.index, start, count, check_status)
286 }
287
288 pub fn write_word(&mut self, index: u32, word: &Word, check_status: bool) -> Result<()> {
291 ensure!(
292 index < self.info.depth,
293 "fpga bkdr_loader write to word {:#x} of {} is out of bounds (depth: {:#x})",
294 index,
295 self.info.id_str(),
296 self.info.depth,
297 );
298 self.backdoor
299 .write_target_word(self.index, index, word, check_status)
300 }
301
302 pub fn read_word(&mut self, index: u32, check_status: bool) -> Result<Word> {
305 ensure!(
306 index < self.info.depth,
307 "fpga bkdr_loader read from word {:#x} of {} is out of bounds (depth: {:#x})",
308 index,
309 self.info.id_str(),
310 self.info.depth,
311 );
312 self.backdoor
313 .read_target_word(self.index, index, check_status)
314 }
315
316 pub fn clear(&mut self, word: &Word, check_status: bool) -> Result<()> {
322 self.backdoor.clear_target(self.index, word, check_status)
323 }
324
325 pub fn read_hash(&mut self) -> Result<u32> {
329 self.backdoor.read_target_hash(self.index)
330 }
331
332 pub fn write_hash(&mut self, hash: u32) -> Result<()> {
334 self.backdoor.write_target_hash(self.index, hash)
335 }
336}
337
338pub struct Backdoor {
340 dmi: OpenOcdDmi,
341 jtag_speed_khz: u64,
342 targets: Vec<BackdoorTargetInfo>,
343}
344
345impl Backdoor {
346 pub fn new(dmi: OpenOcdDmi, jtag_speed_khz: u64, enumerate: bool) -> Result<Self> {
349 let mut fpga_backdoor = Self {
350 dmi,
351 jtag_speed_khz,
352 targets: Vec::new(),
353 };
354 if enumerate {
355 fpga_backdoor.enumerate()?;
356 }
357
358 Ok(fpga_backdoor)
359 }
360
361 fn dmi_read(&mut self, byte_addr: usize) -> Result<u32> {
364 self.dmi.dmi_read(byte_addr as u32 >> 2)
365 }
366
367 fn dmi_write(&mut self, byte_addr: usize, data: u32) -> Result<()> {
370 self.dmi.dmi_write(byte_addr as u32 >> 2, data)
371 }
372
373 pub fn enumerate(&mut self) -> Result<()> {
375 self.targets.clear();
376
377 let num_targets = self
378 .dmi_read(regs::NUM_BKDR_TARGETS_REG_OFFSET)
379 .context("cannot read number of targets")? as usize;
380 log::info!("Number of FPGA bkdr_loader targets: {num_targets:?}");
381 for idx in 0..num_targets {
382 let addr_offset = idx * 4;
383 let target_info = BackdoorTargetInfo {
384 id: self
385 .dmi_read(regs::TARGET_INFO_0_REG_OFFSET + addr_offset)
386 .context("cannot read target info")?,
387 width: self
388 .dmi_read(regs::WIDTH_INFO_0_REG_OFFSET + addr_offset)
389 .context("cannot read width info")?,
390 depth: self
391 .dmi_read(regs::DEPTH_INFO_0_REG_OFFSET + addr_offset)
392 .context("cannot read depth info")?,
393 };
394 self.targets.push(target_info);
395 }
396
397 Ok(())
398 }
399
400 pub fn set_done(mut self) -> Result<()> {
405 log::debug!("Finished using backdoor loader until next reset");
406
407 let jtag_freq_hz: u64 = self.jtag_speed_khz * 1000;
414 let soc_clk_wait_cycles =
415 CW340_MAIN_CLOCK_FREQ_HZ.div_ceil(jtag_freq_hz) * JTAG_DONE_CYCLES;
416 let soc_clk_wait_cycles: u32 = soc_clk_wait_cycles.try_into().unwrap_or_else(|_| {
417 log::warn!(
418 "Configured JTAG speed ({} kHz) may overflow bkdr_loader wait time.",
419 self.jtag_speed_khz
420 );
421 log::warn!("Configuring maximum wait time.");
422 u32::MAX
423 });
424 self.dmi_write(
425 regs::MISSION_MODE_SWITCH_DELAY_REG_OFFSET,
426 soc_clk_wait_cycles,
427 )
428 .context("cannot write FPGA bkdr_loader mission_mode_switch_delay register")?;
429
430 if let Err(e) = self
431 .dmi_write(regs::CONTROL_REG_OFFSET, 0b1 << regs::CONTROL_DONE_BIT)
432 .context("cannot write done to FPGA bkdr_loader control reg")
433 {
434 log::error!("Error received when writing to `CONTROL.DONE`: {:?}", e);
435 log::error!("Trying to continue anyway...");
436 }
437
438 drop(self);
441
442 let done_wait_millis = (JTAG_DONE_CYCLES * 1000).div_ceil(jtag_freq_hz);
446 std::thread::sleep(Duration::from_millis(done_wait_millis));
447
448 Ok(())
449 }
450
451 pub fn targets(&self) -> &[BackdoorTargetInfo] {
453 &self.targets
454 }
455
456 pub fn target_by_id(&mut self, id: u32) -> Option<BackdoorTarget<'_>> {
458 let (index, info) = self.targets.iter().enumerate().find(|&(_, t)| t.id == id)?;
459 let (index, info) = (index as u8, *info);
460
461 Some(BackdoorTarget {
462 backdoor: self,
463 index,
464 info,
465 })
466 }
467
468 pub fn target_by_id_str(&mut self, id: &str) -> Result<Option<BackdoorTarget<'_>>> {
470 let encoded_id = BackdoorTargetInfo::id_from_str(id)?;
471
472 Ok(self.target_by_id(encoded_id))
473 }
474
475 pub fn write_target(
489 &mut self,
490 target_index: u8,
491 start: u32,
492 words: &[Word],
493 write_all: bool,
494 check_status: bool,
495 ) -> Result<()> {
496 ensure!(
497 usize::from(target_index) < self.targets.len(),
498 "Target index {} is out of range for {} targets",
499 target_index,
500 self.targets.len()
501 );
502 let info = self.targets[target_index as usize];
503 let width = info.width as usize;
504 let regs_used = width.div_ceil(u32::BITS as usize);
505 ensure!(
506 regs_used <= DATA_REGS_PER_WORD,
507 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
508 width,
509 regs_used,
510 DATA_REGS_PER_WORD
511 );
512
513 if words.is_empty() {
514 return Ok(());
515 }
516
517 let top_reg_idx = regs_used - 1;
520
521 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
522 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
523 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
524
525 let mut prev_regs = [0u32; DATA_REGS_PER_WORD];
527 let mut first = true;
528
529 let mut writes = vec![
536 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
537 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
538 ];
539
540 for word in words {
541 let regs = word.to_u32_chunks()?;
542 for idx in 0..regs_used {
543 if idx == top_reg_idx || write_all || first || regs[idx] != prev_regs[idx] {
548 let addr_offset = idx * 4;
549 writes.push((
550 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
551 regs[idx],
552 ));
553 prev_regs[idx] = regs[idx];
554 }
555 }
556 first = false;
557 }
558
559 self.dmi
560 .batched_dmi_writes(&writes)
561 .context("failed to perform DMI writes")?;
562
563 if check_status {
564 let end_index = self
568 .dmi_read(regs::INDEX_REG_OFFSET)
569 .context("cannot read back index")?;
570 ensure!(
571 end_index == start + words.len() as u32,
572 "fpga bkdr_loader index is {:#x} after writing {:#x} words at {:#x} of target {} (expected {:#x})",
573 end_index,
574 words.len(),
575 start,
576 info.id_str(),
577 start + words.len() as u32
578 );
579
580 let status = self
581 .dmi_read(regs::STATUS_REG_OFFSET)
582 .context("cannot read status")?;
583 ensure!(
584 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
585 "fpga bkdr_loader reported an error writing to target {}",
586 info.id_str()
587 );
588 }
589
590 Ok(())
591 }
592
593 pub fn write_target_word(
602 &mut self,
603 target_index: u8,
604 index: u32,
605 word: &Word,
606 check_status: bool,
607 ) -> Result<()> {
608 ensure!(
609 usize::from(target_index) < self.targets.len(),
610 "Target index {} is out of range for {} targets",
611 target_index,
612 self.targets.len()
613 );
614 let info = self.targets[target_index as usize];
615 let width = info.width as usize;
616 let regs_used = width.div_ceil(u32::BITS as usize);
617 ensure!(
618 regs_used <= DATA_REGS_PER_WORD,
619 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
620 width,
621 regs_used,
622 DATA_REGS_PER_WORD
623 );
624
625 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
626 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
627
628 let regs = word.to_u32_chunks()?;
631 let writes: Vec<(u32, u32)> =
632 std::iter::once(((regs::CONTROL_REG_OFFSET >> 2) as u32, control))
633 .chain(regs[..regs_used].iter().enumerate().map(|(idx, ®)| {
634 let addr_offset = idx * 4;
635 (
636 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
637 reg,
638 )
639 }))
640 .chain(std::iter::once((
641 (regs::INDEX_REG_OFFSET >> 2) as u32,
642 index,
643 )))
644 .collect();
645
646 self.dmi
647 .batched_dmi_writes(&writes)
648 .context("failed to perform DMI writes")?;
649
650 if check_status {
651 let status = self
652 .dmi_read(regs::STATUS_REG_OFFSET)
653 .context("cannot read status")?;
654 ensure!(
655 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
656 "fpga bkdr_loader reported an error writing to target {}",
657 info.id_str()
658 );
659 }
660
661 Ok(())
662 }
663
664 pub fn read_target(
677 &mut self,
678 target_index: u8,
679 start: u32,
680 count: u32,
681 check_status: bool,
682 ) -> Result<Vec<Word>> {
683 ensure!(
684 usize::from(target_index) < self.targets.len(),
685 "Target index {} is out of range for {} targets",
686 target_index,
687 self.targets.len()
688 );
689 let info = self.targets[target_index as usize];
690 let width = info.width as usize;
691 let bytes_per_word = width.div_ceil(u8::BITS as usize);
692 let regs_used = width.div_ceil(u32::BITS as usize);
693 ensure!(
694 regs_used <= DATA_REGS_PER_WORD,
695 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
696 width,
697 regs_used,
698 DATA_REGS_PER_WORD
699 );
700
701 if count == 0 {
702 return Ok(Vec::new());
703 }
704
705 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
706 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
707 self.dmi
712 .batched_dmi_writes(&[
713 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
714 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
715 ])
716 .context("cannot set up control and index registers")?;
717
718 let addrs: Vec<u32> = (0..count)
722 .flat_map(|_| {
723 (0..regs_used).map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
724 })
725 .collect();
726 let values = self
727 .dmi
728 .batched_dmi_reads(&addrs)
729 .context("cannot read from read_data registers")?;
730
731 let words = values
732 .chunks_exact(regs_used)
733 .map(|chunk| {
734 let mut regs = [0u32; DATA_REGS_PER_WORD];
735 regs[..regs_used].copy_from_slice(chunk);
736 Word::from_u32_chunks(®s, bytes_per_word)
737 })
738 .collect::<Vec<_>>();
739
740 if check_status {
741 let end_index = self
744 .dmi_read(regs::INDEX_REG_OFFSET)
745 .context("cannot read back index")?;
746 ensure!(
747 end_index == start + count,
748 "fpga bkdr_loader index is {:#x} after reading {:#x} words at {:#x} of target {} (expected {:#x})",
749 end_index,
750 count,
751 start,
752 info.id_str(),
753 start + count
754 );
755
756 let status = self
757 .dmi_read(regs::STATUS_REG_OFFSET)
758 .context("cannot read status")?;
759 ensure!(
760 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
761 "fpga bkdr_loader reported an error reading from target {} starting at word {}",
762 info.id_str(),
763 start
764 );
765 }
766
767 Ok(words)
768 }
769
770 pub fn read_target_word(
779 &mut self,
780 target_index: u8,
781 index: u32,
782 check_status: bool,
783 ) -> Result<Word> {
784 ensure!(
785 usize::from(target_index) < self.targets.len(),
786 "Target index {} is out of range for {} targets",
787 target_index,
788 self.targets.len()
789 );
790 let info = self.targets[target_index as usize];
791 let width = info.width as usize;
792 let bytes_per_word = width.div_ceil(u8::BITS as usize);
793 let regs_used = width.div_ceil(u32::BITS as usize);
794 ensure!(
795 regs_used <= DATA_REGS_PER_WORD,
796 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
797 width,
798 regs_used,
799 DATA_REGS_PER_WORD
800 );
801
802 let control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
805 self.dmi
806 .batched_dmi_writes(&[
807 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
808 ((regs::INDEX_REG_OFFSET >> 2) as u32, index),
809 ])
810 .context("cannot set up control and index registers")?;
811
812 if check_status {
813 let status = self
814 .dmi_read(regs::STATUS_REG_OFFSET)
815 .context("cannot read status")?;
816 ensure!(
817 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
818 "fpga bkdr_loader reported an error reading from word idx {} of target {}",
819 index,
820 info.id_str()
821 );
822 }
823
824 let addrs: Vec<u32> = (0..regs_used)
825 .map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
826 .collect();
827 let values = self
828 .dmi
829 .batched_dmi_reads(&addrs)
830 .context("cannot read from read_data registers")?;
831
832 let mut regs = [0u32; DATA_REGS_PER_WORD];
833 regs[..regs_used].copy_from_slice(&values);
834
835 Ok(Word::from_u32_chunks(®s, bytes_per_word))
836 }
837
838 pub fn clear_target(
844 &mut self,
845 target_index: u8,
846 word: &Word,
847 check_status: bool,
848 ) -> Result<()> {
849 ensure!(
850 usize::from(target_index) < self.targets.len(),
851 "Target index {} is out of range for {} targets",
852 target_index,
853 self.targets.len()
854 );
855 let info = self.targets[target_index as usize];
856
857 self.dmi
858 .batched_dmi_writes(
859 &word
860 .to_u32_chunks()?
861 .into_iter()
862 .enumerate()
863 .map(|(idx, reg)| {
864 let addr_offset = idx * 4;
865 (
866 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
867 reg,
868 )
869 })
870 .collect::<Vec<_>>(),
871 )
872 .context("failed to perform DMI writes")?;
873
874 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
875 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
876 control |= 0b1 << regs::CONTROL_CLEAR_START_BIT;
877 self.dmi_write(regs::CONTROL_REG_OFFSET, control)
878 .context("cannot write to control register")?;
879
880 let timeout = Instant::now() + Duration::from_secs(CLEAR_TIMEOUT_SECS);
882 let mut status: u32;
883 loop {
884 status = self
885 .dmi_read(regs::STATUS_REG_OFFSET)
886 .context("cannot read status")?;
887 if status & (0b1 << regs::STATUS_CLEAR_IDLE_BIT) != 0 {
888 break;
889 }
890
891 if Instant::now() > timeout {
892 bail!(
893 "Timed out after {} seconds waiting for {} clear to complete",
894 CLEAR_TIMEOUT_SECS,
895 info.id_str()
896 );
897 }
898 }
899
900 if check_status {
901 ensure!(
902 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
903 "fpga bkdr_loader reported an error writing to target {}",
904 info.id_str()
905 );
906 }
907
908 Ok(())
909 }
910
911 pub fn read_target_hash(&mut self, target_index: u8) -> Result<u32> {
919 ensure!(
920 usize::from(target_index) < self.targets.len(),
921 "Target index {} is out of range for {} targets",
922 target_index,
923 self.targets.len()
924 );
925 self.dmi_read(regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4)
926 .context("cannot read target hash register")
927 }
928
929 pub fn write_target_hash(&mut self, target_index: u8, hash: u32) -> Result<()> {
931 ensure!(
932 usize::from(target_index) < self.targets.len(),
933 "Target index {} is out of range for {} targets",
934 target_index,
935 self.targets.len()
936 );
937 self.dmi_write(
938 regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4,
939 hash,
940 )
941 .context("cannot write target hash register")
942 }
943
944 pub fn read_usr_access_timestamp(&mut self) -> Result<u32> {
949 self.dmi_read(regs::USR_ACCESS_TIMESTAMP_REG_OFFSET)
950 .context("cannot read USR_ACCESS_TIMESTAMP register")
951 }
952}
953
954#[cfg(test)]
955mod tests {
956 use super::*;
957
958 #[test]
959 fn identifer_str_encoding() {
960 let (width, depth) = (1, 1);
961 for (id, id_str) in [
962 (0x4f545020, "OTP"),
963 (0x5352414d, "SRAM"),
964 (0x46493031, "FI01"),
965 ] {
966 assert_eq!(BackdoorTargetInfo { id, width, depth }.id_str(), id_str);
967 assert_eq!(BackdoorTargetInfo::id_from_str(id_str).unwrap(), id);
968 }
969 }
970
971 #[test]
972 fn byte_u32_conversion() {
973 let word = Word::new(vec![
975 0x5a, 0xa5, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xbe, 0xef, 0xca, 0xfe,
976 ]);
977 let mut expected = [0x0; DATA_REGS_PER_WORD];
980 expected[0] = 0xbeefcafe;
981 expected[1] = 0x89abcdef;
982 expected[2] = 0x01234567;
983 expected[3] = 0x00005aa5;
984
985 let chunks = word.to_u32_chunks().unwrap();
986 assert_eq!(chunks, expected);
987 assert_eq!(Word::from_u32_chunks(&chunks, word.bytes.len()), word);
988 }
989}