memory #7
20
src/kmain.c
20
src/kmain.c
@@ -38,6 +38,13 @@ static volatile struct limine_hhdm_request hhdm_request = {
|
||||
.revision = 0
|
||||
};
|
||||
|
||||
// Executable Address/Kernel Address (find base phys/virt address of kernel)
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
static volatile struct limine_kernel_address_request kerneladdr_request = {
|
||||
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
||||
.revision = 0
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests_start")))
|
||||
static volatile LIMINE_REQUESTS_START_MARKER;
|
||||
|
||||
@@ -61,10 +68,12 @@ void kmain()
|
||||
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
||||
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) hcf();
|
||||
|
||||
// We should probably grab all the boot info in a boot context struct
|
||||
// that would be a bit cleaner than this mess
|
||||
|
||||
// Get the first framebuffer from the response
|
||||
framebuffer = framebuffer_request.response->framebuffers[0];
|
||||
|
||||
term_init();
|
||||
serial_init();
|
||||
|
||||
if (memmap_request.response == NULL) hcf();
|
||||
@@ -73,8 +82,8 @@ void kmain()
|
||||
if (hhdm_request.response == NULL) hcf();
|
||||
hhdm_display(hhdm_request.response);
|
||||
|
||||
pmm_init(memmap_request.response, hhdm_request.response);
|
||||
paging_init();
|
||||
if (kerneladdr_request.response == NULL) hcf();
|
||||
DEBUG("kernel: phys_base=0x%p virt_base=0x%p", kerneladdr_request.response->physical_base, kerneladdr_request.response->virtual_base);
|
||||
|
||||
CLEAR_INTERRUPTS;
|
||||
gdt_init();
|
||||
@@ -82,11 +91,16 @@ void kmain()
|
||||
timer_init();
|
||||
SET_INTERRUPTS;
|
||||
|
||||
pmm_init(memmap_request.response, hhdm_request.response);
|
||||
paging_init(kerneladdr_request.response, framebuffer);
|
||||
|
||||
keyboard_init(FR);
|
||||
|
||||
term_init();
|
||||
// Draw something
|
||||
printf("%s, %s!\n", "Hello", "world");
|
||||
// Yoohoooooo!
|
||||
//DEBUG("kernel initialized successfully! hanging... wow=%d", 42);
|
||||
printf("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non justo a magna bibendum auctor viverra rutrum diam. In hac habitasse platea dictumst. Vestibulum suscipit ipsum eget tortor maximus lobortis. Donec vel ipsum id lacus fringilla bibendum id eget risus. Fusce vestibulum diam sit amet nunc ultricies, nec rutrum nibh congue. Donec fringilla a dui sit amet ullamcorper. Donec pharetra quis tortor id congue. Aliquam erat volutpat. Duis suscipit nulla vel ligula iaculis, in gravida mauris pellentesque. Vestibulum nunc nisl, posuere eu eros et, dictum molestie dolor. Donec posuere laoreet hendrerit. Suspendisse potenti. Proin fringilla vehicula malesuada. Quisque a dui est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec aliquam lacus, at lacinia enim. ");
|
||||
hcf();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,156 @@
|
||||
#include "paging.h"
|
||||
#include "pmm.h"
|
||||
#include <kernel.h>
|
||||
#include <stddef.h>
|
||||
#include <limine.h>
|
||||
|
||||
void paging_init()
|
||||
/*
|
||||
Paging on x86 uses four different page table levels:
|
||||
cr3 register contains the phys address for the PML4 (root directory)
|
||||
|
||||
Each directory/table is made of 512 entries, each one uint64_t
|
||||
Each of these entries have special bits (PRESENT/WRITEABLE/USER/etc.)
|
||||
that dictates their attributes. Also these bits fall back on children tables.
|
||||
|
||||
If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
|
||||
2MB huge pages: PML4 -> PDPT -> PD -> 2mb pages
|
||||
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
||||
*/
|
||||
|
||||
static inline void load_cr3(uint64_t value) {
|
||||
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||
}
|
||||
|
||||
// To flush TLB
|
||||
static inline void invlpg(void *addr)
|
||||
{
|
||||
uint64_t cr3;
|
||||
asm volatile ("mov %%cr3, %0" : "=r"(cr3));
|
||||
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||
}
|
||||
|
||||
// Root directory address (cr3 without 12 less significant bits)
|
||||
uint64_t* pml4 = PHYS_TO_VIRT(cr3 & ~0xFFF);
|
||||
// Allocates a 512-entry 64bit page table/directory/whatever (zeroed)
|
||||
static uint64_t* alloc_page_table()
|
||||
{
|
||||
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
||||
|
||||
DEBUG("pml4=0x%p", pml4);
|
||||
for (size_t i=0; i<512; i++)
|
||||
{
|
||||
virt[i] = 0;
|
||||
}
|
||||
return virt;
|
||||
}
|
||||
|
||||
__attribute__((aligned(4096)))
|
||||
static uint64_t *kernel_pml4;
|
||||
|
||||
void map_page(uint64_t virt, uint64_t phys, uint64_t flags)
|
||||
{
|
||||
virt = PAGE_ALIGN_DOWN(virt);
|
||||
phys = PAGE_ALIGN_DOWN(phys);
|
||||
|
||||
// Translate the virt address into page table indexes
|
||||
uint64_t pml4_i = PML4_INDEX(virt);
|
||||
uint64_t pdpt_i = PDPT_INDEX(virt);
|
||||
uint64_t pd_i = PD_INDEX(virt);
|
||||
uint64_t pt_i = PT_INDEX(virt);
|
||||
|
||||
uint64_t *pdpt, *pd, *pt;
|
||||
|
||||
// PML4
|
||||
// If the entry at index is not present, allocate enough space for it
|
||||
// then populate the entry with correct addr + flags
|
||||
if (!(kernel_pml4[pml4_i] & PTE_PRESENT))
|
||||
{
|
||||
pdpt = alloc_page_table();
|
||||
kernel_pml4[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
|
||||
}
|
||||
else {
|
||||
pdpt = (uint64_t *)PHYS_TO_VIRT(kernel_pml4[pml4_i] & ~0xFFFULL);
|
||||
}
|
||||
|
||||
// PDPT: same here
|
||||
if (!(pdpt[pdpt_i] & PTE_PRESENT))
|
||||
{
|
||||
pd = alloc_page_table();
|
||||
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
|
||||
}
|
||||
else {
|
||||
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL);
|
||||
}
|
||||
|
||||
// PD: and here
|
||||
if (!(pd[pd_i] & PTE_PRESENT))
|
||||
{
|
||||
pt = alloc_page_table();
|
||||
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
|
||||
}
|
||||
else {
|
||||
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL);
|
||||
}
|
||||
|
||||
// PT: finally, populate the page table entry
|
||||
pt[pt_i] = phys | flags | PTE_PRESENT;
|
||||
|
||||
// Flush TLB (apply changes)
|
||||
invlpg((void *)virt);
|
||||
}
|
||||
|
||||
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb)
|
||||
{
|
||||
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
||||
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
|
||||
|
||||
uint64_t kernel_phys_base = kaddr->physical_base;
|
||||
uint64_t kernel_virt_base = kaddr->virtual_base;
|
||||
|
||||
kernel_pml4 = alloc_page_table();
|
||||
|
||||
// for debug
|
||||
uint64_t page_count = 0;
|
||||
|
||||
// First 16 MB identity-mapped (phys = virt)
|
||||
// This is because there might be some leftover stuff in the lower phys addresses
|
||||
// from boot/bios/acpi/...
|
||||
for (uint64_t i=0; i<0x1000000; i += PAGE_SIZE)
|
||||
{
|
||||
map_page(i, i, PTE_WRITABLE);
|
||||
page_count++;
|
||||
}
|
||||
DEBUG("Mapped %u pages for the identity-mapping of the first 16 MB", page_count); page_count = 0;
|
||||
|
||||
// HHDM map first 1 GB using given offset
|
||||
for (uint64_t i=0; i<0x40000000; i += PAGE_SIZE)
|
||||
{
|
||||
map_page(i+hhdm_off, i, PTE_WRITABLE);
|
||||
page_count++;
|
||||
}
|
||||
DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); page_count = 0;
|
||||
|
||||
// Map the kernel (according to virt/phys_base given by Limine)
|
||||
// SOME DAY when we want a safer kernel we should map .text as Read/Exec
|
||||
// .rodata as Read and .data as Read/Write
|
||||
// For now who gives a shit, let's RWX all kernel
|
||||
for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE)
|
||||
{
|
||||
map_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
||||
page_count++;
|
||||
}
|
||||
DEBUG("Mapped %u pages for kernel", page_count); page_count = 0;
|
||||
|
||||
// Get the framebuffer phys/virt address, and size
|
||||
uint64_t fb_virt = (uint64_t)fb->address;
|
||||
uint64_t fb_phys = VIRT_TO_PHYS(fb_virt);
|
||||
uint64_t fb_size = fb->pitch * fb->height;
|
||||
uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE;
|
||||
|
||||
// Map the framebuffer (with cache-disable & write-through)
|
||||
for (uint64_t i=0; i<fb_pages; i++)
|
||||
{
|
||||
map_page(fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
||||
page_count++;
|
||||
}
|
||||
DEBUG("Mapped %u pages for framebuffer", page_count);
|
||||
|
||||
// Finally, we load the physical address of our PML4 (root table) into cr3
|
||||
load_cr3(VIRT_TO_PHYS(kernel_pml4));
|
||||
DEBUG("cr3 loaded, we're still alive");
|
||||
}
|
||||
@@ -5,8 +5,9 @@
|
||||
#define BITS_PER_ROW 64
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limine.h>
|
||||
|
||||
void paging_init();
|
||||
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb);
|
||||
|
||||
extern uint64_t hhdm_off;
|
||||
|
||||
@@ -16,14 +17,27 @@ extern uint64_t hhdm_off;
|
||||
// Stole it
|
||||
#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
|
||||
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
|
||||
#define PAGE_ALIGN_DOWN(x) ((x) & ~0xFFFULL)
|
||||
|
||||
#define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
|
||||
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
|
||||
#define PD_INDEX(x) (((x) >> 21) & 0x1FF)
|
||||
#define PT_INDEX(x) (((x) >> 12) & 0x1FF)
|
||||
|
||||
// Page entry special bits
|
||||
// Bits set on a parent (directory, table) fall back to their children
|
||||
#define PTE_PRESENT (1ULL << 0)
|
||||
#define PTE_WRITE (1ULL << 1)
|
||||
#define PTE_USER (1ULL << 2)
|
||||
#define PTE_HUGE (1ULL << 7)
|
||||
#define PTE_NOEXEC (1ULL << 63)
|
||||
#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)
|
||||
|
||||
// Specified in linker.ld
|
||||
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
|
||||
|
||||
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
|
||||
#define KERNEL_SIZE 0x200000
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <limine.h>
|
||||
|
||||
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm);
|
||||
void pmm_free(uintptr_t addr);
|
||||
uintptr_t pmm_alloc();
|
||||
|
||||
// Might be upgraded to a freelist later.
|
||||
// For now, we can take the biggest usable region and we will be fine.
|
||||
|
||||
Reference in New Issue
Block a user