foo
This commit is contained in:
+101
-41
@@ -5,6 +5,7 @@
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <stdckdint.h>
|
||||
|
||||
// Try to create a new, empty virtual address space.
|
||||
errno_t vma_new(struct vma** out) {
|
||||
@@ -22,6 +23,8 @@ errno_t vma_new(struct vma** out) {
|
||||
if (res)
|
||||
goto err1;
|
||||
|
||||
tailq_init(&vmspace->mappings);
|
||||
|
||||
*out = vmspace;
|
||||
return 0;
|
||||
|
||||
@@ -32,20 +35,90 @@ err0:
|
||||
}
|
||||
|
||||
// Delete a virtual address space.
|
||||
void vma_free(struct vma* v) {}
|
||||
void vma_free(struct vma* v) {
|
||||
struct vma_mapping* mapping;
|
||||
while ((mapping = tailq_first(&v->mappings)) != nullptr) {
|
||||
tailq_remove(&v->mappings, mapping, link);
|
||||
vmo_unref(mapping->object);
|
||||
mem_free(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any mappings in the given address range.
|
||||
// May fail if there is no memory and a mapping needs to be split.
|
||||
errno_t vma_unmap(struct vma* v, uintptr_t vaddr, size_t size);
|
||||
errno_t vma_unmap(struct vma* v, uintptr_t vaddr, size_t size) {
|
||||
panic("TODO");
|
||||
}
|
||||
|
||||
// Change the protection of an existing mapping.
|
||||
// May fail if there is no memory and a mapping needs to be split.
|
||||
errno_t vma_protect(struct vma* v, uintptr_t vaddr, size_t size, enum prot_flags flags);
|
||||
errno_t vma_protect(struct vma* v, uintptr_t vaddr, size_t size, enum prot_flags flags) {
|
||||
panic("TODO");
|
||||
}
|
||||
|
||||
static struct vma_mapping* find_mapping(struct vma* v, size_t page) {
|
||||
for (struct vma_mapping* m = tailq_first(&v->mappings); m; m = tailq_next(m, link)) {
|
||||
if (page >= m->start_page && page < m->end_page)
|
||||
return m;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool access_allowed(enum prot_flags prot, const struct vma_fault_info* info) {
|
||||
if (info->caused_by_write)
|
||||
return prot & PROT_WRITE;
|
||||
if (info->caused_by_fetch)
|
||||
return prot & PROT_EXEC;
|
||||
return prot & (PROT_READ | PROT_WRITE);
|
||||
}
|
||||
|
||||
static enum pte_flags prot_to_pte(struct vma* v, enum prot_flags prot) {
|
||||
enum pte_flags pf = 0;
|
||||
if (prot & PROT_READ)
|
||||
pf |= PTE_READ;
|
||||
if (prot & PROT_WRITE)
|
||||
pf |= PTE_WRITE;
|
||||
if (prot & PROT_EXEC)
|
||||
pf |= PTE_EXEC;
|
||||
if (v->pmap.is_user)
|
||||
pf |= PTE_USER;
|
||||
return pf;
|
||||
}
|
||||
|
||||
// Try to handle a page fault.
|
||||
bool vma_page_fault(struct vma* v, const struct vma_fault_info* info) {
|
||||
// TODO
|
||||
return false;
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
const size_t faulty_page = info->addr / page_size;
|
||||
|
||||
spin_lock(&v->lock);
|
||||
struct vma_mapping* mapping = find_mapping(v, faulty_page);
|
||||
if (!mapping || !access_allowed(mapping->prot, info)) {
|
||||
spin_unlock(&v->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct vmo* object = mapping->object;
|
||||
const size_t object_page = (faulty_page - mapping->start_page) + mapping->offset_page;
|
||||
const enum prot_flags prot = mapping->prot;
|
||||
vmo_ref(object);
|
||||
spin_unlock(&v->lock);
|
||||
|
||||
struct page* page;
|
||||
if (object->get_page(object, object_page, &page)) {
|
||||
vmo_unref(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
spin_lock(&v->lock);
|
||||
mapping = find_mapping(v, faulty_page);
|
||||
const bool still_valid = mapping && mapping->object == object &&
|
||||
(faulty_page - mapping->start_page) + mapping->offset_page == object_page &&
|
||||
access_allowed(mapping->prot, info);
|
||||
|
||||
if (still_valid) {
|
||||
errno_t status =
|
||||
pmap_map(&v->pmap, faulty_page * page_size, vm_page_addr(page), prot_to_pte(v, prot), CACHE_ANY);
|
||||
assert(status == 0, "Failed to map a demand-loaded page\n");
|
||||
}
|
||||
spin_unlock(&v->lock);
|
||||
|
||||
vmo_unref(object);
|
||||
return true;
|
||||
}
|
||||
|
||||
errno_t vma_map(
|
||||
@@ -56,44 +129,31 @@ errno_t vma_map(
|
||||
enum prot_flags flags,
|
||||
uintptr_t vmo_offset
|
||||
) {
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
|
||||
if (addr % page_size != vmo_offset % page_size)
|
||||
return EINVAL;
|
||||
|
||||
size_t start_page = addr / page_size;
|
||||
const size_t addr_offset = addr % page_size;
|
||||
const size_t pages = ALIGN_UP(addr_offset + len, page_size) / page_size;
|
||||
size_t end_page = start_page + pages;
|
||||
|
||||
if (!vma || !vmo)
|
||||
return EINVAL;
|
||||
|
||||
const size_t first_vmo_page = vmo_offset / page_size;
|
||||
uintptr_t end;
|
||||
if (ckd_add(&end, addr, len))
|
||||
return ENOMEM;
|
||||
|
||||
for (size_t p = start_page; p < end_page; ++p) {
|
||||
const size_t page_index = p - start_page;
|
||||
struct page* page_obj;
|
||||
errno_t status = vmo->get_page(vmo, first_vmo_page + page_index, &page_obj);
|
||||
if (status != 0)
|
||||
return status;
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
if (addr % page_size != vmo_offset % page_size)
|
||||
return EINVAL;
|
||||
|
||||
phys_t paddr = (phys_t)vm_page_addr(page_obj);
|
||||
struct vma_mapping* mapping = mem_alloc(sizeof(struct vma_mapping), 0);
|
||||
if (!mapping)
|
||||
return ENOMEM;
|
||||
|
||||
enum pte_flags pf = 0;
|
||||
if (flags & PROT_READ)
|
||||
pf |= PTE_READ;
|
||||
if (flags & PROT_WRITE)
|
||||
pf |= PTE_WRITE;
|
||||
if (flags & PROT_EXEC)
|
||||
pf |= PTE_EXEC;
|
||||
if (vma->pmap.is_user)
|
||||
pf |= PTE_USER;
|
||||
vmo_ref(vmo);
|
||||
mapping->start_page = addr / page_size;
|
||||
mapping->end_page = ALIGN_UP(end, page_size) / page_size;
|
||||
mapping->offset_page = vmo_offset / page_size;
|
||||
mapping->object = vmo;
|
||||
mapping->prot = flags;
|
||||
|
||||
status = pmap_map(&vma->pmap, p * page_size, paddr, pf, CACHE_ANY);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
spin_lock(&vma->lock);
|
||||
tailq_insert_head(&vma->mappings, mapping, link);
|
||||
spin_unlock(&vma->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user