5 Commits

16 changed files with 384 additions and 49 deletions

View File

@@ -1,8 +1,10 @@
SOURCES = src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c
SOURCES = src/sched/scheduler.c src/sched/process.c src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c
PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable
build:
rm -f *.o
x86_64-elf-gcc -g -c -Isrc $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel
x86_64-elf-gcc -g -c -Isrc $(SOURCES) $(PROBLEMATIC_FLAGS) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel
objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o
nasm -f elf64 src/idt/idt.S -o idt_stub.o
x86_64-elf-ld -o pepperk -T linker.ld *.o

View File

@@ -11,18 +11,28 @@ To run it with QEMU, `make run`
The basics that I'm targeting are:
### Basic utility of what we call a "kernel"
- Fix terminal driver (backspace issues, scrolling) OR add Flanterm or equivalent
- Implement paging / see what Limine does at boot with memory management
- Implement tasks, and task switching
- Load an executable
- Scheduler (round-robin using the PIT timer interrupt)
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
- Getting to userspace (syscalls)
- Porting musl libc or equivalent
In the future, maybe?
### Scalability/maintenance/expansion features
- SMP support
- Global config header file
- Documentation
- SOME error handling in functions
- Unit tests
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
- Make the panic function work within itself without dependencies + error message (and still get cpu context?)
### Optional features
In the future, maybe?
- SMP support (Limine provides functionality to make this easier)
- Parsing the ACPI tables and using them for something
- Replacing the PIT timer with APIC

View File

@@ -5,6 +5,7 @@
#include "io/kbd/ps2.h"
#include <kernel.h>
#include <stdbool.h>
#include "sched/scheduler.h"
struct interrupt_descriptor idt[256];
struct idtr idt_reg;
@@ -187,6 +188,14 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
case 32:
//DEBUG("Timer Interrupt");
ticks++;
if (ticks % SCHEDULER_QUANTUM == 0)
{
CLEAR_INTERRUPTS;
scheduler_schedule();
SET_INTERRUPTS;
}
// Send an EOI so that we can continue having interrupts
outb(0x20, 0x20);
break;

View File

@@ -36,4 +36,7 @@ struct boot_context
struct limine_kernel_address_response* kaddr;
};
// 1 tick = 1 ms => quantum = 10ms
#define SCHEDULER_QUANTUM 10
#endif

View File

@@ -14,6 +14,8 @@
#include "mem/paging/paging.h"
#include "mem/paging/vmm.h"
#include "mem/heap/kheap.h"
#include "sched/process.h"
#include "sched/scheduler.h"
// Limine version used
__attribute__((used, section(".limine_requests")))
@@ -56,6 +58,7 @@ static volatile LIMINE_REQUESTS_END_MARKER;
// Panic (should dump registers etc. in the future)
void hcf()
{
//CLEAR_INTERRUPTS;
for (;;)
{
asm("hlt");
@@ -64,7 +67,7 @@ void hcf()
void panic(struct cpu_status_t* ctx)
{
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m at rip=%p\nSomething went horribly wrong! vect=0x%.2x errcode=0x%x\nrax=%p rbx=%p rcx=%p rdx=%p\nrsi=%p rdi=%p r8=%p r9=%p\nr10=%p r11=%p r12=%p r13=%p\nr14=%p r15=%p\n\nflags=%p\nstack at rbp=%p\nHalting...",
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m at rip=%p\nSomething went horribly wrong! vect=0x%.2x errcode=0x%x\n\rrax=%p rbx=%p rcx=%p rdx=%p\n\rrsi=%p rdi=%p r8=%p r9=%p\n\rr10=%p r11=%p r12=%p r13=%p\n\rr14=%p r15=%p\n\n\rflags=%p\n\rstack at rbp=%p\n\rHalting...",
ctx->iret_rip,
ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi,
ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags,
@@ -76,6 +79,24 @@ const char* splash = "pepperOS version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSIO
struct boot_context boot_ctx;
extern struct process_t* processes_list;
extern struct process_t* current_process;
void pedicel_main(void* arg)
{
}
void two_main(void* arg)
{
}
void three_main(void* arg)
{
}
// This is our entry point
void kmain()
{
@@ -97,7 +118,6 @@ void kmain()
gdt_init();
idt_init();
timer_init();
SET_INTERRUPTS;
pmm_init(boot_ctx.mmap, boot_ctx.hhdm);
@@ -106,23 +126,20 @@ void kmain()
kheap_init();
void* ptr = kmalloc(10); DEBUG("(KMALLOC TEST) Allocated 10 bytes at 0x%p", ptr);
void* ptr2 = kmalloc(200); DEBUG("(KMALLOC TEST) Allocated 200 bytes at 0x%p", ptr2);
kfree(ptr);
void* ptr3 = kmalloc(5); DEBUG("(KMALLOC TEST) Allocated 5 bytes at 0x%p", ptr3);
vmm_init();
keyboard_init(FR);
struct process_t* pedicel = process_create("pedicel", (void*)pedicel_main, 0);
struct process_t* two = process_create("two", (void*)two_main, 0);
struct process_t* three = process_create("three", (void*)three_main, 0);
process_display_list(processes_list);
scheduler_init();
SET_INTERRUPTS;
keyboard_init(FR);
term_init();
kputs(splash);
for (int i=0; i<10; i++)
{
printf("testing, attention please %d\n", i);
}
// term_scroll();
hcf();
}

View File

@@ -3,6 +3,7 @@
#include "mem/paging/pmm.h"
#include <stddef.h>
#include <kernel.h>
#include "sched/process.h"
extern uint64_t kernel_phys_base;
extern uint64_t kernel_virt_base;
@@ -15,12 +16,24 @@ static uintptr_t end;
// Kernel root table (level 4)
extern uint64_t *kernel_pml4;
static void kheap_map_page()
static void kheap_grow(size_t size)
{
size_t pages = ALIGN_UP(size + sizeof(struct heap_block_t), PAGE_SIZE) / PAGE_SIZE;
if (pages == 0) pages = 1;
for (size_t i = 0; i < pages; i++)
{
kheap_map_page();
}
}
void kheap_map_page()
{
uintptr_t phys = pmm_alloc();
paging_map_page(kernel_pml4, end, phys, PTE_PRESENT | PTE_WRITABLE | PTE_NOEXEC);
end += PAGE_SIZE;
DEBUG("Mapped first kheap page");
//DEBUG("Mapped first kheap page");
}
void kheap_init()
@@ -43,6 +56,7 @@ void* kmalloc(size_t size)
{
// No size, no memory allocated!
if (!size) return NULL;
size = ALIGN(size);
struct heap_block_t* curr = head;
@@ -52,17 +66,16 @@ void* kmalloc(size_t size)
if (curr->free && curr->size >= size)
{
// We split the block if it is big enough
if (curr->size > size + sizeof(struct heap_block_t))
if (curr->size >= size + BLOCK_MIN_SIZE)
{
struct heap_block_t* new_block = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
// We have to subtract the size of our block struct
new_block->size = curr->size - size - sizeof(struct heap_block_t);
new_block->free = true;
//struct heap_block_t* new_block = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
struct heap_block_t* split = (struct heap_block_t*)((uintptr_t)curr + sizeof(*curr) + size);
// Then we chain up the block in the list
new_block->next = curr->next;
curr->next = new_block;
split->size = curr->size - size - sizeof(*curr);
split->free = true;
split->next = curr->next;
curr->next = split;
curr->size = size;
}
@@ -74,14 +87,14 @@ void* kmalloc(size_t size)
curr = curr->next;
}
// If we're hear it means we didn't have enough memory
// If we're here it means we didn't have enough memory
// for the block allocation. So we will allocate more..
uintptr_t old_end = end;
kheap_map_page();
kheap_grow(size + sizeof(struct heap_block_t));
struct heap_block_t* block = (struct heap_block_t*)old_end;
block->size = PAGE_SIZE - sizeof(struct heap_block_t);
block->free = false;
block->size = ALIGN_UP(end - old_end - sizeof(struct heap_block_t), 16);
block->free = true;
block->next = NULL;
// Put the block at the end of the list
@@ -92,7 +105,7 @@ void* kmalloc(size_t size)
}
curr->next = block;
return (void*)((uintptr_t)block + sizeof(struct heap_block_t));
return kmalloc(size);
}
void kfree(void* ptr)
@@ -103,4 +116,25 @@ void kfree(void* ptr)
// Set it free!
struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t));
block->free = true;
// merge adjacent free blocks (coalescing)
struct heap_block_t* curr = head;
while (curr && curr->next)
{
if (curr->free && curr->next->free)
{
curr->size += sizeof(*curr) + curr->next->size;
curr->next = curr->next->next;
continue;
}
curr = curr->next;
}
}
// 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)
void* kalloc_stack()
{
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE);
return ptr ? ptr+PROCESS_STACK_SIZE : NULL;
}

View File

@@ -22,5 +22,7 @@ struct heap_block_t
void kheap_init();
void* kmalloc(size_t size);
void kfree(void* ptr);
void* kalloc_stack();
void kheap_map_page();
#endif

View File

@@ -69,7 +69,7 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
}
else {
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & ~0xFFFULL);
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & PTE_ADDR_MASK);
}
// PDPT: same here
@@ -79,7 +79,7 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
}
else {
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL);
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
}
// PD: and here
@@ -89,7 +89,7 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
}
else {
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL);
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
}
// PT: finally, populate the page table entry
@@ -102,13 +102,16 @@ 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;
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb)
extern struct boot_context boot_ctx;
void paging_init()
{
// We should map the kernel, GDT, IDT, stack, framebuffer.
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
kernel_phys_base = kaddr->physical_base;
kernel_virt_base = kaddr->virtual_base;
kernel_phys_base = boot_ctx.kaddr->physical_base;
kernel_virt_base = boot_ctx.kaddr->virtual_base;
struct limine_framebuffer* fb = boot_ctx.fb;
DEBUG("Kernel lives at virt=0x%p phys=0x%p", kernel_virt_base, kernel_phys_base);
@@ -117,14 +120,38 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra
// for debug
uint64_t page_count = 0;
// HHDM map first 1 GB using given offset
for (uint64_t i=0; i<0x40000000; i += PAGE_SIZE)
// Find max physical address from limine memmap
uint64_t max_phys = 0;
for (uint64_t i=0; i<boot_ctx.mmap->entry_count; i++)
{
struct limine_memmap_entry* entry = boot_ctx.mmap->entries[i];
if (entry->length == 0)
{
continue;
}
uint64_t top = entry->base + entry->length;
if (top > max_phys)
{
max_phys = top;
}
//DEBUG("max_phys=0x%p", max_phys);
}
// 4GB
if (max_phys > 0x100000000)
{
DEBUG("WARNING: max_phys capped to 4GB (0x100000000) (from max_phys=%p)", max_phys);
max_phys = 0x100000000;
}
// HHDM map up to max_phys or 4GB, whichever is smaller, using given offset
for (uint64_t i=0; i<max_phys; i += PAGE_SIZE)
{
//paging_kmap_page(i+hhdm_off, i, PTE_WRITABLE);
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE);
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE | PTE_PRESENT);
page_count++;
}
DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); page_count = 0;
DEBUG("Mapped %u pages up to 0x%p (HHDM)", page_count, max_phys); 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

View File

@@ -6,8 +6,9 @@
#include <stdint.h>
#include <limine.h>
#include "mem/heap/kheap.h"
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb);
void paging_init();
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
extern uint64_t hhdm_off;
@@ -15,10 +16,14 @@ extern uint64_t hhdm_off;
#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off))
#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off)
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000
// 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 PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK)
#define ALIGN(size) ALIGN_UP(size, 16)
#define BLOCK_MIN_SIZE (sizeof(struct heap_block_t) + 16)
#define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)

122
src/sched/process.c Normal file
View File

@@ -0,0 +1,122 @@
#include <stddef.h>
#include "process.h"
#include "mem/heap/kheap.h"
#include "kernel.h"
#include "string/string.h"
#include "mem/gdt/gdt.h"
struct process_t* processes_list;
struct process_t* current_process;
size_t next_free_pid = 0;
void process_init()
{
processes_list = NULL;
current_process = NULL;
}
// Only for debug
void process_display_list(struct process_t* processes_list)
{
int process_view_id = 0;
struct process_t* tmp = processes_list;
while (tmp != NULL)
{
DEBUG("{%d: %p} -> ", process_view_id, tmp);
tmp = tmp->next;
process_view_id++;
}
DEBUG("NULL");
}
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
{
CLEAR_INTERRUPTS;
struct process_t* proc = (struct process_t*)kmalloc(sizeof(struct process_t));
struct cpu_status_t* ctx = (struct cpu_status_t*)kmalloc(sizeof(struct cpu_status_t));
// No more memory?
if (!proc) return NULL;
if (!ctx) return NULL;
strncpy(proc->name, name, PROCESS_NAME_MAX);
proc->pid = next_free_pid++;
proc->status = READY;
proc->context = ctx;
proc->context->iret_ss = KERNEL_DATA_SEGMENT; // process will live in kernel mode
proc->context->iret_rsp = (uint64_t)kalloc_stack();
proc->context->iret_flags = 0x202; //bit 2 and 9 set (Interrupt Flag)
proc->context->iret_cs = KERNEL_CODE_SEGMENT;
proc->context->iret_rip = (uint64_t)function; // beginning of executable code
proc->context->rdi = (uint64_t)arg; // 1st arg is in rdi (as per x64 calling convention)
proc->context->rbp = 0;
proc->next = 0;
process_add(&processes_list, proc);
SET_INTERRUPTS;
return proc;
}
void process_add(struct process_t** processes_list, struct process_t* process)
{
if (!process) return;
process->next = NULL;
if (*processes_list == NULL)
{
// List is empty
*processes_list = process;
return;
}
struct process_t* tmp = *processes_list;
while (tmp->next != NULL)
{
tmp = tmp->next;
}
// We're at last process before NULL
tmp->next = process;
// process->next = NULL;
}
void process_delete(struct process_t** processes_list, struct process_t* process)
{
if (!processes_list || !*processes_list || !process) return;
if (*processes_list == process)
{
// process to delete is at head
*processes_list = process->next;
process->next = NULL;
kfree(process);
return;
}
struct process_t* tmp = *processes_list;
while (tmp->next && tmp->next != process)
{
tmp = tmp->next;
}
if (tmp->next == NULL)
{
// Didn't find the process
return;
}
// We're at process before the one we want to delete
tmp->next = process->next;
process->next = NULL;
kfree(process);
}
struct process_t* process_get_next(struct process_t* process)
{
if (!process) return NULL;
return process->next;
}

34
src/sched/process.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef PROCESS_H
#define PROCESS_H
#include <stddef.h>
typedef enum
{
READY,
RUNNING,
DEAD
} status_t;
#define PROCESS_NAME_MAX 64
#define PROCESS_STACK_SIZE 0x10000 // 64kb
struct process_t
{
size_t pid;
char name[PROCESS_NAME_MAX];
status_t status;
struct cpu_status_t* context;
struct process_t* next;
};
void process_init();
struct process_t* process_create(char* name, void(*function)(void*), void* arg);
void process_add(struct process_t** processes_list, struct process_t* process);
void process_delete(struct process_t** processes_list, struct process_t* process);
struct process_t* process_get_next(struct process_t* process);
void process_display_list(struct process_t* processes_list);
#endif

40
src/sched/scheduler.c Normal file
View File

@@ -0,0 +1,40 @@
#include "kernel.h"
#include "process.h"
extern struct process_t* processes_list;
extern struct process_t* current_process;
void scheduler_init()
{
// Choose first process?
current_process = processes_list;
}
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
{
current_process->context = context;
current_process->status = READY;
for (;;) {
struct process_t* prev_process = current_process;
if (current_process->next != NULL)
{
current_process = current_process->next;
} else
{
current_process = processes_list;
}
if (current_process != NULL && current_process->status == DEAD)
{
process_delete(&prev_process, current_process);
} else
{
current_process->status = RUNNING;
break;
}
}
DEBUG("SCHEDULER CALLED: current_process=%p", current_process);
return current_process->context;
}

7
src/sched/scheduler.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef SCHEDULER_H
#define SCHEDULER_H
void scheduler_schedule();
void scheduler_init();
#endif

View File

@@ -1,6 +1,26 @@
#include <stddef.h>
char* strcpy(char *dest, const char *src)
{
char *temp = dest;
while((*dest++ = *src++));
return temp;
}
// https://stackoverflow.com/questions/2488563/strcat-implementation
char *strcat(char *dest, const char *src){
size_t i,j;
for (i = 0; dest[i] != '\0'; i++)
;
for (j = 0; src[j] != '\0'; j++)
dest[i+j] = src[j];
dest[i+j] = '\0';
return dest;
}
// https://stackoverflow.com/questions/14159625/implementation-of-strncpy
void strncpy(char* dst, const char* src, size_t n)
{
size_t i = 0;
while(i++ != n && (*dst++ = *src++));
}

View File

@@ -2,5 +2,7 @@
#define STRING_H
char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
void strncpy(char* dst, const char* src, size_t n);
#endif

1
test.bin Normal file
View File

@@ -0,0 +1 @@
<EFBFBD>ュ゙<EFBFBD>