650 lines
15 KiB
C
650 lines
15 KiB
C
#include "include/param.h"
|
|
#include "include/types.h"
|
|
#include "include/memlayout.h"
|
|
#include "include/elf.h"
|
|
#include "include/riscv.h"
|
|
#include "include/vm.h"
|
|
#include "include/kalloc.h"
|
|
#include "include/proc.h"
|
|
#include "include/printf.h"
|
|
#include "include/string.h"
|
|
|
|
/*
|
|
* the kernel's page table.
|
|
*/
|
|
pagetable_t kernel_pagetable;
|
|
|
|
extern char etext[]; // kernel.ld sets this to end of kernel code.
|
|
extern char trampoline[]; // trampoline.S
|
|
/*
|
|
* create a direct-map page table for the kernel.
|
|
*/
|
|
void
|
|
kvminit()
|
|
{
|
|
kernel_pagetable = (pagetable_t) kalloc();
|
|
// printf("kernel_pagetable: %p\n", kernel_pagetable);
|
|
|
|
memset(kernel_pagetable, 0, PGSIZE);
|
|
|
|
// uart registers
|
|
kvmmap(UART_V, UART, PGSIZE, PTE_R | PTE_W);
|
|
|
|
#ifdef QEMU
|
|
// virtio mmio disk interface
|
|
kvmmap(VIRTIO0_V, VIRTIO0, PGSIZE, PTE_R | PTE_W);
|
|
#endif
|
|
// CLINT
|
|
kvmmap(CLINT_V, CLINT, 0x10000, PTE_R | PTE_W);
|
|
|
|
// PLIC
|
|
kvmmap(PLIC_V, PLIC, 0x4000, PTE_R | PTE_W);
|
|
kvmmap(PLIC_V + 0x200000, PLIC + 0x200000, 0x4000, PTE_R | PTE_W);
|
|
|
|
#ifndef QEMU
|
|
// GPIOHS
|
|
kvmmap(GPIOHS_V, GPIOHS, 0x1000, PTE_R | PTE_W);
|
|
|
|
// DMAC
|
|
kvmmap(DMAC_V, DMAC, 0x1000, PTE_R | PTE_W);
|
|
|
|
// GPIO
|
|
// kvmmap(GPIO_V, GPIO, 0x1000, PTE_R | PTE_W);
|
|
|
|
// SPI_SLAVE
|
|
kvmmap(SPI_SLAVE_V, SPI_SLAVE, 0x1000, PTE_R | PTE_W);
|
|
|
|
// FPIOA
|
|
kvmmap(FPIOA_V, FPIOA, 0x1000, PTE_R | PTE_W);
|
|
|
|
// SPI0
|
|
kvmmap(SPI0_V, SPI0, 0x1000, PTE_R | PTE_W);
|
|
|
|
// SPI1
|
|
kvmmap(SPI1_V, SPI1, 0x1000, PTE_R | PTE_W);
|
|
|
|
// SPI2
|
|
kvmmap(SPI2_V, SPI2, 0x1000, PTE_R | PTE_W);
|
|
|
|
// SYSCTL
|
|
kvmmap(SYSCTL_V, SYSCTL, 0x1000, PTE_R | PTE_W);
|
|
|
|
#endif
|
|
|
|
// map rustsbi
|
|
// kvmmap(RUSTSBI_BASE, RUSTSBI_BASE, KERNBASE - RUSTSBI_BASE, PTE_R | PTE_X);
|
|
// map kernel text executable and read-only.
|
|
kvmmap(KERNBASE, KERNBASE, (uint64)etext - KERNBASE, PTE_R | PTE_X);
|
|
// map kernel data and the physical RAM we'll make use of.
|
|
kvmmap((uint64)etext, (uint64)etext, PHYSTOP - (uint64)etext, PTE_R | PTE_W);
|
|
// map the trampoline for trap entry/exit to
|
|
// the highest virtual address in the kernel.
|
|
kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
|
|
|
|
#ifdef DEBUG
|
|
printf("kvminit\n");
|
|
#endif
|
|
}
|
|
|
|
// Switch h/w page table register to the kernel's page table,
|
|
// and enable paging.
|
|
void
|
|
kvminithart()
|
|
{
|
|
w_satp(MAKE_SATP(kernel_pagetable));
|
|
// reg_info();
|
|
sfence_vma();
|
|
#ifdef DEBUG
|
|
printf("kvminithart\n");
|
|
#endif
|
|
}
|
|
|
|
// Return the address of the PTE in page table pagetable
|
|
// that corresponds to virtual address va. If alloc!=0,
|
|
// create any required page-table pages.
|
|
//
|
|
// The risc-v Sv39 scheme has three levels of page-table
|
|
// pages. A page-table page contains 512 64-bit PTEs.
|
|
// A 64-bit virtual address is split into five fields:
|
|
// 39..63 -- must be zero.
|
|
// 30..38 -- 9 bits of level-2 index.
|
|
// 21..29 -- 9 bits of level-1 index.
|
|
// 12..20 -- 9 bits of level-0 index.
|
|
// 0..11 -- 12 bits of byte offset within the page.
|
|
pte_t *
|
|
walk(pagetable_t pagetable, uint64 va, int alloc)
|
|
{
|
|
|
|
if(va >= MAXVA)
|
|
panic("walk");
|
|
|
|
for(int level = 2; level > 0; level--) {
|
|
pte_t *pte = &pagetable[PX(level, va)];
|
|
if(*pte & PTE_V) {
|
|
pagetable = (pagetable_t)PTE2PA(*pte);
|
|
} else {
|
|
if(!alloc || (pagetable = (pde_t*)kalloc()) == NULL)
|
|
return NULL;
|
|
memset(pagetable, 0, PGSIZE);
|
|
*pte = PA2PTE(pagetable) | PTE_V;
|
|
}
|
|
}
|
|
return &pagetable[PX(0, va)];
|
|
}
|
|
|
|
// Look up a virtual address, return the physical address,
|
|
// or 0 if not mapped.
|
|
// Can only be used to look up user pages.
|
|
uint64
|
|
walkaddr(pagetable_t pagetable, uint64 va)
|
|
{
|
|
pte_t *pte;
|
|
uint64 pa;
|
|
|
|
if(va >= MAXVA)
|
|
return NULL;
|
|
|
|
pte = walk(pagetable, va, 0);
|
|
if(pte == 0)
|
|
return NULL;
|
|
if((*pte & PTE_V) == 0)
|
|
return NULL;
|
|
if((*pte & PTE_U) == 0)
|
|
return NULL;
|
|
pa = PTE2PA(*pte);
|
|
return pa;
|
|
}
|
|
|
|
// add a mapping to the kernel page table.
|
|
// only used when booting.
|
|
// does not flush TLB or enable paging.
|
|
void
|
|
kvmmap(uint64 va, uint64 pa, uint64 sz, int perm)
|
|
{
|
|
if(mappages(kernel_pagetable, va, sz, pa, perm) != 0)
|
|
panic("kvmmap");
|
|
}
|
|
|
|
// translate a kernel virtual address to
|
|
// a physical address. only needed for
|
|
// addresses on the stack.
|
|
// assumes va is page aligned.
|
|
uint64
|
|
kvmpa(uint64 va)
|
|
{
|
|
return kwalkaddr(kernel_pagetable, va);
|
|
}
|
|
|
|
uint64
|
|
kwalkaddr(pagetable_t kpt, uint64 va)
|
|
{
|
|
uint64 off = va % PGSIZE;
|
|
pte_t *pte;
|
|
uint64 pa;
|
|
|
|
pte = walk(kpt, va, 0);
|
|
if(pte == 0)
|
|
panic("kvmpa");
|
|
if((*pte & PTE_V) == 0)
|
|
panic("kvmpa");
|
|
pa = PTE2PA(*pte);
|
|
return pa+off;
|
|
}
|
|
|
|
// Create PTEs for virtual addresses starting at va that refer to
|
|
// physical addresses starting at pa. va and size might not
|
|
// be page-aligned. Returns 0 on success, -1 if walk() couldn't
|
|
// allocate a needed page-table page.
|
|
int
|
|
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
|
|
{
|
|
uint64 a, last;
|
|
pte_t *pte;
|
|
|
|
a = PGROUNDDOWN(va);
|
|
last = PGROUNDDOWN(va + size - 1);
|
|
|
|
for(;;){
|
|
if((pte = walk(pagetable, a, 1)) == NULL)
|
|
return -1;
|
|
if(*pte & PTE_V)
|
|
panic("remap");
|
|
*pte = PA2PTE(pa) | perm | PTE_V;
|
|
if(a == last)
|
|
break;
|
|
a += PGSIZE;
|
|
pa += PGSIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Remove npages of mappings starting from va. va must be
|
|
// page-aligned. The mappings must exist.
|
|
// Optionally free the physical memory.
|
|
void
|
|
vmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
|
|
{
|
|
uint64 a;
|
|
pte_t *pte;
|
|
|
|
if((va % PGSIZE) != 0)
|
|
panic("vmunmap: not aligned");
|
|
|
|
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
|
|
if((pte = walk(pagetable, a, 0)) == 0)
|
|
panic("vmunmap: walk");
|
|
if((*pte & PTE_V) == 0)
|
|
panic("vmunmap: not mapped");
|
|
if(PTE_FLAGS(*pte) == PTE_V)
|
|
panic("vmunmap: not a leaf");
|
|
if(do_free){
|
|
uint64 pa = PTE2PA(*pte);
|
|
kfree((void*)pa);
|
|
}
|
|
*pte = 0;
|
|
}
|
|
}
|
|
|
|
// create an empty user page table.
|
|
// returns 0 if out of memory.
|
|
pagetable_t
|
|
uvmcreate()
|
|
{
|
|
pagetable_t pagetable;
|
|
pagetable = (pagetable_t) kalloc();
|
|
if(pagetable == NULL)
|
|
return NULL;
|
|
memset(pagetable, 0, PGSIZE);
|
|
return pagetable;
|
|
}
|
|
|
|
// Load the user initcode into address 0 of pagetable,
|
|
// for the very first process.
|
|
// sz must be less than a page.
|
|
void
|
|
uvminit(pagetable_t pagetable, pagetable_t kpagetable, uchar *src, uint sz)
|
|
{
|
|
char *mem;
|
|
|
|
if(sz >= PGSIZE)
|
|
panic("inituvm: more than a page");
|
|
mem = kalloc();
|
|
// printf("[uvminit]kalloc: %p\n", mem);
|
|
memset(mem, 0, PGSIZE);
|
|
mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
|
|
mappages(kpagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X);
|
|
memmove(mem, src, sz);
|
|
// for (int i = 0; i < sz; i ++) {
|
|
// printf("[uvminit]mem: %p, %x\n", mem + i, mem[i]);
|
|
// }
|
|
}
|
|
|
|
// Allocate PTEs and physical memory to grow process from oldsz to
|
|
// newsz, which need not be page aligned. Returns new size or 0 on error.
|
|
uint64
|
|
uvmalloc(pagetable_t pagetable, pagetable_t kpagetable, uint64 oldsz, uint64 newsz)
|
|
{
|
|
char *mem;
|
|
uint64 a;
|
|
|
|
if(newsz < oldsz)
|
|
return oldsz;
|
|
|
|
oldsz = PGROUNDUP(oldsz);
|
|
for(a = oldsz; a < newsz; a += PGSIZE){
|
|
mem = kalloc();
|
|
if(mem == NULL){
|
|
uvmdealloc(pagetable, kpagetable, a, oldsz);
|
|
return 0;
|
|
}
|
|
memset(mem, 0, PGSIZE);
|
|
if (mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0) {
|
|
kfree(mem);
|
|
uvmdealloc(pagetable, kpagetable, a, oldsz);
|
|
return 0;
|
|
}
|
|
if (mappages(kpagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R) != 0){
|
|
int npages = (a - oldsz) / PGSIZE;
|
|
vmunmap(pagetable, oldsz, npages + 1, 1); // plus the page allocated above.
|
|
vmunmap(kpagetable, oldsz, npages, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
return newsz;
|
|
}
|
|
|
|
// Deallocate user pages to bring the process size from oldsz to
|
|
// newsz. oldsz and newsz need not be page-aligned, nor does newsz
|
|
// need to be less than oldsz. oldsz can be larger than the actual
|
|
// process size. Returns the new process size.
|
|
uint64
|
|
uvmdealloc(pagetable_t pagetable, pagetable_t kpagetable, uint64 oldsz, uint64 newsz)
|
|
{
|
|
if(newsz >= oldsz)
|
|
return oldsz;
|
|
|
|
if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
|
|
int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
|
|
vmunmap(kpagetable, PGROUNDUP(newsz), npages, 0);
|
|
vmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
|
|
}
|
|
|
|
return newsz;
|
|
}
|
|
|
|
// Recursively free page-table pages.
|
|
// All leaf mappings must already have been removed.
|
|
void
|
|
freewalk(pagetable_t pagetable)
|
|
{
|
|
// there are 2^9 = 512 PTEs in a page table.
|
|
for(int i = 0; i < 512; i++){
|
|
pte_t pte = pagetable[i];
|
|
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
|
|
// this PTE points to a lower-level page table.
|
|
uint64 child = PTE2PA(pte);
|
|
freewalk((pagetable_t)child);
|
|
pagetable[i] = 0;
|
|
} else if(pte & PTE_V){
|
|
panic("freewalk: leaf");
|
|
}
|
|
}
|
|
kfree((void*)pagetable);
|
|
}
|
|
|
|
// Free user memory pages,
|
|
// then free page-table pages.
|
|
void
|
|
uvmfree(pagetable_t pagetable, uint64 sz)
|
|
{
|
|
if(sz > 0)
|
|
vmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
|
|
freewalk(pagetable);
|
|
}
|
|
|
|
// Given a parent process's page table, copy
|
|
// its memory into a child's page table.
|
|
// Copies both the page table and the
|
|
// physical memory.
|
|
// returns 0 on success, -1 on failure.
|
|
// frees any allocated pages on failure.
|
|
int
|
|
uvmcopy(pagetable_t old, pagetable_t new, pagetable_t knew, uint64 sz)
|
|
{
|
|
pte_t *pte;
|
|
uint64 pa, i = 0, ki = 0;
|
|
uint flags;
|
|
char *mem;
|
|
|
|
while (i < sz){
|
|
if((pte = walk(old, i, 0)) == NULL)
|
|
panic("uvmcopy: pte should exist");
|
|
if((*pte & PTE_V) == 0)
|
|
panic("uvmcopy: page not present");
|
|
pa = PTE2PA(*pte);
|
|
flags = PTE_FLAGS(*pte);
|
|
if((mem = kalloc()) == NULL)
|
|
goto err;
|
|
memmove(mem, (char*)pa, PGSIZE);
|
|
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0) {
|
|
kfree(mem);
|
|
goto err;
|
|
}
|
|
i += PGSIZE;
|
|
if(mappages(knew, ki, PGSIZE, (uint64)mem, flags & ~PTE_U) != 0){
|
|
goto err;
|
|
}
|
|
ki += PGSIZE;
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
vmunmap(knew, 0, ki / PGSIZE, 0);
|
|
vmunmap(new, 0, i / PGSIZE, 1);
|
|
return -1;
|
|
}
|
|
|
|
// mark a PTE invalid for user access.
|
|
// used by exec for the user stack guard page.
|
|
void
|
|
uvmclear(pagetable_t pagetable, uint64 va)
|
|
{
|
|
pte_t *pte;
|
|
|
|
pte = walk(pagetable, va, 0);
|
|
if(pte == NULL)
|
|
panic("uvmclear");
|
|
*pte &= ~PTE_U;
|
|
}
|
|
|
|
// Copy from kernel to user.
|
|
// Copy len bytes from src to virtual address dstva in a given page table.
|
|
// Return 0 on success, -1 on error.
|
|
int
|
|
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
|
{
|
|
uint64 n, va0, pa0;
|
|
|
|
while(len > 0){
|
|
va0 = PGROUNDDOWN(dstva);
|
|
pa0 = walkaddr(pagetable, va0);
|
|
if(pa0 == NULL)
|
|
return -1;
|
|
n = PGSIZE - (dstva - va0);
|
|
if(n > len)
|
|
n = len;
|
|
memmove((void *)(pa0 + (dstva - va0)), src, n);
|
|
|
|
len -= n;
|
|
src += n;
|
|
dstva = va0 + PGSIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
copyout2(uint64 dstva, char *src, uint64 len)
|
|
{
|
|
uint64 sz = myproc()->sz;
|
|
if (dstva + len > sz || dstva >= sz) {
|
|
return -1;
|
|
}
|
|
memmove((void *)dstva, src, len);
|
|
return 0;
|
|
}
|
|
|
|
// Copy from user to kernel.
|
|
// Copy len bytes to dst from virtual address srcva in a given page table.
|
|
// Return 0 on success, -1 on error.
|
|
int
|
|
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
|
|
{
|
|
uint64 n, va0, pa0;
|
|
|
|
while(len > 0){
|
|
va0 = PGROUNDDOWN(srcva);
|
|
pa0 = walkaddr(pagetable, va0);
|
|
if(pa0 == NULL)
|
|
return -1;
|
|
n = PGSIZE - (srcva - va0);
|
|
if(n > len)
|
|
n = len;
|
|
memmove(dst, (void *)(pa0 + (srcva - va0)), n);
|
|
|
|
len -= n;
|
|
dst += n;
|
|
srcva = va0 + PGSIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
copyin2(char *dst, uint64 srcva, uint64 len)
|
|
{
|
|
uint64 sz = myproc()->sz;
|
|
if (srcva + len > sz || srcva >= sz) {
|
|
return -1;
|
|
}
|
|
memmove(dst, (void *)srcva, len);
|
|
return 0;
|
|
}
|
|
|
|
// Copy a null-terminated string from user to kernel.
|
|
// Copy bytes to dst from virtual address srcva in a given page table,
|
|
// until a '\0', or max.
|
|
// Return 0 on success, -1 on error.
|
|
int
|
|
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
|
{
|
|
uint64 n, va0, pa0;
|
|
int got_null = 0;
|
|
|
|
while(got_null == 0 && max > 0){
|
|
va0 = PGROUNDDOWN(srcva);
|
|
pa0 = walkaddr(pagetable, va0);
|
|
if(pa0 == NULL)
|
|
return -1;
|
|
n = PGSIZE - (srcva - va0);
|
|
if(n > max)
|
|
n = max;
|
|
|
|
char *p = (char *) (pa0 + (srcva - va0));
|
|
while(n > 0){
|
|
if(*p == '\0'){
|
|
*dst = '\0';
|
|
got_null = 1;
|
|
break;
|
|
} else {
|
|
*dst = *p;
|
|
}
|
|
--n;
|
|
--max;
|
|
p++;
|
|
dst++;
|
|
}
|
|
|
|
srcva = va0 + PGSIZE;
|
|
}
|
|
if(got_null){
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int
|
|
copyinstr2(char *dst, uint64 srcva, uint64 max)
|
|
{
|
|
int got_null = 0;
|
|
uint64 sz = myproc()->sz;
|
|
while(srcva < sz && max > 0){
|
|
char *p = (char *)srcva;
|
|
if(*p == '\0'){
|
|
*dst = '\0';
|
|
got_null = 1;
|
|
break;
|
|
} else {
|
|
*dst = *p;
|
|
}
|
|
--max;
|
|
srcva++;
|
|
dst++;
|
|
}
|
|
if(got_null){
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// initialize kernel pagetable for each process.
|
|
pagetable_t
|
|
proc_kpagetable()
|
|
{
|
|
pagetable_t kpt = (pagetable_t) kalloc();
|
|
if (kpt == NULL)
|
|
return NULL;
|
|
memmove(kpt, kernel_pagetable, PGSIZE);
|
|
|
|
// remap stack and trampoline, because they share the same page table of level 1 and 0
|
|
char *pstack = kalloc();
|
|
if(pstack == NULL)
|
|
goto fail;
|
|
if (mappages(kpt, VKSTACK, PGSIZE, (uint64)pstack, PTE_R | PTE_W) != 0)
|
|
goto fail;
|
|
|
|
return kpt;
|
|
|
|
fail:
|
|
kvmfree(kpt, 1);
|
|
return NULL;
|
|
}
|
|
|
|
// only free page table, not physical pages
|
|
void
|
|
kfreewalk(pagetable_t kpt)
|
|
{
|
|
for (int i = 0; i < 512; i++) {
|
|
pte_t pte = kpt[i];
|
|
if ((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0) {
|
|
kfreewalk((pagetable_t) PTE2PA(pte));
|
|
kpt[i] = 0;
|
|
} else if (pte & PTE_V) {
|
|
break;
|
|
}
|
|
}
|
|
kfree((void *) kpt);
|
|
}
|
|
|
|
void
|
|
kvmfreeusr(pagetable_t kpt)
|
|
{
|
|
pte_t pte;
|
|
for (int i = 0; i < PX(2, MAXUVA); i++) {
|
|
pte = kpt[i];
|
|
if ((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0) {
|
|
kfreewalk((pagetable_t) PTE2PA(pte));
|
|
kpt[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
kvmfree(pagetable_t kpt, int stack_free)
|
|
{
|
|
if (stack_free) {
|
|
vmunmap(kpt, VKSTACK, 1, 1);
|
|
pte_t pte = kpt[PX(2, VKSTACK)];
|
|
if ((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0) {
|
|
kfreewalk((pagetable_t) PTE2PA(pte));
|
|
}
|
|
}
|
|
kvmfreeusr(kpt);
|
|
kfree(kpt);
|
|
}
|
|
|
|
void vmprint(pagetable_t pagetable)
|
|
{
|
|
const int capacity = 512;
|
|
printf("page table %p\n", pagetable);
|
|
for (pte_t *pte = (pte_t *) pagetable; pte < pagetable + capacity; pte++) {
|
|
if (*pte & PTE_V)
|
|
{
|
|
pagetable_t pt2 = (pagetable_t) PTE2PA(*pte);
|
|
printf("..%d: pte %p pa %p\n", pte - pagetable, *pte, pt2);
|
|
|
|
for (pte_t *pte2 = (pte_t *) pt2; pte2 < pt2 + capacity; pte2++) {
|
|
if (*pte2 & PTE_V)
|
|
{
|
|
pagetable_t pt3 = (pagetable_t) PTE2PA(*pte2);
|
|
printf(".. ..%d: pte %p pa %p\n", pte2 - pt2, *pte2, pt3);
|
|
|
|
for (pte_t *pte3 = (pte_t *) pt3; pte3 < pt3 + capacity; pte3++)
|
|
if (*pte3 & PTE_V)
|
|
printf(".. .. ..%d: pte %p pa %p\n", pte3 - pt3, *pte3, PTE2PA(*pte3));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
} |