Skip to main content

armv8_single_step/
armv8-single-step.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3use std::ops::DerefMut;
4use std::num::ParseIntError;
5use std::time::Instant;
6
7use jtag_taps::cable::{self, Cable};
8use jtag_taps::statemachine::JtagSM;
9use jtag_taps::taps::Taps;
10
11use jtag_adi::{ArmDebugInterface, MemAP};
12use jtag_adi::armv8::ARMv8;
13
14use clap::Parser;
15
16#[derive(Parser, Debug)]
17#[command(author, version, about, long_about = None)]
18struct Args {
19    #[arg(short, long)]
20    cable: String,
21    #[arg(short, long)]
22    baud: u32,
23    #[arg(short, long, default_value_t = 0)]
24    /// Which JTAG TAP to use
25    tap_index: usize,
26    #[arg(short, long, default_value_t = 0)]
27    /// Which access port to use
28    ap_num: u32,
29    #[arg(long)]
30    cpu_base: String,
31    #[arg(long)]
32    cti_base: String,
33    command: Option<String>,
34}
35
36fn cpu_halt<T,U>(mem: &mut MemAP<T>, cti_base: u32)
37    where T: DerefMut<Target=U>,
38          U: Cable + ?Sized
39{
40    // Gate all
41    mem.write(cti_base + 0x140, 0).expect("write ctigate");
42
43    // Enable CTIOUTEN for channel 0
44    mem.write(cti_base + 0x0a0, 1).expect("write ctiouten");
45
46    // Generate HALT to core 0
47    mem.write(cti_base + 0x01c, 1).expect("write ctiouten");
48
49    // ACK the halt
50    mem.write(cti_base + 0x010, 3).expect("write ctiouten");
51    // Wait for ACK
52    while mem.read(cti_base + 0x134).unwrap() != 0 {}
53}
54
55fn parse_int(x: &str) -> Result<u32, ParseIntError> {
56    if x.starts_with("0x") {
57        let len = x.len();
58        u32::from_str_radix(&x[2..len], 16)
59    } else {
60        str::parse(&x)
61    }
62}
63
64fn main() {
65    let args = Args::parse();
66    let cable = cable::new_from_string(&args.cable, args.baud).expect("cable");
67    let jtag = JtagSM::new(cable);
68    let mut taps = Taps::new(jtag);
69    taps.detect();
70
71    // IDCODE instruction
72    let ir = vec![14];
73    taps.select_tap(0, &ir);
74    //let dr = taps.read_dr(32);
75    //let idcode = u32::from_le_bytes(dr.try_into().unwrap());
76    //assert_eq!(idcode, 0x6ba00477);
77
78    let adi = Rc::new(RefCell::new(ArmDebugInterface::new(taps)));
79    let mut mem = MemAP::new(adi.clone(), args.ap_num);
80
81    let cpu_base = parse_int(&args.cpu_base).expect("invalid cpu base");
82    let edprsr = mem.read(cpu_base + 0x314).expect("read edprsr");
83    //println!("edprsr {:x}", edprsr);
84    assert!(edprsr & 1 == 1);
85
86    // Clear OS lock
87    mem.write(cpu_base + 0x300, 0).expect("write oslar");
88
89    // Clear software lock lock
90    mem.write(cpu_base + 0xfb0, 0xC5ACCE55).expect("write oslar");
91    let oslar = mem.read(cpu_base + 0xfb4).expect("read oslar");
92    //println!("swlck {:x}", oslar);
93    assert_eq!(oslar & 2, 0);
94
95    // Enable halting debug
96    let mut edscr = mem.read(cpu_base + 0x088).expect("read edscr");
97    println!("edscr {:x}", edscr);
98    edscr |= 1 << 14;
99    // Make sure memory access mode is disabled
100    edscr &= !(1 << 20);
101    mem.write(cpu_base + 0x088, edscr).expect("write edscr");
102
103    //// Unlock CTI
104    let cti_base = parse_int(&args.cti_base).expect("invalid cti base");
105    mem.write(cti_base + 0xfb0, 0xC5ACCE55).expect("write cti");
106
107    //// Enable CTI
108    let mut cti = mem.read(cti_base).expect("read cti");
109    //println!("cti {:x}", cti);
110    cti |= 1;
111    mem.write(cti_base, cti).expect("write cti");
112    let cti = mem.read(cti_base).expect("read cti");
113    //println!("cti {:x}", cti);
114    assert_eq!(cti & 1, 1);
115
116    // Must be in halt state
117    cpu_halt(&mut mem, cti_base);
118    // enable single step
119    mem.write(cpu_base + 0x024, 1 << 2).expect("write edecr");
120    // clear sticky error bit
121    mem.write(cpu_base + 0x090, 1 << 2).expect("write edrcr");
122
123    let mut v8 = ARMv8::new(mem, cpu_base, cti_base);
124
125    // Read out any data that may already be in the DBGDTR so it doesn't overflow
126    loop {
127        let edscr = v8.read_cpu(0x088).expect("read edscr");
128        if edscr & (1 << 29) == 0 {
129            break;
130        }
131        println!("bit 29");
132        v8.read_cpu(0x08c).expect("read edscr");
133    }
134
135    // Same for the CPU direction, read DBGGTR_EL0 from the CPU
136    loop {
137        let edscr = v8.read_cpu(0x088).expect("read edscr");
138        if edscr & (1 << 30) == 0 {
139            break;
140        }
141        println!("bit 30");
142        // mrs x0, dbgdtr_el0
143        v8.run_instr(0xd5330400).expect("write EDITR");
144    }
145
146    // pull these writes out of the loop for performance
147    v8.mem.write(cti_base + 0x140, 0).expect("write ctigate");
148    v8.mem.write(cti_base + 0x0a4, 2).expect("write ctiouten");
149
150    let start = Instant::now();
151    let mut count = 0;
152    loop {
153
154        // Save x0
155        let orig_x0 = v8.get_reg(0).expect("get x0");
156
157        // mrs x0, dlr_el0
158        v8.run_instr(0xd53b4520).expect("write EDITR");
159
160        let dlr = v8.get_reg(0).expect("get x0");
161        println!("dlr {:016x}", dlr);
162
163        // Restore x0
164        v8.set_reg(0, orig_x0).expect("set x0");
165
166        count += 1;
167        if count % 100 == 0 {
168            let delta = start.elapsed().as_millis();
169            eprintln!("IPS {}", count * 1000 / delta);
170        }
171
172        // resume the CPU so it can run one instruction
173        v8.mem.write(cti_base + 0x01c, 2).expect("write ctiouten");
174    }
175}