From 53d514f24773d3fe4257c2738a45c2453d71e8b4 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Tue, 12 Apr 2022 23:50:20 +0200 Subject: [PATCH] NMI-based tracing works --- README.md | 104 ++++++++++++++++- msp430fr5994.ld | 2 +- nmigen/main.c | 87 +++++++++++--- nmigen/trigctl.pio | 4 +- src/main.c | 276 +++++++++++++++++++++++++++++++++------------ 5 files changed, 378 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index d2392cb..c8473b7 100644 --- a/README.md +++ b/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 +``` + diff --git a/msp430fr5994.ld b/msp430fr5994.ld index 0fb3815..02cc85e 100644 --- a/msp430fr5994.ld +++ b/msp430fr5994.ld @@ -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 diff --git a/nmigen/main.c b/nmigen/main.c index 2f0117a..c153c18 100644 --- a/nmigen/main.c +++ b/nmigen/main.c @@ -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); } } diff --git a/nmigen/trigctl.pio b/nmigen/trigctl.pio index cadbd99..08cf71d 100644 --- a/nmigen/trigctl.pio +++ b/nmigen/trigctl.pio @@ -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); diff --git a/src/main.c b/src/main.c index 4d8c950..9d18ceb 100644 --- a/src/main.c +++ b/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,73 +69,92 @@ 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; + //traceaddr = 0x1002; + TA1CCR0 = DUMP_CYC_OFF; #else #ifdef SKIP_CYC_OFF - if (curticks > SKIP_CYC_OFF) { - done_irq = 0; - TA1CCR0 = SKIP_CYC_OFF; - curticks_ = curticks - SKIP_CYC_OFF /*+ 2*/; - } else + if (curticks > SKIP_CYC_OFF) { + done_irq = 0; + TA1CCR0 = SKIP_CYC_OFF; + curticks_ = curticks - SKIP_CYC_OFF /*+ 2*/; + } else #endif - { - done_irq = 1; - TA1CCR0 = curticks; + { + done_irq = 1; + TA1CCR0 = curticks; + } +#endif + //TA1CCTL0 |= CCIFG; + //TA1CCTL0 &= ~(CCIE|CCIFG); + TA1CCTL0 = CCIE; } -#endif - //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); }