#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.117 2022/03/20 18:56:29 andvar Exp $");
#include "opt_memsize.h"
#include "opt_kmempages.h"
#include "opt_misc.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mutex.h>
#include <sys/buf.h>
#include <sys/kmem.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <machine/thunk.h>
#include <machine/machdep.h>
#include <machine/pcb.h>
#include <uvm/uvm.h>
struct pv_entry {
struct pv_entry *pv_next;
pmap_t pv_pmap;
uintptr_t pv_ppn;
uintptr_t pv_lpn;
vm_prot_t pv_prot;
uint8_t pv_mmap_ppl;
uint8_t pv_vflags;
#define PV_WIRED 0x01
#define PV_UNMANAGED 0x02
uint8_t pv_pflags;
#define PV_REFERENCED 0x01
#define PV_MODIFIED 0x02
};
#define PMAP_L2_SIZE PAGE_SIZE
#define PMAP_L2_NENTRY (PMAP_L2_SIZE / sizeof(struct pv_entry *))
struct pmap_l2 {
struct pv_entry *pm_l2[PMAP_L2_NENTRY];
};
struct pmap {
int pm_count;
int pm_flags;
#define PM_ACTIVE 0x01
struct pmap_statistics pm_stats;
struct pmap_l2 **pm_l1;
};
static struct pv_entry **kernel_pm_entries;
static struct pv_entry *pv_table;
static struct pv_entry **tlb;
static struct pmap pmap_kernel_store;
struct pmap * const kernel_pmap_ptr = &pmap_kernel_store;
static pmap_t active_pmap = NULL;
static char mem_name[20] = "";
static int mem_fh;
static int phys_npages = 0;
static int pm_nentries = 0;
static int pm_nl1 = 0;
static int pm_l1_size = 0;
static uint64_t pm_entries_size = 0;
static void *pm_tmp_p0;
static void *pm_tmp_p1;
static struct pool pmap_pool;
static struct pool pmap_pventry_pool;
void pmap_bootstrap(void);
static void pmap_page_activate(struct pv_entry *pv);
static void pmap_page_deactivate(struct pv_entry *pv);
static void pv_update(struct pv_entry *pv);
static void pmap_update_page(uintptr_t ppn);
bool pmap_fault(pmap_t pmap, vaddr_t va, vm_prot_t *atype);
static struct pv_entry *pv_get(pmap_t pmap, uintptr_t ppn, uintptr_t lpn);
static struct pv_entry *pv_alloc(void);
static void pv_free(struct pv_entry *pv);
static void pmap_deferred_init(void);
extern void setup_signal_handlers(void);
vaddr_t kmem_k_start, kmem_k_end;
vaddr_t kmem_kvm_start, kmem_kvm_end;
vaddr_t kmem_user_start, kmem_user_end;
vaddr_t kmem_kvm_cur_start, kmem_kvm_cur_end;
int num_pv_entries = 0;
int num_pmaps = 0;
#define SPARSE_MEMFILE
void
pmap_bootstrap(void)
{
struct pmap *pmap;
paddr_t DRAM_cfg;
paddr_t fpos, file_len;
paddr_t kernel_fpos, pv_fpos, tlb_fpos, pm_l1_fpos, pm_fpos;
paddr_t wlen;
paddr_t barrier_len;
paddr_t pv_table_size;
vaddr_t free_start, free_end;
paddr_t pa;
vaddr_t va;
size_t kmem_k_length, written;
uintptr_t pg, l1;
void *addr;
int err;
extern void _start(void);
extern int etext;
extern int edata;
extern int end;
vaddr_t vm_min_addr;
vm_min_addr = thunk_get_vm_min_address();
vm_min_addr = vm_min_addr < PAGE_SIZE ? PAGE_SIZE : vm_min_addr;
thunk_printf_debug("Information retrieved from system and elf image\n");
thunk_printf_debug("min VM address at %p\n", (void *) vm_min_addr);
thunk_printf_debug("start kernel at %p\n", _start);
thunk_printf_debug(" end kernel at %p\n", &etext);
thunk_printf_debug(" end of init. data at %p\n", &edata);
thunk_printf_debug("1st end of data at %p\n", &end);
thunk_printf_debug("CUR end data at %p\n", thunk_sbrk(0));
barrier_len = 2 * 1024 * 1024;
kmem_k_start = (vaddr_t) PAGE_SIZE * (atop(_start) );
kmem_k_end = (vaddr_t) PAGE_SIZE * (atop(&etext) + 1);
kmem_k_length = kmem_k_end - kmem_k_start;
DRAM_cfg = (vaddr_t) TEXTADDR;
physmem = DRAM_cfg / PAGE_SIZE;
kmem_kvm_end = kmem_k_start - barrier_len;
kmem_kvm_start = kmem_kvm_end - KVMSIZE;
pm_tmp_p0 = (void *) (kmem_kvm_start);
pm_tmp_p1 = (void *) (kmem_kvm_start + PAGE_SIZE);
kmem_kvm_start += 2*PAGE_SIZE;
kmem_user_start = vm_min_addr;
kmem_user_end = kmem_kvm_start - barrier_len;
aprint_verbose("\nMemory summary\n");
aprint_verbose("\tkmem_user_start\t%p\n", (void *) kmem_user_start);
aprint_verbose("\tkmem_user_end\t%p\n", (void *) kmem_user_end);
aprint_verbose("\tkmem_k_start\t%p\n", (void *) kmem_k_start);
aprint_verbose("\tkmem_k_end\t%p\n", (void *) kmem_k_end);
aprint_verbose("\tkmem_kvm_start\t%p\n", (void *) kmem_kvm_start);
aprint_verbose("\tkmem_kvm_end\t%p\n", (void *) kmem_kvm_end);
aprint_verbose("\tDRAM_cfg\t%10d\n", (int) DRAM_cfg);
aprint_verbose("\tkvmsize\t\t%10d\n", (int) KVMSIZE);
aprint_verbose("\tuser_len\t%10d\n",
(int) (kmem_user_end - kmem_user_start));
aprint_verbose("\n\n");
if (sizeof(struct pcb) > USPACE) {
panic("sizeof(struct pcb) is %d bytes too big for USPACE. "
"Please adjust TRAPSTACKSIZE calculation",
(int) (USPACE - sizeof(struct pcb)));
}
if (TRAPSTACKSIZE < 4*PAGE_SIZE) {
panic("TRAPSTACKSIZE is too small, please increase UPAGES");
}
if (sizeof(struct pmap_l2) > PAGE_SIZE) {
panic("struct pmap_l2 bigger than one page?\n");
}
err = thunk_munmap((void *) kmem_user_start,
kmem_k_start - kmem_user_start);
if (err)
panic("pmap_bootstrap: userland uvm space protection "
"failed (%d)\n", thunk_geterrno());
#if 0
err = thunk_munmap((void *) kmem_kvm_start,
kmem_kvm_end - kmem_kvm_start);
if (err)
panic("pmap_bootstrap: kvm uvm space protection "
"failed (%d)\n", thunk_geterrno());
#endif
thunk_printf_debug("Creating memory mapped backend\n");
strlcpy(mem_name, "/tmp/netbsd.XXXXXX", sizeof(mem_name));
mem_fh = thunk_mkstemp(mem_name);
if (mem_fh < 0)
panic("pmap_bootstrap: can't create memory file\n");
if (thunk_unlink(mem_name) == -1)
panic("pmap_bootstrap: can't unlink %s", mem_name);
file_len = DRAM_cfg;
#ifdef SPARSE_MEMFILE
{
char dummy;
wlen = thunk_pwrite(mem_fh, &dummy, 1, file_len - 1);
if (wlen != 1)
panic("pmap_bootstrap: can't grow file\n");
}
#else
{
char block[PAGE_SIZE];
printf("Creating memory file\r");
for (pg = 0; pg < file_len; pg += PAGE_SIZE) {
wlen = thunk_pwrite(mem_fh, block, PAGE_SIZE, pg);
if (wlen != PAGE_SIZE)
panic("pmap_bootstrap: write fails, disc full?");
}
}
#endif
err = thunk_mprotect((void *) kmem_k_start, kmem_k_length,
THUNK_PROT_READ | THUNK_PROT_EXEC);
assert(err == 0);
err = thunk_madvise((void *) kmem_user_start,
kmem_k_start - kmem_user_start,
THUNK_MADV_WILLNEED | THUNK_MADV_RANDOM);
assert(err == 0);
kernel_fpos = 0;
written = thunk_pwrite(mem_fh, (void *) kmem_k_start, kmem_k_length,
kernel_fpos);
assert(written == kmem_k_length);
fpos = kernel_fpos + kmem_k_length;
free_start = fpos;
free_end = file_len;
kmem_kvm_cur_start = kmem_kvm_start;
phys_npages = file_len / PAGE_SIZE;
pv_table_size = round_page(phys_npages * sizeof(struct pv_entry));
thunk_printf_debug("claiming %"PRIu64" KB of pv_table for "
"%"PRIdPTR" pages of physical memory\n",
(uint64_t) pv_table_size/1024, (uintptr_t) phys_npages);
pm_nentries = (kmem_k_end - VM_MIN_ADDRESS) / PAGE_SIZE;
pm_entries_size = round_page(pm_nentries * sizeof(struct pv_entry *));
thunk_printf_debug("tlb va->pa lookup table is %"PRIu64" KB for "
"%d logical pages\n", pm_entries_size/1024, pm_nentries);
pm_nl1 = pm_nentries / PMAP_L2_NENTRY;
pm_l1_size = round_page(pm_nl1 * sizeof(struct pmap_l1 *));
pv_fpos = fpos;
pv_table = (struct pv_entry *) kmem_kvm_cur_start;
addr = thunk_mmap(pv_table, pv_table_size,
THUNK_PROT_READ | THUNK_PROT_WRITE,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, pv_fpos);
if (addr != (void *) pv_table)
panic("pmap_bootstrap: can't map in pv table\n");
memset(pv_table, 0, pv_table_size);
thunk_printf_debug("pv_table initialised correctly, mmap works\n");
kmem_kvm_cur_start += pv_table_size;
fpos += pv_table_size;
tlb = (struct pv_entry **) kmem_kvm_cur_start;
tlb_fpos = fpos;
addr = thunk_mmap(tlb, pm_entries_size,
THUNK_PROT_READ | THUNK_PROT_WRITE,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, tlb_fpos);
if (addr != (void *) tlb)
panic("pmap_bootstrap: can't map in tlb entries\n");
memset(tlb, 0, pm_entries_size);
thunk_printf_debug("kernel tlb entries initialized correctly\n");
kmem_kvm_cur_start += pm_entries_size;
fpos += pm_entries_size;
pmap = pmap_kernel();
memset(pmap, 0, sizeof(*pmap));
pmap->pm_count = 1;
pmap->pm_flags = PM_ACTIVE;
pmap->pm_l1 = (struct pmap_l2 **) kmem_kvm_cur_start;
pm_l1_fpos = fpos;
addr = thunk_mmap(pmap->pm_l1, pm_l1_size,
THUNK_PROT_READ | THUNK_PROT_WRITE,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, pm_l1_fpos);
if (addr != (void *) pmap->pm_l1)
panic("pmap_bootstrap: can't map in pmap l1 entries\n");
memset(pmap->pm_l1, 0, pm_l1_size);
thunk_printf_debug("kernel pmap l1 table initialised correctly\n");
kmem_kvm_cur_start += round_page(pm_l1_size);
fpos += round_page(pm_l1_size);
pm_fpos = fpos;
kernel_pm_entries = (struct pv_entry **) kmem_kvm_cur_start;
addr = thunk_mmap(kernel_pm_entries, pm_entries_size,
THUNK_PROT_READ | THUNK_PROT_WRITE,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, pm_fpos);
if (addr != (void *) kernel_pm_entries)
panic("pmap_bootstrap: can't map in kernel pmap entries\n");
memset(kernel_pm_entries, 0, pm_entries_size);
kmem_kvm_cur_start += pm_entries_size;
fpos += pm_entries_size;
for (l1 = 0; l1 < pm_nl1; l1++) {
pmap = pmap_kernel();
pmap->pm_l1[l1] = (struct pmap_l2 *)
((vaddr_t) kernel_pm_entries + l1 * PMAP_L2_SIZE);
}
kmem_kvm_cur_end = kmem_kvm_cur_start;
for (pg = 0; pg < pv_table_size; pg += PAGE_SIZE) {
pa = pv_fpos + pg;
va = (vaddr_t) pv_table + pg;
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
}
thunk_printf_debug("pv_table mem added to the kernel pmap\n");
for (pg = 0; pg < pm_entries_size; pg += PAGE_SIZE) {
pa = tlb_fpos + pg;
va = (vaddr_t) tlb + pg;
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
}
thunk_printf_debug("kernel tlb entries mem added to the kernel pmap\n");
for (pg = 0; pg < pm_l1_size; pg += PAGE_SIZE) {
pa = pm_l1_fpos + pg;
va = (vaddr_t) pmap->pm_l1 + pg;
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
}
thunk_printf_debug("kernel pmap l1 mem added to the kernel pmap\n");
for (pg = 0; pg < pm_entries_size; pg += PAGE_SIZE) {
pa = pm_fpos + pg;
va = (vaddr_t) kernel_pm_entries + pg;
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
}
thunk_printf_debug("kernel pmap entries mem added to the kernel pmap\n");
#if 0
for (pg = 0; pg < kmem_k_length; pg += PAGE_SIZE) {
pa = kernel_fpos + pg;
va = (vaddr_t) kmem_k_start + pg;
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, 0);
}
thunk_printf_debug("kernel mem added to the kernel pmap\n");
#endif
uvm_page_physload(atop(0),
atop(free_end),
atop(free_start + fpos),
atop(free_end),
VM_FREELIST_DEFAULT);
if (thunk_syscallemu_init((void *)VM_MIN_ADDRESS,
(void *)VM_MAXUSER_ADDRESS) != 0)
panic("couldn't enable syscall emulation");
aprint_verbose("leaving pmap_bootstrap:\n");
aprint_verbose("\t%"PRIu64" MB of physical pages left\n",
(uint64_t) (free_end - (free_start + fpos))/1024/1024);
aprint_verbose("\t%"PRIu64" MB of kmem left\n",
(uint64_t) (kmem_kvm_end - kmem_kvm_cur_end)/1024/1024);
setup_signal_handlers();
}
void
pmap_init(void)
{
}
void
pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
if (vstartp)
*vstartp = kmem_kvm_cur_start;
if (vendp)
*vendp = kmem_kvm_end - PAGE_SIZE;
}
static void
pmap_deferred_init(void)
{
pool_init(&pmap_pool, sizeof(struct pmap), 0, 0, 0,
"pmappool", NULL, IPL_NONE);
pool_init(&pmap_pventry_pool, sizeof(struct pv_entry), 0, 0, 0,
"pventry", NULL, IPL_HIGH);
}
pmap_t
pmap_create(void)
{
static int pmap_initialised = 0;
struct pmap *pmap;
if (!pmap_initialised) {
pmap_deferred_init();
pmap_initialised = 1;
}
thunk_printf_debug("pmap_create\n");
num_pmaps++;
#if 0
printf("%s: pre alloc: num_pmaps %"PRIu64" (%"PRIu64" kb), "
"num_pv_entries %"PRIu64" (%"PRIu64" kb)\n",
__func__,
(uint64_t) num_pmaps,
(uint64_t) num_pmaps * (sizeof(*pmap) + pm_l1_size) / 1024,
(uint64_t) num_pv_entries,
(uint64_t) num_pv_entries * (sizeof(struct pv_entry)) / 1024);
#endif
pmap = pool_get(&pmap_pool, PR_WAITOK);
memset(pmap, 0, sizeof(*pmap));
pmap->pm_count = 1;
pmap->pm_flags = 0;
pmap->pm_l1 = kmem_zalloc(pm_l1_size, KM_SLEEP);
assert(pmap->pm_l1);
thunk_printf_debug("\tpmap %p\n", pmap);
return pmap;
}
void
pmap_destroy(pmap_t pmap)
{
struct pmap_l2 *l2tbl;
int l1;
thunk_printf_debug("pmap_destroy %p\n", pmap);
if (--pmap->pm_count > 0)
return;
num_pmaps--;
KASSERT((pmap->pm_flags & PM_ACTIVE) == 0);
KASSERT(pmap->pm_stats.resident_count == 0);
KASSERT(pmap->pm_stats.wired_count == 0);
#ifdef DIAGNOSTIC
for (l1 = 0; l1 < pm_nl1; l1++) {
int l2;
l2tbl = pmap->pm_l1[l1];
if (!l2tbl)
continue;
for (l2 = 0; l2 < PMAP_L2_NENTRY; l2++) {
if (l2tbl->pm_l2[l2])
panic("pmap_destroy: pmap isn't empty");
}
}
#endif
for (l1 = 0; l1 < pm_nl1; l1++) {
l2tbl = pmap->pm_l1[l1];
if (!l2tbl)
continue;
kmem_free(l2tbl, PMAP_L2_SIZE);
}
kmem_free(pmap->pm_l1, pm_l1_size);
pool_put(&pmap_pool, pmap);
}
void
pmap_reference(pmap_t pmap)
{
thunk_printf_debug("pmap_reference %p\n", (void *) pmap);
pmap->pm_count++;
}
long
pmap_resident_count(pmap_t pmap)
{
return pmap->pm_stats.resident_count;
}
long
pmap_wired_count(pmap_t pmap)
{
return pmap->pm_stats.wired_count;
}
static struct pv_entry *
pv_alloc(void)
{
struct pv_entry *pv;
num_pv_entries++;
pv = pool_get(&pmap_pventry_pool, PR_WAITOK);
memset(pv, 0, sizeof(struct pv_entry));
return pv;
}
static void
pv_free(struct pv_entry *pv)
{
num_pv_entries--;
pool_put(&pmap_pventry_pool, pv);
}
static struct pv_entry *
pv_get(pmap_t pmap, uintptr_t ppn, uintptr_t lpn)
{
struct pv_entry *pv;
pv = &pv_table[ppn];
if (pv->pv_pmap == NULL) {
pmap->pm_stats.resident_count++;
return pv;
}
for (pv = pv; pv != NULL; pv = pv->pv_next) {
if ((pv->pv_pmap == pmap) && (pv->pv_lpn == lpn)) {
return pv;
}
}
thunk_printf_debug("pv_get: multiple mapped page ppn %"PRIdPTR", "
"lpn %"PRIdPTR"\n", ppn, lpn);
assert(ppn < phys_npages);
pv = pv_alloc();
if (pv == NULL)
return NULL;
pv->pv_next = pv_table[ppn].pv_next;
pv_table[ppn].pv_next = pv;
pmap->pm_stats.resident_count++;
return pv;
}
static void
pmap_set_pv(pmap_t pmap, uintptr_t lpn, struct pv_entry *pv)
{
struct pmap_l2 *l2tbl;
int l1 = lpn / PMAP_L2_NENTRY;
int l2 = lpn % PMAP_L2_NENTRY;
#ifdef DIAGNOSTIC
if (lpn >= pm_nentries)
panic("peeing outside box : addr in page around %"PRIx64"\n",
(uint64_t) lpn*PAGE_SIZE);
#endif
l2tbl = pmap->pm_l1[l1];
if (!l2tbl) {
l2tbl = pmap->pm_l1[l1] = kmem_zalloc(PMAP_L2_SIZE, KM_SLEEP);
}
l2tbl->pm_l2[l2] = pv;
}
static struct pv_entry *
pmap_lookup_pv(pmap_t pmap, uintptr_t lpn)
{
struct pmap_l2 *l2tbl;
int l1 = lpn / PMAP_L2_NENTRY;
int l2 = lpn % PMAP_L2_NENTRY;
if (lpn >= pm_nentries)
return NULL;
l2tbl = pmap->pm_l1[l1];
if (l2tbl)
return l2tbl->pm_l2[l2];
return NULL;
}
bool
pmap_fault(pmap_t pmap, vaddr_t va, vm_prot_t *atype)
{
struct pv_entry *pv, *ppv;
uintptr_t lpn, ppn;
int prot, cur_prot, diff;
thunk_printf_debug("pmap_fault pmap %p, va %p\n", pmap, (void *) va);
lpn = atop(va - VM_MIN_ADDRESS);
pv = pmap_lookup_pv(pmap, lpn);
if (pv == NULL) {
*atype = VM_PROT_READ;
return false;
}
ppn = pv->pv_ppn;
ppv = &pv_table[ppn];
if (ppv->pv_vflags & PV_UNMANAGED) {
printf("%s: oops warning unmanaged page %"PRIiPTR" faulted\n",
__func__, ppn);
pmap_page_activate(pv);
return true;
}
if (tlb[pv->pv_lpn] == NULL) {
if (pv->pv_mmap_ppl != THUNK_PROT_NONE) {
thunk_printf_debug("%s: tlb fault page lpn %"PRIiPTR"\n",
__func__, pv->pv_lpn);
pmap_page_activate(pv);
return true;
}
}
prot = pv->pv_prot;
cur_prot = VM_PROT_NONE;
if (pv->pv_mmap_ppl & THUNK_PROT_READ)
cur_prot |= VM_PROT_READ;
if (pv->pv_mmap_ppl & THUNK_PROT_WRITE)
cur_prot |= VM_PROT_WRITE;
if (pv->pv_mmap_ppl & THUNK_PROT_EXEC)
cur_prot |= VM_PROT_EXECUTE;
diff = prot & (prot ^ cur_prot);
thunk_printf_debug("%s: prot = %d, cur_prot = %d, diff = %d\n",
__func__, prot, cur_prot, diff);
*atype = VM_PROT_READ;
if (diff & VM_PROT_READ) {
if ((ppv->pv_pflags & PV_REFERENCED) == 0) {
ppv->pv_pflags |= PV_REFERENCED;
pmap_update_page(ppn);
return true;
}
panic("pmap: page not readable but marked referenced?");
return false;
}
#if 0
if (diff & VM_PROT_EXECUTE) {
*atype = VM_PROT_EXECUTE;
if (prot & VM_PROT_EXECUTE) {
if ((ppv->pv_pflags & PV_REFERENCED) == 0) {
ppv->pv_pflags |= PV_REFERENCED;
pmap_update_page(ppn);
return true;
}
}
return false;
}
#endif
*atype = VM_PROT_WRITE;
if (diff & VM_PROT_WRITE) {
if (prot & VM_PROT_WRITE) {
if ((ppv->pv_pflags & PV_MODIFIED) == 0) {
ppv->pv_pflags |= PV_MODIFIED;
pmap_update_page(ppn);
return true;
}
}
panic("pmap: page not writable but marked modified?");
return false;
}
return false;
}
static void
pmap_page_activate(struct pv_entry *pv)
{
paddr_t pa = pv->pv_ppn * PAGE_SIZE;
vaddr_t va = pv->pv_lpn * PAGE_SIZE + VM_MIN_ADDRESS;
uint32_t map_flags;
void *addr;
map_flags = THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED;
addr = thunk_mmap((void *) va, PAGE_SIZE, pv->pv_mmap_ppl,
map_flags, mem_fh, pa);
thunk_printf_debug("page_activate: (va %p, pa %p, prot %d, ppl %d) -> %p\n",
(void *) va, (void *) pa, pv->pv_prot, pv->pv_mmap_ppl,
(void *) addr);
if (addr != (void *) va)
panic("pmap_page_activate: mmap failed (expected %p got %p): %d",
(void *)va, addr, thunk_geterrno());
tlb[pv->pv_lpn] = NULL;
if (pv->pv_mmap_ppl != THUNK_PROT_NONE)
tlb[pv->pv_lpn] = pv;
}
static void
pmap_page_deactivate(struct pv_entry *pv)
{
paddr_t pa = pv->pv_ppn * PAGE_SIZE;
vaddr_t va = pv->pv_lpn * PAGE_SIZE + VM_MIN_ADDRESS;
uint32_t map_flags;
void *addr;
if (!tlb[pv->pv_lpn])
return;
if (tlb[pv->pv_lpn]->pv_mmap_ppl == THUNK_PROT_NONE)
goto deactivate;
map_flags = THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED;
addr = thunk_mmap((void *) va, PAGE_SIZE, THUNK_PROT_NONE,
map_flags, mem_fh, pa);
thunk_printf_debug("page_deactivate: (va %p, pa %p, ppl %d) -> %p\n",
(void *) va, (void *) pa, pv->pv_mmap_ppl, (void *) addr);
if (addr != (void *) va)
panic("pmap_page_deactivate: mmap failed");
deactivate:
tlb[pv->pv_lpn] = NULL;
}
static void
pv_update(struct pv_entry *pv)
{
int pflags, vflags;
int mmap_ppl;
pflags = pv_table[pv->pv_ppn].pv_pflags;
vflags = pv_table[pv->pv_ppn].pv_vflags;
KASSERT(THUNK_PROT_READ == VM_PROT_READ);
KASSERT(THUNK_PROT_WRITE == VM_PROT_WRITE);
KASSERT(THUNK_PROT_EXEC == VM_PROT_EXECUTE);
if ((pv->pv_prot & VM_PROT_WRITE) &&
(pflags & PV_REFERENCED) && (pflags & PV_MODIFIED)) {
mmap_ppl = THUNK_PROT_READ | THUNK_PROT_WRITE;
} else if ((pv->pv_prot & (VM_PROT_READ | VM_PROT_EXECUTE)) &&
(pflags & PV_REFERENCED)) {
mmap_ppl = THUNK_PROT_READ;
if (pv->pv_prot & VM_PROT_EXECUTE)
mmap_ppl |= THUNK_PROT_EXEC;
} else {
mmap_ppl = THUNK_PROT_NONE;
}
if (vflags & (PV_UNMANAGED | PV_WIRED))
mmap_ppl = THUNK_PROT_READ | THUNK_PROT_WRITE;
pv->pv_mmap_ppl = mmap_ppl;
}
static void
pmap_update_page(uintptr_t ppn)
{
struct pv_entry *pv;
for (pv = &pv_table[ppn]; pv != NULL; pv = pv->pv_next) {
thunk_printf_debug("pmap_update_page: ppn %"PRIdPTR", pv->pv_map = %p\n",
ppn, pv->pv_pmap);
if (pv->pv_pmap != NULL) {
pv_update(pv);
if (pv->pv_pmap->pm_flags & PM_ACTIVE)
pmap_page_activate(pv);
else
pmap_page_deactivate(pv)
;
}
}
}
static int
pmap_do_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, uint flags, int unmanaged)
{
struct pv_entry *pv, *ppv;
uintptr_t ppn, lpn;
int s;
ppn = atop(pa);
lpn = atop(va - VM_MIN_ADDRESS);
#ifdef DIAGNOSTIC
if ((va < VM_MIN_ADDRESS) || (va > VM_MAX_KERNEL_ADDRESS))
panic("pmap_do_enter: invalid va issued\n");
#endif
s = splvm();
pv = pmap_lookup_pv(pmap, lpn);
if (pv && pv->pv_ppn != ppn)
pmap_remove(pmap, va, va + PAGE_SIZE);
ppv = &pv_table[ppn];
pv = pv_get(pmap, ppn, lpn);
if (pv == NULL)
panic("pamp_do_enter: didn't find pv entry!");
if (pv->pv_vflags & PV_WIRED)
pmap->pm_stats.wired_count--;
pv->pv_pmap = pmap;
pv->pv_ppn = ppn;
pv->pv_lpn = lpn;
pv->pv_prot = prot;
pv->pv_vflags = 0;
if (flags & PMAP_WIRED)
pv->pv_vflags |= PV_WIRED;
if (unmanaged) {
pv->pv_vflags |= PV_UNMANAGED;
} else {
if (flags & VM_PROT_WRITE)
ppv->pv_pflags |= PV_REFERENCED | PV_MODIFIED;
else if (flags & (VM_PROT_ALL))
ppv->pv_pflags |= PV_REFERENCED;
}
pmap_update_page(ppn);
pmap_set_pv(pmap, lpn, pv);
if (pv->pv_vflags & PV_WIRED)
pmap->pm_stats.wired_count++;
splx(s);
if (pmap->pm_flags & PM_ACTIVE)
pmap_page_activate(pv);
return 0;
}
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
thunk_printf_debug("pmap_enter %p : v %p, p %p, prot %d, flags %d\n",
(void *) pmap, (void *) va, (void *) pa, (int) prot, (int) flags);
return pmap_do_enter(pmap, va, pa, prot, flags, 0);
}
static void
pv_release(pmap_t pmap, uintptr_t ppn, uintptr_t lpn)
{
struct pv_entry *pv, *npv;
thunk_printf_debug("pv_release ppn %"PRIdPTR", lpn %"PRIdPTR"\n", ppn, lpn);
pv = &pv_table[ppn];
if ((pmap == pv->pv_pmap) && (lpn == pv->pv_lpn)) {
npv = pv->pv_next;
if (npv) {
memcpy(pv, npv, offsetof(struct pv_entry, pv_pflags));
pmap_set_pv(pv->pv_pmap, pv->pv_lpn, pv);
pv_free(npv);
} else {
memset(pv, 0, offsetof(struct pv_entry, pv_pflags));
}
} else {
for (npv = pv->pv_next; npv; npv = npv->pv_next) {
if ((pmap == npv->pv_pmap) && (lpn == npv->pv_lpn))
break;
pv = npv;
}
KASSERT(npv != NULL);
pv->pv_next = npv->pv_next;
pv_free(npv);
}
pmap_set_pv(pmap, lpn, NULL);
pmap->pm_stats.resident_count--;
}
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
uintptr_t slpn, elpn, lpn;
struct pv_entry *pv;
int s;
slpn = atop(sva - VM_MIN_ADDRESS);
elpn = atop(eva - VM_MIN_ADDRESS);
thunk_printf_debug("pmap_remove() called from "
"lpn %"PRIdPTR" to lpn %"PRIdPTR"\n", slpn, elpn);
s = splvm();
for (lpn = slpn; lpn < elpn; lpn++) {
pv = pmap_lookup_pv(pmap, lpn);
if (pv != NULL) {
if (pmap->pm_flags & PM_ACTIVE) {
pmap_page_deactivate(pv);
}
pmap_set_pv(pmap, lpn, NULL);
if (pv->pv_vflags & PV_WIRED)
pmap->pm_stats.wired_count--;
pv_release(pmap, pv->pv_ppn, lpn);
}
}
splx(s);
}
bool
pmap_remove_all(pmap_t pmap)
{
thunk_printf_debug("pmap_remove_all() dummy called\n");
if (pmap == pmap_kernel())
return false;
#if 0
pmap_remove(pmap, VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS);
thunk_munmap((void *) VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS);
#endif
#if 0
thunk_msync(VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS,
THUNK_MS_SYNC | THUNK_MS_INVALIDATE);
#endif
return false;
}
void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
struct pv_entry *pv;
intptr_t slpn, elpn, lpn;
int s;
if (prot == VM_PROT_NONE) {
pmap_remove(pmap, sva, eva);
return;
}
if (prot & VM_PROT_WRITE)
return;
if (pmap == pmap_kernel())
return;
slpn = atop(sva - VM_MIN_ADDRESS);
elpn = atop(eva - VM_MIN_ADDRESS);
thunk_printf_debug("pmap_protect() called from "
"lpn %"PRIdPTR" to lpn %"PRIdPTR"\n", slpn, elpn);
s = splvm();
for (lpn = slpn; lpn < elpn; lpn++) {
pv = pmap_lookup_pv(pmap, lpn);
if (pv != NULL) {
pv->pv_prot &= prot;
pv_update(pv);
if (pv->pv_pmap->pm_flags & PM_ACTIVE)
pmap_page_activate(pv);
}
}
splx(s);
}
void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
struct pv_entry *pv;
intptr_t lpn;
thunk_printf_debug("pmap_unwire called va = %p\n", (void *) va);
if (pmap == NULL)
return;
lpn = atop(va - VM_MIN_ADDRESS);
pv = pmap_lookup_pv(pmap, lpn);
if (pv == NULL)
return;
if ((pv->pv_vflags & PV_WIRED) == 0)
return;
pmap->pm_stats.wired_count--;
pv->pv_vflags &= ~PV_WIRED;
pmap_update_page(pv->pv_ppn);
}
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *ppa)
{
struct pv_entry *pv;
intptr_t lpn;
thunk_printf_debug("pmap_extract: extracting va %p\n", (void *) va);
#ifdef DIAGNOSTIC
if ((va < VM_MIN_ADDRESS) || (va > VM_MAX_KERNEL_ADDRESS)) {
thunk_printf_debug("pmap_extract: invalid va issued\n");
thunk_printf("%p not in [%p, %p]\n", (void *) va,
(void *) VM_MIN_ADDRESS, (void *) VM_MAX_KERNEL_ADDRESS);
return false;
}
#endif
lpn = atop(va - VM_MIN_ADDRESS);
pv = pmap_lookup_pv(pmap, lpn);
if (pv == NULL)
return false;
if (ppa)
*ppa = ptoa(pv->pv_ppn);
return true;
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
thunk_printf_debug("pmap_kenter_pa : v %p, p %p, prot %d, flags %d\n",
(void *) va, (void *) pa, (int) prot, (int) flags);
pmap_do_enter(pmap_kernel(), va, pa, prot, prot | PMAP_WIRED, 1);
}
void
pmap_kremove(vaddr_t va, vsize_t size)
{
pmap_remove(pmap_kernel(), va, va + size);
}
void
pmap_copy(pmap_t dst_map, pmap_t src_map, vaddr_t dst_addr, vsize_t len,
vaddr_t src_addr)
{
thunk_printf_debug("pmap_copy (dummy)\n");
}
void
pmap_update(pmap_t pmap)
{
thunk_printf_debug("pmap_update (dummy)\n");
}
void
pmap_activate(struct lwp *l)
{
struct proc *p = l->l_proc;
pmap_t pmap;
pmap = p->p_vmspace->vm_map.pmap;
thunk_printf_debug("pmap_activate for lwp %p, pmap = %p\n", l, pmap);
if (pmap == pmap_kernel())
return;
KASSERT(active_pmap == NULL);
KASSERT((pmap->pm_flags & PM_ACTIVE) == 0);
active_pmap = pmap;
pmap->pm_flags |= PM_ACTIVE;
}
void
pmap_deactivate(struct lwp *l)
{
struct proc *p = l->l_proc;
struct pv_entry *pv;
struct pmap_l2 *l2tbl;
pmap_t pmap;
int l1, l2;
pmap = p->p_vmspace->vm_map.pmap;
thunk_printf_debug("pmap_DEactivate for lwp %p, pmap = %p\n", l, pmap);
if (pmap == pmap_kernel())
return;
KASSERT(pmap == active_pmap);
KASSERT(pmap->pm_flags & PM_ACTIVE);
active_pmap = NULL;
pmap->pm_flags &=~ PM_ACTIVE;
for (l1 = 0; l1 < pm_nl1; l1++) {
l2tbl = pmap->pm_l1[l1];
if (!l2tbl)
continue;
for (l2 = 0; l2 < PMAP_L2_NENTRY; l2++) {
pv = l2tbl->pm_l2[l2];
if (pv) {
pmap_page_deactivate(pv);
}
}
}
}
void
pmap_zero_page(paddr_t pa)
{
char *blob;
thunk_printf_debug("pmap_zero_page: pa %p\n", (void *) pa);
if (pa & (PAGE_SIZE-1))
panic("%s: unaligned address passed : %p\n", __func__, (void *) pa);
blob = thunk_mmap(pm_tmp_p0, PAGE_SIZE,
THUNK_PROT_READ | THUNK_PROT_WRITE,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, pa);
if (blob != pm_tmp_p0)
panic("%s: couldn't get mapping", __func__);
memset(blob, 0, PAGE_SIZE);
thunk_munmap(blob, PAGE_SIZE);
}
void
pmap_copy_page(paddr_t src_pa, paddr_t dst_pa)
{
char *sblob, *dblob;
if (src_pa & (PAGE_SIZE-1))
panic("%s: unaligned address passed : %p\n", __func__, (void *) src_pa);
if (dst_pa & (PAGE_SIZE-1))
panic("%s: unaligned address passed : %p\n", __func__, (void *) dst_pa);
thunk_printf_debug("pmap_copy_page: pa src %p, pa dst %p\n",
(void *) src_pa, (void *) dst_pa);
sblob = thunk_mmap(pm_tmp_p0, PAGE_SIZE,
THUNK_PROT_READ,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, src_pa);
if (sblob != pm_tmp_p0)
panic("%s: couldn't get src mapping", __func__);
dblob = thunk_mmap(pm_tmp_p1, PAGE_SIZE,
THUNK_PROT_READ | THUNK_PROT_WRITE,
THUNK_MAP_FILE | THUNK_MAP_FIXED | THUNK_MAP_SHARED,
mem_fh, dst_pa);
if (dblob != pm_tmp_p1)
panic("%s: couldn't get dst mapping", __func__);
memcpy(dblob, sblob, PAGE_SIZE);
thunk_munmap(sblob, PAGE_SIZE);
thunk_munmap(dblob, PAGE_SIZE);
}
void
pmap_page_protect(struct vm_page *page, vm_prot_t prot)
{
intptr_t ppn;
struct pv_entry *pv, *npv;
ppn = atop(VM_PAGE_TO_PHYS(page));
thunk_printf_debug("pmap_page_protect page %"PRIiPTR" to prot %d\n", ppn, prot);
if (prot == VM_PROT_NONE) {
npv = pv = &pv_table[ppn];
while ((pv != NULL) && (pv->pv_pmap != NULL)) {
if (pv->pv_vflags & PV_UNMANAGED) {
pv = pv->pv_next;
continue;
}
if (pv->pv_pmap->pm_flags & PM_ACTIVE)
pmap_page_deactivate(pv);
if (pv != &pv_table[ppn])
npv = pv->pv_next;
pmap_set_pv(pv->pv_pmap, pv->pv_lpn, NULL);
if (pv->pv_vflags & PV_WIRED)
pv->pv_pmap->pm_stats.wired_count--;
pv_release(pv->pv_pmap, ppn, pv->pv_lpn);
pv = npv;
}
} else if (prot != VM_PROT_ALL) {
for (pv = &pv_table[ppn]; pv != NULL; pv = pv->pv_next) {
if ((pv->pv_pmap != NULL) &&
((pv->pv_vflags & PV_UNMANAGED) == 0)) {
pv->pv_prot &= prot;
pv_update(pv);
if (pv->pv_pmap->pm_flags & PM_ACTIVE)
pmap_page_activate(pv);
}
}
}
}
bool
pmap_clear_modify(struct vm_page *page)
{
struct pv_entry *pv;
uintptr_t ppn;
bool rv;
ppn = atop(VM_PAGE_TO_PHYS(page));
rv = pmap_is_modified(page);
thunk_printf_debug("pmap_clear_modify page %"PRIiPTR"\n", ppn);
if (rv) {
for (pv = &pv_table[ppn]; pv != NULL; pv = pv->pv_next)
if (pv->pv_pmap == pmap_kernel() &&
(pv->pv_prot & VM_PROT_WRITE))
return rv;
pv_table[ppn].pv_pflags &= ~PV_MODIFIED;
pmap_update_page(ppn);
}
return rv;
}
bool
pmap_clear_reference(struct vm_page *page)
{
uintptr_t ppn;
bool rv;
ppn = atop(VM_PAGE_TO_PHYS(page));
rv = pmap_is_referenced(page);
thunk_printf_debug("pmap_clear_reference page %"PRIiPTR"\n", ppn);
if (rv) {
pv_table[ppn].pv_pflags &= ~PV_REFERENCED;
pmap_update_page(ppn);
}
return rv;
}
bool
pmap_is_modified(struct vm_page *page)
{
intptr_t ppn;
bool rv;
ppn = atop(VM_PAGE_TO_PHYS(page));
rv = (pv_table[ppn].pv_pflags & PV_MODIFIED) != 0;
thunk_printf_debug("pmap_is_modified page %"PRIiPTR" : %s\n", ppn, rv?"yes":"no");
return rv;
}
bool
pmap_is_referenced(struct vm_page *page)
{
intptr_t ppn;
ppn = atop(VM_PAGE_TO_PHYS(page));
thunk_printf_debug("pmap_is_referenced page %"PRIiPTR"\n", ppn);
return (pv_table[ppn].pv_pflags & PV_REFERENCED) != 0;
}
paddr_t
pmap_phys_address(paddr_t cookie)
{
return ptoa(cookie);
}
vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
thunk_printf_debug("pmap_growkernel: till %p (adding %"PRIu64" KB)\n",
(void *) maxkvaddr,
(uint64_t) (maxkvaddr - kmem_kvm_cur_end)/1024);
if (maxkvaddr > kmem_kvm_end)
return kmem_kvm_end;
kmem_kvm_cur_end = maxkvaddr;
return kmem_kvm_cur_end;
}