Compare commits

...

4 commits

Author SHA1 Message Date
7b4e4afd43 Defined a couple of functions, added documentation templates
Didn't feel the need to make a new branch for this one, but i added some
doxygen documentation templates to all of the headers. It's a bit
overdue.
2025-07-17 07:36:45 -04:00
6261af8e3a Some changes to names and a convenience ASM function
asm.h now can nuke the TLB by refreshing the PD.
also changed some symbols to more accurately represent what they are
for, such as the kernelspace page tables or the global PD.
2025-07-15 23:14:33 -04:00
e1369902f7 started fixing physmem. page fault when allocating. 2025-07-15 17:21:50 -04:00
Jake Holtham
20357559c1 The beginning of paging!
The kernel resides in the higher half, and as of now still identity maps the lower half for compatibility. Will not merge back to master until all the previous features work properly in virtual memory..
2025-07-15 14:52:48 -04:00
20 changed files with 466 additions and 139 deletions

View file

@ -3,4 +3,3 @@
i686-elf-gdb build/ukern.elf \
-ex "target remote localhost:1234" \
-ex "break start" \
-ex "continue"

View file

@ -1,16 +1,32 @@
/* asm.h
* Assembly wrappers
*
* Wrapper functions for using assembly files.
*/
/**
* @file asm.h
* @brief Assembly wrappers
* @author Jake "lordtet" Holtham
* @date 2025
* Wrapper functions for using assembly files.
*/
#ifndef ASM_H
#define ASM_H
#include <stdint.h>
#define COM1 0x3F8
#define COM2 0x2F8
/**
* @brief Wrapper for asm instruction `out` that feeds a byte.
* @param port I/O Address for the port to send to
* @param data Data to send
*/
void outb(uint16_t port, uint8_t data);
/**
* @brief Wrapper for asm instruction "in" that receives bytewise.
* @param port I/O Address for the port to listen from
* @return (uint8_t) The received byte.
*/
uint8_t inb(uint16_t port);
/**
* @brief Refresh the CR3 register.
* Moves CR3 into a scratch register and back into CR3. Usually used to nuke the TLB.
*/
void cr3_reload();
#endif

View file

@ -1,32 +1,62 @@
/* IDT.h
* Interrupt Descriptor Table
*
* IDT related structures, globals and functions
*/
/**
* @file idt.h
* @brief Interrupt Descriptor Table
* @author Jake "lordtet" Holtham
* @date 2025
* IDT related structures, globals and functions
*/
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
/**
* @brief IDT Entry
*/
typedef struct InterruptDescriptor_s {
uint16_t offset_lo;
uint16_t selector;
uint8_t reserved;
uint8_t attributes;
uint16_t offset_hi;
uint16_t offset_lo; /**< Lower 16 bits of ISR ptr */
uint16_t selector; /**< */
uint8_t reserved; /**< Nothing */
uint8_t attributes; /**< */
uint16_t offset_hi; /**< Upper 16 bits of ISR ptr */
} InterruptDescriptor_t;
/**
* @brief
*/
typedef struct IDTR_s {
uint16_t size;
InterruptDescriptor_t* IDT;
uint16_t size; /**< IDT Length */
InterruptDescriptor_t* IDT; /**< Pointer to the IDT Entry array */
} __attribute__((packed)) IDTR_t;
/**
* @brief The IDTR in memory.
*/
extern IDTR_t idtr;
/**
* @brief First IDT Entry
*/
extern InterruptDescriptor_t idt_start;
/**
* @brief Amount of ISRs implemented
*/
extern unsigned char num_interrupts;
/**
* @brief Call setup and write to construct and load the IDT.
*/
void setup_idt();
/**
* @brief Write the IDT and IDTR
*/
void write_descriptors();
/**
* @brief LIDT call
*/
extern void load_idt();
#endif

View file

@ -1,21 +1,31 @@
/* interrupt_handlers.h
* Interrupt Handlers
*
* Functions and structs for the C side of handling interrupts.
*/
/**
* @file interrupt_handlers.h
* @brief Interrupt Handlers
* @details Functions and structs for the C side of handling interrupts.
*/
#ifndef INTERRUPTHANDLERS_H
#define INTERRUPTHANDLERS_H
#include <stdint.h>
/**
* @brief
*/
extern void (*isr_ptrs[])(void);
//Struct is built "backwards" since it is pushed in this order.
/**
* @brief
* @details Struct is built "backwards" since it is pushed in this order.
*/
typedef struct StateSnapshot_s {
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t interrupt_id, error_code;
uint32_t eip, cs, eflags;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /**< @brief */
uint32_t interrupt_id, error_code; /**< @brief */
uint32_t eip, cs, eflags; /**< @brief */
} StateSnapshot_t;
/**
* @brief
* @param cpu_state
*/
void generic_isr_handler(StateSnapshot_t* cpu_state);
#endif

View file

@ -1,26 +1,78 @@
/* io.h
* In/Out
*
* Common library for handling user i/o. Aims to be mechanic agnostic, and instead
* focus on implementing generic functions to format and manipulate data.
*/
/**
* @file io.h
* @brief In/Out
* @details Common library for handling user i/o. Aims to be mechanic agnostic, and instead
* focus on implementing generic functions to format and manipulate data.
*/
#ifndef IO_H
#define IO_H
#include <stdint.h>
/**
* @brief
*/
typedef struct char_writer_s char_writer_t;
/**
* @brief
*/
struct char_writer_s {
int (*putChar)(void* ctx, char out);
void* ctx;
int (*putChar)(void* ctx, char out); /**< @brief */
void* ctx; /**< @brief */
};
/**
* @brief
*/
extern char_writer_t* default_output;
/**
* @brief
* @param
* @param
* @return int
*/
int putc(char_writer_t*, char);
/**
* @brief
* @param
* @param
* @return int
*/
int print(char_writer_t*, const char*);
/**
* @brief
* @param
* @param
* @return int
*/
int println(char_writer_t*, const char*);
/**
* @brief
* @param
* @param
* @return int
*/
int printhex(char_writer_t*, uint64_t);
/**
* @brief
* @param
* @param out
* @return int
*/
int prindec(char_writer_t*, uint64_t out);
/**
* @brief
* @param
* @param fmt
* @param ...
* @return int
*/
int printf(char_writer_t*, const char* fmt, ...);
#endif

View file

@ -1,35 +1,47 @@
/* kmultiboot.h
* Kernel Multiboot
*
* Data needed for multiboot compatibility.
*/
/**
* @file kmultiboot.h
* @brief Kernel Multiboot
* @details Data needed for multiboot compatibility.
*/
#ifndef KMULTIBOOT_H
#define KMULTIBOOT_H
#include <stdint.h>
/**
* @brief
*/
typedef struct multiboot_mmap_entry {
uint32_t entry_size; //size of this struct entry
uint64_t addr; //physical location of memory
uint64_t mem_size; //size of the memory block
uint32_t type;
uint32_t entry_size; /**< @brief size of this struct entry */
uint64_t addr; /**< @brief physical location of memory */
uint64_t mem_size; /**< @brief size of the memory block */
uint32_t type; /**< @brief */
} __attribute__((packed)) mb_mmap_entry_t;
/**
* @brief
*/
typedef struct mb_info_s {
uint32_t flags;
uint32_t mem_lower;
uint32_t mem_upper;
uint32_t boot_device;
uint32_t cmdline;
uint32_t mods_count;
uint32_t mods_addr;
uint32_t syms[4];
uint32_t mmap_length;
uint32_t mmap_addr;
uint32_t flags; /**< @brief */
uint32_t mem_lower; /**< @brief */
uint32_t mem_upper; /**< @brief */
uint32_t boot_device; /**< @brief */
uint32_t cmdline; /**< @brief */
uint32_t mods_count; /**< @brief */
uint32_t mods_addr; /**< @brief */
uint32_t syms[4]; /**< @brief */
uint32_t mmap_length; /**< @brief */
uint32_t mmap_addr; /**< @brief */
} mb_info_t;
/**
* @brief
*/
extern uint8_t _kernel_start;
/**
* @brief
*/
extern uint8_t _kernel_end;
#endif

View file

@ -1,8 +1,8 @@
/* kttools.h
* Kernel Type Tools
*
* Library for converting types and transcribing data.
*/
/**
* @file kttools.h
* @brief Kernel Type Tools
* @details Library for converting types and transcribing data.
*/
#ifndef KTTOOLS_H
#define KTTOOLS_H
#include <stdint.h>

View file

@ -1,11 +1,29 @@
/**
* @file physmem.h
* @brief
* @details
*/
#ifndef PHYSMEM_H
#define PHYSMEM_H
#include <stdint.h>
#include "kmultiboot.h"
/**
* @brief
*/
#define PAGE_SIZE 0x1000
/**
* @brief
*/
extern uint8_t* pmem_bitmap;
/**
* @brief
* @param mmap
* @param mmap_size
* @return unsigned int
*/
unsigned int build_bitmap(mb_mmap_entry_t* mmap, int mmap_size);
#endif

View file

@ -9,6 +9,11 @@
#include "io.h"
#include <stdint.h>
#define COM1 0x3F8
#define COM2 0x2F8
typedef struct serial_ctx_s {
uint16_t port;
} serial_ctx_t;

View file

@ -1,44 +1,64 @@
/* tss.h
* Task State Segment
*
* Structure definitions for the TSS
/**
* @file tss.h
* @brief Task State Segment (TSS) definitions and structures
* @author Jake Holtham
* @date 2025
*
* This header file contains the structure definitions for the x86 Task State Segment (TSS).
*/
#ifndef TSS_H
#define TSS_H
#include <stdint.h>
// TSS
/**
* @brief Task State Segment structure
*
* Data structure that contains information about a task,
* including the state of the processor registers, stack pointers, and other task-specific
* information. It is used by the x86 architecture for task switching and interrupt handling.
*
* Packed to make sure it's aligned and the correct size.
*
* @note This structure must be aligned on a 4byte boundary
*/
typedef struct tss_s {
uint32_t LINK;
uint32_t ESP0;
uint32_t SS0;
uint32_t ESP1;
uint32_t SS1;
uint32_t ESP2;
uint32_t SS2;
uint32_t CR3;
uint32_t EIP;
uint32_t EFLAGS;
uint32_t EAX;
uint32_t ECX;
uint32_t EDX;
uint32_t EBX;
uint32_t ESP;
uint32_t ESI;
uint32_t EDI;
uint32_t ES;
uint32_t CS;
uint32_t SS;
uint32_t DS;
uint32_t FS;
uint32_t GS;
uint32_t LDTR;
uint32_t IOPB;
uint32_t SSP;
uint32_t LINK; /**< Previous TSS selector*/
uint32_t ESP0; /**< Stack pointer for privilege level0*/
uint32_t SS0; /**< Stack segment for privilege level0*/
uint32_t ESP1; /**< Stack pointer for privilege level1*/
uint32_t SS1; /**< Stack segment for privilege level1*/
uint32_t ESP2; /**< Stack pointer for privilege level2*/
uint32_t SS2; /**< Stack segment for privilege level2*/
uint32_t CR3; /**< Page Directory register */
uint32_t EIP; /**< Instruction pointer */
uint32_t EFLAGS; /**< Flags register */
uint32_t EAX; /**< General purpose */
uint32_t ECX; /**< General purpose */
uint32_t EDX; /**< General purpose */
uint32_t EBX; /**< General purpose */
uint32_t ESP; /**< Stack pointer */
uint32_t ESI; /**< Source index register */
uint32_t EDI; /**< Destination index register */
uint32_t ES; /**< Extra segment register */
uint32_t CS; /**< Code segment register */
uint32_t SS; /**< Stack segment register */
uint32_t DS; /**< Data segment register */
uint32_t FS; /**< Extra segment register FS */
uint32_t GS; /**< Extra segment register GS */
uint32_t LDTR; /**< Local descriptor table register */
uint32_t IOPB; /**< I/O permission bitmap offset */
uint32_t SSP; /**< Stack segment pointer */
} __attribute__((packed)) tss_t;
/**
* @brief Global TSS instance
*
* This is the main TSS instance used by the kernel for task management.
* It contains the current task's state information.
* Defined in assembly.
*/
extern tss_t tss;
#endif
#endif /* TSS_H */

View file

@ -1,42 +1,71 @@
/* vga.h
* VGA
*
* Responsible for VGA buffer control.
/**
* @file vga.h
* @brief VGA
* @details Responsible for VGA buffer control.
*/
#ifndef VGA_H
#define VGA_H
/**
* @brief
*/
#define VGA_GRID_COLS 80
/**
* @brief
*/
#define VGA_GRID_ROWS 25
/**
* @brief
*/
#define DEFAULT_ATTRIBUTES 0x0F00
#include <stdint.h>
#include <stdarg.h>
#include "io.h"
/*
* CONSTANTS AND VARIABLES
*/
//Information on the VGA buffer
/**
* @brief Information on the VGA buffer
*/
extern volatile uint16_t* const vga_buffer;
/**
* @brief
*/
typedef struct vga_ctx_s {
int cursor_col;
int cursor_row;
uint16_t attributes;
int cursor_col; /**< @brief */
int cursor_row; /**< @brief */
uint16_t attributes; /**< @brief */
} vga_ctx_t;
//grid is top left origin. This is our cursor!
extern uint16_t vga_attributes; // Black background, White foreground
/**
* @brief grid is top left origin. This is our cursor! Black background, White foreground
*/
extern uint16_t vga_attributes;
/**
* @brief
*/
extern char_writer_t* default_vga;
/*
* Functions
/**
* @brief Clear the VGA buffer, context optional
* @param ctx
*/
//Clear the VGA buffer, context optional
void vga_clear_ctx(vga_ctx_t* ctx);
/**
* @brief
*/
void vga_clear();
//Write character c to cursor
//Implements IO generic ASCII output
/**
* @brief Write character c to cursor. Implements IO generic ASCII output
* @param ctx
* @param c
* @return int
*/
int vga_out(void* ctx, char c);

40
include/virtmem.h Normal file
View file

@ -0,0 +1,40 @@
/**
* @file virtmem.h
* @brief
* @details
*/
#ifndef VIRTMEM_H
#define VIRTMEM_H
#include <stdint.h>
/**
* @brief
*/
extern uint32_t global_page_dir;
/**
* @brief
*/
extern uint32_t kernel_pagetable;
/**
* @brief
* @param page_dir
* @param phys_loc
* @param virtual_loc
* @param page_opts
* @return int
*/
int map(uint32_t* page_dir, uint32_t phys_loc, uint32_t virtual_loc, uint8_t page_opts);
/**
* @brief
* @param phys_loc
* @param virtual_loc
* @param page_opts
* @return int
*/
int kmap(uint32_t phys_loc, uint32_t virtual_loc, uint8_t page_opts);
#endif

View file

@ -6,19 +6,31 @@ SECTIONS
/*start me at 1Mb because below that is x86 essential stuff, which we dont want to be written on top of.*/
. = 1M;
_kernel_start = .;
/* we're going to maintain 4K alignment - apparently useful for paging, and i'm not complaining about the lost space anyway. */
/*our multiboot header from start.s - has to go at the beginning of the executable so the bootloader knows we're loadable.*/
/*executable section*/
.text BLOCK(4K) : ALIGN(4K)
{
.mb_sect :
{
*(.multiboot)
}
.lower_text :
{
*(.lower_text)
}
/* Higher half addressing! We'll have to also specify that we want to be physically placed at 1MB.*/
. += 0xC0000000;
_kernel_start = .;
.text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000)
{
*(.text)
}
/*general read only data*/
.rodata BLOCK(4K) : ALIGN(4K)
.rodata ALIGN (4K) : AT(ADDR(.rodata) - 0xC0000000)
{
*(.rodata*)
*(.gdt_sect)
@ -26,16 +38,18 @@ SECTIONS
}
/*initialized rw data. */
.data BLOCK(4K) : ALIGN(4K)
.data ALIGN (4K) : AT(ADDR(.data) - 0xC0000000)
{
*(.data)
}
/*uninitialized data, and our stack as defined in start.s*/
.bss BLOCK(4K) : ALIGN(4K)
.bss ALIGN(4K) : AT(ADDR(.bss) - 0xC0000000)
{
*(COMMON)
*(.bss)
*(.bootstrap_tables)
*(.tss)
*(.idt)
}

View file

@ -1,4 +1,4 @@
global outb, inb
global outb, inb, invlpg
section .text
outb:
mov dx, [esp+4]
@ -11,3 +11,8 @@ section .text
mov dx, [esp+4]
in al, dx
ret
cr3_reload:
mov eax, cr3
mov cr3, eax
ret

View file

@ -55,5 +55,5 @@ section .gdt_sect
;size of the gdt
dw gdt_end - gdt - 1
;location of the gdt
dd gdt
dd gdt - 0xC0000000

View file

@ -7,6 +7,8 @@
#include "idt.h"
#include "serial.h"
#include "physmem.h"
void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info)
{
//Hello C! Let's get to work in cleaning up our environment a bit and creating some safety.
@ -38,6 +40,8 @@ void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info)
//AND OUR DEFAULT OUTPUT IS:
default_output = default_COM;
printf(default_output, "Output schemes loaded!\n");
printf(default_output, "currently executing in kern_main() at %X\n",kern_main);
printf(default_output, "Entry eax:%X\n", multiboot_magic);
if(multiboot_magic != 0x2BADB002) {
println(default_output, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!");
@ -56,10 +60,8 @@ void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info)
return;
}
unsigned int pages_allocated = build_bitmap((mb_mmap_entry_t*)multiboot_info->mmap_addr, multiboot_info->mmap_length);
printf(default_output, "Available mem:%d Pages | %dK", pages_allocated, pages_allocated * 4);
}

View file

@ -19,17 +19,13 @@ void free_page(void* addr) {
unsigned int build_bitmap(mb_mmap_entry_t* mmap, int mmap_size) {
//rather important global pointer to the bitmap
pmem_bitmap = &_kernel_end;
pmem_bitmap = (&_kernel_end);
//some variables
int mmap_end = (uint32_t)mmap + mmap_size;
unsigned int pages_allocated = 0;
uint32_t max_memory = 0;
//How much memory do we even have? Find the highest usable mmap region, and that'll be our bitmap size.
for(mb_mmap_entry_t* mmap_walk = mmap;
(int)mmap_walk < mmap_end;

View file

@ -5,6 +5,8 @@ bits 32
;Some symbols we'll need from other files...
extern kern_main
extern gdtr
extern _kernel_start
extern _kernel_end
;This will be our entrypoint function name - gotta initialize it now as global so the linker knows later.
global start
@ -17,21 +19,28 @@ section .bss align=16
;Therefore, we put a label here to represent the top of our stack for later.
stack_top:
;We're gonna throw the TSS here too.
section .tss align=16 nobits
tss:
resb 104
section .bootstrap_tables nobits align=4096
global_page_dir:
resb 4096
kernel_pagetable:
resb 4096
;Actual code. Entry point goes here!
section .text
section .lower_text exec
;Here it is!
start:
;We made a GDT! Let's use it!
lgdt [gdtr]
lgdt [gdtr - 0xC0000000]
;Now we start by setting the code segment (CS) with a far jump...
jmp 0x08:segment_be_gone
jmp 0x08:gdt_load
segment_be_gone:
gdt_load:
;Now we go ahead and dump our kernel mode data segment (0x10) into the rest of our segments.
;Also preserving eax because it has the bootloader's multiboot magic in it and I want to check it in main.
mov cx, 0x10
@ -40,7 +49,67 @@ section .text
mov ss, cx
mov fs, cx
mov gs, cx
;Lets set up the stack. Stack grows downward on x86. We did the work earlier of defining where the top of the stack is, so just tell esp.
;So, we want to set up a page table and page directory. Let's take inventory.
;eax - preserve, mb_magic
;ebx - preserve, mb_info
;ecx,edx,esi - can use
;Can technically use esp as the stack is set to stack_top later.
;ecx = our current indexing for pages, this is a 4K-aligned address
mov ecx, 0x0
;edi = our pointer to the current page table entry. Array of 4byte entries. It's at, of course, where we put it earlier.
;Worth noting that it's actually physically at ~1M with the kernel. The pointer, however, is virtually inclined. So we need to adjust.
mov edi, kernel_pagetable - 0xC0000000
page_alloc_loop:
;If we're lower than our kernel, skip this page. If we've passed our kernel, we're done mapping.
cmp ecx, _kernel_start
jl next_page
cmp ecx, _kernel_end - 0xC0000000
jge page_alloc_done
;Okay, we're in kernel zone. Let's map this one.
;Let's make a page table entry in edx.
;The top bits for the page table entry are just the upper bits of the address, so we dont need to touch it.
mov edx, ecx
;Set last two bits, "present" and "writeable". Later on we'll make a proper page table with proper permissions, but we're bootstrapping here.
or edx, 0x003
;edi holds a pointer to the page table entry, so dereference it and throw our new entry (in edx) to it. Done!
mov [edi], edx
next_page:
;address page + 1
add ecx, 0x1000
;page table entry + 1
add edi, 4
jmp page_alloc_loop
page_alloc_done:
;By now we've mapped the kernel in a page table! Let's map VGA too, we'll need that address later.
mov dword [kernel_pagetable - 0xC0000000 + 1023 * 4], 0x000B8000 | 0x27
;We have our page tables mapped to the phys addr of kernel + VGA. Time to add them to our page directory!
;First identity maps, second maps to higher half.
mov dword [global_page_dir - 0xC0000000], kernel_pagetable - 0xC0000000 + 0x003
mov dword [global_page_dir - 0xC0000000 + 4 * (0xC0000000 >> 22)], kernel_pagetable - 0xC0000000 + 0x003
;Let's set it! You do this with cr3.
mov ecx, global_page_dir - 0xC0000000
mov cr3, ecx
;Enable paging in cr0
mov ecx, cr0
or ecx, 0x80010000
mov cr0, ecx
;Done! Leave to the higher half!
lea ecx, setup_c_env
jmp ecx
section .text
setup_c_env:
;set up the stack. Stack grows downward on x86. We did the work earlier of defining where the top of the stack is, so just tell esp.
mov esp, stack_top ;set the stack pointer
push ebx
push eax

View file

@ -5,7 +5,7 @@
* VARS
*/
volatile uint16_t* const vga_buffer = (uint16_t*)0xB8000;
volatile uint16_t* const vga_buffer = (uint16_t*)0xC03FF000;
char_writer_t* default_vga = 0;
/*

10
src/virtmem.c Normal file
View file

@ -0,0 +1,10 @@
#include "virtmem.h"
int map(uint32_t* page_dir, uint32_t phys_loc, uint32_t virtual_loc, uint8_t page_opts) {
}
int kmap(uint32_t phys_loc, uint32_t virtual_loc, uint8_t page_opts){
return map(&global_page_dir, phys_loc, virtual_loc, page_opts);
}