[amd64] amd64 vmalloc/vmfree, slight changes to map.c
This commit is contained in:
+2
-1
@@ -21,7 +21,8 @@ OBJS+=$(O)/arch/amd64/kernel.o \
|
||||
$(O)/arch/amd64/acpi/tables.o \
|
||||
$(O)/arch/amd64/acpi/hpet.o \
|
||||
$(O)/arch/amd64/mm/phys.o \
|
||||
$(O)/arch/amd64/mm/heap.o
|
||||
$(O)/arch/amd64/mm/heap.o \
|
||||
$(O)/arch/amd64/mm/vmalloc.o
|
||||
kernel_OBJS=$(O)/arch/amd64/entry.o \
|
||||
$(OBJS)
|
||||
kernel_LINKER=$(S)/arch/amd64/link.ld
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/** vim: set ft=cpp.doxygen :
|
||||
* @file arch/amd64/mm/map.h
|
||||
* @brief amd64-specific virtual memory space functions
|
||||
*/
|
||||
#pragma once
|
||||
#include "sys/types.h"
|
||||
#include "sys/mm.h"
|
||||
|
||||
/**
|
||||
* @brief Translate a virtual address in `space' to its physical mapping (if one exists).
|
||||
* May optionally return access flags in `flags' if non-NULL
|
||||
* @param space Virtual memory space
|
||||
* @param vaddr Virtual address
|
||||
* @param flags Access flags storage pointer or NULL
|
||||
* @return Physical address corresponding to `vaddr' in `space' if mapping exists,
|
||||
* MM_NADDR otherwise.
|
||||
*/
|
||||
uintptr_t amd64_map_get(const mm_space_t space, uintptr_t vaddr, uint64_t *flags);
|
||||
|
||||
/**
|
||||
* @brief Remove a virtual address mapping from memory space, optionally checking if it
|
||||
* is of a specific page size.
|
||||
* @param space Virtual memory space
|
||||
* @param vaddr Virtual address
|
||||
* @param size Expected page size:
|
||||
* * 1 for 4KiB
|
||||
* * 2 for 2MiB
|
||||
* * 3 for 1GiB
|
||||
* * 0 for any size
|
||||
* @return Physical address of the unmapped page (in case it has to be freed),
|
||||
* MM_NADDR in case the mapping does not exist or the size does not match.
|
||||
*/
|
||||
uintptr_t amd64_map_umap(mm_space_t space, uintptr_t vaddr, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Add a single page mapping to `pml4'
|
||||
* @param pml4 Virtual memory space
|
||||
* @param virt_addr Source virtual address
|
||||
* @param phys Destination physical address
|
||||
* @param flags Access flags:
|
||||
* * TODO: size-specific
|
||||
* * AMD64_MAP_WRITE
|
||||
* * AMD64_MAP_USER
|
||||
* @return 0 on success, -1 otherwise
|
||||
*/
|
||||
int amd64_map_single(mm_space_t pml4, uintptr_t virt_addr, uintptr_t phys, uint32_t flags);
|
||||
@@ -5,4 +5,6 @@
|
||||
|
||||
void amd64_phys_memory_map(const multiboot_memory_map_t *mmap, size_t length);
|
||||
|
||||
void amd64_phys_free(uintptr_t page);
|
||||
uintptr_t amd64_phys_alloc_page(void);
|
||||
uintptr_t amd64_phys_alloc_contiguous(size_t count);
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#pragma once
|
||||
#include "sys/mm.h"
|
||||
|
||||
#define VM_ALLOC_USER (1 << 0)
|
||||
#define VM_ALLOC_WRITE (1 << 1)
|
||||
|
||||
/**
|
||||
* @brief Find a free contiguous memory range inside a given one
|
||||
* in a virtual memory space.
|
||||
@@ -15,7 +18,7 @@
|
||||
* @return Address of the resulting range on success,
|
||||
* MM_NADDR if such range cannot be allocated
|
||||
*/
|
||||
uintptr_t vmfind(mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages);
|
||||
uintptr_t vmfind(const mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages);
|
||||
|
||||
/**
|
||||
* @brief Allocate a contiguous virtual memory range in a space
|
||||
|
||||
+113
-2
@@ -4,7 +4,118 @@
|
||||
#include "sys/mem.h"
|
||||
#include "arch/amd64/mm/pool.h"
|
||||
|
||||
static int amd64_mm_map_single(mm_space_t pml4, uintptr_t virt_addr, uintptr_t phys, uint32_t flags) {
|
||||
uintptr_t amd64_map_get(const mm_space_t pml4, uintptr_t vaddr, uint64_t *flags) {
|
||||
size_t pml4i = (vaddr >> 39) & 0x1FF;
|
||||
size_t pdpti = (vaddr >> 30) & 0x1FF;
|
||||
size_t pdi = (vaddr >> 21) & 0x1FF;
|
||||
size_t pti = (vaddr >> 12) & 0x1FF;
|
||||
|
||||
mm_pdpt_t pdpt;
|
||||
mm_pagedir_t pd;
|
||||
mm_pagetab_t pt;
|
||||
|
||||
if (!(pml4[pml4i] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (pml4[pml4i] & (1 << 7)) {
|
||||
panic("NYI\n");
|
||||
}
|
||||
|
||||
pdpt = (mm_pdpt_t) MM_VIRTUALIZE(pml4[pml4i] & ~0xFFF);
|
||||
|
||||
if (!(pdpt[pdpti] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (pdpt[pdpti] & (1 << 7)) {
|
||||
if (flags) {
|
||||
*flags = 2;
|
||||
}
|
||||
return (pdpt[pdpti] & ~0xFFF) | (vaddr & ((1 << 30) - 1));
|
||||
}
|
||||
|
||||
pd = (mm_pagedir_t) MM_VIRTUALIZE(pdpt[pdpti] & ~0xFFF);
|
||||
|
||||
if (!(pd[pdi] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (pd[pdi] & (1 << 7)) {
|
||||
if (flags) {
|
||||
*flags = 1;
|
||||
}
|
||||
return (pd[pti] & ~0xFFF) | (vaddr & ((1 << 21) - 1));
|
||||
}
|
||||
|
||||
pt = (mm_pagetab_t) MM_VIRTUALIZE(pd[pdi] & ~0xFFF);
|
||||
|
||||
if (!(pt[pti] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
*flags = 0;
|
||||
}
|
||||
|
||||
return (pt[pti] & ~0xFFF) | (vaddr & 0xFFF);
|
||||
}
|
||||
|
||||
uintptr_t amd64_map_umap(mm_space_t pml4, uintptr_t vaddr, uint32_t size) {
|
||||
// TODO: support page sizes other than 4KiB
|
||||
// (Though I can't think of any reason to use it)
|
||||
size_t pml4i = (vaddr >> 39) & 0x1FF;
|
||||
size_t pdpti = (vaddr >> 30) & 0x1FF;
|
||||
size_t pdi = (vaddr >> 21) & 0x1FF;
|
||||
size_t pti = (vaddr >> 12) & 0x1FF;
|
||||
|
||||
mm_pdpt_t pdpt;
|
||||
mm_pagedir_t pd;
|
||||
mm_pagetab_t pt;
|
||||
|
||||
if (!(pml4[pml4i] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (pml4[pml4i] & (1 << 7)) {
|
||||
panic("NYI\n");
|
||||
}
|
||||
|
||||
pdpt = (mm_pdpt_t) MM_VIRTUALIZE(pml4[pml4i] & ~0xFFF);
|
||||
|
||||
if (!(pdpt[pdpti] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (pdpt[pdpti] & (1 << 7)) {
|
||||
panic("NYI\n");
|
||||
}
|
||||
|
||||
pd = (mm_pagedir_t) MM_VIRTUALIZE(pdpt[pdpti] & ~0xFFF);
|
||||
|
||||
if (!(pd[pdi] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
if (pd[pdi] & (1 << 7)) {
|
||||
panic("NYI\n");
|
||||
}
|
||||
|
||||
pt = (mm_pagetab_t) MM_VIRTUALIZE(pd[pdi] & ~0xFFF);
|
||||
|
||||
if (!(pt[pti] & 1)) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
uint64_t old = pt[pti] & ~0xFFF;
|
||||
pt[pti] = 0;
|
||||
asm volatile("invlpg (%0)"::"a"(vaddr):"memory");
|
||||
return old;
|
||||
}
|
||||
|
||||
int amd64_map_single(mm_space_t pml4, uintptr_t virt_addr, uintptr_t phys, uint32_t flags) {
|
||||
// TODO: support page sizes other than 4KiB
|
||||
// (Though I can't think of any reason to use it)
|
||||
size_t pml4i = (virt_addr >> 39) & 0x1FF;
|
||||
size_t pdpti = (virt_addr >> 30) & 0x1FF;
|
||||
size_t pdi = (virt_addr >> 21) & 0x1FF;
|
||||
@@ -64,7 +175,7 @@ int mm_map_pages_contiguous(mm_space_t pml4, uintptr_t virt_base, uintptr_t phys
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uintptr_t virt_addr = virt_base + (i << 12);
|
||||
|
||||
if (amd64_mm_map_single(pml4, virt_addr, phys_base + (i << 12), flags) != 0) {
|
||||
if (amd64_map_single(pml4, virt_addr, phys_base + (i << 12), flags) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "arch/amd64/mm/pool.h"
|
||||
#include "arch/amd64/mm/phys.h"
|
||||
#include "sys/vmalloc.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/panic.h"
|
||||
#include "sys/heap.h"
|
||||
|
||||
@@ -50,6 +50,7 @@ void amd64_phys_free(uintptr_t page) {
|
||||
if (page < PHYS_ALLOWED_BEGIN) {
|
||||
panic("Tried to free kernel physical pages\n");
|
||||
}
|
||||
page -= PHYS_ALLOWED_BEGIN;
|
||||
// Address is too high
|
||||
if (PHYS_TRACK_INDEX(page) >= PHYS_MAX_INDEX) {
|
||||
panic("Tried to free non-available physical page\n");
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "arch/amd64/mm/phys.h"
|
||||
#include "arch/amd64/mm/map.h"
|
||||
#include "sys/vmalloc.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/panic.h"
|
||||
|
||||
uintptr_t vmfind(const mm_space_t pml4, uintptr_t from, uintptr_t to, size_t npages) {
|
||||
// XXX: The slowest approach I could think of
|
||||
// Though the easiest one
|
||||
size_t page_index = from >> 12;
|
||||
|
||||
while ((page_index + npages) <= (to >> 12)) {
|
||||
for (size_t i = 0; i < npages; ++i) {
|
||||
if (amd64_map_get(pml4, (page_index + i) << 12, 0) != MM_NADDR) {
|
||||
goto no_match;
|
||||
}
|
||||
}
|
||||
|
||||
return page_index << 12;
|
||||
no_match:
|
||||
++page_index;
|
||||
continue;
|
||||
}
|
||||
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
uintptr_t vmalloc(mm_space_t pml4, uintptr_t from, uintptr_t to, size_t npages, int flags) {
|
||||
uintptr_t addr = vmfind(pml4, from, to, npages);
|
||||
uintptr_t virt_page, phys_page;
|
||||
uint32_t rflags = 0;
|
||||
|
||||
if (flags & VM_ALLOC_WRITE) {
|
||||
rflags |= 1 << 1;
|
||||
}
|
||||
if (flags & VM_ALLOC_USER) {
|
||||
rflags |= 1 << 2;
|
||||
}
|
||||
|
||||
if (addr == MM_NADDR) {
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < npages; ++i) {
|
||||
virt_page = addr + (i << 12);
|
||||
phys_page = amd64_phys_alloc_page();
|
||||
|
||||
// Allocation of physical page failed, clean up
|
||||
if (phys_page == MM_NADDR) {
|
||||
// Unmap previously allocated pages
|
||||
for (size_t j = 0; j < i; ++j) {
|
||||
virt_page = addr + (j << 12);
|
||||
// Deallocate physical pages that've already been mapped
|
||||
// We've mapped only 4KiB pages, so expect to unmap only
|
||||
// 4KiB pages
|
||||
// TODO: use assert
|
||||
if ((phys_page = amd64_map_umap(pml4, virt_page, 1)) == MM_NADDR) {
|
||||
panic("Failed to deallocate page after a failed allocation\n");
|
||||
}
|
||||
|
||||
amd64_phys_free(phys_page);
|
||||
}
|
||||
return MM_NADDR;
|
||||
}
|
||||
|
||||
// Succeeded, map the page
|
||||
// TODO: use assert
|
||||
if (amd64_map_single(pml4, virt_page, phys_page, 0) != 0) {
|
||||
panic("Failed to map page\n");
|
||||
}
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void vmfree(mm_space_t pml4, uintptr_t addr, size_t npages) {
|
||||
uintptr_t phys;
|
||||
for (size_t i = 0; i < npages; ++i) {
|
||||
if ((phys = amd64_map_umap(pml4, addr + (i << 12), 1)) == MM_NADDR) {
|
||||
panic("Double vmfree error: %p is not an allocated page\n", addr + (i << 12));
|
||||
}
|
||||
amd64_phys_free(phys);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user