#include <elf.h>
#include <stdbool.h>
#include "exceptions.h"
#include "os_apilevel.h"
#include "string.h"
#include "seproxyhal_protocol.h"
#include "os_id.h"
#include "os_io_usb.h"
#include "os_nvm.h"
#include "os_pic.h"
#include "checks.h"
#ifdef HAVE_BLE
#include "ledger_ble.h"
#endif
extern void sample_main();
extern void heap_init();
struct SectionSrc;
struct SectionDst;
io_seph_app_t G_io_app;
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)
# define SYMBOL_SBREL_ADDRESS(DST, SYM) \
__asm volatile( \
"ldr %[result], =" #SYM "(sbrel)\n\t" \
"add %[result], r9, %[result]" \
: [result] "=r" (DST))
#elif defined(TARGET_NANOS)
# define SYMBOL_SBREL_ADDRESS(DST, SYM) \
SYMBOL_ABSOLUTE_VALUE(DST, SYM)
#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)
{
#ifdef TARGET_NANOS
uint32_t buf[16];
#else
uint32_t buf[128];
#endif
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(ST31)
SYMBOL_ABSOLUTE_VALUE(*nvram_ptr_p, _nvram);
SYMBOL_ABSOLUTE_VALUE(*envram_ptr_p, _envram);
#elif 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*));
}
#ifdef HAVE_CCID
#include "usbd_ccid_if.h"
uint8_t G_io_apdu_buffer[260];
#endif
int c_main(void) {
__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);
size_t bss_len;
SYMBOL_ABSOLUTE_VALUE(bss_len, _bss_len);
struct SectionDst* bss;
SYMBOL_SBREL_ADDRESS(bss, _bss);
memset(bss, 0, bss_len);
try_context_set(NULL);
for(;;) {
BEGIN_TRY {
TRY {
#ifdef HAVE_MCU_PROTECT
unsigned char c[4];
c[0] = SEPROXYHAL_TAG_MCU;
c[1] = 0;
c[2] = 1;
c[3] = SEPROXYHAL_TAG_MCU_TYPE_PROTECT;
io_seproxyhal_spi_send(c, 4);
#ifdef HAVE_BLE
unsigned int plane = G_io_app.plane_mode;
#endif
#endif
memset(&G_io_app, 0, sizeof(G_io_app));
#ifdef HAVE_BLE
G_io_app.plane_mode = plane;
#endif
G_io_app.apdu_state = APDU_IDLE;
G_io_app.apdu_length = 0;
G_io_app.apdu_media = IO_APDU_MEDIA_NONE;
G_io_app.ms = 0;
io_usb_hid_init();
USB_power(0);
USB_power(1);
#ifdef HAVE_CCID
io_usb_ccid_set_card_inserted(1);
#endif
#ifdef HAVE_BLE
LEDGER_BLE_init();
#endif
#if !defined(HAVE_BOLOS) && defined(HAVE_PENDING_REVIEW_SCREEN)
check_audited_app();
#endif
heap_init();
sample_main();
}
CATCH(EXCEPTION_IO_RESET) {
continue;
}
CATCH_ALL {
break;
}
FINALLY {
}
}
END_TRY;
}
return 0;
}