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 DATA_REGS_PER_WORD: usize = 8; }
68
69use consts::*;
70
71pub fn enter_backdoor_loader(transport: &TransportWrapper) -> Result<()> {
73 transport.capabilities()?.request(Capability::GPIO).ok()?;
74 let pinmux_tap_backdoor = transport.pin_strapping("PINMUX_TAP_FPGA_BACKDOOR")?;
75 let reset = transport.pin_strapping("RESET")?;
76 let trst = transport.optional_pin_strapping("TRST")?;
80
81 log::info!(
82 "Resetting with PINMUX_TAP_FPGA_BACKDOOR (== DFT) strapping applied to enter the backdoor loader"
83 );
84 pinmux_tap_backdoor.apply()?;
85 if let Some(trst) = &trst {
86 log::info!("Asserting TRST strapping");
87 trst.apply()?;
88 }
89 reset.apply()?;
90 std::thread::sleep(Duration::from_millis(RESET_PULSE_MS));
91 reset.remove()?;
94 if let Some(trst) = &trst {
95 log::info!("Deasserting TRST strapping");
96 trst.remove()?;
97 }
98 std::thread::sleep(Duration::from_millis(HOLD_TAP_STRAPS_MS));
99 pinmux_tap_backdoor.remove()?;
100 log::info!("Reset complete, backdoor TAP strapping released");
101 Ok(())
102}
103
104pub struct BackdoorTap<'a> {
109 jtag: Box<dyn JtagChain + 'a>,
110 jtag_speed_khz: u64,
111 clock_main_hz: u64,
112}
113
114impl BackdoorTap<'_> {
115 pub fn connect(self, enumerate: bool) -> Result<Backdoor> {
117 let openocd = self.jtag.connect(JtagTap::BackdoorTap)?.into_raw()?;
118 Backdoor::new(
119 OpenOcdDmi::new(openocd, "fpga_backdoor.tap")?,
120 self.jtag_speed_khz,
121 self.clock_main_hz,
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 clock_main_hz: transport
140 .clock_frequency("main")
141 .context("main clock frequency must be set to use the FPGA backdoor")?,
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 clock_main_hz: u64,
343 targets: Vec<BackdoorTargetInfo>,
344}
345
346impl Backdoor {
347 pub fn new(
350 dmi: OpenOcdDmi,
351 jtag_speed_khz: u64,
352 clock_main_hz: u64,
353 enumerate: bool,
354 ) -> Result<Self> {
355 let mut fpga_backdoor = Self {
356 dmi,
357 jtag_speed_khz,
358 clock_main_hz,
359 targets: Vec::new(),
360 };
361 if enumerate {
362 fpga_backdoor.enumerate()?;
363 }
364
365 Ok(fpga_backdoor)
366 }
367
368 fn dmi_read(&mut self, byte_addr: usize) -> Result<u32> {
371 self.dmi.dmi_read(byte_addr as u32 >> 2)
372 }
373
374 fn dmi_write(&mut self, byte_addr: usize, data: u32) -> Result<()> {
377 self.dmi.dmi_write(byte_addr as u32 >> 2, data)
378 }
379
380 pub fn enumerate(&mut self) -> Result<()> {
382 self.targets.clear();
383
384 let num_targets = self
385 .dmi_read(regs::NUM_BKDR_TARGETS_REG_OFFSET)
386 .context("cannot read number of targets")? as usize;
387 log::info!("Number of FPGA bkdr_loader targets: {num_targets:?}");
388 for idx in 0..num_targets {
389 let addr_offset = idx * 4;
390 let target_info = BackdoorTargetInfo {
391 id: self
392 .dmi_read(regs::TARGET_INFO_0_REG_OFFSET + addr_offset)
393 .context("cannot read target info")?,
394 width: self
395 .dmi_read(regs::WIDTH_INFO_0_REG_OFFSET + addr_offset)
396 .context("cannot read width info")?,
397 depth: self
398 .dmi_read(regs::DEPTH_INFO_0_REG_OFFSET + addr_offset)
399 .context("cannot read depth info")?,
400 };
401 self.targets.push(target_info);
402 }
403
404 Ok(())
405 }
406
407 pub fn set_done(mut self) -> Result<()> {
412 log::debug!("Finished using backdoor loader until next reset");
413
414 let jtag_freq_hz: u64 = self.jtag_speed_khz * 1000;
420 let soc_clk_wait_cycles = self.clock_main_hz.div_ceil(jtag_freq_hz) * JTAG_DONE_CYCLES;
421 let soc_clk_wait_cycles: u32 = soc_clk_wait_cycles.try_into().unwrap_or_else(|_| {
422 log::warn!(
423 "Configured JTAG speed ({} kHz) may overflow bkdr_loader wait time.",
424 self.jtag_speed_khz
425 );
426 log::warn!("Configuring maximum wait time.");
427 u32::MAX
428 });
429 self.dmi_write(
430 regs::MISSION_MODE_SWITCH_DELAY_REG_OFFSET,
431 soc_clk_wait_cycles,
432 )
433 .context("cannot write FPGA bkdr_loader mission_mode_switch_delay register")?;
434
435 if let Err(e) = self
436 .dmi_write(regs::CONTROL_REG_OFFSET, 0b1 << regs::CONTROL_DONE_BIT)
437 .context("cannot write done to FPGA bkdr_loader control reg")
438 {
439 log::error!("Error received when writing to `CONTROL.DONE`: {:?}", e);
440 log::error!("Trying to continue anyway...");
441 }
442
443 drop(self);
446
447 let done_wait_millis = (JTAG_DONE_CYCLES * 1000).div_ceil(jtag_freq_hz);
451 std::thread::sleep(Duration::from_millis(done_wait_millis));
452
453 Ok(())
454 }
455
456 pub fn targets(&self) -> &[BackdoorTargetInfo] {
458 &self.targets
459 }
460
461 pub fn target_by_id(&mut self, id: u32) -> Option<BackdoorTarget<'_>> {
463 let (index, info) = self.targets.iter().enumerate().find(|&(_, t)| t.id == id)?;
464 let (index, info) = (index as u8, *info);
465
466 Some(BackdoorTarget {
467 backdoor: self,
468 index,
469 info,
470 })
471 }
472
473 pub fn target_by_id_str(&mut self, id: &str) -> Result<Option<BackdoorTarget<'_>>> {
475 let encoded_id = BackdoorTargetInfo::id_from_str(id)?;
476
477 Ok(self.target_by_id(encoded_id))
478 }
479
480 pub fn write_target(
494 &mut self,
495 target_index: u8,
496 start: u32,
497 words: &[Word],
498 write_all: bool,
499 check_status: bool,
500 ) -> Result<()> {
501 ensure!(
502 usize::from(target_index) < self.targets.len(),
503 "Target index {} is out of range for {} targets",
504 target_index,
505 self.targets.len()
506 );
507 let info = self.targets[target_index as usize];
508 let width = info.width as usize;
509 let regs_used = width.div_ceil(u32::BITS as usize);
510 ensure!(
511 regs_used <= DATA_REGS_PER_WORD,
512 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
513 width,
514 regs_used,
515 DATA_REGS_PER_WORD
516 );
517
518 if words.is_empty() {
519 return Ok(());
520 }
521
522 let top_reg_idx = regs_used - 1;
525
526 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
527 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
528 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
529
530 let mut prev_regs = [0u32; DATA_REGS_PER_WORD];
532 let mut first = true;
533
534 let mut writes = vec![
541 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
542 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
543 ];
544
545 for word in words {
546 let regs = word.to_u32_chunks()?;
547 for idx in 0..regs_used {
548 if idx == top_reg_idx || write_all || first || regs[idx] != prev_regs[idx] {
553 let addr_offset = idx * 4;
554 writes.push((
555 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
556 regs[idx],
557 ));
558 prev_regs[idx] = regs[idx];
559 }
560 }
561 first = false;
562 }
563
564 self.dmi
565 .batched_dmi_writes(&writes)
566 .context("failed to perform DMI writes")?;
567
568 if check_status {
569 let end_index = self
573 .dmi_read(regs::INDEX_REG_OFFSET)
574 .context("cannot read back index")?;
575 ensure!(
576 end_index == start + words.len() as u32,
577 "fpga bkdr_loader index is {:#x} after writing {:#x} words at {:#x} of target {} (expected {:#x})",
578 end_index,
579 words.len(),
580 start,
581 info.id_str(),
582 start + words.len() as u32
583 );
584
585 let status = self
586 .dmi_read(regs::STATUS_REG_OFFSET)
587 .context("cannot read status")?;
588 ensure!(
589 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
590 "fpga bkdr_loader reported an error writing to target {}",
591 info.id_str()
592 );
593 }
594
595 Ok(())
596 }
597
598 pub fn write_target_word(
607 &mut self,
608 target_index: u8,
609 index: u32,
610 word: &Word,
611 check_status: bool,
612 ) -> Result<()> {
613 ensure!(
614 usize::from(target_index) < self.targets.len(),
615 "Target index {} is out of range for {} targets",
616 target_index,
617 self.targets.len()
618 );
619 let info = self.targets[target_index as usize];
620 let width = info.width as usize;
621 let regs_used = width.div_ceil(u32::BITS as usize);
622 ensure!(
623 regs_used <= DATA_REGS_PER_WORD,
624 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
625 width,
626 regs_used,
627 DATA_REGS_PER_WORD
628 );
629
630 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
631 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
632
633 let regs = word.to_u32_chunks()?;
636 let writes: Vec<(u32, u32)> =
637 std::iter::once(((regs::CONTROL_REG_OFFSET >> 2) as u32, control))
638 .chain(regs[..regs_used].iter().enumerate().map(|(idx, ®)| {
639 let addr_offset = idx * 4;
640 (
641 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
642 reg,
643 )
644 }))
645 .chain(std::iter::once((
646 (regs::INDEX_REG_OFFSET >> 2) as u32,
647 index,
648 )))
649 .collect();
650
651 self.dmi
652 .batched_dmi_writes(&writes)
653 .context("failed to perform DMI writes")?;
654
655 if check_status {
656 let status = self
657 .dmi_read(regs::STATUS_REG_OFFSET)
658 .context("cannot read status")?;
659 ensure!(
660 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
661 "fpga bkdr_loader reported an error writing to target {}",
662 info.id_str()
663 );
664 }
665
666 Ok(())
667 }
668
669 pub fn read_target(
682 &mut self,
683 target_index: u8,
684 start: u32,
685 count: u32,
686 check_status: bool,
687 ) -> Result<Vec<Word>> {
688 ensure!(
689 usize::from(target_index) < self.targets.len(),
690 "Target index {} is out of range for {} targets",
691 target_index,
692 self.targets.len()
693 );
694 let info = self.targets[target_index as usize];
695 let width = info.width as usize;
696 let bytes_per_word = width.div_ceil(u8::BITS as usize);
697 let regs_used = width.div_ceil(u32::BITS as usize);
698 ensure!(
699 regs_used <= DATA_REGS_PER_WORD,
700 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
701 width,
702 regs_used,
703 DATA_REGS_PER_WORD
704 );
705
706 if count == 0 {
707 return Ok(Vec::new());
708 }
709
710 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
711 control |= 0b1 << regs::CONTROL_AUTO_INCR_BIT;
712 self.dmi
717 .batched_dmi_writes(&[
718 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
719 ((regs::INDEX_REG_OFFSET >> 2) as u32, start),
720 ])
721 .context("cannot set up control and index registers")?;
722
723 let addrs: Vec<u32> = (0..count)
727 .flat_map(|_| {
728 (0..regs_used).map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
729 })
730 .collect();
731 let values = self
732 .dmi
733 .batched_dmi_reads(&addrs)
734 .context("cannot read from read_data registers")?;
735
736 let words = values
737 .chunks_exact(regs_used)
738 .map(|chunk| {
739 let mut regs = [0u32; DATA_REGS_PER_WORD];
740 regs[..regs_used].copy_from_slice(chunk);
741 Word::from_u32_chunks(®s, bytes_per_word)
742 })
743 .collect::<Vec<_>>();
744
745 if check_status {
746 let end_index = self
749 .dmi_read(regs::INDEX_REG_OFFSET)
750 .context("cannot read back index")?;
751 ensure!(
752 end_index == start + count,
753 "fpga bkdr_loader index is {:#x} after reading {:#x} words at {:#x} of target {} (expected {:#x})",
754 end_index,
755 count,
756 start,
757 info.id_str(),
758 start + count
759 );
760
761 let status = self
762 .dmi_read(regs::STATUS_REG_OFFSET)
763 .context("cannot read status")?;
764 ensure!(
765 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
766 "fpga bkdr_loader reported an error reading from target {} starting at word {}",
767 info.id_str(),
768 start
769 );
770 }
771
772 Ok(words)
773 }
774
775 pub fn read_target_word(
784 &mut self,
785 target_index: u8,
786 index: u32,
787 check_status: bool,
788 ) -> Result<Word> {
789 ensure!(
790 usize::from(target_index) < self.targets.len(),
791 "Target index {} is out of range for {} targets",
792 target_index,
793 self.targets.len()
794 );
795 let info = self.targets[target_index as usize];
796 let width = info.width as usize;
797 let bytes_per_word = width.div_ceil(u8::BITS as usize);
798 let regs_used = width.div_ceil(u32::BITS as usize);
799 ensure!(
800 regs_used <= DATA_REGS_PER_WORD,
801 "Advertised target width {:#x} is too wide for the data registers (needs: {:#x}, has: {:#x})",
802 width,
803 regs_used,
804 DATA_REGS_PER_WORD
805 );
806
807 let control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
810 self.dmi
811 .batched_dmi_writes(&[
812 ((regs::CONTROL_REG_OFFSET >> 2) as u32, control),
813 ((regs::INDEX_REG_OFFSET >> 2) as u32, index),
814 ])
815 .context("cannot set up control and index registers")?;
816
817 if check_status {
818 let status = self
819 .dmi_read(regs::STATUS_REG_OFFSET)
820 .context("cannot read status")?;
821 ensure!(
822 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
823 "fpga bkdr_loader reported an error reading from word idx {} of target {}",
824 index,
825 info.id_str()
826 );
827 }
828
829 let addrs: Vec<u32> = (0..regs_used)
830 .map(|idx| ((regs::READ_DATA_0_REG_OFFSET + idx * 4) >> 2) as u32)
831 .collect();
832 let values = self
833 .dmi
834 .batched_dmi_reads(&addrs)
835 .context("cannot read from read_data registers")?;
836
837 let mut regs = [0u32; DATA_REGS_PER_WORD];
838 regs[..regs_used].copy_from_slice(&values);
839
840 Ok(Word::from_u32_chunks(®s, bytes_per_word))
841 }
842
843 pub fn clear_target(
849 &mut self,
850 target_index: u8,
851 word: &Word,
852 check_status: bool,
853 ) -> Result<()> {
854 ensure!(
855 usize::from(target_index) < self.targets.len(),
856 "Target index {} is out of range for {} targets",
857 target_index,
858 self.targets.len()
859 );
860 let info = self.targets[target_index as usize];
861
862 self.dmi
863 .batched_dmi_writes(
864 &word
865 .to_u32_chunks()?
866 .into_iter()
867 .enumerate()
868 .map(|(idx, reg)| {
869 let addr_offset = idx * 4;
870 (
871 ((regs::WRITE_DATA_0_REG_OFFSET + addr_offset) >> 2) as u32,
872 reg,
873 )
874 })
875 .collect::<Vec<_>>(),
876 )
877 .context("failed to perform DMI writes")?;
878
879 let mut control = (target_index as u32) << regs::CONTROL_TARGET_IDX_OFFSET;
880 control |= 0b1 << regs::CONTROL_WRITE_ENA_BIT;
881 control |= 0b1 << regs::CONTROL_CLEAR_START_BIT;
882 self.dmi_write(regs::CONTROL_REG_OFFSET, control)
883 .context("cannot write to control register")?;
884
885 let timeout = Instant::now() + Duration::from_secs(CLEAR_TIMEOUT_SECS);
887 let mut status: u32;
888 loop {
889 status = self
890 .dmi_read(regs::STATUS_REG_OFFSET)
891 .context("cannot read status")?;
892 if status & (0b1 << regs::STATUS_CLEAR_IDLE_BIT) != 0 {
893 break;
894 }
895
896 if Instant::now() > timeout {
897 bail!(
898 "Timed out after {} seconds waiting for {} clear to complete",
899 CLEAR_TIMEOUT_SECS,
900 info.id_str()
901 );
902 }
903 }
904
905 if check_status {
906 ensure!(
907 status & (0b1 << regs::STATUS_ERROR_BIT) == 0,
908 "fpga bkdr_loader reported an error writing to target {}",
909 info.id_str()
910 );
911 }
912
913 Ok(())
914 }
915
916 pub fn read_target_hash(&mut self, target_index: u8) -> Result<u32> {
924 ensure!(
925 usize::from(target_index) < self.targets.len(),
926 "Target index {} is out of range for {} targets",
927 target_index,
928 self.targets.len()
929 );
930 self.dmi_read(regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4)
931 .context("cannot read target hash register")
932 }
933
934 pub fn write_target_hash(&mut self, target_index: u8, hash: u32) -> Result<()> {
936 ensure!(
937 usize::from(target_index) < self.targets.len(),
938 "Target index {} is out of range for {} targets",
939 target_index,
940 self.targets.len()
941 );
942 self.dmi_write(
943 regs::HASH_LAST_LOADED_0_REG_OFFSET + (target_index as usize) * 4,
944 hash,
945 )
946 .context("cannot write target hash register")
947 }
948
949 pub fn read_usr_access_timestamp(&mut self) -> Result<u32> {
954 self.dmi_read(regs::USR_ACCESS_TIMESTAMP_REG_OFFSET)
955 .context("cannot read USR_ACCESS_TIMESTAMP register")
956 }
957}
958
959#[cfg(test)]
960mod tests {
961 use super::*;
962
963 #[test]
964 fn identifer_str_encoding() {
965 let (width, depth) = (1, 1);
966 for (id, id_str) in [
967 (0x4f545020, "OTP"),
968 (0x5352414d, "SRAM"),
969 (0x46493031, "FI01"),
970 ] {
971 assert_eq!(BackdoorTargetInfo { id, width, depth }.id_str(), id_str);
972 assert_eq!(BackdoorTargetInfo::id_from_str(id_str).unwrap(), id);
973 }
974 }
975
976 #[test]
977 fn byte_u32_conversion() {
978 let word = Word::new(vec![
980 0x5a, 0xa5, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xbe, 0xef, 0xca, 0xfe,
981 ]);
982 let mut expected = [0x0; DATA_REGS_PER_WORD];
985 expected[0] = 0xbeefcafe;
986 expected[1] = 0x89abcdef;
987 expected[2] = 0x01234567;
988 expected[3] = 0x00005aa5;
989
990 let chunks = word.to_u32_chunks().unwrap();
991 assert_eq!(chunks, expected);
992 assert_eq!(Word::from_u32_chunks(&chunks, word.bytes.len()), word);
993 }
994}