diff options
-rw-r--r-- | pk/devicetree.c | 65 | ||||
-rw-r--r-- | pk/devicetree.h | 8 | ||||
-rw-r--r-- | pk/file.c | 102 | ||||
-rw-r--r-- | pk/file.h | 28 | ||||
-rw-r--r-- | pk/pk.h | 2 | ||||
-rw-r--r-- | pk/syscall.c | 9 | ||||
-rw-r--r-- | pk/vm.c | 31 |
7 files changed, 213 insertions, 32 deletions
diff --git a/pk/devicetree.c b/pk/devicetree.c index 242f013..797f937 100644 --- a/pk/devicetree.c +++ b/pk/devicetree.c @@ -6,6 +6,14 @@ #define ntohl(x) __builtin_bswap32(x) +#define FDT_TABLE_CAP 16 + +struct fdt_table_entry fdt_table[FDT_TABLE_CAP]; +int fdt_table_size = 0; + +uintptr_t fdt_base; +size_t fdt_size; + static uintptr_t max_hart_id; static uint64_t fdt_read_uint64(uint32_t* addr) { @@ -38,6 +46,43 @@ static void fdt_handle_mem(uint32_t* reg_addr, uint32_t reg_len) mem_size = size; } +static void fdt_handle_device(const char *dev_type, + uint32_t *reg_addr, uint32_t reg_len, int prot) +{ + struct fdt_table_entry *entry; + + kassert(reg_len == 16); + kassert(fdt_table_size < FDT_TABLE_CAP); + + entry = &fdt_table[fdt_table_size]; + + entry->dev_type = dev_type; + entry->base = fdt_read_uint64(reg_addr); + entry->size = fdt_read_uint64(reg_addr + 2); + entry->prot = prot; + + debug_printk("found device %s@%lx (size: %ld, prot: %d)\n", + dev_type, entry->base, entry->size, prot); + + fdt_table_size++; +} + +struct fdt_table_entry *fdt_find_device(const char *dev_type, int pos) +{ + int i; + int found = 0; + + for (i = 0; i < fdt_table_size; i++) { + if (strcmp(fdt_table[i].dev_type, dev_type) == 0) { + if (found == pos) + return &fdt_table[i]; + found++; + } + } + + return NULL; +} + // This code makes the following assumptions about FDTs: // - They are trusted and don't need to be sanitized // - All addresses and sizes are 64 bits (we don't parse #address-cells etc) @@ -45,7 +90,7 @@ static void fdt_handle_mem(uint32_t* reg_addr, uint32_t reg_len) static uint32_t* parse_node(uint32_t* token, char* strings) { const char* nodename = (const char*)token, *s, *dev_type = 0, *isa = 0; - uint32_t reg_len = 0, *reg_addr = 0; + uint32_t reg_len = 0, *reg_addr = 0, prot = 0; token = (uint32_t*)nodename + strlen(nodename)/4+1; while (1) switch (ntohl(*token)) { @@ -60,6 +105,8 @@ static uint32_t* parse_node(uint32_t* token, char* strings) } else if (strcmp(name, "reg") == 0) { reg_len = len; reg_addr = token; + } else if (strcmp(name, "protection") == 0) { + prot = ntohl(*token); } token += (len+3)/4; continue; @@ -76,19 +123,25 @@ static uint32_t* parse_node(uint32_t* token, char* strings) } out: - if (dev_type && strcmp(dev_type, "cpu") == 0) - fdt_handle_cpu(isa, reg_addr, reg_len); - else if (dev_type && strcmp(dev_type, "memory") == 0) - fdt_handle_mem(reg_addr, reg_len); + if (dev_type) { + if (strcmp(dev_type, "cpu") == 0) + fdt_handle_cpu(isa, reg_addr, reg_len); + else if (strcmp(dev_type, "memory") == 0) + fdt_handle_mem(reg_addr, reg_len); + else if (prot) + fdt_handle_device(dev_type, reg_addr, reg_len, prot); + } return token+1; } void parse_device_tree() { - struct fdt_header* hdr = (struct fdt_header*)read_csr(miobase); + fdt_base = read_csr(miobase); + struct fdt_header* hdr = (struct fdt_header*) fdt_base; debug_printk("reading device tree at %p\n", hdr); kassert(ntohl(hdr->magic) == FDT_MAGIC); + fdt_size = ntohl(hdr->totalsize); char* strings = (char*)hdr + ntohl(hdr->off_dt_strings); uint32_t* root = (uint32_t*)((char*)hdr + ntohl(hdr->off_dt_struct)); while (ntohl(*root++) != FDT_BEGIN_NODE); diff --git a/pk/devicetree.h b/pk/devicetree.h index 1daab89..ceebaa3 100644 --- a/pk/devicetree.h +++ b/pk/devicetree.h @@ -32,6 +32,14 @@ struct fdt_reserve_entry { uint64_t size; }; +struct fdt_table_entry { + const char *dev_type; + uint64_t base; + uint64_t size; + int prot; +}; + void parse_device_tree(); +struct fdt_table_entry *fdt_find_device(const char *dev_type, int pos); #endif @@ -2,15 +2,19 @@ #include <string.h> #include <errno.h> +#include <fcntl.h> #include "file.h" #include "pk.h" #include "frontend.h" #include "vm.h" +#include "devicetree.h" +#include "sbi.h" +#include "mcall.h" #define MAX_FDS 128 static file_t* fds[MAX_FDS]; #define MAX_FILES 128 -file_t files[MAX_FILES] = {[0 ... MAX_FILES-1] = {-1,0}}; +file_t files[MAX_FILES] = {0}; void file_incref(file_t* f) { @@ -22,11 +26,13 @@ void file_decref(file_t* f) { if (atomic_add(&f->refcnt, -1) == 2) { - int kfd = f->kfd; + enum file_type typ = f->typ; + int kfd = ((host_file_t *) f)->kfd; mb(); atomic_set(&f->refcnt, 0); - frontend_syscall(SYS_close, kfd, 0, 0, 0, 0, 0, 0); + if (typ == FILE_HOST) + frontend_syscall(SYS_close, kfd, 0, 0, 0, 0, 0, 0); } } @@ -55,9 +61,10 @@ void file_init() { // create stdin, stdout, stderr and FDs 0-2 for (int i = 0; i < 3; i++) { - file_t* f = file_get_free(); + host_file_t* f = (host_file_t *) file_get_free(); + f->typ = FILE_HOST; f->kfd = i; - file_dup(f); + file_dup((file_t *) f); } } @@ -82,9 +89,38 @@ file_t* file_open(const char* fn, int flags, int mode) return file_openat(AT_FDCWD, fn, flags, mode); } -file_t* file_openat(int dirfd, const char* fn, int flags, int mode) +device_t *device_open(const char *name, int flags) +{ + struct fdt_table_entry *entry; + device_t *dev; + int retval; + + entry = fdt_find_device(name, 0); + if (entry == NULL) + return NULL; + + // check permissions + if ((entry->prot & PROT_READ) == 0) { + debug_printk("insufficient permissions to access device %s\n", name); + return NULL; + } + + if (flags != O_RDONLY && (entry->prot & PROT_WRITE) == 0) { + debug_printk("device %s is read only\n", name); + return NULL; + } + + dev = (device_t *) file_get_free(); + dev->typ = FILE_DEVICE; + dev->base = entry->base; + dev->size = entry->size; + + return dev; +} + +host_file_t *host_file_openat(int dirfd, const char *fn, int flags, int mode) { - file_t* f = file_get_free(); + host_file_t* f = (host_file_t *) file_get_free(); if (f == NULL) return ERR_PTR(-ENOMEM); @@ -92,16 +128,24 @@ file_t* file_openat(int dirfd, const char* fn, int flags, int mode) long ret = frontend_syscall(SYS_openat, dirfd, (long)fn, fn_size, flags, mode, 0, 0); if (ret >= 0) { + f->typ = FILE_HOST; f->kfd = ret; return f; } else { - file_decref(f); + file_decref((file_t *) f); return ERR_PTR(ret); } } +file_t* file_openat(int dirfd, const char* fn, int flags, int mode) +{ + if (strncmp("/dev/", fn, 5) == 0) + return (file_t *) device_open(fn + 5, flags); + return (file_t *) host_file_openat(dirfd, fn, flags, mode); +} + int fd_close(int fd) { file_t* f = file_get(fd); @@ -118,39 +162,67 @@ int fd_close(int fd) ssize_t file_read(file_t* f, void* buf, size_t size) { populate_mapping(buf, size, PROT_WRITE); - return frontend_syscall(SYS_read, f->kfd, (uintptr_t)buf, size, 0, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_read, hf->kfd, (uintptr_t)buf, size, 0, 0, 0, 0); + } + panic("read not supported on device file\n"); } ssize_t file_pread(file_t* f, void* buf, size_t size, off_t offset) { populate_mapping(buf, size, PROT_WRITE); - return frontend_syscall(SYS_pread, f->kfd, (uintptr_t)buf, size, offset, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_pread, hf->kfd, (uintptr_t)buf, size, offset, 0, 0, 0); + } + panic("pread not supported on device file\n"); } ssize_t file_write(file_t* f, const void* buf, size_t size) { populate_mapping(buf, size, PROT_READ); - return frontend_syscall(SYS_write, f->kfd, (uintptr_t)buf, size, 0, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_write, hf->kfd, (uintptr_t)buf, size, 0, 0, 0, 0); + } + panic("write not supported on device file\n"); } ssize_t file_pwrite(file_t* f, const void* buf, size_t size, off_t offset) { populate_mapping(buf, size, PROT_READ); - return frontend_syscall(SYS_pwrite, f->kfd, (uintptr_t)buf, size, offset, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_pwrite, hf->kfd, (uintptr_t)buf, size, offset, 0, 0, 0); + } + panic("pwrite not supported on device file\n"); } int file_stat(file_t* f, struct stat* s) { populate_mapping(s, sizeof(*s), PROT_WRITE); - return frontend_syscall(SYS_fstat, f->kfd, (uintptr_t)s, 0, 0, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_fstat, hf->kfd, (uintptr_t)s, 0, 0, 0, 0, 0); + } + panic("stat not supported on device file\n"); } int file_truncate(file_t* f, off_t len) { - return frontend_syscall(SYS_ftruncate, f->kfd, len, 0, 0, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_ftruncate, hf->kfd, len, 0, 0, 0, 0, 0); + } + panic("stat not supported on device file\n"); } ssize_t file_lseek(file_t* f, size_t ptr, int dir) { - return frontend_syscall(SYS_lseek, f->kfd, ptr, dir, 0, 0, 0, 0); + if (f->typ == FILE_HOST) { + host_file_t *hf = (host_file_t *) f; + return frontend_syscall(SYS_lseek, hf->kfd, ptr, dir, 0, 0, 0, 0); + } + panic("stat not supported on device file\n"); } @@ -8,12 +8,34 @@ #include <stdint.h> #include "atomic.h" +enum file_type +{ + FILE_HOST, + FILE_DEVICE +}; + typedef struct file { - int kfd; // file descriptor on the host side of the HTIF + enum file_type typ; uint32_t refcnt; + uint64_t padding[1]; // padding to make room for device parameters } file_t; +typedef struct host_file +{ + enum file_type typ; + uint32_t refcnt; + int32_t kfd; // file descriptor on the host side of the HTIF +} host_file_t; + +typedef struct device +{ + enum file_type typ; + uint32_t refcnt; + uint64_t base; + uint64_t size; +} device_t; + extern file_t files[]; #define stdin (files + 0) #define stdout (files + 1) @@ -37,4 +59,8 @@ int fd_close(int fd); void file_init(); +device_t *device_open(const char *name, int flags); +ssize_t device_pread(device_t *dev, void *buf, size_t size, off_t offset); +ssize_t device_pwrite(device_t *dev, void *buf, size_t size, off_t offset); + #endif @@ -51,6 +51,8 @@ extern uintptr_t mem_size; extern int have_vm; extern uint32_t num_harts; extern volatile uint32_t booted_harts_mask; +extern uintptr_t fdt_base; +extern size_t fdt_size; struct mainvars* parse_args(struct mainvars*); void printk(const char* s, ...); diff --git a/pk/syscall.c b/pk/syscall.c index 2d96282..d4785b6 100644 --- a/pk/syscall.c +++ b/pk/syscall.c @@ -102,7 +102,9 @@ static int at_kfd(int dirfd) file_t* dir = file_get(dirfd); if (dir == NULL) return -1; - return dir->kfd; + if (dir->typ != FILE_HOST) + return -1; + return ((host_file_t*) dir)->kfd; } int sys_openat(int dirfd, const char* name, int flags, int mode) @@ -156,9 +158,10 @@ int sys_fcntl(int fd, int cmd, int arg) int r = -EBADF; file_t* f = file_get(fd); - if (f) + if (f && f->typ == FILE_HOST) { - r = frontend_syscall(SYS_fcntl, f->kfd, cmd, arg, 0, 0, 0, 0); + host_file_t *hf = (host_file_t *) f; + r = frontend_syscall(SYS_fcntl, hf->kfd, cmd, arg, 0, 0, 0, 0); file_decref(f); } @@ -2,6 +2,7 @@ #include "file.h" #include "atomic.h" #include "pk.h" +#include "devicetree.h" #include <stdint.h> #include <errno.h> @@ -43,8 +44,9 @@ static vmr_t* __vmr_alloc(uintptr_t addr, size_t length, file_t* file, for (vmr_t* v = vmrs; v < vmrs + MAX_VMR; v++) { if (v->refcnt == 0) { - if (file) + if (file) { file_incref(file); + } v->addr = addr; v->length = length; v->file = file; @@ -201,21 +203,35 @@ static int __handle_page_fault(uintptr_t vaddr, int prot) return -1; else if (!(*pte & PTE_V)) { - uintptr_t ppn = vpn; - + uintptr_t ppn; vmr_t* v = (vmr_t*)*pte; + + if (v->file && v->file->typ == FILE_DEVICE) { + device_t *dev = (device_t *) v->file; + uintptr_t pstart = dev->base + v->offset; + uintptr_t voff = v->addr - vaddr; + debug_printk("device file map %lx -> %lx\n", vaddr, pstart + voff); + // make sure we're still within the device bounds + if ((pstart + voff) >= (dev->base + dev->size)) { + debug_printk("memory access %lu outside device bounds\n", vaddr); + return -1; + } + ppn = (pstart + voff) >> RISCV_PGSHIFT; + } else ppn = vpn; + *pte = pte_create(ppn, PROT_READ|PROT_WRITE, 0); flush_tlb(); - if (v->file) - { + + if (!v->file) { + memset((void*)vaddr, 0, RISCV_PGSIZE); + } else if (v->file->typ == FILE_HOST) { size_t flen = MIN(RISCV_PGSIZE, v->length - (vaddr - v->addr)); ssize_t ret = file_pread(v->file, (void*)vaddr, flen, vaddr - v->addr + v->offset); kassert(ret > 0); if (ret < RISCV_PGSIZE) memset((void*)vaddr + ret, 0, RISCV_PGSIZE - ret); } - else - memset((void*)vaddr, 0, RISCV_PGSIZE); + __vmr_decref(v, 1); *pte = pte_create(ppn, v->prot, 1); } @@ -488,6 +504,7 @@ uintptr_t pk_vm_init() __map_kernel_range(0, 0, current.first_free_paddr, PROT_READ|PROT_WRITE|PROT_EXEC); __map_kernel_range(first_free_page, first_free_page, free_pages * RISCV_PGSIZE, PROT_READ|PROT_WRITE); + __map_kernel_range(fdt_base, fdt_base, fdt_size, PROT_READ); size_t stack_size = RISCV_PGSIZE * CLAMP(mem_size/(RISCV_PGSIZE*32), 1, 256); current.stack_bottom = __do_mmap(current.mmap_max - stack_size, stack_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0); |