diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 7af841a4e..6f6fa35ba 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -1,10 +1,8 @@ import struct -import zlib import logging from enum import Enum from fractions import Fraction -from artiq.language import units from artiq.coredevice.runtime import Environment from artiq.coredevice import runtime_exceptions from artiq.language import core as core_language @@ -15,33 +13,30 @@ logger = logging.getLogger(__name__) class _H2DMsgType(Enum): - LINK_MESSAGE = 1 - - REQUEST_IDENT = 2 + LOG_REQUEST = 1 + IDENT_REQUEST = 2 SWITCH_CLOCK = 3 LOAD_OBJECT = 4 RUN_KERNEL = 5 + + RPC_REPLY = 6 class _D2HMsgType(Enum): - MESSAGE_UNRECOGNIZED = 1 - LOG = 2 + LOG_REPLY = 1 + IDENT_REPLY = 2 + CLOCK_SWITCH_COMPLETED = 3 + CLOCK_SWITCH_FAILED = 4 - IDENT = 3 - CLOCK_SWITCH_COMPLETED = 4 - CLOCK_SWITCH_FAILED = 5 + LOAD_COMPLETED = 5 + LOAD_FAILED = 6 - OBJECT_LOADED = 6 - OBJECT_INCORRECT_LENGTH = 7 - OBJECT_CRC_FAILED = 8 - OBJECT_UNRECOGNIZED = 9 + KERNEL_FINISHED = 7 + KERNEL_STARTUP_FAILED = 8 + KERNEL_EXCEPTION = 9 - KERNEL_FINISHED = 10 - KERNEL_STARTUP_FAILED = 11 - KERNEL_EXCEPTION = 12 - - RPC_REQUEST = 13 + RPC_REQUEST = 10 class UnsupportedDevice(Exception): @@ -71,39 +66,35 @@ class CommGeneric: raise NotImplementedError # - def _read(self, length): + def _read_header(self): self.open() - return self.read(length) - def _write(self, data): - self.open() - self.write(data) - - def _get_device_msg(self): - while True: - (reply, ) = struct.unpack("B", self._read(1)) - msg = _D2HMsgType(reply) - if msg == _D2HMsgType.LOG: - (length, ) = struct.unpack(">h", self._read(2)) - log_message = "" - for i in range(length): - (c, ) = struct.unpack("B", self._read(1)) - log_message += chr(c) - logger.info("DEVICE LOG: %s", log_message) + sync_count = 0 + while sync_count < 4: + (c, ) = struct.unpack("B", self.read(1)) + if c == 0x5a: + sync_count += 1 else: - logger.debug("message received: %r", msg) - return msg + sync_count = 0 + length, tyv = struct.unpack(">lB", self.read(5)) + ty = _D2HMsgType(tyv) + logger.debug("receiving message: type=%r length=%d", ty, length) + return length, ty + + def _write_header(self, length, ty): + self.open() + logger.debug("sending message: type=%r length=%d", ty, length) + self.write(struct.pack(">llB", 0x5a5a5a5a, length, ty.value)) def get_runtime_env(self): - self._write(struct.pack(">lb", 0x5a5a5a5a, - _H2DMsgType.REQUEST_IDENT.value)) - msg = self._get_device_msg() - if msg != _D2HMsgType.IDENT: - raise IOError("Incorrect reply from device: {}".format(msg)) - (reply, ) = struct.unpack("B", self._read(1)) + self._write_header(9, _H2DMsgType.IDENT_REQUEST) + _, ty = self._read_header() + if ty != _D2HMsgType.IDENT_REPLY: + raise IOError("Incorrect reply from device: {}".format(ty)) + (reply, ) = struct.unpack("B", self.read(1)) runtime_id = chr(reply) for i in range(3): - (reply, ) = struct.unpack("B", self._read(1)) + (reply, ) = struct.unpack("B", self.read(1)) runtime_id += chr(reply) if runtime_id != "AROR": raise UnsupportedDevice("Unsupported runtime ID: {}" @@ -111,63 +102,59 @@ class CommGeneric: return Environment() def switch_clock(self, external): - self._write(struct.pack( - ">lbb", 0x5a5a5a5a, _H2DMsgType.SWITCH_CLOCK.value, - int(external))) - msg = self._get_device_msg() - if msg != _D2HMsgType.CLOCK_SWITCH_COMPLETED: - raise IOError("Incorrect reply from device: {}".format(msg)) + self._write_header(10, _H2DMsgType.SWITCH_CLOCK) + self.write(struct.pack("B", int(external))) + _, ty = self._read_header() + if ty != _D2HMsgType.CLOCK_SWITCH_COMPLETED: + raise IOError("Incorrect reply from device: {}".format(ty)) def load(self, kcode): - self._write(struct.pack( - ">lblL", - 0x5a5a5a5a, _H2DMsgType.LOAD_OBJECT.value, - len(kcode), zlib.crc32(kcode))) - self._write(kcode) - msg = self._get_device_msg() - if msg != _D2HMsgType.OBJECT_LOADED: - raise IOError("Incorrect reply from device: "+str(msg)) + self._write_header(len(kcode) + 9, _H2DMsgType.LOAD_OBJECT) + self.write(kcode) + _, ty = self._read_header() + if ty != _D2HMsgType.LOAD_COMPLETED: + raise IOError("Incorrect reply from device: "+str(ty)) def run(self, kname): - self._write(struct.pack( - ">lbl", 0x5a5a5a5a, _H2DMsgType.RUN_KERNEL.value, len(kname))) - for c in kname: - self._write(struct.pack(">B", ord(c))) + self._write_header(len(kname) + 9, _H2DMsgType.RUN_KERNEL) + self.write(bytes(kname, "ascii")) logger.debug("running kernel: %s", kname) def _receive_rpc_values(self): r = [] while True: - type_tag = chr(struct.unpack(">B", self._read(1))[0]) + type_tag = chr(struct.unpack("B", self.read(1))[0]) if type_tag == "\x00": return r if type_tag == "n": r.append(None) if type_tag == "b": - r.append(bool(struct.unpack(">B", self._read(1))[0])) + r.append(bool(struct.unpack("B", self.read(1))[0])) if type_tag == "i": - r.append(struct.unpack(">l", self._read(4))[0]) + r.append(struct.unpack(">l", self.read(4))[0]) if type_tag == "I": - r.append(struct.unpack(">q", self._read(8))[0]) + r.append(struct.unpack(">q", self.read(8))[0]) if type_tag == "f": - r.append(struct.unpack(">d", self._read(8))[0]) + r.append(struct.unpack(">d", self.read(8))[0]) if type_tag == "F": - n, d = struct.unpack(">qq", self._read(16)) + n, d = struct.unpack(">qq", self.read(16)) r.append(Fraction(n, d)) if type_tag == "l": r.append(self._receive_rpc_values()) def _serve_rpc(self, rpc_wrapper, rpc_map, user_exception_map): - rpc_num = struct.unpack(">h", self._read(2))[0] + rpc_num = struct.unpack(">l", self.read(4))[0] args = self._receive_rpc_values() logger.debug("rpc service: %d %r", rpc_num, args) eid, r = rpc_wrapper.run_rpc( user_exception_map, rpc_map[rpc_num], args) - self._write(struct.pack(">ll", eid, r)) - logger.debug("rpc service: %d %r == %r", rpc_num, args, r) + self._write_header(9+2*4, _H2DMsgType.RPC_REPLY) + self.write(struct.pack(">ll", eid, r)) + logger.debug("rpc service: %d %r == %r (eid %d)", rpc_num, args, + r, eid) def _serve_exception(self, rpc_wrapper, user_exception_map): - eid, p0, p1, p2 = struct.unpack(">lqqq", self._read(4+3*8)) + eid, p0, p1, p2 = struct.unpack(">lqqq", self.read(4+3*8)) rpc_wrapper.filter_rpc_exception(eid) if eid < core_language.first_user_eid: exception = runtime_exceptions.exception_map[eid] @@ -179,17 +166,24 @@ class CommGeneric: def serve(self, rpc_map, user_exception_map): rpc_wrapper = RPCWrapper() while True: - msg = self._get_device_msg() - if msg == _D2HMsgType.RPC_REQUEST: + _, ty = self._read_header() + if ty == _D2HMsgType.RPC_REQUEST: self._serve_rpc(rpc_wrapper, rpc_map, user_exception_map) - elif msg == _D2HMsgType.KERNEL_EXCEPTION: + elif ty == _D2HMsgType.KERNEL_EXCEPTION: self._serve_exception(rpc_wrapper, user_exception_map) - elif msg == _D2HMsgType.KERNEL_FINISHED: + elif ty == _D2HMsgType.KERNEL_FINISHED: return else: - raise IOError("Incorrect request from device: "+str(msg)) + raise IOError("Incorrect request from device: "+str(ty)) - def send_link_message(self, data): - self._write(struct.pack( - ">lb", 0x5a5a5a5a, _H2DMsgType.LINK_MESSAGE.value)) - self._write(data) + def get_log(self): + self._write_header(9, _H2DMsgType.LOG_REQUEST) + length, ty = self._read_header() + if ty != _D2HMsgType.LOG_REPLY: + raise IOError("Incorrect request from device: "+str(ty)) + r = "" + for i in range(length - 9): + c = struct.unpack("B", self.read(1))[0] + if c: + r += chr(c) + return r diff --git a/artiq/coredevice/comm_serial.py b/artiq/coredevice/comm_serial.py index 5eccd3b45..691b43928 100644 --- a/artiq/coredevice/comm_serial.py +++ b/artiq/coredevice/comm_serial.py @@ -17,15 +17,12 @@ class Comm(CommGeneric, AutoDB): def open(self): if hasattr(self, "port"): return - self.port = serial.serial_for_url(self.serial_dev, baudrate=115200) - self.port.flush() - self.set_remote_baud(self.baud_rate) - self.set_baud(self.baud_rate) + self.port = serial.serial_for_url(self.serial_dev, + baudrate=self.baud_rate) def close(self): if not hasattr(self, "port"): return - self.set_remote_baud(115200) self.port.close() del self.port @@ -42,27 +39,3 @@ class Comm(CommGeneric, AutoDB): written = self.port.write(data[pos:]) remaining -= written pos += written - - def set_baud(self, baud): - self.port.baudrate = baud - self.port.flush() - logger.debug("local baud rate set to %d", baud) - - def set_remote_baud(self, baud): - self.send_link_message(struct.pack(">l", baud)) - handshake = 0 - fails = 0 - while handshake < 4: - (recv, ) = struct.unpack("B", self.read(1)) - if recv == 0x5a: - handshake += 1 - else: - # FIXME: when loading immediately after a board reset, - # we erroneously get some zeros back. - logger.warning("unexpected sync character: %02x", recv) - handshake = 0 - if recv != 0: - fails += 1 - if fails > 3: - raise IOError("Baudrate ack failed") - logger.debug("remote baud rate set to %d", baud) diff --git a/soc/runtime/Makefile b/soc/runtime/Makefile index df6642548..100f72b2d 100644 --- a/soc/runtime/Makefile +++ b/soc/runtime/Makefile @@ -1,6 +1,6 @@ include $(MSCDIR)/software/common.mak -OBJECTS := isr.o elf_loader.o services.o comm_serial.o test_mode.o main.o +OBJECTS := isr.o elf_loader.o services.o session.o log.o test_mode.o kloader.o main.o OBJECTS_KSUPPORT := exception_jmp.o exceptions.o rtio.o dds.o # NOTE: this does not handle dependencies well. Run "make clean" @@ -8,7 +8,7 @@ OBJECTS_KSUPPORT := exception_jmp.o exceptions.o rtio.o dds.o UNIPROCESSOR := $(shell printf "\#include \nCSR_KERNEL_CPU_BASE" | $(CC_normal) $(CFLAGS) -E - | tail -n 1 | grep -c CSR_KERNEL_CPU_BASE) ifeq ($(UNIPROCESSOR),0) -OBJECTS += mailbox.o kernelcpu.o ksupport_data.o +OBJECTS += mailbox.o ksupport_data.o OBJECTS_KSUPPORT += mailbox.o bridge.o ksupport.o CFLAGS += -DARTIQ_AMP SERVICE_TABLE_INPUT = ksupport.elf diff --git a/soc/runtime/comm.h b/soc/runtime/comm.h deleted file mode 100644 index bf4d9f91e..000000000 --- a/soc/runtime/comm.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __COMM_H -#define __COMM_H - -#include - -enum { - KERNEL_RUN_INVALID_STATUS, - - KERNEL_RUN_FINISHED, - KERNEL_RUN_EXCEPTION, - KERNEL_RUN_STARTUP_FAILED -}; - -typedef int (*object_loader)(void *, int); -typedef int (*kernel_runner)(const char *, int *, long long int *); - -void comm_serve(object_loader load_object, kernel_runner run_kernel); -void comm_rpc_va(int rpc_num, va_list args, int *eid, int *retval); -int comm_rpc(int rpc_num, ...); -void comm_log_va(const char *fmt, va_list args); -void comm_log(const char *fmt, ...); - -#endif /* __COMM_H */ diff --git a/soc/runtime/comm_serial.c b/soc/runtime/comm_serial.c deleted file mode 100644 index 9822b2316..000000000 --- a/soc/runtime/comm_serial.c +++ /dev/null @@ -1,292 +0,0 @@ -#include -#include -#include -#include - -#include "comm.h" - -#ifndef ARTIQ_AMP -#include "exceptions.h" -#endif - - -/* host to device */ -enum { - MSGTYPE_SET_BAUD_RATE = 1, - - MSGTYPE_REQUEST_IDENT, - MSGTYPE_SWITCH_CLOCK, - - MSGTYPE_LOAD_OBJECT, - MSGTYPE_RUN_KERNEL, -}; - -/* device to host */ -enum { - MSGTYPE_MESSAGE_UNRECOGNIZED = 1, - MSGTYPE_LOG, - - MSGTYPE_IDENT, - MSGTYPE_CLOCK_SWITCH_COMPLETED, - MSGTYPE_CLOCK_SWITCH_FAILED, - - MSGTYPE_OBJECT_LOADED, - MSGTYPE_OBJECT_INCORRECT_LENGTH, - MSGTYPE_OBJECT_CRC_FAILED, - MSGTYPE_OBJECT_UNRECOGNIZED, - - MSGTYPE_KERNEL_FINISHED, - MSGTYPE_KERNEL_STARTUP_FAILED, - MSGTYPE_KERNEL_EXCEPTION, - - MSGTYPE_RPC_REQUEST, -}; - -static int receive_int(void) -{ - unsigned int r; - int i; - - r = 0; - for(i=0;i<4;i++) { - r <<= 8; - r |= (unsigned char)uart_read(); - } - return r; -} - -static char receive_char(void) -{ - return uart_read(); -} - -static void send_llint(long long int x) -{ - int i; - - for(i=0;i<8;i++) { - uart_write((x & 0xff00000000000000LL) >> 56); - x <<= 8; - } -} - -static void send_int(int x) -{ - int i; - - for(i=0;i<4;i++) { - uart_write((x & 0xff000000) >> 24); - x <<= 8; - } -} - -static void send_sint(short int i) -{ - uart_write((i >> 8) & 0xff); - uart_write(i & 0xff); -} - -static void send_char(char c) -{ - uart_write(c); -} - -static void receive_sync(void) -{ - char c; - int recognized; - - recognized = 0; - while(recognized < 4) { - c = uart_read(); - if(c == 0x5a) - recognized++; - else - recognized = 0; - } -} - -static void receive_and_load_object(object_loader load_object) -{ - int length; - int i; - unsigned char buffer[256*1024]; - unsigned int crc; - - length = receive_int(); - if(length > sizeof(buffer)) { - send_char(MSGTYPE_OBJECT_INCORRECT_LENGTH); - return; - } - crc = receive_int(); - for(i=0;i (sizeof(kernel_name)-1)) { - send_char(MSGTYPE_OBJECT_INCORRECT_LENGTH); - return; - } - for(i=0;i> 8, (char *)value + p); - send_char(0); - return p; - } - return 0; -} - -void comm_rpc_va(int rpc_num, va_list args, int *eid, int *retval) -{ - int type_tag; - - send_char(MSGTYPE_RPC_REQUEST); - send_sint(rpc_num); - - while((type_tag = va_arg(args, int))) - send_value(type_tag, type_tag == 'n' ? NULL : va_arg(args, void *)); - send_char(0); - - *eid = receive_int(); - *retval = receive_int(); -} - -#ifndef ARTIQ_AMP -int comm_rpc(int rpc_num, ...) -{ - va_list args; - int eid, retval; - - va_start(args, rpc_num); - comm_rpc_va(rpc_num, args, &eid, &retval); - va_end(args); - - if(eid != EID_NONE) - exception_raise(eid); - return retval; -} -#endif - -void comm_log_va(const char *fmt, va_list args) -{ - int len; - char outbuf[256]; - int i; - - len = vscnprintf(outbuf, sizeof(outbuf), fmt, args); - - send_char(MSGTYPE_LOG); - send_sint(len); - for(i=0;i -#include "comm.h" +#include "log.h" #include "elf_loader.h" #define EI_NIDENT 16 @@ -85,11 +85,11 @@ struct elf32_sym { #define SANITIZE_OFFSET_SIZE(offset, size) \ if(offset > 0x10000000) { \ - comm_log("Incorrect offset in ELF data"); \ + log("Incorrect offset in ELF data"); \ return 0; \ } \ if((offset + size) > elf_length) { \ - comm_log("Attempted to access past the end of ELF data"); \ + log("Attempted to access past the end of ELF data"); \ return 0; \ } @@ -121,7 +121,7 @@ static int fixup(void *dest, int dest_length, struct elf32_rela *rela, void *tar val = _target - (_dest + offset); _dest[offset] = (_dest[offset] & 0xfc000000) | (val & 0x03ffffff); } else - comm_log("Unsupported relocation type: %d", type); + log("Unsupported relocation type: %d", type); return 1; } @@ -141,15 +141,15 @@ int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data, /* validate ELF */ GET_POINTER_SAFE(ehdr, struct elf32_ehdr, 0); if(memcmp(ehdr->ident, elf_magic_header, sizeof(elf_magic_header)) != 0) { - comm_log("Incorrect ELF header"); + log("Incorrect ELF header"); return 0; } if(ehdr->type != ET_REL) { - comm_log("ELF is not relocatable"); + log("ELF is not relocatable"); return 0; } if(ehdr->machine != EM_OR1K) { - comm_log("ELF is for a different machine"); + log("ELF is for a different machine"); return 0; } @@ -190,7 +190,7 @@ int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data, /* load .text section */ if(textsize > dest_length) { - comm_log(".text section is too large"); + log(".text section is too large"); return 0; } memcpy(dest, (char *)elf_data + textoff, textsize); @@ -209,13 +209,13 @@ int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data, name = (char *)elf_data + strtaboff + sym->name; target = resolver(name); if(target == NULL) { - comm_log("Undefined symbol: %s", name); + log("Undefined symbol: %s", name); return 0; } if(!fixup(dest, dest_length, rela, target)) return 0; } else { - comm_log("Unsupported relocation"); + log("Unsupported relocation"); return 0; } } diff --git a/soc/runtime/exceptions.c b/soc/runtime/exceptions.c index 6c7207fc2..5c82f5c43 100644 --- a/soc/runtime/exceptions.c +++ b/soc/runtime/exceptions.c @@ -1,6 +1,6 @@ #include -#include "comm.h" +#include "log.h" #include "exceptions.h" #define MAX_EXCEPTION_CONTEXTS 64 @@ -52,7 +52,7 @@ void exception_raise_params(int id, stored_params[2] = p2; exception_longjmp(exception_contexts[--ec_top].jb); } else { - comm_log("ERROR: uncaught exception, ID=%d\n", id); + log("ERROR: uncaught exception, ID=%d\n", id); while(1); } } diff --git a/soc/runtime/gen_service_table.py b/soc/runtime/gen_service_table.py index 1e1e0aee1..9721920c0 100755 --- a/soc/runtime/gen_service_table.py +++ b/soc/runtime/gen_service_table.py @@ -5,7 +5,7 @@ import sys services = [ ("syscalls", [ - ("rpc", "comm_rpc"), + ("rpc", "rpc"), ("rtio_set_o", "rtio_set_o"), ("rtio_set_oe", "rtio_set_oe"), ("rtio_set_sensitivity", "rtio_set_sensitivity"), diff --git a/soc/runtime/kernelcpu.c b/soc/runtime/kernelcpu.c deleted file mode 100644 index ca42632d4..000000000 --- a/soc/runtime/kernelcpu.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -#include - -#include "mailbox.h" -#include "kernelcpu.h" - -extern char _binary_ksupport_bin_start; -extern char _binary_ksupport_bin_end; - -void kernelcpu_start(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 kernelcpu_stop(void) -{ - kernel_cpu_reset_write(1); -} diff --git a/soc/runtime/kernelcpu.h b/soc/runtime/kernelcpu.h deleted file mode 100644 index 46259e43b..000000000 --- a/soc/runtime/kernelcpu.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __KERNELCPU_H -#define __KERNELCPU_H - -#include - -#define KERNELCPU_EXEC_ADDRESS 0x40020000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x40024000 - -void kernelcpu_start(void *addr); -void kernelcpu_stop(void); - -#endif /* __KERNELCPU_H */ diff --git a/soc/runtime/kloader.c b/soc/runtime/kloader.c new file mode 100644 index 000000000..c421ac652 --- /dev/null +++ b/soc/runtime/kloader.c @@ -0,0 +1,111 @@ +#include +#include + +#include "log.h" +#include "mailbox.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) +{ +#ifdef ARTIQ_AMP + if(!kernel_cpu_reset_read()) { + log("BUG: attempted to load while kernel CPU running"); + return 0; + } +#endif + 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); +} + +#ifdef ARTIQ_AMP + +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); +} + +void kloader_start_idle_kernel(void) +{ + if(!kernel_cpu_reset_read()) { + log("BUG: attempted to start kernel CPU while already running (idle kernel)"); + return; + } + /* TODO */ +} + +void kloader_stop_kernel(void) +{ + kernel_cpu_reset_write(1); +} + +#endif diff --git a/soc/runtime/kloader.h b/soc/runtime/kloader.h new file mode 100644 index 000000000..380ca2fb3 --- /dev/null +++ b/soc/runtime/kloader.h @@ -0,0 +1,19 @@ +#ifndef __KLOADER_H +#define __KLOADER_H + +#define KERNELCPU_EXEC_ADDRESS 0x40020000 +#define KERNELCPU_PAYLOAD_ADDRESS 0x40024000 + +typedef void (*kernel_function)(void); + +int kloader_load(void *buffer, int length); +kernel_function kloader_find(const char *name); + +#ifdef ARTIQ_AMP +void kloader_start_bridge(void); +void kloader_start_idle_kernel(void); +void kloader_start_user_kernel(kernel_function k); +void kloader_stop_kernel(void); +#endif + +#endif /* __KLOADER_H */ diff --git a/soc/runtime/ksupport.c b/soc/runtime/ksupport.c index dbeac95fa..81b5cd25c 100644 --- a/soc/runtime/ksupport.c +++ b/soc/runtime/ksupport.c @@ -7,8 +7,10 @@ #include "rtio.h" #include "dds.h" -/* for the prototypes for comm_rpc and comm_log */ -#include "comm.h" +/* for the prototype for rpc() */ +#include "session.h" +/* for the prototype for log() */ +#include "log.h" void exception_handler(unsigned long vect, unsigned long *sp); void exception_handler(unsigned long vect, unsigned long *sp) @@ -59,7 +61,7 @@ int main(void) while(1); } -int comm_rpc(int rpc_num, ...) +int rpc(int rpc_num, ...) { struct msg_rpc_request request; struct msg_rpc_reply *reply; @@ -83,7 +85,7 @@ int comm_rpc(int rpc_num, ...) return retval; } -void comm_log(const char *fmt, ...) +void log(const char *fmt, ...) { struct msg_log request; diff --git a/soc/runtime/log.c b/soc/runtime/log.c new file mode 100644 index 000000000..931a50ecf --- /dev/null +++ b/soc/runtime/log.c @@ -0,0 +1,41 @@ +#include +#include + +#include "log.h" + +static int buffer_index; +static char buffer[LOG_BUFFER_SIZE]; + +void log_va(const char *fmt, va_list args) +{ + char outbuf[256]; + int i, len; + + len = vscnprintf(outbuf, sizeof(outbuf), fmt, args); + for(i=0;i + +#define LOG_BUFFER_SIZE 4096 + +void log_va(const char *fmt, va_list args); +void log(const char *fmt, ...); + +void log_get(char *outbuf); + +#endif /* __LOG_H */ diff --git a/soc/runtime/main.c b/soc/runtime/main.c index 3fa54d11c..873e2aaca 100644 --- a/soc/runtime/main.c +++ b/soc/runtime/main.c @@ -8,153 +8,41 @@ #include #include "test_mode.h" -#include "comm.h" -#include "kernelcpu.h" -#include "elf_loader.h" -#include "exceptions.h" -#include "services.h" -#include "rtio.h" -#include "dds.h" +#include "session.h" -#ifdef ARTIQ_AMP -#include "mailbox.h" -#include "messages.h" -#endif - -static struct symbol symtab[128]; -static int _symtab_count; -static char _symtab_strings[128*16]; -static char *_symtab_strptr; - -static void symtab_init(void) +void comm_service(void) { - memset(symtab, 0, sizeof(symtab)); - _symtab_count = 0; - _symtab_strptr = _symtab_strings; + char *txdata; + int txlen; + static char rxdata; + static int rxpending; + int r, i; + + if(!rxpending && uart_read_nonblock()) { + rxdata = uart_read(); + rxpending = 1; + } + if(rxpending) { + r = session_input(&rxdata, 1); + if(r > 0) + rxpending = 0; + } + + session_poll((void **)&txdata, &txlen); + if(txlen > 0) { + for(i=0;i= sizeof(symtab)/sizeof(symtab[0])) { - comm_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)]) { - comm_log("Provided symbol string table overflow"); - symtab_init(); - return 0; - } - *_symtab_strptr = *name; - _symtab_strptr++; - if(*name == 0) - break; - name++; - } - - return 1; + session_start(); + while(1) + comm_service(); } -static int load_object(void *buffer, int length) -{ - symtab_init(); - return load_elf( - resolve_service_symbol, symtab_add, - buffer, length, (void *)KERNELCPU_PAYLOAD_ADDRESS, 4*1024*1024); -} - - -#ifdef ARTIQ_AMP -static int process_msg(struct msg_base *umsg, int *eid, long long int *eparams) -{ - int i; - - switch(umsg->type) { - case MESSAGE_TYPE_FINISHED: - return KERNEL_RUN_FINISHED; - case MESSAGE_TYPE_EXCEPTION: { - struct msg_exception *msg = (struct msg_exception *)umsg; - - *eid = msg->eid; - for(i=0;i<3;i++) - eparams[i] = msg->eparams[i]; - return KERNEL_RUN_EXCEPTION; - } - case MESSAGE_TYPE_RPC_REQUEST: { - struct msg_rpc_request *msg = (struct msg_rpc_request *)umsg; - struct msg_rpc_reply reply; - - reply.type = MESSAGE_TYPE_RPC_REPLY; - comm_rpc_va(msg->rpc_num, msg->args, &reply.eid, &reply.retval); - mailbox_send_and_wait(&reply); - return KERNEL_RUN_INVALID_STATUS; - } - case MESSAGE_TYPE_LOG: { - struct msg_log *msg = (struct msg_log *)umsg; - - comm_log(msg->fmt, msg->args); - return KERNEL_RUN_INVALID_STATUS; - } - default: - *eid = EID_INTERNAL_ERROR; - for(i=0;i<3;i++) - eparams[i] = 0; - return KERNEL_RUN_EXCEPTION; - } -} -#endif - -typedef void (*kernel_function)(void); - -static int run_kernel(const char *kernel_name, int *eid, long long int *eparams) -{ - kernel_function k; -#ifdef ARTIQ_AMP - int r; -#else - void *jb; -#endif - - k = find_symbol(symtab, kernel_name); - if(k == NULL) { - comm_log("Failed to find kernel entry point '%s' in object", kernel_name); - return KERNEL_RUN_STARTUP_FAILED; - } - -#ifdef ARTIQ_AMP - kernelcpu_start(k); - while(1) { - struct msg_base *umsg; - - umsg = mailbox_receive(); - r = KERNEL_RUN_INVALID_STATUS; - if(umsg) - r = process_msg(umsg, eid, eparams); - if(r != KERNEL_RUN_INVALID_STATUS) - break; - } - kernelcpu_stop(); - return r; -#else - jb = exception_push(); - if(exception_setjmp(jb)) { - *eid = exception_getid(eparams); - return KERNEL_RUN_EXCEPTION; - } else { - dds_init(); - rtio_init(); - flush_cpu_icache(); - k(); - exception_pop(1); - return KERNEL_RUN_FINISHED; - } -#endif -} static void blink_led(void) { @@ -208,7 +96,7 @@ int main(void) test_main(); } else { puts("Entering regular mode."); - comm_serve(load_object, run_kernel); + regular_main(); } return 0; } diff --git a/soc/runtime/services.c b/soc/runtime/services.c index 8c602379f..720df7afa 100644 --- a/soc/runtime/services.c +++ b/soc/runtime/services.c @@ -1,7 +1,7 @@ #include #include "elf_loader.h" -#include "comm.h" +#include "session.h" #include "rtio.h" #include "dds.h" #include "exceptions.h" diff --git a/soc/runtime/session.c b/soc/runtime/session.c new file mode 100644 index 000000000..5fbb906b0 --- /dev/null +++ b/soc/runtime/session.c @@ -0,0 +1,503 @@ +#include +#include + +#include + +#ifdef ARTIQ_AMP +#include "mailbox.h" +#include "messages.h" +#else +#include +#include "exceptions.h" +#include "rtio.h" +#include "dds.h" +#endif + +#include "log.h" +#include "kloader.h" +#include "exceptions.h" +#include "session.h" + +#define BUFFER_IN_SIZE (32*1024) +#define BUFFER_OUT_SIZE (32*1024) + +static int buffer_in_index; +/* The 9th byte (right after the header) of buffer_in must be aligned + * to a 32-bit boundary for elf_loader to work. + */ +static struct { + char padding[3]; + char data[BUFFER_IN_SIZE]; +} __attribute__((packed)) _buffer_in __attribute__((aligned(4))); +#define buffer_in _buffer_in.data +static int buffer_out_index; +static char buffer_out[BUFFER_OUT_SIZE]; + +static int get_in_packet_len(void) +{ + int r; + + memcpy(&r, &buffer_in[4], 4); + return r; +} + +static int get_out_packet_len(void) +{ + int r; + + memcpy(&r, &buffer_out[4], 4); + return r; +} + +static void submit_output(int len) +{ + memset(&buffer_out[0], 0x5a, 4); + memcpy(&buffer_out[4], &len, 4); + buffer_out_index = 0; +} + +static int user_kernel_state; + +enum { + USER_KERNEL_NONE = 0, + USER_KERNEL_LOADED, + USER_KERNEL_RUNNING, + USER_KERNEL_WAIT_RPC /* < must come after _RUNNING */ +}; + +void session_start(void) +{ + buffer_in_index = 0; + buffer_out_index = 0; + memset(&buffer_out[4], 0, 4); +#ifdef ARTIQ_AMP + kloader_stop_kernel(); +#endif + user_kernel_state = USER_KERNEL_NONE; +} + +void session_end(void) +{ +#ifdef ARTIQ_AMP + kloader_stop_kernel(); + kloader_start_idle_kernel(); +#endif +} + +/* host to device */ +enum { + REMOTEMSG_TYPE_LOG_REQUEST = 1, + REMOTEMSG_TYPE_IDENT_REQUEST, + REMOTEMSG_TYPE_SWITCH_CLOCK, + + REMOTEMSG_TYPE_LOAD_OBJECT, + REMOTEMSG_TYPE_RUN_KERNEL, + + REMOTEMSG_TYPE_RPC_REPLY +}; + +/* device to host */ +enum { + REMOTEMSG_TYPE_LOG_REPLY = 1, + REMOTEMSG_TYPE_IDENT_REPLY, + REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED, + REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED, + + REMOTEMSG_TYPE_LOAD_COMPLETED, + REMOTEMSG_TYPE_LOAD_FAILED, + + REMOTEMSG_TYPE_KERNEL_FINISHED, + REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED, + REMOTEMSG_TYPE_KERNEL_EXCEPTION, + + REMOTEMSG_TYPE_RPC_REQUEST, +}; + +static int add_rpc_value(int bi, int type_tag, void *value) +{ + char base_type; + int obi, r; + int i, p; + int len; + + obi = bi; + base_type = type_tag; + + if((bi + 1) > BUFFER_OUT_SIZE) + return -1; + buffer_out[bi++] = base_type; + + switch(base_type) { + case 'n': + return bi - obi; + case 'b': + if((bi + 1) > BUFFER_OUT_SIZE) + return -1; + if(*(char *)value) + buffer_out[bi++] = 1; + else + buffer_out[bi++] = 0; + return bi - obi; + case 'i': + if((bi + 4) > BUFFER_OUT_SIZE) + return -1; + memcpy(&buffer_out[bi], value, 4); + bi += 4; + return bi - obi; + case 'I': + case 'f': + if((bi + 8) > BUFFER_OUT_SIZE) + return -1; + memcpy(&buffer_out[bi], value, 8); + bi += 8; + return bi - obi; + case 'F': + if((bi + 16) > BUFFER_OUT_SIZE) + return -1; + memcpy(&buffer_out[bi], value, 16); + bi += 16; + return bi - obi; + case 'l': + len = *(int *)value; + p = 4; + for(i=0;i> 8, (char *)value + p); + if(r < 0) + return r; + bi += r; + p += r; + } + if((bi + 1) > BUFFER_OUT_SIZE) + return -1; + buffer_out[bi++] = 0; + return bi - obi; + } + return -1; +} + +static int send_rpc_request(int rpc_num, va_list args) +{ + int r; + int bi = 8; + int type_tag; + + buffer_out[bi++] = REMOTEMSG_TYPE_RPC_REQUEST; + + memcpy(&buffer_out[bi], &rpc_num, 4); + bi += 4; + + while((type_tag = va_arg(args, int))) { + r = add_rpc_value(bi, type_tag, + type_tag == 'n' ? NULL : va_arg(args, void *)); + if(bi < 0) + return 0; + bi += r; + } + if((bi + 1) > BUFFER_OUT_SIZE) + return 0; + buffer_out[bi++] = 0; + + submit_output(bi); + return 1; +} + +#ifndef ARTIQ_AMP +static int rpc_reply_eid; +static int rpc_reply_retval; + +int rpc(int rpc_num, ...) +{ + va_list args; + + va_start(args, rpc_num); + send_rpc_request(rpc_num, args); + va_end(args); + + user_kernel_state = USER_KERNEL_WAIT_RPC; + while(user_kernel_state == USER_KERNEL_WAIT_RPC) + comm_service(); + + if(rpc_reply_eid != EID_NONE) + exception_raise(rpc_reply_eid); + return rpc_reply_retval; +} + +/* assumes output buffer is empty when called */ +static void run_kernel_up(kernel_function k) +{ + void *jb; + int eid; + long long eparams[3]; + + jb = exception_push(); + if(exception_setjmp(jb)) { + eid = exception_getid(eparams); + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_EXCEPTION; + memcpy(&buffer_out[9], &eid, 4); + memcpy(&buffer_out[13], eparams, 3*8); + submit_output(9+4+3*8); + } else { + dds_init(); + rtio_init(); + flush_cpu_icache(); + k(); + exception_pop(1); + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_FINISHED; + submit_output(9); + } +} +#endif /* !ARTIQ_AMP */ + +static int process_input(void) +{ + switch(buffer_in[8]) { + case REMOTEMSG_TYPE_LOG_REQUEST: +#if (LOG_BUFFER_SIZE + 9) > BUFFER_OUT_SIZE +#error Output buffer cannot hold the log buffer +#endif + buffer_out[8] = REMOTEMSG_TYPE_LOG_REPLY; + log_get(&buffer_out[9]); + submit_output(9 + LOG_BUFFER_SIZE); + break; + case REMOTEMSG_TYPE_IDENT_REQUEST: + buffer_out[8] = REMOTEMSG_TYPE_IDENT_REPLY; + buffer_out[9] = 'A'; + buffer_out[10] = 'R'; + buffer_out[11] = 'O'; + buffer_out[12] = 'R'; + submit_output(13); + break; + case REMOTEMSG_TYPE_SWITCH_CLOCK: + if(user_kernel_state >= USER_KERNEL_RUNNING) { + log("Attempted to switch RTIO clock while kernel running"); + buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED; + submit_output(9); + break; + } + rtiocrg_clock_sel_write(buffer_in[9]); + buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED; + submit_output(9); + break; + case REMOTEMSG_TYPE_LOAD_OBJECT: + if(user_kernel_state >= USER_KERNEL_RUNNING) { + log("Attempted to load new kernel while already running"); + buffer_out[8] = REMOTEMSG_TYPE_LOAD_FAILED; + submit_output(9); + break; + } + if(kloader_load(&buffer_in[9], get_in_packet_len() - 8)) { + buffer_out[8] = REMOTEMSG_TYPE_LOAD_COMPLETED; + user_kernel_state = USER_KERNEL_LOADED; + } else + buffer_out[8] = REMOTEMSG_TYPE_LOAD_FAILED; + submit_output(9); + break; + case REMOTEMSG_TYPE_RUN_KERNEL: { + kernel_function k; + + if(user_kernel_state != USER_KERNEL_LOADED) { + log("Attempted to run kernel while not in the LOADED state"); + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED; + submit_output(9); + break; + } + + if((buffer_in_index + 1) > BUFFER_OUT_SIZE) { + log("Kernel name too long"); + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED; + submit_output(9); + break; + } + buffer_in[buffer_in_index] = 0; + + k = kloader_find((char *)&buffer_in[9]); + if(k == NULL) { + log("Failed to find kernel entry point '%s' in object", &buffer_in[9]); + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED; + submit_output(9); + break; + } + +#ifdef ARTIQ_AMP + kloader_start_user_kernel(k); +#else + run_kernel_up(k); +#endif + user_kernel_state = USER_KERNEL_RUNNING; + break; + } + case REMOTEMSG_TYPE_RPC_REPLY: { +#ifdef ARTIQ_AMP + struct msg_rpc_reply reply; +#endif + + if(user_kernel_state != USER_KERNEL_WAIT_RPC) { + log("Unsolicited RPC reply"); + return 0; + } + +#ifdef ARTIQ_AMP + reply.type = MESSAGE_TYPE_RPC_REPLY; + memcpy(&reply.eid, &buffer_in[9], 4); + memcpy(&reply.retval, &buffer_in[13], 4); + mailbox_send_and_wait(&reply); +#else + memcpy(&rpc_reply_eid, &buffer_in[9], 4); + memcpy(&rpc_reply_retval, &buffer_in[13], 4); +#endif + user_kernel_state = USER_KERNEL_RUNNING; + break; + } + default: + return 0; + } + return 1; +} + +/* Returns -1 in case of irrecoverable error + * (the session must be dropped and session_end called) + */ +int session_input(void *data, int len) +{ + unsigned char *_data = data; + int consumed; + + consumed = 0; + while(len > 0) { + /* Make sure the output buffer is available for any reply + * we might need to send. */ + if(get_out_packet_len() != 0) + return consumed; + + if(buffer_in_index < 4) { + /* synchronizing */ + if(_data[consumed] == 0x5a) + buffer_in[buffer_in_index++] = 0x5a; + else + buffer_in_index = 0; + consumed++; len--; + } else if(buffer_in_index < 8) { + /* receiving length */ + buffer_in[buffer_in_index++] = _data[consumed]; + consumed++; len--; + } else { + /* receiving payload */ + int packet_len; + int count; + + packet_len = get_in_packet_len(); + if(packet_len > BUFFER_IN_SIZE) + return -1; + count = packet_len - buffer_in_index; + if(count > len) + count = len; + memcpy(&buffer_in[buffer_in_index], &_data[consumed], count); + buffer_in_index += count; + + if(buffer_in_index == packet_len) { + if(!process_input()) + return -1; + buffer_in_index = 0; + } + + consumed += count; len -= count; + } + } + return consumed; +} + +#ifdef ARTIQ_AMP +/* assumes output buffer is empty when called */ +static void process_kmsg(struct msg_base *umsg) +{ + if(user_kernel_state != USER_KERNEL_RUNNING) { + log("Received message from kernel CPU while not in running state"); + return; + } + + switch(umsg->type) { + case MESSAGE_TYPE_FINISHED: + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_FINISHED; + submit_output(9); + + kloader_stop_kernel(); + user_kernel_state = USER_KERNEL_LOADED; + break; + case MESSAGE_TYPE_EXCEPTION: { + struct msg_exception *msg = (struct msg_exception *)umsg; + + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_EXCEPTION; + memcpy(&buffer_out[9], &msg->eid, 4); + memcpy(&buffer_out[13], msg->eparams, 3*8); + submit_output(9+4+3*8); + + kloader_stop_kernel(); + user_kernel_state = USER_KERNEL_LOADED; + break; + } + case MESSAGE_TYPE_RPC_REQUEST: { + struct msg_rpc_request *msg = (struct msg_rpc_request *)umsg; + + send_rpc_request(msg->rpc_num, msg->args); + user_kernel_state = USER_KERNEL_WAIT_RPC; + break; + } + case MESSAGE_TYPE_LOG: { + struct msg_log *msg = (struct msg_log *)umsg; + + log(msg->fmt, msg->args); + break; + } + default: { + int eid; + + log("Received invalid message type from kernel CPU"); + + buffer_out[8] = REMOTEMSG_TYPE_KERNEL_EXCEPTION; + eid = EID_INTERNAL_ERROR; + memcpy(&buffer_out[9], &eid, 4); + memset(&buffer_out[13], 0, 3*8); + submit_output(9+4+3*8); + + kloader_stop_kernel(); + user_kernel_state = USER_KERNEL_LOADED; + break; + } + } +} +#endif /* ARTIQ_AMP */ + +void session_poll(void **data, int *len) +{ + int l; + + l = get_out_packet_len(); + +#ifdef ARTIQ_AMP + /* If the output buffer is available, + * check if the kernel CPU has something to transmit. + */ + if(l == 0) { + struct msg_base *umsg; + + umsg = mailbox_receive(); + if(umsg) { + process_kmsg(umsg); + mailbox_acknowledge(); + } + l = get_out_packet_len(); + } +#endif + + *len = l - buffer_out_index; + *data = &buffer_out[buffer_out_index]; +} + +void session_ack(int len) +{ + buffer_out_index += len; + if(buffer_out_index >= get_out_packet_len()) { + memset(&buffer_out[4], 0, 4); + buffer_out_index = 0; + } +} diff --git a/soc/runtime/session.h b/soc/runtime/session.h new file mode 100644 index 000000000..8aab42cbb --- /dev/null +++ b/soc/runtime/session.h @@ -0,0 +1,14 @@ +#ifndef __SESSION_H +#define __SESSION_H + +void session_start(void); +void session_end(void); + +int session_input(void *data, int len); +void session_poll(void **data, int *len); +void session_ack(int len); + +int rpc(int rpc_num, ...); +void comm_service(void); + +#endif /* __SESSION_H */ diff --git a/soc/runtime/test_mode.c b/soc/runtime/test_mode.c index 7c5c9c65a..2e91a19a4 100644 --- a/soc/runtime/test_mode.c +++ b/soc/runtime/test_mode.c @@ -12,7 +12,7 @@ #ifdef ARTIQ_AMP -#include "kernelcpu.h" +#include "kloader.h" #include "mailbox.h" #include "messages.h" @@ -20,7 +20,7 @@ static void amp_bridge_init(void) { struct msg_base *umsg; - kernelcpu_start(NULL); + kloader_start_bridge(); while(1) { umsg = mailbox_wait_and_receive();