Skip to content
Snippets Groups Projects
Verified Commit 05e2cb35 authored by rahix's avatar rahix
Browse files

feat(epicardium): Add l0der


Signed-off-by: default avatarRahix <rahix@rahix.de>
parents 9018244b 3bc1ab55
No related branches found
No related tags found
No related merge requests found
Showing with 1577 additions and 43 deletions
#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
#define SHT_DYNSYM 11
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
#define ELF32_ST_BIND(i) ((i)>>4)
#define ELF32_ST_TYPE(i) ((i)&0xf)
#define STB_WEAK 2
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 0x17
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
#include "l0der/l0der.h"
#include <alloca.h>
#include <stdio.h>
#include <string.h>
#include "epicardium.h"
#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.
*/
#define WEAK_SYMBOL_MAX 128
struct _pie_load_info {
/// Populated by _load_pie
// Addresses within ELF file.
uint32_t image_start;
uint32_t image_limit;
// Highest alignment request for a segment.
uint32_t strictest_alignment;
/// Populated by _parse_dynamic_symbols
// List of weak symbols for which relocations can be ignored.
uint32_t weak_symbols[WEAK_SYMBOL_MAX];
uint32_t weak_symbol_count;
/// Populated by _get_load_addr
// Load address of ELF file.
uint32_t load_address;
// Addresses within memory of ELF file.
uint32_t image_load_start;
uint32_t image_load_limit;
// Stack top.
uint32_t stack_top;
};
/*
* Read an ELF header, check E_IDENT.
*/
static int _read_elf_header(int fd, Elf32_Ehdr *hdr)
{
int res;
epic_file_seek(fd, 0, SEEK_SET);
if ((res = epic_file_read(fd, hdr, sizeof(Elf32_Ehdr))) !=
sizeof(Elf32_Ehdr)) {
LOG_ERR("l0der", "_read_elf_header: read failed: %d", res);
return res;
}
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 -ENOEXEC;
}
if (hdr->e_ident[4] != ELFCLASS32) {
LOG_ERR("l0der", "_read_elf_header: not a 32-bit ELF");
return -ENOEXEC;
}
if (hdr->e_ident[5] != ELFDATA2LSB) {
LOG_ERR("l0der", "_read_elf_header: not a little-endian ELF");
return -ENOEXEC;
}
if (hdr->e_ident[6] != EV_CURRENT) {
LOG_ERR("l0der", "_read_elf_header: not a v1 ELF");
return -ENOEXEC;
}
if (hdr->e_ehsize < sizeof(Elf32_Ehdr)) {
LOG_ERR("l0der", "_raed_elf_header: header too small");
return -ENOEXEC;
}
return 0;
}
/*
* Read bytes from file at a given offset.
*
* :param int fd: 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 _seek_and_read(int fd, uint32_t address, void *data, size_t count)
{
int res;
if ((res = epic_file_seek(fd, address, SEEK_SET)) != 0) {
LOG_ERR("l0der",
"_seek_and_read: could not seek to 0x%lx: %d",
address,
res);
return res;
}
if ((res = epic_file_read(fd, data, count)) != count) {
LOG_ERR("l0der", "_seek_and_read: could not read: %d", res);
return res;
}
return 0;
}
/*
* Read an ELF program header header.
*/
static int _read_program_header(int fd, uint32_t phdr_addr, Elf32_Phdr *phdr)
{
return _seek_and_read(fd, phdr_addr, phdr, sizeof(Elf32_Phdr));
}
/*
* Read an ELF section header header.
*/
static int _read_section_header(int fd, uint32_t shdr_addr, Elf32_Shdr *shdr)
{
return _seek_and_read(fd, shdr_addr, shdr, sizeof(Elf32_Shdr));
}
/*
* 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(int fd, int size, Elf32_Phdr *phdr)
{
// 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(int fd, int size, Elf32_Shdr *shdr)
{
// 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;
}
/*
* 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(int fd, Elf32_Phdr *phdr)
{
int res;
uint32_t buffer_size = strlen(_interpreter) + 1;
char *interp = alloca(buffer_size);
memset(interp, 0, buffer_size);
if ((res = _seek_and_read(fd, phdr->p_offset, interp, buffer_size)) !=
0) {
return res;
}
if (strncmp(interp, _interpreter, strlen(_interpreter)) != 0) {
LOG_ERR("l0der",
"_check_interp: invalid interpreter, want card10-l0dable");
return -1;
}
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(struct _pie_load_info *li)
{
uint32_t image_size = li->image_limit - li->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;
}
// Place image at bottom of core1 memory range.
li->load_address = core1_mem_start;
li->image_load_start = li->load_address + li->image_start;
li->image_load_limit = li->load_address + li->image_limit;
// Ensure within alignment requests.
if ((li->load_address % li->strictest_alignment) != 0) {
LOG_ERR("l0der",
"_get_load_addr: too strict alignment request for %ld bytes",
li->strictest_alignment);
return -ENOEXEC;
}
// Place stack at top of core1 memory range.
li->stack_top = core1_mem_limit;
// Check that there is enough stack space.
uint32_t stack_space = li->stack_top - li->image_load_limit;
if (stack_space < 8192) {
LOG_WARN(
"l0der",
"_get_load_addr: low stack space (%ld bytes)",
stack_space
);
} else if (stack_space < 256) {
LOG_ERR("l0der",
"_get_load_addr: low stack space (%ld bytes), cannot continue",
stack_space);
return -ENOMEM;
}
LOG_INFO(
"l0der",
"Stack at %08lx, %ld bytes available",
li->stack_top,
stack_space
);
return 0;
}
/*
* Load a program segment into memory.
*
* Segment must be a LOAD segment.
*/
static int _load_segment(int fd, struct _pie_load_info *li, Elf32_Phdr *phdr)
{
uint32_t segment_start = li->load_address + 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);
return _seek_and_read(
fd, phdr->p_offset, (void *)segment_start, phdr->p_filesz
);
}
/*
* Parse dynamic symbol sections.
*/
static int _parse_dynamic_symbols(
int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr
) {
int res;
Elf32_Shdr shdr;
Elf32_Sym sym;
// Go through all dynamic symbol 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(fd, shdr_addr, &shdr)) != 0) {
return res;
}
if (shdr.sh_type != SHT_DYNSYM) {
continue;
}
if ((res = _check_section_header(fd, size, &shdr)) != 0) {
return res;
}
if ((shdr.sh_size % sizeof(Elf32_Sym)) != 0) {
LOG_ERR("l0der",
"_parse_dynamic_symbols: SHT_DYN section with invalid size: %ld",
shdr.sh_size);
return -EIO;
}
uint32_t sym_count = shdr.sh_size / sizeof(Elf32_Sym);
// Read symbols one by one.
if ((res = epic_file_seek(fd, shdr.sh_offset, SEEK_SET)) != 0) {
LOG_ERR("l0der",
"_parse_dynamic_symbols: seek to first relocation (at 0x%lx) failed",
shdr.sh_offset);
return res;
}
for (int j = 0; j < sym_count; j++) {
if ((res = epic_file_read(
fd, &sym, sizeof(Elf32_Sym))) !=
sizeof(Elf32_Sym)) {
LOG_ERR("l0der",
"__parse_dynamic_symbols: symbol read failed: %d",
res);
return res;
}
uint32_t bind = ELF32_ST_BIND(sym.st_info);
if (bind != STB_WEAK) {
continue;
}
if (li->weak_symbol_count >= WEAK_SYMBOL_MAX) {
LOG_ERR("l0der",
"__parse_dynamic_symbols: too many weak symbols (limit: %d)",
WEAK_SYMBOL_MAX);
return -ENOMEM;
}
li->weak_symbols[li->weak_symbol_count++] = j;
}
}
return 0;
}
/*
* 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(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr)
{
int res;
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(fd, 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(fd, size, &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 -ENOEXEC;
}
uint32_t reloc_count = shdr.sh_size / sizeof(Elf32_Rel);
// Read relocations one by one.
if ((res = epic_file_seek(fd, shdr.sh_offset, SEEK_SET)) != 0) {
LOG_ERR("l0der",
"_run_relocations: seek to first relocation (at 0x%lx) failed",
shdr.sh_offset);
return res;
}
for (int j = 0; j < reloc_count; j++) {
if ((res = epic_file_read(
fd, &rel, sizeof(Elf32_Rel))) !=
sizeof(Elf32_Rel)) {
LOG_ERR("l0der",
"_run_relocations: relocation read failed: %d",
res);
return res;
}
uint32_t sym = ELF32_R_SYM(rel.r_info);
uint8_t type = ELF32_R_TYPE(rel.r_info);
// Skip relocations that are for weak symbols.
// (ie., do not resolve relocation - they default to a safe NULL)
uint8_t skip = 0;
if (sym != 0) {
for (int k = 0; k < li->weak_symbol_count;
k++) {
if (li->weak_symbols[k] == sym) {
skip = 1;
break;
}
}
}
if (skip) {
continue;
}
switch (type) {
case R_ARM_RELATIVE:
// Relocate.
if ((rel.r_offset % 4) != 0) {
LOG_ERR("l0der",
"_run_relocations: R_ARM_RELATIVE address must be 4-byte aligned");
return -ENOEXEC;
}
volatile uint32_t *addr =
(uint32_t
*)(rel.r_offset + li->load_address);
if ((uint32_t)addr < li->image_load_start ||
(uint32_t)addr >= li->image_load_limit) {
LOG_ERR("l0der",
"_run_relocations: R_ARM_RELATIVE address (%08lx) is outside image boundaries",
(uint32_t)addr);
return -ENOEXEC;
}
*addr += li->load_address;
break;
default:
LOG_ERR("l0der",
"_run_relocations: unsupported relocation type %d",
type);
return -ENOEXEC;
}
}
}
return 0;
}
/*
* Load a l0dable PIE binary.
*/
static int
_load_pie(int fd, int size, Elf32_Ehdr *hdr, struct l0dable_info *info)
{
int res;
struct _pie_load_info li = { 0 };
// First pass over program headers: sanity check sizes, calculate image
// size bounds, check alignment.
li.image_start = 0xffffffff;
li.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(fd, phdr_addr, &phdr)) != 0) {
return res;
}
if ((res = _check_program_header(fd, size, &phdr)) != 0) {
return res;
}
if (phdr.p_type == PT_INTERP) {
status_interp = _check_interp(fd, &phdr);
continue;
}
if (phdr.p_type == PT_LOAD) {
// Check alignment request.
if ((phdr.p_vaddr % phdr.p_align) != 0) {
LOG_ERR("l0der",
"_load_pie: phdr %d alignment too strict",
i);
return -ENOEXEC;
}
if (phdr.p_align > li.strictest_alignment) {
li.strictest_alignment = phdr.p_align;
}
uint32_t mem_start = phdr.p_vaddr;
uint32_t mem_limit = phdr.p_vaddr + phdr.p_memsz;
// Record memory usage.
if (mem_start < li.image_start) {
li.image_start = mem_start;
}
if (mem_limit > li.image_limit) {
li.image_limit = mem_limit;
}
}
}
if (status_interp != 0) {
// Expected interpreter string was not found.
LOG_ERR("l0der", "_load_pie: not a card10 l0dable");
return -ENOEXEC;
}
if (li.image_limit < li.image_start) {
// We didn't find any LOAD segment.
LOG_ERR("l0der", "_load_pie: no loadable segments");
return -ENOEXEC;
}
LOG_INFO(
"l0der",
"Image bounds %08lx - %08lx",
li.image_start,
li.image_limit
);
if ((res = _get_load_addr(&li)) != 0) {
return res;
}
LOG_INFO("l0der", "Loading at %08lx", li.load_address);
// 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(fd, phdr_addr, &phdr)) != 0) {
return res;
}
if (phdr.p_type != PT_LOAD) {
continue;
}
if ((res = _load_segment(fd, &li, &phdr)) != 0) {
return res;
}
}
// Load dynamic symbols.
if ((res = _parse_dynamic_symbols(fd, size, &li, hdr)) != 0) {
return res;
}
// Run relocations.
if ((res = _run_relocations(fd, size, &li, hdr)) != 0) {
return res;
}
uint32_t image_entrypoint = li.load_address + hdr->e_entry;
LOG_INFO("l0der", "Entrypoint (ISR Vector) at %08lx", image_entrypoint);
// Setup stack
uint32_t *isr = (uint32_t *)image_entrypoint;
isr[0] = li.stack_top;
info->isr_vector = (void *)image_entrypoint;
return 0;
}
int l0der_load_path(const char *path, struct l0dable_info *info)
{
int fd, res;
if ((fd = epic_file_open(path, "rb")) < 0) {
LOG_ERR("l0der",
"l0der_load_path: could not open ELF file %s: %d",
path,
fd);
return fd;
}
if ((res = epic_file_seek(fd, 0, SEEK_END)) != 0) {
return res;
}
int size = epic_file_tell(fd);
if ((res = epic_file_seek(fd, 0, SEEK_SET)) != 0) {
return res;
}
// Load ELF header and ensure it's somewhat sane.
Elf32_Ehdr hdr;
if ((res = _read_elf_header(fd, &hdr)) != 0) {
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(fd, size, &hdr, info);
goto done;
} else {
LOG_ERR("l0der",
"l0der_load_path: %s: not an ARM PIE, cannot load.",
path);
res = -ENOEXEC;
goto done;
}
done:
epic_file_close(fd);
return res;
}
#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. 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);
l0der_sources = files(
'l0der.c',
)
#include <stdio.h>
#include <stdlib.h>
#include <ff.h>
#include "max32665.h"
#include "uart.h"
#include "cdcacm.h"
......@@ -9,6 +11,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"
......@@ -92,15 +95,38 @@ int main(void)
NULL,
tskIDLE_PRIORITY + 2,
&dispatcher_task_id) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "API Dispatcher");
LOG_CRIT(
"startup",
"Failed to create %s task!",
"API Dispatcher"
);
abort();
}
LOG_INFO("startup", "Initializing dispatcher ...");
api_dispatcher_init();
LOG_INFO("startup", "Starting core1 payload ...");
core1_start();
/*
* See if there's a l0dable.elf to run. If not, run pycardium.
* This is temporary until epicardium gets a l0dable API from pycardium.
*/
const char *l0dable = "l0dable.elf";
if (f_stat(l0dable, NULL) == FR_OK) {
LOG_INFO("startup", "Running %s ...", l0dable);
struct l0dable_info info;
int res = l0der_load_path(l0dable, &info);
if (res != 0) {
LOG_ERR("startup", "l0der failed: %d\n", res);
} else {
LOG_INFO(
"startup", "Starting %s on core1 ...", l0dable
);
core1_start(info.isr_vector);
}
} else {
LOG_INFO("startup", "Starting pycardium on core1 ...");
core1_start((void *)0x10080000);
}
LOG_INFO("startup", "Starting FreeRTOS ...");
vTaskStartScheduler();
......
......@@ -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],
......
......@@ -18,40 +18,47 @@
int main(void)
{
card10_init();
card10_diag();
Paint_DrawImage(Heart, 0, 0, 160, 80);
LCD_Update();
for(int i=0; i<11; i++) {
leds_set_dim(i, 1);
}
int h = 0;
// Release core1
core1_start();
while (1) {
#define NUM 15
for(int i=0; i<NUM; i++) {
if(i < 12) {
leds_set_hsv(i, (h + 360/NUM * i) % 360, 1., 1./8);
} else {
leds_set_hsv(i, (h + 360/NUM * i) % 360, 1., 1.);
}
}
leds_update();
TMR_Delay(MXC_TMR0, MSEC(10), 0);
h++;
// Send a txev using `sev` every once in a while to wake up core1
// and let it do something
if (h % 100 == 0) {
printf("core0: Triggering core1 using SEV ...\n");
__asm volatile("sev");
}
}
card10_init();
card10_diag();
Paint_DrawImage(Heart, 0, 0, 160, 80);
LCD_Update();
for (int i = 0; i < 11; i++) {
leds_set_dim(i, 1);
}
int h = 0;
// Release core1
core1_start((void *)0x10080000);
while (1) {
#define NUM 15
for (int i = 0; i < NUM; i++) {
if (i < 12) {
leds_set_hsv(
i,
(h + 360 / NUM * i) % 360,
1.,
1. / 8
);
} else {
leds_set_hsv(
i, (h + 360 / NUM * i) % 360, 1., 1.
);
}
}
leds_update();
TMR_Delay(MXC_TMR0, MSEC(10), 0);
h++;
// Send a txev using `sev` every once in a while to wake up core1
// and let it do something
if (h % 100 == 0) {
printf("core0: Triggering core1 using SEV ...\n");
__asm volatile("sev");
}
}
}
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.
#include "epicardium.h"
#include <math.h>
int levels[11] = { 0 };
int levels_display[11] = { 0 };
// 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
};
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)
{
// 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] = 128;
levels_display[i] = 1;
for (int j = 0; j < 32; j++) {
fade();
}
}
for (int i = 9; i > 0; i--) {
levels[i] = 128;
levels_display[i] = 1;
for (int j = 0; j < 32; j++) {
fade();
}
}
}
}
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,
)
/*
* C Runtime for l0dable.
*
* Also known as a startup file.
*
* We provide the following to l0dables:
* - calling GCC initializers.
* - an ISR vector.
*
* The stack is provided by l0der.
*/
.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:
.long 0 /* Top of Stack, overriden by l0der at load time */
.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 DefaultHandler /* 0x12 0x0048 18: USB, used by core0, unoverridable */
.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 DefaultHandler /* 0x1E 0x0078 30: UART 0, used by core0, unoverridable */
.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 DefaultHandler /* 0x28 0x00A0 40: GPIO0, used by core0, unoverridable */
.long DefaultHandler /* 0x29 0x00A4 41: GPIO2, used by core0, unoverridable */
.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 */
/*
* Reset_Handler, or, l0dable entrypoint.
*/
.text
.thumb
.thumb_func
.align 2
Reset_Handler:
/* 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
/*
* C code done, spin forever.
* TODO(q3k): let epicardium know we're done.
*/
.spin:
bl .spin
/*
* Used by __libc_init_array.
*/
.globl _init
_init:
bx lr
/*
* The default handler for all IRQs just spins forwever.
* TODO(q3k): let epicardium know we've reached an infinite loop due to
* an exception and/or unhandled IRQ, perhaps by splitting
* DefaultHandler into multiple handlers that report different
* error conditions to epicardium (eg. unhandled IRQ, fault, ...)
*/
.thumb_func
.type DefaultHandler, %function
DefaultHandler:
b .
.macro def_irq_handler handler_name
.weakref \handler_name, DefaultHandler
.endm
/*
* Declare all 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 PF_IRQHandler
def_irq_handler WDT0_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 RSV11_IRQHandler
def_irq_handler RSV12_IRQHandler
def_irq_handler I2C0_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 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
/*
* 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 <stddef.h>
#include "epicardium.h"
#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();
// Enable API interrupt.
NVIC_EnableIRQ(TMR5_IRQn);
}
// newlib syscall to allow printf to work.
long _write(int fd, const char *buf, size_t cnt)
{
// Only print one line at a time. Insert `\r` between lines so
// they are properly displayed on the serial console.
size_t i, last = 0;
for (i = 0; i < cnt; i++) {
if (buf[i] == '\n') {
epic_uart_write_str(&buf[last], i - last);
epic_uart_write_str("\r", 1);
last = i;
}
}
epic_uart_write_str(&buf[last], cnt - last);
return cnt;
}
// newlib syscall to allow for a heap
extern uint32_t __heap_start;
uint32_t _sbrk(int incr)
{
static char *brk = NULL;
if (brk == NULL) {
brk = (char *)&__heap_start;
}
// Ensure we don't overflow the heap by checking agsinst the current stack
// pointer (the heap grows towards the stack, and vice-versa).
//
// This is only a last-ditch attempt at saving ourselves from memory
// corruption. It doesn't prevent the stack from growing into the heap
// first.
void *sp;
__asm__ __volatile__("mov %0, sp" : "=r"(sp));
// Require a 'safe margin' of 4k between the heap and stack.
uint32_t stack_bottom = (uint32_t)sp - 4096;
if (((uint32_t)brk + incr) > stack_bottom) {
errno = ENOMEM;
return (uint32_t)-1;
}
char *prev_brk = brk;
brk += incr;
return (uint32_t)prev_brk;
}
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 ;
}
/*
* ELF sections.
*/
SECTIONS {
. = 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);
} :interp :text
.text :
{
*(.text*)
*(.rodata*)
KEEP(*(.init))
KEEP(*(.fini))
} :text
.data :
{
. = 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.*)))
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
.bss :
{
. = ALIGN(4);
*(.bss*)
*(COMMON)
} :data
/* Used by hardware.c as start of heap. */
__heap_start = .;
/* Limit based on current limitations of l0dable setup - only uses core1 RAM. */
ASSERT(. < 0x40000, "Exceeded available RAM")
/DISCARD/ :
{
/* 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)
}
}
l0dable_startup_lib = static_library(
'l0dable-startup',
'crt.s',
'hardware.c',
dependencies: [api_caller],
pic: true,
)
l0dable_startup = declare_dependency(
link_args: [
'-nostdlib', '-n',
'-T', meson.current_source_dir() + 'l0dable.ld',
],
compile_args: [
'-fPIE', '-pie',
],
)
subdir('lib/')
subdir('blinky/')
......@@ -212,10 +212,9 @@ void card10_diag(void)
#endif
}
void core1_start(void)
void core1_start(void *isr)
{
//MXC_GCR->gp0 = (uint32_t)(&__isr_vector_core1);
MXC_GCR->gp0 = 0x10080000;
MXC_GCR->gp0 = (uint32_t)isr;
MXC_GCR->perckcn1 &= ~MXC_F_GCR_PERCKCN1_CPU1;
}
......
......@@ -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);
......
......@@ -37,3 +37,5 @@ subdir('epicardium/')
subdir('pycardium/')
subdir('hw-tests/')
subdir('l0dables/')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment