Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion xv6-os/ci/test/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ def read_header(qemu: Qemu):
assert_eq(prefix[3], "")
assert prefix[4] in (f"hart {i + 1} starting" for i in range(2))
assert prefix[5] in (f"hart {i + 1} starting" for i in range(2))
assert_eq(prefix[6], "init: starting sh")
# lab-3 dumps the initial page table (vmprint) at boot before the shell
# starts; skip any such lines until the shell banner appears.
line = prefix[6]
for _ in range(256):
if line == "init: starting sh":
break
line = qemu.readline()
assert_eq(line, "init: starting sh")


if __name__ == "__main__":
Expand Down
37 changes: 7 additions & 30 deletions xv6-os/ci/test/suite/usertests.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,41 +57,18 @@
Xv6UserTest(name="forktest", timeout=timedelta(seconds=1)),
Xv6UserTest(name="sbrkbasic", timeout=timedelta(seconds=3)),
Xv6UserTest(name="sbrkmuch", timeout=timedelta(seconds=2)),
Xv6UserTest(
name="kernmem",
timeout=timedelta(milliseconds=500),
suffix_size=len("usertrap(): unexpected scause 0xd pid=6452"),
extra_lines=79,
),
Xv6UserTest(
name="MAXVAplus",
timeout=timedelta(seconds=30),
suffix_size=len("usertrap(): unexpected scause 0xf pid=6515"),
extra_lines=51,
),
Xv6UserTest(
name="sbrkfail",
timeout=timedelta(seconds=8),
suffix_size=len("usertrap(): unexpected scause 0xd pid=6553"),
extra_lines=1,
),
# CoW handles page faults (scause 13/15) and kills the process silently,
# so these tests print only "test <name>: OK" without the usertrap dump.
Xv6UserTest(name="kernmem", timeout=timedelta(milliseconds=500)),
Xv6UserTest(name="MAXVAplus", timeout=timedelta(seconds=30)),
Xv6UserTest(name="sbrkfail", timeout=timedelta(seconds=8)),
Xv6UserTest(name="sbrkarg", timeout=timedelta(seconds=2)),
Xv6UserTest(name="validatetest", timeout=timedelta(seconds=2)),
Xv6UserTest(name="bsstest", timeout=timedelta(seconds=2)),
Xv6UserTest(name="bigargtest", timeout=timedelta(seconds=2)),
Xv6UserTest(name="argptest", timeout=timedelta(seconds=2)),
Xv6UserTest(
name="stacktest",
timeout=timedelta(seconds=2),
suffix_size=len("usertrap(): unexpected scause 0xd pid=6561"),
extra_lines=1,
),
Xv6UserTest(
name="nowrite",
timeout=timedelta(seconds=2),
suffix_size=len("usertrap(): unexpected scause 0xf pid=6563"),
extra_lines=11,
),
Xv6UserTest(name="stacktest", timeout=timedelta(seconds=2)),
Xv6UserTest(name="nowrite", timeout=timedelta(seconds=2)),
Xv6UserTest(name="pgbug", timeout=timedelta(seconds=2)),
Xv6UserTest(
name="sbrkbugs",
Expand Down
4 changes: 4 additions & 0 deletions xv6-os/kernel/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ void ramdiskrw(struct buf*);
void* kalloc(void);
void kfree(void *);
void kinit(void);
void inc_refcount(void *);
uint get_refcount(void *);

// log.c
void initlog(int, struct superblock*);
Expand Down Expand Up @@ -174,6 +176,8 @@ uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
int cowalloc(pagetable_t, uint64);
void vmprint(pagetable_t);

// plic.c
void plicinit(void);
Expand Down
3 changes: 3 additions & 0 deletions xv6-os/kernel/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ exec(char *path, char **argv)
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);

if(p->pid == 1)
vmprint(pagetable);

return argc; // this ends up in a0, the first argument to main(argc, argv)

bad:
Expand Down
50 changes: 48 additions & 2 deletions xv6-os/kernel/kalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct run {
struct {
struct spinlock lock;
struct run *freelist;
uint refcount[PHYSTOP / PGSIZE];
} kmem;

void
Expand All @@ -35,8 +36,11 @@ freerange(void *pa_start, void *pa_end)
{
char *p;
p = (char*)PGROUNDUP((uint64)pa_start);
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) {
int pindex = (uint64)p / PGSIZE;
kmem.refcount[pindex] = 1;
kfree(p);
}
}

// Free the page of physical memory pointed at by pa,
Expand All @@ -51,6 +55,17 @@ kfree(void *pa)
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");

acquire(&kmem.lock);
int pindex = (uint64)pa / PGSIZE;
if(kmem.refcount[pindex] == 0)
panic("kfree refcount");
kmem.refcount[pindex]--;
int current_refcount = kmem.refcount[pindex];
release(&kmem.lock);

if(current_refcount > 0)
return;

// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);

Expand All @@ -72,11 +87,42 @@ kalloc(void)

acquire(&kmem.lock);
r = kmem.freelist;
if(r)
if(r) {
kmem.freelist = r->next;
int pindex = (uint64)r / PGSIZE;

if(kmem.refcount[pindex] > 0)
panic("kalloc");

kmem.refcount[pindex] = 1;
}
release(&kmem.lock);

if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}

void
inc_refcount(void *pa)
{
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("inc_refcount");

acquire(&kmem.lock);
int pindex = (uint64)pa / PGSIZE;
if(kmem.refcount[pindex] == 0)
panic("inc_refcount");
kmem.refcount[pindex]++;
release(&kmem.lock);
}

uint
get_refcount(void *pa)
{
acquire(&kmem.lock);
int pindex = (uint64)pa / PGSIZE;
uint curr_refcount = kmem.refcount[pindex];
release(&kmem.lock);
return curr_refcount;
}
1 change: 1 addition & 0 deletions xv6-os/kernel/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ typedef uint64 *pagetable_t; // 512 PTEs
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // user can access
#define PTE_RSW (1L << 8)

// shift a physical address to the right place for a PTE.
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
Expand Down
6 changes: 6 additions & 0 deletions xv6-os/kernel/trap.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ usertrap(void)
intr_on();

syscall();
} else if(r_scause() == 13 || r_scause() == 15) {
uint64 va = PGROUNDDOWN(r_stval());

if(cowalloc(p->pagetable, va) == -1) {
setkilled(p);
}
} else if((which_dev = devintr()) != 0){
// ok
} else {
Expand Down
95 changes: 87 additions & 8 deletions xv6-os/kernel/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
pte_t *pte;
uint64 pa, i;
uint flags;
char *mem;

for(i = 0; i < sz; i += PGSIZE){
if((pte = walk(old, i, 0)) == 0)
Expand All @@ -324,13 +323,19 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
panic("uvmcopy: page not present");
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte);
if((mem = kalloc()) == 0)
goto err;
memmove(mem, (char*)pa, PGSIZE);
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem);

if((flags & PTE_W) != 0)
flags = (flags & (~PTE_W)) | PTE_RSW;


if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){
goto err;
}

if((*pte& PTE_W) != 0)
*pte = (*pte & (~PTE_W)) | PTE_RSW;

inc_refcount((void*)pa);
}
return 0;

Expand All @@ -339,6 +344,42 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
return -1;
}

int
cowalloc(pagetable_t pagetable, uint64 va)
{
if(va >= MAXVA || va % PGSIZE != 0)
return -1;

pte_t *pte;
if ((pte = walk(pagetable, va, 0)) == 0)
return -1;

if((*pte & PTE_U) == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_RSW) == 0)
return -1;

uint64 old_pa = PTE2PA(*pte);

if(get_refcount((void*)old_pa) == 1) {
*pte = (*pte & (~PTE_RSW)) | PTE_W;
return 0;
}

uint64 new_pa = (uint64)kalloc();
if(new_pa == 0)
return -1;

uint flags = PTE_FLAGS(*pte);
flags = (flags & (~PTE_RSW)) | PTE_W;

memmove((void*)new_pa, (void*)old_pa, PGSIZE);
uvmunmap(pagetable, va, 1, 0);
mappages(pagetable, va, PGSIZE, new_pa, flags);

kfree((void *)old_pa);

return 0;
}

// mark a PTE invalid for user access.
// used by exec for the user stack guard page.
void
Expand Down Expand Up @@ -366,9 +407,13 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
if(va0 >= MAXVA)
return -1;
pte = walk(pagetable, va0, 0);
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
(*pte & PTE_W) == 0)
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
return -1;

if ((*pte & PTE_W) == 0 && cowalloc(pagetable, va0) == -1) {
return -1;
}

pa0 = PTE2PA(*pte);
n = PGSIZE - (dstva - va0);
if(n > len)
Expand Down Expand Up @@ -449,3 +494,37 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
return -1;
}
}

#define PT_SIZE 512

void
vmprinttable(pagetable_t pagetable, int level)
{
for (int i = 0; i < PT_SIZE; i++) {
pagetable_t temp_pagetable = pagetable;
pte_t pte = temp_pagetable[i];

if (!(pte & PTE_V))
continue;;

for (int j = 0; j <= 2 - level; j++) {
printf("..");
if (j != 2 - level)
printf(" ");
}
printf("%d ", i);

printf("pte %p pa %p\n", (void *) pte, (void *) PTE2PA(pte));

if (level != 0)
vmprinttable((pagetable_t) PTE2PA(pte), level-1);
}
}

void
vmprint(pagetable_t pagetable)
{
printf("page table %p\n", pagetable);

vmprinttable(pagetable, 2);
}
Loading