mm: minor page management refactor

This seems like a huge commit but it's really just
renaming a bunch of symbols.  The entire mm
subsystem is probably gonna have to go through
some major changes in the near future, so it's
best to start off with something that is not too
chaotic i guess.
main
anna 3 years ago
parent 03f31df67f
commit a3941b6dc4
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -13,18 +13,26 @@
#include <gay/cdefs.h> #include <gay/cdefs.h>
#include <gay/types.h> #include <gay/types.h>
/**
* @brief A single 32-bit Page Table Entry.
* The layout matches that of the Intel SDM, vol 3, sect 4.3, fig 4-4.
* Bits 9 and 10 (`slab` and `atomic`) are marked as AVL in the manual and
* ignored by the MMU. We only use them for `get_pflags()`/`set_pflags()`.
*/
struct x86_page_table_entry { struct x86_page_table_entry {
unsigned present:1; /**< Page Fault on access if 0 */ /* 0 */bool present:1; /**< Page Fault on access if 0 */
unsigned rw:1; /**< Page Fault on write if 0 */ /* 1 */bool rw:1; /**< Page Fault on write if 0 */
unsigned user:1; /**< Page Fault on user mode access if 0 */ /* 2 */bool user:1; /**< Page Fault on user mode access if 0 */
unsigned write_through:1; /**< Enable write-through caching */ /* 3 */bool write_through:1; /**< Enable write-through caching */
unsigned cache_disabled:1; /**< Disable caching in TLB */ /* 4 */bool cache_disabled:1; /**< Disable caching in TLB */
unsigned accessed:1; /**< 1 if page has been accessed */ /* 5 */bool accessed:1; /**< 1 if page has been accessed */
unsigned dirty:1; /**< 1 if page has been written to */ /* 6 */bool dirty:1; /**< 1 if page has been written to */
unsigned _reserved0:1; /* 7 */unsigned _reserved0:1;
unsigned global:1; /**< Don't update the TLB on table swap if 1 */ /* 8 */bool global:1; /**< Don't update the TLB on table swap if 1 */
unsigned _reserved1:3; /* 9 */bool slab:1; /**< Used by the slab allocator */
uintptr_t shifted_address:20; /**< Aligned pointer to the physical page */ /* 10 */bool atomic:1; /**< Allocated atomically */
/* 11 */unsigned _unused:1;
/* 12 */uintptr_t shifted_address:20; /**< Aligned pointer to the physical page */
} __packed; } __packed;
#define __PFLAG_PRESENT (1 << 0) #define __PFLAG_PRESENT (1 << 0)
#define __PFLAG_RW (1 << 1) #define __PFLAG_RW (1 << 1)
@ -34,6 +42,8 @@ struct x86_page_table_entry {
#define __PFLAG_ACCESSED (1 << 5) #define __PFLAG_ACCESSED (1 << 5)
#define __PFLAG_DIRTY (1 << 6) #define __PFLAG_DIRTY (1 << 6)
#define __PFLAG_GLOBAL (1 << 8) #define __PFLAG_GLOBAL (1 << 8)
#define __PFLAG_SLAB (1 << 9)
#define __PFLAG_ATOMIC (1 << 10)
struct x86_page_table { struct x86_page_table {
struct x86_page_table_entry entries[1024]; struct x86_page_table_entry entries[1024];
@ -50,19 +60,22 @@ struct x86_page_table {
* @param index Table index in the page directory * @param index Table index in the page directory
*/ */
#define X86_CURRENT_PT(index) ( &((struct x86_page_table *)X86_PD_OFFSET)[index] ) #define X86_CURRENT_PT(index) ( &((struct x86_page_table *)X86_PD_OFFSET)[index] )
#define X86_CURRENT_PTE(pd_index, pt_index) (&(X86_CURRENT_PT(pd_index)->entries[pt_index]))
struct x86_page_directory_entry { struct x86_page_directory_entry {
unsigned present:1; /**< Page Fault on access if 0 */ /* 0 */bool present:1; /**< Page Fault on access if 0 */
unsigned rw:1; /**< Page Fault on write if 0 */ /* 1 */bool rw:1; /**< Page Fault on write if 0 */
unsigned user:1; /**< Page Fault on user mode access if 0 */ /* 2 */bool user:1; /**< Page Fault on user mode access if 0 */
unsigned write_through:1; /**< Enable write-through caching */ /* 3 */bool write_through:1; /**< Enable write-through caching */
unsigned cache_disabled:1; /**< Disable caching in TLB */ /* 4 */bool cache_disabled:1; /**< Disable caching in TLB */
unsigned accessed:1; /**< 1 if page has been accessed */ /* 5 */bool accessed:1; /**< 1 if page has been accessed */
unsigned _reserved0:1; /* 6 */unsigned _reserved0:1;
unsigned huge:1; /**< 0 = 4K, 1 = 4M */ /* 7 */bool huge:1; /**< 0 = 4K, 1 = 4M */
unsigned _reserved1:1; /* 8 */unsigned _reserved1:1;
unsigned _ignored2:3; /* 9 */bool slab:1; /**< Used by the slab allocator (only if `!huge`) */
uintptr_t shifted_address:20; /**< Aligned pointer to `struct x86_page_table` */ /* 10 */bool atomic:1; /**< Allocated atomically (only if `!huge`) */
/* 11 */unsigned _unused:1;
/* 12 */uintptr_t shifted_address:20; /**< Aligned pointer to `struct x86_page_table` */
} __packed; } __packed;
#define __PFLAG_HUGE (1 << 7) #define __PFLAG_HUGE (1 << 7)
@ -77,6 +90,7 @@ struct x86_page_directory {
* `arch/x86/mm/page.c` for a more detailed explanation. * `arch/x86/mm/page.c` for a more detailed explanation.
*/ */
#define X86_CURRENT_PD ((struct x86_page_directory *)X86_CURRENT_PT(1023)) #define X86_CURRENT_PD ((struct x86_page_directory *)X86_CURRENT_PT(1023))
#define X86_CURRENT_PDE(index) (&X86_CURRENT_PD->entries[index])
/** /**
* @brief Arch dependent virtual memory information data structure (x86 version). * @brief Arch dependent virtual memory information data structure (x86 version).

@ -17,8 +17,8 @@
#include <gay/errno.h> #include <gay/errno.h>
#include <gay/kprintf.h> #include <gay/kprintf.h>
#include <gay/mm.h> #include <gay/mm.h>
#include <gay/systm.h>
#include <gay/types.h> #include <gay/types.h>
#include <gay/util.h>
#include <string.h> #include <string.h>
@ -48,7 +48,7 @@ int map_page(uintptr_t phys, void *virt, enum pflags flags)
usize pt_index = ((uintptr_t)virt >> PAGE_SHIFT) % 1024; usize pt_index = ((uintptr_t)virt >> PAGE_SHIFT) % 1024;
struct x86_page_directory_entry *pde = &X86_CURRENT_PD->entries[pd_index]; struct x86_page_directory_entry *pde = &X86_CURRENT_PD->entries[pd_index];
if (flags & PFLAG_HUGE) { if (flags & P_HUGE) {
# ifdef DEBUG # ifdef DEBUG
if (phys != HUGEPAGE_ALIGN(phys)) { if (phys != HUGEPAGE_ALIGN(phys)) {
kprintf("map_page(): unaligned physical address %p!\n", kprintf("map_page(): unaligned physical address %p!\n",
@ -61,13 +61,18 @@ int map_page(uintptr_t phys, void *virt, enum pflags flags)
} }
# endif # endif
if (pde->present && !pde->huge) {
void *pt = __v(pde->shifted_address << PAGE_SHIFT);
free_pages(pt);
}
*(unsigned long *)pde = 0; *(unsigned long *)pde = 0;
pde->present = 1; pde->present = 1;
pde->huge = 1; pde->huge = 1;
pde->rw = (flags & PFLAG_RW) != 0; pde->rw = (flags & P_RW) != 0;
pde->user = (flags & PFLAG_USER) != 0; pde->user = (flags & P_USER) != 0;
pde->accessed = (flags & PFLAG_ACCESSED) != 0; pde->accessed = (flags & P_ACCESSED) != 0;
pde->cache_disabled = (flags & PFLAG_NOCACHE) != 0; pde->cache_disabled = (flags & P_NOCACHE) != 0;
pde->shifted_address = phys >> PAGE_SHIFT; pde->shifted_address = phys >> PAGE_SHIFT;
return 0; return 0;
} }
@ -80,7 +85,7 @@ int map_page(uintptr_t phys, void *virt, enum pflags flags)
struct x86_page_table *pt = X86_CURRENT_PT(pd_index); struct x86_page_table *pt = X86_CURRENT_PT(pd_index);
if (!pde->present) { if (!pde->present) {
uintptr_t pt_phys = vtophys(get_pages(1, MM_ATOMIC)); uintptr_t pt_phys = vtophys(get_pages(1, M_ATOMIC));
if (!pt_phys) if (!pt_phys)
return -ENOMEM; return -ENOMEM;
@ -94,9 +99,9 @@ int map_page(uintptr_t phys, void *virt, enum pflags flags)
struct x86_page_table_entry *pte = &pt->entries[pt_index]; struct x86_page_table_entry *pte = &pt->entries[pt_index];
*(unsigned long *)pte = 0; /* zero out the entire entry first */ *(unsigned long *)pte = 0; /* zero out the entire entry first */
pte->rw = (flags & PFLAG_RW) != 0; pte->rw = (flags & P_RW) != 0;
pte->user = (flags & PFLAG_USER) != 0; pte->user = (flags & P_USER) != 0;
pte->cache_disabled = (flags & PFLAG_NOCACHE) != 0; pte->cache_disabled = (flags & P_NOCACHE) != 0;
pte->shifted_address = phys >> PAGE_SHIFT; pte->shifted_address = phys >> PAGE_SHIFT;
pte->present = 1; pte->present = 1;
@ -107,20 +112,20 @@ int map_page(uintptr_t phys, void *virt, enum pflags flags)
* The only difference between this and map_page() is that we can't allocate * The only difference between this and map_page() is that we can't allocate
* new pages using get_pages() but have to use __early_get_page() instead here. * new pages using get_pages() but have to use __early_get_page() instead here.
* So, all we need to do is ensure that map_page() doesn't need to allocate new * So, all we need to do is ensure that map_page() doesn't need to allocate new
* pages when we call it, which it only does if pflags does not have PFLAG_HUGE * pages when we call it, which it only does if pflags does not have P_HUGE
* set and the page table doesn't exist (present bit in the page directory is * set and the page table doesn't exist (present bit in the page directory is
* clear). Therefore, we just need to make sure that, if PFLAG_HUGE is *not* * clear). Therefore, we just need to make sure that, if P_HUGE is *not*
* set, the page table is already allocated and marked as present in the page * set, the page table is already allocated and marked as present in the page
* directory. * directory.
*/ */
void __early_map_page(uintptr_t phys, void *virt, enum pflags pflags) void __early_map_page(uintptr_t phys, void *virt, enum pflags pflags)
{ {
if (!(pflags & PFLAG_HUGE)) { if (!(pflags & P_HUGE)) {
usize pd_index = ((uintptr_t)virt >> PAGE_SHIFT) / 1024; usize pd_index = ((uintptr_t)virt >> PAGE_SHIFT) / 1024;
struct x86_page_directory_entry *pde = &X86_CURRENT_PD->entries[pd_index]; struct x86_page_directory_entry *pde = &X86_CURRENT_PD->entries[pd_index];
if (!pde->present) { if (!pde->present) {
uintptr_t pt_phys = __early_get_page(); uintptr_t pt_phys = __early_get_page();
*(unsigned long *)pde = PFLAG_PRESENT | PFLAG_RW; *(unsigned long *)pde = P_PRESENT | P_RW;
pde->shifted_address = pt_phys >> PAGE_SHIFT; pde->shifted_address = pt_phys >> PAGE_SHIFT;
} }
} }
@ -146,23 +151,21 @@ uintptr_t unmap_page(void *virt)
uintptr_t phys = 0; uintptr_t phys = 0;
if (pde->huge) { if (pde->huge) {
phys = pde->shifted_address; phys = pde->shifted_address << PAGE_SHIFT;
phys <<= HUGEPAGE_SHIFT; pde->present = 0;
*(unsigned long *)pde = 0;
} else { } else {
struct x86_page_table *pt = X86_CURRENT_PT(pd_index); struct x86_page_table *pt = X86_CURRENT_PT(pd_index);
struct x86_page_table_entry *pte = &pt->entries[pt_index]; struct x86_page_table_entry *pte = &pt->entries[pt_index];
if (pte->present) { if (pte->present) {
phys = pte->shifted_address; phys = pte->shifted_address << PAGE_SHIFT;
phys <<= PAGE_SHIFT; pte->present = 0;
*(unsigned long *)pte = 0;
} }
} }
return phys; return phys;
} }
void x86_isr_page_fault(struct x86_trap_frame *frame, u32 error_code) void x86_isr_page_fault(trap_frame_t *frame, u32 error_code)
{ {
void *address; void *address;
__asm__ volatile( __asm__ volatile(
@ -193,7 +196,7 @@ void x86_isr_page_fault(struct x86_trap_frame *frame, u32 error_code)
kprintf("\n########## B O N K ##########\n"); kprintf("\n########## B O N K ##########\n");
kprintf("Illegal %s %s%s address %p!\n", space, rwx, present, address); kprintf("Illegal %s %s%s address %p!\n", space, rwx, present, address);
x86_print_regs(frame); print_regs(frame);
kprintf("system halted"); kprintf("system halted");
__asm__ volatile( __asm__ volatile(
" cli \n" " cli \n"
@ -207,7 +210,7 @@ uintptr_t vtophys(void *virt)
usize pd_index = ((uintptr_t)virt >> PAGE_SHIFT) / 1024; usize pd_index = ((uintptr_t)virt >> PAGE_SHIFT) / 1024;
usize pt_index = ((uintptr_t)virt >> PAGE_SHIFT) % 1024; usize pt_index = ((uintptr_t)virt >> PAGE_SHIFT) % 1024;
struct x86_page_directory_entry *pde = &X86_CURRENT_PD->entries[pd_index]; struct x86_page_directory_entry *pde = X86_CURRENT_PDE(pd_index);
if (!pde->present) if (!pde->present)
return 0; return 0;
@ -217,8 +220,7 @@ uintptr_t vtophys(void *virt)
phys <<= PAGE_SHIFT; /* attention, this is not HUGEPAGE_SHIFT */ phys <<= PAGE_SHIFT; /* attention, this is not HUGEPAGE_SHIFT */
phys |= (uintptr_t)virt & ~HUGEPAGE_MASK; phys |= (uintptr_t)virt & ~HUGEPAGE_MASK;
} else { } else {
struct x86_page_table *pt = X86_CURRENT_PT(pd_index); struct x86_page_table_entry *pte = X86_CURRENT_PTE(pd_index, pt_index);
struct x86_page_table_entry *pte = &pt->entries[pt_index];
if (pte->present) { if (pte->present) {
phys = pte->shifted_address; phys = pte->shifted_address;
phys <<= PAGE_SHIFT; phys <<= PAGE_SHIFT;

@ -21,24 +21,25 @@
#include <arch/page.h> #include <arch/page.h>
#include <gay/cdefs.h>
#include <gay/types.h> #include <gay/types.h>
/** /**
* @brief Memory allocation flags passed to `kmalloc()`. * @brief Memory allocation flags passed to `kmalloc()`.
*/ */
enum mm_flags { enum mflags {
/** @brief Physically contiguous memory for DMA. */ /** @brief Physically contiguous memory for DMA. */
MM_CONTIG = (1 << 0), M_CONTIG = (1 << 0),
/** @brief Use emergency memory reserves if necessary. */ /** @brief Use emergency memory reserves if necessary. */
MM_EMERG = (1 << 1), M_EMERG = (1 << 1),
/** @brief Don't sleep during the allocation. */ /** @brief Don't sleep during the allocation. */
MM_NOSLEEP = (1 << 2), M_NOSLEEP = (1 << 2),
/** @brief Allocate userspace memory. */ /** @brief Allocate userspace memory. */
MM_USER = (1 << 4), M_USER = (1 << 4),
/** @brief Kernel memory */ /** @brief Kernel memory */
MM_KERN = MM_CONTIG, M_KERN = M_CONTIG,
/** @brief Allocate memory in atomic (irq) context. */ /** @brief Allocate memory in atomic (irq) context. */
MM_ATOMIC = MM_EMERG | MM_NOSLEEP, M_ATOMIC = M_EMERG | M_NOSLEEP,
}; };
/** /**
@ -50,7 +51,7 @@ enum mm_flags {
* @param flags Allocation flags * @param flags Allocation flags
* @returns The allocated memory area, or `NULL` if OOM * @returns The allocated memory area, or `NULL` if OOM
*/ */
void *kmalloc(size_t size, enum mm_flags flags) __malloc_like __alloc_size(1); void *kmalloc(size_t size, enum mflags flags) __malloc_like __alloc_size(1);
/** /**
* @brief Release memory. * @brief Release memory.
@ -59,16 +60,30 @@ void *kmalloc(size_t size, enum mm_flags flags) __malloc_like __alloc_size(1);
*/ */
void kfree(void *ptr); void kfree(void *ptr);
/**
* @brief Flags for the paging structures.
*
* The macros with two underscores in front of them are defined in `arch/page.h`
* and match the respective bit positions in the platform's native hardware
* layout for better performance (no shifting around required).
*/
enum pflags { enum pflags {
PFLAG_PRESENT = __PFLAG_PRESENT, P_PRESENT = __PFLAG_PRESENT, /**< @brief Page exists */
PFLAG_RW = __PFLAG_RW, P_RW = __PFLAG_RW, /**< @brief Page is writable */
PFLAG_USER = __PFLAG_USER, P_USER = __PFLAG_USER, /**< @brief Page is accessible from ring 3 */
PFLAG_ACCESSED = __PFLAG_ACCESSED, P_ACCESSED = __PFLAG_ACCESSED, /**< @brief Page has been accessed */
PFLAG_DIRTY = __PFLAG_DIRTY, P_DIRTY = __PFLAG_DIRTY, /**< @brief Page has been written */
PFLAG_GLOBAL = __PFLAG_GLOBAL, P_GLOBAL = __PFLAG_GLOBAL, /**< @brief The entry survives `vm_flush()` */
PFLAG_NOCACHE = __PFLAG_NOCACHE, P_NOCACHE = __PFLAG_NOCACHE, /**< @brief The TLB won't cache this entry */
P_SLAB = __PFLAG_SLAB, /**< @brief Page is used by the slab allocator */
P_NOSLEEP = __PFLAG_ATOMIC, /**< @brief Page is atomic */
#ifdef __HAVE_HUGEPAGES #ifdef __HAVE_HUGEPAGES
PFLAG_HUGE = __PFLAG_HUGE, /** @brief This page is `HUGEPAGE_SIZE` bytes long, rather than `PAGE_SIZE` */
P_HUGE = __PFLAG_HUGE,
#endif
#ifdef __HAVE_NOEXEC
/** @brief No instructions can be fetched from this page */
P_NOEXEC = __PFLAG_NOEXEC,
#endif #endif
}; };
@ -77,7 +92,7 @@ enum pflags {
* set up. Don't ever use these anywhere, because they *will* break everything. * set up. Don't ever use these anywhere, because they *will* break everything.
*/ */
void __early_map_page(uintptr_t phys, void *virt, enum pflags flags); void __early_map_page(uintptr_t phys, void *virt, enum pflags flags);
/* This just shrinks the usable physical area by PAGE_SIZE and returns the page */ /* This just shrinks phys_end by PAGE_SIZE and returns the page */
uintptr_t __early_get_page(void); uintptr_t __early_get_page(void);
/** /**
@ -134,18 +149,21 @@ extern uintptr_t phys_end;
int pages_init(void); int pages_init(void);
/** /**
* @brief Allocate and map a contiguous region in physical memory. * @brief Allocate a contiguous region in physical memory.
* The physical region will be mapped to its corresponding virtual address * The returned region will be `(1 << order) * PAGE_SIZE` bytes long.
* between `DMAP_START` and `DMAP_END`, such that the physical address can be *
* calculated with `ptr - DMAP_OFFSET`. * @param order Order of magnitude (as in `1 << order`) for the region size
* * @param flags How to allocate (`order` must be 0 if `M_NOSLEEP` is specified)
* @param count Number of contiguous pages to allocate * @return A pointer to the beginning of the region in the direct mapping area,
* @param flags * or `nil` if the allocation failed
* @return
*/ */
void *get_pages(usize count, enum mflags flags) __malloc_like; void *get_pages(int order, enum mflags flags) __malloc_like;
#define GET_PAGE_LEVELS (HUGEPAGE_SHIFT - PAGE_SHIFT + 1) #ifdef __HAVE_HUGEPAGES
#define GET_PAGE_MAXCOUNT (1 << (HUGEPAGE_SHIFT - PAGE_SHIFT)) #define GET_PAGE_ORDERS (HUGEPAGE_SHIFT - PAGE_SHIFT + 1)
#else
#define GET_PAGE_ORDERS 10
#endif
#define GET_PAGE_MAX_ORDER (GET_PAGE_ORDERS - 1)
void free_pages(void *ptr); void free_pages(void *ptr);
@ -156,19 +174,6 @@ void free_pages(void *ptr);
*/ */
void slab_init(void); void slab_init(void);
/**
* @brief Allocate contiguous memory from the slab caches.
* This is only used internally by `kmalloc()` and for relatively small
* objects (<< PAGE_SIZE). If you need memory, use `kmalloc()` instead.
*
* @param size Requested memory size
* @param flags Flags that are passed to `get_pages` for creating new caches
* @return The allocated pointer, or `nil` if OOM or `size` was too big
*/
void *slab_alloc(usize size, enum mm_flags flags) __malloc_like __alloc_size(1);
void slab_free(void *ptr);
/** /**
* @brief Return where a physical address maps to in the direct memory area. * @brief Return where a physical address maps to in the direct memory area.
* The returned pointer will be within the range `DMAP_START` (inclusive) * The returned pointer will be within the range `DMAP_START` (inclusive)
@ -177,10 +182,11 @@ void slab_free(void *ptr);
* @param phys Physical address * @param phys Physical address
* @return Virtual address * @return Virtual address
*/ */
static __always_inline void *__v(uintptr_t phys) static inline void *__v(uintptr_t phys)
{ {
# ifdef DEBUG # ifdef DEBUG
if (phys > phys_end) if (phys > phys_end) {
kprintf("__v(%p): phys ptr out of range!\n", (void *)phys);
return nil; return nil;
# endif # endif
return (void *)phys + DMAP_OFFSET; return (void *)phys + DMAP_OFFSET;
@ -197,11 +203,13 @@ static __always_inline void *__v(uintptr_t phys)
* @return The physical address, i.e. `virt - DMAP_OFFSET` * @return The physical address, i.e. `virt - DMAP_OFFSET`
* @see vtophys() * @see vtophys()
*/ */
static __always_inline uintptr_t __p(void *virt) static inline uintptr_t __p(void *virt)
{ {
# ifdef DEBUG # ifdef DEBUG
if (virt < DMAP_START || virt >= DMAP_END) if (virt < DMAP_START || virt >= DMAP_END) {
kprintf("__p(%p): virt ptr out of range!\n", virt);
return 0; return 0;
}
# endif # endif
return (uintptr_t)virt - DMAP_OFFSET; return (uintptr_t)virt - DMAP_OFFSET;
} }

@ -7,7 +7,7 @@
#ifdef _KERNEL #ifdef _KERNEL
#include <gay/mm.h> #include <gay/mm.h>
#define malloc(size) kmalloc(size, MM_KERN) #define malloc(size) kmalloc(size, M_KERN)
#efine free(ptr) kfree(ptr) #efine free(ptr) kfree(ptr)
#else #else
/* /*

@ -58,6 +58,21 @@ int kmalloc_init(uintptr_t _phys_start, uintptr_t _phys_end)
return 0; return 0;
} }
__weak void *malloc(usize size)
{
return kmalloc(size, M_KERN);
}
__weak void free(void *ptr)
{
kfree(ptr);
}
/*
* Looking for kmalloc() and kfree()?
* Those two are in slab.c for purely organizational reasons.
*/
/* /*
* This file is part of GayBSD. * This file is part of GayBSD.
* Copyright (c) 2021 fef <owo@fef.moe>. * Copyright (c) 2021 fef <owo@fef.moe>.

@ -8,6 +8,7 @@
#include <gay/kprintf.h> #include <gay/kprintf.h>
#include <gay/mm.h> #include <gay/mm.h>
#include <gay/mutex.h> #include <gay/mutex.h>
#include <gay/systm.h>
#include <gay/types.h> #include <gay/types.h>
#include <gay/util.h> #include <gay/util.h>
@ -37,6 +38,7 @@
#endif #endif
#if CFG_DEBUG_PAGE_ALLOCS #if CFG_DEBUG_PAGE_ALLOCS
# define PAGE_ASSERT(x) KASSERT(x)
# define page_debug(msg, ...) kprintf("[page] " msg, ##__VA_ARGS__) # define page_debug(msg, ...) kprintf("[page] " msg, ##__VA_ARGS__)
# if CFG_DEBUG_PAGE_ALLOCS_NOISY # if CFG_DEBUG_PAGE_ALLOCS_NOISY
# define page_debug_noisy(msg, ...) kprintf("[page] " msg, ##__VA_ARGS__) # define page_debug_noisy(msg, ...) kprintf("[page] " msg, ##__VA_ARGS__)
@ -44,6 +46,7 @@
# define page_debug_noisy(msg, ...) ({}) # define page_debug_noisy(msg, ...) ({})
# endif # endif
#else #else
# define PAGE_ASSERT(x) ({})
# define page_debug(msg, ...) ({}) # define page_debug(msg, ...) ({})
# define page_debug_noisy(msg, ...) ({}) # define page_debug_noisy(msg, ...) ({})
#endif #endif
@ -54,14 +57,14 @@
* the one below it, starting at one page per entry. The effective result is * the one below it, starting at one page per entry. The effective result is
* that a single entry in the cache on level L covers `(1 << L)` pages. * that a single entry in the cache on level L covers `(1 << L)` pages.
*/ */
#define CACHE_LEVELS GET_PAGE_LEVELS #define CACHE_ORDERS GET_PAGE_ORDERS
#define LEVEL_SHIFT(level) (PAGE_SHIFT + (level)) #define ORDER_SHIFT(order) (PAGE_SHIFT + (order))
/** @brief There is one of this for every cache level. */ /** @brief There is one of this for every cache order. */
struct cache_pool { struct cache_pool {
/** /**
* @brief List of free blocks on this level of granularity. * @brief List of free blocks on this order of granularity.
* The individual entries sit right at the beginning of each free block, * The individual entries sit right at the beginning of each free block,
* and are always aligned to `entry_size` bytes. * and are always aligned to `entry_size` bytes.
*/ */
@ -74,11 +77,9 @@ struct cache_pool {
/** @brief Number of items in `freelist`. */ /** @brief Number of items in `freelist`. */
usize free_entries; usize free_entries;
}; };
static struct cache_pool caches[CACHE_LEVELS]; static struct cache_pool caches[CACHE_ORDERS];
static MTX(caches_lock); static MTX(caches_lock);
#define LONG_BIT_MASK (~(LONG_BIT - 1))
/* these get set in kmalloc_init() */ /* these get set in kmalloc_init() */
uintptr_t phys_start; uintptr_t phys_start;
uintptr_t phys_end; uintptr_t phys_end;
@ -91,28 +92,48 @@ uintptr_t __early_get_page(void)
static int sanity_check(void) static int sanity_check(void)
{ {
KASSERT(phys_start < phys_end);
KASSERT(phys_start == HUGEPAGE_ALIGN(phys_start));
/* phys_end is only page aligned, see kmalloc_init() */ /* phys_end is only page aligned, see kmalloc_init() */
if (phys_end != PAGE_ALIGN(phys_end) || phys_start != HUGEPAGE_ALIGN(phys_start)) { KASSERT(phys_end == PAGE_ALIGN(phys_end));
kprintf("Unaligned memory, this should never be possible\n");
return 1;
}
if ((phys_end - phys_start) < (32 * 1024 * 1024)) { if ((phys_end - phys_start) < (32 * 1024 * 1024)) {
kprintf("Less than 32 MB of usable RAM, this wouldn't go well\n"); kprintf("Less than 32 MB of usable RAM, this wouldn't go well\n");
return 1; return 1;
} }
if (phys_start > phys_end) {
kprintf("Hey, this is funny. pages_init() was called with parameters "
"such that phys_start > phys_end (%p > %p), which "
"should absolutely never be possible. I can't really continue "
"like this, so have a nice day.\n", (void *)phys_start, (void *)phys_end);
return 1;
}
return 0; return 0;
} }
/*
* Map the entire physical memory into the direct contiguous area.
* __early_map_page() might call __early_get_page() in order to allocate
* new page table structures, which in turn shrinks the physical memory
* size (see above).
*/
static inline void map_direct_area(void)
{
#ifdef __HAVE_HUGEPAGES
const usize step = HUGEPAGE_SIZE;
const enum pflags flags = P_PRESENT | P_RW | P_HUGE;
#else
const usize step = PAGE_SIZE;
const enum pflags flags = P_PRESENT | P_RW;
#endif
/*
* It might be necessary to use a volatile pointer to phys_end for this
* loop in case clang does The Optimization and caches its value for
* whatever reason, even though at least for x86 this is not the case
* (and i don't even thing the C standard allows it when calling
* external functions in between, but still, Never Trust The Compiler).
*/
for (uintptr_t pos = phys_start; pos <= phys_end - step; pos += step)
__early_map_page(pos, __v(pos), flags);
vm_flush();
}
/* /*
* This function maps the entire physical memory into the direct region * This function maps the entire physical memory into the direct region
* (DMAP_START - DMAP_END) and sets up the caches. * (DMAP_START - DMAP_END) and sets up the caches.
@ -124,18 +145,7 @@ int pages_init(void)
if (sanity_check() != 0) if (sanity_check() != 0)
return 1; return 1;
/* map_direct_area();
* Map the entire physical memory into the direct contiguous area.
* __early_map_page() might call __early_get_page() in order to allocate
* new page table structures, which in turn shrinks the physical memory
* size (see above).
* It might be necessary to use a volatile pointer to phys_end for this
* loop in case clang does The Optimization and caches its value for
* whatever reason, even though at least for x86 this is not the case.
*/
for (uintptr_t physptr = phys_start; physptr < phys_end; physptr += HUGEPAGE_SIZE)
__early_map_page(physptr, __v(physptr), PFLAG_HUGE | PFLAG_RW | PFLAG_GLOBAL);
vm_flush();
/* phys_end gets aligned, as promised by the comment in kmalloc_init() */ /* phys_end gets aligned, as promised by the comment in kmalloc_init() */
phys_end = align_floor(phys_end, HUGEPAGE_SIZE); phys_end = align_floor(phys_end, HUGEPAGE_SIZE);
@ -145,13 +155,9 @@ int pages_init(void)
* calculate the size of each bitmap, as well as their combined size * calculate the size of each bitmap, as well as their combined size
*/ */
usize bitmap_bytes = 0; usize bitmap_bytes = 0;
for (int i = 0; i < CACHE_LEVELS; i++) { for (int i = 0; i < CACHE_ORDERS; i++) {
usize bits = phys_size >> LEVEL_SHIFT(i); usize bits = phys_size >> ORDER_SHIFT(i);
/* round up to the next full long */ bits = align_ceil(bits, LONG_BIT);
if (bits & ~LONG_BIT_MASK) {
bits &= LONG_BIT_MASK;
bits += LONG_BIT;
}
bitmap_bytes += bits / 8; bitmap_bytes += bits / 8;
} }
@ -169,11 +175,11 @@ int pages_init(void)
* preallocate entries that can't be handed out (i.e. the cache bitmaps) * preallocate entries that can't be handed out (i.e. the cache bitmaps)
*/ */
unsigned long *bitmap_pos = bitmap_start; unsigned long *bitmap_pos = bitmap_start;
for (int i = 0; i < CACHE_LEVELS; i++) { for (int i = 0; i < CACHE_ORDERS; i++) {
/* total amount of entries on this level */ /* total amount of entries on this level */
usize total_bits = phys_size >> LEVEL_SHIFT(i); usize total_bits = phys_size >> ORDER_SHIFT(i);
/* number of entries on this level that the bitmap itself takes up */ /* number of entries on this level that the bitmap itself takes up */
usize wasted_bits = bitmap_bytes >> LEVEL_SHIFT(i); usize wasted_bits = bitmap_bytes >> ORDER_SHIFT(i);
if (wasted_bits == 0) if (wasted_bits == 0)
wasted_bits = 1; wasted_bits = 1;
bit_set_range(bitmap_pos, total_bits - wasted_bits, wasted_bits); bit_set_range(bitmap_pos, total_bits - wasted_bits, wasted_bits);
@ -190,11 +196,11 @@ int pages_init(void)
kheap_end = align_floor(bitmap_start, HUGEPAGE_SIZE); kheap_end = align_floor(bitmap_start, HUGEPAGE_SIZE);
/* /*
* populate the freelist on the highest level, all levels beneath it * populate the freelist on the highest order, all orders beneath it
* stay empty until one of the large blocks gets split up * stay empty until one of the large blocks gets split up
*/ */
struct cache_pool *high_pool = &caches[CACHE_LEVELS - 1]; struct cache_pool *high_pool = &caches[CACHE_ORDERS - 1];
usize step = 1 << LEVEL_SHIFT(CACHE_LEVELS - 1); usize step = 1 << ORDER_SHIFT(CACHE_ORDERS - 1);
for (void *pos = kheap_start; pos < kheap_end; pos += step) { for (void *pos = kheap_start; pos < kheap_end; pos += step) {
struct clist *entry = pos; struct clist *entry = pos;
clist_add(&high_pool->freelist, entry); clist_add(&high_pool->freelist, entry);
@ -218,62 +224,62 @@ static void *split_buddy(void *ptr, int level);
/** /**
* @brief Attempt to coalesce a block with its buddy. * @brief Attempt to coalesce a block with its buddy.
* If coalition is possible, the buddy is removed from its freelist at `level`. * If coalition is possible, the buddy is removed from its freelist at `order`.
* *
* @param ptr Pointer to the block * @param ptr Pointer to the block
* @param level Cache level, must be less than `CACHE_LEVELS - 1` (because you * @param order Cache order, must be less than `CACHE_ORDERS - 1` (because you
* can't join blocks at the highest cache level) * can't join blocks at the highest cache order)
* @return The joined block, or `nil` if coalition was not possible * @return The joined block, or `nil` if coalition was not possible
*/ */
static void *try_join_buddy(void *ptr, int level); static void *try_join_buddy(void *ptr, int order);
static inline usize get_bit_number(void *ptr, int level) static inline usize get_bit_number(void *ptr, int order)
{ {
return ((uintptr_t)ptr - (uintptr_t)kheap_start) >> LEVEL_SHIFT(level); return ((uintptr_t)ptr - (uintptr_t)kheap_start) >> ORDER_SHIFT(order);
} }
void *get_pages(usize count, enum mm_flags flags) void *get_pages(int order, enum mflags flags)
{ {
int level; PAGE_ASSERT(order >= 0);
for (level = 0; level < CACHE_LEVELS; level++) {
if ((1 << level) >= count) if (order >= GET_PAGE_ORDERS) {
break; page_debug("get_pages(%d, %#08x): Order too high!\n", order, flags);
}
if (level == CACHE_LEVELS) {
page_debug("get_pages(%zu, %08x): count too large!\n", count, flags);
return nil; return nil;
} }
if (flags & MM_NOSLEEP) { if (flags & M_NOSLEEP) {
kprintf("get_pages(): MM_NOSLEEP requested, this is not implemented yet :(\n"); kprintf("get_pages(): M_NOSLEEP requested, this is not implemented yet :(\n");
return nil; return nil;
} }
mtx_lock(&caches_lock); mtx_lock(&caches_lock);
struct clist *entry = nil; struct clist *entry = nil;
int entry_level; int entry_order;
for (entry_level = level; entry_level < CACHE_LEVELS; entry_level++) { for (entry_order = order; entry_order < CACHE_ORDERS; entry_order++) {
if (caches[entry_level].free_entries > 0) { if (caches[entry_order].free_entries > 0) {
entry = caches[entry_level].freelist.next; entry = caches[entry_order].freelist.next;
break; break;
} }
} }
if (entry_level == CACHE_LEVELS)
goto unlock; if (entry_order != CACHE_ORDERS) {
clist_del(entry);
clist_del(entry); caches[entry_order].free_entries--;
caches[entry_level].free_entries--;
usize bit_number = get_bit_number(entry, entry_order);
usize bit_number = get_bit_number(entry, entry_level); while (entry_order > order) {
while (entry_level > level) { entry = split_buddy(entry, entry_order);
entry = split_buddy(entry, entry_level); bit_set(caches[entry_order].bitmap, bit_number);
bit_set(caches[entry_level].bitmap, bit_number); entry_order--;
entry_level--; bit_number <<= 1;
bit_number <<= 1; }
bit_set(caches[order].bitmap, bit_number);
# if CFG_POISON_PAGES
memset(entry, 'a', 1 << ORDER_SHIFT(order));
# endif
} }
bit_set(caches[level].bitmap, bit_number);
unlock:
mtx_unlock(&caches_lock); mtx_unlock(&caches_lock);
return (void *)entry; return (void *)entry;
} }
@ -287,54 +293,66 @@ void free_pages(void *ptr)
} }
# endif # endif
mtx_lock(&caches_lock); if (sus_nil(ptr)) {
page_debug("free_pages(%p): tried to free NULL!\n", ptr);
return;
}
int level = 0; int order = 0;
usize bit_number = get_bit_number(ptr, level); usize bit_number = get_bit_number(ptr, order);
for (; level < CACHE_LEVELS; level++) { for (; order < CACHE_ORDERS; order++) {
if (bit_tst(caches[level].bitmap, bit_number)) if (bit_tst(caches[order].bitmap, bit_number))
break; break;
bit_number >>= 1; bit_number >>= 1;
} }
if (level == CACHE_LEVELS) { if (order == CACHE_ORDERS) {
page_debug("free_pages(%p): double free!\n", ptr); page_debug("free_pages(%p): double free!\n", ptr);
goto unlock; return;
} }
int original_order = order;
while (level < CACHE_LEVELS - 1) { mtx_lock(&caches_lock);
bit_clr(caches[level].bitmap, bit_number);
while (order < CACHE_ORDERS - 1) {
bit_clr(caches[order].bitmap, bit_number);
void *tmp = try_join_buddy(ptr, level); void *tmp = try_join_buddy(ptr, order);
if (tmp == nil) if (tmp == nil)
break; break;
ptr = tmp; ptr = tmp;
level++; order++;
bit_number >>= 1; bit_number >>= 1;
} }
clist_add(&caches[level].freelist, (struct clist *)ptr); if (order == CACHE_ORDERS - 1 && original_order != CACHE_ORDERS - 1)
caches[level].free_entries++; set_pflags(HUGEPAGE_ALIGN(ptr), P_HUGE | P_RW);
#if CFG_POISON_PAGES
memset(ptr, 'A', 1 << ORDER_SHIFT(order));
#endif
clist_add(&caches[order].freelist, (struct clist *)ptr);
caches[order].free_entries++;
unlock:
mtx_unlock(&caches_lock); mtx_unlock(&caches_lock);
} }
static inline void *split_buddy(void *ptr, int level) static inline void *split_buddy(void *ptr, int level)
{ {
# if CFG_DEBUG_PAGE_ALLOCS # if CFG_DEBUG_PAGE_ALLOCS
if ((uintptr_t)ptr % (1 << LEVEL_SHIFT(level))) { if ((uintptr_t)ptr % (1 << ORDER_SHIFT(level))) {
kprintf("split_buddy(ptr = %p, level = %d): unaligned ptr!\n", ptr, level); kprintf("split_buddy(ptr = %p, level = %d): unaligned ptr!\n", ptr, level);
return nil; return nil;
} }
if (level < 1 || level >= CACHE_LEVELS) { if (level < 1 || level >= CACHE_ORDERS) {
kprintf("split_buddy(ptr = %p, level = %d): invalid level!\n", ptr, level); kprintf("split_buddy(ptr = %p, level = %d): invalid level!\n", ptr, level);
return nil; return nil;
} }
# endif # endif
struct clist *high_buddy = ptr + (1 << LEVEL_SHIFT(level - 1)); struct clist *high_buddy = ptr + (1 << ORDER_SHIFT(level - 1));
clist_add(&caches[level - 1].freelist, high_buddy); clist_add(&caches[level - 1].freelist, high_buddy);
caches[level - 1].free_entries++; caches[level - 1].free_entries++;
@ -343,19 +361,19 @@ static inline void *split_buddy(void *ptr, int level)
return ptr; return ptr;
} }
static void *try_join_buddy(void *ptr, int level) static void *try_join_buddy(void *ptr, int order)
{ {
const usize entry_size = 1 << LEVEL_SHIFT(level); const usize entry_size = 1 << ORDER_SHIFT(order);
# if CFG_DEBUG_PAGE_ALLOCS # if CFG_DEBUG_PAGE_ALLOCS
if ((uintptr_t)ptr % entry_size) { if ((uintptr_t)ptr % entry_size) {
kprintf("try_join_buddy(%p, %d): unaligned ptr!\n", ptr, level); kprintf("try_join_buddy(%p, %d): unaligned ptr!\n", ptr, order);
return nil; return nil;
} }
/* level must be < CACHE_LEVELS - 1 because you /* order must be < CACHE_ORDERS - 1 because you
* can't join blocks on the topmost level */ * can't join blocks on the topmost order */
if (level >= CACHE_LEVELS - 1) { if (order >= CACHE_ORDERS - 1) {
kprintf("try_join_buddy(%p, %d): level >= CACHE_LEVELS - 1!\n", ptr, level); kprintf("try_join_buddy(%p, %d): order >= CACHE_ORDERS - 1!\n", ptr, order);
return nil; return nil;
} }
# endif # endif
@ -367,15 +385,15 @@ static void *try_join_buddy(void *ptr, int level)
* for any if branches. * for any if branches.
*/ */
uintptr_t buddy = (uintptr_t)ptr ^ entry_size; uintptr_t buddy = (uintptr_t)ptr ^ entry_size;
usize buddy_bitnum = get_bit_number((void *)buddy, level); usize buddy_bitnum = get_bit_number((void *)buddy, order);
if (bit_tst(caches[level].bitmap, buddy_bitnum)) if (bit_tst(caches[order].bitmap, buddy_bitnum))
return nil; return nil;
page_debug_noisy("join (%p:%p), lvl=%d\n", ptr, (void *)buddy, level); page_debug_noisy("join (%p:%p), order=%d\n", ptr, (void *)buddy, order);
/* If the buddy is free, we remove it from the freelist ... */ /* If the buddy is free, we remove it from the freelist ... */
clist_del((struct clist *)buddy); clist_del((struct clist *)buddy);
caches[level].free_entries--; caches[order].free_entries--;
/* /*
* ... and return a pointer to the coalesced block. * ... and return a pointer to the coalesced block.

@ -53,7 +53,6 @@ struct slab {
#define SLAB_STEP (sizeof(struct clist)) #define SLAB_STEP (sizeof(struct clist))
#define SLAB_OVERHEAD (sizeof(struct slab)) #define SLAB_OVERHEAD (sizeof(struct slab))
#define SLAB_EFFECTIVE_SIZE (SLAB_SIZE - SLAB_OVERHEAD)
#define SLAB_MAX_ALLOC (SLAB_SIZE - SLAB_OVERHEAD) #define SLAB_MAX_ALLOC (SLAB_SIZE - SLAB_OVERHEAD)
/* slabs are always aligned ... */ /* slabs are always aligned ... */
#define SLAB_PTR_MASK (~(SLAB_SIZE - 1)) #define SLAB_PTR_MASK (~(SLAB_SIZE - 1))
@ -65,18 +64,60 @@ struct slab {
# if CFG_DEBUG_SLAB_ALLOCS_NOISY # if CFG_DEBUG_SLAB_ALLOCS_NOISY
# define slab_debug_noisy(msg, ...) kprintf("[slab] " msg, ##__VA_ARGS__) # define slab_debug_noisy(msg, ...) kprintf("[slab] " msg, ##__VA_ARGS__)
# else # else
# define slab_debug_noisy(msg, ...) # define slab_debug_noisy(msg, ...) ({})
# endif # endif
#else #else
# define slab_debug(msg, ...) # define slab_debug(msg, ...) ({})
# define slab_debug_noisy(msg, ...) # define slab_debug_noisy(msg, ...) ({})
#endif #endif
/** @brief All slab pools, indexed by `entry_size / SLAB_STEP - 1` */ /** @brief All slabs grouped by entry_size, indexed by `entry_size / SLAB_STEP - 1` */
struct clist pools[SLAB_MAX_ALLOC / SLAB_STEP]; struct clist pools[SLAB_MAX_ALLOC / SLAB_STEP];
static struct slab *slab_create(unsigned int entry_size, enum mm_flags flags); static void *slab_alloc(usize size, enum mflags flags);
static usize round_size(usize size); static void slab_free(void *ptr);
static struct slab *slab_create(unsigned int entry_size, enum mflags flags);
static inline int get_order(usize size)
{
int order;
usize order_size = PAGE_SIZE;
for (order = 0; order <= GET_PAGE_MAX_ORDER; order++) {
if (order_size >= size)
break;
order_size <<= 1;
}
return order;
}
void *kmalloc(usize size, enum mflags flags)
{
if (size > SLAB_MAX_ALLOC) {
if (flags & M_CONTIG) {
int order = get_order(size);
if (order > GET_PAGE_MAX_ORDER) {
slab_debug("Requested alloc size %zu too large for get_pages()\n",
size);
return nil;
} else {
return get_pages(order, flags);
}
} else {
slab_debug("Refusing to allocate %zu bytes as slabs\n", size);
return nil;
}
} else {
return slab_alloc(size, flags);
}
}
void kfree(void *ptr)
{
kprintf("kfree() is not implemented yet lmao\n");
}
void slab_init(void) void slab_init(void)
{ {
@ -86,10 +127,10 @@ void slab_init(void)
clist_init(&pools[i]); clist_init(&pools[i]);
} }
void *slab_alloc(usize size, enum mm_flags flags) static inline void *slab_alloc(usize size, enum mflags flags)
{ {
size = round_size(size); size = align_ceil(size, SLAB_STEP);
if (size == 0) if (size == 0 || size > SLAB_MAX_ALLOC)
return nil; return nil;
struct clist *pool = &pools[size / SLAB_STEP - 1]; struct clist *pool = &pools[size / SLAB_STEP - 1];
@ -119,7 +160,7 @@ void *slab_alloc(usize size, enum mm_flags flags)
return (void *)ret; return (void *)ret;
} }
void slab_free(void *ptr) static inline void slab_free(void *ptr)
{ {
# if CFG_DEBUG_SLAB_ALLOCS # if CFG_DEBUG_SLAB_ALLOCS
if (ptr < kheap_start || ptr >= kheap_end) { if (ptr < kheap_start || ptr >= kheap_end) {
@ -138,7 +179,7 @@ void slab_free(void *ptr)
memset(ptr, 'A', slab->entry_size); memset(ptr, 'A', slab->entry_size);
# endif # endif
if (slab->free_entries * slab->entry_size + slab->entry_size > SLAB_EFFECTIVE_SIZE) { if (slab->free_entries * slab->entry_size + slab->entry_size > SLAB_MAX_ALLOC) {
/* none of the entries are in use, free the slab */ /* none of the entries are in use, free the slab */
slab_debug_noisy("Destroying empty cache of size %zu\n", slab->entry_size); slab_debug_noisy("Destroying empty cache of size %zu\n", slab->entry_size);
free_pages(slab); free_pages(slab);
@ -147,7 +188,7 @@ void slab_free(void *ptr)
} }
} }
static struct slab *slab_create(unsigned int entry_size, enum mm_flags flags) static struct slab *slab_create(unsigned int entry_size, enum mflags flags)
{ {
slab_debug_noisy("Creating new cache for size %zu\n", entry_size); slab_debug_noisy("Creating new cache for size %zu\n", entry_size);
struct slab *slab = get_pages(SLAB_SIZE / PAGE_SIZE, flags); struct slab *slab = get_pages(SLAB_SIZE / PAGE_SIZE, flags);
@ -168,19 +209,6 @@ static struct slab *slab_create(unsigned int entry_size, enum mm_flags flags)
return slab; return slab;
} }
static inline usize round_size(usize size)
{
if (size > SLAB_MAX_ALLOC)
return 0;
/* SLAB_STEP is a power of 2, so clang will (hopefully)
* replace these with fancy bit banging tricks */
if (size % SLAB_STEP)
size = (size / SLAB_STEP) * SLAB_STEP + SLAB_STEP;
return size;
}
/* /*
* This file is part of GayBSD. * This file is part of GayBSD.
* Copyright (c) 2021 fef <owo@fef.moe>. * Copyright (c) 2021 fef <owo@fef.moe>.

Loading…
Cancel
Save