Function comments (v1)
This commit is contained in:
@@ -14,11 +14,20 @@
|
||||
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
||||
struct GDTR gdtr;
|
||||
|
||||
/*
|
||||
* gdt_load - Loads Global Descriptor Table
|
||||
*/
|
||||
static void gdt_load()
|
||||
{
|
||||
asm("lgdt %0" : : "m"(gdtr));
|
||||
}
|
||||
|
||||
/*
|
||||
* gdt_flush - Flushes the Global Descriptor Table
|
||||
*
|
||||
* This function loads new Segment Selectors to make
|
||||
* the GDT changes take effect
|
||||
*/
|
||||
static void gdt_flush()
|
||||
{
|
||||
// Here, 0x8 is the kernel code selector
|
||||
@@ -42,6 +51,15 @@ static void gdt_flush()
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* gdt_init - Global Descriptor Table initialization
|
||||
*
|
||||
* This function loads a new GDT in the CPU.
|
||||
* It contains a null descriptor, kernel code and data
|
||||
* segments, and user code and data segments.
|
||||
* However, we do not use segmentation to manage memory on
|
||||
* 64-bit x86, as it's deprecated. Instead, we use paging.
|
||||
*/
|
||||
void gdt_init()
|
||||
{
|
||||
// Null descriptor (required)
|
||||
|
||||
@@ -23,6 +23,15 @@ static uintptr_t end;
|
||||
// Kernel root table (level 4)
|
||||
extern uint64_t *kernel_pml4;
|
||||
|
||||
/*
|
||||
* kheap_init - Kernel heap initialization
|
||||
*
|
||||
* This function physically allocates and maps enough pages
|
||||
* of memory for KHEAP_SIZE, which is defined in config.h.
|
||||
*
|
||||
* It then creates one big heap block, which will be the
|
||||
* base for a linked list.
|
||||
*/
|
||||
void kheap_init()
|
||||
{
|
||||
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
||||
@@ -53,6 +62,18 @@ void kheap_init()
|
||||
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* kmalloc - Kernel memory allocation
|
||||
* @size: number of bytes to allocate
|
||||
*
|
||||
* Looks for a big enough free block and marks it
|
||||
* as taken. Each block of memory is preceded by
|
||||
* the linked list header.
|
||||
*
|
||||
* Return:
|
||||
* <ptr> - Pointer to at least <size> bytes of usable memory
|
||||
* NULL - No more memory, or no valid size given
|
||||
*/
|
||||
void* kmalloc(size_t size)
|
||||
{
|
||||
// No size, no memory allocated!
|
||||
@@ -92,6 +113,14 @@ void* kmalloc(size_t size)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* kfree - Kernel memory freeing
|
||||
* @ptr: pointer to memory region to free
|
||||
*
|
||||
* Marks the memory block beginning at <ptr>
|
||||
* as free. Also merges adjacent free blocks
|
||||
* to lessen fragmentation.
|
||||
*/
|
||||
void kfree(void* ptr)
|
||||
{
|
||||
// Nothing to free
|
||||
@@ -113,8 +142,17 @@ void kfree(void* ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// Should alloc enough for a stack (at least 64kb) to be used for a process.
|
||||
// Should return a pointer to top of the stack (as stack grows DOWNWARDS)
|
||||
/*
|
||||
* kalloc_stack - Stack memory allocation
|
||||
*
|
||||
* Allocates a memory region of at least PROCESS_STACK_SIZE,
|
||||
* to be used as a stack for a process. The pointer returned
|
||||
* points to the end of the region, as the stack grows downwards.
|
||||
*
|
||||
* Return:
|
||||
* <ptr> - Pointer to a region after at least PROCESS_STACK_SIZE bytes of usable memory
|
||||
* NULL - No more memory
|
||||
*/
|
||||
void* kalloc_stack()
|
||||
{
|
||||
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
|
||||
|
||||
@@ -16,6 +16,19 @@
|
||||
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
||||
// do more optimization on them (and as it's a much used function, it's good to
|
||||
// be able to do that)
|
||||
|
||||
/*
|
||||
* memcpy - Copy memory from one place to another
|
||||
* @dest: pointer to the destination region
|
||||
* @src: pointer to the source region
|
||||
* @n: amount of bytes to copy
|
||||
*
|
||||
* This function copies n bytes of memory from
|
||||
* src to dest.
|
||||
*
|
||||
* Return:
|
||||
* <dest> - Pointer to destination region
|
||||
*/
|
||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
||||
{
|
||||
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
||||
@@ -28,6 +41,18 @@ void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
* memset - Sets a memory region to given byte
|
||||
* @s: pointer to memory region
|
||||
* @c: byte to be written
|
||||
* @n: amount of bytes to write
|
||||
*
|
||||
* This function writes n times the byte c
|
||||
* to the memory region pointed to by s.
|
||||
*
|
||||
* Return:
|
||||
* <s> - Pointer to memory region
|
||||
*/
|
||||
void* memset(void* s, int c, size_t n)
|
||||
{
|
||||
uint8_t* p = (uint8_t*)s;
|
||||
@@ -39,6 +64,18 @@ void* memset(void* s, int c, size_t n)
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* memmove - Move memory from one place to another
|
||||
* @dest: pointer to the destination region
|
||||
* @src: pointer to the source region
|
||||
* @n: amount of bytes to move
|
||||
*
|
||||
* This function moves n bytes of memory from
|
||||
* src to dest.
|
||||
*
|
||||
* Return:
|
||||
* <dest> - Pointer to destination region
|
||||
*/
|
||||
void* memmove(void *dest, const void* src, size_t n)
|
||||
{
|
||||
uint8_t* pdest = (uint8_t*)dest;
|
||||
@@ -56,6 +93,20 @@ void* memmove(void *dest, const void* src, size_t n)
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
* memcmp - Compare two memory regions
|
||||
* @s1: pointer to the first region
|
||||
* @s2: pointer to the second region
|
||||
* @n: amount of bytes to compare
|
||||
*
|
||||
* This function compares n bytes of memory
|
||||
* bewteen regions pointed to by s1 and s2.
|
||||
*
|
||||
* Return:
|
||||
* %0 - if s1 and s2 are equal
|
||||
* %-1 - if s1 is smaller than s2
|
||||
* %1 - if s1 is greater than s2
|
||||
*/
|
||||
int memcmp(const void* s1, const void* s2, size_t n)
|
||||
{
|
||||
const uint8_t* p1 = (const uint8_t*)s1;
|
||||
|
||||
@@ -24,17 +24,41 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
|
||||
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
||||
*/
|
||||
|
||||
/*
|
||||
* load_cr3 - Load a new value into the CR3 register
|
||||
* @value: the value to load
|
||||
*
|
||||
* This function is used to load the physical address
|
||||
* of the root page table (PML4), to switch the paging
|
||||
* structures the CPU sees and uses.
|
||||
*/
|
||||
void load_cr3(uint64_t value) {
|
||||
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||
}
|
||||
|
||||
// To flush TLB
|
||||
/*
|
||||
* invlpg - Invalidates a Translation Lookaside Buffer entry
|
||||
* @addr: page memory address
|
||||
*
|
||||
* This function is used to flush at least the TLB entrie(s)
|
||||
* for the page that contains the <addr> address.
|
||||
*/
|
||||
static inline void invlpg(void *addr)
|
||||
{
|
||||
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||
}
|
||||
|
||||
// Allocates a 512-entry 64bit page table/directory/whatever (zeroed)
|
||||
/*
|
||||
* alloc_page_table - Page table allocation
|
||||
*
|
||||
* This function allocates enough memory for a 512-entry
|
||||
* 64-bit page table, for any level (PML4/3/2).
|
||||
*
|
||||
* Memory allocated here is zeroed.
|
||||
*
|
||||
* Return:
|
||||
* <virt> - Pointer to allocated page table
|
||||
*/
|
||||
static uint64_t* alloc_page_table()
|
||||
{
|
||||
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
||||
@@ -49,10 +73,19 @@ static uint64_t* alloc_page_table()
|
||||
__attribute__((aligned(4096)))
|
||||
uint64_t *kernel_pml4;
|
||||
|
||||
// Map a page, taking virt and phys address. This will go through the paging structures
|
||||
// beginning at the given root table, translate the virtual address in indexes in
|
||||
// page table/directories, and then mapping the correct page table entry with the
|
||||
// given physical address + flags
|
||||
/*
|
||||
* paging_map_page - Mapping a memory page
|
||||
* @root_table: Address of the PML4
|
||||
* @virt: Virtual address
|
||||
* @phys: Physical address
|
||||
* @flags: Flags to set on page
|
||||
*
|
||||
* This function maps the physical address <phys> to the virtual
|
||||
* address <virt>, using the paging structures beginning at
|
||||
* <root_table>. <flags> can be set according to the PTE_FLAGS enum.
|
||||
*
|
||||
* If a page table/directory entry is not present yet, it creates it.
|
||||
*/
|
||||
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
||||
{
|
||||
virt = PAGE_ALIGN_DOWN(virt);
|
||||
@@ -102,6 +135,15 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
|
||||
uint64_t kernel_phys_base;
|
||||
uint64_t kernel_virt_base;
|
||||
|
||||
/*
|
||||
* paging_init - Paging initialization
|
||||
* @boot_ctx: Boot context structure
|
||||
*
|
||||
* This function initializes new paging structures, to replace
|
||||
* the ones given by the bootloader.
|
||||
*
|
||||
* It maps the kernel, the HHDM space, and the framebuffer.
|
||||
*/
|
||||
void paging_init(struct boot_context boot_ctx)
|
||||
{
|
||||
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
||||
|
||||
@@ -41,12 +41,15 @@ extern uint64_t hhdm_off;
|
||||
|
||||
// Page entry special bits
|
||||
// Bits set on a parent (directory, table) fall back to their children
|
||||
#define PTE_PRESENT (1ULL << 0)
|
||||
#define PTE_WRITABLE (1ULL << 1)
|
||||
#define PTE_USER (1ULL << 2)
|
||||
#define PTE_PWT (1ULL << 3)
|
||||
#define PTE_PCD (1ULL << 4)
|
||||
#define PTE_HUGE (1ULL << 7)
|
||||
#define PTE_NOEXEC (1ULL << 63)
|
||||
enum PTE_FLAGS
|
||||
{
|
||||
PTE_PRESENT = (1ULL << 0),
|
||||
PTE_WRITABLE = (1ULL << 1),
|
||||
PTE_USER = (1ULL << 2),
|
||||
PTE_PWT = (1ULL << 3),
|
||||
PTE_PCD = (1ULL << 4),
|
||||
PTE_HUGE = (1ULL << 7),
|
||||
PTE_NOEXEC = (1ULL << 63)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -24,13 +24,16 @@ First we'll have to discover the physical memory layout,
|
||||
and for that we can use a Limine request.
|
||||
*/
|
||||
|
||||
/*
|
||||
We will look for the biggest usable physical memory region
|
||||
and use this for the bitmap. The reserved memory will be ignored.
|
||||
*/
|
||||
|
||||
struct limine_memmap_entry* biggest_entry;
|
||||
|
||||
/*
|
||||
* pmm_find_biggest_usable_region - Finding the biggest free memory region
|
||||
* @memmap: Limine memory map
|
||||
* @hhdm: Limine HHDM offset
|
||||
*
|
||||
* This function uses the memory map provided by the bootloader
|
||||
* to find the single biggest free memory region we can use.
|
||||
*/
|
||||
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||
{
|
||||
// Max length of a usable memory region
|
||||
@@ -62,6 +65,14 @@ uint64_t hhdm_off;
|
||||
|
||||
static uintptr_t g_freelist = 0;
|
||||
|
||||
/*
|
||||
* pmm_alloc - Allocate a physical page
|
||||
*
|
||||
* This function allocates a single physical page (frame)
|
||||
*
|
||||
* Return:
|
||||
* <addr> - Address for the allocated page
|
||||
*/
|
||||
uintptr_t pmm_alloc()
|
||||
{
|
||||
if (!g_freelist) {
|
||||
@@ -72,12 +83,22 @@ uintptr_t pmm_alloc()
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* pmm_free - Frees a memory page
|
||||
* @addr: Address to the page
|
||||
*/
|
||||
void pmm_free(uintptr_t addr)
|
||||
{
|
||||
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
||||
g_freelist = addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* pmm_init_freelist - PMM freelist initialization
|
||||
*
|
||||
* This function marks the biggest memory region as
|
||||
* free, so we can use it in pmm_alloc.
|
||||
*/
|
||||
static void pmm_init_freelist()
|
||||
{
|
||||
// We simply call pmm_free() on each page that is marked USABLE
|
||||
@@ -93,6 +114,13 @@ static void pmm_init_freelist()
|
||||
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* pmm_init - Physical memory manager initialization
|
||||
* @boot_ctx: Boot context structure
|
||||
*
|
||||
* This function prepares the PMM for use.
|
||||
* The PMM works with a freelist.
|
||||
*/
|
||||
void pmm_init(struct boot_context boot_ctx)
|
||||
{
|
||||
hhdm_off = boot_ctx.hhdm->offset;
|
||||
|
||||
@@ -24,6 +24,14 @@ void* vmm_pt_root = 0;
|
||||
// Linked list head for virtual memory objects
|
||||
struct vm_object* vm_objs = NULL;
|
||||
|
||||
/*
|
||||
* Will have to be rewritten and expanded,
|
||||
* to prepare for userspace.
|
||||
* The platform-agnostic flags will be removed
|
||||
* because as long as the kernel is x86 only,
|
||||
* we don't need over complication.
|
||||
* Plus I don't plan to port to other architectures
|
||||
*/
|
||||
|
||||
uint64_t convert_x86_vm_flags(size_t flags)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user