commit 3c8366de2d0535392225ac753c9997b68efc8560 Author: kuwoyuki Date: Mon Dec 2 19:03:20 2024 +0600 first commit diff --git a/flash_map.png b/flash_map.png new file mode 100644 index 0000000..d7a3bfd Binary files /dev/null and b/flash_map.png differ diff --git a/rom-boot-seq.md b/rom-boot-seq.md new file mode 100644 index 0000000..5a8b1d8 --- /dev/null +++ b/rom-boot-seq.md @@ -0,0 +1,1048 @@ +# Ameba-Z (RTL8710BX) + + + +- [Ameba-Z RTL8710BX](#ameba-z-rtl8710bx) + - [Memory Map](#memory-map) + - [RAM Regions 256KB Total at 0x10000000](#ram-regions-256kb-total-at-0x10000000) + - [Flash Regions 1MB Total at 0x08000000](#flash-regions-1mb-total-at-0x08000000) + - [ROM Region](#rom-region) + - [Boot Process](#boot-process) + - [Reset_Handler](#reset_handler) + - [HalResetVsr](#halresetvsr) + - [HW states](#hw-states) + - [Setup](#setup) + - [Boot source select](#boot-source-select) + - [Early Boot Paths pre-populated SRAM](#early-boot-paths-pre-populated-sram) + - [Normal Boot Path](#normal-boot-path) + - [BOOT_ROM_Process](#boot_rom_process) + - [BOOT_ROM_FromFlash](#boot_rom_fromflash) + - [XIP bootloader flash layout](#xip-bootloader-flash-layout) + - [Image Location Calculation](#image-location-calculation) + - [Image Header @ 0x8000C88](#image-header--0x8000c88) + - [Function](#function) + - [TL;DR](#tldr) + - [XIPBOOT structure](#xipboot-structure) + - [Decompiled function](#decompiled-function) + - [FLASH_LoadAndBootImage](#flash_loadandbootimage) + - [control flow graph](#control-flow-graph) + - [check flash encryption in eFuse](#check-flash-encryption-in-efuse) + - [set up pointers to BD_RAM in BOOTLOADER_RAM](#set-up-pointers-to-bd_ram-in-bootloader_ram) + - [flash configuration](#flash-configuration) + - [OTA slot selection](#ota-slot-selection) + - [OTA_SelectBootImage decompiled](#ota_selectbootimage-decompiled) + - [check if OTA1 is forced by GPIO:](#check-if-ota1-is-forced-by-gpio) + - [final image selection and address set](#final-image-selection-and-address-set) + - [OTA1-TEXT header](#ota1-text-header) + - [OTA1-DATA](#ota1-data) + - [jump](#jump) + - [Decompiled function](#decompiled-function) + + + +## Memory Map + +### RAM Regions (256KB Total at 0x10000000) + +| Region | Start Address | End Address | Size | Flags | Description | +| --------- | ------------- | ----------- | ------- | ----- | ------------------ | +| ROM BSS | 0x10000000 | 0x10001FFF | 8KB | RW | ROM BSS Storage | +| IMG1 RAM | 0x10002000 | 0x10004FFF | 12KB | RWX | Boot Loader Code | +| IMG2 RAM | 0x10005000 | 0x1003DFFF | 172KB | RWX | Main Program RAM | +| MSP Stack | 0x1003E000 | 0x1003EFFF | 4KB | WX | Main Stack Pointer | +| RDP | 0x1003F000 | 0x1003FFEF | 4KB-16B | WX | RDP Operations | + +### Flash Regions (1MB Total at 0x08000000) + +| Region | Start Address | End Address | Size | Flags | Description | +| ----------- | ------------- | ----------- | ----- | ----- | --------------- | +| Boot | 0x08000020 | 0x08003FFF | ~16KB | RX | Boot Code | +| System Data | 0x08009000 | 0x08009FFF | 4KB | R | System Data | +| Reserved | 0x0800A000 | 0x0800AFFF | 4KB | R | Reserved Region | +| OTA1 | 0x0800B020 | 0x0807FFFF | 468KB | RX | OTA Region 1 | +| OTA2 | 0x08080020 | 0x080F4FFF | 468KB | RX | OTA Region 2 | + +### ROM Region + +| Region | Start Address | End Address | Size | Flags | Description | +| ------ | ------------- | ----------- | ----- | ----- | ----------- | +| ROM | 0x00000000 | 0x0007FFFF | 512KB | RX | Main ROM | + +Notes: + +- RDP region reserves 16 bytes at the end (0x1003FFF0-0x1003FFFF) +- Flash regions marked with 0x20 offset indicate header reservation +- Image2 RAM combines DATA, BSS, and HEAP sections + +![Flash map](./flash_map.png) + +## Boot Process + +```mermaid +sequenceDiagram + participant CPU as CPU + participant ROM as ROM + participant FLASH as Flash + participant RAM as RAM + participant eFuse as eFuse + + Note over CPU,RAM: Reset Sequence + CPU->>CPU: Reset_Handler + CPU->>CPU: Enable FPU + CPU->>CPU: Set Stack @ 0x1003EFFC + CPU->>ROM: Jump to HalResetVsr + + Note over ROM,RAM: Early Boot Check + ROM->>ROM: Check SOC_FUNC_EN Register + alt Boot from JTAG + ROM->>RAM: IMG2_JTAGPreloadBoot + else Unlock from JTAG + ROM->>RAM: IMG2_QuickValidateAndBoot + else Direct Patch + ROM->>RAM: IMG2_DirectPatchBoot + else Normal Boot + ROM->>ROM: Continue Boot Process + end + + Note over ROM,RAM: Normal Boot Init + ROM->>RAM: Clear Vector Table + ROM->>ROM: CPU_ClkSet + ROM->>ROM: Vector_TableInit + ROM->>ROM: OSC32K_Cmd + ROM->>ROM: Init UART/Debug + + Note over ROM,eFuse: Boot Source Selection + ROM->>eFuse: Read Boot Type + + alt Boot from Flash + ROM->>ROM: BOOT_ROM_FromFlash + ROM->>eFuse: Check Flash Encryption + + Note over ROM,FLASH: Load XIP Bootloader + ROM->>FLASH: Read IMG1_HEADER @ 0x8000C88 + ROM->>RAM: Copy to 0x10002000 (12KB) + ROM->>RAM: Verify Signature + ROM->>RAM: Jump to FLASH_LoadAndBootImage + + Note over RAM,FLASH: Image Selection + RAM->>FLASH: Configure Flash Speed/Mode + RAM->>RAM: IMG2_SetupAddresses + RAM->>FLASH: OTA_SelectBootImage + + alt Valid OTA2 & No GPIO Force + RAM->>FLASH: Load OTA2 + else Otherwise + RAM->>FLASH: Load OTA1 + end + + RAM->>RAM: Decrypt if needed + RAM->>RAM: Jump to Entry @ 0x10005000 + else Other Boot Sources + ROM->>ROM: Handle SDIO/USB/UART/SPI boot + end + + Note over RAM: Final Execution + RAM->>RAM: Start Application +``` + +### `Reset_Handler` + +```nasm +Reset_Handler: + LDR.W R0, =0xE000ED88 ; Enable FPU (CP10/11) + LDR R1, [R0] + ORR.W R1, R1, #0xF00000 + STR R1, [R0] + LDR.W SP, =0x1003EFFC ; Set stack pointer + B.W HalResetVsr ; Jump to init +``` + +### `HalResetVsr` + +#### HW states + +```c +// REG_SOC_FUNC_EN @ 0x40000210 +#define BIT_SOC_BOOT_FROM_JTAG (1 << 31) +#define BIT_SOC_UNLOCK_FROM_JTAG (1 << 30) +#define BIT_SOC_WAKE_FROM_PS (1 << 29) +#define BIT_SOC_PATCH_FUNC0 (1 << 28) +#define BIT_SOC_PATCH_FUNC1 (1 << 27) +#define BIT_SOC_PATCH_FUNC2 (1 << 26) + +// REG_SYS_EFUSE_SYSCFG6 @ 0x40000038 +#define BIT_MASK_SYS_BOOT_TYPE_SEL (3 << 28) +#define BIT_SYS_ICFG2_TEST (1 << 31) + +// Debug configuration +#define BIT_DBG_MSG_EN (1 << 30) // ConfigDebugErr bit 30 +``` + +#### 1. Setup + +```nasm +PUSH {R4-R6,LR} +LDR R3, =__rom_bss_end__ +LDR R5, =NewVectorTable +LDR R2, =(SYSCFG2_ROMINFO_Set+1) +LDR R4, =off_10002000 ; SRAM boot table ptr (may be empty) +SUB SP, SP, #8 +SUBS R5, R3, R5 ; Calculate vector table size +``` + +#### 2. Boot source select + +```c +// hw init first +SYSCFG2_ROMINFO_Set(); +RCC_PeriphClockCmd(0, 0x40u, 1); + +// early boot paths - SRAM must be pre-populated +if (REG_SOC_FUNC_EN & BIT_SOC_BOOT_FROM_JTAG) { // bit 31 + IMG2_JTAGPreloadBoot(); // off_10002004 +} else if (REG_SOC_FUNC_EN & BIT_SOC_UNLOCK_FROM_JTAG) { // bit 30 + IMG2_QuickValidateAndBoot(); // off_10002000 +} else if (REG_SOC_FUNC_EN & BIT_SOC_PATCH_FUNC0) { // bit 28 + IMG2_DirectPatchBoot(); // off_1000200C +} else { + // normal boot path - init everything + + // debug flags setup + ConfigDebugWarn = 0; + ConfigDebugErr = 0xFFFFFFFF; + ConfigDebugInfo = 0; + if (REG_SYS_EFUSE_SYSCFG6 & BIT_SYS_ICFG2_TEST) { + ConfigDebugErr = 0; + } + + // clear vector table + memset(&NewVectorTable, 0, &_rom_bss_end__ - (_UNKNOWN *)&NewVectorTable); + + // core system initialization + CPU_ClkSet(0); + VECTOR_TableInit(&unk_1003EFFC); + OSC32K_Cmd(1); + xtal_freq = XTAL_ClkGet(); + NCO32K_Init(0x8000, xtal_freq, 9, 2); + DIAG_UartInit(1); + BOOT_ROM_ShowBuildInfo(0); + SYSTIMER_Init(); + BOOT_ROM_InitFlash(); + BOOT_ROM_Simulation(); + + if (REG_SOC_FUNC_EN & BIT_SOC_PATCH_FUNC1) { // bit 27 + IMG2_QuickValidateAndBoot(); // off_10002010 + } + + // UART/Debug console handling + if (SYSCFG1_TRP_ICFG() == 2) { + if (SYSCFG1_AutoLoadDone() && (REG_SYS_EFUSE_SYSCFG6 & 0x80000000)) { + if (ConfigDebugErr & 0x40000000) { + DiagPrintf("\rROM BOOT: DEBUG mode \n"); + } + RtlConsolRom(100000); + } + } else { + RtlConsolRom(20); + } + + // check for UART image download + if (SYSCFG1_TRP_UARTImage() || (BKUP_Read() & 8)) { + BKUP_Clear(0, 8); + UARTIMG_Download(2); + + if (!(REG_SOC_FUNC_EN & BIT_SOC_PATCH_FUNC2)) { // bit 26 + goto NORMAL_BOOT; + } + } else if (!(REG_SOC_FUNC_EN & BIT_SOC_PATCH_FUNC2)) { + goto NORMAL_BOOT; + } + + IMG2_QuickValidateAndBoot(); // off_10002008 + +NORMAL_BOOT: + // turn off SWD based on eFuse + uint8_t efuse_val; + EFUSE_OneByteReadROM(REG_SYS_EFUSE_SYSCFG6, 211, &efuse_val, 7); + if (!(efuse_val & 1)) { + PINMUX_SWD_OFF(); + } + + // enable mac and init USB + REG_SOC_HCI_COM_FUNC_EN |= BIT_SOC_HCI_WL_MACON_EN; + BOOT_ROM_InitUsb(); + + // process normal boot + BOOT_ROM_Process(); +} + +while (1); +``` + +#### Early Boot Paths (pre-populated SRAM) + +1. **JTAG Boot** (BIT_SOC_BOOT_FROM_JTAG) + - boot via `IMG2_JTAGPreloadBoot` @ `0x10002004` +2. **JTAG Unlock** (BIT_SOC_UNLOCK_FROM_JTAG) + - boot via `IMG2_QuickValidateAndBoot` @ `0x10002000` +3. **Direct Patch** (BIT_SOC_PATCH_FUNC0) + - boot via `IMG2_DirectPatchBoot` @ `0x1000200C` + +#### Normal Boot Path + +- clear vector table +- **Patch Check** (`BIT_SOC_PATCH_FUNC1`): if set, boot via `IMG2_QuickValidateAndBoot` @ `0x10002000` +- **UART Image Handler** + - check for UART image or backup flag + - if found: + - clear backup flag + - download UART image + - if `BIT_SOC_PATCH_FUNC2` set: boot via`IMG2_QuickValidateAndBoot` @ `0x10002000` + - otherwise: goto normal boot +- disable SWD based if set in eFuse +- enable MAC +- init USB +- `BL BOOT_ROM_Process` + +### `BOOT_ROM_Process` + +Boot types: + +```c +enum _BOOT_TYPE_ { + BOOT_FROM_FLASH = 0, + BOOT_FROM_SDIO = 1, + BOOT_FROM_USB = 2, + BOOT_FROM_UART0 = 3, + BOOT_FROM_UART1 = 4, + BOOT_FROM_SPI = 5, + BOOT_FROM_RSVD = 6, +}; +``` + +```c +int __fastcall BOOT_ROM_Process(uint8_t *config, int param) { + // setup registers and read eFuse + uint32_t *efuse = (uint32_t *)0x40000038; // REG_SYS_EFUSE_SYSCFG6 + uint32_t debug = ConfigDebugErr; + _BOOT_TYPE_ boot_type = (*efuse >> 28) & 7; // BIT_MASK_SYS_BOOT_TYPE_SEL + + // If debug not enabled, direct jump to handler + if (!(debug & 0x80000000)) { // BMI check in asm + // TBB jump table implementation + switch (boot_type) { + case 0: + return BOOT_ROM_FromFlash((int)config, param); + case 1: + return SDIO_Boot_Up(config, param, debug << 1); + case 2: + return USB_Boot_ROM(config, param, debug << 1); + case 3: + return UARTIMG_Download(0, param, debug << 1); + case 4: + return UARTIMG_Download(1, param, debug << 1); + case 5: + return SPI_Boot_ROM(config, param, debug << 1); + default: + return RtlConsolRom(0x186A0, param, debug << 1); + } + } + + // Debug path - print info first + int xtal = XTAL_ClkGet(config, param, debug << 1); + DiagPrintf("\rBOOT TYPE:%x XTAL:%d\n", boot_type, xtal); + + switch (boot_type) { + case 0: + return BOOT_ROM_FromFlash((int)config, param); + case 1: + return SDIO_Boot_Up(config, param, debug << 1); + case 2: + return USB_Boot_ROM(config, param, debug << 1); + case 3: + return UARTIMG_Download(0, param, debug << 1); + case 4: + return UARTIMG_Download(1, param, debug << 1); + case 5: + return SPI_Boot_ROM(config, param, debug << 1); + default: + DiagPrintf("\rNo support boot type !!!!!!!\n"); + return RtlConsolRom(0x186A0, param, debug << 1); + } +} +``` + +### `BOOT_ROM_FromFlash` + +Loads the XIP bootloader RAM section from flash into SRAM + +#### XIP bootloader flash layout + +``` +0x8000000 - Flash Base + 0x8000008: IMG1_HEADER_OFFSET = 0xC68 +``` + +#### Image Location Calculation + +``` +1. Base (0x8000000) + IMG1_HEADER_OFFSET (0xC68) = 0x8000C68 +2. 0x8000C68 + 0x20 = 0x8000C88 (Final image location) +``` + +#### Image Header (@ 0x8000C88) + +``` +0x8000C88-0x8000C8F: Signature (99 99 96 96 3F CC 66 FC) +0x8000C90: Size (0x468) +0x8000C94: Destination (0x10002000) +``` + +#### Function + +1. check encryption in eFuse + +```nasm +LDR R3, =0x40000038 ; Check flash encryption status +LDR R3, [R3] +LSLS R4, R3, #0x12 ; Test bit 13 +BMI init_flash_encryption +``` + +2. calc image location + +```nasm +MOV.W R3, #0x8000000 ; Load flash base +LDR R5, [R3,#(IMG1_HEADER_OFFSET - 0x8000000)] ; Get 0xC68 +ADD.W R5, R5, #0x8000000 ; R5 = 0x8000C68 +ADDS R5, #0x20 ; R5 = 0x8000C88 (Image start) +``` + +3. load image params + +```nasm +LDR.W R8, [R5,#(dword_8000C90 - 0x8000C88)] ; Load size (0x468) +LDR R7, [R5,#(dword_8000C94 - 0x8000C88)] ; Load dest (0x10002000) +``` + +4. `memcpy` + +```c +memcpy(destination_addr, source_addr + 32, size); +// Copies from 0x8000C88 to 0x10002000 +``` + +5. entrypoint selection + +```nasm +LDR R3, [R4,#(off_10002014 - 0x10002000)] ; load FLASH_LoadAndBootImage +LDR R2, =off_10002000 ; base address for fallback +CBNZ R3, loc_5476 ; if primary entry valid, go to exit +LDR R3, [R2] ; load IMG2_QuickValidateAndBoot +loc_5476: ; exit sequence + POP.W {R4-R8,LR} + BX R3 ; jump to entry point + +``` + +#### TL;DR + +1. check flash encryption +2. calculate image location: + - start @ `0x8000000` + - add `IMG1_HEADER_OFFSET` (`0xC68` in factory boot_all.bin) + - add `0x20` to reach image +3. read image metadata from `0x8000C88` +4. copy `0x468` bytes to RAM @ `0x10002000` +5. validate copied data +6. jump to entry point (should be `FLASH_LoadAndBootImage`) + +#### XIPBOOT structure + +``` +Flash: +0x8000000 [Base] + └── +0xC68 [IMG1_HEADER_OFFSET] + └── +0x20 [Header Skip] + └── 0x8000C88 [Image Start] + ├── +0x00: Signature + ├── +0x08: Size (0x468) + └── +0x0C: Destination (0x10002000) +``` + +#### Decompiled function + +```c +__int64 __fastcall BOOT_ROM_FromFlash(int a1, int a2) { + int img_source; // r5 + int img_size; // r8 + int img_dest; // r7 + _BYTE *v5; // r0 + int v6; // r1 + char *pattern_dest; // r3 + void *pattern_src; // r2 + _BYTE *v9; // t1 + int pattern_byte; // t1 + __int64(__fastcall * v11)(int, int, int, int); // r3 + int v13; // r2 + int v14; // r3 + + if ((MEMORY[0x40000038] & 0x2000) != 0) init_flash_encryption(); + img_source = IMG1_HEADER_OFFSET + 0x8000020; + img_size = *(int *)((char *)&dword_8 + IMG1_HEADER_OFFSET + 0x8000020); + img_dest = *(int *)((char *)&dword_C + IMG1_HEADER_OFFSET + 0x8000020); + if ((ConfigDebugErr & 0x40000000) != 0) + DiagPrintf("\rIMG1 DATA[%d:%x]\n", + *(int *)((char *)&dword_8 + IMG1_HEADER_OFFSET + 0x8000020), + *(int *)((char *)&dword_C + IMG1_HEADER_OFFSET + 0x8000020)); + if (img_size == -1 || (a2 = img_dest + 1, img_dest == -1)) { + while (1) { + v13 = 2 * ConfigDebugErr; + if ((ConfigDebugErr & 0x40000000) != 0) + DiagPrintf("\rFlash not Program \n", a2, v13); + RtlConsolRom(100000, a2, v13); + } + } + v5 = (_BYTE *)memcpy(img_dest, img_source + 32, img_size); + pattern_dest = &byte_10002018; + pattern_src = (void *)(unsigned __int8)byte_10002018; + if (byte_10002018 != 35) { + validation_failed: + v14 = ConfigDebugErr & 0x40000000; + if ((ConfigDebugErr & 0x40000000) != 0) { + DiagPrintf("\rImage1 Validation Incorrect !!!\n"); + v14 = ConfigDebugErr & 0x40000000; + } + if (v14) goto LABEL_24; + while (1) { + do RtlConsolRom(100000, v6, pattern_src); + while ((ConfigDebugErr & 0x40000000) == 0); + LABEL_24: + DiagPrintf( + "\rPlease Re-boot and try again, or re-burn the flash image\n"); + } + } + pattern_src = &ROM_IMG1_VALID_PATTEN; + while (1) { + pattern_byte = *((unsigned __int8 *)pattern_src + 1); + pattern_src = (char *)pattern_src + 1; + v6 = pattern_byte; + if (pattern_byte == 255) break; + v9 = (_BYTE *)(unsigned __int8)*++pattern_dest; + v5 = v9; + if (v9 != (_BYTE *)v6) goto validation_failed; + } + if ((ConfigDebugErr & 0x40000000) != 0) + v5 = DiagPrintf("\rIMG1 ENTRY[%x:%x]\n", off_10002014, off_10002000); + v11 = (__int64(__fastcall *)(int, int, int, int))off_10002014; + if (!off_10002014) + v11 = (__int64(__fastcall *)(int, int, int, int))off_10002000; + return v11((int)v5, v6, (int)&off_10002000, (int)v11); +} +``` + +### `FLASH_LoadAndBootImage` + +#### control flow graph + +```mermaid +graph TD + A[Start] --> B[GPIO Setup] + B --> C[Security Config] + C --> D[Memory Setup] + D --> E[Flash Init] + E --> F[OTA Select] + F --> G{Valid OTA2?} + G -->|Yes| H[Load OTA2] + G -->|No| I[Load OTA1] + H --> J[Verify Image] + I --> J + J -->|Valid| K[Boot Image] + J -->|Invalid| L[Error Loop] +``` + +inits gpio: + +```nasm +; Configure SPI Flash pins +MOVS R1, #2 ; Pull control value +MOVS R0, #6 ; Pin 6 +BL j_PAD_PullCtrl ; Set pin pull control +MOVS R1, #2 +MOVS R0, #7 ; Pin 7 +BL j_PAD_PullCtrl +; ... repeats for pins 8,9,10,11,29,30 +``` + +#### check flash encryption in eFuse + +```c +if ((*efuseReg & 0x2000) != 0) { + j_OTF_Mask(0, 0x8000, 2, 1); + set_flash_crypt_masks(); +} +``` + +#### set up pointers to `BD_RAM` in `BOOTLOADER_RAM` + +done by `IMG2_SetupAddresses()` + +```nasm +; setup addresses for IMG2 +LDR R0, =IMG2_VECTOR_TABLE +LDR R1, =0x10005000 ; Vector table address +STR R1, [R0] ; Store vector table addr +ADDS R1, #8 ; Entry point = vector table + 8 +STR R1, [R0,#IMG2_ENTRY] +``` + +-> + +```c +// struct for ota load @ 0x10002430 +typedef struct { + uint32_t *vector_table; // where IMG2 will be loaded in RAM + uint32_t *signature_loc; // points to IMG2 signature location (+8 offset) + uint32_t *ram_start; // working RAM area start + uint32_t *ram_end; // working RAM area end + // a lot of other stuff +} IMG2_Config; + +void IMG2_SetupAddresses(void) { + IMG2_Config *config = (IMG2_Config *)0x10002430; + + config->vector_table = (uint32_t *)0x10005000; // Base RAM address + config->signature_loc = + (uint32_t *)(0x10005000 + 8); // where signature check will be + config->ram_start = (uint32_t *)0x10002464; // Working RAM space + config->ram_end = (uint32_t *)0x10002464; // Working RAM end + + // after fw is copied to RAM, we'd expect to find at 0x10005000: + // - entry_point (sub_8084DDA + 1) // +1 - Thumb + // - 0x803A95D // at 0x04 unrelated but some magic + // number? or address? + // - "RTKWin\0" // signature string +} +``` + +#### flash configuration + +```c +// Configure flash speed and mode +BYTE4(v33[0]) = count_leading_zeros(XIP_SYSTEM_DATA.Spic_Mode); +v14 = count_leading_zeros(XIP_SYSTEM_DATA.Spic_Speed); +if (v14 >= 7) v14 = 6; // Cap max speed + +FLASH_ConfigureClock((char *)v33 + 4); +FLASH_DetectAndInitType(BYTE4(v33[0])); +j_FLASH_ClockDiv(v14); +``` + +#### OTA slot selection + +```mermaid +graph TD + A[Start OTA Selection] --> B{Check Valid_Image2 bitmap} + B -->|OTA2 bit set| C{Check OTA2_FlashAddr} + B -->|OTA1 bit set| D[Select OTA1] + C -->|Valid Address| E{Verify OTA2 Signature} + C -->|Invalid/-1| D + E -->|Valid| F{Check Force GPIO} + E -->|Invalid| D + F -->|GPIO Set| G{Check GPIO State} + F -->|No GPIO/255| H[Select OTA2] + G -->|Force Active| D + G -->|Not Forced| H +``` + +```c +OTA2_FlashAddr = + (_DWORD *)XIP_SYSTEM_DATA.OTA2_FlashAddr; // address of OTA2 in flash +otaSelect = XIP_SYSTEM_DATA.Valid_Image2; // bitmap of valid images +selectedSlot = 0; + +// Find first valid slot +do { + if ((otaSelect >> selectedSlot) & 1) break; + ++selectedSlot; +} while (selectedSlot < 0x20); +``` + +then verify signature: + +```c +v9 = *(_DWORD *)OTA_SIGNATURE_1; +v10 = *OTA2_FlashAddr == *(_DWORD *)OTA_SIGNATURE_1; +if (*OTA2_FlashAddr == *(_DWORD *)OTA_SIGNATURE_1) { + v9 = OTA_SIGNATURE_2; + v10 = *(int *)((char *)&dword_4 + (_DWORD)OTA2_FlashAddr) == OTA_SIGNATURE_2; +} +``` + +#### `OTA_SelectBootImage` decompiled + +```c +void __fastcall OTA_SelectBootImage(_DWORD *destAddr) { + int *v1; // r7 + _DWORD *OTA2_FlashAddr; // r5 + unsigned int otaSelect; // r10 + unsigned int selectedSlot; // r4 + int v6; // r0 + unsigned int v7; // r1 + int v8; // r0 + int v9; // r1 + bool v10; // zf + int v11; // r4 + int v12; // r9 + int v13; // r1 + int v14; // [sp+0h] [bp-38h] BYREF + int v15; // [sp+4h] [bp-34h] + int v16; // [sp+14h] [bp-24h] + + v1 = ConfigDebugErr_ptr; + OTA2_FlashAddr = (_DWORD *)XIP_SYSTEM_DATA.OTA2_FlashAddr; + otaSelect = XIP_SYSTEM_DATA.Valid_Image2; + selectedSlot = 0; + if ((*ConfigDebugErr_ptr & 0x40000000) != 0) { + j_DiagPrintf("\rOTA2 ADDR[%x]\n", XIP_SYSTEM_DATA.OTA2_FlashAddr); + if ((*v1 & 0x40000000) != 0) j_DiagPrintf("\rOTAx SELE[%x]\n", otaSelect); + } + do { + if (((otaSelect >> selectedSlot) & 1) != 0) break; + ++selectedSlot; + } while (selectedSlot < 0x20); + v6 = *v1; + v7 = selectedSlot << 31; + if ((selectedSlot & 1) != 0) { + if ((v6 & 0x40000000) != 0) j_DiagPrintf(aOta2Use, v7); + v8 = *v1; + if (OTA2_FlashAddr == (_DWORD *)-1) { + if ((v8 & 0x40000000) != 0) j_DiagPrintf(aOta2NotInFlash, v7); + } else { + if ((v8 & 0x40000000) != 0) + j_DiagPrintf("\rOTA2 SIGN[%x:%x]\n", *OTA2_FlashAddr, + *(int *)((char *)&dword_4 + (_DWORD)OTA2_FlashAddr)); + v9 = *(_DWORD *)OTA_SIGNATURE_1; + v10 = *OTA2_FlashAddr == *(_DWORD *)OTA_SIGNATURE_1; + if (*OTA2_FlashAddr == *(_DWORD *)OTA_SIGNATURE_1) { + v9 = OTA_SIGNATURE_2; + v10 = *(int *)((char *)&dword_4 + (_DWORD)OTA2_FlashAddr) == + OTA_SIGNATURE_2; + } + if (v10) { + v11 = 0; + if (LOBYTE(XIP_SYSTEM_DATA.OTA1_ForceGpio) == 255) goto LABEL_31; + v16 = XIP_SYSTEM_DATA.OTA1_ForceGpio & 0x3F; + v14 = 0; + if ((XIP_SYSTEM_DATA.OTA1_ForceGpio & 0x80) != 0) { + v15 = 1; + v12 = 1; + } else { + v15 = 2; + v12 = 0; + } + j_GPIO_Init(&v14); + if (j_GPIO_ReadDataBit(v16) == v12) v11 = 1; + j_GPIO_DeInit(v16); + if (!v11) { + LABEL_31: + *destAddr = (char *)OTA2_FlashAddr + + *(int *)((char *)&dword_8 + (_DWORD)OTA2_FlashAddr) + 32; + } else if ((*v1 & 0x40000000) != 0) { + j_DiagPrintf("\rGPIO force OTA1 \n", v13); + } + } else if ((*v1 & 0x40000000) != 0) { + j_DiagPrintf(aOta2SignatureW, v9); + } + } + } else if ((v6 & 0x40000000) != 0) { + j_DiagPrintf(aOta1Use, v7); + } + JUMPOUT(0x8000236); +} +``` + +#### check if OTA1 is forced by GPIO: + +```c +if (LOBYTE(XIP_SYSTEM_DATA.OTA1_ForceGpio) != 255) { // if GPIO force enabled + gpioPin = XIP_SYSTEM_DATA.OTA1_ForceGpio & 0x3F; // get pin number + activeLevel = (XIP_SYSTEM_DATA.OTA1_ForceGpio & 0x80) ? 1 : 0; + + GPIO_Init(&gpioConfig); + if (GPIO_ReadDataBit(gpioPin) == activeLevel) { + forceOTA1 = 1; // force boot OTA1 + } + GPIO_DeInit(gpioPin); +} +``` + +#### final image selection and address set + +```c +if (!forceOTA1 && validOTA2) { + // calculate source address for OTA2 header + *headerAddr = (char *)OTA2_FlashAddr + + *(int *)((char *)&dword_8 + (_DWORD)OTA2_FlashAddr); +} else { + // calculate source address for OTA1 header + *headerAddr = (char *)&IMG2_OTA1_SIGNATURE_STR + + *(int *)((char *)&IMG2_OTA1_SIGNATURE_STR - 24); +} + +// read from header: +destAddr = *(int *)(*headerAddr + 12); // destination from header+12 +size = *(int *)(*headerAddr + 8); // size from header+8 + +// validation (1MB) +if (destAddr == -1 || size > 0x100000) { + // ... + return; +} + +// copy image data that follows the header +memcpy(destAddr, // destination from header + *headerAddr + 32, // source = header + 32 byte header size + size); // size from header +``` + +Example: + +1. Start at `IMG2_OTA1_SIGNATURE_STR` (`0x0800B020`) +2. Get offset at -24 bytes (`0x0800B008`): `0x823AC` + +3. Calculate header location: + `0x0800B020` + `0x823AC` = `0x808D3CC` + +4. From header at `0x808D3CC`: + + - At offset 8 (`0x808D3D4`): Size info (`0x746C`) + - At offset 12 (`0x808D3D8`): Destination = 0x10005000 (RAM address) + +5. Final memcpy: + +```c +memcpy(0x10005000, // destination (from header+12 at 0x808D3D8) + 0x808D3EC, // source (header + 32) + 0x746C) // size 0x746C (from header+8) +``` + +#### OTA1-TEXT header + +``` +seg001:0800B000 OTA1_HEADER DCD 0x35393138, 0x31313738; signature +seg001:0800B000 ; DATA XREF: FLASH_LoadAndBootImage+1AA↑r +seg001:0800B000 ; sub_8076A94+48↓o ... +seg001:0800B008 DCD 0x823AC ; image_size +seg001:0800B00C DCD 0x800B020 ; image_addr +seg001:0800B010 DCD 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF; reserved +seg001:0800B020 IMG2_OTA1_SIGNATURE_STR DCB "Customer Signature-modelxxx",0 +``` + +#### OTA1-DATA + +``` +seg001:0808D3CC DCD 0x35393138, 0x31313738 +seg001:0808D3D4 dword_808D3D4 DCD 0x746C ; DATA XREF: sub_8076A94+54↑r +seg001:0808D3D4 ; sub_80B0534+50↓r +seg001:0808D3D8 DCD unk_10005000 ; RAM destination address!!! +seg001:0808D3DC DCB 0xFF +seg001:0808D3DD DCB 0xFF +seg001:0808D3DE DCB 0xFF +seg001:0808D3DF DCB 0xFF +seg001:0808D3E0 DCB 0xFF +seg001:0808D3E1 DCB 0xFF +seg001:0808D3E2 DCB 0xFF +seg001:0808D3E3 DCB 0xFF +seg001:0808D3E4 DCB 0xFF +seg001:0808D3E5 DCB 0xFF +seg001:0808D3E6 DCB 0xFF +seg001:0808D3E7 DCB 0xFF +seg001:0808D3E8 DCB 0xFF +seg001:0808D3E9 DCB 0xFF +seg001:0808D3EA DCB 0xFF +seg001:0808D3EB DCB 0xFF +seg001:0808D3EC DCD sub_8084DDA+1 +seg001:0808D3F0 DCD 0x803A95D +seg001:0808D3F4 aRtkwin_2 DCB "RTKWin",0 +seg001:0808D3FB DCB 0xFF +``` + +#### jump + +```nasm +LDR.W R0, [R8] ; load address +BLX R0 ; jump to entry point +BL j_BOOT_RunInitializerArray ; this is dead code, since FreeRTOS will never return from the entry point +``` + +#### Decompiled function + +```c +__int64 __fastcall FLASH_LoadAndBootImage(int a1, int a2, int a3, int a4) { + int v4; // r0 + _DWORD *efuseReg; // r5 + int v6; // r4 + _DWORD *ramDest; // r7 + unsigned int *vectorTable; // r8 + int *debugErr; // r5 + int ramSize; // r4 + int v11; // r1 + int v12; // r2 + int v13; // r3 + int v14; // r7 + int v15; // r1 + __int64 v16; // r2 + int *v17; // r4 + int v18; // r0 + int v19; // r6 + int v20; // r11 + bool v21; // zf + bool v22; // zf + int v23; // r0 + int v24; // r7 + int v25; // r1 + unsigned int v26; // r2 + int v27; // r0 + int v28; // r0 + int *v29; // r4 + unsigned int v30; // r1 + int v31; // r0 + _QWORD v33[5]; // [sp+0h] [bp-28h] BYREF + + HIDWORD(v33[0]) = a4; + LODWORD(v33[0]) = 0; + j_PAD_PullCtrl(6, 2); + j_PAD_PullCtrl(7, 2); + j_PAD_PullCtrl(8, 2); + j_PAD_PullCtrl(9, 2); + j_PAD_PullCtrl(10, 2); + j_PAD_PullCtrl(11, 2); + j_PAD_PullCtrl(29, 2); + v4 = j_PAD_PullCtrl(30, 2); + efuseReg = REG_SYS_EFUSE_SYSCFG6; + v6 = *((_DWORD *)REG_SYS_EFUSE_SYSCFG6 + 27); + if (!j_SYSCFG1_TRP_LDOMode(v4)) efuseReg[27] = v6 & 0xFFFFFFFD; + efuseReg[27] &= ~4u; + if ((*efuseReg & 0x2000) != 0) { + j_OTF_Mask(0, 0x8000, 2, 1); + set_flash_crypt_masks(); + } + BYTE4(v33[0]) = 0; + thunk_IMG2_SetupAddresses(); + ramDest = (_DWORD *)IMG2_RAM_START_ptr[0]; + vectorTable = (unsigned int *)*VECTOR_TABLE_ptr; + debugErr = ConfigDebugErr_ptr; + ramSize = *(_DWORD *)IMG2_RAM_END_ptr[0] - *(_DWORD *)IMG2_RAM_START_ptr[0]; + *(_DWORD *)ConfigDebugWarn_ptr[0] = 0; + *(_DWORD *)ConfigDebugInfo_ptr = 0; + *debugErr = -1; + if (!XIP_SYSTEM_DATA.UlogDbgEn) *debugErr = 0; + if ((*debugErr & 0x40000000) != 0) j_DiagPrintf(aImg1Enter); + j__memset(*ramDest, 0, ramSize); + BYTE4(v33[0]) = count_leading_zeros(XIP_SYSTEM_DATA.Spic_Mode); + v14 = count_leading_zeros(XIP_SYSTEM_DATA.Spic_Speed); + if (v14 >= 7) v14 = 6; + FLASH_ConfigureClock((char *)v33 + 4, v11, v12, v13); + if ((*debugErr & 0x40000000) != 0) + j_DiagPrintf("\rread_mode idx:%d, flash_speed idx:%d\n", BYTE4(v33[0]), + v14); + FLASH_DetectAndInitType(BYTE4(v33[0]), v15, v16); + j_FLASH_ClockDiv(v14); + FLASH_SetExtendedConfig(XIP_SYSTEM_DATA.Flash_Status); + v17 = off_80007DC; + v18 = *(int *)((char *)&dword_4 + (_DWORD)(off_80007DC + 2)); + if (v18) j_FLASH_SetStatusBits(v18, 1); + v19 = BYTE4(v33[0]); + byte_25[(_DWORD)v17 + 46] = 0; + v20 = 0; + v21 = v19 == 0; + if (v19) v21 = v19 == 1; + if (v21) { + v20 = 2; + } else { + v22 = v19 == 2; + if (v19 != 2) v22 = v19 == 3; + if (v22) v20 = 1; + } + if (FLASH_CalibrateTimings(v20, v14) == 1) { + if ((*debugErr & 0x40000000) != 0) j_DiagPrintf("\rFLASH CALIB[NEW OK]\n"); + j_FLASH_CalibrationNewCmd(1); + j_FLASH_CalibrationPhaseIdx((unsigned __int8)byte_25[(_DWORD)v17 + 47]); + if (v19) { + switch (v19) { + case 1: + v23 = 107; + break; + case 2: + v23 = 187; + break; + case 3: + v23 = 59; + break; + default: + LABEL_36: + j_FLASH_Init(v20); + goto LABEL_37; + } + } else { + v23 = 235; + } + *(int *)((char *)&dword_8 + (_DWORD)v17) = v23; + goto LABEL_36; + } +LABEL_37: + j_Cache_Enable(1); + v24 = 1; + OTA_SelectBootImage(v33); + if (v27 != 1) { + v25 = *(_DWORD *)&IMG2_OTA1_SIGNATURE_STR_ptr[-24]; + LODWORD(v33[0]) = &IMG2_OTA1_SIGNATURE_STR_ptr[v25]; + v24 = 0; + } + if ((*debugErr & 0x40000000) != 0) + j_DiagPrintf("\rIMG2 DATA[0x%x:%d:0x%x]\n", LODWORD(v33[0]) + 32, + *(_DWORD *)(LODWORD(v33[0]) + 8), + *(_DWORD *)(LODWORD(v33[0]) + 12)); + v28 = *(_DWORD *)(LODWORD(v33[0]) + 12); + if (v28 == -1 || + (v25 = v33[0], v26 = *(_DWORD *)(LODWORD(v33[0]) + 8), v26 > 0x100000)) { + if ((*debugErr & 0x40000000) != 0) j_DiagPrintf("\rIMG2 ADDR Invalid\n"); + goto LABEL_45; + } + j__memcpy(v28, LODWORD(v33[0]) + 32, v26); + v29 = off_8000808; + if ((*debugErr & 0x40000000) != 0) { + j_DiagPrintf("\rIMG2 SIGN[%s(%x)]\n", (const char *)*off_8000808, + *off_8000808); + if ((*debugErr & 0x40000000) != 0) + j_DiagPrintf("\rIMG2 ENTRY[0x%x:0x%x]\n", *VECTOR_TABLE_ptr, + *vectorTable); + } + if (j__strcmp(*v29, aRtkwin_0)) { + if ((*debugErr & 0x40000000) != 0) j_DiagPrintf(aImg2SignInvali); + while (1) + LABEL_45: + j_RtlConsolRom(1000, v25, v26); + } + if (v24) { + v30 = *vectorTable; + if (XIP_SYSTEM_DATA.OTA2_FlashAddr >= *vectorTable) { + while (1) { + if ((*debugErr & 0x40000000) != 0) { + j_DiagPrintf(aOta2ImageError); + if ((*debugErr & 0x40000000) != 0) + j_DiagPrintf( + "\rplease try to rebuild your project to generate OTA2 image " + "again...\n\n\n\n"); + } + j_RtlConsolRom(50000, v30, v26); + } + } + } + FLASH_CheckAndDecryptRDP(); + v31 = ((int (*)(void)) * vectorTable)(); + j_BOOT_RunInitializerArray(v31); + return v33[0]; +} +```