mirror of https://github.com/m-labs/artiq.git
224 lines
5.5 KiB
C
224 lines
5.5 KiB
C
#include <string.h>
|
|
#include <generated/csr.h>
|
|
|
|
#include "log.h"
|
|
#include "clock.h"
|
|
#include "flash_storage.h"
|
|
#include "mailbox.h"
|
|
#include "messages.h"
|
|
#include "elf_loader.h"
|
|
#include "services.h"
|
|
#include "kloader.h"
|
|
|
|
static struct symbol symtab[128];
|
|
static int _symtab_count;
|
|
static char _symtab_strings[128*16];
|
|
static char *_symtab_strptr;
|
|
|
|
static void symtab_init(void)
|
|
{
|
|
memset(symtab, 0, sizeof(symtab));
|
|
_symtab_count = 0;
|
|
_symtab_strptr = _symtab_strings;
|
|
}
|
|
|
|
static int symtab_add(const char *name, void *target)
|
|
{
|
|
if(_symtab_count >= sizeof(symtab)/sizeof(symtab[0])) {
|
|
log("Too many provided symbols in object");
|
|
symtab_init();
|
|
return 0;
|
|
}
|
|
symtab[_symtab_count].name = _symtab_strptr;
|
|
symtab[_symtab_count].target = target;
|
|
_symtab_count++;
|
|
|
|
while(1) {
|
|
if(_symtab_strptr >= &_symtab_strings[sizeof(_symtab_strings)]) {
|
|
log("Provided symbol string table overflow");
|
|
symtab_init();
|
|
return 0;
|
|
}
|
|
*_symtab_strptr = *name;
|
|
_symtab_strptr++;
|
|
if(*name == 0)
|
|
break;
|
|
name++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int kloader_load(void *buffer, int length)
|
|
{
|
|
if(!kernel_cpu_reset_read()) {
|
|
log("BUG: attempted to load while kernel CPU running");
|
|
return 0;
|
|
}
|
|
symtab_init();
|
|
return load_elf(
|
|
resolve_service_symbol, symtab_add,
|
|
buffer, length, (void *)KERNELCPU_PAYLOAD_ADDRESS, 4*1024*1024);
|
|
}
|
|
|
|
kernel_function kloader_find(const char *name)
|
|
{
|
|
return find_symbol(symtab, name);
|
|
}
|
|
|
|
extern char _binary_ksupport_bin_start;
|
|
extern char _binary_ksupport_bin_end;
|
|
|
|
static void start_kernel_cpu(void *addr)
|
|
{
|
|
memcpy((void *)KERNELCPU_EXEC_ADDRESS, &_binary_ksupport_bin_start,
|
|
&_binary_ksupport_bin_end - &_binary_ksupport_bin_start);
|
|
mailbox_acknowledge();
|
|
mailbox_send(addr);
|
|
kernel_cpu_reset_write(0);
|
|
}
|
|
|
|
void kloader_start_bridge(void)
|
|
{
|
|
start_kernel_cpu(NULL);
|
|
}
|
|
|
|
void kloader_start_user_kernel(kernel_function k)
|
|
{
|
|
if(!kernel_cpu_reset_read()) {
|
|
log("BUG: attempted to start kernel CPU while already running (user kernel)");
|
|
return;
|
|
}
|
|
start_kernel_cpu((void *)k);
|
|
}
|
|
|
|
static int kloader_start_flash_kernel(char *key)
|
|
{
|
|
char buffer[32*1024];
|
|
unsigned int len, remain;
|
|
kernel_function k;
|
|
|
|
if(!kernel_cpu_reset_read()) {
|
|
log("BUG: attempted to start kernel CPU while already running (%s)", key);
|
|
return 0;
|
|
}
|
|
#if (defined CSR_SPIFLASH_BASE && defined SPIFLASH_PAGE_SIZE)
|
|
len = fs_read(key, buffer, sizeof(buffer), &remain);
|
|
if(len <= 0)
|
|
return 0;
|
|
if(remain) {
|
|
log("ERROR: %s too long", key);
|
|
return 0;
|
|
}
|
|
if(!kloader_load(buffer, len)) {
|
|
log("ERROR: failed to load ELF binary (%s)", key);
|
|
return 0;
|
|
}
|
|
k = kloader_find("run");
|
|
if(!k) {
|
|
log("ERROR: failed to find entry point for ELF kernel (%s)", key);
|
|
return 0;
|
|
}
|
|
start_kernel_cpu((void *)k);
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int kloader_start_startup_kernel(void)
|
|
{
|
|
return kloader_start_flash_kernel("startup_kernel");
|
|
}
|
|
|
|
int kloader_start_idle_kernel(void)
|
|
{
|
|
return kloader_start_flash_kernel("idle_kernel");
|
|
}
|
|
|
|
void kloader_stop(void)
|
|
{
|
|
kernel_cpu_reset_write(1);
|
|
mailbox_acknowledge();
|
|
}
|
|
|
|
int kloader_validate_kpointer(void *p)
|
|
{
|
|
unsigned int v = (unsigned int)p;
|
|
if((v < 0x40400000) || (v > (0x4fffffff - 1024*1024))) {
|
|
log("Received invalid pointer from kernel CPU: 0x%08x", v);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int kloader_is_essential_kmsg(int msgtype)
|
|
{
|
|
switch(msgtype) {
|
|
case MESSAGE_TYPE_NOW_INIT_REQUEST:
|
|
case MESSAGE_TYPE_NOW_SAVE:
|
|
case MESSAGE_TYPE_LOG:
|
|
case MESSAGE_TYPE_WATCHDOG_SET_REQUEST:
|
|
case MESSAGE_TYPE_WATCHDOG_CLEAR:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
long long int now;
|
|
|
|
void kloader_service_essential_kmsg(void)
|
|
{
|
|
struct msg_base *umsg;
|
|
|
|
umsg = mailbox_receive();
|
|
if(umsg) {
|
|
if(!kloader_validate_kpointer(umsg))
|
|
return;
|
|
switch(umsg->type) {
|
|
case MESSAGE_TYPE_NOW_INIT_REQUEST: {
|
|
struct msg_now_init_reply reply;
|
|
|
|
reply.type = MESSAGE_TYPE_NOW_INIT_REPLY;
|
|
reply.now = now;
|
|
mailbox_send_and_wait(&reply);
|
|
break;
|
|
}
|
|
case MESSAGE_TYPE_NOW_SAVE: {
|
|
struct msg_now_save *msg = (struct msg_now_save *)umsg;
|
|
|
|
now = msg->now;
|
|
mailbox_acknowledge();
|
|
break;
|
|
}
|
|
case MESSAGE_TYPE_LOG: {
|
|
struct msg_log *msg = (struct msg_log *)umsg;
|
|
|
|
log_va(msg->fmt, msg->args);
|
|
mailbox_acknowledge();
|
|
break;
|
|
}
|
|
case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: {
|
|
struct msg_watchdog_set_request *msg = (struct msg_watchdog_set_request *)umsg;
|
|
struct msg_watchdog_set_reply reply;
|
|
|
|
reply.type = MESSAGE_TYPE_WATCHDOG_SET_REPLY;
|
|
reply.id = watchdog_set(msg->ms);
|
|
mailbox_send_and_wait(&reply);
|
|
break;
|
|
}
|
|
case MESSAGE_TYPE_WATCHDOG_CLEAR: {
|
|
struct msg_watchdog_clear *msg = (struct msg_watchdog_clear *)umsg;
|
|
|
|
watchdog_clear(msg->id);
|
|
mailbox_acknowledge();
|
|
break;
|
|
}
|
|
default:
|
|
/* handled elsewhere */
|
|
break;
|
|
}
|
|
}
|
|
}
|