[amd64] amd64 vmalloc/vmfree, slight changes to map.c

This commit is contained in:
Mark
2019-09-19 18:52:15 +03:00
parent 61f9ebbdae
commit becf1ccc54
8 changed files with 253 additions and 4 deletions
+2 -1
View File
@@ -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
+46
View File
@@ -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);
+2
View File
@@ -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);
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+1
View File
@@ -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");
+84
View File
@@ -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);
}
}