Nuclei-Software / nuclei-sdk

Nuclei RISC-V Software Development Kit
https://doc.nucleisys.com/nuclei_sdk
Apache License 2.0
117 stars 50 forks source link

[GD32VF103] Jump to embedded bootloader from user code #29

Closed KarlK90 closed 10 months ago

KarlK90 commented 3 years ago

I'm wondering if it is possible to jump to the embedded bootloader that is present at 0x1FFFB000 in the devices ROM without a reset and externally pulling Boot0 high?

So far I have tried to jump to 0x1FFFB000 immediately after the device has been reset in the startup code. Observing the execution path with a debugger I can confirm that this indeed produced a valid jump, but the bootloader code is not executed successfully e.g. with a USB connection it doesn't show up as a USB-DFU device.

I know that this is possible with STM32F103 and GD32F103, so there is a chance that this possible as well?

fanghuaqi commented 3 years ago

Hi @KarlK90 , I also didn't know what is happening in gd32v bootrom(it is not a chip made by Nuclei, we just provided RISC-V CORE ip to Gigabyte), maybe you need to check the datasheet for more details.

KarlK90 commented 3 years ago

@fanghuaqi Thanks for clarification! I share my findings with you or others that maybe have the same question.

Some further investigation in the disassembly of the bootloader revealed that it wasn't compiled to be position independent. E.g in the startup assembly the gp and sp pointers are calculated from the current pc which produces invalid addresses if the bootloader is not aliased to 0x0. Unfortunately there is no way to change the memory aliasing at runtime (like on some STM32 with the SYSCFG register).

I tried to rewrite the startup code and vector table with the correct values and jump over the original startup assembly. but it just traps at runtime. Maybe I did an error but it seems ATM as if this is just not possible with the current bootloader code.

See below update.

; Init gp
0x0000018c      auipc gp, 0x20001
0x00000190      addi gp, gp, -684
; Bootloader running from 0x1FFFB000 gives invalid address
; 0x1FFFB18C + 0x20001000 = 0x3FFFBEE0
;
; Bootloader running from 0x0 gives valid address
; 0x0000018C + 0x20001000 = 0x20000EE0
;
; Init sp
0x00000194      auipc sp, 0x20001
0x00000198      addi sp, sp, 1644  
; Bootloader running from 0x1FFFB000 gives invalid address
; 0x1FFFB194 + 0x20001000  + 1644 = 0x3FFFC800
;
; Bootloader running from 0x0 gives valid address
; 0x00000194 + 0x20001000 + 1644 = 0x20001800
rewritten startup assembly ```asm #define BOOTLOADER_ADDRESS 0x1FFFB000 #define VECTOR_TABLE_BASE 0x20001800 // Address needs 512 Byte alignment #define VECTOR_TABLE_END (VECTOR_TABLE_BASE + (87 * 4)) #define VECTOR_TIMER2_OFFSET (0xC0 + VECTOR_TABLE_BASE) #define VECTOR_TIMER2_ADDR (0x216C + BOOTLOADER_ADDRESS) #define VECTOR_USART0_OFFSET (0xE0 + VECTOR_TABLE_BASE) #define VECTOR_USART0_ADDR (0x20CC + BOOTLOADER_ADDRESS) #define VECTOR_USART1_OFFSET (0xE4 + VECTOR_TABLE_BASE) #define VECTOR_USART1_ADDR (0x211C + BOOTLOADER_ADDRESS) #define VECTOR_USBFS_OFFSET (0x158 + VECTOR_TABLE_BASE) #define VECTOR_USBFS_ADDR (0x20B4 + BOOTLOADER_ADDRESS) /* * Set the the NMI base mnvec to share * with mtvec by setting CSR_MMISC_CTL * bit 9 NMI_CAUSE_FFF to 1 */ li t0, MMISC_CTL_NMI_CAUSE_FFF csrs CSR_MMISC_CTL, t0 /* * Intialize ECLIC vector interrupt * base address mtvt to bootloader entry */ la t0, VECTOR_TABLE_BASE csrw CSR_MTVT, t0 /* * Set ECLIC non-vector entry to be controlled * by mtvt2 CSR register. * Intialize ECLIC non-vector interrupt * base address mtvt2 to irq_handler. */ #define IRQ_HANDLER 0x1FFFD1C4 la t0, IRQ_HANDLER csrw CSR_MTVT2, t0 csrs CSR_MTVT2, 0x1 /* Set the interrupt processing mode to ECLIC mode */ li t0, 0x3f csrc CSR_MTVEC, t0 csrs CSR_MTVEC, 0x3 /* Set Exception Entry MTVEC */ #define EXCEPTION_HANDLER 0x1FFFD180 la t0, EXCEPTION_HANDLER csrw CSR_MTVEC, t0 /* Set bootloader global pointer */ la gp, 0x20000EE0 /* 0x20001000+0x18c-684 */ /* Set bootloader stack pointer */ la sp, 0x20001800 /*0x20001000+0x194+1644*/ #define DATA_BASE 0x20000000 /* 0x20000000 + 0x1a4 - 420 */ #define DATA_END 0x20000708 /* 0x20000000 + 0x1ac + 1372 */ #define DATA_ROM 0x1FFFED14 /* 0x4000 + 0x19c - 1160 + BOOTLOADER_ADDRESS */ /* Data initialization. */ la a0, DATA_ROM la a1, DATA_BASE la a2, DATA_END dloop_boot: bge a1, a2, enddloop_boot lw a3, 0(a0) sw a3, 0(a1) addi a0, a0, 4 addi a1, a1, 4 j dloop_boot enddloop_boot: #define BSS_BASE 0x20000708 /* 0x20000000 + 0x1c8 + 1344 */ #define BSS_END 0x20001270 /* gp + 912 = 0x20000EE0 + 912 */ /* BSS initialization. */ la a1, BSS_BASE la a2, BSS_END bloop_boot: bge a1, a2, endbloop_boot sw zero, 0(a1) addi a1, a1, 4 j bloop_boot endbloop_boot: /* Reconstruct vector table. */ la a1, VECTOR_TABLE_BASE la a2, VECTOR_TABLE_END vloop_boot: bge a1, a2, endvloop_boot sw zero, 0(a1) addi a1, a1, 4 j vloop_boot endvloop_boot: la a1 , VECTOR_TIMER2_OFFSET la a2, VECTOR_TIMER2_ADDR sw a2, 0(a1) la a1 , VECTOR_USART0_OFFSET la a2, VECTOR_USART0_ADDR sw a2, 0(a1) la a1 , VECTOR_USART1_OFFSET la a2, VECTOR_USART1_ADDR sw a2, 0(a1) la a1 , VECTOR_USBFS_OFFSET la a2, VECTOR_USBFS_ADDR sw a2, 0(a1) /* Jump into bootloader after inti section. */ la a0, (BOOTLOADER_ADDRESS + 0x1E2) jr a0 ```
KarlK90 commented 3 years ago

Actually I got the numbers for the data section wrong and the vector table was outside the SRAM region... After updating the numbers the bootloader doesn't trap anymore and tries to enumerate as a USB device now. But it fails the setup, the code is caught in a loop. This is very similar to the behavior often seen when putting the device into bootloader mode via BOOT0 pin.

[ 8837.860409] usb 1-1.3: new full-speed USB device number 9 using xhci_hcd
[ 8837.968448] usb 1-1.3: device descriptor read/64, error -32
[ 8838.156448] usb 1-1.3: device descriptor read/64, error -32
[ 8838.262241] usb 1-1-port3: attempt power cycle
fanghuaqi commented 3 years ago

Hi @KarlK90 , sorry I can't give you more help with this issue, I didn't know too much details in side the bootrom of gd32v.

KarlK90 commented 3 years ago

@fanghuaqi no problem, as stated above jumping into the bootloader is not an easily accomplished. Therefore I have started to port the tinyuf2 userland bootloader for the GD32VF103. There are some problems with the USB peripheral, but when this is sorted this should solve my problems.