#include <elf.h>
#include <stdbool.h>
#include "exceptions.h"
#include "os_apilevel.h"
#include "string.h"
#include "os_io.h"
#include "os_id.h"
#include "os_nvm.h"
#include "os_pic.h"
#include "checks.h"
#ifdef HAVE_IO_USB
#include "usbd_ledger.h"
#endif
#ifdef HAVE_BLE
#include "ble_ledger.h"
#include "ble_ledger_profile_apdu.h"
#endif
extern void sample_main(int arg0);
extern void heap_init();
struct SectionSrc;
struct SectionDst;
extern Elf32_Rel _relocs;
extern Elf32_Rel _erelocs;
#ifdef SPECULOS_DEBUGGING
#define PRINTLNC(str) println_c(str)
void println_c(char* str);
#define PRINTHEXC(str, n) printhex_c(str, n)
void printhex_c(char* str, uint32_t m);
#else
#define PRINTLNC(str) while(0)
#define PRINTHEXC(str, n) while(0)
#endif
#ifdef TARGET_NANOS2
# define SYMBOL_ABSOLUTE_VALUE(DST, SYM) \
__asm volatile( \
"movw %[result], #:lower16:" #SYM "\n\t" \
"movt %[result], #:upper16:" #SYM \
: [result] "=r" (DST))
#else
# define SYMBOL_ABSOLUTE_VALUE(DST, SYM) \
__asm volatile( \
"ldr %[result], =" #SYM \
: [result] "=r" (DST))
#endif
#ifdef TARGET_NANOS2
# define SYMBOL_SBREL_ADDRESS(DST, SYM) \
__asm volatile( \
"movw %[result], #:lower16:" #SYM "(sbrel)\n\t" \
"movt %[result], #:upper16:" #SYM "(sbrel)\n\t" \
"add %[result], r9, %[result]" \
: [result] "=r" (DST))
#elif defined(TARGET_NANOX) || defined(TARGET_STAX) || defined(TARGET_FLEX) || defined(TARGET_APEX_P)
# define SYMBOL_SBREL_ADDRESS(DST, SYM) \
__asm volatile( \
"ldr %[result], =" #SYM "(sbrel)\n\t" \
"add %[result], r9, %[result]" \
: [result] "=r" (DST))
#else
# error "unknown machine"
#endif
void link_pass(
size_t sec_len,
struct SectionSrc *sec_src,
struct SectionDst *sec_dst,
int nvram_move_amt,
void* nvram_prev,
void* envram_prev,
int dst_ram)
{
uint32_t buf[128];
typedef typeof(*buf) link_addr_t;
Elf32_Rel* relocs;
SYMBOL_ABSOLUTE_VALUE(relocs, _relocs);
Elf32_Rel* erelocs;
SYMBOL_ABSOLUTE_VALUE(erelocs, _erelocs);
Elf32_Rel *reloc_start = pic(relocs);
Elf32_Rel *reloc_end = ((Elf32_Rel*)pic(erelocs-1)) + 1;
PRINTHEXC("Section base address:", sec_dst);
PRINTHEXC("Section base address runtime:", pic(sec_dst));
for (size_t i = 0; i < sec_len; i += sizeof(buf)) {
bool is_changed = 0;
size_t buf_size = sec_len - i < sizeof(buf)
? sec_len - i
: sizeof(buf);
memcpy(buf, pic(sec_src) + i, buf_size);
link_addr_t page_link_addr = (link_addr_t)sec_dst + i;
PRINTHEXC("Chunk base: ", page_link_addr);
PRINTHEXC("First reloc: ", reloc_start->r_offset);
for (Elf32_Rel* reloc = reloc_start; reloc < reloc_end; reloc++) {
link_addr_t abs_offset = reloc->r_offset;
size_t page_offset = abs_offset - page_link_addr;
size_t word_offset = page_offset / sizeof(*buf);
if (word_offset < sizeof(buf) / sizeof(*buf)) {
PRINTLNC("Possible reloc");
void* old = (void*) buf[word_offset];
if (old >= nvram_prev && old < envram_prev) {
void* new = old + nvram_move_amt;
is_changed |= (old != new);
buf[word_offset] = (uint32_t) new;
}
}
}
if (dst_ram) {
PRINTLNC("Chunk to ram");
memcpy((void*)sec_dst + i, buf, buf_size);
} else if (is_changed) {
PRINTLNC("Chunk to flash");
nvm_write(pic((void *)sec_dst + i), buf, buf_size);
if (memcmp(pic((void *)sec_dst + i), buf, buf_size)) {
try_context_set(NULL);
os_sched_exit(1);
}
} else {
PRINTLNC("Unchanged flash chunk");
}
}
}
void get_link_time_nvram_values(
void** nvram_ptr_p,
void** envram_ptr_p)
{
#if defined(ST33) || defined(ST33K1M5)
__asm volatile("ldr %0, =_nvram":"=r"(*nvram_ptr_p));
__asm volatile("ldr %0, =_envram":"=r"(*envram_ptr_p));
#else
#error "invalid architecture"
#endif
}
void link_pass_ram(
size_t sec_len,
struct SectionSrc *sec_src,
struct SectionDst *sec_dst)
{
void* nvram_ptr;
void* envram_ptr;
get_link_time_nvram_values(&nvram_ptr, &envram_ptr);
void* nvram_current = pic(nvram_ptr);
int nvram_move_amt = nvram_current - nvram_ptr;
link_pass(sec_len, sec_src, sec_dst, nvram_move_amt, nvram_ptr, envram_ptr, true);
}
void link_pass_nvram(
size_t sec_len,
struct SectionSrc *sec_src,
struct SectionDst *sec_dst)
{
void* nvram_ptr;
void* envram_ptr;
get_link_time_nvram_values(&nvram_ptr, &envram_ptr);
void* nvram_current = pic(nvram_ptr);
void** nvram_prev_link_ptr;
SYMBOL_ABSOLUTE_VALUE(nvram_prev_link_ptr, _nvram_prev_run);
void** nvram_prev_val_ptr = (void**)pic(nvram_prev_link_ptr);
void* nvram_prev = *nvram_prev_val_ptr;
void* envram_prev = nvram_prev + (envram_ptr - nvram_ptr);
void* link_pass_in_progress_tag = (void*) 0x1;
if (nvram_prev == link_pass_in_progress_tag) {
os_sched_exit(1);
}
int nvram_move_amt = nvram_current - nvram_prev;
if (nvram_move_amt == 0) {
return;
}
nvm_write(nvram_prev_val_ptr, &link_pass_in_progress_tag, sizeof(void*));
link_pass(sec_len, sec_src, sec_dst, nvram_move_amt, nvram_prev, envram_prev, false);
nvm_write(nvram_prev_val_ptr, &nvram_current, sizeof(void*));
}
void c_reset_bss() {
size_t bss_len;
SYMBOL_ABSOLUTE_VALUE(bss_len, _bss_len);
struct SectionDst* bss;
SYMBOL_SBREL_ADDRESS(bss, _bss);
memset(bss, 0, bss_len);
}
bolos_ux_params_t G_ux_params = {0};
void c_boot_std() {
memset(&G_ux_params, 0, sizeof(G_ux_params));
G_ux_params.ux_id = BOLOS_UX_IO_RESET;
for (uint8_t i = 0; i < 2; i++) {
os_ux(&G_ux_params);
if (os_sched_last_status(TASK_BOLOS_UX) == BOLOS_UX_OK) {
break;
}
}
os_io_init_t init_io;
init_io.usb.pid = 0;
init_io.usb.vid = 0;
init_io.usb.class_mask = 0;
memset(init_io.usb.name, 0, sizeof(init_io.usb.name));
#ifdef HAVE_IO_USB
init_io.usb.class_mask = USBD_LEDGER_CLASS_HID;
#ifdef HAVE_WEBUSB
init_io.usb.class_mask |= USBD_LEDGER_CLASS_WEBUSB;
#endif #ifdef HAVE_IO_U2F
init_io.usb.class_mask |= USBD_LEDGER_CLASS_HID_U2F;
init_io.usb.hid_u2f_settings.protocol_version = 2;
init_io.usb.hid_u2f_settings.major_device_version_number = 0;
init_io.usb.hid_u2f_settings.minor_device_version_number = 1;
init_io.usb.hid_u2f_settings.build_device_version_number = 0;
init_io.usb.hid_u2f_settings.capabilities_flag = 0;
#endif #endif
init_io.ble.profile_mask = 0;
#ifdef HAVE_BLE
init_io.ble.profile_mask = BLE_LEDGER_PROFILE_APDU;
#endif
os_io_init(&init_io);
os_io_start();
heap_init();
}
int c_main(int arg0) {
__asm volatile("cpsie i");
size_t rodata_len;
SYMBOL_ABSOLUTE_VALUE(rodata_len, _rodata_len);
struct SectionSrc* rodata_src;
SYMBOL_ABSOLUTE_VALUE(rodata_src, _rodata_src);
struct SectionDst* rodata;
SYMBOL_ABSOLUTE_VALUE(rodata, _rodata);
link_pass_nvram(rodata_len, rodata_src, rodata);
size_t data_len;
SYMBOL_ABSOLUTE_VALUE(data_len, _data_len);
struct SectionSrc* sidata_src;
SYMBOL_ABSOLUTE_VALUE(sidata_src, _sidata_src);
struct SectionDst* data;
__asm volatile("mov %[result],r9" : [result] "=r" (data));
link_pass_ram(data_len, sidata_src, data);
if (arg0 == 0)
c_reset_bss();
try_context_set(NULL);
for(;;) {
BEGIN_TRY {
TRY {
if (arg0 == 0)
c_boot_std();
sample_main(arg0);
}
CATCH(EXCEPTION_IO_RESET) {
continue;
}
CATCH_ALL {
break;
}
FINALLY {
}
}
END_TRY;
}
return 0;
}