diff --git a/Makefile b/Makefile index a074a801..32d7260a 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,7 @@ UPROGS=\ $U/_bcachetest\ $U/_alloctest\ $U/_bigfile\ + $U/_mmaptest\ fs.img: mkfs/mkfs README user/xargstest.sh $(UPROGS) mkfs/mkfs fs.img README user/xargstest.sh $(UPROGS) diff --git a/kernel/defs.h b/kernel/defs.h index 8a2171a2..ca3cac29 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -59,6 +59,7 @@ void ramdiskintr(void); void ramdiskrw(struct buf*); // kalloc.c +void in_ref(void *); void* kalloc(void); void kfree(void *); void kinit(); @@ -154,6 +155,7 @@ void uartputc(int); int uartgetc(void); // vm.c +pte_t* walk(pagetable_t, uint64, int); void kvminit(void); void kvminithart(void); uint64 kvmpa(uint64); diff --git a/kernel/kalloc.c b/kernel/kalloc.c index fa6a0aca..a2a51d25 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -9,6 +9,8 @@ #include "riscv.h" #include "defs.h" +#define REF_INDEX(pa) ((char*) pa - end) >> 12 + void freerange(void *pa_start, void *pa_end); extern char end[]; // first address after kernel. @@ -21,13 +23,21 @@ struct run { struct { struct spinlock lock; struct run *freelist; + char *kref; } kmem; +void in_ref(void *pa){ + kmem.kref[REF_INDEX(pa)]++; +} + + void kinit() { initlock(&kmem.lock, "kmem"); - freerange(end, (void*)PHYSTOP); + kmem.kref = end; + uint64 rc_offset = (((((PHYSTOP - (uint64)end) >> 12) + 1) * sizeof(uint) >> 12) + 1) << 12; + freerange(end + rc_offset, (void*)PHYSTOP); } void @@ -35,8 +45,14 @@ freerange(void *pa_start, void *pa_end) { char *p; p = (char*)PGROUNDUP((uint64)pa_start); - for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) - kfree(p); + for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE){ + memset(p, 1, PGSIZE); + struct run *r = (struct run*)p; + acquire(&kmem.lock); + r->next = kmem.freelist; + kmem.freelist = r; + release(&kmem.lock); + } } // Free the page of physical memory pointed at by v, @@ -52,14 +68,17 @@ kfree(void *pa) panic("kfree"); // Fill with junk to catch dangling refs. - memset(pa, 1, PGSIZE); + char ref = --kmem.kref[REF_INDEX(pa)]; + if(ref == 0){ + memset(pa, 1, PGSIZE); - r = (struct run*)pa; + r = (struct run*)pa; - acquire(&kmem.lock); - r->next = kmem.freelist; - kmem.freelist = r; - release(&kmem.lock); + acquire(&kmem.lock); + r->next = kmem.freelist; + kmem.freelist = r; + release(&kmem.lock); + } } // Allocate one 4096-byte page of physical memory. @@ -72,8 +91,10 @@ kalloc(void) acquire(&kmem.lock); r = kmem.freelist; - if(r) + if(r){ + kmem.kref[REF_INDEX(r)] = 1; kmem.freelist = r->next; + } release(&kmem.lock); if(r) diff --git a/kernel/param.h b/kernel/param.h index 8fc3a7d3..b27f9847 100644 --- a/kernel/param.h +++ b/kernel/param.h @@ -12,3 +12,9 @@ #define FSSIZE 2000 // size of file system in blocks #define MAXPATH 128 // maximum file path name #define NDISK 2 + +#define NMAP 5 //maximum number of mapped files per process +#define PROT_READ (1L << 1) +#define PROT_WRITE (1L << 2) +#define MAP_PRIVATE 0 +#define MAP_SHARED 1 \ No newline at end of file diff --git a/kernel/proc.c b/kernel/proc.c index c6788135..0d5c0491 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -123,6 +123,10 @@ allocproc(void) p->context.ra = (uint64)forkret; p->context.sp = p->kstack + PGSIZE; + memset(&p->vma_list, 0, sizeof p->vma_list); + + + return p; } @@ -272,9 +276,26 @@ fork(void) np->tf->a0 = 0; // increment reference counts on open file descriptors. - for(i = 0; i < NOFILE; i++) + for(i = 0; i < NOFILE; i++){ if(p->ofile[i]) np->ofile[i] = filedup(p->ofile[i]); + } + + for(i = 0; i < NMAP; i++){ + if(p->vma_list[i].valid){ + np->vma_list[i].f = p->vma_list[i].f; + filedup(p->vma_list[i].f); + np->vma_list[i].valid = 1; + np->vma_list[i].va = p->vma_list[i].va; + np->vma_list[i].length = p->vma_list[i].length; + np->vma_list[i].unmap_length = p->vma_list[i].unmap_length; + np->vma_list[i].prot = p->vma_list[i].prot; + np->vma_list[i].offset = p->vma_list[i].offset; + np->vma_list[i].flag = p->vma_list[i].flag; + } else{ + np->vma_list[i].valid = 0; + } + } np->cwd = idup(p->cwd); safestrcpy(np->name, p->name, sizeof(p->name)); @@ -334,6 +355,14 @@ exit(int status) } } + for(int i=0; i < NMAP; i++){ + if(p->vma_list[i].valid){ + struct file *f = p->vma_list[i].f; + fileclose(f); + p->vma_list[i].valid = 0; + } + } + begin_op(ROOTDEV); iput(p->cwd); end_op(ROOTDEV); diff --git a/kernel/proc.h b/kernel/proc.h index 812c7691..fb64ca37 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -80,6 +80,19 @@ struct trapframe { /* 280 */ uint64 t6; }; +struct vma +{ + int valid; + uint64 va; + int length; + int unmap_length; + struct file *f; + int prot; + int offset; + int flag; +}; + + enum procstate { UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; // Per-process state @@ -103,4 +116,5 @@ struct proc { struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) + struct vma vma_list[NMAP]; }; diff --git a/kernel/riscv.h b/kernel/riscv.h index 0aec0036..48db08ee 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -331,6 +331,8 @@ sfence_vma() #define PTE_W (1L << 2) #define PTE_X (1L << 3) #define PTE_U (1L << 4) // 1 -> user can access +#define PTE_D (1L << 7) +#define PTE_M (1L << 8) // shift a physical address to the right place for a PTE. #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) diff --git a/kernel/syscall.c b/kernel/syscall.c index ec78f3d8..7e368c8d 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -105,6 +105,8 @@ extern uint64 sys_wait(void); extern uint64 sys_write(void); extern uint64 sys_uptime(void); extern uint64 sys_ntas(void); +extern uint64 sys_mmap(void); +extern uint64 sys_munmap(void); static uint64 (*syscalls[])(void) = { [SYS_fork] sys_fork, @@ -129,6 +131,8 @@ static uint64 (*syscalls[])(void) = { [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, [SYS_ntas] sys_ntas, +[SYS_mmap] sys_mmap, +[SYS_munmap] sys_munmap, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index d329a6c3..a6da2517 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -23,3 +23,5 @@ // System calls for labs #define SYS_ntas 22 +#define SYS_mmap 23 +#define SYS_munmap 24 \ No newline at end of file diff --git a/kernel/trap.c b/kernel/trap.c index ca07db50..e8dfc110 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -4,6 +4,9 @@ #include "riscv.h" #include "spinlock.h" #include "proc.h" +#include "sleeplock.h" +#include "fs.h" +#include "file.h" #include "defs.h" struct spinlock tickslock; @@ -70,7 +73,41 @@ usertrap(void) syscall(); } else if((which_dev = devintr()) != 0){ // ok - } else { + } else if(r_scause() == 13 || r_scause() == 15){ + uint64 fault_addr = r_stval(); + uint64 vpage_addr = PGROUNDDOWN(fault_addr); + char *mem; + int prot; + for(struct vma *vma = p->vma_list; vma < p->vma_list + NMAP; vma++){ + if(vma->valid != 0 && vma->va <= fault_addr && vma->va + vma->length > fault_addr){ + if((mem = (char*) walkaddr(p->pagetable, vpage_addr)) == 0){ + mem = kalloc(); + prot = vma->prot; + } + + if(mem == 0){ + p->killed = 1; + printf("usertrap(): alloc memory failed\n"); + exit(-1); + } + memset(mem, 0, PGSIZE); + + if(mappages(p->pagetable, vpage_addr, PGSIZE, (uint64)mem, PTE_U|prot) != 0){ + kfree(mem); + p->killed = 1; + printf("usertrap(): cannot mapping\n"); + exit(-1); + } + + struct file *f = vma->f; + f->off = vpage_addr - vma->va; + fileread(f, vpage_addr, PGSIZE); + goto TRAP_END; + } + } + printf("usertrap(): page fault\n"); + p->killed = 1; + } else{ printf("usertrap(): unexpected scause %p (%s) pid=%d\n", r_scause(), scause_desc(r_scause()), p->pid); printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); p->killed = 1; @@ -79,6 +116,7 @@ usertrap(void) if(p->killed) exit(-1); +TRAP_END: // give up the CPU if this is a timer interrupt. if(which_dev == 2) yield(); diff --git a/kernel/vm.c b/kernel/vm.c index dd651843..67fc63f6 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -5,6 +5,10 @@ #include "riscv.h" #include "defs.h" #include "fs.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "proc.h" +#include "file.h" /* * the kernel's page table. @@ -75,7 +79,7 @@ kvminithart() // 21..39 -- 9 bits of level-1 index. // 12..20 -- 9 bits of level-0 index. // 0..12 -- 12 bits of byte offset within the page. -static pte_t * +pte_t * walk(pagetable_t pagetable, uint64 va, int alloc) { if(va >= MAXVA) @@ -190,6 +194,11 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 size, int do_free) if((pte = walk(pagetable, a, 0)) == 0) panic("uvmunmap: walk"); if((*pte & PTE_V) == 0){ + if(a == last) + break; + a += PGSIZE; + pa += PGSIZE; + continue; printf("va=%p pte=%p\n", a, *pte); panic("uvmunmap: not mapped"); } @@ -325,10 +334,27 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) char *mem; for(i = 0; i < sz; i += PGSIZE){ - if((pte = walk(old, i, 0)) == 0) - panic("uvmcopy: pte should exist"); - if((*pte & PTE_V) == 0) - panic("uvmcopy: page not present"); + if((pte = walk(old, i, 0)) == 0){ + // panic("uvmcopy: pte should exist"); + if((mem = kalloc()) == 0) + goto err; + + if(mappages(old, i, PGSIZE, (uint64)mem, PTE_U|PTE_M) != 0){ + kfree(mem); + goto err; + } + in_ref((void*)mem); + if(mappages(new, i, PGSIZE, (uint64)mem, PTE_U|PTE_M) != 0){ + kfree(mem); + goto err; + } + continue; + } + + if((*pte & PTE_V) == 0){ + // panic("uvmcopy: page not present"); + continue; + } pa = PTE2PA(*pte); flags = PTE_FLAGS(*pte); if((mem = kalloc()) == 0) @@ -339,6 +365,8 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) goto err; } } + + return 0; err: @@ -451,3 +479,86 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) return -1; } } + +uint64 sys_mmap(void) +{ + uint64 addr; + int length; + int prot; + int flags; + int fd; + int offset; + struct file *f; + struct proc *p = myproc(); + struct vma *vma; + uint sz = PGROUNDUP(p->sz); + + + if(argaddr(0, &addr) < 0 || + argint(1, &length) < 0 || + argint(2, &prot) < 0 || + argint(3, &flags) < 0 || + argint(4, &fd) < 0 || + argint(5, &offset) < 0) + return -1; + + f = p->ofile[fd]; + + if((prot & PROT_READ) && f->readable == 0) + return -1; + + if((prot & PROT_WRITE) && f->writable == 0 && flags) + return -1; + + + filedup(f); + for(vma=p->vma_list; vma < p->vma_list + NMAP; vma++){ + if(vma->valid == 0){ + vma->valid = 1; + vma->va = addr == 0 ? sz : addr; + vma->length = PGROUNDUP(length); + vma->unmap_length = 0; + vma->f = f; + vma->prot = prot; + vma->offset = offset; + vma->flag = flags; + p->sz = sz + length; + return vma->va; + } + } + fileclose(f); + return -1; +} + +uint64 sys_munmap(void) +{ + uint64 addr; + int length; + struct proc *p = myproc(); + struct vma *vma; + + + if(argaddr(0, &addr) < 0 || argint(1, &length) < 0) + return -1; + + + for(vma=p->vma_list; vma < p->vma_list + NMAP; vma++){ + if(vma->valid && vma->va <= addr && vma->va + vma->length >= addr){ + pte_t *pte = walk(p->pagetable, addr, 0); + vma->unmap_length += length; + if(vma->flag && (*pte & PTE_D)){ + struct file *f = vma->f; + f->off = addr - vma->va; + filewrite(f, addr, length); + } + uvmunmap(p->pagetable, addr, length, 1); + if(PGROUNDUP(vma->unmap_length) == vma->length){ + fileclose(vma->f); + vma->valid = 0; + } + return 0; + } + } + + return -1; +} diff --git a/user/mmaptest.c b/user/mmaptest.c index d782b5b4..ef304635 100644 --- a/user/mmaptest.c +++ b/user/mmaptest.c @@ -115,7 +115,6 @@ mmap_test(void) _v1(p); if (munmap(p, PGSIZE*2) == -1) err("munmap (1)"); - // should be able to map file opened read-only with private writable // mapping p = mmap(0, PGSIZE*2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); @@ -268,4 +267,3 @@ fork_test(void) printf("fork_test OK\n"); } - diff --git a/user/user.h b/user/user.h index 9fa19214..ccde1cc2 100644 --- a/user/user.h +++ b/user/user.h @@ -27,6 +27,8 @@ int ntas(); int crash(const char*, int); int mount(char*, char *); int umount(char*); +void *mmap(void*, int, int, int, int, int); +int munmap(void*, int); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index 1645c2b0..9beb6204 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -37,3 +37,5 @@ sub entry { entry("sleep"); entry("uptime"); entry("ntas"); +entry("mmap"); +entry("munmap");