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.

193 lines
5.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 is not physically contiguous (for regular user
* allocations). The high memory is statically mapped to the area after the
* kernel image, which starts at `CFG_KERN_ORIGIN + KERN_OFFSET`. Om i386,
* this results in a kernel image mapping to `0xf0100000`.
*/
#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
};
/**
* @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 mm_flags flags) __malloc_like;
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)
{
if (phys > phys_end)
return nil;
return (void *)phys + 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.
*/