# Ameba-Z (RTL8710BX)

<!-- TOC -->

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

<!-- /TOC -->

## 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];
}
```
