aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHoward Mao <zhehao.mao@gmail.com>2015-12-04 16:52:44 -0800
committerHoward Mao <zhehao.mao@gmail.com>2015-12-05 00:24:04 -0800
commitdd7fc844c4059a18ffaeeb3a9573a8d27d30923b (patch)
tree006dda168d8c18488d3c80bb5efca77fa41dfbf9
parent82357114e9eecd3bec9374da9cd6ce4ea77d7e83 (diff)
downloadpk-dd7fc844c4059a18ffaeeb3a9573a8d27d30923b.zip
pk-dd7fc844c4059a18ffaeeb3a9573a8d27d30923b.tar.gz
pk-dd7fc844c4059a18ffaeeb3a9573a8d27d30923b.tar.bz2
implement open and mmap for device files
-rw-r--r--pk/devicetree.c65
-rw-r--r--pk/devicetree.h8
-rw-r--r--pk/file.c102
-rw-r--r--pk/file.h28
-rw-r--r--pk/pk.h2
-rw-r--r--pk/syscall.c9
-rw-r--r--pk/vm.c31
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
diff --git a/pk/file.c b/pk/file.c
index 0c9c96a..b087920 100644
--- a/pk/file.c
+++ b/pk/file.c
@@ -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");
}
diff --git a/pk/file.h b/pk/file.h
index c9f7ce4..8a7cb41 100644
--- a/pk/file.h
+++ b/pk/file.h
@@ -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
diff --git a/pk/pk.h b/pk/pk.h
index b8e9772..ced0ca3 100644
--- a/pk/pk.h
+++ b/pk/pk.h
@@ -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);
}
diff --git a/pk/vm.c b/pk/vm.c
index cd2cba1..5dc1ada 100644
--- a/pk/vm.c
+++ b/pk/vm.c
@@ -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);