summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMole Shang <[email protected]>2024-02-19 21:51:26 +0800
committerMole Shang <[email protected]>2024-02-19 22:13:01 +0800
commit09ba9112386d5d59d7f2a31c469768c582acb939 (patch)
treeb1dce737cdd94164e8d7f3f651ea7a86a22a42f3
parentd86118fc80267649b4791c8c0c72ebd60edf1ef2 (diff)
downloadxv6-labs-09ba9112386d5d59d7f2a31c469768c582acb939.tar.gz
xv6-labs-09ba9112386d5d59d7f2a31c469768c582acb939.tar.bz2
xv6-labs-09ba9112386d5d59d7f2a31c469768c582acb939.zip
lab mmap: finish
-rw-r--r--Makefile3
-rw-r--r--kernel/defs.h10
-rw-r--r--kernel/file.c31
-rw-r--r--kernel/memlayout.h2
-rw-r--r--kernel/proc.c156
-rw-r--r--kernel/proc.h16
-rw-r--r--kernel/syscall.c6
-rw-r--r--kernel/sysfile.c2
-rw-r--r--kernel/sysproc.c36
-rw-r--r--kernel/vm.c8
-rw-r--r--user/user.h11
-rwxr-xr-xuser/usys.pl2
12 files changed, 267 insertions, 16 deletions
diff --git a/Makefile b/Makefile
index 4c3d16a..2d97d63 100644
--- a/Makefile
+++ b/Makefile
@@ -243,6 +243,9 @@ endif
UPROGS += \
$U/_nettests
+UPROGS += \
+ $U/_mmaptest
+
UEXTRA=
UEXTRA += user/xargstest.sh
diff --git a/kernel/defs.h b/kernel/defs.h
index 541c97e..6257437 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -45,6 +45,9 @@ void fileinit(void);
int fileread(struct file*, uint64, int n);
int filestat(struct file*, uint64 addr);
int filewrite(struct file*, uint64, int n);
+int fileperm(struct file*);
+int mmap_read(struct file*, uint64, uint64, int);
+int munmap_write(struct file*, uint64, uint64, int);
// fs.c
void fsinit(int);
@@ -124,6 +127,8 @@ int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void procdump(void);
int get_nproc(void);
int pgaccess(uint64 base, int len, uint64 mask);
+uint64 mmap(uint64 addr, uint64 len, int prot, int flags, int fd, struct file* file, uint64 offset);
+int munmap(uint64 addr, uint64 len);
// swtch.S
void swtch(struct context*, struct context*);
@@ -163,6 +168,9 @@ int fetchstr(uint64, char*, int);
int fetchaddr(uint64, uint64*);
void syscall();
+// sysfile.c
+int argfd(int n, int *pfd, struct file **pf);
+
// sysinfo.c
int sys_info(uint64);
@@ -189,7 +197,7 @@ pagetable_t uvmcreate(void);
void uvmfirst(pagetable_t, uchar *, uint);
uint64 uvmalloc(pagetable_t, uint64, uint64, int);
uint64 uvmdealloc(pagetable_t, uint64, uint64);
-int uvmcopy(pagetable_t, pagetable_t, uint64);
+int uvmcopy(pagetable_t, pagetable_t, uint64, uint64);
void uvmfree(pagetable_t, uint64);
void uvmunmap(pagetable_t, uint64, uint64, int);
void uvmclear(pagetable_t, uint64);
diff --git a/kernel/file.c b/kernel/file.c
index 0fba21b..add6575 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -6,6 +6,7 @@
#include "riscv.h"
#include "defs.h"
#include "param.h"
+#include "fcntl.h"
#include "fs.h"
#include "spinlock.h"
#include "sleeplock.h"
@@ -197,3 +198,33 @@ filewrite(struct file *f, uint64 addr, int n)
return ret;
}
+int fileperm(struct file* f)
+{
+ int flags = 0;
+ if(f->writable)
+ flags |= PROT_WRITE;
+ if(f->readable)
+ flags |= PROT_READ;
+ return flags;
+}
+
+// reads file from disk to physical address, needed by mmap
+int mmap_read(struct file *file, uint64 pa, uint64 off, int size) {
+ int n = 0;
+ ilock(file->ip);
+ n = readi(file->ip, 0, pa, off, size);
+ off += n;
+ iunlock(file->ip);
+ return off;
+}
+
+// writes from virtual address to disk, needed by munmap
+int munmap_write(struct file *file, uint64 va, uint64 off, int size) {
+ int r;
+ begin_op();
+ ilock(file->ip);
+ r = writei(file->ip, 1, va, off, size);
+ iunlock(file->ip);
+ end_op();
+ return r;
+}
diff --git a/kernel/memlayout.h b/kernel/memlayout.h
index 74d2fd4..517a69e 100644
--- a/kernel/memlayout.h
+++ b/kernel/memlayout.h
@@ -77,3 +77,5 @@ struct usyscall {
int pid; // Process ID
};
#endif
+
+#define MAX_VM_ADDR (USYSCALL - PGSIZE)
diff --git a/kernel/proc.c b/kernel/proc.c
index 9a9bae9..923729d 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -1,4 +1,5 @@
#include "types.h"
+#include "fcntl.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
@@ -160,6 +161,14 @@ found:
return 0;
}
+ // setup mmap vm area
+ p->cur_max_vm_addr = MAX_VM_ADDR;
+ for(int i = 0; i < MAX_VM_AREA; i++){
+ struct vm_area* vma = &p->vma[i];
+ memset(&p->vma[i], 0, sizeof(struct vm_area));
+ vma->start_addr = -1;
+ }
+
// Set up new context to start executing at forkret,
// which returns to user space.
memset(&p->context, 0, sizeof(p->context));
@@ -328,7 +337,7 @@ fork(void)
}
// Copy user memory from parent to child.
- if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
+ if(uvmcopy(p->pagetable, np->pagetable, 0, p->sz) < 0){
freeproc(np);
release(&np->lock);
return -1;
@@ -350,6 +359,17 @@ fork(void)
np->ofile[i] = filedup(p->ofile[i]);
np->cwd = idup(p->cwd);
+ // copy vm areas
+ // TODO: cow & remap vm to pa
+ for(int i = 0; i < MAX_VM_AREA; i++){
+ if(p->vma[i].start_addr != -1){
+ memmove(&np->vma[i], &p->vma[i], sizeof(struct vm_area));
+ uvmcopy(p->pagetable, np->pagetable, p->vma[i].start_addr, p->vma[i].len);
+ if(p->vma[i].file)
+ filedup(p->vma[i].file);
+ }
+ }
+
safestrcpy(np->name, p->name, sizeof(p->name));
pid = np->pid;
@@ -393,6 +413,12 @@ exit(int status)
if(p == initproc)
panic("init exiting");
+ for(int i = 0; i < MAX_VM_AREA; i++){
+ if(p->vma[i].start_addr != -1){
+ uvmunmap(p->pagetable, p->vma[i].start_addr, p->vma[i].len/PGSIZE, 1);
+ }
+ }
+
// Close all open files.
for(int fd = 0; fd < NOFILE; fd++){
if(p->ofile[fd]){
@@ -768,3 +794,131 @@ pgaccess(uint64 base, int len, uint64 mask_addr)
// now copyout the mask to user memory
return copyout(pgtbl, mask_addr, (char *)&mask, sizeof(mask));
}
+
+// lab mmap
+// all the addrs should be page aligned.
+uint64 mmap(uint64 addr, uint64 len, int prot, int flags, int fd, struct file* file, uint64 offset)
+{
+ struct proc *p = myproc();
+ struct vm_area *vma = 0;
+
+ len = PGROUNDUP(len);
+ addr = PGROUNDDOWN(addr);
+
+ if(p->sz+len > MAXVA)
+ return -1;
+
+ if(offset < 0 || offset & PGSIZE)
+ return -1;
+
+ for(int i = 0; i < MAX_VM_AREA; i++){
+ if(p->vma[i].start_addr == -1){
+ vma = &p->vma[i];
+ break;
+ }
+ }
+ if(!vma)
+ goto mmap_bad;
+
+ vma->len = len;
+
+ if(addr >= p->sz && addr <= MAXVA)
+ vma->start_addr = addr;
+ else if (addr == 0) {
+ vma->start_addr = p->cur_max_vm_addr - len;
+ } else {
+ goto mmap_bad;
+ }
+ p->cur_max_vm_addr = vma->start_addr;
+
+ vma->fd = fd;
+ filedup(file);
+ vma->flags = flags;
+ vma->prot = prot;
+ vma->file = file;
+ vma->roff = offset;
+
+ int pte_flags = PTE_U|PTE_V;
+
+ if(vma->prot & PROT_READ)
+ pte_flags |= PTE_R;
+ if(vma->prot & PROT_WRITE)
+ pte_flags |= PTE_W;
+ if(vma->prot & PROT_EXEC)
+ pte_flags |= PTE_X;
+
+ for(uint64 a = vma->start_addr; a < vma->start_addr+len; a+= PGSIZE){
+ char *mem = kalloc();
+
+ if(mem == 0){
+ goto mmap_bad;
+ }
+ memset(mem, 0, PGSIZE);
+ vma->roff = mmap_read(vma->file, (uint64)mem, vma->roff, PGSIZE);
+
+ if(mappages(p->pagetable, a, PGSIZE, (uint64)mem, pte_flags) != 0){
+ kfree(mem);
+ uvmunmap(p->pagetable, a, PGSIZE, 0);
+ goto mmap_bad;
+ }
+ }
+
+ return vma->start_addr;
+
+mmap_bad:
+ return 0xffffffffffffffff;
+}
+
+// all the addrs should be page aligned.
+// ceil-round if not.
+int munmap(uint64 addr, uint64 len)
+{
+ struct proc *p = myproc();
+ struct vm_area *vma = 0;
+ int r = 0, dec_refcnt = 0;
+
+ if(addr+len > MAXVA)
+ return -1;
+
+ addr = PGROUNDDOWN(addr);
+ len = PGROUNDUP(len);
+
+ for(int i = 0; i < MAX_VM_AREA; i++){
+ if((p->vma[i].start_addr != -1) && addr >= p->vma[i].start_addr && addr+len <= p->vma[i].start_addr+p->vma[i].len){
+ vma = &p->vma[i];
+ break;
+ }
+ }
+
+ if(!vma){
+ return -1;
+ }
+
+ if(vma->flags & MAP_SHARED){
+ // do the writeback
+ vma->woff = addr-vma->start_addr;
+ if(vma->woff+len >= vma->roff){
+ // should decrease refcnt
+ dec_refcnt = 1;
+ }
+ r = munmap_write(vma->file, addr, vma->woff, len);
+ if(r)
+ return r;
+ if(dec_refcnt){
+ fileclose(vma->file);
+ }
+ vma->woff += len;
+ }
+
+ uvmunmap(p->pagetable, addr, len/PGSIZE, 1);
+
+ vma->len -= len;
+ if(dec_refcnt){
+ // mark it invalid
+ memset(vma, 0, sizeof(struct vm_area));
+ vma->start_addr = -1;
+ } else
+ vma->start_addr += len;
+
+ return r;
+}
diff --git a/kernel/proc.h b/kernel/proc.h
index a195b02..ebdbb7a 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -81,6 +81,20 @@ struct trapframe {
enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
+// mmap vm area
+#define MAX_VM_AREA 0x40
+
+struct vm_area {
+ uint64 start_addr;
+ uint64 roff;
+ uint64 woff;
+ uint64 len;
+ int prot;
+ int flags;
+ struct file* file;
+ int fd;
+};
+
// Per-process state
struct proc {
struct spinlock lock;
@@ -111,4 +125,6 @@ struct proc {
int alarm_tickspassed; // record how many ticks passed since last sigalarm handler call
int alarm_caninvoke; // prevent re-entrant calls to handler
struct trapframe *atpfm; // trapframe to resume after handling, must hold p->lock
+ struct vm_area vma[MAX_VM_AREA]; // vm_area
+ uint64 cur_max_vm_addr; // current max vm addr, used by mmap
};
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c39ebd8..3c8d3d8 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -120,6 +120,8 @@ extern uint64 sys_connect(void);
extern uint64 sys_pgaccess(void);
#endif
extern uint64 sys_symlink(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.
@@ -156,6 +158,8 @@ static uint64 (*syscalls[])(void) = {
[SYS_sigalarm] sys_sigalarm,
[SYS_sigreturn] sys_sigreturn,
[SYS_symlink] sys_symlink,
+[SYS_mmap] sys_mmap,
+[SYS_munmap] sys_munmap,
};
// syscall name maps for SYS_trace:
@@ -192,6 +196,8 @@ static char *syscall_names[] = {
[SYS_sigalarm] "sigalarm",
[SYS_sigreturn] "sigreturn",
[SYS_symlink] "symlink",
+[SYS_mmap] "mmap",
+[SYS_munmap] "munmap",
};
diff --git a/kernel/sysfile.c b/kernel/sysfile.c
index 9c12d44..639c02b 100644
--- a/kernel/sysfile.c
+++ b/kernel/sysfile.c
@@ -27,7 +27,7 @@ struct symlink {
// Fetch the nth word-sized system call argument as a file descriptor
// and return both the descriptor and the corresponding struct file.
-static int
+int
argfd(int n, int *pfd, struct file **pf)
{
int fd;
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index 715a511..abe1cdd 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -1,4 +1,5 @@
#include "types.h"
+#include "fcntl.h"
#include "riscv.h"
#include "param.h"
#include "defs.h"
@@ -140,7 +141,8 @@ sys_sigalarm(void)
return 0;
}
-uint64 sys_sigreturn(void)
+uint64
+sys_sigreturn(void)
{
struct proc *p = myproc();
// retore saved trapframe to resume
@@ -150,3 +152,35 @@ uint64 sys_sigreturn(void)
// make sure return the original a0 in trapframe to pass test3
return p->trapframe->a0;
}
+
+uint64
+sys_mmap(void)
+{
+ uint64 addr, len, offset;
+ int prot, flags, fd;
+ struct file* file;
+
+ argaddr(0, &addr);
+ argaddr(1, &len);
+ argint(2, &prot);
+ argint(3, &flags);
+ if(argfd(4, &fd, &file) == -1)
+ return 0xffffffffffffffff;
+ argaddr(5, &offset);
+
+ if(!(fileperm(file) & PROT_WRITE) && (prot & PROT_WRITE) && (flags == MAP_SHARED))
+ return 0xffffffffffffffff;
+
+ return mmap(addr, len, prot, flags, fd, file, offset);
+}
+
+uint64
+sys_munmap(void)
+{
+ uint64 addr, len;
+
+ argaddr(0, &addr);
+ argaddr(1, &len);
+
+ return munmap(addr, len);
+}
diff --git a/kernel/vm.c b/kernel/vm.c
index be7d042..08859ae 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -182,6 +182,7 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
uint64 a;
pte_t *pte;
+ int cow_pg = 0;
if((va % PGSIZE) != 0)
panic("uvmunmap: not aligned");
@@ -192,10 +193,11 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
if((*pte & PTE_V) == 0) {
printf("va=%p pte=%p\n", a, *pte);
panic("uvmunmap: not mapped");
+ cow_pg = 1;
}
if(PTE_FLAGS(*pte) == PTE_V)
panic("uvmunmap: not a leaf");
- if(do_free){
+ if(do_free && !cow_pg){
uint64 pa = PTE2PA(*pte);
kfree((void*)pa);
}
@@ -315,14 +317,14 @@ uvmfree(pagetable_t pagetable, uint64 sz)
// returns 0 on success, -1 on failure.
// frees any allocated pages on failure.
int
-uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
+uvmcopy(pagetable_t old, pagetable_t new, uint64 start, uint64 sz)
{
pte_t *pte;
uint64 pa, i;
uint flags;
// char *mem;
- for(i = 0; i < sz; i += PGSIZE){
+ for(i = start; i < start+sz; i += PGSIZE){
if((pte = walk(old, i, 0)) == 0)
panic("uvmcopy: pte should exist");
if((*pte & PTE_V) == 0)
diff --git a/user/user.h b/user/user.h
index ed7737a..5d6222e 100644
--- a/user/user.h
+++ b/user/user.h
@@ -35,14 +35,13 @@ int pgaccess(void *base, int len, void *mask);
// usyscall region
int ugetpid(void);
#endif
-<<<<<<< HEAD
-=======
int trace(int);
int sysinfo(struct sysinfo*);
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);
int symlink(char *, char *);
->>>>>>> fs
+void *mmap(void *, uint64, int, int, int, uint64);
+int munmap(void *, uint64);
// ulib.c
int stat(const char*, struct stat*);
@@ -60,12 +59,6 @@ void free(void*);
int atoi(const char*);
int memcmp(const void *, const void *, uint);
void *memcpy(void *, const void *, uint);
-<<<<<<< HEAD
-#ifdef LAB_LOCK
-int statistics(void*, int);
-#endif
-=======
// statistics.c
int statistics(void*, int);
->>>>>>> fs
diff --git a/user/usys.pl b/user/usys.pl
index 7f07795..8038a4c 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -43,3 +43,5 @@ entry("pgaccess");
entry("sigalarm");
entry("sigreturn");
entry("symlink");
+entry("mmap");
+entry("munmap");