slrve/srve.c

399 lines
10 KiB
C
Raw Normal View History

// Simple emulator program
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define SRVE_IMPLEMENTATION
#include "srve.h"
#include "../libc/elf.h"
// Some defines for virtio emulation
#define VIOE_UART0 0x10000000ULL
#define VIOE_UART_SIZE 0x1000ULL
#define VIOE_UART_HOLDING 0x0
#define VIOE_UART_LINESTATUS 0x5
#define VIOE_UART_LINESTATUS_RECEIVEABLE 1
#define VIOE_UART_LINESTATUS_TRANSMITTABLE (1<<5)
// Some defines for new BIOS functionality
#define VMRD_BASEADDRESS 0x0000000010001000UL
#define VMRD_SIZE 0x1000ULL
#define VMRD_REG_MAGIC 0
#define VMRD_REG_BLKSIZE 4
#define VMRD_REG_MEMADDR 8
#define VMRD_REG_BLKADDR 16
#define VMRD_REG_ACTION 24
// The magic number is BA5DB105, which drivers can use to distinguish
// it from virtio.
#define VMRD_MAGIC 0xBA5DB105
// This range needs to cover all I/O areas
#define IORANGE_BEGIN 0x10000000ULL
#define IORANGE_END 0x10002000ULL
typedef struct vmrd vmrd_t;
struct vmrd {
uint64_t memoryaddress;
uint64_t blockaddress;
uint32_t blocksize;
uint32_t action;
FILE* file;
uint64_t imagesize;
};
srve_core64_t core;
// Loads binary into memory at address, returning size or -1 on failure.
int64_t loadf(unsigned char* memory, unsigned long long addr, const char* fname) {
fprintf(stderr, "loadf(%p, 0x%llx, \"%s\");\n", memory, addr, fname);
FILE* f = fopen(fname, "r");
if (!f) {
fprintf(stderr, "Failed to open '%s'\n", fname);
return -1;
}
unsigned long sz = 0;
size_t nread;
while ((nread = fread(memory + addr + sz, 1, 1024*1024, f)) > 0) {
sz += (unsigned long) nread;
}
fclose(f);
}
int uart(int devnum, uint32_t offset, void* buffer, unsigned int storing, unsigned int size) {
//fprintf(stderr, "uart(%i, 0x%x, %p, %d, %d);\n", devnum, offset, buffer, storing, size);
switch (offset) {
case VIOE_UART_HOLDING:
if (storing) {
fflush(stderr);
//printf("char value is 0x%x\n", *((uint8_t*)buffer));
//putc('X',stdout);
putc(*((uint8_t*)buffer), stdout);
fflush(stdout);
//exit(0);
return 0;
} else {
return -1;
}
case VIOE_UART_LINESTATUS:
if (storing) {
return -1;
} else {
*((char*)buffer) = VIOE_UART_LINESTATUS_TRANSMITTABLE;
return 0;
}
default:
fprintf(stderr, "uart: bad offset 0x%x\n", offset);
return 0;
}
}
// Only allow one disk for now, but design as though there may be more.
#define VMRD_MAX 1
vmrd_t* vmrd_array[VMRD_MAX];
uint32_t vmrd_performaction(void* memory, vmrd_t* device, uint32_t action) {
if (memory == NULL || device == NULL || device->file == NULL || device->imagesize == 0) {
return 'f';
}
char* membytes = memory;
switch (action) {
case 't':
core.tracing = 1;
return 's';
case 'e':
core.tracing = 0;
return 's';
case 'r':
if (fseek(device->file, device->blocksize*device->blockaddress, SEEK_SET) != 0) {
return 'f';
}
// TODO: Validate memory address...
if (fread(membytes+device->memoryaddress, device->blocksize, 1, device->file) != 1) {
return 'f';
}
return 's';
default:
return 'f';
}
}
int vmrd(void* memory, int devnum, uint32_t offset, void* buffer, unsigned int storing, unsigned int size) {
vmrd_t* device = &vmrd_array[devnum];
switch (offset) {
case VMRD_REG_MAGIC:
if (!storing && size == 4) {
*((uint32_t*)buffer) = VMRD_MAGIC;
return 0;
} else {
goto fail;
}
case VMRD_REG_BLKADDR:
if (storing && size == 8) {
device->blockaddress = *((uint64_t*)buffer);
return 0;
} else if (!storing && size == 8) {
*((uint64_t*)buffer) = device->blockaddress;
return 0;
} else {
goto fail;
}
case VMRD_REG_MEMADDR:
if (storing && size == 8) {
device->memoryaddress = *((uint64_t*)buffer);
return 0;
} else if (!storing && size == 8) {
*((uint64_t*)buffer) = device->memoryaddress;
return 0;
} else {
goto fail;
}
case VMRD_REG_BLKSIZE:
if (storing && size == 4) {
device->blocksize = *((uint32_t*)buffer);
return 0;
} else if (!storing && size == 4) {
*((uint32_t*)buffer) = device->blocksize;
return 0;
} else {
goto fail;
}
case VMRD_REG_ACTION:
if (storing && size == 4) {
device->action = '*'; // Asterix action state indicates it's in use
device->action = vmrd_performaction(memory, device, *((uint32_t*)buffer));
return 0;
} else if (!storing && size == 4) {
*((uint32_t*)buffer) = device->action;
return 0;
} else {
goto fail;
}
default:
fail:
device->action = 'f';
fprintf(stderr, "vmrd: bad access at offset 0x%x\n", offset);
return -1;
}
return 0;
}
int iofunc(void* memory, uint64_t address, void* buffer, unsigned int storing, unsigned int size) {
if (address >= VIOE_UART0 && address+size <= VIOE_UART0+VIOE_UART_SIZE) {
return uart(0, (uint32_t) (address - VIOE_UART0), buffer, storing, size);
}
if (address >= VMRD_BASEADDRESS && address+size <= VMRD_BASEADDRESS+VMRD_SIZE) {
return vmrd(memory, 0, (uint32_t) (address - VMRD_BASEADDRESS), buffer, storing, size);
}
fprintf(stderr, "iofunc: Bad I/O address 0x%llx\n", address);
return -1;
}
int memfunc(void* memory, uint64_t address, void* buffer, unsigned int storing, unsigned int size) {
if (address >= IORANGE_BEGIN && address+size <= IORANGE_END) {
return iofunc(memory, address, buffer, storing, size);
}
//if (address % size) {
//fprintf(stderr, "memfunc(%p, 0x%lx, %p, %d, %d);\n", memory, address, buffer, storing, size);
//exit(-1);
//}
//address = (address << 32) >> 32;
//fprintf(stderr, "memfunc(%p, 0x%lx, %p, %d, %d);\n", memory, address, buffer, storing, size);
unsigned char* bmemory = memory;
if (size == 4) {
uint32_t* ibuffer = buffer;
uint32_t* istorage = (void*)(bmemory + address);
if (storing) {
*istorage = *ibuffer;
} else {
*ibuffer = *istorage;
}
return 0;
} else if (size == 8) {
uint64_t* ibuffer = buffer;
uint64_t* istorage = (void*)(bmemory + address);
if (storing) {
*istorage = *ibuffer;
} else {
*ibuffer = *istorage;
}
return 0;
} else if (size == 1) {
uint8_t* ibuffer = buffer;
uint8_t* istorage = (void*)(bmemory + address);
if (storing) {
*istorage = *ibuffer;
} else {
*ibuffer = *istorage;
}
return 0;
} else if (size == 2) {
uint16_t* ibuffer = buffer;
uint16_t* istorage = (void*)(bmemory + address);
if (storing) {
*istorage = *ibuffer;
} else {
*ibuffer = *istorage;
}
return 0;
} else {
return -1;
}
}
int hexvalue(char ch) {
switch (ch) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default: return -1;
}
}
char linebuf[1000];
int insertsymbol(srve_debugsymbol_t** symvar, uint64_t addr, char* namebuffer) {
for (int i = 0; namebuffer[i] != 0; i++) {
if (namebuffer[i] == '.') {
return 0; // Skip adding entries for filenames and sections
}
}
srve_debugsymbol_t* sym = malloc(sizeof(srve_debugsymbol_t));
if (!sym) {
return -1;
}
sym->addr = addr;
sym->name = strdup(namebuffer);
sym->next = NULL;
for (int i = 0; sym->name[i] != 0; i++) {
if (sym->name[i] == '\r' || sym->name[i] == '\n') {
sym->name[i] = 0;
break;
}
}
srve_debugsymbol_t* search = *symvar;
if (search == NULL || addr < search->addr) {
sym->next = search;
*symvar = sym;
return 0;
}
while (search->addr < addr) {
if (search->next == NULL || search->next->addr >= addr) {
sym->next = search->next;
search->next = sym;
return 0;
}
search = search->next;
}
sym->next = search->next;
search->next = sym;
return 0;
}
int loadsymbols(srve_debugsymbol_t** symvar, char* fname) {
FILE* f = fopen(fname, "r");
while (fgets(linebuf, 1000, f)) {
char* s = linebuf;
uint64_t x = 0;
int digit;
while ((digit = hexvalue(*s)) >= 0) {
x *= 0x10;
x += digit;
s++;
}
while (*s == ' ') {
s++;
}
if (insertsymbol(symvar, x, s) != 0) {
return -1;
}
}
fclose(f);
return 0;
}
int loaddiskimage(vmrd_t* device, char* fname) {
FILE* f = fopen(fname, "r+");
if (f) {
fseek(f, 0, SEEK_END);
device->file = f;
device->imagesize = ftell(f);
device->action = 'i'; // Initial action state
return 0;
} else {
device->file = NULL;
device->imagesize = 0;
device->action = 'f'; // Failed action state
return -1;
}
}
int main(int argc, char** argv) {
fprintf(stderr, "SecureLang RV64/RV32 emulator, early testing version\n");
char* memory = calloc(4*1024*1024*1024ULL, 1);
if (memory == NULL) {
fprintf(stderr, "Setup failed!\n");
return -1;
}
uint64_t loadaddr = 0x8000000ULL; // TODO: Compiler bug prevents 0x80000000 as a constant, must be an integer conversion somewhere
loadaddr *= 0x10;
//printf("Loading at %dMB\n", loadaddr/(1024*1024));
char* kfname = argv[1];
char* sname = argv[2];
char* fsname = argv[3];
loadf(memory, loadaddr, argv[1]);
srve_debugsymbol_t* syms = NULL;
loadsymbols(&syms, sname);
loaddiskimage(&vmrd_array[0], fsname);
srve_debugsymbol_t* iter = syms;
while (iter) {
fprintf(stderr, "%lx %s\n", iter->addr, iter->name);
iter = iter->next;
}
if (srve_core64_init(&core, &memfunc, memory, loadaddr) != 0) {
return -1;
}
core.symbols = syms;
core.tracing = 0;
int64_t result;
do {
result = srve_core64_doinstr(&core);
} while (result == 0);
return 0;
}