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
79 log::info!(
80 "Resetting with PINMUX_TAP_FPGA_BACKDOOR (== DFT) strapping applied to enter the backdoor loader"
81 );
82 pinmux_tap_backdoor.apply()?;
83 reset.apply()?;
84 std::thread::sleep(Duration::from_millis(RESET_PULSE_MS));
85 reset.remove()?;
86 std::thread::sleep(Duration::from_millis(HOLD_TAP_STRAPS_MS));
87 pinmux_tap_backdoor.remove()?;
88 log::info!("Reset complete, backdoor TAP strapping released");
89 Ok(())
90}
91
92pub struct BackdoorTap<'a> {
97 jtag: Box<dyn JtagChain + 'a>,
98 jtag_speed_khz: u64,
99}
100
101impl BackdoorTap<'_> {
102 pub fn connect(self, enumerate: bool) -> Result<Backdoor> {
104 let openocd = self.jtag.connect(JtagTap::BackdoorTap)?.into_raw()?;
105 Backdoor::new(
106 OpenOcdDmi::new(openocd, "fpga_backdoor.tap")?,
107 self.jtag_speed_khz,
108 enumerate,
109 )
110 }
111}
112
113#[derive(Debug, Args, Clone)]
114pub struct BackdoorParams {
115 #[command(flatten)]
117 pub jtag: JtagParams,
118}
119
120impl BackdoorParams {
121 pub fn create<'a>(&self, transport: &'a TransportWrapper) -> Result<BackdoorTap<'a>> {
122 Ok(BackdoorTap {
123 jtag: self.jtag.create(transport)?,
124 jtag_speed_khz: self.jtag.adapter_speed_khz,
125 })
126 }
127}
128
129#[derive(Debug, Clone, Copy)]
131pub struct BackdoorTargetInfo {
132 pub id: u32,
134 pub width: u32,
136 pub depth: u32,
138}
139
140impl BackdoorTargetInfo {
141 pub fn id_str(&self) -> String {
143 let bytes = self.id.to_be_bytes();
144
145 String::from_utf8_lossy(&bytes).trim_end().to_owned()
146 }
147
148 pub fn id_from_str(id: &str) -> Result<u32> {
150 let mut bytes = [32u8; 4];
151 let src = id.as_bytes();
152 let len = id.len().min(4);
153 bytes[..len].copy_from_slice(&src[..len]);
154
155 Ok(u32::from_be_bytes(bytes))
156 }
157}
158
159impl Serialize for BackdoorTargetInfo {
160 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 S: Serializer,
163 {
164 let mut s = serializer.serialize_struct("BackdoorTargetInfo", 4)?;
165 s.serialize_field("id", &self.id)?;
166 s.serialize_field("id_str", &self.id_str())?;
167 s.serialize_field("width", &self.width)?;
168 s.serialize_field("depth", &self.depth)?;
169 s.end()
170 }
171}
172
173impl std::fmt::Display for BackdoorTargetInfo {
174 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
175 write!(f, "{} {} x {}", self.id_str(), self.width, self.depth)
176 }
177}
178
179impl Word {
180 fn to_u32_chunks(&self) -> Result<[u32; DATA_REGS_PER_WORD]> {
182 ensure!(
183 self.bytes.len() <= DATA_REGS_PER_WORD * 4,
184 "Word '{}' with {} bytes will not fit into {} 32-bit registers.",
185 hex::encode(self.bytes.clone()),
186 self.bytes.len(),
187 DATA_REGS_PER_WORD
188 );
189 let mut chunks = [0u32; DATA_REGS_PER_WORD];
190
191 for (i, &b) in self.bytes.iter().rev().enumerate() {
194 let chunk_idx = i / 4;
196 let byte_pos = i % 4;
197 chunks[chunk_idx] |= (b as u32) << (byte_pos * 8);
198 }
199
200 Ok(chunks)
201 }
202
203 fn from_u32_chunks(chunks: &[u32; DATA_REGS_PER_WORD], bytes_per_word: usize) -> Self {
205 let num_chunks = bytes_per_word.div_ceil(size_of::<u32>());
206 let padding_bytes = (num_chunks * size_of::<u32>()) - bytes_per_word;
207
208 Self {
209 bytes: chunks
210 .iter()
211 .take(num_chunks)
212 .rev()
213 .flat_map(|chunk| chunk.to_be_bytes())
214 .skip(padding_bytes)
215 .collect(),
216 }
217 }
218}
219
220pub struct BackdoorTarget<'a> {
222 backdoor: &'a mut Backdoor,
223 index: u8,
224 pub info: BackdoorTargetInfo,
226}
227
228impl<'a> BackdoorTarget<'a> {
229 pub fn write(
236 &mut self,
237 start: u32,
238 words: &[Word],
239 write_all: bool,
240 check_status: bool,
241 ) -> Result<()> {
242 ensure!(
243 start + words.len() as u32 <= self.info.depth,
244 "fpga bkdr_loader write of len {:#x} to word {:#x} of {} is out of bounds (depth: {:#x})",
245 words.len(),
246 start,
247 self.info.id_str(),
248 self.info.depth,
249 );
250 self.backdoor
251 .write_target(self.index, start, words, write_all, check_status)
252 }
253
254 pub fn read(&mut self, start: u32, count: u32, check_status: bool) -> Result<Vec<Word>> {
259 ensure!(
260 start + count <= self.info.depth,
261 "fpga bkdr_loader read of len {:#x} to word {:#x} of {} is out of bounds (depth: {:#x})",
262 count,
263 start,
264 self.info.id_str(),
265 self.info.depth,
266 );
267 self.backdoor
268 .read_target(self.index, start, count, check_status)
269 }
270
271 pub fn write_word(&mut self, index: u32, word: &Word, check_status: bool) -> Result<()> {
274 ensure!(
275 index < self.info.depth,
276 "fpga bkdr_loader write to word {:#x} of {} is out of bounds (depth: {:#x})",
277 index,
278 self.info.id_str(),
279 self.info.depth,
280 );
281 self.backdoor
282 .write_target_word(self.index, index, word, check_status)
283 }
284
285 pub fn read_word(&mut self, index: u32, check_status: bool) -> Result<Word> {
288 ensure!(
289 index < self.info.depth,
290 "fpga bkdr_loader read from 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 .read_target_word(self.index, index, check_status)
297 }
298
299 pub fn clear(&mut self, word: &Word, check_status: bool) -> Result<()> {
305 self.backdoor.clear_target(self.index, word, check_status)
306 }
307
308 pub fn read_hash(&mut self) -> Result<u32> {
312 self.backdoor.read_target_hash(self.index)
313 }
314
315 pub fn write_hash(&mut self, hash: u32) -> Result<()> {
317 self.backdoor.write_target_hash(self.index, hash)
318 }
319}
320
321pub struct Backdoor {
323 dmi: OpenOcdDmi,
324 jtag_speed_khz: u64,
325 targets: Vec<BackdoorTargetInfo>,
326}
327
328impl Backdoor {
329 pub fn new(dmi: OpenOcdDmi, jtag_speed_khz: u64, enumerate: bool) -> Result<Self> {
332 let mut fpga_backdoor = Self {
333 dmi,
334 jtag_speed_khz,
335 targets: Vec::new(),
336 };
337 if enumerate {
338 fpga_backdoor.enumerate()?;
339 }
340
341 Ok(fpga_backdoor)
342 }
343
344 fn dmi_read(&mut self, byte_addr: usize) -> Result<u32> {
347 self.dmi.dmi_read(byte_addr as u32 >> 2)
348 }
349
350 fn dmi_write(&mut self, byte_addr: usize, data: u32) -> Result<()> {
353 self.dmi.dmi_write(byte_addr as u32 >> 2, data)
354 }
355
356 pub fn enumerate(&mut self) -> Result<()> {
358 self.targets.clear();
359
360 let num_targets = self
361 .dmi_read(regs::NUM_BKDR_TARGETS_REG_OFFSET)
362 .context("cannot read number of targets")? as usize;
363 log::info!("Number of FPGA bkdr_loader targets: {num_targets:?}");
364 for idx in 0..num_targets {
365 let addr_offset = idx * 4;
366 let target_info = BackdoorTargetInfo {
367 id: self
368 .dmi_read(regs::TARGET_INFO_0_REG_OFFSET + addr_offset)
369 .context("cannot read target info")?,
370 width: self
371 .dmi_read(regs::WIDTH_INFO_0_REG_OFFSET + addr_offset)
372 .context("cannot read width info")?,
373 depth: self
374 .dmi_read(regs::DEPTH_INFO_0_REG_OFFSET + addr_offset)
375 .context("cannot read depth info")?,
376 };
377 self.targets.push(target_info);
378 }
379
380 Ok(())
381 }
382
383 pub fn set_done(mut self) -> Result<()> {
388 log::debug!("Finished using backdoor loader until next reset");
389
390 let jtag_freq_hz: u64 = self.jtag_speed_khz * 1000;
397 let soc_clk_wait_cycles =
398 CW340_MAIN_CLOCK_FREQ_HZ.div_ceil(jtag_freq_hz) * JTAG_DONE_CYCLES;
399 let soc_clk_wait_cycles: u32 = soc_clk_wait_cycles.try_into().unwrap_or_else(|_| {
400 log::warn!(
401 "Configured JTAG speed ({} kHz) may overflow bkdr_loader wait time.",
402 self.jtag_speed_khz
403 );
404 log::warn!("Configuring maximum wait time.");
405 u32::MAX
406 });
407 self.dmi_write(
408 regs::MISSION_MODE_SWITCH_DELAY_REG_OFFSET,
409 soc_clk_wait_cycles,
410 )
411 .context("cannot write FPGA bkdr_loader mission_mode_switch_delay register")?;
412
413 if let Err(e) = self
414 .dmi_write(regs::CONTROL_REG_OFFSET, 0b1 << regs::CONTROL_DONE_BIT)
415 .context("cannot write done to FPGA bkdr_loader control reg")
416 {
417 log::error!("Error received when writing to `CONTROL.DONE`: {:?}", e);
418 log::error!("Trying to continue anyway...");
419 }
420
421 drop(self);
424
425 let done_wait_millis = (JTAG_DONE_CYCLES * 1000).div_ceil(jtag_freq_hz);
429 std::thread::sleep(Duration::from_millis(done_wait_millis));
430
431 Ok(())
432 }
433
434 pub fn targets(&self) -> &[BackdoorTargetInfo] {
436 &self.targets
437 }
438
439 pub fn target_by_id(&mut self, id: u32) -> Option<BackdoorTarget<'_>> {
441 let (index, info) = self.targets.iter().enumerate().find(|&(_, t)| t.id == id)?;
442 let (index, info) = (index as u8, *info);
443
444 Some(BackdoorTarget {
445 backdoor: self,
446 index,
447 info,
448 })
449 }
450
451 pub fn target_by_id_str(&mut self, id: &str) -> Result<Option<BackdoorTarget<'_>>> {
453 let encoded_id = BackdoorTargetInfo::id_from_str(id)?;
454
455 Ok(self.target_by_id(encoded_id))
456 }
457
458 pub fn write_target(
472 &mut self,
473 target_index: u8,
474 start: u32,
475 words: &[Word],
476 write_all: bool,
477 check_status: bool,
478 ) -> Result<()> {
479 ensure!(
480 usize::from(target_index) < self.targets.len(),
481 "Target index {} is out of range for {} targets",
482 target_index,
483 self.targets.len()
484 );
485 let info = self.targets[target_index as usize];
486 let width = info.width as usize;
487 let regs_used = width.div_ceil(u32::BITS as usize);
488 ensure!(
489 regs_used <= DATA_REGS_PER_WORD,
490 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
491 width,
492 regs_used,
493 DATA_REGS_PER_WORD
494 );
495
496 if words.is_empty() {
497 return Ok(());
498 }
499
500 let top_reg_idx = regs_used - 1;
503
504 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
505 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
506 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
507
508 let mut prev_regs = [0u32; DATA_REGS_PER_WORD];
510 let mut first = true;
511
512 let mut writes = vec![
519 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
520 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
521 ];
522
523 for word in words {
524 let regs = word.to_u32_chunks()?;
525 for idx in 0..regs_used {
526 if idx == top_reg_idx || write_all || first || regs[idx] != prev_regs[idx] {
531 let addr_offset = idx * 4;
532 writes.push((
533 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
534 regs[idx],
535 ));
536 prev_regs[idx] = regs[idx];
537 }
538 }
539 first = false;
540 }
541
542 self.dmi
543 .batched_dmi_writes(&writes)
544 .context("failed to perform DMI writes")?;
545
546 if check_status {
547 let end_index = self
551 .dmi_read(regs::INDEX_REG_OFFSET)
552 .context("cannot read back index")?;
553 ensure!(
554 end_index == start + words.len() as u32,
555 "fpga bkdr_loader index is {:#x} after writing {:#x} words at {:#x} of target {} (expected {:#x})",
556 end_index,
557 words.len(),
558 start,
559 info.id_str(),
560 start + words.len() as u32
561 );
562
563 let status = self
564 .dmi_read(regs::STATUS_REG_OFFSET)
565 .context("cannot read status")?;
566 ensure!(
567 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
568 "fpga bkdr_loader reported an error writing to target {}",
569 info.id_str()
570 );
571 }
572
573 Ok(())
574 }
575
576 pub fn write_target_word(
585 &mut self,
586 target_index: u8,
587 index: u32,
588 word: &Word,
589 check_status: bool,
590 ) -> Result<()> {
591 ensure!(
592 usize::from(target_index) < self.targets.len(),
593 "Target index {} is out of range for {} targets",
594 target_index,
595 self.targets.len()
596 );
597 let info = self.targets[target_index as usize];
598 let width = info.width as usize;
599 let regs_used = width.div_ceil(u32::BITS as usize);
600 ensure!(
601 regs_used <= DATA_REGS_PER_WORD,
602 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
603 width,
604 regs_used,
605 DATA_REGS_PER_WORD
606 );
607
608 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
609 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
610
611 let regs = word.to_u32_chunks()?;
614 let writes: Vec<(u32, u32)> =
615 std::iter::once(((regs::CONTROL_REG_OFFSET >> 2) as u32, control))
616 .chain(regs[..regs_used].iter().enumerate().map(|(idx, ®)| {
617 let addr_offset = idx * 4;
618 (
619 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
620 reg,
621 )
622 }))
623 .chain(std::iter::once((
624 (regs::INDEX_REG_OFFSET >> 2) as u32,
625 index,
626 )))
627 .collect();
628
629 self.dmi
630 .batched_dmi_writes(&writes)
631 .context("failed to perform DMI writes")?;
632
633 if check_status {
634 let status = self
635 .dmi_read(regs::STATUS_REG_OFFSET)
636 .context("cannot read status")?;
637 ensure!(
638 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
639 "fpga bkdr_loader reported an error writing to target {}",
640 info.id_str()
641 );
642 }
643
644 Ok(())
645 }
646
647 pub fn read_target(
660 &mut self,
661 target_index: u8,
662 start: u32,
663 count: u32,
664 check_status: bool,
665 ) -> Result<Vec<Word>> {
666 ensure!(
667 usize::from(target_index) < self.targets.len(),
668 "Target index {} is out of range for {} targets",
669 target_index,
670 self.targets.len()
671 );
672 let info = self.targets[target_index as usize];
673 let width = info.width as usize;
674 let bytes_per_word = width.div_ceil(u8::BITS as usize);
675 let regs_used = width.div_ceil(u32::BITS as usize);
676 ensure!(
677 regs_used <= DATA_REGS_PER_WORD,
678 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
679 width,
680 regs_used,
681 DATA_REGS_PER_WORD
682 );
683
684 if count == 0 {
685 return Ok(Vec::new());
686 }
687
688 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
689 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
690 self.dmi
695 .batched_dmi_writes(&[
696 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
697 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
698 ])
699 .context("cannot set up control and index registers")?;
700
701 let addrs: Vec<u32> = (0..count)
705 .flat_map(|_| {
706 (0..regs_used).map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
707 })
708 .collect();
709 let values = self
710 .dmi
711 .batched_dmi_reads(&addrs)
712 .context("cannot read from read_data registers")?;
713
714 let words = values
715 .chunks_exact(regs_used)
716 .map(|chunk| {
717 let mut regs = [0u32; DATA_REGS_PER_WORD];
718 regs[..regs_used].copy_from_slice(chunk);
719 Word::from_u32_chunks(®s, bytes_per_word)
720 })
721 .collect::<Vec<_>>();
722
723 if check_status {
724 let end_index = self
727 .dmi_read(regs::INDEX_REG_OFFSET)
728 .context("cannot read back index")?;
729 ensure!(
730 end_index == start + count,
731 "fpga bkdr_loader index is {:#x} after reading {:#x} words at {:#x} of target {} (expected {:#x})",
732 end_index,
733 count,
734 start,
735 info.id_str(),
736 start + count
737 );
738
739 let status = self
740 .dmi_read(regs::STATUS_REG_OFFSET)
741 .context("cannot read status")?;
742 ensure!(
743 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
744 "fpga bkdr_loader reported an error reading from target {} starting at word {}",
745 info.id_str(),
746 start
747 );
748 }
749
750 Ok(words)
751 }
752
753 pub fn read_target_word(
762 &mut self,
763 target_index: u8,
764 index: u32,
765 check_status: bool,
766 ) -> Result<Word> {
767 ensure!(
768 usize::from(target_index) < self.targets.len(),
769 "Target index {} is out of range for {} targets",
770 target_index,
771 self.targets.len()
772 );
773 let info = self.targets[target_index as usize];
774 let width = info.width as usize;
775 let bytes_per_word = width.div_ceil(u8::BITS as usize);
776 let regs_used = width.div_ceil(u32::BITS as usize);
777 ensure!(
778 regs_used <= DATA_REGS_PER_WORD,
779 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
780 width,
781 regs_used,
782 DATA_REGS_PER_WORD
783 );
784
785 let control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
788 self.dmi
789 .batched_dmi_writes(&[
790 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
791 ((regs::INDEX_REG_OFFSET >> 2) as u32, index),
792 ])
793 .context("cannot set up control and index registers")?;
794
795 if check_status {
796 let status = self
797 .dmi_read(regs::STATUS_REG_OFFSET)
798 .context("cannot read status")?;
799 ensure!(
800 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
801 "fpga bkdr_loader reported an error reading from word idx {} of target {}",
802 index,
803 info.id_str()
804 );
805 }
806
807 let addrs: Vec<u32> = (0..regs_used)
808 .map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
809 .collect();
810 let values = self
811 .dmi
812 .batched_dmi_reads(&addrs)
813 .context("cannot read from read_data registers")?;
814
815 let mut regs = [0u32; DATA_REGS_PER_WORD];
816 regs[..regs_used].copy_from_slice(&values);
817
818 Ok(Word::from_u32_chunks(®s, bytes_per_word))
819 }
820
821 pub fn clear_target(
827 &mut self,
828 target_index: u8,
829 word: &Word,
830 check_status: bool,
831 ) -> Result<()> {
832 ensure!(
833 usize::from(target_index) < self.targets.len(),
834 "Target index {} is out of range for {} targets",
835 target_index,
836 self.targets.len()
837 );
838 let info = self.targets[target_index as usize];
839
840 self.dmi
841 .batched_dmi_writes(
842 &word
843 .to_u32_chunks()?
844 .into_iter()
845 .enumerate()
846 .map(|(idx, reg)| {
847 let addr_offset = idx * 4;
848 (
849 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
850 reg,
851 )
852 })
853 .collect::<Vec<_>>(),
854 )
855 .context("failed to perform DMI writes")?;
856
857 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
858 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
859 control |= 0b1 << regs::CONTROL_CLEAR_START_BIT;
860 self.dmi_write(regs::CONTROL_REG_OFFSET, control)
861 .context("cannot write to control register")?;
862
863 let timeout = Instant::now() + Duration::from_secs(CLEAR_TIMEOUT_SECS);
865 let mut status: u32;
866 loop {
867 status = self
868 .dmi_read(regs::STATUS_REG_OFFSET)
869 .context("cannot read status")?;
870 if status & (0b1 << regs::STATUS_CLEAR_IDLE_BIT) != 0 {
871 break;
872 }
873
874 if Instant::now() > timeout {
875 bail!(
876 "Timed out after {} seconds waiting for {} clear to complete",
877 CLEAR_TIMEOUT_SECS,
878 info.id_str()
879 );
880 }
881 }
882
883 if check_status {
884 ensure!(
885 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
886 "fpga bkdr_loader reported an error writing to target {}",
887 info.id_str()
888 );
889 }
890
891 Ok(())
892 }
893
894 pub fn read_target_hash(&mut self, target_index: u8) -> Result<u32> {
902 ensure!(
903 usize::from(target_index) < self.targets.len(),
904 "Target index {} is out of range for {} targets",
905 target_index,
906 self.targets.len()
907 );
908 self.dmi_read(regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4)
909 .context("cannot read target hash register")
910 }
911
912 pub fn write_target_hash(&mut self, target_index: u8, hash: u32) -> Result<()> {
914 ensure!(
915 usize::from(target_index) < self.targets.len(),
916 "Target index {} is out of range for {} targets",
917 target_index,
918 self.targets.len()
919 );
920 self.dmi_write(
921 regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4,
922 hash,
923 )
924 .context("cannot write target hash register")
925 }
926
927 pub fn read_usr_access_timestamp(&mut self) -> Result<u32> {
932 self.dmi_read(regs::USR_ACCESS_TIMESTAMP_REG_OFFSET)
933 .context("cannot read USR_ACCESS_TIMESTAMP register")
934 }
935}
936
937#[cfg(test)]
938mod tests {
939 use super::*;
940
941 #[test]
942 fn identifer_str_encoding() {
943 let (width, depth) = (1, 1);
944 for (id, id_str) in [
945 (0x4f545020, "OTP"),
946 (0x5352414d, "SRAM"),
947 (0x46493031, "FI01"),
948 ] {
949 assert_eq!(BackdoorTargetInfo { id, width, depth }.id_str(), id_str);
950 assert_eq!(BackdoorTargetInfo::id_from_str(id_str).unwrap(), id);
951 }
952 }
953
954 #[test]
955 fn byte_u32_conversion() {
956 let word = Word::new(vec![
958 0x5a, 0xa5, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xbe, 0xef, 0xca, 0xfe,
959 ]);
960 let mut expected = [0x0; DATA_REGS_PER_WORD];
963 expected[0] = 0xbeefcafe;
964 expected[1] = 0x89abcdef;
965 expected[2] = 0x01234567;
966 expected[3] = 0x00005aa5;
967
968 let chunks = word.to_u32_chunks().unwrap();
969 assert_eq!(chunks, expected);
970 assert_eq!(Word::from_u32_chunks(&chunks, word.bytes.len()), word);
971 }
972}