pde_t* setupkvm(void);
char* uva2ka(pde_t*, char*);
int allocuvm(pde_t*, char*, uint);
+int deallocuvm(pde_t *pgdir, char *addr, uint sz);
void freevm(pde_t*);
void inituvm(pde_t*, char*, char*, uint);
int loaduvm(pde_t*, char*, struct inode *ip, uint, uint);
int
growproc(int n)
{
- if (!allocuvm(proc->pgdir, (char *)proc->sz, n))
- return -1;
+ if(n > 0){
+ if (!allocuvm(proc->pgdir, (char *)proc->sz, n))
+ return -1;
+ } else if(n < 0){
+ if (!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n))
+ return -1;
+ }
proc->sz += n;
switchuvm(proc);
return 0;
void
sbrktest(void)
{
+ int pid;
+
printf(stdout, "sbrk test\n");
+
+ // can one sbrk() less than a page?
char *a = sbrk(0);
int i;
for(i = 0; i < 5000; i++){
*b = 1;
a = b + 1;
}
- int pid = fork();
+ pid = fork();
if(pid < 0){
printf(stdout, "sbrk test fork failed\n");
exit();
if(pid == 0)
exit();
wait();
+
+ // can one allocate the full 640K?
+ a = sbrk(0);
+ uint amt = (640 * 1024) - (uint) a;
+ char *p = sbrk(amt);
+ if(p != a){
+ printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a);
+ exit();
+ }
+ char *lastaddr = (char *)(640 * 1024 - 1);
+ *lastaddr = 99;
+
+ // is one forbidden from allocating more than 640K?
+ c = sbrk(4096);
+ if(c != (char *) 0xffffffff){
+ printf(stdout, "sbrk allocated more than 640K, c %x\n", c);
+ exit();
+ }
+
+ // can one de-allocate?
+ a = sbrk(0);
+ c = sbrk(-4096);
+ if(c == (char *) 0xffffffff){
+ printf(stdout, "sbrk could not deallocate\n");
+ exit();
+ }
+ c = sbrk(0);
+ if(c != a - 4096){
+ printf(stdout, "sbrk deallocation produced wrong address, a %x c %x\n", a, c);
+ exit();
+ }
+
+ // can one re-allocate that page?
+ a = sbrk(0);
+ c = sbrk(4096);
+ if(c != a || sbrk(0) != a + 4096){
+ printf(stdout, "sbrk re-allocation failed, a %x c %x\n", a, c);
+ exit();
+ }
+ if(*lastaddr == 99){
+ // should be zero
+ printf(stdout, "sbrk de-allocation didn't really deallocate\n");
+ exit();
+ }
+
+ c = sbrk(4096);
+ if(c != (char *) 0xffffffff){
+ printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c);
+ exit();
+ }
+
printf(stdout, "sbrk test OK\n");
}
int
allocuvm(pde_t *pgdir, char *addr, uint sz)
{
- if (addr + sz >= (char*)USERTOP)
+ if (addr + sz > (char*)USERTOP)
return 0;
char *first = PGROUNDDOWN(addr);
char *last = PGROUNDDOWN(addr + sz - 1);
return 1;
}
+// deallocate some of the user pages, in response to sbrk()
+// with a negative argument. if addr is not page-aligned,
+// then only deallocates starting at the next page boundary.
+int
+deallocuvm(pde_t *pgdir, char *addr, uint sz)
+{
+ if (addr + sz > (char*)USERTOP)
+ return 0;
+ char *first = (char*) PGROUNDUP((uint)addr);
+ char *last = PGROUNDDOWN(addr + sz - 1);
+ char *a;
+ for(a = first; a <= last; a += PGSIZE){
+ pte_t *pte = walkpgdir(pgdir, a, 0);
+ if(pte && (*pte & PTE_P) != 0){
+ uint pa = PTE_ADDR(*pte);
+ if(pa == 0)
+ panic("deallocuvm");
+ kfree((void *) pa, PGSIZE);
+ *pte = 0;
+ }
+ }
+ return 1;
+}
+
// free a page table and all the physical memory pages
// in the user part.
void