struct kmem_cache *vmr_kcache;
-static int __vmr_free_pgs(struct proc *p, pte_t *pte, void *va, void *arg);
+static int __vmr_free_pgs(struct proc *p, pte_t pte, void *va, void *arg);
/* minor helper, will ease the file->chan transition */
static struct page_map *file2pm(struct file *file)
{
vm_i = TAILQ_FIRST(&p->vm_regions);
/* This works for now, but if all we have is BRK_END ones, we'll start
* growing backwards (TODO) */
- if (!vm_i || (va + len < vm_i->vm_base)) {
+ if (!vm_i || (va + len <= vm_i->vm_base)) {
vmr = kmem_cache_alloc(vmr_kcache, 0);
if (!vmr)
panic("EOM!");
/* Helper: copies the contents of pages from p to new p. For pages that aren't
* present, once we support swapping or CoW, we can do something more
- * intelligent. 0 on success, -ERROR on failure. */
+ * intelligent. 0 on success, -ERROR on failure. Can't handle jumbos. */
static int copy_pages(struct proc *p, struct proc *new_p, uintptr_t va_start,
uintptr_t va_end)
{
va_end);
return -EINVAL;
}
- int copy_page(struct proc *p, pte_t *pte, void *va, void *arg) {
+ int copy_page(struct proc *p, pte_t pte, void *va, void *arg) {
struct proc *new_p = (struct proc*)arg;
struct page *pp;
- if (PAGE_UNMAPPED(*pte))
+ if (pte_is_unmapped(pte))
return 0;
/* pages could be !P, but right now that's only for file backed VMRs
* undergoing page removal, which isn't the caller of copy_pages. */
- if (PAGE_PRESENT(*pte)) {
+ if (pte_is_mapped(pte)) {
/* TODO: check for jumbos */
if (upage_alloc(new_p, &pp, 0))
return -ENOMEM;
- if (page_insert(new_p->env_pgdir, pp, va, *pte & PTE_PERM)) {
+ if (page_insert(new_p->env_pgdir, pp, va, pte_get_settings(pte))) {
page_decref(pp);
return -ENOMEM;
}
- memcpy(page2kva(pp), ppn2kva(PTE2PPN(*pte)), PGSIZE);
+ memcpy(page2kva(pp), KADDR(pte_get_paddr(pte)), PGSIZE);
page_decref(pp);
- } else if (PAGE_PAGED_OUT(*pte)) {
+ } else if (pte_is_paged_out(pte)) {
/* TODO: (SWAP) will need to either make a copy or CoW/refcnt the
* backend store. For now, this PTE will be the same as the
* original PTE */
panic("Swapping not supported!");
} else {
- panic("Weird PTE %p in %s!", *pte, __FUNCTION__);
+ panic("Weird PTE %p in %s!", pte_print(pte), __FUNCTION__);
}
return 0;
}
vmr->vm_file, vmr->vm_foff);
}
+void enumerate_vmrs(struct proc *p,
+ void (*func)(struct vm_region *vmr, void *opaque),
+ void *opaque)
+{
+ struct vm_region *vmr;
+
+ spin_lock(&p->vmr_lock);
+ TAILQ_FOREACH(vmr, &p->vm_regions, vm_link)
+ func(vmr, opaque);
+ spin_unlock(&p->vmr_lock);
+}
+
/* Error values aren't quite comprehensive - check man mmap() once we do better
* with the FS.
*
return FALSE;
}
-/* Helper, maps in page at addr, but only if nothing is present there. Returns
+/* Helper, maps in page at addr, but only if nothing is mapped there. Returns
* 0 on success. If this is called by non-PM code, we'll store your ref in the
* PTE. */
static int map_page_at_addr(struct proc *p, struct page *page, uintptr_t addr,
int prot)
{
- pte_t *pte;
+ pte_t pte;
spin_lock(&p->pte_lock); /* walking and changing PTEs */
/* find offending PTE (prob don't read this in). This might alloc an
* intermediate page table page. */
pte = pgdir_walk(p->env_pgdir, (void*)addr, TRUE);
- if (!pte) {
+ if (!pte_walk_okay(pte)) {
spin_unlock(&p->pte_lock);
return -ENOMEM;
}
/* a spurious, valid PF is possible due to a legit race: the page might have
* been faulted in by another core already (and raced on the memory lock),
* in which case we should just return. */
- if (PAGE_PRESENT(*pte)) {
+ if (pte_is_present(pte)) {
spin_unlock(&p->pte_lock);
/* callers expect us to eat the ref if we succeed. */
page_decref(page);
return 0;
}
+ if (pte_is_mapped(pte)) {
+ /* we're clobbering an old entry. if we're just updating the prot, then
+ * it's no big deal. o/w, there might be an issue. */
+ if (page2pa(page) != pte_get_paddr(pte)) {
+ warn_once("Clobbered a PTE mapping (%p -> %p)\n", pte_print(pte),
+ page2pa(page) | prot);
+ }
+ page_decref(pa2page(pte_get_paddr(pte)));
+ }
/* preserve the dirty bit - pm removal could be looking concurrently */
- prot |= (*pte & PTE_D ? PTE_D : 0);
+ prot |= (pte_is_dirty(pte) ? PTE_D : 0);
/* We have a ref to page, which we are storing in the PTE */
- *pte = PTE(page2ppn(page), PTE_P | prot);
+ pte_write(pte, page2pa(page), prot);
spin_unlock(&p->pte_lock);
return 0;
}
int __do_mprotect(struct proc *p, uintptr_t addr, size_t len, int prot)
{
struct vm_region *vmr, *next_vmr;
- pte_t *pte;
+ pte_t pte;
bool shootdown_needed = FALSE;
int pte_prot = (prot & PROT_WRITE) ? PTE_USER_RW :
- (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
+ (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : PTE_NONE;
/* TODO: this is aggressively splitting, when we might not need to if the
* prots are the same as the previous. Plus, there are three excessive
* scans. Finally, we might be able to merge when we are done. */
}
vmr->vm_prot = prot;
spin_lock(&p->pte_lock); /* walking and changing PTEs */
- /* TODO: use a memwalk */
- for (uintptr_t va = vmr->vm_base; va < vmr->vm_end; va += PGSIZE) {
+ /* TODO: use a memwalk. At a minimum, we need to change every existing
+ * PTE that won't trigger a PF (meaning, present PTEs) to have the new
+ * prot. The others will fault on access, and we'll change the PTE
+ * then. In the off chance we have a mapped but not present PTE, we
+ * might as well change it too, since we're already here. */
+ for (uintptr_t va = vmr->vm_base; va < vmr->vm_end; va += PGSIZE) {
pte = pgdir_walk(p->env_pgdir, (void*)va, 0);
- if (pte && PAGE_PRESENT(*pte)) {
- *pte = (*pte & ~PTE_PERM) | pte_prot;
+ if (pte_walk_okay(pte) && pte_is_mapped(pte)) {
+ pte_replace_perm(pte, pte_prot);
shootdown_needed = TRUE;
}
}
return ret;
}
-static int __munmap_mark_not_present(struct proc *p, pte_t *pte, void *va,
+static int __munmap_mark_not_present(struct proc *p, pte_t pte, void *va,
void *arg)
{
bool *shootdown_needed = (bool*)arg;
- struct page *page;
/* could put in some checks here for !P and also !0 */
- if (!PAGE_PRESENT(*pte)) /* unmapped (== 0) *ptes are also not PTE_P */
+ if (!pte_is_present(pte)) /* unmapped (== 0) *ptes are also not PTE_P */
return 0;
- page = ppn2page(PTE2PPN(*pte));
- *pte &= ~PTE_P;
+ pte_clear_present(pte);
*shootdown_needed = TRUE;
return 0;
}
* 0 or not. If it isn't, then we're still okay to look at the page. Consider
* the PTE a weak ref on the page. So long as you hold the mm lock, you can
* look at the PTE and know the page isn't being freed. */
-static int __vmr_free_pgs(struct proc *p, pte_t *pte, void *va, void *arg)
+static int __vmr_free_pgs(struct proc *p, pte_t pte, void *va, void *arg)
{
struct page *page;
- if (!*pte)
+ if (pte_is_unmapped(pte))
return 0;
- page = ppn2page(PTE2PPN(*pte));
- *pte = 0;
+ page = pa2page(pte_get_paddr(pte));
+ pte_clear(pte);
if (!(atomic_read(&page->pg_flags) & PG_PAGEMAP))
page_decref(page);
return 0;
int __do_munmap(struct proc *p, uintptr_t addr, size_t len)
{
struct vm_region *vmr, *next_vmr, *first_vmr;
- pte_t *pte;
bool shootdown_needed = FALSE;
/* TODO: this will be a bit slow, since we end up doing three linear
return 0;
}
-/* Returns 0 on success, or an appropriate -error code.
+/* Returns 0 on success, or an appropriate -error code.
*
* Notes: if your TLB caches negative results, you'll need to flush the
* appropriate tlb entry. Also, you could have a weird race where a present PTE
struct vm_region *vmr;
struct page *a_page;
unsigned int f_idx; /* index of the missing page in the file */
- pte_t *pte;
int ret = 0;
bool first = TRUE;
va = ROUNDDOWN(va,PGSIZE);
- if (prot != PROT_READ && prot != PROT_WRITE && prot != PROT_EXEC)
- panic("bad prot!");
refault:
/* read access to the VMRs TODO: RCU */
spin_lock(&p->vmr_lock);
/* Check the vmr's protection */
vmr = find_vmr(p, va);
if (!vmr) { /* not mapped at all */
+ printd("fault: %p not mapped\n", va);
ret = -EFAULT;
goto out;
}
int pte_prot = (vmr->vm_prot & PROT_WRITE) ? PTE_USER_RW :
(vmr->vm_prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
ret = map_page_at_addr(p, a_page, va, pte_prot);
+ if (ret) {
+ printd("map_page_at for %p fails with %d\n", va, ret);
+ }
/* fall through, even for errors */
out_put_pg:
/* the VMR's existence in the PM (via the mmap) allows us to have PTE point
* isn't enough, since there might be a race on outer levels of page tables.
* For now, we'll just use the dyn_vmap_lock (which technically works). */
spin_lock(&dyn_vmap_lock);
- pte_t *pte;
+ pte_t pte;
#ifdef CONFIG_X86
perm |= PTE_G;
#endif
for (int i = 0; i < num_pages; i++) {
pte = pgdir_walk(boot_pgdir, (void*)(vaddr + i * PGSIZE), 1);
- if (!pte) {
+ if (!pte_walk_okay(pte)) {
spin_unlock(&dyn_vmap_lock);
return -ENOMEM;
}
/* You probably should have unmapped first */
- if (*pte)
- warn("Existing PTE value %p\n", *pte);
- *pte = PTE(pa2ppn(paddr + i * PGSIZE), perm);
+ if (pte_is_mapped(pte))
+ warn("Existing PTE value %p\n", pte_print(pte));
+ pte_write(pte, paddr + i * PGSIZE, perm);
}
spin_unlock(&dyn_vmap_lock);
return 0;
warn("Incomplete, don't call this yet.");
spin_lock(&dyn_vmap_lock);
/* TODO: For all pgdirs */
- pte_t *pte;
+ pte_t pte;
for (int i = 0; i < num_pages; i++) {
pte = pgdir_walk(boot_pgdir, (void*)(vaddr + i * PGSIZE), 1);
- *pte = 0;
+ if (pte_walk_okay(pte))
+ pte_clear(pte);
}
/* TODO: TLB shootdown. Also note that the global flag is set on the PTE
* (for x86 for now), which requires a global shootdown. bigger issue is
/* it's not strictly necessary to drop paddr's pgoff, but it might save some
* vmap heartache in the future. */
if (map_vmap_segment(vaddr, PG_ADDR(paddr), nr_pages,
- PTE_P | PTE_KERN_RW | flags)) {
+ PTE_KERN_RW | flags)) {
warn("Unable to map a vmap segment"); /* probably a bug */
return 0;
}