Commit c28046eb authored by Eddie Kohler's avatar Eddie Kohler

Pset 3 for 2019.

parent 8c084f28
......@@ -94,11 +94,11 @@ $(OBJDIR)/stamp $(BUILDSTAMP):
$(call run,touch $@)
ifneq ($(strip $(INITFS_CONTENTS)),$(DEP_INITFS_CONTENTS))
INITFS_BUILDSTAMP := $(shell echo "DEP_INITFS_CONTENTS:=$(INITFS_CONTENTS)" > $(DEPSDIR)/_initfs.d; echo always)
INITFS_BUILDSTAMP := $(shell echo "DEP_INITFS_CONTENTS:=$(strip $(INITFS_CONTENTS))" > $(DEPSDIR)/_initfs.d; echo always)
endif
ifneq ($(strip $(DISKFS_CONTENTS)),$(DEP_DISKFS_CONTENTS))
DISKFS_BUILDSTAMP := $(shell echo "DEP_DISKFS_CONTENTS:=$(DISKFS_CONTENTS)" > $(DEPSDIR)/_diskfs.d; echo always)
DISKFS_BUILDSTAMP := $(shell echo "DEP_DISKFS_CONTENTS:=$(strip $(DISKFS_CONTENTS))" > $(DEPSDIR)/_diskfs.d; echo always)
endif
......
......@@ -227,17 +227,88 @@ void console_show_cursor(int cpos) {
// memfile functions
memfile* memfile::initfs_lookup(const char* name, size_t namelen) {
// memfile::initfs_lookup(name, namelen, create)
// Search `memfile::initfs` for a file named `name`. Return the
// index of that `memfile` if found; this will be >= 0 and <
// `memfile::initfs_size`. If not found, and `create == true`,
// attempt to create and initialize a new file and return its
// index. Return an error code on failure.
int memfile::initfs_lookup(const char* name, bool create) {
memfile* empty = nullptr;
size_t namelen = strlen(name);
// search for a file named `name`
for (memfile* f = initfs; f != initfs + initfs_size; ++f) {
if (!f->empty()
&& memcmp(f->name_, name, namelen) == 0
&& f->name_[namelen] == 0) {
return f;
return f - initfs;
} else if (f->empty()
&& !empty) {
empty = f;
}
}
return nullptr;
if (!create) {
// file not found
return E_NOENT;
} else if (!empty) {
// no space in directory
return E_NOSPC;
} else if (namelen >= namesize) {
// name too long for `memfile::name_`
return E_NAMETOOLONG;
} else {
memcpy(empty->name_, name, namelen);
empty->name_[namelen] = 0;
empty->data_ = nullptr;
empty->len_ = 0;
empty->capacity_ = 0;
return empty - initfs;
}
}
// memfile::set_length(len)
// Set the length of this `memfile` to `len`. This might require
// extending this `memfile`’s capacity; allocates memory if so.
// Returns 0 on success and an error code such as `E_NOSPC` on
// failure.
int memfile::set_length(size_t len) {
// grow file if necessary
if (len > capacity_) {
// allocate new data
if (len > size_t(SSIZE_MAX)) { // too large for safe round_up_pow2
return E_NOSPC;
}
size_t new_capacity = round_up_pow2(len);
unsigned char* new_data = new(std::nothrow) unsigned char[new_capacity];
if (!new_data) {
return E_NOSPC;
}
// copy old data over
if (len_ != 0) {
memcpy(new_data, data_, len_);
}
// delete old data, unless it is kernel static data
if (data_ && physical_ranges.type(kptr2pa(data_)) != mem_kernel) {
delete[] data_;
}
data_ = new_data;
capacity_ = new_capacity;
}
len_ = len;
return 0;
}
// memfile_loader functions
// These functions fulfill the requirements of `loader` using a `memfile`.
ssize_t memfile_loader::get_page(uint8_t** pg, size_t off) {
if (!memfile_) {
return E_NOENT;
......
......@@ -80,15 +80,20 @@ struct memfile {
inline memfile();
inline memfile(const char* name, unsigned char* first,
unsigned char* last);
inline bool empty() const; // test if empty
inline bool is_kernel_data() const; // test if in kernel data segment
// return true iff this `memfile` is not being used
inline bool empty() const;
// set file length to `len`; return 0 or an error like `E_NOSPC` on failure
int set_length(size_t len);
// memfile::initfs[] is the init file system built in to the kernel.
static constexpr unsigned initfs_size = 64;
static memfile initfs[initfs_size];
static inline memfile* initfs_lookup(const char* name);
static memfile* initfs_lookup(const char* name, size_t namelen);
// look up the memfile named `name`, creating it if it does not exist
// and `create == true`. Return an index into `initfs` or an error code.
static int initfs_lookup(const char* name, bool create = false);
};
inline memfile::memfile()
......@@ -107,16 +112,6 @@ inline memfile::memfile(const char* name, unsigned char* first,
inline bool memfile::empty() const {
return name_[0] == 0;
}
inline bool memfile::is_kernel_data() const {
extern unsigned char _kernel_start[], _kernel_end[];
uintptr_t data = reinterpret_cast<uintptr_t>(data_);
return data >= reinterpret_cast<uintptr_t>(_kernel_start)
&& data < reinterpret_cast<uintptr_t>(_kernel_end);
}
inline memfile* memfile::initfs_lookup(const char* name) {
return initfs_lookup(name, strlen(name));
}
// memfile::loader: loads a `proc` from a `memfile`
......@@ -126,6 +121,11 @@ struct memfile_loader : public proc_loader {
inline memfile_loader(memfile* mf, x86_64_pagetable* pt)
: proc_loader(pt), memfile_(mf) {
}
inline memfile_loader(int mf_index, x86_64_pagetable* pt)
: proc_loader(pt) {
assert(mf_index >= 0 && unsigned(mf_index) < memfile::initfs_size);
memfile_ = &memfile::initfs[mf_index];
}
ssize_t get_page(uint8_t** pg, size_t off) override;
void put_page(uint8_t* pg) override;
};
......
......@@ -179,6 +179,9 @@ bool lookup_symbol(uintptr_t addr, const char** name, uintptr_t* start) {
if (sym.st_value <= addr
&& (m + 1 == symtab.nsym || addr < (&sym)[1].st_value)
&& (sym.st_size == 0 || addr <= sym.st_value + sym.st_size)) {
if (!sym.st_value) {
return false;
}
if (name) {
*name = symtab.strtab + sym.st_name;
}
......
......@@ -99,7 +99,6 @@ struct spinlock_guard {
}
NO_COPY_OR_ASSIGN(spinlock_guard);
private:
spinlock& lock_;
irqstate state_;
};
......
......@@ -92,7 +92,8 @@ void proc::init_kernel(pid_t pid, void (*f)(proc*)) {
// proc::load(proc_loader& ld)
// Load the executable specified by the `proc_loader` into `ld.pagetable_`
// and set `ld.entry_rip_` to its entry point. Calls `kalloc` and maps
// memory. Returns 0 on success, negative on failure (e.g. out-of-memory).
// memory. Returns 0 on success and a negative error code on failure,
// such as `E_NOMEM` for out of memory or `E_NOEXEC` for not an executable.
int proc::load(proc_loader& ld) {
union {
......@@ -165,6 +166,9 @@ int proc::load_segment(const elf_program& ph, proc_loader& ld) {
|| ph.p_memsz < ph.p_filesz) {
return E_NOEXEC;
}
if (!ld.pagetable_) {
return E_NOMEM;
}
// allocate memory
for (vmiter it(ld.pagetable_, round_down(va, PAGESIZE));
......
......@@ -192,6 +192,12 @@ uintptr_t proc::syscall(regstate* regs) {
// Your code here
return -1;
case SYSCALL_READ:
return syscall_read(regs);
case SYSCALL_WRITE:
return syscall_write(regs);
default:
// no such system call
log_printf("%d: no such system call %u\n", id_, regs->reg_rax);
......@@ -201,6 +207,70 @@ uintptr_t proc::syscall(regstate* regs) {
}
// proc::syscall_read(regs), proc::syscall_write(regs)
// Handle read and write system calls.
uintptr_t proc::syscall_read(regstate* regs) {
int fd = regs->reg_rdi;
uintptr_t addr = regs->reg_rsi;
size_t sz = regs->reg_rdx;
auto& kbd = keyboardstate::get();
auto irqs = kbd.lock_.lock();
// mark that we are now reading from the keyboard
// (so `q` should not power off)
if (kbd.state_ == kbd.boot) {
kbd.state_ = kbd.input;
}
// yield until a line is available
// (special case: do not block if the user wants to read 0 bytes)
while (sz != 0 && kbd.eol_ == 0) {
kbd.lock_.unlock(irqs);
yield();
irqs = kbd.lock_.lock();
}
// read that line or lines
size_t n = 0;
while (kbd.eol_ != 0 && n < sz) {
if (kbd.buf_[kbd.pos_] == 0x04) {
// Ctrl-D means EOF
if (n == 0) {
kbd.consume(1);
}
break;
} else {
*reinterpret_cast<char*>(addr) = kbd.buf_[kbd.pos_];
++addr;
++n;
kbd.consume(1);
}
}
kbd.lock_.unlock(irqs);
return n;
}
uintptr_t proc::syscall_write(regstate* regs) {
int fd = regs->reg_rdi;
uintptr_t addr = regs->reg_rsi;
size_t sz = regs->reg_rdx;
auto& csl = consolestate::get();
auto irqs = csl.lock_.lock();
size_t n = 0;
while (n < sz) {
int ch = *reinterpret_cast<const char*>(addr);
++addr;
++n;
console_printf(0x0F00, "%c", ch);
}
csl.lock_.unlock(irqs);
return n;
}
// memshow()
// Draw a picture of memory (physical and virtual) on the CGA console.
// Switches to a new process's virtual memory map every 0.25 sec.
......
......@@ -59,6 +59,9 @@ struct __attribute__((aligned(4096))) proc {
inline bool resumable() const;
uintptr_t syscall_read(regstate* reg);
uintptr_t syscall_write(regstate* reg);
inline irqstate lock_pagetable_read();
inline void unlock_pagetable_read(irqstate& irqs);
......
......@@ -306,19 +306,26 @@ void printer_vprintf(printer* p, int color, const char* format, va_list val) {
}
}
// process length
int length = 0;
switch (*format) {
case 'l':
case 't': // ptrdiff_t
case 'z': // size_t, ssize_t
length = 1;
++format;
break;
case 'h':
++format;
break;
}
// process main conversion character
int base = 10;
unsigned long num = 0;
int length = 0;
const char* data = "";
again:
switch (*format) {
case 'l':
case 'z':
length = 1;
++format;
goto again;
case 'd':
case 'i': {
long x = length ? va_arg(val, long) : va_arg(val, int);
......
......@@ -27,6 +27,7 @@ unsigned long strtoul(const char* s, char** endptr = nullptr, int base = 0);
ssize_t snprintf(char* s, size_t size, const char* format, ...);
ssize_t vsnprintf(char* s, size_t size, const char* format, va_list val);
inline bool isspace(unsigned char c);
inline bool isdigit(unsigned char c);
}
#define RAND_MAX 0x7FFFFFFF
......@@ -156,6 +157,10 @@ inline bool isspace(unsigned char ch) {
return (ch >= '\t' && ch <= '\r') || ch == ' ';
}
inline bool isdigit(unsigned char ch) {
return ch >= '0' && ch <= '9';
}
// Checksums
......@@ -208,6 +213,7 @@ inline uint32_t crc32c(const void* buf, size_t sz) {
#define E_INVAL -22 // Invalid argument
#define E_IO -5 // I/O error
#define E_MFILE -24 // Too many open files
#define E_NAMETOOLONG -36 // File name too long
#define E_NFILE -23 // File table overflow
#define E_NOENT -2 // No such file or directory
#define E_NOEXEC -8 // Exec format error
......
#include "u-lib.hh"
void process_main(int argc, char** argv) {
char buf[256];
for (int i = 1; i == 1 || i < argc; ++i) {
int f = 0;
if (i < argc && strcmp(argv[i], "-") != 0) {
f = sys_open(argv[i], OF_READ);
if (f < 0) {
dprintf(2, "%s: error %d\n", argv[i], f);
sys_exit(1);
}
}
while (1) {
ssize_t n = sys_read(f, buf, sizeof(buf));
if (n == 0 || (n < 0 && n != E_AGAIN)) {
break;
}
if (n > 0) {
ssize_t w = sys_write(1, buf, n);
if (w != n) {
break;
}
}
}
if (f != 0) {
sys_close(f);
}
}
sys_exit(0);
}
#include "u-lib.hh"
void process_main(int argc, char** argv) {
for (int i = 1; i < argc; ++i) {
if (i > 1) {
sys_write(1, " ", 1);
}
sys_write(1, argv[i], strlen(argv[i]));
}
sys_write(1, "\n", 1);
sys_exit(0);
}
#include "u-lib.hh"
void process_main() {
for (int i = 5; i > 0; --i) {
printf("Running memviewer in %d...\n", i);
sys_msleep(250);
}
const char* args[] = {
"allocexit", nullptr
};
int r = sys_execv("allocexit", args);
assert_eq(r, 0);
sys_exit(0);
}
#include "u-lib.hh"
void process_main() {
sys_write(1, "About to greet you...\n", 22);
const char* args[] = {
"echo", "hello,", "world", nullptr
};
int r = sys_execv("echo", args);
assert_eq(r, 0);
sys_exit(0);
}
#include "u-lib.hh"
void process_main() {
sys_exit(1);
}
#include "u-lib.hh"
#define ANDAND 1
#define OROR 2
static pid_t create_child(char** words, char nextch,
char** redir, int* pipein);
void process_main() {
static char buf[4096]; // static so stack size is small
static char* words[256];
while (1) {
// print prompt, read line
ssize_t n = sys_write(1, "sh$ ", 4);
assert(n == 4);
n = sys_read(0, buf, sizeof(buf) - 1);
if (n <= 0) {
break;
}
buf[n] = 0;
char* s = buf;
char* end = buf + n;
char** word = words;
int pipein = 0;
int skip_status = -1;
char* redir[] = { nullptr, nullptr, nullptr };
int redir_status = -1;
while (s != end || word != words) {
// read a word
while (isspace((unsigned char) *s)
&& *s != '\n') {
++s;
}
char* wordstart = s;
while (*s != 0
&& !isspace((unsigned char) *s)
&& *s != ';'
&& *s != '&'
&& *s != '|'
&& *s != '<'
&& *s != '>') {
++s;
}
char nextch = *s;
*s = 0;
if (s != end) {
++s;
}
if (nextch == '&' && *s == '&') {
nextch = ANDAND;
++s;
} else if (nextch == '|' && *s == '|') {
nextch = OROR;
++s;
}
// add to word list
if (*wordstart != 0) {
if (redir_status != -1) {
redir[redir_status] = wordstart;
redir_status = -1;
} else {
*word = wordstart;
++word;
assert(word < words + arraysize(words));
}
} else {
if (word == words
&& redir_status == -1
&& pipein == 0) {
break;
}
assert(word != words);
assert(redir_status == -1);
}
// maybe execute
if (nextch == '<') {
redir_status = 0;
} else if (nextch == '>') {
redir_status = 1;
} else if (!isspace((unsigned char) nextch)) {
*word = nullptr;
if (skip_status < 0) {
pid_t child = create_child(words, nextch,
redir, &pipein);
if (nextch != '|'
&& nextch != '&'
&& child > 0) {
int r, status = -1;
while ((r = sys_waitpid(child, &status)) == E_AGAIN) {
}
assert(r == child);
if ((status == 0 && nextch == OROR)
|| (status != 0 && nextch == ANDAND)) {
skip_status = status != 0;
}
}
redir[0] = redir[1] = redir[2] = nullptr;
} else {
if ((skip_status == 0 && nextch == ANDAND)
|| (skip_status != 0 && nextch == OROR)) {
skip_status = -1;
}
}
word = words;
}
}
while (sys_waitpid(0, nullptr, W_NOHANG) > 0) {
}
}
sys_exit(0);
}
static void child_redirect(int fd, char* pathname) {
int flags;
if (fd == 0) {
flags = OF_READ;
} else {
flags = OF_WRITE | OF_CREATE | OF_TRUNC;
}
int r = sys_open(pathname, flags);
if (r < 0) {
dprintf(2, "%s: error %d\n", pathname, r);
sys_exit(1);
}
if (r != fd) {
sys_dup2(r, fd);
sys_close(r);
}
}
static pid_t create_child(char** words, char nextch,
char** redir, int* pipein) {
int pfd[2] = {0, 0};
if (nextch == '|') {
int r = sys_pipe(pfd);
assert_gt(r, -1);
}
// handle `exit [N]`
int exit_status = -1;
if (strcmp(words[0], "exit") == 0) {
if (words[1] && isdigit((unsigned char) words[1][0])) {
char* endl;
exit_status = strtol(words[1], &endl, 10);
if (*endl != '\0') {
exit_status = -2;
}
} else if (words[1]) {
exit_status = -2;
}
}
pid_t child = sys_fork();
if (child == 0) {
if (*pipein != 0) {
sys_dup2(*pipein, 0);
sys_close(*pipein);
}
for (int fd = 0; fd != 3; ++fd) {
if (redir[fd] != nullptr) {
child_redirect(fd, redir[fd]);
}
}
if (nextch == '|') {
sys_close(pfd[0]);
sys_dup2(pfd[1], 1);
sys_close(pfd[1]);