diff --git a/epicardium/l0der/elf.h b/epicardium/l0der/elf.h new file mode 100644 index 0000000000000000000000000000000000000000..ce90fc270aef8b145d583c15cd93a4eafeadee40 --- /dev/null +++ b/epicardium/l0der/elf.h @@ -0,0 +1,96 @@ +#pragma once + +/* + * 32-bit ELF structures. + * + * ref: Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification + * Version 1.2, May 1995 + * http://refspecs.linuxbase.org/elf/elf.pdf + * + * ref: ELF for the ARM Architecture + * ARM IHI 0044F, current through ABI release 2.10, 24th November 2015 + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf + * + */ + +#include <stdint.h> + +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; + +#define EI_NIDENT 16 + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +#define ET_DYN 3 // Shared object file or PIE binary + +#define EM_ARM 40 + +#define EV_CURRENT 1 + +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +#define ELFCLASS32 1 + +#define ELFDATA2LSB 1 + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +#define SHT_RELA 4 +#define SHT_REL 9 + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) + +#define R_ARM_RELATIVE 23 + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +#define PT_LOAD 1 +#define PT_INTERP 3 diff --git a/epicardium/l0der/l0der.c b/epicardium/l0der/l0der.c new file mode 100644 index 0000000000000000000000000000000000000000..93a0ecba597bcaa8cef0af29a0362fddb71cdf87 --- /dev/null +++ b/epicardium/l0der/l0der.c @@ -0,0 +1,491 @@ +#include "l0der/l0der.h" + +#include <stdio.h> +#include <string.h> +#include <ff.h> + +#include "epicardium.h" +#include "l0der/elf.h" +#include "modules/log.h" + +/* + * Read an ELF header, check E_IDENT. + */ +static int _read_elf_header(FIL *fp, Elf32_Ehdr *hdr) +{ + f_lseek(fp, 0); + + unsigned int read; + FRESULT fres = f_read(fp, hdr, sizeof(Elf32_Ehdr), &read); + if (fres != FR_OK) { + LOG_ERR("l0der", "_read_elf_header: f_read failed: %d", fres); + return -1; + } + + if (read != sizeof(Elf32_Ehdr)) { + LOG_ERR("l0der", "_read_elf_header: file truncated"); + return -1; + } + + if (hdr->e_ident[0] != ELFMAG0 || + hdr->e_ident[1] != ELFMAG1 || + hdr->e_ident[2] != ELFMAG2 || + hdr->e_ident[3] != ELFMAG3) { + LOG_ERR("l0der", "_read_elf_header: not an ELF file"); + return -1; + } + + if (hdr->e_ident[4] != ELFCLASS32) { + LOG_ERR("l0der", "_read_elf_header: not a 32-bit ELF"); + return -1; + } + + if (hdr->e_ident[5] != ELFDATA2LSB) { + LOG_ERR("l0der", "_read_elf_header: not a little-endian ELF"); + return -1; + } + + if (hdr->e_ident[6] != EV_CURRENT) { + LOG_ERR("l0der", "_read_elf_header: not a v1 ELF"); + return -1; + } + + if (hdr->e_ehsize < sizeof(Elf32_Ehdr)) { + LOG_ERR("l0der", "_raed_elf_header: header too small"); + return -1; + } + + return 0; +} + +/* + * Read an ELF program header header. + */ +static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr) +{ + FRESULT fres; + + if ((fres = f_lseek(fp, phdr_addr)) != FR_OK) { + LOG_ERR("l0der", "_read_program_header: could not seek to 0x%lx: %d", phdr_addr, fres); + return -EIO; + } + + unsigned int read; + if ((fres = f_read(fp, phdr, sizeof(Elf32_Phdr), &read)) != FR_OK || read < sizeof(Elf32_Phdr)) { + LOG_ERR("l0der", "_read_program_header: could not read phdr: %d", fres); + return -EIO; + } + + return 0; +} + +/* + * Read an ELF program header header. + */ +static int _read_section_header(FIL *fp, uint32_t shdr_addr, Elf32_Shdr *shdr) +{ + FRESULT fres; + + if ((fres = f_lseek(fp, shdr_addr)) != FR_OK) { + LOG_ERR("l0der", "_read_section_header: could not seek to 0x%lx: %d", shdr_addr, fres); + return -EIO; + } + + unsigned int read; + if ((fres = f_read(fp, shdr, sizeof(Elf32_Shdr), &read)) != FR_OK || read < sizeof(Elf32_Shdr)) { + LOG_ERR("l0der", "_read_section_header: could not read shdr (0x%x bytes) at %08lx: %d, got 0x%x bytes", + sizeof(Elf32_Shdr), shdr_addr, fres, read); + return -EIO; + } + + return 0; +} + +/* + * Check an ELF program header. + * + * This function ensures basic memory sanity of a program header / segment. + * It ensures that it points to a file region that is contained within the file fully. + */ +static int _check_program_header(FIL *fp, Elf32_Phdr *phdr) { + size_t size = f_size(fp); + + // Check file size/offset. + uint32_t file_start = phdr->p_offset; + uint32_t file_limit = phdr->p_offset + phdr->p_filesz; + if (file_limit < file_start) { + LOG_ERR("l0der", "_check_program_header: file size overflow"); + return -ENOEXEC; + } + if (file_limit > size) { + LOG_ERR("l0der", "_check_program_header: extends past end of file"); + return -ENOEXEC; + } + + if (phdr->p_type == PT_LOAD) { + // Check mem/file size. + if (phdr->p_filesz > phdr->p_memsz) { + LOG_ERR("l0der", "_check_program_header: file size larger than memory size"); + return -ENOEXEC; + } + + uint32_t mem_start = phdr->p_vaddr; + uint32_t mem_limit = phdr->p_vaddr + phdr->p_memsz; + + if (mem_limit < mem_start) { + LOG_ERR("l0der", "_check_program_header: mem size overflow"); + return -ENOEXEC; + } + } + + return 0; +} + +/* + * Check an ELF section header. + * + * This function ensures basic memory sanity of a section header. + * It ensures that it points to a file region that is contained within the file fully. + */ +static int _check_section_header(FIL *fp, Elf32_Shdr *shdr) { + size_t size = f_size(fp); + + // Check file size/offset. + uint32_t file_start = shdr->sh_offset; + uint32_t file_limit = shdr->sh_offset + shdr->sh_size; + if (file_limit < file_start) { + LOG_ERR("l0der", "_check_section_header: file size overflow"); + return -ENOEXEC; + } + if (file_limit > size) { + LOG_ERR("l0der", "_check_section_header: extends past end of file"); + return -ENOEXEC; + } + + return 0; +} + +static const char *_interpreter = "card10-l0dable"; + +static int _check_interp(FIL *fp, Elf32_Phdr *phdr) +{ + uint32_t buffer_size = 64; + char interp[buffer_size]; + memset(interp, 0, buffer_size); + + if (phdr->p_filesz > buffer_size) { + LOG_ERR("l0der", "_check_interp: interpreter size too large"); + return -1; + } + + FRESULT fres; + if ((fres = f_lseek(fp, phdr->p_offset)) != FR_OK) { + LOG_ERR("l0der", "_check_interp: could not seek to 0x%lx: %d", phdr->p_offset, fres); + return -1; + } + + unsigned int read; // unused (we don't care if the read gets truncated) + if ((fres = f_read(fp, interp, buffer_size, &read)) != FR_OK) { + LOG_ERR("l0der", "_check_interp: could not read segment %d", fres); + return -1; + } + + if (strncmp(interp, _interpreter, strlen(_interpreter)) != 0) { + LOG_ERR("l0der", "_check_interp: invalid interpreter, want card10-l0dable"); + return -1; + } + + return 0; +} + +static int _get_load_addr(uint32_t image_start, uint32_t image_limit, void **load) +{ + uint32_t image_size = image_limit - image_start; + + // ref: Documentation/memorymap.rst + uint32_t core1_mem_start = 0x20040000; + uint32_t core1_mem_limit = 0x20080000; + uint32_t core1_mem_size = core1_mem_limit - core1_mem_start; + + if (image_size > core1_mem_size) { + LOG_ERR("l0der", "_get_load_addr: image too large (need 0x%08lx bytes, have %08lx", + image_size, core1_mem_size); + return -ENOMEM; + } + + *load = (void *)core1_mem_start; + + return 0; +} + +static int _load_segment(FIL *fp, void *image_load_addr, Elf32_Phdr *phdr) +{ + uint32_t segment_start = (uint32_t)image_load_addr + phdr->p_vaddr; + uint32_t segment_limit = segment_start + phdr->p_memsz; + + LOG_INFO("l0der", "Segment %08lx-%08lx: 0x%lx bytes from file", + segment_start, segment_limit, phdr->p_filesz); + memset((void *)segment_start, 0, phdr->p_memsz); + + FRESULT fres; + unsigned int read; + + if ((fres = f_lseek(fp, phdr->p_offset)) != FR_OK) { + LOG_ERR("l0der", "_load_segment: seek failed: %d", fres); + return -EIO; + } + + if ((fres = f_read(fp, (void *)segment_start, phdr->p_filesz, &read)) != FR_OK || read != phdr->p_filesz) { + LOG_ERR("l0der", "_load_segment: read failed"); + return -EIO; + } + + return 0; +} + +static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) { + int res; + FRESULT fres; + Elf32_Shdr shdr; + Elf32_Rel rel; + + // Go through all relocation sections. + for (int i = 0; i < hdr->e_shnum; i++) { + uint32_t shdr_addr = hdr->e_shoff + (i * hdr->e_shentsize); + if ((res = _read_section_header(fp, shdr_addr, &shdr)) != 0) { + return res; + } + + // We don't support RELA (relocation with addend) sections (yet?). + if (shdr.sh_type == SHT_RELA) { + LOG_ERR("l0der", "_run_relocations: found unsupported SHT_RELA section, bailing"); + return -ENOEXEC; + } + + if (shdr.sh_type != SHT_REL) { + continue; + } + + if ((res = _check_section_header(fp, &shdr)) != 0) { + return res; + } + + if ((shdr.sh_size % sizeof(Elf32_Rel)) != 0) { + LOG_ERR("l0der", "_run_relocations: SHT_REL section with invalid size: %ld", shdr.sh_size); + return -EIO; + } + uint32_t reloc_count = shdr.sh_size / sizeof(Elf32_Rel); + + // Read relocations one by one. + if ((fres = f_lseek(fp, shdr.sh_offset)) != FR_OK) { + LOG_ERR("l0der", "_run_relocations: seek to first relocation (at 0x%lx) failed", shdr.sh_offset); + return -EIO; + } + + for (int j = 0; j < reloc_count; j++) { + unsigned int read; + if ((fres = f_read(fp, &rel, sizeof(Elf32_Rel), &read)) != FR_OK || read != sizeof(Elf32_Rel)) { + LOG_ERR("l0der", "_run_relocations: relocation read failed: %d", fres); + return -EIO; + } + + uint8_t type = ELF32_R_TYPE(rel.r_info); + switch (type) { + case R_ARM_RELATIVE: + if ((rel.r_offset % 4) != 0) { + LOG_ERR("l0der", "_run_relocations: R_ARM_RELATIVE address must be 4-byte aligned"); + return -ENOEXEC; + } + // TODO(q3k): check whether offset is contained in binary. + volatile uint32_t *addr = (uint32_t *)(rel.r_offset + load_addr); + *addr += (uint32_t)load_addr; + break; + default: + LOG_ERR("l0der", "_run_relocations: unsupported relocation type %d", type); + return -ENOEXEC; + } + } + } + + return 0; +} + +static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info) +{ + int res; + + // First pass over program headers: sanity check sizes and calculate + // memory image bounds. l0der currently only supports loading the image into + // the core1 address space, that is from 0x1008_0000 to 0x1010_0000. Thus, + // we need to ensure that all the LOADable segments can fit within this + // range. + + uint32_t image_start = 0xFFFFFFFF; + uint32_t image_limit = 0x0; + + Elf32_Phdr phdr; + + int status_interp = -1; + + for (int i = 0; i < hdr->e_phnum; i++) { + uint32_t phdr_addr = hdr->e_phoff + (i * hdr->e_phentsize); + if ((res = _read_program_header(fp, phdr_addr, &phdr)) != 0) { + return res; + } + + if ((res = _check_program_header(fp, &phdr)) != 0) { + return res; + } + + if (phdr.p_type == PT_INTERP) { + status_interp = _check_interp(fp, &phdr); + continue; + } + + if (phdr.p_type == PT_LOAD) { + // Check alignment request. + if (phdr.p_align > 4) { + LOG_ERR("l0der", "_load_pie: phdr %d alignment too strict", i); + return -ENOEXEC; + } + + uint32_t mem_start = phdr.p_vaddr; + uint32_t mem_limit = phdr.p_vaddr + phdr.p_memsz; + + // Record memory usage. + if (mem_start < image_start) { + image_start = mem_start; + } + if (mem_limit > image_limit) { + image_limit = mem_limit; + } + } + } + + if (status_interp != 0) { + LOG_ERR("l0der", "_load_pie: not a card10 l0dable"); + return -ENOEXEC; + } + + + if (image_limit < image_start) { + LOG_ERR("l0der", "_load_pie: no loadable segments"); + return -ENOEXEC; + } + + LOG_INFO("l0der", "Image bounds %08lx - %08lx", image_start, image_limit); + + void *load_addr; + if ((res = _get_load_addr(image_start, image_limit, &load_addr)) != 0) { + return res; + } + + LOG_INFO("l0der", "Loading at %08lx", (uint32_t)load_addr); + + // Second pass through program headers: load all LOAD segments. + + for (int i = 0; i < hdr->e_phnum; i++) { + uint32_t phdr_addr = hdr->e_phoff + (i * hdr->e_phentsize); + if ((res = _read_program_header(fp, phdr_addr, &phdr)) != 0) { + return res; + } + + if (phdr.p_type != PT_LOAD) { + continue; + } + + if ((res = _load_segment(fp, load_addr, &phdr)) != 0) { + return res; + } + } + + // Run relocations. + + if ((res = _run_relocations(fp, load_addr, hdr)) != 0) { + return res; + } + + uint32_t image_entrypoint = (uint32_t)load_addr + hdr->e_entry; + LOG_INFO("l0der", "Entrypoint (ISR Vector) at %08lx", image_entrypoint); + + info->isr_vector = (void *)image_entrypoint; + + return 0; +} + +int l0der_load_path(const char *path, struct l0dable_info *info) +{ + FIL fh; + + FRESULT fres = f_open(&fh, path, FA_OPEN_EXISTING|FA_READ); + if (fres != FR_OK) { + LOG_ERR("l0der", "l0der_load_path: could not open ELF file %s: %d", path, fres); + return -ENOENT; + } + + int size = f_size(&fh); + + int res = 0; + + // Load ELF header and ensure it's somewhat sane. + + Elf32_Ehdr hdr; + if (_read_elf_header(&fh, &hdr) != 0) { + res = -EINVAL; + goto done; + } + + // Sanitize segments. + + uint32_t ph_start = hdr.e_phoff; + uint32_t ph_limit = hdr.e_phoff + (hdr.e_phnum * hdr.e_phentsize); + if (ph_limit < ph_start) { + LOG_ERR("l0der", "l0der_load_path: invalid program header count/size: overflow"); + return -ENOEXEC; + } + if (ph_limit - ph_start == 0) { + LOG_ERR("l0der", "l0der_load_path: no segments"); + return -ENOEXEC; + } + if (ph_limit > size) { + LOG_ERR("l0der", "l0der_load_path: program header table extends past end of file"); + return -ENOEXEC; + } + if (hdr.e_phentsize < sizeof(Elf32_Phdr)) { + LOG_ERR("l0der", "l0der_load_path: invalid program header table entry size"); + return -ENOEXEC; + } + + // Sanitize sections. + + uint32_t sh_start = hdr.e_shoff; + uint32_t sh_limit = hdr.e_shoff + (hdr.e_shnum + hdr.e_shentsize); + if (sh_limit < sh_start) { + LOG_ERR("l0der", "l0der_load_path: invalid section header count/size: overflow"); + return -ENOEXEC; + } + if (sh_limit > size) { + LOG_ERR("l0der", "l0der_load_path: section header table extends past end of file"); + return -ENOEXEC; + } + if (hdr.e_shentsize < sizeof(Elf32_Shdr)) { + LOG_ERR("l0der", "l0der_load_path: invalid section header table entry size"); + return -ENOEXEC; + } + + // Check whether it's something that we can load. + + if (hdr.e_type == ET_DYN && hdr.e_machine == EM_ARM && hdr.e_version == EV_CURRENT) { + LOG_INFO("l0der", "Loading PIE l0dable %s ...", path); + res = _load_pie(&fh, &hdr, info); + goto done; + } else { + LOG_ERR("l0der", "l0der_load_path: %s: not an ARM PIE, cannot load.", path); + res = -ENOEXEC; + goto done; + } + +done: + f_close(&fh); + return res; +} diff --git a/epicardium/l0der/l0der.h b/epicardium/l0der/l0der.h new file mode 100644 index 0000000000000000000000000000000000000000..80ce3242dc1f239a7e9c5fa3eef50e53fac2868b --- /dev/null +++ b/epicardium/l0der/l0der.h @@ -0,0 +1,27 @@ +#pragma once + +/* + * l0der, the l0dable loader. + * + * l0der is the ELF loader responsible for retrieving a l0dable from FAT and + * into memory for core1 to execute. + * + * l0dables are PIE ELF binaries. They can be loaded anywhere into memory, + * although for now we load them at a static address (but that might change + * with address space evolution and/or multi-app / resident app support. + * + */ + +struct l0dable_info { + /** The address of the entry ISR vector. */ + void *isr_vector; +}; + +/** + * Load a l0dable into memory. + * + * :param const char *path: Path of l0dable on FAT filesystem. + * :param l0dable_info l0dable: Information about loaded l0dable. + * :returns: ``0`` on success or a negative value on error. + */ +int l0der_load_path(const char *path, struct l0dable_info *l0dable); diff --git a/epicardium/l0der/meson.build b/epicardium/l0der/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..e727bcfa223a0a6b2fd4d9dd0bc1f1edc780d269 --- /dev/null +++ b/epicardium/l0der/meson.build @@ -0,0 +1,3 @@ +l0der_sources = files( + 'l0der.c', +) diff --git a/epicardium/main.c b/epicardium/main.c index 75bb9c79537a82de067d2618d1f3071348d2b032..a44f73d81e15b04882c8e27e8394585b7f055d2d 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -9,6 +9,7 @@ #include "pmic.h" #include "leds.h" #include "api/dispatcher.h" +#include "l0der/l0der.h" #include "modules/modules.h" #include "modules/log.h" #include "modules/stream.h" @@ -99,8 +100,16 @@ int main(void) LOG_INFO("startup", "Initializing dispatcher ..."); api_dispatcher_init(); - LOG_INFO("startup", "Starting core1 payload ..."); - core1_start(); + LOG_INFO("startup", "Testing l0der ..."); + struct l0dable_info info; + int res = l0der_load_path("blinky.elf", &info); + if (res != 0) { + LOG_ERR("startup", "l0der failed: %d\n", res); + } else { + LOG_INFO("startup", "Starting core1 payload ..."); + core1_start(info.isr_vector); + } + LOG_INFO("startup", "Starting FreeRTOS ..."); vTaskStartScheduler(); diff --git a/epicardium/meson.build b/epicardium/meson.build index ce584e64f6b54b6ae4f32ec19e81e52b98f42be4..c5dc701d6164c79246e9a6aa8bc761c01626b32e 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -66,12 +66,15 @@ freertos = static_library( subdir('modules/') +subdir('l0der/') + elf = executable( name + '.elf', 'cdcacm.c', 'main.c', 'support.c', module_sources, + l0der_sources, dependencies: [libcard10, max32665_startup_core0, maxusb, libff13], link_with: [api_dispatcher_lib, freertos], link_whole: [max32665_startup_core0_lib, board_card10_lib, newlib_heap_lib], diff --git a/hw-tests/dual-core/main.c b/hw-tests/dual-core/main.c index adc1076c6b0ff468da44cdf2fc98efb7a44efeb1..bee6db9fbbd6ab8f37e75b8cafe1893a664be7bf 100644 --- a/hw-tests/dual-core/main.c +++ b/hw-tests/dual-core/main.c @@ -31,7 +31,7 @@ int main(void) int h = 0; // Release core1 - core1_start(); + core1_start((void *)0x10080000); while (1) { #define NUM 15 diff --git a/l0dables/blinky/main.c b/l0dables/blinky/main.c new file mode 100644 index 0000000000000000000000000000000000000000..7d9c1e10002bbc24496818321fe2f3021e40f905 --- /dev/null +++ b/l0dables/blinky/main.c @@ -0,0 +1,80 @@ +#include "max32665.h" +#include "mxc_sys.h" +#include "gcr_regs.h" +#include "icc_regs.h" +#include "pwrseq_regs.h" + +#include "epicardium.h" + +uint32_t SystemCoreClock = HIRC_FREQ >> 1; +volatile uint32_t tombstone = 0; + +void SystemCoreClockUpdate(void) +{ + uint32_t base_freq, div, clk_src; + + // Determine the clock source and frequency + clk_src = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CLKSEL); + switch (clk_src) + { + case MXC_S_GCR_CLKCN_CLKSEL_HIRC: + base_freq = HIRC_FREQ; + break; + case MXC_S_GCR_CLKCN_CLKSEL_XTAL32M: + base_freq = XTAL32M_FREQ; + break; + case MXC_S_GCR_CLKCN_CLKSEL_LIRC8: + base_freq = LIRC8_FREQ; + break; + case MXC_S_GCR_CLKCN_CLKSEL_HIRC96: + base_freq = HIRC96_FREQ; + break; + case MXC_S_GCR_CLKCN_CLKSEL_HIRC8: + base_freq = HIRC8_FREQ; + break; + case MXC_S_GCR_CLKCN_CLKSEL_XTAL32K: + base_freq = XTAL32K_FREQ; + break; + default: + // Values 001 and 111 are reserved, and should never be encountered. + base_freq = HIRC_FREQ; + break; + } + // Clock divider is retrieved to compute system clock + div = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_PSC) >> MXC_F_GCR_CLKCN_PSC_POS; + + SystemCoreClock = base_freq >> div; +} + +int main(void) { + tombstone = 0x10; + // Enable FPU. + SCB->CPACR |= SCB_CPACR_CP10_Msk | SCB_CPACR_CP11_Msk; + __DSB(); + __ISB(); + tombstone = 0x11; + + // Enable ICache1 Clock + MXC_GCR->perckcn1 &= ~(1 << 22); + tombstone = 0x12; + + // Invalidate cache and wait until ready + MXC_ICC1->invalidate = 1; + while (!(MXC_ICC1->cache_ctrl & MXC_F_ICC_CACHE_CTRL_CACHE_RDY)); + tombstone = 0x13; + + // Enable Cache + MXC_ICC1->cache_ctrl |= MXC_F_ICC_CACHE_CTRL_CACHE_EN; + tombstone = 0x14; + + SystemCoreClockUpdate(); + tombstone = 0x15; + + /* TMR5 is used to notify on keyboard interrupt */ + //NVIC_EnableIRQ(TMR5_IRQn); + tombstone = 0x16; + + epic_leds_set(0, 255, 255, 255); + tombstone = 0x17; + for (;;) {} +} diff --git a/l0dables/blinky/meson.build b/l0dables/blinky/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..9a5c199dcaf503aafb2b966256b56938ae22be4e --- /dev/null +++ b/l0dables/blinky/meson.build @@ -0,0 +1,13 @@ +name = 'blinky' + +elf = executable( + name + '.elf', + 'main.c', + build_by_default: true, + dependencies: [l0dable_startup, api_caller], + link_whole: [l0dable_startup_lib], + link_args: [ + '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map', + ], + pie: true, +) diff --git a/l0dables/core1.ld b/l0dables/core1.ld new file mode 100644 index 0000000000000000000000000000000000000000..4a7ff28de2bc564c5840f8ef7041d8f095567bc0 --- /dev/null +++ b/l0dables/core1.ld @@ -0,0 +1,91 @@ +/*MEMORY { + SPIX (rx) : ORIGIN = 0x08000000, LENGTH = 128M + FLASH (rx) : ORIGIN = 0x10080000, LENGTH = 512k + SRAM (rwx) : ORIGIN = 0x20040000, LENGTH = 256k + SPID (r) : ORIGIN = 0x80000000, LENGTH = 512M +}*/ + +ENTRY(isr_vector); + +CARD10_CORE1_START = 0x20040000; +CARD10_CORE1_LIMIT = 0x20080000; + +PHDRS +{ + header PT_PHDR PHDRS ; + interp PT_INTERP ; + text PT_LOAD FILEHDR PHDRS ; + data PT_LOAD ; + dynamic PT_DYNAMIC ; +} + +SECTIONS { + /* . = CARD10_CORE1_START; */ + + . = SIZEOF_HEADERS; + + .cinterp : + { + *(.cinterp); + } :interp :text + + .text : + { + *(.text*) + *(.rodata*) + + KEEP(*(.init)) + KEEP(*(.fini)) + } :text + + .data : + { + . = ALIGN(4); + *(.data*) + + . = ALIGN(4); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + } :data + + .dynamic : + { + *(.dynamic) + } :data :dynamic + + .bss : + { + . = ALIGN(4); + CARD10_BSS_OFF_START = .; + *(.bss*) + *(COMMON) + CARD10_BSS_LIMIT = .; + } :data + + . = ALIGN(4096); + .stack : + { + CARD10_STACK_START = .; + . += 4096; + CARD10_STACK_LIMIT = .; + } :data + + /DISCARD/ : + { + /* *(*) */ + /* *(.symtab) */ + /* *(.strtab) */ + /* *(.shstrtab) */ + /**(.interp)*/ + *(.comment) + *(.interp) + } +} diff --git a/l0dables/crt.s b/l0dables/crt.s new file mode 100644 index 0000000000000000000000000000000000000000..732c9c988ceee5640348ba9d92a356ab6728f806 --- /dev/null +++ b/l0dables/crt.s @@ -0,0 +1,265 @@ + .syntax unified + .arch armv7-m + + .section .data + .align 2 + .globl isr_vector +isr_vector: + .long CARD10_STACK_LIMIT /* Top of Stack */ + .long Reset_Handler /* Reset Handler */ + .long NMI_Handler /* NMI Handler */ + .long HardFault_Handler /* Hard Fault Handler */ + .long MemManage_Handler /* MPU Fault Handler */ + .long BusFault_Handler /* Bus Fault Handler */ + .long UsageFault_Handler /* Usage Fault Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long SVC_Handler /* SVCall Handler */ + .long 0 /* Reserved */ /* @TODO: Is this the Debug Montior Interrupt? */ + .long 0 /* Reserved */ + .long PendSV_Handler /* PendSV Handler */ + .long SysTick_Handler /* SysTick Handler */ + + /* Device-specific Interrupts */ + .long PF_IRQHandler /* 0x10 0x0040 16: Power Fail */ + .long WDT0_IRQHandler /* 0x11 0x0044 17: Watchdog 0 */ + .long USB_IRQHandler /* 0x12 0x0048 18: USB */ + .long RTC_IRQHandler /* 0x13 0x004C 19: RTC */ + .long TRNG_IRQHandler /* 0x14 0x0050 20: True Random Number Generator */ + .long TMR0_IRQHandler /* 0x15 0x0054 21: Timer 0 */ + .long TMR1_IRQHandler /* 0x16 0x0058 22: Timer 1 */ + .long TMR2_IRQHandler /* 0x17 0x005C 23: Timer 2 */ + .long TMR3_IRQHandler /* 0x18 0x0060 24: Timer 3*/ + .long TMR4_IRQHandler /* 0x19 0x0064 25: Timer 4*/ + .long TMR5_IRQHandler /* 0x1A 0x0068 26: Timer 5 */ + .long RSV11_IRQHandler /* 0x1B 0x006C 27: Reserved */ + .long RSV12_IRQHandler /* 0x1C 0x0070 28: Reserved */ + .long I2C0_IRQHandler /* 0x1D 0x0074 29: I2C0 */ + .long UART0_IRQHandler /* 0x1E 0x0078 30: UART 0 */ + .long UART1_IRQHandler /* 0x1F 0x007C 31: UART 1 */ + .long SPI1_IRQHandler /* 0x20 0x0080 32: SPI1 */ + .long SPI2_IRQHandler /* 0x21 0x0084 33: SPI2 */ + .long RSV18_IRQHandler /* 0x22 0x0088 34: Reserved */ + .long RSV19_IRQHandler /* 0x23 0x008C 35: Reserved */ + .long ADC_IRQHandler /* 0x24 0x0090 36: ADC */ + .long RSV21_IRQHandler /* 0x25 0x0094 37: Reserved */ + .long RSV22_IRQHandler /* 0x26 0x0098 38: Reserved */ + .long FLC0_IRQHandler /* 0x27 0x009C 39: Flash Controller */ + .long GPIO0_IRQHandler /* 0x28 0x00A0 40: GPIO0 */ + .long GPIO1_IRQHandler /* 0x29 0x00A4 41: GPIO2 */ + .long RSV26_IRQHandler /* 0x2A 0x00A8 42: GPIO3 */ + .long TPU_IRQHandler /* 0x2B 0x00AC 43: Crypto */ + .long DMA0_IRQHandler /* 0x2C 0x00B0 44: DMA0 */ + .long DMA1_IRQHandler /* 0x2D 0x00B4 45: DMA1 */ + .long DMA2_IRQHandler /* 0x2E 0x00B8 46: DMA2 */ + .long DMA3_IRQHandler /* 0x2F 0x00BC 47: DMA3 */ + .long RSV32_IRQHandler /* 0x30 0x00C0 48: Reserved */ + .long RSV33_IRQHandler /* 0x31 0x00C4 49: Reserved */ + .long UART2_IRQHandler /* 0x32 0x00C8 50: UART 2 */ + .long RSV35_IRQHandler /* 0x33 0x00CC 51: Reserved */ + .long I2C1_IRQHandler /* 0x34 0x00D0 52: I2C1 */ + .long RSV37_IRQHandler /* 0x35 0x00D4 53: Reserved */ + .long SPIXFC_IRQHandler /* 0x36 0x00D8 54: SPI execute in place */ + .long BTLE_TX_DONE_IRQHandler /* 0x37 0x00DC 55: BTLE TX Done */ + .long BTLE_RX_RCVD_IRQHandler /* 0x38 0x00E0 56: BTLE RX Recived */ + .long BTLE_RX_ENG_DET_IRQHandler /* 0x39 0x00E4 57: BTLE RX Energy Dectected */ + .long BTLE_SFD_DET_IRQHandler /* 0x3A 0x00E8 58: BTLE SFD Detected */ + .long BTLE_SFD_TO_IRQHandler /* 0x3B 0x00EC 59: BTLE SFD Timeout*/ + .long BTLE_GP_EVENT_IRQHandler /* 0x3C 0x00F0 60: BTLE Timestamp*/ + .long BTLE_CFO_IRQHandler /* 0x3D 0x00F4 61: BTLE CFO Done */ + .long BTLE_SIG_DET_IRQHandler /* 0x3E 0x00F8 62: BTLE Signal Detected */ + .long BTLE_AGC_EVENT_IRQHandler /* 0x3F 0x00FC 63: BTLE AGC Event */ + .long BTLE_RFFE_SPIM_IRQHandler /* 0x40 0x0100 64: BTLE RFFE SPIM Done */ + .long BTLE_TX_AES_IRQHandler /* 0x41 0x0104 65: BTLE TX AES Done */ + .long BTLE_RX_AES_IRQHandler /* 0x42 0x0108 66: BTLE RX AES Done */ + .long BTLE_INV_APB_ADDR_IRQHandler /* 0x43 0x010C 67: BTLE Invalid APB Address*/ + .long BTLE_IQ_DATA_VALID_IRQHandler /* 0x44 0x0110 68: BTLE IQ Data Valid */ + .long WUT_IRQHandler /* 0x45 0x0114 69: WUT Wakeup */ + .long GPIOWAKE_IRQHandler /* 0x46 0x0118 70: GPIO Wakeup */ + .long RSV55_IRQHandler /* 0x47 0x011C 71: Reserved */ + .long SPI0_IRQHandler /* 0x48 0x0120 72: SPI AHB */ + .long WDT1_IRQHandler /* 0x49 0x0124 73: Watchdog 1 */ + .long RSV58_IRQHandler /* 0x4A 0x0128 74: Reserved */ + .long PT_IRQHandler /* 0x4B 0x012C 75: Pulse train */ + .long SDMA0_IRQHandler /* 0x4C 0x0130 76: Smart DMA 0 */ + .long RSV61_IRQHandler /* 0x4D 0x0134 77: Reserved */ + .long I2C2_IRQHandler /* 0x4E 0x0138 78: I2C 2 */ + .long RSV63_IRQHandler /* 0x4F 0x013C 79: Reserved */ + .long RSV64_IRQHandler /* 0x50 0x0140 80: Reserved */ + .long RSV65_IRQHandler /* 0x51 0x0144 81: Reserved */ + .long SDHC_IRQHandler /* 0x52 0x0148 82: SDIO/SDHC */ + .long OWM_IRQHandler /* 0x53 0x014C 83: One Wire Master */ + .long DMA4_IRQHandler /* 0x54 0x0150 84: DMA4 */ + .long DMA5_IRQHandler /* 0x55 0x0154 85: DMA5 */ + .long DMA6_IRQHandler /* 0x56 0x0158 86: DMA6 */ + .long DMA7_IRQHandler /* 0x57 0x015C 87: DMA7 */ + .long DMA8_IRQHandler /* 0x58 0x0160 88: DMA8 */ + .long DMA9_IRQHandler /* 0x59 0x0164 89: DMA9 */ + .long DMA10_IRQHandler /* 0x5A 0x0168 90: DMA10 */ + .long DMA11_IRQHandler /* 0x5B 0x016C 91: DMA11 */ + .long DMA12_IRQHandler /* 0x5C 0x0170 92: DMA12 */ + .long DMA13_IRQHandler /* 0x5D 0x0174 93: DMA13 */ + .long DMA14_IRQHandler /* 0x5E 0x0178 94: DMA14 */ + .long DMA15_IRQHandler /* 0x5F 0x017C 95: DMA15 */ + .long USBDMA_IRQHandler /* 0x60 0x0180 96: USB DMA */ + .long WDT2_IRQHandler /* 0x61 0x0184 97: Watchdog Timer 2 */ + .long ECC_IRQHandler /* 0x62 0x0188 98: Error Correction */ + .long DVS_IRQHandler /* 0x63 0x018C 99: DVS Controller */ + .long SIMO_IRQHandler /* 0x64 0x0190 100: SIMO Controller */ + .long RPU_IRQHandler /* 0x65 0x0194 101: RPU */ /* @TODO: Is this correct? */ + .long AUDIO_IRQHandler /* 0x66 0x0198 102: Audio subsystem */ + .long FLC1_IRQHandler /* 0x67 0x019C 103: Flash Control 1 */ + .long RSV88_IRQHandler /* 0x68 0x01A0 104: UART 3 */ + .long RSV89_IRQHandler /* 0x69 0x01A4 105: UART 4 */ + .long RSV90_IRQHandler /* 0x6A 0x01A8 106: UART 5 */ + .long RSV91_IRQHandler /* 0x6B 0x01AC 107: Camera IF */ + .long RSV92_IRQHandler /* 0x6C 0x01B0 108: I3C */ + .long HTMR0_IRQHandler /* 0x6D 0x01B4 109: HTmr */ + .long HTMR1_IRQHandler /* 0x6E 0x01B8 109: HTmr */ + + + .text + .thumb + .thumb_func + .align 2 +Reset_Handler: + // Set stack according to limits from linker script. + ldr r0, =CARD10_STACK_LIMIT + mov sp, r0 + + // Jump to C code + ldr r0, =main + blx r0 + + // Spin + // TODO(q3k): let epicardium know we're done +.spin: + bl .spin + + // Macro to define default handlers. Default handler + // will be weak symbol and just dead loops. They can be + // overwritten by other handlers. + .macro def_irq_handler handler_name + .align 1 + .thumb_func + .weak \handler_name + .type \handler_name, %function +\handler_name : + b . + .size \handler_name, . - \handler_name + .endm + + + // Default ISRs. + + def_irq_handler NMI_Handler + def_irq_handler HardFault_Handler + def_irq_handler MemManage_Handler + def_irq_handler BusFault_Handler + def_irq_handler UsageFault_Handler + def_irq_handler SVC_Handler + def_irq_handler DebugMon_Handler + def_irq_handler PendSV_Handler + def_irq_handler Default_Handler + + def_irq_handler PF_IRQHandler + def_irq_handler WDT0_IRQHandler + def_irq_handler USB_IRQHandler + def_irq_handler RTC_IRQHandler + def_irq_handler TRNG_IRQHandler + def_irq_handler TMR0_IRQHandler + def_irq_handler TMR1_IRQHandler + def_irq_handler TMR2_IRQHandler + def_irq_handler TMR3_IRQHandler + def_irq_handler TMR4_IRQHandler + def_irq_handler TMR5_IRQHandler + def_irq_handler RSV11_IRQHandler + def_irq_handler RSV12_IRQHandler + def_irq_handler I2C0_IRQHandler + def_irq_handler UART0_IRQHandler + def_irq_handler UART1_IRQHandler + def_irq_handler SPI1_IRQHandler + def_irq_handler SPI2_IRQHandler + def_irq_handler RSV18_IRQHandler + def_irq_handler RSV19_IRQHandler + def_irq_handler ADC_IRQHandler + def_irq_handler RSV21_IRQHandler + def_irq_handler RSV22_IRQHandler + def_irq_handler FLC0_IRQHandler + def_irq_handler GPIO0_IRQHandler + def_irq_handler GPIO1_IRQHandler + def_irq_handler RSV26_IRQHandler + def_irq_handler TPU_IRQHandler + def_irq_handler DMA0_IRQHandler + def_irq_handler DMA1_IRQHandler + def_irq_handler DMA2_IRQHandler + def_irq_handler DMA3_IRQHandler + def_irq_handler RSV32_IRQHandler + def_irq_handler RSV33_IRQHandler + def_irq_handler UART2_IRQHandler + def_irq_handler RSV35_IRQHandler + def_irq_handler I2C1_IRQHandler + def_irq_handler RSV37_IRQHandler + def_irq_handler SPIXFC_IRQHandler + def_irq_handler BTLE_TX_DONE_IRQHandler + def_irq_handler BTLE_RX_RCVD_IRQHandler + def_irq_handler BTLE_RX_ENG_DET_IRQHandler + def_irq_handler BTLE_SFD_DET_IRQHandler + def_irq_handler BTLE_SFD_TO_IRQHandler + def_irq_handler BTLE_GP_EVENT_IRQHandler + def_irq_handler BTLE_CFO_IRQHandler + def_irq_handler BTLE_SIG_DET_IRQHandler + def_irq_handler BTLE_AGC_EVENT_IRQHandler + def_irq_handler BTLE_RFFE_SPIM_IRQHandler + def_irq_handler BTLE_TX_AES_IRQHandler + def_irq_handler BTLE_RX_AES_IRQHandler + def_irq_handler BTLE_INV_APB_ADDR_IRQHandler + def_irq_handler BTLE_IQ_DATA_VALID_IRQHandler + def_irq_handler WUT_IRQHandler + def_irq_handler GPIOWAKE_IRQHandler + def_irq_handler RSV55_IRQHandler + def_irq_handler SPI0_IRQHandler + def_irq_handler WDT1_IRQHandler + def_irq_handler RSV58_IRQHandler + def_irq_handler PT_IRQHandler + def_irq_handler SDMA0_IRQHandler + def_irq_handler RSV61_IRQHandler + def_irq_handler I2C2_IRQHandler + def_irq_handler RSV63_IRQHandler + def_irq_handler RSV64_IRQHandler + def_irq_handler RSV65_IRQHandler + def_irq_handler SDHC_IRQHandler + def_irq_handler OWM_IRQHandler + def_irq_handler DMA4_IRQHandler + def_irq_handler DMA5_IRQHandler + def_irq_handler DMA6_IRQHandler + def_irq_handler DMA7_IRQHandler + def_irq_handler DMA8_IRQHandler + def_irq_handler DMA9_IRQHandler + def_irq_handler DMA10_IRQHandler + def_irq_handler DMA11_IRQHandler + def_irq_handler DMA12_IRQHandler + def_irq_handler DMA13_IRQHandler + def_irq_handler DMA14_IRQHandler + def_irq_handler DMA15_IRQHandler + def_irq_handler USBDMA_IRQHandler + def_irq_handler WDT2_IRQHandler + def_irq_handler ECC_IRQHandler + def_irq_handler DVS_IRQHandler + def_irq_handler SIMO_IRQHandler + def_irq_handler RPU_IRQHandler + def_irq_handler AUDIO_IRQHandler + def_irq_handler FLC1_IRQHandler + def_irq_handler RSV88_IRQHandler + def_irq_handler RSV89_IRQHandler + def_irq_handler RSV90_IRQHandler + def_irq_handler RSV91_IRQHandler + def_irq_handler RSV92_IRQHandler + def_irq_handler HTMR0_IRQHandler + def_irq_handler HTMR1_IRQHandler + + .section .cinterp + .asciz "card10-l0dable" + .byte diff --git a/l0dables/meson.build b/l0dables/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..12bb45b3db79d8f0952ce4b7ee7897a4f8051967 --- /dev/null +++ b/l0dables/meson.build @@ -0,0 +1,17 @@ +l0dable_startup_lib = static_library( + 'l0dable-startup', + 'crt.s', + pic: true, +) + +l0dable_startup = declare_dependency( + link_args: [ + '-nostdlib', '-n', + '-T', meson.current_source_dir() + 'core1.ld', + ], + compile_args: [ + '-fPIE', '-pie', + ], +) + +subdir('blinky/') diff --git a/lib/card10/card10.c b/lib/card10/card10.c index 92ddee1be65cda57b9c7f8b8459e301dda328bbf..de332612850c6a4cd076d8cb0fa5b45016542f7f 100644 --- a/lib/card10/card10.c +++ b/lib/card10/card10.c @@ -199,11 +199,9 @@ void card10_diag(void) #endif } -void core1_start(void) -{ - //MXC_GCR->gp0 = (uint32_t)(&__isr_vector_core1); - MXC_GCR->gp0 = 0x10080000; - MXC_GCR->perckcn1 &= ~MXC_F_GCR_PERCKCN1_CPU1; +void core1_start(void *isr) { + MXC_GCR->gp0 = (uint32_t)isr; + MXC_GCR->perckcn1 &= ~MXC_F_GCR_PERCKCN1_CPU1; } void core1_stop(void) diff --git a/lib/card10/card10.h b/lib/card10/card10.h index 488575a104f4c289998deaabfea14626d533de8c..853a27be90980deda9df88d1c3156a704ada648e 100644 --- a/lib/card10/card10.h +++ b/lib/card10/card10.h @@ -9,7 +9,7 @@ extern const gpio_cfg_t bhi_interrupt_pin; void card10_init(void); void card10_diag(void); -void core1_start(void); +void core1_start(void *isr); void core1_stop(void); void card10_poll(void); diff --git a/meson.build b/meson.build index 20b7040b216e7d4294c9a911230e71bd3b96e257..e58eb189d3fcc084bad0acc72c8e030a97771d5a 100644 --- a/meson.build +++ b/meson.build @@ -37,3 +37,5 @@ subdir('epicardium/') subdir('pycardium/') subdir('hw-tests/') + +subdir('l0dables/')