diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e11d185 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": ["LAB_MMAP"], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/Makefile b/Makefile index 9b83591..5f6af9c 100644 --- a/Makefile +++ b/Makefile @@ -188,6 +188,7 @@ UPROGS=\ $U/_grind\ $U/_wc\ $U/_zombie\ + $U/_mmaptest\ diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b..e618007 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -187,3 +187,6 @@ void virtio_disk_intr(void); // number of elements in fixed-size array #define NELEM(x) (sizeof(x)/sizeof((x)[0])) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) \ No newline at end of file diff --git a/kernel/proc.c b/kernel/proc.c index 959b778..f6090dd 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -5,6 +5,15 @@ #include "spinlock.h" #include "proc.h" #include "defs.h" +#include "fcntl.h" +#include "sleeplock.h" +#include "fs.h" +#include "file.h" + +static struct vma vma_pool[NMAXVMA]; +static struct spinlock lock_vma_pool; +uint64 do_munmap(uint64 addr, int length); +uint64 do_mmap(struct proc* p, int length, int prot, int flags, int fd); struct cpu cpus[NCPU]; @@ -33,7 +42,7 @@ void proc_mapstacks(pagetable_t kpgtbl) { struct proc *p; - + initlock(&lock_vma_pool, "vma_pool"); for(p = proc; p < &proc[NPROC]; p++) { char *pa = kalloc(); if(pa == 0) @@ -318,6 +327,30 @@ fork(void) np->parent = p; release(&wait_lock); + acquire(&np->lock); + for (int i = 0; i < NMAXVMA; ++ i) { + if (p->map_region[i]) { + struct vma* newvma = 0; + acquire(&lock_vma_pool); + for (int i = 0; i < NMAXVMA; ++ i) { + if (vma_pool[i].length == 0) { + newvma = &vma_pool[i]; + break; + } + } + if (!newvma || newvma->length) + panic("mmap: vma pool run out"); + newvma->f = filedup(p->map_region[i]->f); + newvma->addr = p->map_region[i]->addr; + newvma->prot = p->map_region[i]->prot; + newvma->length = p->map_region[i]->length; + newvma->flag = p->map_region[i]->flag; + release(&lock_vma_pool); + np->map_region[i] = newvma; + } + } + release(&np->lock); + acquire(&np->lock); np->state = RUNNABLE; release(&np->lock); @@ -351,6 +384,13 @@ exit(int status) if(p == initproc) panic("init exiting"); + // clear mmaps + for (int i = 0; i < NMAXVMA; ++ i) { + if (p->map_region[i]) { + do_munmap(p->map_region[i]->addr, p->map_region[i]->length); + } + } + // Close all open files. for(int fd = 0; fd < NOFILE; fd++){ if(p->ofile[fd]){ @@ -681,3 +721,125 @@ procdump(void) printf("\n"); } } + +uint64 do_mmap(struct proc* p, int length, int prot, int flags, int fd) +{ + uint64 addr; + struct vma* newvma = 0; + if (fd < 0 || fd > NOFILE || p->ofile[fd] == 0){ + return -1; + } + if (!p->ofile[fd]->writable && (prot & PROT_WRITE) && (flags != MAP_PRIVATE)) { + return -1; + } +// find vacant addr space + addr = MMAP_START; + for (int i = 0; i < NMAXVMA; ++ i) { + // in case some va has been occupied by some other vma but not accessed + if (p->map_region[i]) { + int _tmp = PGROUNDUP(p->map_region[i]->addr + length); + addr = _tmp > addr ? _tmp : addr; + } + } + + acquire(&lock_vma_pool); + for (int i = 0; i < NMAXVMA; ++ i) { + if (vma_pool[i].length == 0) { + newvma = &vma_pool[i]; + break; + } + } + if (!newvma || newvma->length) + panic("mmap: vma pool run out"); + newvma->f = p->ofile[fd]; + newvma->addr = addr; + newvma->prot = prot; + newvma->length = length; + newvma->flag = flags; + release(&lock_vma_pool); + + filedup(newvma->f); + for (int i = 0; i < NMAXVMA; ++ i) { + if (p->map_region[i] == 0) { + p->map_region[i] = newvma; + break; + } + } + return addr; +} + +// void *mmap(void *addr, int length, int prot, int flags, int fd, int offset); +// int munmap(void *addr, int length); +uint64 sys_mmap(void) +{ + uint64 addr; + int length, prot, flags, fd, offset; + argaddr(0, &addr); + argint(1, &length); + argint(2, &prot); + argint(3, &flags); + argint(4, &fd); + argint(5, &offset); +// some preliminary check + if (addr != 0 || offset != 0) panic("Unsupported non-zero value for arg addr and off"); + return do_mmap(myproc(), length, prot, flags, fd); +} + +uint64 do_munmap(uint64 addr, int length) +{ + struct vma* vma0 = 0; + struct proc* p = myproc(); + int imap = 0; + for (int i = 0; i < NMAXVMA; ++ i) { + if(p->map_region[i] + && p->map_region[i]->addr <= addr + && addr < p->map_region[i]->addr + p->map_region[i]->length) { + vma0 = p->map_region[i]; + imap = i; + break; + } + } + if (vma0 == 0) { + return -1; + } + // remove all relavant pages + // by reading the test code, no need to worry about non-aligned unmap + // or those unmaps which would break a vma into parts + uint64 vma_end = PGROUNDUP(vma0->addr + vma0->length); + uint64 free_start = MAX(PGROUNDDOWN(addr), vma0->addr); + uint64 free_end = MIN(PGROUNDUP(addr + length), vma_end); + for (uint64 va0 = free_start; va0 < free_end; va0 += PGSIZE) { + if (vma0->flag == MAP_SHARED) { + begin_op(); + ilock(vma0->f->ip); + writei(vma0->f->ip, 1, va0, va0 - addr, PGSIZE); // ? no sure if to write whole page + iunlock(vma0->f->ip); + end_op(); + } + if (walkaddr(p->pagetable, va0)) + uvmunmap(p->pagetable, va0, 1, 1); + } + if (free_start <= vma0->addr && free_end >= vma_end) { + // all released + fileclose(vma0->f); + memset(vma0, 0, sizeof(struct vma)); + p->map_region[imap] = 0; + } else if (free_end >= vma_end) { + vma0->length = free_start - vma0->addr; + } else if (free_start <= vma0->addr) { + vma0->length -= free_end - vma0->addr; + vma0->addr = free_end; + } else { + panic("unsupported unmap type"); + } + return 0; +} + +uint64 sys_munmap(void) +{ + uint64 addr; + int length; + argaddr(0, &addr); + argint(1, &length); + return do_munmap(addr, length); +} \ No newline at end of file diff --git a/kernel/proc.h b/kernel/proc.h index d021857..eab73eb 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -81,6 +81,17 @@ struct trapframe { enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; +struct vma { + uint64 addr; + uint length; + uint prot; + uint flag; + struct file* f; +}; + +#define NMAXVMA 20 +#define MMAP_START (0x40000000) + // Per-process state struct proc { struct spinlock lock; @@ -103,5 +114,6 @@ struct proc { struct context context; // swtch() here to run process struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory + struct vma* map_region[NMAXVMA]; char name[16]; // Process name (debugging) }; diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..4fb9baa 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,6 +101,8 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_mmap(void); +extern uint64 sys_munmap(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +128,8 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_mmap] sys_mmap, +[SYS_munmap] sys_munmap, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..e7b18d6 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,5 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_mmap 22 +#define SYS_munmap 23 diff --git a/kernel/trap.c b/kernel/trap.c index 512c850..941171f 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -5,6 +5,10 @@ #include "spinlock.h" #include "proc.h" #include "defs.h" +#include "fcntl.h" +#include "sleeplock.h" +#include "fs.h" +#include "file.h" struct spinlock tickslock; uint ticks; @@ -65,6 +69,28 @@ usertrap(void) intr_on(); syscall(); + } else if(r_scause() == 13){ + int va0 = r_stval(); + struct vma* vma0 = 0; + if (va0 < MMAP_START) panic("unknown page fault"); + for (int i = 0; i < NMAXVMA; ++ i) { + if(p->map_region[i] + && p->map_region[i]->addr <= va0 + && va0 < p->map_region[i]->addr + p->map_region[i]->length) { + vma0 = p->map_region[i]; + break; + } + } + uint64 phy_addr = (uint64)kalloc(); + uint pgflag = PTE_U | (vma0->prot << 1); + // save some code... there is PTE_R/W/X = PROT_READ/WRITE/EXEC << 1 + mappages(p->pagetable, va0, PGSIZE, phy_addr, pgflag); + memset((void*)phy_addr, 0, PGSIZE); + begin_op(); + ilock(vma0->f->ip); + readi(vma0->f->ip, 0, phy_addr, va0 - vma0->addr, PGSIZE); + iunlock(vma0->f->ip); + end_op(); } else if((which_dev = devintr()) != 0){ // ok } else { diff --git a/kernel/vm.c b/kernel/vm.c index 9f69783..fd7e9f1 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -179,8 +179,10 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ if((pte = walk(pagetable, a, 0)) == 0) panic("uvmunmap: walk"); - if((*pte & PTE_V) == 0) + if((*pte & PTE_V) == 0){ + printf("%p\n", va); panic("uvmunmap: not mapped"); + } if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); if(do_free){ diff --git a/user/user.h b/user/user.h index 4d398d5..4a7f8d0 100644 --- a/user/user.h +++ b/user/user.h @@ -22,6 +22,8 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +void *mmap(void *addr, int length, int prot, int flags, int fd, int offset); +int munmap(void *addr, int length); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index 01e426e..d23b9cc 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,3 +36,5 @@ entry("getpid"); entry("sbrk"); entry("sleep"); entry("uptime"); +entry("mmap"); +entry("munmap");