Skip to content

Commit

Permalink
Implement clone system call, and copy-on-write
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Jul 11, 2017
1 parent 7509adb commit 6292a97
Show file tree
Hide file tree
Showing 25 changed files with 282 additions and 120 deletions.
97 changes: 58 additions & 39 deletions emu/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ static void tlb_flush(struct mem *mem);
void mem_init(struct mem *mem) {
mem->pt = calloc(PT_SIZE, sizeof(struct pt_entry *));
mem->tlb = malloc(TLB_SIZE * sizeof(struct tlb_entry));
tlb_flush(mem);
}

page_t pt_find_hole(struct mem *mem, pages_t size) {
Expand All @@ -41,7 +42,7 @@ int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned
for (page_t page = start; page < start + pages; page++) {
if (mem->pt[page] != NULL) {
// FIXME this is probably wrong
pt_unmap(mem, page, 1);
pt_unmap(mem, page, 1, 0);
}
struct pt_entry *entry = malloc(sizeof(struct pt_entry));
// FIXME this could allocate some of the memory and then abort
Expand All @@ -52,38 +53,26 @@ int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned
mem->pt[page] = entry;
memory = (char *) memory + PAGE_SIZE;
}
if (flags & P_GROWSDOWN) {
pt_map(mem, start - 1, 1, NULL, P_GUARD);
}
tlb_flush(mem);
return 0;
}

static void pt_drop(struct mem *mem, page_t page) {
struct pt_entry *entry = mem->pt[page];
mem->pt[page] = NULL;
entry->refcount--;
if (entry->refcount == 0) {
// TODO actually free the memory
free(entry);
}
}

int pt_unmap(struct mem *mem, page_t start, pages_t pages) {
for (page_t page = start; page < start + pages; page++)
if (mem->pt[page] == NULL)
return -1;
for (page_t page = start; page < start + pages; page++) {
pt_drop(mem, page);
}
tlb_flush(mem);
return 0;
}
int pt_unmap(struct mem *mem, page_t start, pages_t pages, int force) {
if (!force)
for (page_t page = start; page < start + pages; page++)
if (mem->pt[page] == NULL)
return -1;

int pt_unmap_force(struct mem *mem, page_t start, pages_t pages) {
for (page_t page = start; page < start + pages; page++) {
if (mem->pt[page] != NULL)
pt_drop(mem, page);
if (mem->pt[page] != NULL) {
struct pt_entry *entry = mem->pt[page];
mem->pt[page] = NULL;
entry->refcount--;
if (entry->refcount == 0) {
// TODO actually free the memory
free(entry);
}
}
}
tlb_flush(mem);
return 0;
Expand Down Expand Up @@ -116,15 +105,22 @@ int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags) {
return 0;
}

void pt_dump(struct mem *mem) {
for (unsigned i = 0; i < PT_SIZE; i++) {
if (mem->pt[i] != NULL) {
TRACE("page %u", i);
TRACE("data at %p", mem->pt[i]->data);
TRACE("refcount %u", mem->pt[i]->refcount);
TRACE("flags %x", mem->pt[i]->flags);
int pt_copy_on_write(struct mem *src, page_t src_start, struct mem *dst, page_t dst_start, page_t pages) {
for (page_t src_page = src_start, dst_page = dst_start;
src_page < src_start + pages;
src_page++, dst_page++) {
if (src->pt[src_page] != NULL) {
if (pt_unmap(dst, dst_page, 1, PT_FORCE) < 0)
return -1;
struct pt_entry *entry = src->pt[src_page];
entry->flags |= P_COW;
entry->refcount++;
dst->pt[dst_page] = entry;
}
}
tlb_flush(src);
tlb_flush(dst);
return 0;
}

static void tlb_flush(struct mem *mem) {
Expand All @@ -136,17 +132,40 @@ static void tlb_flush(struct mem *mem) {

void *tlb_handle_miss(struct mem *mem, addr_t addr, int type) {
struct pt_entry *pt = mem->pt[PAGE(addr)];
if (pt == NULL)
return NULL; // page does not exist
if (type == TLB_WRITE && !(pt->flags & P_WRITE))
return NULL; // unwritable

if (pt == NULL) {
// page does not exist
// check if the stack needs to grow
struct pt_entry *next_pt = mem->pt[PAGE(addr) + 1];
if (next_pt != NULL && next_pt->flags & P_GROWSDOWN) {
pt_map_nothing(mem, PAGE(addr), 1, P_WRITE | P_GROWSDOWN);
pt = mem->pt[PAGE(addr)];
} else {
return NULL;
}
}

if (type == TLB_WRITE && !P_WRITABLE(pt->flags)) {
// page is unwritable or cow
// if page is cow, ~~milk~~ copy it
if (pt->flags & P_COW) {
void *data = pt->data;
void *copy = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
memcpy(copy, data, PAGE_SIZE);
pt_map(mem, PAGE(addr), 1, copy, pt->flags &~ P_COW);
pt = mem->pt[PAGE(addr)];
} else {
return NULL;
}
}

// TODO if page is unwritable maybe we shouldn't bail and still add an
// entry to the TLB

struct tlb_entry *tlb = &mem->tlb[TLB_INDEX(addr)];
tlb->page = TLB_PAGE(addr);
if (pt->flags & P_WRITE)
if (P_WRITABLE(pt->flags))
tlb->page_if_writable = tlb->page;
else
// 1 is not a valid page so this won't look like a hit
Expand Down
13 changes: 7 additions & 6 deletions emu/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,25 @@ struct pt_entry {
#define P_WRITE (1 << 1)
#define P_EXEC (1 << 2)
#define P_GROWSDOWN (1 << 3)
#define P_GUARD (1 << 4)
#define P_COW (1 << 4)
#define P_WRITABLE(flags) (flags & P_WRITE && !(flags & P_COW))

page_t pt_find_hole(struct mem *mem, pages_t size);

#define PT_FORCE 1

// Map real memory into fake memory (unmaps existing mappings)
int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned flags);
// Map fake file into fake memory
int pt_map_file(struct mem *mem, page_t start, pages_t pages, int fd, off_t off, unsigned flags);
// Map empty space into fake memory
int pt_map_nothing(struct mem *mem, page_t page, pages_t pages, unsigned flags);
// Unmap fake memory, return -1 if any part of the range isn't mapped and 0 otherwise
int pt_unmap(struct mem *mem, page_t start, pages_t pages);
// Same as pt_unmap, but skips over unmapped parts
int pt_unmap_force(struct mem *mem, page_t start, pages_t pages);
int pt_unmap(struct mem *mem, page_t start, pages_t pages, int force);
// Set the flags on memory
int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags);

void pt_dump(struct mem *mem);
// Copy pages from src memory to dst memory using copy-on-write
int pt_copy_on_write(struct mem *src, page_t src_start, struct mem *dst, page_t dst_start, page_t pages);

struct tlb_entry {
page_t page;
Expand Down
25 changes: 0 additions & 25 deletions emu/process.c

This file was deleted.

2 changes: 1 addition & 1 deletion main.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <stdlib.h>
#include "sys/calls.h"
#include "emu/process.h"
#include "sys/process.h"

int main(int argc, char *const argv[]) {
int err;
Expand Down
13 changes: 9 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
project('ish', 'c',
default_options: ['default_library=static', 'c_std=gnu99'])
default_options: ['default_library=static', 'c_std=c11'])

if get_option('debug_log')
add_global_arguments('-DDEBUG_LOG=1', language: 'c')
Expand All @@ -21,6 +21,8 @@ sys_src = [
'sys/exec.c',
'sys/vdso.c',

'sys/process.c',

'sys/exit.c',
'sys/time.c',
'sys/mm.c',
Expand All @@ -40,20 +42,23 @@ sys_src = [
]
emu_src = [
'emu/memory.c',
'emu/process.c',
'emu/interp.c',
'emu/modrm.c',
'emu/debug.c',
cified_vdso,
]

threads = dependency('threads')

libish = library('ish', sys_src + emu_src + ['setup.c'],
include_directories: includes)
ish = declare_dependency(
link_with: library('ish', sys_src + emu_src + ['setup.c'], include_directories: includes),
link_with: libish,
include_directories: includes)

# testing programs
subdir('tests')
# ptraceomatic et al
subdir('tools')

executable('ish', ['main.c'], dependencies: [ish])
executable('ish', ['main.c'], dependencies: [ish, threads])
3 changes: 2 additions & 1 deletion misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
#endif
#define TRACEI(msg, ...) TRACE(msg "\t", ##__VA_ARGS__)

#define TODO(msg, ...) TRACE("TODO: " msg "\n", ##__VA_ARGS__); abort();
#define TODO(msg, ...) TRACE("TODO: " msg "\n", ##__VA_ARGS__); abort()
#define FIXME(msg, ...) printf("FIXME " msg "\n", ##__VA_ARGS__)

#if defined(__i386__) || defined(__x86_64__)
#define debugger __asm__("int3")
Expand Down
2 changes: 1 addition & 1 deletion setup.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "emu/process.h"
#include "sys/process.h"
#include "sys/fs.h"

void setup() {
Expand Down
13 changes: 4 additions & 9 deletions sys/calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ syscall_t syscall_table[] = {
[90] = (syscall_t) sys_mmap,
[91] = (syscall_t) sys_munmap,
[116] = (syscall_t) sys_sysinfo,
[120] = (syscall_t) sys_clone,
[122] = (syscall_t) _sys_uname,
[125] = (syscall_t) sys_mprotect,
[140] = (syscall_t) sys__llseek,
Expand All @@ -59,7 +60,7 @@ syscall_t syscall_table[] = {
};

// returns true if a step is necessary (subject to change)
int handle_interrupt(struct cpu_state *cpu, int interrupt) {
void handle_interrupt(struct cpu_state *cpu, int interrupt) {
TRACE("\nint %d ", interrupt);
if (interrupt == INT_SYSCALL) {
int syscall_num = cpu->eax;
Expand All @@ -77,13 +78,8 @@ int handle_interrupt(struct cpu_state *cpu, int interrupt) {
}
} else if (interrupt == INT_GPF) {
// page fault handling is a thing
TRACE("page fault at %x\n", cpu->segfault_addr);
int res = handle_pagefault(cpu->segfault_addr);
if (res == 0) {
printf("could not handle page fault at %x, exiting\n", cpu->segfault_addr);
sys_exit(1);
}
return res;
printf("could not handle page fault at %x, exiting\n", cpu->segfault_addr);
sys_exit(1);
} else if (interrupt == INT_UNDEFINED) {
printf("illegal instruction\n");
if (send_signal(SIGILL_) < 0)
Expand All @@ -92,5 +88,4 @@ int handle_interrupt(struct cpu_state *cpu, int interrupt) {
printf("exiting\n");
sys_exit(interrupt);
}
return 0;
}
7 changes: 4 additions & 3 deletions sys/calls.h
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
#ifndef CALLS_H
#define CALLS_H

#include "emu/process.h"
#include "sys/process.h"
#include "sys/errno.h"
#include "sys/fs.h"
#include "misc.h"

#include "sys/signal.h"

int handle_interrupt(struct cpu_state *cpu, int interrupt);
void handle_interrupt(struct cpu_state *cpu, int interrupt);

dword_t user_get(addr_t addr);
byte_t user_get8(addr_t addr);
void user_put(addr_t addr, dword_t value);
void user_put_proc(struct process *proc, addr_t addr, dword_t value);
void user_put8(addr_t addr, byte_t value);
int user_get_string(addr_t addr, char *buf, size_t max);
void user_put_string(addr_t addr, const char *buf);
int user_get_count(addr_t addr, void *buf, size_t count);
void user_put_count(addr_t addr, const void *buf, size_t count);

// process lifecycle
dword_t sys_clone(dword_t flags, addr_t stack, addr_t ptid, addr_t tls, addr_t ctid);
int sys_execve(const char *file, char *const argv[], char *const envp[]);
dword_t _sys_execve(addr_t file, addr_t argv, addr_t envp);
dword_t sys_exit(dword_t status);
dword_t sys_exit_group(dword_t status);

// memory management
addr_t sys_brk(addr_t new_brk);
int handle_pagefault(addr_t addr);

#define MMAP_SHARED 0x1
#define MMAP_PRIVATE 0x2
Expand Down
2 changes: 1 addition & 1 deletion sys/fs.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <string.h>
#include "sys/calls.h"
#include "sys/errno.h"
#include "emu/process.h"
#include "sys/process.h"
#include "sys/fs.h"

fd_t find_fd() {
Expand Down
2 changes: 1 addition & 1 deletion sys/fs/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <string.h>

#include "sys/fs.h"
#include "emu/process.h"
#include "sys/process.h"

path_t find_mount(char *pathname, const struct fs_ops **fs) {
struct mount *mount;
Expand Down
2 changes: 1 addition & 1 deletion sys/fs/pathname.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <string.h>

#include "emu/process.h"
#include "sys/process.h"

// for now just collapses slashes, will eventually do something with . and ..
// TODO move to fs/pathname.c or something
Expand Down
Loading

0 comments on commit 6292a97

Please sign in to comment.