You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
6.8 KiB
C

/* See the end of this file for copyright and license terms. */
#pragma once
/**
* @file include/gay/mm.h
* @brief Header for dynamic memory management
*
* To avoid possible confusion, physical memory addresses always use type
* `uintptr_t` and virtual ones are `void *`. This should give us at least some
* type of compiler warning if they are accidentally mixed up.
*
* GayBSD uses a classic slab algorithm for its own data structures, which is
* backed by a buddy page frame allocator. The latter is also used for getting
* bigger areas of memory that are not physically contiguous (for regular user
* allocations). The entire physical memory is mapped statically in the range
* `DMAP_START - DMAP_END`.
*/
#ifdef _KERNEL
#include <arch/page.h>
#include <gay/types.h>
/**
* @brief Memory allocation flags passed to `kmalloc()`.
*/
enum mm_flags {
/** @brief Physically contiguous memory for DMA. */
MM_CONTIG = (1 << 0),
/** @brief Use emergency memory reserves if necessary. */
MM_EMERG = (1 << 1),
/** @brief Don't sleep during the allocation. */
MM_NOSLEEP = (1 << 2),
/** @brief Allocate userspace memory. */
MM_USER = (1 << 4),
/** @brief Kernel memory */
MM_KERN = MM_CONTIG,
/** @brief Allocate memory in atomic (irq) context. */
MM_ATOMIC = MM_EMERG | MM_NOSLEEP,
};
/**
* @brief Allocate memory.
*
* Memory must be released with `kfree()` after use.
*
* @param size Memory size in bytes
* @param flags Allocation flags
* @returns The allocated memory area, or `NULL` if OOM
*/
void *kmalloc(size_t size, enum mm_flags flags) __malloc_like __alloc_size(1);
/**
* @brief Release memory.
*
* @param ptr The pointer returned by `kmalloc()`.
*/
void kfree(void *ptr);
enum pflags {
PFLAG_PRESENT = __PFLAG_PRESENT,
PFLAG_RW = __PFLAG_RW,
PFLAG_USER = __PFLAG_USER,
PFLAG_ACCESSED = __PFLAG_ACCESSED,
PFLAG_DIRTY = __PFLAG_DIRTY,
PFLAG_GLOBAL = __PFLAG_GLOBAL,
PFLAG_NOCACHE = __PFLAG_NOCACHE,
#ifdef __HAVE_HUGEPAGES
PFLAG_HUGE = __PFLAG_HUGE,
#endif
};
/*
* Terrible hack that allows us to map pages before the page frame allocator is
* 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);
/* This just shrinks the usable physical area by PAGE_SIZE and returns the page */
uintptr_t __early_get_page(void);
/**
* @brief Map a page in physical memory to a virtual address.
* Remember that if `vm` is the memory map currently in use, you will most
* likely need to call `vm_update()` when you've finished mapping everything
* to flush the TLB.
*
* @param phys Physical address of the page
* @param virt Virtual address to map the page to
* @param flags Flags to apply to the page
* @returns 0 on success, or `-ENOMEM` if OOM (for allocating new page tables)
*/
int map_page(uintptr_t phys, void *virt, enum pflags flags);
/**
* @brief Remove a page mapping.
*
* @param virt Virtual address the page is mapped to, must be page aligned
* @returns The physical page address that was being mapped
*/
uintptr_t unmap_page(void *virt);
/** @brief Flush the TLB. */
void vm_flush(void);
/**
* @brief Initialize the memory allocator.
*
* This can only be called once, from the early `_boot()` routine.
*
* @param _phys_start Physical start address of the page area
* @param _phys_end Physical end address of the page area
* @returns 0 on success, or -1 if the pointers were garbage
*/
int kmalloc_init(uintptr_t _phys_start, uintptr_t _phys_end);
/** @brief Start of the mapped, physically contiguous kernel heap */
extern void *kheap_start;
/** @brief End of the mapped, physically contiguous kernel heap */
extern void *kheap_end;
/** @brief Start of the kernel heap in physical memory */
extern uintptr_t phys_start;
/** @brief End of the kernel heap in physical memory */
extern uintptr_t phys_end;
/**
* @brief Initialize the buddy page frame allocator.
* This is only called once, internally from `kmalloc_init()`.
*
* @return 0 on success, or -1 if it messed up
*/
int pages_init(void);
/**
* @brief Allocate and map a contiguous region in physical memory.
* The physical region will be mapped to its corresponding virtual address
* between `DMAP_START` and `DMAP_END`, such that the physical address can be
* calculated with `ptr - DMAP_OFFSET`.
*
* @param count Number of contiguous pages to allocate
* @param flags
* @return
*/
void *get_pages(usize count, enum mflags flags) __malloc_like;
#define GET_PAGE_LEVELS (HUGEPAGE_SHIFT - PAGE_SHIFT + 1)
#define GET_PAGE_MAXCOUNT (1 << (HUGEPAGE_SHIFT - PAGE_SHIFT))
void free_pages(void *ptr);
/**
* @brief Initialize the slab caches.
* This is called only once by `kmalloc_init()` after the buddy page frame
* allocator is initialized.
*/
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.
* The returned pointer will be within the range `DMAP_START` (inclusive)
* and `DMAP_END` (exclusive).
*
* @param phys Physical address
* @return Virtual address
*/
static __always_inline void *__v(uintptr_t phys)
{
# ifdef DEBUG
if (phys > phys_end)
return nil;
# endif
return (void *)phys + DMAP_OFFSET;
}
/**
* @brief Return where a virtual address in the direct mapping region is in
* physical memory. This does **not** perform a lookup in the page table
* structures, and should generally only be used from within mm code (hence the
* two underscores). The reliable way of determining where any virtual address
* maps to is `vtophys()`.
*
* @param virt Virtual address, must be within `DMAP_START - DMAP_END`
* @return The physical address, i.e. `virt - DMAP_OFFSET`
* @see vtophys()
*/
static __always_inline uintptr_t __p(void *virt)
{
# ifdef DEBUG
if (virt < DMAP_START || virt >= DMAP_END)
return 0;
# endif
return (uintptr_t)virt - DMAP_OFFSET;
}
#endif /* _KERNEL */
/*
* This file is part of GayBSD.
* Copyright (c) 2021 fef <owo@fef.moe>.
*
* GayBSD is nonviolent software: you may only use, redistribute, and/or
* modify it under the terms of the Cooperative Nonviolent Public License
* (CNPL) as found in the LICENSE file in the source code root directory
* or at <https://git.pixie.town/thufie/npl-builder>; either version 7
* of the license, or (at your option) any later version.
*
* GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent
* permitted by applicable law. See the CNPL for details.
*/