Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kernel: memory): switch to a higher-half kernel #7

Merged
merged 6 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .gdbinit

This file was deleted.

52 changes: 43 additions & 9 deletions kernel/arch/i686/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,71 @@
*/
ENTRY(_kernel_start)

_kernel_physical_start = 0x00100000;
_kernel_higher_half_offset = 0xC0000000;
_kernel_virtual_start = (_kernel_physical_start + _kernel_higher_half_offset);

SECTIONS
{
/*
* Starting from #6 we now use a higher-half kernel.
*
* The load address of the kernel is different from the relocation address (AT).
* We run the code as if in higher-half, but is should in fact be loaded at the
* beginning of the RAM to be detected by the bootloader.
*
* We still need to run the piece of code that jumps to our main function,
* that is why we have 2 different sections for the code:
* * .text.startup: should be identically mapped (load address == relocation address)
* * .text: should be loaded as higher half
*
* The code inside the .text.startup section can be discarded once we jumped to our
* main C function, we shall never get back to it anyway.
*
* For more information, refer to: https://wiki.osdev.org/Higher_Half_x86_Bare_Bones
*/

/* We place our kernel at 1MiB, it's a conventional place for it to be here. */
. = 1M;
. = ALIGN(4K);
. = _kernel_physical_start;

/* Code needed to bootstrap our kernel, cannot be remmaped */
.startup ALIGN(4K) :
{
KEEP(*(.multiboot))
*(.text.startup)
}

/* Everything after this is relocated to the end of the address space (> 3GiB) and needs paging to be activated */

. = ALIGN(4K);
. += _kernel_higher_half_offset;

kernel_code_start_address = .;
_kernel_code_start = .;

.text BLOCK(4K) : ALIGN(4K)
.text ALIGN(4K) : AT (ADDR (.text) - _kernel_higher_half_offset)
{
*(.multiboot)
*(.text)
}

/* Read-only data */
.rodata BLOCK(4K) : ALIGN(4K)
.rodata ALIGN(4K) : AT (ADDR (.rodata) - _kernel_higher_half_offset)
{
*(.rodata)
}

/* Initailized data (RW) */
.data BLOCK(4K) : ALIGN(4K)
/* Initialized data (RW) */
.data ALIGN(4K) : AT (ADDR (.data) - _kernel_higher_half_offset)
{
*(.data)
}

/* Uninitialized data and stack (RW) */
.bss BLOCK(4K) : ALIGN(4K)
.bss ALIGN(4K) : AT (ADDR (.bss) - _kernel_higher_half_offset)
{
*(COMMON)
*(.bss)
}

kernel_code_end_address = .;
_kernel_code_end = .;
}
58 changes: 45 additions & 13 deletions kernel/arch/i686/memory/mmu.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#include <kernel/logger.h>
#include <kernel/memory.h>
#include <kernel/mmu.h>
#include <kernel/pmm.h>

#include <stdbool.h>
#include <stddef.h>
#include <utils/align.h>
#include <utils/compiler.h>
#include <utils/cpu_ops.h>
#include <utils/macro.h>
Expand Down Expand Up @@ -61,6 +62,10 @@ typedef struct PACKED {
static __attribute__((__aligned__(PAGE_SIZE)))
mmu_pde_entry kernel_page_directory[MMU_PDE_COUNT];

// Keep track whether paging has been fully enabled.
// This doesn't count temporarily enabling it to jump into higher half.
static bool paging_enabled = false;

static inline void mmu_flush_tlb(u32 tlb_entry)
{
ASM("invlpg (%0)" ::"r"(tlb_entry) : "memory");
Expand All @@ -75,7 +80,8 @@ static inline void mmu_flush_tlb(u32 tlb_entry)
// way to set its contentt to be that of the currently running process.
bool mmu_start_paging(void)
{
static void *page_directory = kernel_page_directory;
static void *page_directory =
(void *)KERNEL_HIGHER_HALF_PHYSICAL(kernel_page_directory);

// Set CR3 to point to our page directory
write_cr3((u32)page_directory);
Expand All @@ -89,9 +95,18 @@ bool mmu_start_paging(void)
u32 cr0 = read_cr0();
write_cr0(BIT_SET(cr0, 31)); // PG = bit 32

paging_enabled = true;

return true;
}

/// @brief Remap an address range given an offset.
///
/// example:
/// mmu_offset_map(0x0000, 0x00FF, 0xFF) will remap the physical range
/// [0x0000; 0x00FF] to the virtual range [0xFF00; 0xFFFF]
static void mmu_offset_map(uint32_t start, uint32_t end, int64_t offset);

bool mmu_init(void)
{
log_info("MMU", "Initializing MMU");
Expand All @@ -105,10 +120,17 @@ bool mmu_init(void)
// @link https://medium.com/@connorstack/recursive-page-tables-ad1e03b20a85
kernel_page_directory[MMU_PDE_COUNT - 1].present = 1;
kernel_page_directory[MMU_PDE_COUNT - 1].page_table =
MMU_PAGE_ADDRESS(kernel_page_directory);
MMU_PAGE_ADDRESS(KERNEL_HIGHER_HALF_PHYSICAL(kernel_page_directory));

// For more simplicity, we identity map the content of our kernel.
mmu_identity_map(align(KERNEL_CODE_START, PAGE_SIZE), KERNEL_CODE_END);
// We remap our higher-half kernel.
// The addresses over 0xC0000000 will point to our kernel's code (0x00000000
// in physical)
mmu_offset_map(KERNEL_HIGHER_HALF_PHYSICAL(KERNEL_CODE_START),
KERNEL_HIGHER_HALF_PHYSICAL(KERNEL_CODE_END),
KERNEL_HIGHER_HALF_OFFSET);

// FIXME: Check conflict with PPM
// FIXME: Is it really necessary ?
// We also map the first 1M of physical memory, it will be reserved for
// hardware structs.
mmu_identity_map(0x0, 0x100000);
Expand All @@ -121,6 +143,8 @@ bool mmu_map(u32 virtual, u32 pageframe)
u16 pde_index = virtual >> 22; // bits 31-22
u16 pte_index = (virtual >> 12) & ((1 << 10) - 1); // bits 21-12

// TODO: ASSERT alignment

// TODO: Hard-coded to work with kernel page tables only
// This should take the pagedir/pagetables as input later on
//
Expand All @@ -135,12 +159,15 @@ bool mmu_map(u32 virtual, u32 pageframe)
}

// Virtual address of the corresponding page table (physical if CR0.PG=0)
bool paging_enabled = BIT(read_cr0(), 31);
mmu_pte_entry *page_table =
(mmu_pte_entry *)(paging_enabled
? MMU_RECURSIVE_PAGE_TABLE_ADDRESS(pde_index)
: (kernel_page_directory[pde_index].page_table
<< 12));
mmu_pte_entry *page_table;

if (paging_enabled) {
page_table =
(mmu_pte_entry *)MMU_RECURSIVE_PAGE_TABLE_ADDRESS(pde_index);
} else {
page_table = (mmu_pte_entry *)KERNEL_HIGHER_HALF_VIRTUAL(
kernel_page_directory[pde_index].page_table << 12);
}

if (page_table[pte_index].present) {
log_err("MMU",
Expand Down Expand Up @@ -180,9 +207,14 @@ void mmu_unmap(u32 virtual)
mmu_flush_tlb(virtual);
}

void mmu_identity_map(uint32_t start, uint32_t end)
static void mmu_offset_map(uint32_t start, uint32_t end, int64_t offset)
{
for (; start < end; start += PAGE_SIZE) {
mmu_map(start, start);
mmu_map(start + offset, start);
}
}

void mmu_identity_map(uint32_t start, uint32_t end)
{
mmu_offset_map(start, end, 0);
}
74 changes: 74 additions & 0 deletions kernel/include/kernel/memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/** @file memory.h
*
* Constants related to the kernel's memory layout.
*/

#ifndef KERNEL_MEMORY_H
#define KERNEL_MEMORY_H

#ifndef KERNEL_STACK_SIZE
#define KERNEL_STACK_SIZE 0x4000
#endif

// NOTE: This maybe belong inside the arch directory ? (cf. #5)

// The size of a single page
#define PAGE_SIZE (4096)

// 32-bit address bus -> 4GiB of addressable memory
#define ADDRESS_SPACE_SIZE (0x100000000UL)
#define ADDRESS_SPACE_END (ADDRESS_SPACE_SIZE - 1)

/// Starting from #6, our kernel uses the higher-half design.
///
/// This means that the kernel code's virtual address differs from its physical
/// one. The following constants are used by the linker script and should be
/// used by the functions that depend on them instead of hardcoding values.

#define KERNEL_IS_HIGHER_HALF 1

// TODO: These values are duplicated inside the linkerscript
// We should maybe find a way to include this header before linking the
// kernel to avoid conflicts

#define KERNEL_PHYSICAL_START 0x00100000UL
#define KERNEL_HIGHER_HALF_OFFSET 0xC0000000UL
#define KERNEL_VIRTUAL_START (KERNEL_PHYSICAL_START + KERNEL_HIGHER_HALF_OFFSET)

#ifdef __ASSEMBLER__

#define KERNEL_HIGHER_HALF_PHYSICAL(_virtual) \
((_virtual)-KERNEL_HIGHER_HALF_OFFSET)
#define KERNEL_HIGHER_HALF_VIRTUAL(_physical) \
((_physical) + KERNEL_HIGHER_HALF_OFFSET)

#else

#define KERNEL_HIGHER_HALF_PHYSICAL(_virtual) \
((u32)(_virtual)-KERNEL_HIGHER_HALF_OFFSET)
#define KERNEL_HIGHER_HALF_VIRTUAL(_physical) \
((u32)(_physical) + KERNEL_HIGHER_HALF_OFFSET)

#include <utils/types.h>

/// @brief Address of the byte located just before the end of the kernel's code
///
/// Any byte written after this address **WILL** overwrite our kernel's
/// executable binary.
///
/// @info this address is defined inside the kernel's linker scrpit.
extern u32 _kernel_code_start;
#define KERNEL_CODE_START ((u32)&_kernel_code_start)

/// @brief Address of the byte located just after the end of the kernel's code
///
/// Any byte written after this address will not overwrite our kernel's
/// executable binary.
///
/// @info this address is defined inside the kernel's linker scrpit.
extern u32 _kernel_code_end;
#define KERNEL_CODE_END ((u32)&_kernel_code_end)

#endif /* __ASSEMBLER__ */

#endif /* KERNEL_MEMORY_H */
27 changes: 2 additions & 25 deletions kernel/include/kernel/pmm.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,12 @@
#ifndef KERNEL_PMM_H
#define KERNEL_PMM_H

#include <kernel/memory.h>

#include <multiboot.h>
#include <stdbool.h>
#include <utils/types.h>

// The size of a single page
#define PAGE_SIZE (4096)

// 32-bit address bus -> 4GiB of addressable memory
#define ADDRESS_SPACE_SIZE (0x100000000UL)
#define ADDRESS_SPACE_END (ADDRESS_SPACE_SIZE - 1)

// Error value returned by the PMM in case of errors
#define PMM_INVALID_PAGEFRAME (0xFFFFFFFFUL)

Expand All @@ -39,24 +34,6 @@
// reference value (e.g. the physical memory manager's bit map size).
#define TOTAL_PAGEFRAMES_COUNT (ADDRESS_SPACE_SIZE / PAGE_SIZE)

/// @brief Address of the byte located just after the end of the kernel's code
///
/// Any byte written after this address will not overwrite our kernel's
/// executable binary.
///
/// @info this address is defined inside the kernel's linker scrpit.
extern u32 kernel_code_end_address;
#define KERNEL_CODE_END ((u32)&kernel_code_end_address)

/// @brief Address of the byte located just before the end of the kernel's code
///
/// Any byte written after this address **WILL** overwrite our kernel's
/// executable binary.
///
/// @info this address is defined inside the kernel's linker scrpit.
extern u32 kernel_code_start_address;
#define KERNEL_CODE_START ((u32)&kernel_code_start_address)

/// \defgroup pmm_allocation_flags
///
/// Flags used when allocating a page frame to specify that the allocation must
Expand Down
1 change: 0 additions & 1 deletion kernel/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
arch = target_machine.cpu()

kernel_compile_flags = [
'-DSTACK_SIZE=16384',
'-fomit-frame-pointer',
]

Expand Down
Loading
Loading