physical memory management and started usermode

This commit is contained in:
rami 2024-05-24 18:53:28 -05:00
parent e1b6525a7e
commit 8b58e8663f
13 changed files with 463 additions and 21 deletions

View File

@ -15,6 +15,7 @@ KERNELIMG := $(BUILDDIR)/kernel.bin
QEMUFLAGS = -d int -s \
-kernel $(KERNELIMG) \
-m 512M \
-device piix3-ide,id=ide -drive id=disk,file=image.img,format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 \
.PHONY: all kernel qemu clean docs

12
idt.c
View File

@ -1,4 +1,6 @@
#include <idt.h>
#include <vga.h>
#include <printf.h>
void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t segment_selector, uint8_t attributes) {
idt[i].offset_lo = (uint16_t)(offset & 0xFFFF);
@ -7,3 +9,13 @@ void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t s
idt[i].attributes = attributes;
idt[i].offset_hi = (uint16_t)(offset >> 16);
}
void exception_handler(struct registers regs) {
clear_screen();
printf("Exception 0x%X (0x%X) has occurred\nEAX: 0x%08X\nECX: 0x%08X\nEDX: 0x%08X\nEBX: 0x%08X\nESI: 0x%08X\nEDI: 0x%08X\nEBP: 0x%08X\nESP: 0x%08X\n",
regs.int_no, regs.err_code, regs.eax, regs.ecx, regs.edx, regs.ebx, regs.esi, regs.edi, regs.ebp, regs.esp+24);
printf("\nEIP: 0x%08X\nCS: 0x%08X\nEFLAGS: 0x%08X",
regs.eip, regs.cs, regs.eflags);
}

View File

@ -1 +0,0 @@
SKIBIDI ANDEW PINGAS BALLS ANDEW

34
include/fat12.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef RK_FAT12_H_
#define RK_FAT12_H_
#include <stdint.h>
struct fat12_boot_sector {
uint8_t jump_boot[3]; // Jump instruction to boot code
uint8_t oem_name[8]; // OEM name and version
uint16_t bytes_per_sector; // Bytes per sector
uint8_t sectors_per_cluster; // Sectors per cluster
uint16_t reserved_sectors; // Reserved sectors
uint8_t num_fats; // Number of FATs
uint16_t root_entry_count; // Number of root directory entries
uint16_t total_sectors_16; // Total sectors (16-bit)
uint8_t media; // Media descriptor
uint16_t fat_size_16; // Sectors per FAT (16-bit)
uint16_t sectors_per_track; // Sectors per track (for BIOS)
uint16_t num_heads; // Number of heads (for BIOS)
uint32_t hidden_sectors; // Hidden sectors
uint32_t total_sectors_32; // Total sectors (32-bit)
// Extended Boot Record (EBR) fields
uint8_t drive_number; // Drive number
uint8_t reserved1; // Reserved
uint8_t boot_signature; // Extended boot signature
uint32_t volume_id; // Volume ID (serial number)
uint8_t volume_label[11]; // Volume label
uint8_t file_system_type[8]; // File system type (e.g., "FAT12 ")
uint8_t boot_code[448]; // Boot code
uint16_t boot_sector_signature; // Boot sector signature (0xAA55)
} __attribute__((packed));
#endif

View File

@ -1,9 +1,50 @@
#ifndef RK_GDT_H_
#define RK_GDT_H_
#include <stdint.h>
#define GDT_NULL 0
#define GDT_KERNEL_CODE 1
#define GDT_KERNEL_DATA 2
#define GDT_SEGMENT_SELECTOR(i, p) ((i << 3) | p)
struct gdt_entry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
struct tss_entry {
uint32_t prev_tss; // The previous TSS - with hardware task switching this is used
uint32_t esp0; // The stack pointer to load when we change to kernel mode
uint32_t ss0; // The stack segment to load when we change to kernel mode
uint32_t esp1; // Unused...
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 ebp;
uint32_t esi;
uint32_t edi;
uint32_t es; // The value to load into ES when we change to kernel mode
uint32_t cs; // The value to load into CS when we change to kernel mode
uint32_t ss; // The value to load into SS when we change to kernel mode
uint32_t ds; // The value to load into DS when we change to kernel mode
uint32_t fs; // The value to load into FS when we change to kernel mode
uint32_t gs; // The value to load into GS when we change to kernel mode
uint32_t ldt; // Unused...
uint16_t trap;
uint16_t iomap_base;
} __attribute__((packed));
#endif

View File

@ -36,7 +36,17 @@ enum IDT_ATTRIBUTES {
INT_PRESENT = 0x80,
};
struct registers {
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */
uint32_t eip;
uint32_t cs;
uint32_t eflags;
};
void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t segment_selector, uint8_t attributes);
void flush_idt(uint32_t idt_pointer);
void exception_handler(struct registers regs);
#endif

22
include/kernel.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef RK_KERNEL_H_
#define RK_KERNEL_H_
#include <multiboot.h>
#include <acpi.h>
struct kernel_context {
struct rsdp *rsdp;
struct xsdp *xsdp;
struct rsdt *rsdt;
struct mcfg *mcfg;
uint32_t multi_mmap_size;
struct multiboot_mmap_entry *multi_mmap;
uint32_t available_bytes;
uint32_t mmap_size;
uint32_t *mmap;
};
#endif

20
include/mem.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef RK_MEM_H_
#define RK_MEM_H_
#include <kernel.h>
#include <stdint.h>
#define KERNEL_START 0x1000000 // 1 MiB
#define KERNEL_SIZE 0xC800 // 50 KiB
#define BLOCK_SIZE 4096
#define BITS 32
uint8_t mmap_init(struct kernel_context *ctx);
void mmap_set_block(struct kernel_context *ctx, int block);
void mmap_free_block(struct kernel_context *ctx, int block);
void *mmap_find_first_free_block(struct kernel_context *ctx);
void *mmap_block_to_physical(struct kernel_context *ctx, int block);
void *memset(void *s, int c, uint32_t len);
#endif

View File

@ -28,4 +28,19 @@ struct multiboot_info {
uint32_t vbeInterfaceLength;
};
struct multiboot_mmap_entry
{
uint32_t size;
uint32_t addr_low;
uint32_t addr_high;
uint32_t len_low;
uint32_t len_high;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
uint32_t type;
} __attribute__((packed));
#endif

111
kmain.c
View File

@ -1,4 +1,4 @@
#include "ide.h"
#include <kernel.h>
#include <multiboot.h>
#include <acpi.h>
#include <vga.h>
@ -9,33 +9,112 @@
#include <idt.h>
#include <pic8259.h>
#include <ps2.h>
struct kernel_context {
struct rsdp *rsdp;
struct xsdp *xsdp;
struct rsdt *rsdt;
};
#include <ide.h>
#include <mem.h>
#include <fat12.h>
struct kernel_context ctx = {0};
struct idt_entry g_idt[256] = {0};
struct idtr g_idtr = {0};
extern void exception_handler();
extern uint32_t isr_stub_table[32];
extern void ps2_isr();
#define MAGIC_BREAKPOINT __asm__ volatile("xchgw %bx, %bx");
#define USER_DS 0x23 // User data segment selector
#define USER_CS 0x1B // User code segment selector
extern struct gdt_entry gdt[6];
struct tss_entry tss;
void tss_flush(uint16_t tss_selector) {
asm volatile (
"ltr %0"
: // no output
: "r" (tss_selector)
);
}
void test() {
printf("\n\nHello from user mode!\n");
for (;;) {}
}
void switch_to_user_mode(void *user_stack, void *user_entry) {
asm volatile (
"movl $0x23, %%eax \n" // User data segment selector
"mov %%ax, %%ds \n"
"mov %%ax, %%es \n"
"mov %%ax, %%fs \n"
"mov %%ax, %%gs \n"
"pushl $0x23 \n" // User stack segment
"pushl %0 \n" // User stack pointer
"pushfl \n" // Push EFLAGS
"popl %%eax \n"
"orl $0x200, %%eax \n" // Enable interrupts (set IF)
"pushl %%eax \n"
"pushl $0x1B \n" // User code segment
"pushl %1 \n" // User entry point
"iret \n" // Interrupt return (switches to user mode)
:
: "r"(user_stack), "r"(user_entry)
: "eax"
);
}
void write_tss(int num, uint16_t ss0, uint32_t esp0) {
uint32_t base = (uint32_t)&tss;
uint32_t limit = base + sizeof(tss);
gdt[num].base_low = base & 0xFFFF;
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = limit & 0xFFFF;
gdt[num].granularity = (limit >> 16) & 0x0F;
gdt[num].granularity |= 0x40;
gdt[num].access = 0x89;
memset(&tss, 0, sizeof(tss));
tss.ss0 = ss0;
tss.esp0 = esp0;
tss.cs = 0x0b;
tss.ss = tss.ds = tss.es = tss.fs = tss.gs = 0x13;
}
void kmain(struct multiboot_info *info) {
clear_screen();
set_color(VGA_COLOR_CYAN);
// Check if the bootloader gave us the upper and lower memory
if (!(info->flags & 0x1)) goto halt;
ctx.multi_mmap = (struct multiboot_mmap_entry *)info->memMapAddress;
ctx.multi_mmap_size = info->memMapLength / sizeof(struct multiboot_mmap_entry);
for (uint32_t i = 0; i < ctx.multi_mmap_size; i++) {
struct multiboot_mmap_entry entry = ctx.multi_mmap[i];
if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue;
uint32_t len = (uint32_t)(entry.len_low | entry.len_high);
uint32_t addr = (uint32_t)(entry.addr_low | entry.addr_high);
printf("Region #%d: 0x%08X-0x%08X\n", i, addr, addr+len);
}
if (!mmap_init(&ctx)) {
set_color(VGA_COLOR_RED);
printf("Failed to create memory map\n");
goto halt;
}
printf("Detected %d bytes of memory\n", ctx.available_bytes);
// Create and load an IDT
for (int i = 0; i < 32; i++) {
encode_idt_entry(g_idt, i, (uint32_t)&exception_handler, GDT_SEGMENT_SELECTOR(GDT_KERNEL_CODE, 0x00), TRAP_GATE_32 | INT_RING0 | INT_PRESENT);
encode_idt_entry(g_idt, i, isr_stub_table[i], GDT_SEGMENT_SELECTOR(GDT_KERNEL_CODE, 0x00), TRAP_GATE_32 | INT_RING0 | INT_PRESENT);
}
g_idtr.size = (sizeof(struct idt_entry) * 256) - 1;
@ -119,7 +198,17 @@ void kmain(struct multiboot_info *info) {
}
set_color(VGA_COLOR_WHITE);
printf("\nYou are now being dropped into a kernel shell\n$ ");
void *buf = mmap_find_first_free_block(&ctx);
ata_read_sector(ATA_PRIMARY, ATA_MASTER, 0, (uint8_t *)buf);
// struct fat12_boot_sector *boot_sect = (struct fat12_boot_sector *)buf;
// int fat_size = boot_sect->total_sectors_16 * boot_sect->bytes_per_sector;
write_tss(5, 0x10, 0x0);
tss_flush(0x2B);
void *stack_bottom = mmap_find_first_free_block(&ctx);
void *stack_top = stack_bottom + 4096;
switch_to_user_mode(stack_top, buf);
halt:
for (;;) {}
}

121
mem.c Normal file
View File

@ -0,0 +1,121 @@
#include <mem.h>
#include <printf.h>
// TODO: Fix this, it wastes memory when switching regions because the previous offset isnt being subtracted
// Should work fine for now?
void *mmap_block_to_physical(struct kernel_context *ctx, int block) {
uint32_t offset = block * BLOCK_SIZE;
for (uint32_t i = 0; i < ctx->multi_mmap_size; i++) {
struct multiboot_mmap_entry entry = ctx->multi_mmap[i];
if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue;
uint32_t len = (uint32_t)(entry.len_low | entry.len_high);
uint32_t addr = (uint32_t)(entry.addr_low | entry.addr_high);
if (len > offset)
return (void *)(addr + offset);
}
return 0;
}
void *memset(void *s, int c, uint32_t len) {
unsigned char *dst = s;
while (len > 0) {
*dst = (unsigned char) c;
dst++;
len--;
}
return s;
}
uint8_t mmap_init(struct kernel_context *ctx) {
// Calculate the number of bytes available to us
for (uint32_t i = 0; i < ctx->multi_mmap_size; i++) {
struct multiboot_mmap_entry entry = ctx->multi_mmap[i];
if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue;
uint32_t len = (uint32_t)(entry.len_low | entry.len_high);
ctx->available_bytes += len;
}
ctx->mmap_size = ctx->available_bytes / BLOCK_SIZE / 8;
printf("Memory map size: %d bytes\n", ctx->mmap_size);
// Loop again to find the first region with enough space to hold the memory map
int index = -1;
for (uint32_t i = 0; i < ctx->multi_mmap_size; i++) {
struct multiboot_mmap_entry entry = ctx->multi_mmap[i];
if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue;
uint32_t len = (uint32_t)(entry.len_low | entry.len_high);
if (len > ctx->mmap_size) {
index = i;
break;
}
}
if (index == -1) return 0; // Failed
// Create map
struct multiboot_mmap_entry entry = ctx->multi_mmap[index];
uint32_t addr = (uint32_t)(entry.addr_low | entry.addr_high);
ctx->mmap = (uint32_t *)addr;
// Zero the map
memset(ctx->mmap, 0, ctx->mmap_size);
// Reserve the blocks that hold the memory map + 1
uint32_t blocks_to_set = ctx->mmap_size / BLOCK_SIZE + 1;
for (uint32_t i = 0; i < blocks_to_set; i++)
mmap_set_block(ctx, i);
// Reserve the kernel space
int bits = ctx->mmap_size * 8;
for (int i = 0; i < bits; i++) {
uint32_t phys = (uint32_t)mmap_block_to_physical(ctx, i);
if (phys > KERNEL_START && phys < KERNEL_START + KERNEL_SIZE)
mmap_set_block(ctx, i);
}
return 1;
}
void mmap_set_block(struct kernel_context *ctx, int block) {
int index = block / BITS;
int bit = block;
if (block >= BITS)
bit -= BITS;
uint32_t mask = 1 << bit;
if (!(ctx->mmap[index] & mask))
ctx->mmap[index] |= mask;
}
void mmap_free_block(struct kernel_context *ctx, int block) {
int index = block / BITS;
int bit = block;
if (block >= BITS)
bit -= BITS;
uint32_t mask = 1 << bit;
if (ctx->mmap[index] & mask)
ctx->mmap[index] ^= mask;
}
void *mmap_find_first_free_block(struct kernel_context *ctx) {
int bits = ctx->mmap_size * 8;
for (int i = 0; i < bits; i++) {
int index = i / BITS;
int bit = i;
if (i >= BITS)
bit -= BITS;
uint32_t mask = 1 << bit;
uint32_t dword = ctx->mmap[index];
if (!(dword & mask)) {
return mmap_block_to_physical(ctx, i);
}
}
return 0;
}

View File

@ -1,5 +1,5 @@
%define MAGIC 0x1BADB002
%define FLAGS 0b100
%define FLAGS 0b110
; Multiboot v1 Specification
; https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
@ -49,13 +49,74 @@ _start:
halt:
hlt
jmp halt
global exception_handler
exception_handler:
add esp, 4
mov byte [0xb8000], 'X'
iret
extern exception_handler
%macro isr_err_stub 1
isr_stub_%+%1:
cli
push %1
pusha
call exception_handler
popa
add esp, 8
iret
%endmacro
%macro isr_no_err_stub 1
isr_stub_%+%1:
cli
push 0
push %1
pusha
call exception_handler
popa
add esp, 12
iret
%endmacro
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
isr_no_err_stub 4
isr_no_err_stub 5
isr_no_err_stub 6
isr_no_err_stub 7
isr_err_stub 8
isr_no_err_stub 9
isr_err_stub 10
isr_err_stub 11
isr_err_stub 12
isr_err_stub 13
isr_err_stub 14
isr_no_err_stub 15
isr_no_err_stub 16
isr_err_stub 17
isr_no_err_stub 18
isr_no_err_stub 19
isr_no_err_stub 20
isr_no_err_stub 21
isr_no_err_stub 22
isr_no_err_stub 23
isr_no_err_stub 24
isr_no_err_stub 25
isr_no_err_stub 26
isr_no_err_stub 27
isr_no_err_stub 28
isr_no_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
global isr_stub_table
isr_stub_table:
%assign i 0
%rep 32
dd isr_stub_%+i
%assign i i+1
%endrep
global ps2_isr
ps2_isr:
cli
extern ps2_handler
pushad
cld
@ -66,7 +127,8 @@ ps2_isr:
; GDT for a flat memory layout
; We get access to all memory and can utilize paging
gdt_start:
global gdt
gdt:
gdt_null:
dq 0
gdt_kern_code:
@ -83,11 +145,27 @@ gdt_kern_data:
db 0b10010010
db 0b11001111
db 0x00
gdt_user_code:
dw 0xffff
dw 0x0000
db 0x00
db 0b11111010
db 0b11001111
db 0x00
gdt_user_data:
dw 0xffff
dw 0x0000
db 0x00
db 0b11110010
db 0b11001111
db 0x00
tss:
dq 0
gdt_end:
gdtp:
dw gdt_end - gdt_start - 1
dd gdt_start
dw gdt_end - gdt - 1
dd gdt
; Reserve 16KiB of kernel stack space