xref: /xv6-public/vm.c (revision 171c2cc6)
1b364c4b8SFrans Kaashoek #include "param.h"
2b364c4b8SFrans Kaashoek #include "types.h"
3b364c4b8SFrans Kaashoek #include "defs.h"
4b364c4b8SFrans Kaashoek #include "x86.h"
59aa0337dSFrans Kaashoek #include "memlayout.h"
6b364c4b8SFrans Kaashoek #include "mmu.h"
7b364c4b8SFrans Kaashoek #include "proc.h"
8b364c4b8SFrans Kaashoek #include "elf.h"
9b364c4b8SFrans Kaashoek 
10dd4438b4SAustin Clements extern char data[];  // defined by kernel.ld
11bd71a450SFrans Kaashoek pde_t *kpgdir;  // for use in scheduler()
12b364c4b8SFrans Kaashoek 
13b364c4b8SFrans Kaashoek // Set up CPU's kernel segment descriptors.
14a4b213cfSFrans Kaashoek // Run once on entry on each CPU.
15b364c4b8SFrans Kaashoek void
seginit(void)16faad047aSRobert Morris seginit(void)
17b364c4b8SFrans Kaashoek {
18b364c4b8SFrans Kaashoek   struct cpu *c;
19b364c4b8SFrans Kaashoek 
20d9818bddSFrans Kaashoek   // Map "logical" addresses to virtual addresses using identity map.
211afc9d3fSRobert Morris   // Cannot share a CODE descriptor for both kernel and user
221afc9d3fSRobert Morris   // because it would have to have DPL_USR, but the CPU forbids
231afc9d3fSRobert Morris   // an interrupt from CPL=0 to DPL=3.
24ed396c06SFrans Kaashoek   c = &cpus[cpuid()];
25b364c4b8SFrans Kaashoek   c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
26b364c4b8SFrans Kaashoek   c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
271afc9d3fSRobert Morris   c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
281afc9d3fSRobert Morris   c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
29b364c4b8SFrans Kaashoek   lgdt(c->gdt, sizeof(c->gdt));
30b364c4b8SFrans Kaashoek }
31b364c4b8SFrans Kaashoek 
3293a1e4cbSAustin Clements // Return the address of the PTE in page table pgdir
33d9818bddSFrans Kaashoek // that corresponds to virtual address va.  If alloc!=0,
34f25a3f9aSAustin Clements // create any required page table pages.
35f25a3f9aSAustin Clements static pte_t *
walkpgdir(pde_t * pgdir,const void * va,int alloc)36c092540eSRobert Morris walkpgdir(pde_t *pgdir, const void *va, int alloc)
37f25a3f9aSAustin Clements {
38f25a3f9aSAustin Clements   pde_t *pde;
39f25a3f9aSAustin Clements   pte_t *pgtab;
40f25a3f9aSAustin Clements 
41f25a3f9aSAustin Clements   pde = &pgdir[PDX(va)];
42f25a3f9aSAustin Clements   if(*pde & PTE_P){
43a7c03bd9SRobert Morris     pgtab = (pte_t*)P2V(PTE_ADDR(*pde));
441a81e38bSRuss Cox   } else {
45c092540eSRobert Morris     if(!alloc || (pgtab = (pte_t*)kalloc()) == 0)
46f25a3f9aSAustin Clements       return 0;
47f25a3f9aSAustin Clements     // Make sure all those PTE_P bits are zero.
48f25a3f9aSAustin Clements     memset(pgtab, 0, PGSIZE);
49f25a3f9aSAustin Clements     // The permissions here are overly generous, but they can
50f25a3f9aSAustin Clements     // be further restricted by the permissions in the page table
51f25a3f9aSAustin Clements     // entries, if necessary.
52a7c03bd9SRobert Morris     *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
53f25a3f9aSAustin Clements   }
54f25a3f9aSAustin Clements   return &pgtab[PTX(va)];
55f25a3f9aSAustin Clements }
56f25a3f9aSAustin Clements 
57c3dcf479SFrans Kaashoek // Create PTEs for virtual addresses starting at va that refer to
58c3dcf479SFrans Kaashoek // physical addresses starting at pa. va and size might not
59f25a3f9aSAustin Clements // be page-aligned.
60f25a3f9aSAustin Clements static int
mappages(pde_t * pgdir,void * va,uint size,uint pa,int perm)61c092540eSRobert Morris mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
62f25a3f9aSAustin Clements {
631a81e38bSRuss Cox   char *a, *last;
641a81e38bSRuss Cox   pte_t *pte;
65f25a3f9aSAustin Clements 
66c3dcf479SFrans Kaashoek   a = (char*)PGROUNDDOWN((uint)va);
67c3dcf479SFrans Kaashoek   last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
681a81e38bSRuss Cox   for(;;){
69c092540eSRobert Morris     if((pte = walkpgdir(pgdir, a, 1)) == 0)
701a81e38bSRuss Cox       return -1;
71f25a3f9aSAustin Clements     if(*pte & PTE_P)
72f25a3f9aSAustin Clements       panic("remap");
73f25a3f9aSAustin Clements     *pte = pa | perm | PTE_P;
74f25a3f9aSAustin Clements     if(a == last)
75f25a3f9aSAustin Clements       break;
76f25a3f9aSAustin Clements     a += PGSIZE;
77f25a3f9aSAustin Clements     pa += PGSIZE;
78f25a3f9aSAustin Clements   }
791a81e38bSRuss Cox   return 0;
80f25a3f9aSAustin Clements }
81f25a3f9aSAustin Clements 
827e7cb106SRobert Morris // There is one page table per process, plus one that's used when
837e7cb106SRobert Morris // a CPU is not running any process (kpgdir). The kernel uses the
847e7cb106SRobert Morris // current process's page table during system calls and interrupts;
857e7cb106SRobert Morris // page protection bits prevent user code from using the kernel's
867e7cb106SRobert Morris // mappings.
87f25a3f9aSAustin Clements //
88f25a3f9aSAustin Clements // setupkvm() and exec() set up every page table like this:
897e7cb106SRobert Morris //
907e7cb106SRobert Morris //   0..KERNBASE: user memory (text+data+stack+heap), mapped to
917e7cb106SRobert Morris //                phys memory allocated by the kernel
92e25b74caSFrans Kaashoek //   KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space)
937e7cb106SRobert Morris //   KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data)
947e7cb106SRobert Morris //                for the kernel's instructions and r/o data
957e7cb106SRobert Morris //   data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP,
967e7cb106SRobert Morris //                                  rw data + free physical memory
97f25a3f9aSAustin Clements //   0xfe000000..0: mapped direct (devices such as ioapic)
98f25a3f9aSAustin Clements //
997e7cb106SRobert Morris // The kernel allocates physical memory for its heap and for user memory
1007e7cb106SRobert Morris // between V2P(end) and the end of physical memory (PHYSTOP)
1017e7cb106SRobert Morris // (directly addressable from end..P2V(PHYSTOP)).
1027e7cb106SRobert Morris 
1037e7cb106SRobert Morris // This table defines the kernel's mappings, which are present in
1047e7cb106SRobert Morris // every process's page table.
105cf4b1ad9SRuss Cox static struct kmap {
106d9818bddSFrans Kaashoek   void *virt;
107d9818bddSFrans Kaashoek   uint phys_start;
108d9818bddSFrans Kaashoek   uint phys_end;
109cf4b1ad9SRuss Cox   int perm;
110cf4b1ad9SRuss Cox } kmap[] = {
1117e7cb106SRobert Morris  { (void*)KERNBASE, 0,             EXTMEM,    PTE_W}, // I/O space
112792d83caSRobert Morris  { (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0},     // kern text+rodata
113792d83caSRobert Morris  { (void*)data,     V2P(data),     PHYSTOP,   PTE_W}, // kern data+memory
1145460667cSFrans Kaashoek  { (void*)DEVSPACE, DEVSPACE,      0,         PTE_W}, // more devices
115cf4b1ad9SRuss Cox };
116f25a3f9aSAustin Clements 
117f25a3f9aSAustin Clements // Set up kernel part of a page table.
118f25a3f9aSAustin Clements pde_t*
setupkvm(void)1194ce832ddSFrans Kaashoek setupkvm(void)
120f25a3f9aSAustin Clements {
1211a81e38bSRuss Cox   pde_t *pgdir;
122cf4b1ad9SRuss Cox   struct kmap *k;
1231a81e38bSRuss Cox 
124c092540eSRobert Morris   if((pgdir = (pde_t*)kalloc()) == 0)
125f25a3f9aSAustin Clements     return 0;
126f25a3f9aSAustin Clements   memset(pgdir, 0, PGSIZE);
127a7c03bd9SRobert Morris   if (P2V(PHYSTOP) > (void*)DEVSPACE)
1285f069dcfSFrans Kaashoek     panic("PHYSTOP too high");
129cf4b1ad9SRuss Cox   for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
130e25b74caSFrans Kaashoek     if(mappages(pgdir, k->virt, k->phys_end - k->phys_start,
13103b30863SSaarett                 (uint)k->phys_start, k->perm) < 0) {
13203b30863SSaarett       freevm(pgdir);
133f25a3f9aSAustin Clements       return 0;
13403b30863SSaarett     }
135f25a3f9aSAustin Clements   return pgdir;
136f25a3f9aSAustin Clements }
137f25a3f9aSAustin Clements 
1389aa0337dSFrans Kaashoek // Allocate one page table for the machine for the kernel address
1399aa0337dSFrans Kaashoek // space for scheduler processes.
1409aa0337dSFrans Kaashoek void
kvmalloc(void)1419aa0337dSFrans Kaashoek kvmalloc(void)
1429aa0337dSFrans Kaashoek {
143c092540eSRobert Morris   kpgdir = setupkvm();
1449aa0337dSFrans Kaashoek   switchkvm();
1459aa0337dSFrans Kaashoek }
1469aa0337dSFrans Kaashoek 
14793a1e4cbSAustin Clements // Switch h/w page table register to the kernel-only page table,
14893a1e4cbSAustin Clements // for when no process is running.
149f25a3f9aSAustin Clements void
switchkvm(void)150cf4b1ad9SRuss Cox switchkvm(void)
151f25a3f9aSAustin Clements {
152a7c03bd9SRobert Morris   lcr3(V2P(kpgdir));   // switch to the kernel page table
153f25a3f9aSAustin Clements }
154f25a3f9aSAustin Clements 
155cf4b1ad9SRuss Cox // Switch TSS and h/w page table to correspond to process p.
156b364c4b8SFrans Kaashoek void
switchuvm(struct proc * p)157c4cc10daSRobert Morris switchuvm(struct proc *p)
158b364c4b8SFrans Kaashoek {
1598d1f9963SPeter Froehlich   if(p == 0)
1608d1f9963SPeter Froehlich     panic("switchuvm: no process");
1618d1f9963SPeter Froehlich   if(p->kstack == 0)
1628d1f9963SPeter Froehlich     panic("switchuvm: no kstack");
1638d1f9963SPeter Froehlich   if(p->pgdir == 0)
1648d1f9963SPeter Froehlich     panic("switchuvm: no pgdir");
1658d1f9963SPeter Froehlich 
166b364c4b8SFrans Kaashoek   pushcli();
1674638cabfSRobert Morris   mycpu()->gdt[SEG_TSS] = SEG16(STS_T32A, &mycpu()->ts,
1684638cabfSRobert Morris                                 sizeof(mycpu()->ts)-1, 0);
169abf847a0SFrans Kaashoek   mycpu()->gdt[SEG_TSS].s = 0;
170abf847a0SFrans Kaashoek   mycpu()->ts.ss0 = SEG_KDATA << 3;
171abf847a0SFrans Kaashoek   mycpu()->ts.esp0 = (uint)p->kstack + KSTACKSIZE;
17237939f24SFrans Kaashoek   // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit
17337939f24SFrans Kaashoek   // forbids I/O instructions (e.g., inb and outb) from user space
174abf847a0SFrans Kaashoek   mycpu()->ts.iomb = (ushort) 0xFFFF;
175b364c4b8SFrans Kaashoek   ltr(SEG_TSS << 3);
176a7c03bd9SRobert Morris   lcr3(V2P(p->pgdir));  // switch to process's address space
177b364c4b8SFrans Kaashoek   popcli();
178b364c4b8SFrans Kaashoek }
179b364c4b8SFrans Kaashoek 
1809a4670a1SAustin Clements // Load the initcode into address 0 of pgdir.
1819a4670a1SAustin Clements // sz must be less than a page.
182f25a3f9aSAustin Clements void
inituvm(pde_t * pgdir,char * init,uint sz)183f25a3f9aSAustin Clements inituvm(pde_t *pgdir, char *init, uint sz)
184f25a3f9aSAustin Clements {
1851a81e38bSRuss Cox   char *mem;
1861a81e38bSRuss Cox 
187f25a3f9aSAustin Clements   if(sz >= PGSIZE)
188f25a3f9aSAustin Clements     panic("inituvm: more than a page");
1891a81e38bSRuss Cox   mem = kalloc();
190f25a3f9aSAustin Clements   memset(mem, 0, PGSIZE);
191a7c03bd9SRobert Morris   mappages(pgdir, 0, PGSIZE, V2P(mem), PTE_W|PTE_U);
192f25a3f9aSAustin Clements   memmove(mem, init, sz);
193f25a3f9aSAustin Clements }
194f25a3f9aSAustin Clements 
1959a4670a1SAustin Clements // Load a program segment into pgdir.  addr must be page-aligned
1969a4670a1SAustin Clements // and the pages from addr to addr+sz must already be mapped.
197f25a3f9aSAustin Clements int
loaduvm(pde_t * pgdir,char * addr,struct inode * ip,uint offset,uint sz)198f25a3f9aSAustin Clements loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
199f25a3f9aSAustin Clements {
200f25a3f9aSAustin Clements   uint i, pa, n;
201f25a3f9aSAustin Clements   pte_t *pte;
202f25a3f9aSAustin Clements 
203f25a3f9aSAustin Clements   if((uint) addr % PGSIZE != 0)
204cf4b1ad9SRuss Cox     panic("loaduvm: addr must be page aligned");
205f25a3f9aSAustin Clements   for(i = 0; i < sz; i += PGSIZE){
2068a9933a2SFrans Kaashoek     if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
207cf4b1ad9SRuss Cox       panic("loaduvm: address should exist");
208f25a3f9aSAustin Clements     pa = PTE_ADDR(*pte);
2091a81e38bSRuss Cox     if(sz - i < PGSIZE)
2101a81e38bSRuss Cox       n = sz - i;
2111a81e38bSRuss Cox     else
2121a81e38bSRuss Cox       n = PGSIZE;
213a7c03bd9SRobert Morris     if(readi(ip, P2V(pa), offset+i, n) != n)
2141a81e38bSRuss Cox       return -1;
215f25a3f9aSAustin Clements   }
2161a81e38bSRuss Cox   return 0;
217f25a3f9aSAustin Clements }
218f25a3f9aSAustin Clements 
219cf4b1ad9SRuss Cox // Allocate page tables and physical memory to grow process from oldsz to
220cf4b1ad9SRuss Cox // newsz, which need not be page aligned.  Returns new size or 0 on error.
221b364c4b8SFrans Kaashoek int
allocuvm(pde_t * pgdir,uint oldsz,uint newsz)22279cd8b3eSAustin Clements allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
223b364c4b8SFrans Kaashoek {
224417c3711SRuss Cox   char *mem;
225417c3711SRuss Cox   uint a;
2261a81e38bSRuss Cox 
2276f232758SFrans Kaashoek   if(newsz >= KERNBASE)
228b364c4b8SFrans Kaashoek     return 0;
2291a81e38bSRuss Cox   if(newsz < oldsz)
2301a81e38bSRuss Cox     return oldsz;
2311a81e38bSRuss Cox 
232417c3711SRuss Cox   a = PGROUNDUP(oldsz);
233417c3711SRuss Cox   for(; a < newsz; a += PGSIZE){
2341a81e38bSRuss Cox     mem = kalloc();
235eb18645fSRobert Morris     if(mem == 0){
236dd645ef1SAustin Clements       cprintf("allocuvm out of memory\n");
23779cd8b3eSAustin Clements       deallocuvm(pgdir, newsz, oldsz);
238b364c4b8SFrans Kaashoek       return 0;
239b364c4b8SFrans Kaashoek     }
240b364c4b8SFrans Kaashoek     memset(mem, 0, PGSIZE);
241a7c03bd9SRobert Morris     if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
2420a4a4230SRobert Morris       cprintf("allocuvm out of memory (2)\n");
2430a4a4230SRobert Morris       deallocuvm(pgdir, newsz, oldsz);
2440a4a4230SRobert Morris       kfree(mem);
2450a4a4230SRobert Morris       return 0;
2460a4a4230SRobert Morris     }
247eb18645fSRobert Morris   }
2481a81e38bSRuss Cox   return newsz;
249b364c4b8SFrans Kaashoek }
250b364c4b8SFrans Kaashoek 
25179cd8b3eSAustin Clements // Deallocate user pages to bring the process size from oldsz to
25279cd8b3eSAustin Clements // newsz.  oldsz and newsz need not be page-aligned, nor does newsz
25379cd8b3eSAustin Clements // need to be less than oldsz.  oldsz can be larger than the actual
25479cd8b3eSAustin Clements // process size.  Returns the new process size.
25583d2db91SRobert Morris int
deallocuvm(pde_t * pgdir,uint oldsz,uint newsz)25679cd8b3eSAustin Clements deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
25783d2db91SRobert Morris {
2581a81e38bSRuss Cox   pte_t *pte;
259417c3711SRuss Cox   uint a, pa;
2601a81e38bSRuss Cox 
2611a81e38bSRuss Cox   if(newsz >= oldsz)
2621a81e38bSRuss Cox     return oldsz;
2631a81e38bSRuss Cox 
264417c3711SRuss Cox   a = PGROUNDUP(newsz);
265417c3711SRuss Cox   for(; a  < oldsz; a += PGSIZE){
2668a9933a2SFrans Kaashoek     pte = walkpgdir(pgdir, (char*)a, 0);
267052e1848SAustin Clements     if(!pte)
268ffe44492SCody Cutler       a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
269052e1848SAustin Clements     else if((*pte & PTE_P) != 0){
2701a81e38bSRuss Cox       pa = PTE_ADDR(*pte);
27183d2db91SRobert Morris       if(pa == 0)
27279cd8b3eSAustin Clements         panic("kfree");
273a7c03bd9SRobert Morris       char *v = P2V(pa);
274547c28fcSFrans Kaashoek       kfree(v);
27583d2db91SRobert Morris       *pte = 0;
27683d2db91SRobert Morris     }
27783d2db91SRobert Morris   }
2781a81e38bSRuss Cox   return newsz;
27983d2db91SRobert Morris }
28083d2db91SRobert Morris 
28193a1e4cbSAustin Clements // Free a page table and all the physical memory pages
282c9959978SRobert Morris // in the user part.
283b364c4b8SFrans Kaashoek void
freevm(pde_t * pgdir)284b364c4b8SFrans Kaashoek freevm(pde_t *pgdir)
285b364c4b8SFrans Kaashoek {
286b3cfd7fcSAustin Clements   uint i;
287b364c4b8SFrans Kaashoek 
2881a81e38bSRuss Cox   if(pgdir == 0)
289b3cfd7fcSAustin Clements     panic("freevm: no pgdir");
2906f232758SFrans Kaashoek   deallocuvm(pgdir, KERNBASE, 0);
291b364c4b8SFrans Kaashoek   for(i = 0; i < NPDENTRIES; i++){
292547c28fcSFrans Kaashoek     if(pgdir[i] & PTE_P){
293a7c03bd9SRobert Morris       char * v = P2V(PTE_ADDR(pgdir[i]));
294547c28fcSFrans Kaashoek       kfree(v);
295547c28fcSFrans Kaashoek     }
296b364c4b8SFrans Kaashoek   }
2971a81e38bSRuss Cox   kfree((char*)pgdir);
298b364c4b8SFrans Kaashoek }
299b364c4b8SFrans Kaashoek 
30091aa0f32SAustin Clements // Clear PTE_U on a page. Used to create an inaccessible
30191aa0f32SAustin Clements // page beneath the user stack.
30291aa0f32SAustin Clements void
clearpteu(pde_t * pgdir,char * uva)3032eb214c9SAustin Clements clearpteu(pde_t *pgdir, char *uva)
30491aa0f32SAustin Clements {
30591aa0f32SAustin Clements   pte_t *pte;
30691aa0f32SAustin Clements 
30791aa0f32SAustin Clements   pte = walkpgdir(pgdir, uva, 0);
30891aa0f32SAustin Clements   if(pte == 0)
3092eb214c9SAustin Clements     panic("clearpteu");
31091aa0f32SAustin Clements   *pte &= ~PTE_U;
31191aa0f32SAustin Clements }
31291aa0f32SAustin Clements 
31393a1e4cbSAustin Clements // Given a parent process's page table, create a copy
314c4cc10daSRobert Morris // of it for a child.
315b364c4b8SFrans Kaashoek pde_t*
copyuvm(pde_t * pgdir,uint sz)316b364c4b8SFrans Kaashoek copyuvm(pde_t *pgdir, uint sz)
317b364c4b8SFrans Kaashoek {
3181a81e38bSRuss Cox   pde_t *d;
319b364c4b8SFrans Kaashoek   pte_t *pte;
320ff278344SStephen Tu   uint pa, i, flags;
321b364c4b8SFrans Kaashoek   char *mem;
322b364c4b8SFrans Kaashoek 
323c092540eSRobert Morris   if((d = setupkvm()) == 0)
3241a81e38bSRuss Cox     return 0;
325b364c4b8SFrans Kaashoek   for(i = 0; i < sz; i += PGSIZE){
3268a9933a2SFrans Kaashoek     if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0)
327cf4b1ad9SRuss Cox       panic("copyuvm: pte should exist");
328d3ecf3ebSAustin Clements     if(!(*pte & PTE_P))
329cf4b1ad9SRuss Cox       panic("copyuvm: page not present");
330b364c4b8SFrans Kaashoek     pa = PTE_ADDR(*pte);
331ff278344SStephen Tu     flags = PTE_FLAGS(*pte);
3321a81e38bSRuss Cox     if((mem = kalloc()) == 0)
333ba04b3e7SAustin Clements       goto bad;
334a7c03bd9SRobert Morris     memmove(mem, (char*)P2V(pa), PGSIZE);
335*171c2cc6SFrans Kaashoek     if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) {
336*171c2cc6SFrans Kaashoek       kfree(mem);
337ba04b3e7SAustin Clements       goto bad;
338b364c4b8SFrans Kaashoek     }
339*171c2cc6SFrans Kaashoek   }
340b364c4b8SFrans Kaashoek   return d;
341ba04b3e7SAustin Clements 
342ba04b3e7SAustin Clements bad:
343ba04b3e7SAustin Clements   freevm(d);
344ba04b3e7SAustin Clements   return 0;
345b364c4b8SFrans Kaashoek }
346b364c4b8SFrans Kaashoek 
347cf4b1ad9SRuss Cox //PAGEBREAK!
348547c28fcSFrans Kaashoek // Map user virtual address to kernel address.
349cf4b1ad9SRuss Cox char*
uva2ka(pde_t * pgdir,char * uva)350cf4b1ad9SRuss Cox uva2ka(pde_t *pgdir, char *uva)
351cf4b1ad9SRuss Cox {
352cf4b1ad9SRuss Cox   pte_t *pte;
353cf4b1ad9SRuss Cox 
3548a9933a2SFrans Kaashoek   pte = walkpgdir(pgdir, uva, 0);
355cf4b1ad9SRuss Cox   if((*pte & PTE_P) == 0)
356cf4b1ad9SRuss Cox     return 0;
357cf4b1ad9SRuss Cox   if((*pte & PTE_U) == 0)
358cf4b1ad9SRuss Cox     return 0;
359a7c03bd9SRobert Morris   return (char*)P2V(PTE_ADDR(*pte));
360cf4b1ad9SRuss Cox }
361cf4b1ad9SRuss Cox 
362cf4b1ad9SRuss Cox // Copy len bytes from p to user address va in page table pgdir.
363cf4b1ad9SRuss Cox // Most useful when pgdir is not the current page table.
3644655d42eSRobert Morris // uva2ka ensures this only works for PTE_U pages.
3654655d42eSRobert Morris int
copyout(pde_t * pgdir,uint va,void * p,uint len)366cf4b1ad9SRuss Cox copyout(pde_t *pgdir, uint va, void *p, uint len)
3674655d42eSRobert Morris {
3681a81e38bSRuss Cox   char *buf, *pa0;
3691a81e38bSRuss Cox   uint n, va0;
3701a81e38bSRuss Cox 
371cf4b1ad9SRuss Cox   buf = (char*)p;
3724655d42eSRobert Morris   while(len > 0){
3731a81e38bSRuss Cox     va0 = (uint)PGROUNDDOWN(va);
3741a81e38bSRuss Cox     pa0 = uva2ka(pgdir, (char*)va0);
3754655d42eSRobert Morris     if(pa0 == 0)
3761a81e38bSRuss Cox       return -1;
3771a81e38bSRuss Cox     n = PGSIZE - (va - va0);
3784655d42eSRobert Morris     if(n > len)
3794655d42eSRobert Morris       n = len;
3804655d42eSRobert Morris     memmove(pa0 + (va - va0), buf, n);
3814655d42eSRobert Morris     len -= n;
3824655d42eSRobert Morris     buf += n;
3834655d42eSRobert Morris     va = va0 + PGSIZE;
3844655d42eSRobert Morris   }
3851a81e38bSRuss Cox   return 0;
3864655d42eSRobert Morris }
38774c77da6SFrans Kaashoek 
38874c77da6SFrans Kaashoek //PAGEBREAK!
38974c77da6SFrans Kaashoek // Blank page.
39074c77da6SFrans Kaashoek //PAGEBREAK!
39174c77da6SFrans Kaashoek // Blank page.
39274c77da6SFrans Kaashoek //PAGEBREAK!
39374c77da6SFrans Kaashoek // Blank page.
39474c77da6SFrans Kaashoek 
395