Files
rtl8710bx-re/docs/rom-boot-seq.md
2024-12-05 06:40:11 +06:00

32 KiB

Ameba-Z (RTL8710BX)

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

Boot Process

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

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

// 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

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

// 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 viaIMG2_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:

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,
};
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
LDR     R3, =0x40000038    ; Check flash encryption status
LDR     R3, [R3]
LSLS    R4, R3, #0x12      ; Test bit 13
BMI     init_flash_encryption
  1. calc image location
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)
  1. load image params
LDR.W   R8, [R5,#(dword_8000C90 - 0x8000C88)] ; Load size (0x468)
LDR     R7, [R5,#(dword_8000C94 - 0x8000C88)] ; Load dest (0x10002000)
  1. memcpy
memcpy(destination_addr, source_addr + 32, size);
// Copies from 0x8000C88 to 0x10002000
  1. entrypoint selection
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

__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

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:

; 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

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()

; 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]

->

// 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

// 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

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
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:

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

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:

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

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:

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

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

__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];
}