Compare commits
32 Commits
834891fd2a
...
flanterm
| Author | SHA1 | Date | |
|---|---|---|---|
|
9df33b49d8
|
|||
|
1f055ab31c
|
|||
|
70f19ab299
|
|||
|
9470dedb61
|
|||
|
4cf4fb0dda
|
|||
|
ac7216d84a
|
|||
|
458ba375f3
|
|||
| b920c87bab | |||
|
4fbd9b3987
|
|||
|
8aad1235c3
|
|||
|
38710653be
|
|||
|
7f997f6611
|
|||
|
7bb542d901
|
|||
|
4a90de9521
|
|||
|
c46157fad0
|
|||
| 6e633b44b7 | |||
|
b8a155fada
|
|||
|
091f94f89e
|
|||
|
b469952d91
|
|||
|
9cbecc1689
|
|||
|
12ab12f1b2
|
|||
|
0f72987bc1
|
|||
|
d9dfd4c749
|
|||
| be1be41a64 | |||
|
923758a4ea
|
|||
|
e18b73c8a0
|
|||
|
c065df6ff3
|
|||
|
bb5fb9db33
|
|||
|
075058a958
|
|||
|
05a862e97a
|
|||
| 8f5e2eae3e | |||
|
cf4915d9f4
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,3 +7,6 @@ iso_root
|
|||||||
*.gch
|
*.gch
|
||||||
*/*.gch
|
*/*.gch
|
||||||
*/*/*.gch
|
*/*/*.gch
|
||||||
|
.gdb_history
|
||||||
|
symbols.map
|
||||||
|
symbols.S
|
||||||
24
DOOM.txt
Normal file
24
DOOM.txt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
up to doom:
|
||||||
|
|
||||||
|
- Return from pedicel_main() normally (to idle)
|
||||||
|
|
||||||
|
** Checkpoint: ring0 process working
|
||||||
|
|
||||||
|
- VFS layer (open/read/write/...) with USTar filesystem (for initrd)
|
||||||
|
|
||||||
|
** Checkpoint: files not linked to but accessible by the kernel
|
||||||
|
|
||||||
|
- Ring3 memory mappings
|
||||||
|
- Ring3 privilege switch
|
||||||
|
|
||||||
|
** Checkpoint: welcome to userland
|
||||||
|
|
||||||
|
- Syscall interface
|
||||||
|
- Implement syscalls needed for doom
|
||||||
|
|
||||||
|
** Checkpoint: can run simple programs, ring 3, loaded from filesystem
|
||||||
|
|
||||||
|
- Properly handle the keyboard interrupt (keyboard buffer)
|
||||||
|
- Port DOOMgeneric (few functions with Framebuffer/ticks/etc.)
|
||||||
|
|
||||||
|
** Achievement: It runs doom!
|
||||||
21
Makefile
21
Makefile
@@ -1,10 +1,17 @@
|
|||||||
SOURCES = 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/io/term/flanterm_backends/fb.c src/io/term/flanterm.c src/debug/panic.c src/debug/stacktrace.c src/boot/boot.c 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:
|
build:
|
||||||
rm -f *.o
|
rm -f *.o
|
||||||
x86_64-elf-gcc -g -c -I src $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -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
|
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
|
nasm -f elf64 src/idt/idt.S -o idt_stub.o
|
||||||
|
nasm -f elf64 src/entry.S -o entry.o
|
||||||
|
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
||||||
|
nm -n pepperk | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
|
||||||
|
python3 symbols.py
|
||||||
|
nasm -f elf64 symbols.S -o symbols.o
|
||||||
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
||||||
|
|
||||||
limine/limine:
|
limine/limine:
|
||||||
@@ -30,11 +37,15 @@ build-iso: limine/limine build
|
|||||||
./limine/limine bios-install pepper.iso
|
./limine/limine bios-install pepper.iso
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot &
|
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
|
||||||
gdb pepperk --command=debug.gdb
|
gdb pepperk --command=debug.gdb
|
||||||
|
|
||||||
|
debug2:
|
||||||
|
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
|
||||||
|
pwndbg pepperk --command=debug.gdb
|
||||||
|
|
||||||
run: build-iso
|
run: build-iso
|
||||||
qemu-system-x86_64 -cdrom pepper.iso -serial stdio
|
/usr/bin/qemu-system-x86_64 -cdrom pepper.iso -serial stdio
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o pepperk iso_root pepper.iso limine
|
rm -rf *.o symbols.map symbols.S pepperk iso_root pepper.iso limine
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -2,11 +2,39 @@
|
|||||||
|
|
||||||
## Trying the kernel
|
## Trying the kernel
|
||||||
|
|
||||||
First install the dependencies: `sudo apt install xorriso make qemu-system`
|
First install the dependencies: `sudo apt install python3 xorriso make qemu-system`
|
||||||
|
|
||||||
Then, to compile the kernel and make an ISO image file: `make build-iso`
|
Then, to compile the kernel and make an ISO image file: `make build-iso`
|
||||||
To run it with QEMU, `make run`
|
To run it with QEMU, `make run`
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
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 tasks, and task switching + context switching and spinlock acquire/release
|
||||||
|
- Load an executable
|
||||||
|
- 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
|
||||||
|
|
||||||
|
### Scalability/maintenance/expansion features
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
PepperOS wouldn't be possible without the following freely-licensed software:
|
PepperOS wouldn't be possible without the following freely-licensed software:
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
target remote localhost:1234
|
target remote localhost:1234
|
||||||
set disassembly-flavor intel
|
set disassembly-flavor intel
|
||||||
display/8i $rip
|
display/4i $rip
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
OUTPUT_FORMAT(elf64-x86-64)
|
OUTPUT_FORMAT(elf64-x86-64)
|
||||||
|
|
||||||
ENTRY(kmain)
|
ENTRY(_start)
|
||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
|
|||||||
41
src/boot/boot.c
Normal file
41
src/boot/boot.c
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Limine requests for boot
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limine.h>
|
||||||
|
|
||||||
|
// Framebuffer request
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_framebuffer_request framebuffer_request = {
|
||||||
|
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Memory map request
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_memmap_request memmap_request = {
|
||||||
|
.id = LIMINE_MEMMAP_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Higher Half Direct Map
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_hhdm_request hhdm_request = {
|
||||||
|
.id = LIMINE_HHDM_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Executable Address/Kernel Address (find base phys/virt address of kernel)
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_kernel_address_request kerneladdr_request = {
|
||||||
|
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests_start")))
|
||||||
|
volatile LIMINE_REQUESTS_START_MARKER;
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests_end")))
|
||||||
|
volatile LIMINE_REQUESTS_END_MARKER;
|
||||||
38
src/config.h
Normal file
38
src/config.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PepperOS configuration file
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
/* version */
|
||||||
|
#define PEPPEROS_VERSION_MAJOR "0"
|
||||||
|
#define PEPPEROS_VERSION_MINOR "0"
|
||||||
|
#define PEPPEROS_VERSION_PATCH "58"
|
||||||
|
#define PEPPEROS_SPLASH "pepperOS version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\n"
|
||||||
|
|
||||||
|
/* process */
|
||||||
|
#define PROCESS_NAME_MAX 64
|
||||||
|
#define PROCESS_STACK_SIZE 0x10000 // 64kb
|
||||||
|
#define PROCESS_BASE 0x400000
|
||||||
|
#define PROCESS_STACK_BASE 0x1000000
|
||||||
|
|
||||||
|
/* sched */
|
||||||
|
// 1 tick = 1 ms => quantum = 10ms
|
||||||
|
#define SCHEDULER_QUANTUM 10
|
||||||
|
|
||||||
|
/* kernel */
|
||||||
|
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
|
||||||
|
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
|
||||||
|
#define KERNEL_SIZE 0x200000
|
||||||
|
#define KERNEL_STACK_SIZE 65536
|
||||||
|
|
||||||
|
/* heap */
|
||||||
|
#define KHEAP_SIZE (16*1024*1024)
|
||||||
|
|
||||||
|
/* term */
|
||||||
|
#define TERM_HISTORY_MAX_LINES 256
|
||||||
|
|
||||||
|
#endif
|
||||||
25
src/debug/panic.c
Normal file
25
src/debug/panic.c
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include "idt/idt.h"
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
void panic(struct cpu_status_t* ctx, const char* str)
|
||||||
|
{
|
||||||
|
CLEAR_INTERRUPTS;
|
||||||
|
if (ctx == NULL)
|
||||||
|
{
|
||||||
|
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m Something went horribly wrong! (no cpu ctx)");
|
||||||
|
DIE_DEBUG(str);
|
||||||
|
skputc('\r');
|
||||||
|
skputc('\n');
|
||||||
|
DEBUG("\x1b[38;5;231m\x1b[48;5;196mend Kernel panic - halting...\x1b[0m");
|
||||||
|
hcf();
|
||||||
|
}
|
||||||
|
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m at rip=%p\r\nSomething went horribly wrong! (%s) 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\rHalting...",
|
||||||
|
ctx->iret_rip,
|
||||||
|
str,
|
||||||
|
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);
|
||||||
|
debug_stack_trace(100);
|
||||||
|
hcf();
|
||||||
|
}
|
||||||
75
src/debug/stacktrace.c
Normal file
75
src/debug/stacktrace.c
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
void debug_stack_trace(unsigned int max_frames)
|
||||||
|
{
|
||||||
|
DEBUG("*** begin stack trace ***");
|
||||||
|
// Thanks GCC :)
|
||||||
|
uintptr_t* rbp = (uintptr_t*)__builtin_frame_address(0);
|
||||||
|
|
||||||
|
for (unsigned int frame=0; frame<max_frames && rbp != NULL; frame++)
|
||||||
|
{
|
||||||
|
// Return address, 1 word above saved rbp
|
||||||
|
uintptr_t rip = rbp[1];
|
||||||
|
uintptr_t offset = 0;
|
||||||
|
const char* name = debug_find_symbol(rip, &offset);
|
||||||
|
DEBUG("[%u] <0x%p> (%s+0x%x)", frame, (void*)rip, name, offset);
|
||||||
|
|
||||||
|
uintptr_t* next_rbp = (uintptr_t*)rbp[0];
|
||||||
|
|
||||||
|
// invalid rbp or we're at the end
|
||||||
|
if (next_rbp <= rbp || next_rbp == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rbp = next_rbp;
|
||||||
|
}
|
||||||
|
DEBUG("*** end stack trace ***");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint64_t addr;
|
||||||
|
const char *name;
|
||||||
|
} __attribute__((packed)) kernel_symbol_t;
|
||||||
|
|
||||||
|
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
|
||||||
|
__attribute__((weak)) extern uint64_t symbol_count;
|
||||||
|
|
||||||
|
// binary search
|
||||||
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
||||||
|
{
|
||||||
|
if (!symbol_table || symbol_count == 0)
|
||||||
|
{
|
||||||
|
if (offset) *offset = 0;
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
int low = 0, high = (int)symbol_count - 1;
|
||||||
|
int best = -1;
|
||||||
|
|
||||||
|
while (low <= high)
|
||||||
|
{
|
||||||
|
int mid = (low + high) / 2;
|
||||||
|
if (symbol_table[mid].addr <= rip)
|
||||||
|
{
|
||||||
|
best = mid;
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best != -1)
|
||||||
|
{
|
||||||
|
if (offset)
|
||||||
|
{
|
||||||
|
*offset = rip - symbol_table[best].addr;
|
||||||
|
}
|
||||||
|
return symbol_table[best].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset) *offset = 0;
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
23
src/entry.S
Normal file
23
src/entry.S
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
bits 64
|
||||||
|
global _start
|
||||||
|
|
||||||
|
extern kmain
|
||||||
|
extern kernel_stack
|
||||||
|
|
||||||
|
KERNEL_STACK_SIZE equ 65536
|
||||||
|
|
||||||
|
section .text
|
||||||
|
|
||||||
|
_start:
|
||||||
|
cli
|
||||||
|
|
||||||
|
; load kernel stack
|
||||||
|
lea rsp, [kernel_stack+KERNEL_STACK_SIZE]
|
||||||
|
|
||||||
|
; rbp=0 so last frame in stack trace
|
||||||
|
xor rbp, rbp
|
||||||
|
|
||||||
|
; 16 byte align
|
||||||
|
and rsp, -16
|
||||||
|
|
||||||
|
call kmain
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
; Assembly stub for the IDT
|
;
|
||||||
|
; @author xamidev <xamidev@riseup.net>
|
||||||
|
; @brief Stub for Interrupt Descriptor Table handlers
|
||||||
|
; @license GPL-3.0-only
|
||||||
|
;
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Interrupt Descriptor Table setup and dispatching
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include "idt.h"
|
#include "idt.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "../io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include "../io/kbd/ps2.h"
|
#include "io/kbd/ps2.h"
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "sched/scheduler.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "sched/process.h"
|
||||||
|
|
||||||
struct interrupt_descriptor idt[256];
|
struct interrupt_descriptor idt[256];
|
||||||
struct idtr idt_reg;
|
struct idtr idt_reg;
|
||||||
@@ -53,8 +63,67 @@ void idt_init()
|
|||||||
DEBUG("IDT initialized");
|
DEBUG("IDT initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint64_t read_cr2(void)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
asm volatile ("mov %%cr2, %0" : "=r"(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void page_fault_handler(struct cpu_status_t* ctx)
|
||||||
|
{
|
||||||
|
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
|
||||||
|
// interested in is getting more info out of those numbers cause i'm lost each time i have
|
||||||
|
// to read all this mess
|
||||||
|
uint64_t cr2 = read_cr2();
|
||||||
|
|
||||||
|
DEBUG("\x1b[38;5;231mPage Fault at rip=0x%p, err=%u (%s%s%s%s%s%s%s%s) when accessing addr=0x%p\x1b[0m", ctx->iret_rip, ctx->error_code,
|
||||||
|
CHECK_BIT(ctx->error_code, 0) ? "PAGE_PROTECTION_VIOLATION " : "PAGE_NOT_PRESENT ",
|
||||||
|
CHECK_BIT(ctx->error_code, 1) ? "ON_WRITE " : "ON_READ ",
|
||||||
|
CHECK_BIT(ctx->error_code, 2) ? "IN_USER_MODE" : "IN_KERNEL_MODE",
|
||||||
|
CHECK_BIT(ctx->error_code, 3) ? " WAS_RESERVED" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 4) ? " ON_INSTRUCTION_FETCH" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 5) ? " PK_VIOLATION" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 6) ? " ON_SHADOWSTACK_ACCESS" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "",
|
||||||
|
cr2);
|
||||||
|
|
||||||
|
panic(ctx, "page fault");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gp_fault_handler(struct cpu_status_t* ctx)
|
||||||
|
{
|
||||||
|
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
|
||||||
|
ctx->iret_rip,
|
||||||
|
ctx->error_code,
|
||||||
|
(ctx->error_code == 0) ? "NOT_SEGMENT_RELATED" : "SEGMENT_RELATED");
|
||||||
|
|
||||||
|
// Segment-related
|
||||||
|
if (ctx->error_code != 0)
|
||||||
|
{
|
||||||
|
bool is_external = CHECK_BIT(ctx->error_code, 0);
|
||||||
|
// is it IDT, GDT, LDT?
|
||||||
|
uint8_t table = ctx->error_code & 0x6; // 0b110 (isolate table)
|
||||||
|
uint16_t index = ctx->error_code & 0xFFF8; // 13*1 1111111111111 + 000 = 1111111111111000
|
||||||
|
|
||||||
|
char* table_names[4] = {"GDT", "IDT", "LDT", "IDT"};
|
||||||
|
|
||||||
|
DEBUG("\x1b[38;5;231m%s in %s index %u\x1b[0m",
|
||||||
|
is_external ? "EXTERNAL" : "INTERNAL",
|
||||||
|
table_names[table],
|
||||||
|
index);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(ctx, "gp fault");
|
||||||
|
}
|
||||||
|
|
||||||
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
||||||
{
|
{
|
||||||
|
if (context == NULL)
|
||||||
|
{
|
||||||
|
panic(NULL, "Interrupt dispatch recieved NULL context!");
|
||||||
|
}
|
||||||
|
|
||||||
switch(context->vector_number)
|
switch(context->vector_number)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@@ -77,6 +146,7 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
|||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
DEBUG("Invalid Opcode!");
|
DEBUG("Invalid Opcode!");
|
||||||
|
panic(context, "Invalid Opcode!");
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
DEBUG("Device Not Available!");
|
DEBUG("Device Not Available!");
|
||||||
@@ -97,10 +167,11 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
|||||||
DEBUG("Stack-Segment Fault!");
|
DEBUG("Stack-Segment Fault!");
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
DEBUG("General Protection Fault!");
|
gp_fault_handler(context);
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
DEBUG("Page Fault!");
|
// Better debugging for page faults...
|
||||||
|
page_fault_handler(context);
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
|
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
|
||||||
@@ -124,14 +195,23 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
|||||||
DEBUG("Control Protection Exception!");
|
DEBUG("Control Protection Exception!");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 32:
|
case 32: // Timer Interrupt
|
||||||
//DEBUG("Tick!");
|
|
||||||
ticks++;
|
ticks++;
|
||||||
// Send an EOI so that we can continue having interrupts
|
// Send an EOI so that we can continue having interrupts
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
|
|
||||||
|
if (ticks % SCHEDULER_QUANTUM == 0)
|
||||||
|
{
|
||||||
|
return scheduler_schedule(context);
|
||||||
|
//struct cpu_status_t* current_ctx = scheduler_schedule(context);
|
||||||
|
//process_switch(current_ctx->iret_rsp, current_ctx->iret_rip);
|
||||||
|
//SET_INTERRUPTS;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 33:
|
case 33:
|
||||||
|
DEBUG("Keyboard Interrupt");
|
||||||
keyboard_handler();
|
keyboard_handler();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Interrupt Descriptor Table setup and dispatching
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef IDT_H
|
#ifndef IDT_H
|
||||||
#define IDT_H
|
#define IDT_H
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
// PS/2 Keyboard support
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PS/2 Keyboard driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include "../serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include "ps2.h"
|
#include "ps2.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../term/term.h"
|
#include "io/term/term.h"
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
|
||||||
// The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed
|
// The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed
|
||||||
@@ -201,12 +205,11 @@ void keyboard_handler()
|
|||||||
|
|
||||||
if (c)
|
if (c)
|
||||||
{
|
{
|
||||||
putchar(c);
|
// Should probably have a keyboard buffer here... instead of this
|
||||||
|
_putchar(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skputs("key pressed!\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// End of Interrupt (to master PIC)
|
// End of Interrupt (to master PIC)
|
||||||
@@ -233,5 +236,11 @@ void keyboard_init(unsigned char layout)
|
|||||||
skputs("Unsupported layout.");
|
skputs("Unsupported layout.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unmask IRQ1
|
||||||
|
uint8_t mask = inb(0x21);
|
||||||
|
mask &= ~(1 << 1);
|
||||||
|
outb(0x21, mask);
|
||||||
|
|
||||||
DEBUG("PS/2 Keyboard initialized");
|
DEBUG("PS/2 Keyboard initialized");
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PS/2 Keyboard driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef PS2_H
|
#ifndef PS2_H
|
||||||
#define PS2_H
|
#define PS2_H
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Debug serial driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Debug serial driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef SERIAL_H
|
#ifndef SERIAL_H
|
||||||
#define SERIAL_H
|
#define SERIAL_H
|
||||||
|
|
||||||
|
|||||||
2129
src/io/term/flanterm.c
Normal file
2129
src/io/term/flanterm.c
Normal file
File diff suppressed because it is too large
Load Diff
72
src/io/term/flanterm.h
Normal file
72
src/io/term/flanterm.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_H
|
||||||
|
#define FLANTERM_H 1
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_CB_DEC 10
|
||||||
|
#define FLANTERM_CB_BELL 20
|
||||||
|
#define FLANTERM_CB_PRIVATE_ID 30
|
||||||
|
#define FLANTERM_CB_STATUS_REPORT 40
|
||||||
|
#define FLANTERM_CB_POS_REPORT 50
|
||||||
|
#define FLANTERM_CB_KBD_LEDS 60
|
||||||
|
#define FLANTERM_CB_MODE 70
|
||||||
|
#define FLANTERM_CB_LINUX 80
|
||||||
|
#define FLANTERM_CB_OSC 90
|
||||||
|
|
||||||
|
#ifdef FLANTERM_IN_FLANTERM
|
||||||
|
|
||||||
|
#include "flanterm_private.h"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct flanterm_context;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count);
|
||||||
|
void flanterm_flush(struct flanterm_context *ctx);
|
||||||
|
void flanterm_full_refresh(struct flanterm_context *ctx);
|
||||||
|
void flanterm_deinit(struct flanterm_context *ctx, void (*_free)(void *ptr, size_t size));
|
||||||
|
|
||||||
|
void flanterm_get_dimensions(struct flanterm_context *ctx, size_t *cols, size_t *rows);
|
||||||
|
void flanterm_set_autoflush(struct flanterm_context *ctx, bool state);
|
||||||
|
void flanterm_set_callback(struct flanterm_context *ctx, void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t));
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
1441
src/io/term/flanterm_backends/fb.c
Normal file
1441
src/io/term/flanterm_backends/fb.c
Normal file
File diff suppressed because it is too large
Load Diff
79
src/io/term/flanterm_backends/fb.h
Normal file
79
src/io/term/flanterm_backends/fb.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_FB_H
|
||||||
|
#define FLANTERM_FB_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../flanterm.h"
|
||||||
|
|
||||||
|
#ifdef FLANTERM_IN_FLANTERM
|
||||||
|
|
||||||
|
#include "fb_private.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_FB_ROTATE_0 0
|
||||||
|
#define FLANTERM_FB_ROTATE_90 1
|
||||||
|
#define FLANTERM_FB_ROTATE_180 2
|
||||||
|
#define FLANTERM_FB_ROTATE_270 3
|
||||||
|
|
||||||
|
struct flanterm_context *flanterm_fb_init(
|
||||||
|
/* If _malloc and _free are nulled, use the bump allocated instance (1 use only). */
|
||||||
|
void *(*_malloc)(size_t size),
|
||||||
|
void (*_free)(void *ptr, size_t size),
|
||||||
|
uint32_t *framebuffer, size_t width, size_t height, size_t pitch,
|
||||||
|
uint8_t red_mask_size, uint8_t red_mask_shift,
|
||||||
|
uint8_t green_mask_size, uint8_t green_mask_shift,
|
||||||
|
uint8_t blue_mask_size, uint8_t blue_mask_shift,
|
||||||
|
uint32_t *canvas, /* If nulled, no canvas. */
|
||||||
|
uint32_t *ansi_colours, uint32_t *ansi_bright_colours, /* If nulled, default. */
|
||||||
|
uint32_t *default_bg, uint32_t *default_fg, /* If nulled, default. */
|
||||||
|
uint32_t *default_bg_bright, uint32_t *default_fg_bright, /* If nulled, default. */
|
||||||
|
/* If font is null, use default font and font_width and font_height ignored. */
|
||||||
|
void *font, size_t font_width, size_t font_height, size_t font_spacing,
|
||||||
|
/* If scale_x and scale_y are 0, automatically scale font based on resolution. */
|
||||||
|
size_t font_scale_x, size_t font_scale_y,
|
||||||
|
size_t margin,
|
||||||
|
/* One of FLANTERM_FB_ROTATE_* values. */
|
||||||
|
int rotation
|
||||||
|
);
|
||||||
|
|
||||||
|
void flanterm_fb_set_flush_callback(struct flanterm_context *ctx, void (*flush_callback)(volatile void *address, size_t length));
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
127
src/io/term/flanterm_backends/fb_private.h
Normal file
127
src/io/term/flanterm_backends/fb_private.h
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_FB_PRIVATE_H
|
||||||
|
#define FLANTERM_FB_PRIVATE_H 1
|
||||||
|
|
||||||
|
#ifndef FLANTERM_IN_FLANTERM
|
||||||
|
#error "Do not use fb_private.h. Use interfaces defined in fb.h only."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_FB_FONT_GLYPHS 256
|
||||||
|
|
||||||
|
struct flanterm_fb_char {
|
||||||
|
uint32_t c;
|
||||||
|
uint32_t fg;
|
||||||
|
uint32_t bg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flanterm_fb_queue_item {
|
||||||
|
size_t x, y;
|
||||||
|
struct flanterm_fb_char c;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flanterm_fb_context {
|
||||||
|
struct flanterm_context term;
|
||||||
|
|
||||||
|
void (*plot_char)(struct flanterm_context *ctx, struct flanterm_fb_char *c, size_t x, size_t y);
|
||||||
|
void (*flush_callback)(volatile void *address, size_t length);
|
||||||
|
|
||||||
|
size_t font_width;
|
||||||
|
size_t font_height;
|
||||||
|
size_t glyph_width;
|
||||||
|
size_t glyph_height;
|
||||||
|
|
||||||
|
size_t font_scale_x;
|
||||||
|
size_t font_scale_y;
|
||||||
|
|
||||||
|
size_t offset_x, offset_y;
|
||||||
|
|
||||||
|
volatile uint32_t *framebuffer;
|
||||||
|
size_t pitch;
|
||||||
|
size_t width;
|
||||||
|
size_t height;
|
||||||
|
size_t phys_height;
|
||||||
|
size_t bpp;
|
||||||
|
|
||||||
|
uint8_t red_mask_size, red_mask_shift;
|
||||||
|
uint8_t green_mask_size, green_mask_shift;
|
||||||
|
uint8_t blue_mask_size, blue_mask_shift;
|
||||||
|
|
||||||
|
int rotation;
|
||||||
|
|
||||||
|
size_t font_bits_size;
|
||||||
|
uint8_t *font_bits;
|
||||||
|
size_t font_bool_size;
|
||||||
|
bool *font_bool;
|
||||||
|
|
||||||
|
uint32_t ansi_colours[8];
|
||||||
|
uint32_t ansi_bright_colours[8];
|
||||||
|
uint32_t default_fg, default_bg;
|
||||||
|
uint32_t default_fg_bright, default_bg_bright;
|
||||||
|
|
||||||
|
size_t canvas_size;
|
||||||
|
uint32_t *canvas;
|
||||||
|
|
||||||
|
size_t grid_size;
|
||||||
|
size_t queue_size;
|
||||||
|
size_t map_size;
|
||||||
|
|
||||||
|
struct flanterm_fb_char *grid;
|
||||||
|
|
||||||
|
struct flanterm_fb_queue_item *queue;
|
||||||
|
size_t queue_i;
|
||||||
|
|
||||||
|
struct flanterm_fb_queue_item **map;
|
||||||
|
|
||||||
|
uint32_t text_fg;
|
||||||
|
uint32_t text_bg;
|
||||||
|
size_t cursor_x;
|
||||||
|
size_t cursor_y;
|
||||||
|
|
||||||
|
uint32_t saved_state_text_fg;
|
||||||
|
uint32_t saved_state_text_bg;
|
||||||
|
size_t saved_state_cursor_x;
|
||||||
|
size_t saved_state_cursor_y;
|
||||||
|
|
||||||
|
size_t old_cursor_x;
|
||||||
|
size_t old_cursor_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
133
src/io/term/flanterm_private.h
Normal file
133
src/io/term/flanterm_private.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_PRIVATE_H
|
||||||
|
#define FLANTERM_PRIVATE_H 1
|
||||||
|
|
||||||
|
#ifndef FLANTERM_IN_FLANTERM
|
||||||
|
#error "Do not use flanterm_private.h. Use interfaces defined in flanterm.h only."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_MAX_ESC_VALUES 16
|
||||||
|
|
||||||
|
struct flanterm_context {
|
||||||
|
/* internal use */
|
||||||
|
|
||||||
|
size_t tab_size;
|
||||||
|
bool autoflush;
|
||||||
|
bool cursor_enabled;
|
||||||
|
bool scroll_enabled;
|
||||||
|
bool wrap_enabled;
|
||||||
|
bool origin_mode;
|
||||||
|
bool control_sequence;
|
||||||
|
bool escape;
|
||||||
|
bool osc;
|
||||||
|
bool osc_escape;
|
||||||
|
size_t osc_buf_i;
|
||||||
|
uint8_t osc_buf[256];
|
||||||
|
bool rrr;
|
||||||
|
bool discard_next;
|
||||||
|
bool bold;
|
||||||
|
bool bg_bold;
|
||||||
|
bool reverse_video;
|
||||||
|
bool dec_private;
|
||||||
|
bool insert_mode;
|
||||||
|
bool csi_unhandled;
|
||||||
|
uint64_t code_point;
|
||||||
|
size_t unicode_remaining;
|
||||||
|
uint8_t g_select;
|
||||||
|
uint8_t charsets[2];
|
||||||
|
size_t current_charset;
|
||||||
|
size_t escape_offset;
|
||||||
|
size_t esc_values_i;
|
||||||
|
size_t saved_cursor_x;
|
||||||
|
size_t saved_cursor_y;
|
||||||
|
size_t current_primary;
|
||||||
|
size_t current_bg;
|
||||||
|
size_t scroll_top_margin;
|
||||||
|
size_t scroll_bottom_margin;
|
||||||
|
uint32_t esc_values[FLANTERM_MAX_ESC_VALUES];
|
||||||
|
uint8_t last_printed_char;
|
||||||
|
bool last_was_graphic;
|
||||||
|
bool saved_state_bold;
|
||||||
|
bool saved_state_bg_bold;
|
||||||
|
bool saved_state_reverse_video;
|
||||||
|
bool saved_state_origin_mode;
|
||||||
|
bool saved_state_wrap_enabled;
|
||||||
|
size_t saved_state_current_charset;
|
||||||
|
uint8_t saved_state_charsets[2];
|
||||||
|
size_t saved_state_current_primary;
|
||||||
|
size_t saved_state_current_bg;
|
||||||
|
|
||||||
|
/* to be set by backend */
|
||||||
|
|
||||||
|
size_t rows, cols;
|
||||||
|
|
||||||
|
void (*raw_putchar)(struct flanterm_context *, uint8_t c);
|
||||||
|
void (*clear)(struct flanterm_context *, bool move);
|
||||||
|
void (*set_cursor_pos)(struct flanterm_context *, size_t x, size_t y);
|
||||||
|
void (*get_cursor_pos)(struct flanterm_context *, size_t *x, size_t *y);
|
||||||
|
void (*set_text_fg)(struct flanterm_context *, size_t fg);
|
||||||
|
void (*set_text_bg)(struct flanterm_context *, size_t bg);
|
||||||
|
void (*set_text_fg_bright)(struct flanterm_context *, size_t fg);
|
||||||
|
void (*set_text_bg_bright)(struct flanterm_context *, size_t bg);
|
||||||
|
void (*set_text_fg_rgb)(struct flanterm_context *, uint32_t fg);
|
||||||
|
void (*set_text_bg_rgb)(struct flanterm_context *, uint32_t bg);
|
||||||
|
void (*set_text_fg_default)(struct flanterm_context *);
|
||||||
|
void (*set_text_bg_default)(struct flanterm_context *);
|
||||||
|
void (*set_text_fg_default_bright)(struct flanterm_context *);
|
||||||
|
void (*set_text_bg_default_bright)(struct flanterm_context *);
|
||||||
|
void (*move_character)(struct flanterm_context *, size_t new_x, size_t new_y, size_t old_x, size_t old_y);
|
||||||
|
void (*scroll)(struct flanterm_context *);
|
||||||
|
void (*revscroll)(struct flanterm_context *);
|
||||||
|
void (*swap_palette)(struct flanterm_context *);
|
||||||
|
void (*save_state)(struct flanterm_context *);
|
||||||
|
void (*restore_state)(struct flanterm_context *);
|
||||||
|
void (*double_buffer_flush)(struct flanterm_context *);
|
||||||
|
void (*full_refresh)(struct flanterm_context *);
|
||||||
|
void (*deinit)(struct flanterm_context *, void (*)(void *, size_t));
|
||||||
|
|
||||||
|
/* to be set by client */
|
||||||
|
|
||||||
|
void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
void flanterm_context_reinit(struct flanterm_context *ctx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,11 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Framebuffer-based terminal driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
// Terminal output
|
// Terminal output
|
||||||
|
/*
|
||||||
|
There are a couple of bugs here and there but for now I don't care too much
|
||||||
|
because this shitty implementation will be replaced one day by Flanterm
|
||||||
|
(once memory management is okay: paging & kernel malloc)
|
||||||
|
*/
|
||||||
|
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include "term.h"
|
#include "term.h"
|
||||||
|
#include "mem/misc/utils.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "flanterm.h"
|
||||||
|
|
||||||
extern struct limine_framebuffer* framebuffer;
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
// Importing the PSF object file
|
// Importing the PSF object file
|
||||||
extern unsigned char _binary_zap_light16_psf_start[];
|
extern unsigned char _binary_zap_light16_psf_start[];
|
||||||
@@ -17,128 +31,97 @@ uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header);
|
|||||||
#define FONT_WIDTH 8
|
#define FONT_WIDTH 8
|
||||||
#define FONT_HEIGHT font->characterSize
|
#define FONT_HEIGHT font->characterSize
|
||||||
|
|
||||||
|
extern struct flanterm_context* ft_ctx;
|
||||||
|
|
||||||
// Character cursor
|
// Character cursor
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
unsigned int x;
|
size_t x;
|
||||||
unsigned int y;
|
size_t y;
|
||||||
} Cursor;
|
} Cursor;
|
||||||
|
|
||||||
Cursor cursor = {0};
|
static Cursor cursor = {0, 0};
|
||||||
|
|
||||||
unsigned char* fb;
|
static uint8_t* fb;
|
||||||
|
static struct limine_framebuffer* framebuffer;
|
||||||
|
|
||||||
|
uint8_t lines_length[TERM_HISTORY_MAX_LINES];
|
||||||
|
|
||||||
|
static inline size_t term_max_cols(void)
|
||||||
|
{
|
||||||
|
return framebuffer->width / FONT_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t term_max_lines(void)
|
||||||
|
{
|
||||||
|
return framebuffer->height / FONT_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
int term_init()
|
int term_init()
|
||||||
{
|
{
|
||||||
// Get framebuffer address from Limine struct
|
// Get framebuffer address from Limine struct
|
||||||
|
|
||||||
if (framebuffer)
|
if (!boot_ctx.fb)
|
||||||
{
|
{
|
||||||
fb = framebuffer->address;
|
return -ENOMEM;
|
||||||
DEBUG("terminal initialized");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return -ENOMEM;
|
|
||||||
|
framebuffer = boot_ctx.fb;
|
||||||
|
fb = (uint8_t*)framebuffer->address;
|
||||||
|
|
||||||
|
memset(lines_length, 0, sizeof(lines_length));
|
||||||
|
|
||||||
|
DEBUG("terminal initialized, fb=0x%p (width=%u height=%u pitch=%u bpp=%u)", fb, framebuffer->width, framebuffer->height, framebuffer->pitch, framebuffer->bpp);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are marked "static" because we don't wanna expose them all around
|
// These are marked "static" because we don't wanna expose them all around
|
||||||
// AKA they should just be seen here (kind of like private functions in cpp)
|
// AKA they should just be seen here (kind of like private functions in cpp)
|
||||||
static void putpixel(int x, int y, int color)
|
static inline void putpixel(size_t x, size_t y, uint32_t color)
|
||||||
{
|
{
|
||||||
|
// Guard so we don't write past fb boundaries
|
||||||
|
if (x >= framebuffer->width || y >= framebuffer->height) return;
|
||||||
// Depth isn't part of limine_framebuffer attributes so it will be 4
|
// Depth isn't part of limine_framebuffer attributes so it will be 4
|
||||||
unsigned pos = x*4 + y*framebuffer->pitch;
|
size_t pos = x*4 + y*framebuffer->pitch;
|
||||||
fb[pos] = color & 255; // blue channel
|
fb[pos] = color & 255; // blue channel
|
||||||
fb[pos+1] = (color >> 8) & 255; // green
|
fb[pos+1] = (color >> 8) & 255; // green
|
||||||
fb[pos+2] = (color >> 16) & 255; // blue
|
fb[pos+2] = (color >> 16) & 255; // blue
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_char(char c, int px, int py, int fg, int bg)
|
void term_scroll()
|
||||||
{
|
{
|
||||||
uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT);
|
// Erase first text line
|
||||||
|
memset(fb, 255, FONT_HEIGHT*framebuffer->pitch);
|
||||||
|
|
||||||
for (size_t y=0; y<FONT_HEIGHT; y++)
|
// Move whole framebuffer up by one text line
|
||||||
|
memmove(fb, fb+(FONT_HEIGHT*framebuffer->pitch), (framebuffer->height-FONT_HEIGHT)*framebuffer->pitch);
|
||||||
|
|
||||||
|
// Clear last text line
|
||||||
|
size_t clear_start = (framebuffer->height - FONT_HEIGHT) * framebuffer->pitch;
|
||||||
|
memset(fb + clear_start, 255, FONT_HEIGHT * framebuffer->pitch);
|
||||||
|
|
||||||
|
// Shift line lengths by 1 (for backspace handling)
|
||||||
|
size_t max_lines = term_max_lines();
|
||||||
|
for (size_t i = 1; i < max_lines; i++)
|
||||||
{
|
{
|
||||||
uint8_t row = glyph[y];
|
lines_length[i - 1] = lines_length[i];
|
||||||
for (size_t x=0; x<8; x++)
|
|
||||||
{
|
|
||||||
int color = (row & (0x80 >> x)) ? fg : bg;
|
|
||||||
putpixel(px+x, py+y, color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
lines_length[max_lines - 1] = 0;
|
||||||
|
|
||||||
static void erase_char(int px, int py)
|
|
||||||
{
|
|
||||||
for (size_t y=0; y<FONT_HEIGHT; y++)
|
|
||||||
{
|
|
||||||
for (size_t x=0; x<8; x++)
|
|
||||||
{
|
|
||||||
// Black
|
|
||||||
putpixel(px+x, py+y, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void putchar(char c)
|
|
||||||
{
|
|
||||||
if (c == '\n')
|
|
||||||
{
|
|
||||||
cursor.x = 0;
|
|
||||||
cursor.y++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Improperly handled.
|
|
||||||
// When we're on an empty line it should get to the upper line's last character
|
|
||||||
// NOT just the last position possible; we would need to track the last line's character amount for that
|
|
||||||
if (c == '\b')
|
|
||||||
{
|
|
||||||
if (cursor.x == 0 && cursor.y == 0)
|
|
||||||
{
|
|
||||||
// Top-left corner
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.x == 0)
|
|
||||||
{
|
|
||||||
cursor.y--;
|
|
||||||
cursor.x = (framebuffer->width / FONT_WIDTH) -1; // here
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cursor.x--;
|
|
||||||
}
|
|
||||||
|
|
||||||
int px = cursor.x * FONT_WIDTH;
|
|
||||||
int py = cursor.y * FONT_HEIGHT;
|
|
||||||
erase_char(px, py);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((cursor.x+1)*FONT_WIDTH >= framebuffer->width)
|
|
||||||
{
|
|
||||||
cursor.x = 0;
|
|
||||||
cursor.y++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int px = cursor.x * FONT_WIDTH;
|
|
||||||
int py = cursor.y * FONT_HEIGHT;
|
|
||||||
draw_char(c, px, py, WHITE, BLACK);
|
|
||||||
cursor.x++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overhead that could be avoided, right? (for printf)
|
// Overhead that could be avoided, right? (for printf)
|
||||||
void _putchar(char character)
|
void _putchar(char character)
|
||||||
{
|
{
|
||||||
putchar(character);
|
flanterm_write(ft_ctx, &character, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug-printing
|
// Debug-printing
|
||||||
void kputs(const char* str)
|
void kputs(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int i=0;
|
size_t i=0;
|
||||||
while (str[i] != 0)
|
while (str[i] != 0)
|
||||||
{
|
{
|
||||||
putchar(str[i]);
|
_putchar(str[i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Framebuffer-based terminal driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef TERM_H
|
#ifndef TERM_H
|
||||||
#define TERM_H
|
#define TERM_H
|
||||||
|
|
||||||
int term_init();
|
int term_init();
|
||||||
void kputs(const char* str);
|
void kputs(const char* str);
|
||||||
void putchar(char c);
|
void _putchar(char character);
|
||||||
|
|
||||||
enum TermColors
|
enum TermColors
|
||||||
{
|
{
|
||||||
@@ -20,4 +26,7 @@ typedef struct
|
|||||||
uint8_t characterSize; // height
|
uint8_t characterSize; // height
|
||||||
} PSF1_Header;
|
} PSF1_Header;
|
||||||
|
|
||||||
|
// debug
|
||||||
|
void term_scroll();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
41
src/kernel.h
41
src/kernel.h
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel global macros
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef KERNEL_H
|
#ifndef KERNEL_H
|
||||||
#define KERNEL_H
|
#define KERNEL_H
|
||||||
|
|
||||||
@@ -12,10 +18,37 @@ enum ErrorCodes
|
|||||||
|
|
||||||
#include "io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include "io/term/printf.h"
|
#include "io/term/printf.h"
|
||||||
|
#include "idt/idt.h"
|
||||||
|
|
||||||
// Still lacks print formatting...
|
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
|
||||||
#define DEBUG(log, ...) \
|
|
||||||
printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); \
|
|
||||||
fctprintf((void*)&skputc, 0, "debug: [%s]: %s\n", __FILE__, log)
|
/* #define DEBUG(log, ...) \
|
||||||
|
printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \
|
||||||
|
fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DIE_DEBUG(str) fctprintf((void*)&skputc, 0, str)
|
||||||
|
|
||||||
|
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
|
||||||
|
|
||||||
|
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
|
||||||
|
|
||||||
|
void panic(struct cpu_status_t* ctx, const char* str);
|
||||||
|
void hcf();
|
||||||
|
void idle();
|
||||||
|
|
||||||
|
void debug_stack_trace(unsigned int max_frames);
|
||||||
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset);
|
||||||
|
|
||||||
|
#define assert(check) do { if(!(check)) hcf(); } while(0)
|
||||||
|
|
||||||
|
struct boot_context
|
||||||
|
{
|
||||||
|
struct limine_framebuffer* fb;
|
||||||
|
struct limine_memmap_response* mmap;
|
||||||
|
struct limine_hhdm_response* hhdm;
|
||||||
|
struct limine_kernel_address_response* kaddr;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
136
src/kmain.c
136
src/kmain.c
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PepperOS kernel entry point
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
@@ -10,58 +16,128 @@
|
|||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "time/timer.h"
|
#include "time/timer.h"
|
||||||
#include "io/kbd/ps2.h"
|
#include "io/kbd/ps2.h"
|
||||||
|
#include "mem/paging/pmm.h"
|
||||||
|
#include "mem/paging/paging.h"
|
||||||
|
#include "mem/paging/vmm.h"
|
||||||
|
#include "mem/heap/kheap.h"
|
||||||
|
#include "sched/process.h"
|
||||||
|
#include "sched/scheduler.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "io/term/flanterm.h"
|
||||||
|
#include "io/term/flanterm_backends/fb.h"
|
||||||
|
|
||||||
// Limine version used
|
// Limine version used
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
static volatile LIMINE_BASE_REVISION(3);
|
volatile LIMINE_BASE_REVISION(3);
|
||||||
|
|
||||||
// Framebuffer request
|
struct flanterm_context *ft_ctx;
|
||||||
__attribute__((used, section(".limine_requests")))
|
|
||||||
static volatile struct limine_framebuffer_request framebuffer_request = {
|
|
||||||
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
|
||||||
.revision = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests_start")))
|
// Halt and catch fire (makes machine stall)
|
||||||
static volatile LIMINE_REQUESTS_START_MARKER;
|
void hcf()
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests_end")))
|
|
||||||
static volatile LIMINE_REQUESTS_END_MARKER;
|
|
||||||
|
|
||||||
struct limine_framebuffer* framebuffer;
|
|
||||||
|
|
||||||
// Panic
|
|
||||||
static void hcf()
|
|
||||||
{
|
{
|
||||||
for (;;)
|
CLEAR_INTERRUPTS; for (;;)asm("hlt");
|
||||||
{
|
}
|
||||||
asm("hlt");
|
|
||||||
}
|
// Doing nothing (can be interrupted)
|
||||||
|
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
|
||||||
|
|
||||||
|
uint8_t kernel_stack[KERNEL_STACK_SIZE] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
struct boot_context boot_ctx;
|
||||||
|
|
||||||
|
extern volatile struct limine_framebuffer_request framebuffer_request;
|
||||||
|
extern volatile struct limine_memmap_request memmap_request;
|
||||||
|
extern volatile struct limine_hhdm_request hhdm_request;
|
||||||
|
extern volatile struct limine_kernel_address_request kerneladdr_request;
|
||||||
|
|
||||||
|
extern struct process_t* processes_list;
|
||||||
|
extern struct process_t* current_process;
|
||||||
|
|
||||||
|
void pedicel_main(void* arg)
|
||||||
|
{
|
||||||
|
//printf("Hello, world from a KERNEL PROCESS!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void two_main(void* arg)
|
||||||
|
{
|
||||||
|
//printf("...process 2 speaking!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void idle_main(void* arg)
|
||||||
|
{
|
||||||
|
for(;;)asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is our entry point
|
// This is our entry point
|
||||||
void kmain()
|
void kmain()
|
||||||
{
|
{
|
||||||
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
||||||
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) hcf();
|
|
||||||
|
|
||||||
// Get the first framebuffer from the response
|
// Populate boot context
|
||||||
framebuffer = framebuffer_request.response->framebuffers[0];
|
boot_ctx.fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
|
||||||
|
boot_ctx.mmap = memmap_request.response ? memmap_request.response : NULL;
|
||||||
|
boot_ctx.hhdm = hhdm_request.response ? hhdm_request.response : NULL;
|
||||||
|
boot_ctx.kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
|
||||||
|
|
||||||
|
pmm_init(boot_ctx.mmap, boot_ctx.hhdm);
|
||||||
|
|
||||||
|
// Remap kernel , HHDM and framebuffer
|
||||||
|
paging_init(boot_ctx.kaddr, boot_ctx.fb);
|
||||||
|
|
||||||
|
uint32_t bgColor = 0x252525;
|
||||||
|
ft_ctx = flanterm_fb_init(
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
boot_ctx.fb->address, boot_ctx.fb->width, boot_ctx.fb->height, boot_ctx.fb->pitch,
|
||||||
|
boot_ctx.fb->red_mask_size, boot_ctx.fb->red_mask_shift,
|
||||||
|
boot_ctx.fb->green_mask_size, boot_ctx.fb->green_mask_shift,
|
||||||
|
boot_ctx.fb->blue_mask_size, boot_ctx.fb->blue_mask_shift,
|
||||||
|
NULL,
|
||||||
|
NULL, NULL,
|
||||||
|
&bgColor, NULL, // &bgColor
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, 0, 0, 1,
|
||||||
|
0, 0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
term_init();
|
|
||||||
serial_init();
|
serial_init();
|
||||||
|
|
||||||
|
memmap_display(boot_ctx.mmap);
|
||||||
|
hhdm_display(boot_ctx.hhdm);
|
||||||
|
DEBUG("kernel: phys_base=0x%p virt_base=0x%p", boot_ctx.kaddr->physical_base, boot_ctx.kaddr->virtual_base);
|
||||||
|
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
gdt_init();
|
gdt_init();
|
||||||
idt_init();
|
idt_init();
|
||||||
timer_init();
|
timer_init();
|
||||||
|
|
||||||
|
kheap_init();
|
||||||
|
|
||||||
|
vmm_init();
|
||||||
|
|
||||||
|
struct process_t* idle_proc = process_create("idle", (void*)idle_main, 0);
|
||||||
|
struct process_t* pedicel = process_create("pedicel", (void*)pedicel_main, 0);
|
||||||
|
struct process_t* two = process_create("two", (void*)two_main, 0);
|
||||||
|
|
||||||
|
process_display_list(processes_list);
|
||||||
|
|
||||||
|
scheduler_init();
|
||||||
|
|
||||||
|
current_process = idle_proc;
|
||||||
|
current_process->status = RUNNING;
|
||||||
|
|
||||||
SET_INTERRUPTS;
|
SET_INTERRUPTS;
|
||||||
|
|
||||||
keyboard_init(FR);
|
keyboard_init(FR);
|
||||||
|
//term_init();
|
||||||
|
|
||||||
// Draw something
|
//printf(PEPPEROS_SPLASH);
|
||||||
printf("%s, %s!\n", "Hello", "world");
|
|
||||||
// Yoohoooooo!
|
//printf("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu diam id sem tincidunt vestibulum. Etiam sed congue nisl, vitae aliquet orci. Donec magna turpis, semper sed ipsum eget, semper blandit dolor. Morbi faucibus posuere sapien. Vestibulum aliquet mi vel orci finibus, vestibulum rhoncus tellus sagittis. Vivamus a arcu suscipit sem iaculis volutpat vel nec est. Nulla malesuada, urna vel pretium pretium, enim tortor pulvinar velit, porttitor dictum lectus turpis id tortor. Quisque egestas ultricies lorem, egestas ultrices tellus elementum porta. Fusce consequat nisi in diam placerat fermentum. Suspendisse tempus turpis nec turpis condimentum fringilla. Maecenas nec orci pharetra, feugiat enim vel, viverra neque. Vivamus placerat purus in tincidunt ultricies. Pellentesque vel mi molestie, congue nibh nec, cursus nisl. Cras dapibus lectus mauris, sed interdum risus tristique non. ");
|
||||||
DEBUG("kernel initialized successfully! hanging... wow=%d", 42);
|
|
||||||
hcf();
|
flanterm_write(ft_ctx, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu diam id sem tincidunt vestibulum. Etiam sed congue nisl, vitae aliquet orci. Donec magna turpis, semper sed ipsum eget, semper blandit dolor. Morbi faucibus posuere sapien. Vestibulum aliquet mi vel orci finibus, vestibulum rhoncus tellus sagittis. Vivamus a arcu suscipit sem iaculis volutpat vel nec est. Nulla malesuada, urna vel pretium pretium, enim tortor pulvinar velit, porttitor dictum lectus turpis id tortor. Quisque egestas ultricies lorem, egestas ultrices tellus elementum porta. Fusce consequat nisi in diam placerat fermentum. Suspendisse tempus turpis nec turpis condimentum fringilla. Maecenas nec orci pharetra, feugiat enim vel, viverra neque. Vivamus placerat purus in tincidunt ultricies. Pellentesque vel mi molestie, congue nibh nec, cursus nisl. Cras dapibus lectus mauris, sed interdum risus tristique non.", sizeof("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu diam id sem tincidunt vestibulum. Etiam sed congue nisl, vitae aliquet orci. Donec magna turpis, semper sed ipsum eget, semper blandit dolor. Morbi faucibus posuere sapien. Vestibulum aliquet mi vel orci finibus, vestibulum rhoncus tellus sagittis. Vivamus a arcu suscipit sem iaculis volutpat vel nec est. Nulla malesuada, urna vel pretium pretium, enim tortor pulvinar velit, porttitor dictum lectus turpis id tortor. Quisque egestas ultricies lorem, egestas ultrices tellus elementum porta. Fusce consequat nisi in diam placerat fermentum. Suspendisse tempus turpis nec turpis condimentum fringilla. Maecenas nec orci pharetra, feugiat enim vel, viverra neque. Vivamus placerat purus in tincidunt ultricies. Pellentesque vel mi molestie, congue nibh nec, cursus nisl. Cras dapibus lectus mauris, sed interdum risus tristique non. "));
|
||||||
|
|
||||||
|
idle();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Global Descriptor Table (for legacy reasons)
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../../io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
|
||||||
// Descriptors are 8-byte wide (64bits)
|
// Descriptors are 8-byte wide (64bits)
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Global Descriptor Table (for legacy reasons)
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef GDT_H
|
#ifndef GDT_H
|
||||||
#define GDT_H
|
#define GDT_H
|
||||||
|
|
||||||
|
|||||||
147
src/mem/heap/kheap.c
Normal file
147
src/mem/heap/kheap.c
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel heap
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kheap.h"
|
||||||
|
#include "mem/paging/paging.h"
|
||||||
|
#include "mem/paging/pmm.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include "sched/process.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
extern uint64_t kernel_phys_base;
|
||||||
|
extern uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
uintptr_t kheap_start;
|
||||||
|
|
||||||
|
static struct heap_block_t* head = NULL;
|
||||||
|
static uintptr_t end;
|
||||||
|
|
||||||
|
// Kernel root table (level 4)
|
||||||
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
void kheap_init()
|
||||||
|
{
|
||||||
|
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
||||||
|
end = kheap_start;
|
||||||
|
|
||||||
|
// At least 1 page must be mapped for it to work
|
||||||
|
kheap_map_page();
|
||||||
|
|
||||||
|
// Give linked list head its properties
|
||||||
|
head = (struct heap_block_t*)kheap_start;
|
||||||
|
head->size = PAGE_SIZE - sizeof(struct heap_block_t);
|
||||||
|
head->free = true;
|
||||||
|
head->next = NULL;
|
||||||
|
DEBUG("kheap initialized, head=0x%p, size=%u", head, head->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* kmalloc(size_t size)
|
||||||
|
{
|
||||||
|
// No size, no memory allocated!
|
||||||
|
if (!size) return NULL;
|
||||||
|
size = ALIGN(size);
|
||||||
|
|
||||||
|
struct heap_block_t* curr = head;
|
||||||
|
|
||||||
|
while (curr)
|
||||||
|
{
|
||||||
|
// Is block free and big enough for us?
|
||||||
|
if (curr->free && curr->size >= size)
|
||||||
|
{
|
||||||
|
// We split the block if it is big enough
|
||||||
|
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);
|
||||||
|
struct heap_block_t* split = (struct heap_block_t*)((uintptr_t)curr + sizeof(*curr) + size);
|
||||||
|
|
||||||
|
split->size = curr->size - size - sizeof(*curr);
|
||||||
|
split->free = true;
|
||||||
|
split->next = curr->next;
|
||||||
|
|
||||||
|
curr->next = split;
|
||||||
|
curr->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found a good block, we return it
|
||||||
|
curr->free = false;
|
||||||
|
return (void*)((uintptr_t)curr + sizeof(struct heap_block_t));
|
||||||
|
}
|
||||||
|
// Continue browsing the list if nothing good was found yet
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_grow(size + sizeof(struct heap_block_t));
|
||||||
|
|
||||||
|
struct heap_block_t* block = (struct heap_block_t*)old_end;
|
||||||
|
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
|
||||||
|
curr = head;
|
||||||
|
while (curr->next)
|
||||||
|
{
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
curr->next = block;
|
||||||
|
|
||||||
|
return kmalloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kfree(void* ptr)
|
||||||
|
{
|
||||||
|
// Nothing to free
|
||||||
|
if (!ptr) return;
|
||||||
|
|
||||||
|
// 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); // As it's out of kmalloc, stack is already mapped into kernel space
|
||||||
|
return ptr ? ptr+PROCESS_STACK_SIZE : NULL;
|
||||||
|
}
|
||||||
31
src/mem/heap/kheap.h
Normal file
31
src/mem/heap/kheap.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel heap
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KHEAP_H
|
||||||
|
#define KHEAP_H
|
||||||
|
|
||||||
|
// We need some kind of simple kernel heap to make our linked list
|
||||||
|
// for the VMM, as we need "malloc" and "free" for that data structure.
|
||||||
|
// When the kernel heap is ready, we can alloc our VM object linked list
|
||||||
|
// and then continue working on the VMM.
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct heap_block_t
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
bool free;
|
||||||
|
struct heap_block_t* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void kheap_init();
|
||||||
|
void* kmalloc(size_t size);
|
||||||
|
void kfree(void* ptr);
|
||||||
|
void* kalloc_stack();
|
||||||
|
void kheap_map_page();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Common memory utilities
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "string/string.h"
|
||||||
|
|
||||||
// We won't be linked to standard library, but still need the basic mem* functions
|
// We won't be linked to standard library, but still need the basic mem* functions
|
||||||
// so everything goes allright with the compiler
|
// so everything goes allright with the compiler
|
||||||
@@ -68,3 +77,52 @@ int memcmp(const void* s1, const void* s2, size_t n)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display the memmap so we see how the memory is laid out at handoff
|
||||||
|
void memmap_display(struct limine_memmap_response* response)
|
||||||
|
{
|
||||||
|
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
|
||||||
|
|
||||||
|
for (size_t i=0; i<response->entry_count; i++)
|
||||||
|
{
|
||||||
|
struct limine_memmap_entry* entry = response->entries[i];
|
||||||
|
char type[32] = {0};
|
||||||
|
switch(entry->type)
|
||||||
|
{
|
||||||
|
case LIMINE_MEMMAP_USABLE:
|
||||||
|
strcpy(type, "USABLE");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_RESERVED:
|
||||||
|
strcpy(type, "RESERVED");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
||||||
|
strcpy(type, "ACPI_RECLAIMABLE");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_ACPI_NVS:
|
||||||
|
strcpy(type, "ACPI_NVS");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_BAD_MEMORY:
|
||||||
|
strcpy(type, "BAD_MEMORY");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
||||||
|
strcpy(type, "BOOTLOADER_RECLAIMABLE");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
|
||||||
|
strcpy(type, "KERNEL_AND_MODULES");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_FRAMEBUFFER:
|
||||||
|
strcpy(type, "FRAMEBUFFER");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strcpy(type, "UNKNOWN");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUG("entry %02u: [0x%016x | %016u bytes] - %s", i, entry->base, entry->length, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the HHDM
|
||||||
|
void hhdm_display(struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
|
||||||
|
}
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Common memory utilities
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MEM_UTILS_H
|
#ifndef MEM_UTILS_H
|
||||||
#define MEM_UTILS_H
|
#define MEM_UTILS_H
|
||||||
|
|
||||||
@@ -8,4 +14,8 @@ void* memset(void* s, int c, size_t n);
|
|||||||
void* memmove(void *dest, const void* src, size_t n);
|
void* memmove(void *dest, const void* src, size_t n);
|
||||||
int memcmp(const void* s1, const void* s2, size_t n);
|
int memcmp(const void* s1, const void* s2, size_t n);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
void memmap_display(struct limine_memmap_response* response);
|
||||||
|
void hhdm_display(struct limine_hhdm_response* hhdm);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
201
src/mem/paging/paging.c
Normal file
201
src/mem/paging/paging.c
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x64 4-level paging implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "paging.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
void load_cr3(uint64_t value) {
|
||||||
|
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// To flush TLB
|
||||||
|
static inline void invlpg(void *addr)
|
||||||
|
{
|
||||||
|
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
for (size_t i=0; i<512; i++)
|
||||||
|
{
|
||||||
|
virt[i] = 0;
|
||||||
|
}
|
||||||
|
return virt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernel paging root table, that will be placed in cr3
|
||||||
|
__attribute__((aligned(4096)))
|
||||||
|
uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
// Map a page, taking virt and phys address. This will go through the paging structures
|
||||||
|
// beginning at the given root table, translate the virtual address in indexes in
|
||||||
|
// page table/directories, and then mapping the correct page table entry with the
|
||||||
|
// given physical address + flags
|
||||||
|
void paging_map_page(uint64_t* root_table, 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 (!(root_table[pml4_i] & PTE_PRESENT))
|
||||||
|
{
|
||||||
|
pdpt = alloc_page_table();
|
||||||
|
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & PTE_ADDR_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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] & PTE_ADDR_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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] & PTE_ADDR_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PT: finally, populate the page table entry
|
||||||
|
pt[pt_i] = phys | flags | PTE_PRESENT;
|
||||||
|
|
||||||
|
// Flush TLB (apply changes)
|
||||||
|
invlpg((void *)virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t kernel_phys_base;
|
||||||
|
uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
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 = 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);
|
||||||
|
|
||||||
|
kernel_pml4 = alloc_page_table();
|
||||||
|
|
||||||
|
// for debug
|
||||||
|
uint64_t page_count = 0;
|
||||||
|
|
||||||
|
// 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 | PTE_PRESENT);
|
||||||
|
page_count++;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
// .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)
|
||||||
|
{
|
||||||
|
//paging_kmap_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
||||||
|
paging_map_page(kernel_pml4, 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++)
|
||||||
|
{
|
||||||
|
//paging_kmap_page(fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
||||||
|
paging_map_page(kernel_pml4, 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);
|
||||||
|
|
||||||
|
// test for flanterm
|
||||||
|
// When 10 pages are mapped, SOMETIMES (1 out of 50 times) it prints everything without problem!
|
||||||
|
// Other times it prints garbage (almost full cursors) and/or panics.
|
||||||
|
/* for (uint64_t i=0; i<10; i++)
|
||||||
|
{
|
||||||
|
paging_map_page(kernel_pml4, 0, kernel_phys_base+KERNEL_SIZE+i*PAGE_SIZE, PTE_WRITABLE);
|
||||||
|
} */
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
51
src/mem/paging/paging.h
Normal file
51
src/mem/paging/paging.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x64 4-level paging implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PAGING_H
|
||||||
|
#define PAGING_H
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include "mem/heap/kheap.h"
|
||||||
|
|
||||||
|
void paging_init();
|
||||||
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
|
||||||
|
|
||||||
|
// To swap root page tables
|
||||||
|
void load_cr3(uint64_t value);
|
||||||
|
|
||||||
|
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) & 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)
|
||||||
|
#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_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)
|
||||||
|
|
||||||
|
#endif
|
||||||
110
src/mem/paging/pmm.c
Normal file
110
src/mem/paging/pmm.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Physical memory manager from freelist
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
pmm - Physical Memory Manager
|
||||||
|
will manage 4kb pages physically
|
||||||
|
it will probably need to get some info from Limine,
|
||||||
|
to see which pages are used by kernel/bootloader/mmio/fb etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "paging.h"
|
||||||
|
#include <limine.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include "mem/misc/utils.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
First we'll have to discover the physical memory layout,
|
||||||
|
and for that we can use a Limine request.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
We will look for the biggest usable physical memory region
|
||||||
|
and use this for the bitmap. The reserved memory will be ignored.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct limine_memmap_entry* biggest_entry;
|
||||||
|
|
||||||
|
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
// Max length of a usable memory region
|
||||||
|
uint64_t length_max = 0;
|
||||||
|
uint64_t offset = hhdm->offset;
|
||||||
|
|
||||||
|
DEBUG("Usable Memory:");
|
||||||
|
for (size_t i=0; i<memmap->entry_count; i++)
|
||||||
|
{
|
||||||
|
struct limine_memmap_entry* entry = memmap->entries[i];
|
||||||
|
|
||||||
|
if (entry->type == LIMINE_MEMMAP_USABLE)
|
||||||
|
{
|
||||||
|
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length,
|
||||||
|
entry->base+offset, entry->base+entry->length+offset);
|
||||||
|
if (entry->length > length_max)
|
||||||
|
{
|
||||||
|
length_max = entry->length;
|
||||||
|
biggest_entry = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Biggest usable memory region:");
|
||||||
|
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", biggest_entry->base, biggest_entry->base + biggest_entry->length,
|
||||||
|
biggest_entry->base+offset, biggest_entry->base+biggest_entry->length+offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset from Higher Half Direct Map
|
||||||
|
uint64_t hhdm_off;
|
||||||
|
|
||||||
|
static uintptr_t g_freelist = 0;
|
||||||
|
|
||||||
|
uintptr_t pmm_alloc()
|
||||||
|
{
|
||||||
|
if (!g_freelist)
|
||||||
|
{
|
||||||
|
panic(NULL, "PMM is out of memory!");
|
||||||
|
}
|
||||||
|
uintptr_t addr = g_freelist;
|
||||||
|
g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_free(uintptr_t addr)
|
||||||
|
{
|
||||||
|
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
||||||
|
g_freelist = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmm_init_freelist()
|
||||||
|
{
|
||||||
|
// We simply call pmm_free() on each page that is marked USABLE
|
||||||
|
// in our big memory region.
|
||||||
|
uint64_t base = ALIGN_UP(biggest_entry->base, PAGE_SIZE);
|
||||||
|
uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE);
|
||||||
|
|
||||||
|
uint64_t page_count=0;
|
||||||
|
for (uint64_t addr = base; addr < end; addr += PAGE_SIZE)
|
||||||
|
{
|
||||||
|
pmm_free(addr);
|
||||||
|
//DEBUG("page %u lives at phys 0x%p (virt 0x%p)", page_count, addr, PHYS_TO_VIRT(addr));
|
||||||
|
page_count++;
|
||||||
|
}
|
||||||
|
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
hhdm_off = hhdm->offset;
|
||||||
|
pmm_find_biggest_usable_region(memmap, hhdm);
|
||||||
|
//pmm_allocate_bitmap(hhdm); too complicated for my small brain
|
||||||
|
|
||||||
|
// Now we have biggest USABLE region,
|
||||||
|
// so to populate the free list we just iterate through it
|
||||||
|
pmm_init_freelist();
|
||||||
|
}
|
||||||
16
src/mem/paging/pmm.h
Normal file
16
src/mem/paging/pmm.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Physical memory manager from freelist
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PAGING_PMM_H
|
||||||
|
#define PAGING_PMM_H
|
||||||
|
|
||||||
|
#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();
|
||||||
|
|
||||||
|
#endif
|
||||||
72
src/mem/paging/vmm.c
Normal file
72
src/mem/paging/vmm.c
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Virtual memory manager
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
The VMM (virtual memory manager) will have two roles:
|
||||||
|
- mapping pages
|
||||||
|
- unmapping pages
|
||||||
|
in a specified virtual space
|
||||||
|
|
||||||
|
compared to the PMM which allocs/frees 4kb frames ("physical pages").
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vmm.h"
|
||||||
|
#include "paging.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "pmm.h"
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
void* vmm_pt_root = 0;
|
||||||
|
|
||||||
|
// Linked list head for virtual memory objects
|
||||||
|
struct vm_object* vm_objs = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t convert_x86_vm_flags(size_t flags)
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
if (flags & VM_FLAG_WRITE)
|
||||||
|
{
|
||||||
|
value |= PTE_WRITABLE;
|
||||||
|
}
|
||||||
|
if (flags & VM_FLAG_USER)
|
||||||
|
{
|
||||||
|
value |= PTE_USER;
|
||||||
|
}
|
||||||
|
if ((flags & VM_FLAG_EXEC) == 0)
|
||||||
|
{
|
||||||
|
value |= PTE_NOEXEC;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
void vmm_setup_pt_root()
|
||||||
|
{
|
||||||
|
// We alloc a physical page (frame) for the pointer, then map it
|
||||||
|
// to virt (pointer)
|
||||||
|
uintptr_t phys = pmm_alloc();
|
||||||
|
vmm_pt_root = (void*)kernel_pml4;
|
||||||
|
paging_map_page(kernel_pml4, (uint64_t)vmm_pt_root, phys, convert_x86_vm_flags(VM_FLAG_WRITE | VM_FLAG_EXEC));
|
||||||
|
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* void* vmm_alloc(size_t length, size_t flags)
|
||||||
|
{
|
||||||
|
// We will try to allocate at least length bytes, which have to be rounded UP to
|
||||||
|
// the next page so its coherent with the PMM
|
||||||
|
size_t len = ALIGN_UP(length, PAGE_SIZE);
|
||||||
|
|
||||||
|
// Need to implement this (as linked list)
|
||||||
|
// but for now kernel heap is sufficient
|
||||||
|
// The VMM will prob be more useful when we have userspace
|
||||||
|
} */
|
||||||
|
|
||||||
|
void vmm_init()
|
||||||
|
{
|
||||||
|
vmm_setup_pt_root();
|
||||||
|
}
|
||||||
35
src/mem/paging/vmm.h
Normal file
35
src/mem/paging/vmm.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Virtual memory manager
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VMM_H
|
||||||
|
#define VMM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
This will be our linked list of virtual memory objects.
|
||||||
|
Flags here aren't x86 flags, they are platform-agnostic
|
||||||
|
kernel-defined flags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct vm_object
|
||||||
|
{
|
||||||
|
uintptr_t base;
|
||||||
|
size_t length;
|
||||||
|
size_t flags;
|
||||||
|
struct vm_object* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flags bitfield
|
||||||
|
#define VM_FLAG_NONE 0
|
||||||
|
#define VM_FLAG_WRITE (1 << 0)
|
||||||
|
#define VM_FLAG_EXEC (1 << 1)
|
||||||
|
#define VM_FLAG_USER (1 << 2)
|
||||||
|
|
||||||
|
void vmm_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
176
src/sched/process.c
Normal file
176
src/sched/process.c
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Process linked list implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "process.h"
|
||||||
|
#include "mem/heap/kheap.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "string/string.h"
|
||||||
|
#include "mem/gdt/gdt.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
|
||||||
|
struct process_t* processes_list;
|
||||||
|
struct process_t* current_process;
|
||||||
|
|
||||||
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
uint64_t* stack_top = (uint64_t*)kalloc_stack();
|
||||||
|
// push return address to the stack so when "ret" hits we jmp to exit instead of idk what
|
||||||
|
// stack grows DOWNWARDS!!
|
||||||
|
*(--stack_top) = (uint64_t)process_exit;
|
||||||
|
|
||||||
|
proc->context = ctx;
|
||||||
|
proc->context->iret_ss = KERNEL_DATA_SEGMENT; // process will live in kernel mode
|
||||||
|
proc->context->iret_rsp = (uint64_t)stack_top;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Kernel PML4 as it already maps code/stack (when switching to userland we'll have to change that)
|
||||||
|
proc->root_page_table = kernel_pml4;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (from gdt) This will switch tasks ONLY in ring 0
|
||||||
|
// KERNEL CS = 0x08
|
||||||
|
// KERNEL SS = 0x10
|
||||||
|
__attribute__((naked, noreturn))
|
||||||
|
void process_switch(uint64_t stack_addr, uint64_t code_addr)
|
||||||
|
{
|
||||||
|
asm volatile(" \
|
||||||
|
push $0x10 \n\
|
||||||
|
push %0 \n\
|
||||||
|
push $0x202 \n\
|
||||||
|
push $0x08 \n\
|
||||||
|
push %1 \n\
|
||||||
|
iretq \n\
|
||||||
|
" :: "r"(stack_addr), "r"(code_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Will be used to clean up resources (if any, when we implement it)
|
||||||
|
// Just mark as DEAD then idle. Scheduler will delete process at next timer interrupt % quantum.
|
||||||
|
void process_exit()
|
||||||
|
{
|
||||||
|
DEBUG("Exiting from process '%s'", current_process->name);
|
||||||
|
CLEAR_INTERRUPTS;
|
||||||
|
if (current_process)
|
||||||
|
{
|
||||||
|
current_process->status = DEAD;
|
||||||
|
}
|
||||||
|
SET_INTERRUPTS;
|
||||||
|
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
asm("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/sched/process.h
Normal file
42
src/sched/process.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Process definition
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROCESS_H
|
||||||
|
#define PROCESS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
READY,
|
||||||
|
RUNNING,
|
||||||
|
DEAD
|
||||||
|
} status_t;
|
||||||
|
|
||||||
|
struct process_t
|
||||||
|
{
|
||||||
|
size_t pid;
|
||||||
|
char name[PROCESS_NAME_MAX];
|
||||||
|
|
||||||
|
status_t status;
|
||||||
|
struct cpu_status_t* context;
|
||||||
|
void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511]
|
||||||
|
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_switch(uint64_t stack_addr, uint64_t code_addr);
|
||||||
|
void process_exit();
|
||||||
|
|
||||||
|
void process_display_list(struct process_t* processes_list);
|
||||||
|
|
||||||
|
#endif
|
||||||
55
src/sched/scheduler.c
Normal file
55
src/sched/scheduler.c
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Round-robin scheduler
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "process.h"
|
||||||
|
#include "mem/paging/paging.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "io/serial/serial.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Current_process gets wrong context?? (iret_rip points to other stuff than process function; like putpixel() for example)
|
||||||
|
DEBUG("current_process={pid=%u, name='%s', root_page_table[virt]=%p}", current_process->pid, current_process->name, current_process->root_page_table);
|
||||||
|
|
||||||
|
load_cr3(VIRT_TO_PHYS((uint64_t)current_process->root_page_table));
|
||||||
|
DEBUG("loaded process pml4 into cr3");
|
||||||
|
|
||||||
|
return current_process->context;
|
||||||
|
}
|
||||||
13
src/sched/scheduler.h
Normal file
13
src/sched/scheduler.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Round-robin scheduler
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCHEDULER_H
|
||||||
|
#define SCHEDULER_H
|
||||||
|
|
||||||
|
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context);
|
||||||
|
void scheduler_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
7
src/sched/task.S
Normal file
7
src/sched/task.S
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
; Task (process) switching
|
||||||
|
|
||||||
|
bits 64
|
||||||
|
|
||||||
|
global switch_to_task
|
||||||
|
switch_to_task:
|
||||||
|
|
||||||
32
src/string/string.c
Normal file
32
src/string/string.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief String manipulation utilities
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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++));
|
||||||
|
}
|
||||||
14
src/string/string.h
Normal file
14
src/string/string.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief String manipulation functions
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STRING_H
|
||||||
|
#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,5 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Programmable Interval Timer init and enabling
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -44,7 +50,7 @@ void pic_enable()
|
|||||||
// Enabling IRQ0 (unmasking it) but not the others
|
// Enabling IRQ0 (unmasking it) but not the others
|
||||||
uint8_t mask = inb(0x21);
|
uint8_t mask = inb(0x21);
|
||||||
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
|
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
|
||||||
mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
|
//mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
|
||||||
outb(0x21, mask);
|
outb(0x21, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +71,17 @@ void pit_init()
|
|||||||
outb(0x40, (divisor >> 8) & 0xFF);
|
outb(0x40, (divisor >> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait n ticks
|
||||||
|
// Given that there's a tick every 1ms, wait n milliseconds
|
||||||
|
void timer_wait(uint64_t wait_ticks)
|
||||||
|
{
|
||||||
|
uint64_t then = ticks + wait_ticks;
|
||||||
|
while (ticks < then)
|
||||||
|
{
|
||||||
|
asm("hlt");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void timer_init()
|
void timer_init()
|
||||||
{
|
{
|
||||||
// Remapping the PIC, because at startup it conflicts with
|
// Remapping the PIC, because at startup it conflicts with
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PIT functions
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef TIMER_H
|
#ifndef TIMER_H
|
||||||
#define TIMER_H
|
#define TIMER_H
|
||||||
|
|
||||||
void timer_init();
|
void timer_init();
|
||||||
|
void timer_wait(unsigned int wait_ticks);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
33
symbols.py
Normal file
33
symbols.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Make assembly file from ELF symbols map
|
||||||
|
# Then link it to kernel so it's aware of symbol names
|
||||||
|
# then we can use that for the stack trace.
|
||||||
|
|
||||||
|
print("Extracting symbols from map file to assembly...")
|
||||||
|
|
||||||
|
with open("symbols.map", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
symbols = []
|
||||||
|
for line in lines:
|
||||||
|
parts = line.split()
|
||||||
|
# output is formed like "address name"
|
||||||
|
symbols.append((parts[0], parts[1]))
|
||||||
|
|
||||||
|
with open("symbols.S", "w") as f:
|
||||||
|
f.write("section .rodata\n")
|
||||||
|
f.write("global symbol_table\n")
|
||||||
|
f.write("global symbol_count\n")
|
||||||
|
f.write("symbol_table:\n")
|
||||||
|
|
||||||
|
for i, (addr, name) in enumerate(symbols):
|
||||||
|
f.write(f" dq 0x{addr}\n")
|
||||||
|
f.write(f" dq sym_name_{i}\n")
|
||||||
|
|
||||||
|
f.write("\nsymbol_count: dq " + str(len(symbols)) + "\n\n")
|
||||||
|
|
||||||
|
for i, (addr, name) in enumerate(symbols):
|
||||||
|
# escaping quotes
|
||||||
|
safe_name = name.replace('"', '\\"')
|
||||||
|
f.write(f'sym_name_{i}: db "{safe_name}", 0\n')
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
Reference in New Issue
Block a user