Skip to content

Commit 7082519

Browse files
committed
[realtek-ambz2] Implement OTA
1 parent 3d23211 commit 7082519

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

builder/family/realtek-ambz2.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from os.path import isfile, join
44
from shutil import copyfile
55

6+
from ltchiptool.soc.ambz2.util.models.config import ImageConfig
67
from platformio.platform.base import PlatformBase
78
from platformio.platform.board import PlatformBoardConfig
89
from SCons.Script import DefaultEnvironment, Environment
@@ -15,6 +16,22 @@
1516

1617
COMPONENT_DIR = join("$SDK_DIR", "component")
1718

19+
20+
# Get image decryption public key
21+
def get_public_key(private: bytes) -> bytes:
22+
from ltchiptool.util.curve25519 import X25519PrivateKey
23+
24+
key = X25519PrivateKey.from_private_bytes(private)
25+
return key.public_key()
26+
27+
28+
def encode_for_define(data: bytes) -> str:
29+
# we need to escape both shell and the C string
30+
return '\\"' + "".join(f"\\\\x{byte:02x}" for byte in data) + '\\"'
31+
32+
33+
public_key_bytes = get_public_key(ImageConfig(**board.get("image")).keys.decryption)
34+
1835
# Flags
1936
queue.AppendPublic(
2037
CCFLAGS=[
@@ -39,6 +56,7 @@
3956
("__ARM_ARCH_8M_MAIN__", "1"),
4057
("CONFIG_BUILD_RAM", "1"),
4158
"V8M_STKOVF",
59+
("IMAGE_PUBLIC_KEY", encode_for_define(public_key_bytes)),
4260
],
4361
CPPPATH=[
4462
# allow including <ctype.h> from GCC instead of RTL SDK
@@ -419,17 +437,20 @@
419437
image_part_table = "${BUILD_DIR}/image_part_table.${FLASH_PART_TABLE_OFFSET}.bin"
420438
image_bootloader = "${BUILD_DIR}/image_bootloader.${FLASH_BOOT_OFFSET}.bin"
421439
image_firmware_is = "${BUILD_DIR}/image_firmware_is.${FLASH_OTA1_OFFSET}.bin"
440+
image_firmware_is_ota = "${BUILD_DIR}/image_firmware_is_ota.${FLASH_OTA1_OFFSET}.bin"
422441
env.Replace(
423442
# linker command (dual .bin outputs)
424443
LINK='${LTCHIPTOOL} link2bin ${BOARD_JSON} "" ""',
425444
# UF2OTA input list
426445
UF2OTA=[
427-
# same OTA images for flasher and device
428-
f"{image_firmware_is},{image_firmware_is}=device:ota1,ota2;flasher:ota1,ota2",
446+
# use unmodified image for flasher
447+
f"{image_firmware_is},{image_firmware_is}=flasher:ota1,ota2",
448+
# use patched OTA image for device
449+
f"{image_firmware_is_ota},{image_firmware_is_ota}=device:ota1,ota2",
429450
# having flashed an application image, update the bootloader and partition table (incl. keys)
430451
f"{image_bootloader},{image_bootloader}=flasher:boot,boot",
431452
f"{image_part_table},{image_part_table}=flasher:part_table,part_table",
432453
# clearing headers of the "other" OTA image (hence the indexes are swapped)
433-
f"{image_ota_clear},{image_ota_clear}=device:ota2,ota1;flasher:ota2,ota1",
454+
f"{image_ota_clear},{image_ota_clear}=flasher:ota2,ota1",
434455
],
435456
)

cores/realtek-ambz2/base/api/lt_ota.c

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,130 @@
22

33
#include <libretiny.h>
44
#include <sdk_private.h>
5+
#include <osdep_service.h>
6+
#include <device_lock.h>
7+
8+
// from SDK
9+
extern uint32_t sys_update_ota_get_curr_fw_idx(void);
10+
11+
#define FLASH_SECTOR_SIZE 0x1000
12+
// IMAGE_PUBLIC_KEY is defined by the build script
13+
#define IMAGE_PUBLIC_KEY_OFFSET 32
14+
#define IMAGE_PUBLIC_KEY_LENGTH 32
15+
16+
typedef enum {
17+
INVALID = 0,
18+
DISABLED = 1,
19+
ENABLED = 2,
20+
} lt_ota_image_state_t;
21+
22+
static bool lt_ota_get_image_offset(uint8_t index, uint32_t *offset) {
23+
switch (index) {
24+
case 1:
25+
*offset = FLASH_OTA1_OFFSET;
26+
break;
27+
case 2:
28+
*offset = FLASH_OTA2_OFFSET;
29+
break;
30+
default:
31+
return false;
32+
}
33+
return true;
34+
}
35+
36+
static uint8_t lt_ota_get_other_index(uint8_t index) {
37+
return index ^ 0b11; // 1 -> 2, 2 -> 1
38+
}
39+
40+
static lt_ota_image_state_t lt_ota_get_image_state(uint8_t index) {
41+
uint32_t offset;
42+
if (!lt_ota_get_image_offset(index, &offset))
43+
return INVALID;
44+
45+
uint8_t public_key[IMAGE_PUBLIC_KEY_LENGTH];
46+
uint32_t num_read = lt_flash_read(offset + IMAGE_PUBLIC_KEY_OFFSET, public_key, sizeof(public_key));
47+
if (num_read != sizeof(public_key))
48+
return INVALID;
49+
50+
if (memcmp(public_key, IMAGE_PUBLIC_KEY, sizeof(public_key)) == 0)
51+
return ENABLED;
52+
53+
public_key[0] = ~(public_key[0]);
54+
if (memcmp(public_key, IMAGE_PUBLIC_KEY, sizeof(public_key)) == 0)
55+
return DISABLED;
56+
57+
return INVALID;
58+
}
59+
60+
static bool lt_ota_set_image_enabled(uint8_t index, bool new_enabled) {
61+
uint32_t offset;
62+
if (!lt_ota_get_image_offset(index, &offset))
63+
return false;
64+
65+
_irqL irqL;
66+
uint8_t *header = (uint8_t *)malloc(FLASH_SECTOR_SIZE);
67+
68+
rtw_enter_critical(NULL, &irqL);
69+
device_mutex_lock(RT_DEV_LOCK_FLASH);
70+
flash_stream_read(&lt_flash_obj, offset, FLASH_SECTOR_SIZE, header);
71+
72+
bool enabled = header[IMAGE_PUBLIC_KEY_OFFSET] == IMAGE_PUBLIC_KEY[0];
73+
if (enabled != new_enabled) {
74+
// negate first byte of OTA signature
75+
header[0] = ~(header[0]);
76+
// negate first byte of public key
77+
header[IMAGE_PUBLIC_KEY_OFFSET] = ~(header[IMAGE_PUBLIC_KEY_OFFSET]);
78+
79+
// write to flash
80+
hal_flash_sector_erase(lt_flash_obj.phal_spic_adaptor, offset);
81+
hal_flash_burst_write(lt_flash_obj.phal_spic_adaptor, FLASH_SECTOR_SIZE, offset, header);
82+
}
83+
84+
device_mutex_unlock(RT_DEV_LOCK_FLASH);
85+
rtw_exit_critical(NULL, &irqL);
86+
free(header);
87+
88+
return true;
89+
}
90+
91+
// public interface implementation
592

693
lt_ota_type_t lt_ota_get_type() {
794
return OTA_TYPE_DUAL;
895
}
996

1097
bool lt_ota_is_valid(uint8_t index) {
11-
return false;
98+
return lt_ota_get_image_state(index) != INVALID;
1299
}
13100

14101
uint8_t lt_ota_dual_get_current() {
15-
return 0;
102+
// ambz2 uses virtual memory, so we can't use function address to determine active image
103+
// use the SDK instead
104+
return sys_update_ota_get_curr_fw_idx();
16105
}
17106

18107
uint8_t lt_ota_dual_get_stored() {
19-
return 0;
108+
// bootloader prioritizes FW1 if both are valid
109+
return lt_ota_get_image_state(1) == ENABLED ? 1 : 2;
20110
}
21111

22112
bool lt_ota_switch(bool revert) {
23-
return false;
113+
uint8_t current = lt_ota_dual_get_current();
114+
uint8_t stored = lt_ota_dual_get_stored();
115+
if ((current == stored) == revert)
116+
return true;
117+
118+
uint8_t to_enable = lt_ota_get_other_index(stored);
119+
uint8_t to_disable = stored;
120+
121+
if (!lt_ota_is_valid(to_enable))
122+
return false;
123+
124+
// enable first, so there is always at least one enabled image
125+
if (!lt_ota_set_image_enabled(to_enable, true))
126+
return false;
127+
if (!lt_ota_set_image_enabled(to_disable, false))
128+
return false;
129+
130+
return true;
24131
}

cores/realtek-ambz2/base/lt_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
#define LT_HAS_LWIP2 1
88
#define LT_HAS_MBEDTLS 1
99
#define LT_HAS_PRINTF 1
10+
#define LT_HAS_OTA 1
1011
#define LT_HW_BLE 1

0 commit comments

Comments
 (0)