diff --git a/epicardium/l0der/l0der.c b/epicardium/l0der/l0der.c index 93a0ecba597bcaa8cef0af29a0362fddb71cdf87..0051988c28fd0cfe9eecaa4eec82df8f6270b7c1 100644 --- a/epicardium/l0der/l0der.c +++ b/epicardium/l0der/l0der.c @@ -1,5 +1,6 @@ #include "l0der/l0der.h" +#include <alloca.h> #include <stdio.h> #include <string.h> #include <ff.h> @@ -8,6 +9,25 @@ #include "l0der/elf.h" #include "modules/log.h" +/* + * l0der is, in reality, a boneless operating-system style ELF loader. + * + * To implement it, we parse an ELF file somewhat defensively, trying to + * not DoS ourselves by overallocating RAM (no heap allocations, no recursion). + * + * Currently we support only relocatable, PIE binaries. Adding support for + * static ELFs would be trivial, however we want to keep the possibility to + * shuffle around memory areas in future versions of card10 (possibly giving + * l0dables more RAM than 256k) without having to recompile all l0dables. We + * are also keeping the opportunity to have separate loading schemes in the + * future, for instance: + * - l0dables running next to pycardium, without unloading it + * - multiple l0dables running next to each other (TSR-style) + * + * Thus, we use PIE l0dables to keep these possibilities open and not write down + * a memory map in stone. + */ + /* * Read an ELF header, check E_IDENT. */ @@ -59,20 +79,31 @@ static int _read_elf_header(FIL *fp, Elf32_Ehdr *hdr) } /* - * Read an ELF program header header. + * Read bytes from file at a given offset. + * + * :param FIL* fp: file pointer to read from + * :param uint32_t address: address from which to read + * :param void *data: buffer into which to read + * :param size_t count: amount of bytes to read + * :returns: ``0`` on success or a negative value on error. Possible errors: + * + * - ``-EIO``: Could not read from FAT - address out of bounds of not enough bytes available. */ -static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr) -{ +static int _seek_and_read(FIL *fp, uint32_t address, void *data, size_t count) { 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); + if ((fres = f_lseek(fp, address)) != FR_OK) { + LOG_ERR("l0der", "_seek_and_read: could not seek to 0x%lx: %d", address, 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); + if ((fres = f_read(fp, data, count, &read)) != FR_OK || read < count) { + if (fres == FR_OK) { + LOG_ERR("l0der", "_seek_and_read: could not read: wanted %d bytes, got %d", count, read); + } else { + LOG_ERR("l0der", "_seek_and_read: could not read: %d", fres); + } return -EIO; } @@ -82,23 +113,17 @@ static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr) /* * Read an ELF program header header. */ -static int _read_section_header(FIL *fp, uint32_t shdr_addr, Elf32_Shdr *shdr) +static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr) { - 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 _seek_and_read(fp, phdr_addr, phdr, sizeof(Elf32_Phdr)); +} - return 0; +/* + * Read an ELF section header header. + */ +static int _read_section_header(FIL *fp, uint32_t shdr_addr, Elf32_Shdr *shdr) +{ + return _seek_and_read(fp, shdr_addr, shdr, sizeof(Elf32_Shdr)); } /* @@ -165,30 +190,25 @@ static int _check_section_header(FIL *fp, Elf32_Shdr *shdr) { return 0; } +/* + * Interpreter expected in l0dable PIE binaries. + */ static const char *_interpreter = "card10-l0dable"; +/* + * Check that the given INTERP program header contains the correct interpreter string. + */ static int _check_interp(FIL *fp, Elf32_Phdr *phdr) { - uint32_t buffer_size = 64; - char interp[buffer_size]; + int res; + uint32_t buffer_size = strlen(_interpreter) + 1; + char *interp = alloca(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; + if ((res = _seek_and_read(fp, phdr->p_offset, interp, buffer_size)) != FR_OK) { + return res; } - 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"); @@ -198,6 +218,11 @@ static int _check_interp(FIL *fp, Elf32_Phdr *phdr) return 0; } +/* + * Calculate address at which binary should be loaded. + * + * Currently this means trying to fit it into core1 RAM. + */ static int _get_load_addr(uint32_t image_start, uint32_t image_limit, void **load) { uint32_t image_size = image_limit - image_start; @@ -218,6 +243,11 @@ static int _get_load_addr(uint32_t image_start, uint32_t image_limit, void **loa return 0; } +/* + * Load a program segment into memory. + * + * Segment must be a LOAD segment. + */ 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; @@ -227,23 +257,17 @@ static int _load_segment(FIL *fp, void *image_load_addr, Elf32_Phdr *phdr) 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; + return _seek_and_read(fp, phdr->p_offset, (void*)segment_start, phdr->p_filesz); } -static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) { +/* + * Apply dynamic relocations from ELF. + * + * Currently, we only support R_ARM_RELATIVE relocations. These seem to be + * the only one used when making 'standard' PIE binaries on RAM. However, other + * kinds might have to be implemented in the future. + */ +static int _run_relocations(FIL *fp, void *load_addr, uint32_t image_start, uint32_t image_limit, Elf32_Ehdr *hdr) { int res; FRESULT fres; Elf32_Shdr shdr; @@ -296,8 +320,12 @@ static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) { 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); + if ((uint32_t)addr < image_start || (uint32_t)addr >= image_limit) { + LOG_ERR("l0der", "_run_relocations: R_ARM_RELATIVE address is outside image boundaries"); + return -ENOEXEC; + } + *addr += (uint32_t)load_addr; break; default: @@ -310,6 +338,9 @@ static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) { return 0; } +/* + * Load a l0dable PIE binary. + */ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info) { int res; @@ -363,12 +394,14 @@ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info) } if (status_interp != 0) { + // Expected interpreter string was not found. LOG_ERR("l0der", "_load_pie: not a card10 l0dable"); return -ENOEXEC; } if (image_limit < image_start) { + // We didn't find any LOAD segment. LOG_ERR("l0der", "_load_pie: no loadable segments"); return -ENOEXEC; } @@ -401,7 +434,7 @@ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info) // Run relocations. - if ((res = _run_relocations(fp, load_addr, hdr)) != 0) { + if ((res = _run_relocations(fp, load_addr, image_start, image_limit, hdr)) != 0) { return res; } diff --git a/epicardium/l0der/l0der.h b/epicardium/l0der/l0der.h index 80ce3242dc1f239a7e9c5fa3eef50e53fac2868b..d7f6b462c80694bc4304fa4baab68a206b21760a 100644 --- a/epicardium/l0der/l0der.h +++ b/epicardium/l0der/l0der.h @@ -22,6 +22,11 @@ struct l0dable_info { * * :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. + * :returns: ``0`` on success or a negative value on error. Possible errors: + * + * - ``-ENOENT``: l0dable not present at given path. + * - ``-EIO``: Read failed: l0dable corrupted or truncated. + * - ``-ENOEXEC``: Corrupted/invalid l0dable. + * - ``-ENOMEM``: l0dable too large to fit in RAM. */ int l0der_load_path(const char *path, struct l0dable_info *l0dable); diff --git a/l0dables/blinky/README.md b/l0dables/blinky/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9b13b5104a1d717acef4f3af5592deb7b182948b --- /dev/null +++ b/l0dables/blinky/README.md @@ -0,0 +1,6 @@ +blinky +====== + +It works! Blink it! + +This is a battery-hungry l0dable that shows you how to blink some LEDs while burning CPU time and battery. diff --git a/l0dables/blinky/main.c b/l0dables/blinky/main.c index 7d9c1e10002bbc24496818321fe2f3021e40f905..1736e0c4935890becfa7eb26c320b17e12e94d21 100644 --- a/l0dables/blinky/main.c +++ b/l0dables/blinky/main.c @@ -1,80 +1,65 @@ -#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; +#include <math.h> -void SystemCoreClockUpdate(void) -{ - uint32_t base_freq, div, clk_src; +int levels[11] = {0}; +int levels_display[11] = {0}; - // 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; +// From https://learn.adafruit.com/led-tricks-gamma-correction/the-quick-fix +const uint8_t gamma8[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, + 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, + 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, + 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, + 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; - SystemCoreClock = base_freq >> div; +void fade() { + for (int i = 0; i < 11; i++) { + int level = gamma8[levels[i]]; + if (levels_display[i] > 0) { + epic_leds_set(i, level, 0, 0); + if (level == 0) { + levels_display[i] = 0; + } + } + if (levels[i] > 0) { + levels[i]--; + } + } } +/* + * main() is called when l0dable is loaded and executed. + */ 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 (;;) {} + // l0dables are running on a separate, exclusive-to-l0dables core. + // Busy-waiting will not block the main operating system on core0 from + // running - but it will drain batteries. + for (;;) { + for (int i = 0; i < 11; i++) { + levels[i] = 255; + levels_display[i] = 1; + for (int j = 0; j < 64; j++) { + fade(); + } + } + for (int i = 9; i > 0; i--) { + levels[i] = 255; + levels_display[i] = 1; + for (int j = 0; j < 64; j++) { + fade(); + } + } + } } diff --git a/l0dables/blinky/meson.build b/l0dables/blinky/meson.build index 9a5c199dcaf503aafb2b966256b56938ae22be4e..9a2fdb4c23bde11073b8e792dcb6f5b792aa6e9f 100644 --- a/l0dables/blinky/meson.build +++ b/l0dables/blinky/meson.build @@ -4,7 +4,7 @@ elf = executable( name + '.elf', 'main.c', build_by_default: true, - dependencies: [l0dable_startup, api_caller], + dependencies: [l0dable_startup, api_caller, periphdriver], link_whole: [l0dable_startup_lib], link_args: [ '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map', diff --git a/l0dables/crt.s b/l0dables/lib/crt.s similarity index 84% rename from l0dables/crt.s rename to l0dables/lib/crt.s index 732c9c988ceee5640348ba9d92a356ab6728f806..b2d1ba76af534f8621bcf7b452518aa85b4a09ba 100644 --- a/l0dables/crt.s +++ b/l0dables/lib/crt.s @@ -1,26 +1,44 @@ + /* + * C Runtime for l0dable. + * + * Also known as a startup file. + * + * We provide the following to l0dables: + * - a 8k stack. + * - calling GCC initializers. + * - an ISR vector. + */ + .syntax unified .arch armv7-m + /* + * ISR Vector. + * + * All of the following (apart from Reset_Handler, which calls main()) + * are backed by weak referenced symbols, which you can override just + * by defining them in C code. + */ .section .data .align 2 - .globl isr_vector -isr_vector: + .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 */ + .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 */ @@ -46,7 +64,7 @@ isr_vector: .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 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 */ @@ -107,7 +125,7 @@ isr_vector: .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 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 */ @@ -119,28 +137,43 @@ isr_vector: .long HTMR0_IRQHandler /* 0x6D 0x01B4 109: HTmr */ .long HTMR1_IRQHandler /* 0x6E 0x01B8 109: HTmr */ - + /* + * Reset_Handler, or, l0dable entrypoint. + */ .text .thumb .thumb_func .align 2 Reset_Handler: - // Set stack according to limits from linker script. + /* Set stack according to limits from linker script. */ ldr r0, =CARD10_STACK_LIMIT mov sp, r0 - // Jump to C code + /* Call system initialization from l0dables/lib/hardware.c. */ + blx SystemInit + + /* Call GCC constructors. */ + ldr r0, =__libc_init_array + blx r0 + + /* Jump to C code */ ldr r0, =main blx r0 - // Spin - // TODO(q3k): let epicardium know we're done + /* + * Spin forever. + * 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. + .globl _init +_init: + bx lr + + /* 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 @@ -151,9 +184,9 @@ Reset_Handler: .size \handler_name, . - \handler_name .endm - - // Default ISRs. - + /* + * Declare all default ISRs. + */ def_irq_handler NMI_Handler def_irq_handler HardFault_Handler def_irq_handler MemManage_Handler diff --git a/l0dables/lib/hardware.c b/l0dables/lib/hardware.c new file mode 100644 index 0000000000000000000000000000000000000000..1e08e5ed5c5381bc9eebf7567bc43e35b120fe5f --- /dev/null +++ b/l0dables/lib/hardware.c @@ -0,0 +1,74 @@ +/* + * Hardware routines for l0dables. + * + * You shouldn't have to do much here. SystemInit/SystemCoreClockUpdate are + * called automatically before main(), and provide you with a sensible execution + * environment. + * + * However, if you wish, you can define your own SystemInit and take over the + * initialization before main() gets called. + */ + +#include "max32665.h" +#include "mxc_sys.h" +#include "gcr_regs.h" +#include "icc_regs.h" +#include "pwrseq_regs.h" + +uint32_t SystemCoreClock = HIRC_FREQ >> 1; + +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; +} + +__weak void SystemInit() { + // Enable FPU. + SCB->CPACR |= SCB_CPACR_CP10_Msk | SCB_CPACR_CP11_Msk; + __DSB(); + __ISB(); + + // Enable ICache1 Clock + MXC_GCR->perckcn1 &= ~(1 << 22); + + // Invalidate cache and wait until ready + MXC_ICC1->invalidate = 1; + while (!(MXC_ICC1->cache_ctrl & MXC_F_ICC_CACHE_CTRL_CACHE_RDY)); + + // Enable Cache + MXC_ICC1->cache_ctrl |= MXC_F_ICC_CACHE_CTRL_CACHE_EN; + + SystemCoreClockUpdate(); +} diff --git a/l0dables/core1.ld b/l0dables/lib/l0dable.ld similarity index 52% rename from l0dables/core1.ld rename to l0dables/lib/l0dable.ld index 4a7ff28de2bc564c5840f8ef7041d8f095567bc0..8c96d669000692d7446555b604863daf555d29bf 100644 --- a/l0dables/core1.ld +++ b/l0dables/lib/l0dable.ld @@ -1,29 +1,31 @@ -/*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; +ENTRY(__isr_vector); +/* + * Segment in the output l0dable. + * + * They are mostly standard, but we define them explicitely so that we can + * target them in sections. + */ PHDRS { header PT_PHDR PHDRS ; interp PT_INTERP ; text PT_LOAD FILEHDR PHDRS ; data PT_LOAD ; - dynamic PT_DYNAMIC ; } +/* + * ELF sections. + */ SECTIONS { - /* . = CARD10_CORE1_START; */ - . = SIZEOF_HEADERS; + /* + * Customer card10-l0dable INTERP/intepreter path. + * + * We nuke the original one (.interp) provided by gcc/ld, and inject out + * own. This section is populated in l0dable.ld. + */ .cinterp : { *(.cinterp); @@ -43,6 +45,12 @@ SECTIONS { . = ALIGN(4); *(.data*) + . = ALIGN(4); + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); PROVIDE_HIDDEN (__init_array_start = .); KEEP(*(SORT(.init_array.*))) @@ -56,36 +64,33 @@ SECTIONS { 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; + . += 8192; CARD10_STACK_LIMIT = .; } :data + /* Limit based on current limitations of l0dable setup - only uses core1 RAM. */ + ASSERT(. < 0x40000, "Exceeded available RAM") + /DISCARD/ : { - /* *(*) */ - /* *(.symtab) */ - /* *(.strtab) */ - /* *(.shstrtab) */ - /**(.interp)*/ + /* Compiler version - nuke. */ *(.comment) + /* ARM attributes - nuke. */ + *(.ARM.attributes) + /* Original interpreter path from gcc/ld - nuke. */ *(.interp) + /* Dynamic linking section - nuke, we're not a .so and nothing is going to link against us. */ + *(.dynamic) } } diff --git a/l0dables/lib/meson.build b/l0dables/lib/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c9d81c080b70cce7f445d65828717344ebaabfee --- /dev/null +++ b/l0dables/lib/meson.build @@ -0,0 +1,18 @@ +l0dable_startup_lib = static_library( + 'l0dable-startup', + 'crt.s', + 'hardware.c', + dependencies: periphdriver, + pic: true, +) + +l0dable_startup = declare_dependency( + link_args: [ + '-nostdlib', '-n', + '-T', meson.current_source_dir() + 'l0dable.ld', + ], + compile_args: [ + '-fPIE', '-pie', + ], +) + diff --git a/l0dables/meson.build b/l0dables/meson.build index 12bb45b3db79d8f0952ce4b7ee7897a4f8051967..f4be10b507d2e028d204202e14fa68bdf195a6ac 100644 --- a/l0dables/meson.build +++ b/l0dables/meson.build @@ -1,17 +1,3 @@ -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('lib/') subdir('blinky/')