Compare commits
17 Commits
9df33b49d8
...
style
| Author | SHA1 | Date | |
|---|---|---|---|
|
e5c296238c
|
|||
|
5c0d02579b
|
|||
|
8026c33639
|
|||
|
8e2a612d88
|
|||
|
9d409317e2
|
|||
|
1dd4e728d4
|
|||
|
b9c77a316a
|
|||
| 6fc28806e2 | |||
|
3f9b78b05e
|
|||
|
42c7a55d3f
|
|||
|
5e9c582833
|
|||
| 77d9df6f48 | |||
|
90dc26ee11
|
|||
|
c8a72244b1
|
|||
|
b9f55d89f6
|
|||
|
a7d9e70a61
|
|||
| 95c801b991 |
@@ -10,3 +10,7 @@ iso_root
|
|||||||
.gdb_history
|
.gdb_history
|
||||||
symbols.map
|
symbols.map
|
||||||
symbols.S
|
symbols.S
|
||||||
|
*.log
|
||||||
|
build/
|
||||||
|
compile_commands.json
|
||||||
|
.cache/
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
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!
|
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
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
|
SOURCES = src/debug/misc.c 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
|
CC_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
|
||||||
|
CC_PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable
|
||||||
|
|
||||||
|
.PHONY: build build-iso debug debug2 run clean
|
||||||
|
|
||||||
build:
|
build:
|
||||||
rm -f *.o
|
mkdir -p build
|
||||||
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
|
rm -f *.o build/*.o
|
||||||
objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o
|
x86_64-elf-gcc -g -c -Isrc $(SOURCES) $(CC_PROBLEMATIC_FLAGS) $(CC_FLAGS)
|
||||||
nasm -f elf64 src/idt/idt.S -o idt_stub.o
|
mv *.o build/
|
||||||
nasm -f elf64 src/entry.S -o entry.o
|
nasm -f elf64 src/idt/idt.S -o build/idt_stub.o
|
||||||
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
x86_64-elf-ld -o pepperk -T linker.ld build/*.o
|
||||||
nm -n pepperk | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
|
nm -n pepperk | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
|
||||||
python3 symbols.py
|
python3 symbols.py
|
||||||
nasm -f elf64 symbols.S -o symbols.o
|
nasm -f elf64 symbols.S -o build/symbols.o
|
||||||
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
x86_64-elf-ld -o pepperk -T linker.ld build/*.o
|
||||||
|
|
||||||
limine/limine:
|
limine/limine:
|
||||||
rm -rf limine
|
rm -rf limine
|
||||||
@@ -37,7 +40,7 @@ build-iso: limine/limine build
|
|||||||
./limine/limine bios-install pepper.iso
|
./limine/limine bios-install pepper.iso
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
|
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -D qemu.log -no-reboot -no-shutdown &
|
||||||
gdb pepperk --command=debug.gdb
|
gdb pepperk --command=debug.gdb
|
||||||
|
|
||||||
debug2:
|
debug2:
|
||||||
@@ -48,4 +51,4 @@ run: build-iso
|
|||||||
/usr/bin/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 symbols.map symbols.S pepperk iso_root pepper.iso limine
|
rm -rf *.o symbols.map symbols.S pepperk iso_root pepper.iso limine build/*.o
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
First install the dependencies: `sudo apt install python3 xorriso make qemu-system`
|
First install the dependencies: `sudo apt install python3 xorriso make qemu-system`
|
||||||
|
|
||||||
|
Also, you have to get an x86_64 toolchain for compilation. The easiest way to do that on most systems is to install it from Homebrew:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew install x86_64-elf-gcc
|
||||||
|
```
|
||||||
|
|
||||||
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`
|
||||||
|
|
||||||
@@ -13,11 +19,10 @@ The basics that I'm targeting are:
|
|||||||
|
|
||||||
### Basic utility of what we call a "kernel"
|
### 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
|
- Implement tasks, and task switching + context switching and spinlock acquire/release
|
||||||
- Load an executable
|
- Load an executable
|
||||||
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
|
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
|
||||||
- Getting to userspace (syscalls)
|
- Getting to userspace (ring 3 switching, syscall interface)
|
||||||
- Porting musl libc or equivalent
|
- Porting musl libc or equivalent
|
||||||
|
|
||||||
### Scalability/maintenance/expansion features
|
### Scalability/maintenance/expansion features
|
||||||
@@ -26,7 +31,6 @@ The basics that I'm targeting are:
|
|||||||
- SOME error handling in functions
|
- SOME error handling in functions
|
||||||
- Unit tests
|
- Unit tests
|
||||||
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
|
- 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
|
### Optional features
|
||||||
|
|
||||||
@@ -41,8 +45,10 @@ PepperOS wouldn't be possible without the following freely-licensed software:
|
|||||||
|
|
||||||
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
|
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
|
||||||
- Marco Paland's freestanding [printf implementation](https://github.com/mpaland)
|
- Marco Paland's freestanding [printf implementation](https://github.com/mpaland)
|
||||||
- the [ZAP](https://www.zap.org.au/projects/console-fonts-zap/) PSF console fonts
|
- Mintuski's [Flanterm](https://codeberg.org/Mintsuki/Flanterm) terminal emulator
|
||||||
|
|
||||||
...and without these amazing resources:
|
...and without these amazing resources:
|
||||||
|
|
||||||
- the [OSDev](https://osdev.org) wiki & forums
|
- the [OSDev](https://osdev.org) wiki & forums
|
||||||
|
- Intel 64 and IA-32 Architectures Software Developer's Manual
|
||||||
|
- Documentation for the [GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc/)
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
target remote localhost:1234
|
target remote localhost:1234
|
||||||
set disassembly-flavor intel
|
set disassembly-flavor intel
|
||||||
display/4i $rip
|
display/4i $rip
|
||||||
|
|
||||||
|
# Trying to debug that flanterm page fault
|
||||||
|
|
||||||
|
# b plot_char_unscaled_uncanvas if $rdi == 0 || $rsi == 0 || $rdx == 0 || $r10 == 0
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Pepper kernel coding style
|
||||||
|
|
||||||
|
This document describes the coding style for the Pepper kernel. It is used as a guideline across all source files.
|
||||||
|
|
||||||
|
## Indentation
|
||||||
|
|
||||||
|
Indentations should be 4 characters long.
|
||||||
|
|
||||||
|
## Line length
|
||||||
|
|
||||||
|
Lines should not be more than 100 characters long. Exceptions is made for printing strings.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Variables should be declared at most once per line.
|
||||||
|
|
||||||
|
## Braces
|
||||||
|
|
||||||
|
Non-function statement blocks should have an opening brace last on the line, and a closing brace first. Exception is made for `else`, `else if` statements and the like:
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (something) {
|
||||||
|
do_something();
|
||||||
|
} else if (something_else) {
|
||||||
|
do_something_else();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Having no braces for a single statement structure is fine.
|
||||||
|
|
||||||
|
Functions should have their opening brace on a separate line, and the same goes for the closing brace:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void function()
|
||||||
|
{
|
||||||
|
do_something();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Spaces
|
||||||
|
|
||||||
|
Use a space after `if, switch, case, for, do, while` keywords, but not for `sizeof, typeof, alignof, __attribute__` and the like.
|
||||||
|
|
||||||
|
For pointers, the asterisk should always be placed adjacent to the type name, like `char* str;`.
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
Functions should be named with whole words, beginning with the corresponding name of the module in the kernel (the parent folder). Words should be spaced with underscores, like so:
|
||||||
|
|
||||||
|
```c
|
||||||
|
serial_init(void* ptr, char* str, int foo);
|
||||||
|
```
|
||||||
|
|
||||||
|
Constants should be named in all caps, separated by underscores:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define MAX_HEAP_SIZE 0x1000
|
||||||
|
```
|
||||||
|
|
||||||
|
Global variables need to have descriptive names. Local variables can be kept short (especially for loop counters).
|
||||||
|
|
||||||
|
## Typedefs
|
||||||
|
|
||||||
|
Structures should not be `typedef`'d. However using `typedef` for an enumeration is fine.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
Functions should be short, simple, and only do one thing.
|
||||||
|
|
||||||
|
Function prototypes should include parameter names and their data types.
|
||||||
|
|
||||||
|
## Commenting
|
||||||
|
|
||||||
|
Comments should describe what a function does and why, not how it does it. The preferred way of commenting functions is the following:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/*
|
||||||
|
* function_name - Function brief description
|
||||||
|
* @arg1: Argument 1 description
|
||||||
|
* @arg2: Argument 2 description
|
||||||
|
*
|
||||||
|
* A longer description can be featured here, explaining more
|
||||||
|
* in detail what the function does and why it does it.
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kernel messages
|
||||||
|
|
||||||
|
When printing kernel messages with the `DEBUG` macro, they should start with a capital letter.
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
|
||||||
|
Some of the elements here are inspired by the [Linux kernel coding style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html).
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
OUTPUT_FORMAT(elf64-x86-64)
|
OUTPUT_FORMAT(elf64-x86-64)
|
||||||
|
|
||||||
ENTRY(_start)
|
ENTRY(kmain)
|
||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-4
@@ -1,33 +1,34 @@
|
|||||||
/*
|
/*
|
||||||
* @author xamidev <xamidev@riseup.net>
|
* @author xamidev <xamidev@riseup.net>
|
||||||
* @brief Limine requests for boot
|
* @brief Limine requests for boot
|
||||||
|
* @description
|
||||||
|
* The kernel makes a few requests to the Limine bootloader
|
||||||
|
* in order to get precious information about the system.
|
||||||
|
* We get a framebuffer, a memory map, the address of the
|
||||||
|
* kernel in memory, and the Higher Half Direct Map offset.
|
||||||
* @license GPL-3.0-only
|
* @license GPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
|
|
||||||
// Framebuffer request
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile struct limine_framebuffer_request framebuffer_request = {
|
volatile struct limine_framebuffer_request framebuffer_request = {
|
||||||
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
||||||
.revision = 0
|
.revision = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Memory map request
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile struct limine_memmap_request memmap_request = {
|
volatile struct limine_memmap_request memmap_request = {
|
||||||
.id = LIMINE_MEMMAP_REQUEST,
|
.id = LIMINE_MEMMAP_REQUEST,
|
||||||
.revision = 0
|
.revision = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Higher Half Direct Map
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile struct limine_hhdm_request hhdm_request = {
|
volatile struct limine_hhdm_request hhdm_request = {
|
||||||
.id = LIMINE_HHDM_REQUEST,
|
.id = LIMINE_HHDM_REQUEST,
|
||||||
.revision = 0
|
.revision = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Executable Address/Kernel Address (find base phys/virt address of kernel)
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile struct limine_kernel_address_request kerneladdr_request = {
|
volatile struct limine_kernel_address_request kerneladdr_request = {
|
||||||
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
||||||
|
|||||||
+9
-2
@@ -11,7 +11,7 @@
|
|||||||
#define PEPPEROS_VERSION_MAJOR "0"
|
#define PEPPEROS_VERSION_MAJOR "0"
|
||||||
#define PEPPEROS_VERSION_MINOR "0"
|
#define PEPPEROS_VERSION_MINOR "0"
|
||||||
#define PEPPEROS_VERSION_PATCH "58"
|
#define PEPPEROS_VERSION_PATCH "58"
|
||||||
#define PEPPEROS_SPLASH "pepperOS version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\n"
|
#define PEPPEROS_SPLASH "\x1b[38;5;196mPepperOS\x1b[0m version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\n"
|
||||||
|
|
||||||
/* process */
|
/* process */
|
||||||
#define PROCESS_NAME_MAX 64
|
#define PROCESS_NAME_MAX 64
|
||||||
@@ -28,11 +28,18 @@
|
|||||||
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
|
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
|
||||||
#define KERNEL_SIZE 0x200000
|
#define KERNEL_SIZE 0x200000
|
||||||
#define KERNEL_STACK_SIZE 65536
|
#define KERNEL_STACK_SIZE 65536
|
||||||
|
#define KERNEL_IDT_ENTRIES 33
|
||||||
|
|
||||||
|
/* paging */
|
||||||
|
#define PAGING_MAX_PHYS 0x100000000
|
||||||
|
|
||||||
/* heap */
|
/* heap */
|
||||||
#define KHEAP_SIZE (16*1024*1024)
|
#define KHEAP_SIZE (32*1024*1024)
|
||||||
|
|
||||||
/* term */
|
/* term */
|
||||||
#define TERM_HISTORY_MAX_LINES 256
|
#define TERM_HISTORY_MAX_LINES 256
|
||||||
|
|
||||||
|
/* time */
|
||||||
|
#define TIMER_FREQUENCY 1000
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Miscellaneous debug features
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include "limine.h"
|
||||||
|
#include "string/string.h"
|
||||||
|
|
||||||
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memmap_display - displays a memory map
|
||||||
|
* @response: Limine memory map response
|
||||||
|
*
|
||||||
|
* Displays the memory map we get from Limine
|
||||||
|
* to see different regions, their sizes, and
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hhdm_display - displays the HHDM offset
|
||||||
|
* @hhdm: Limine HHDM offset response
|
||||||
|
*/
|
||||||
|
void hhdm_display(struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* boot_mem_display - displays all memory info
|
||||||
|
*/
|
||||||
|
void boot_mem_display()
|
||||||
|
{
|
||||||
|
memmap_display(boot_ctx.mmap);
|
||||||
|
hhdm_display(boot_ctx.hhdm);
|
||||||
|
DEBUG("Kernel is at phys_base=0x%p virt_base=0x%p", boot_ctx.kaddr->physical_base, boot_ctx.kaddr->virtual_base);
|
||||||
|
}
|
||||||
+37
-3
@@ -1,25 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel panic
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "idt/idt.h"
|
#include "idt/idt.h"
|
||||||
#include "io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* panic - Kernel panic
|
||||||
|
* @ctx: CPU context (optional)
|
||||||
|
* @str: Error message
|
||||||
|
*
|
||||||
|
* Ends execution of the kernel in case of an unrecoverable error.
|
||||||
|
* Will display to terminal if it is initialized, otherwise serial only.
|
||||||
|
* Can be called with or without a CPU context.
|
||||||
|
*/
|
||||||
void panic(struct cpu_status_t* ctx, const char* str)
|
void panic(struct cpu_status_t* ctx, const char* str)
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
if (ctx == NULL)
|
if (ctx == NULL) {
|
||||||
{
|
|
||||||
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m Something went horribly wrong! (no cpu ctx)");
|
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m Something went horribly wrong! (no cpu ctx)");
|
||||||
|
fctprintf((void*)&skputc, 0, "\x1b[38;5;231m\x1b[48;5;27m");
|
||||||
DIE_DEBUG(str);
|
DIE_DEBUG(str);
|
||||||
|
fctprintf((void*)&skputc, 0, "\x1b[0m");
|
||||||
skputc('\r');
|
skputc('\r');
|
||||||
skputc('\n');
|
skputc('\n');
|
||||||
DEBUG("\x1b[38;5;231m\x1b[48;5;196mend Kernel panic - halting...\x1b[0m");
|
DEBUG("\x1b[38;5;231m\x1b[48;5;196mend Kernel panic - halting...\x1b[0m");
|
||||||
|
|
||||||
|
if (init.terminal) {
|
||||||
|
printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232m Something went horribly wrong! (no cpu ctx)");
|
||||||
|
printf("\r\n%s\r\n\x1b[38;5;231mend Kernel panic - halting...\x1b[0m", str);
|
||||||
|
}
|
||||||
|
|
||||||
hcf();
|
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...",
|
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...\x1b[0m",
|
||||||
ctx->iret_rip,
|
ctx->iret_rip,
|
||||||
str,
|
str,
|
||||||
ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi,
|
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);
|
ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags);
|
||||||
|
|
||||||
|
if (init.terminal) {
|
||||||
|
printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232mat 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...\x1b[0m",
|
||||||
|
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);
|
debug_stack_trace(100);
|
||||||
|
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
+47
-19
@@ -1,35 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Stack trace tools
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* debug_stack_trace - Prints the stack trace
|
||||||
|
* @max_frames: Maximum amount of stack frames to walk
|
||||||
|
*
|
||||||
|
* Walks back the stack and gets all return values (RIP)
|
||||||
|
* and prints them to the DEBUG interface.
|
||||||
|
*/
|
||||||
void debug_stack_trace(unsigned int max_frames)
|
void debug_stack_trace(unsigned int max_frames)
|
||||||
{
|
{
|
||||||
DEBUG("*** begin stack trace ***");
|
DEBUG("*** begin stack trace ***");
|
||||||
|
if (init.terminal) {
|
||||||
|
printf("\r\n\x1b[48;5;232m\x1b[38;5;231m*** begin stack trace ***\r\n");
|
||||||
|
}
|
||||||
// Thanks GCC :)
|
// Thanks GCC :)
|
||||||
uintptr_t* rbp = (uintptr_t*)__builtin_frame_address(0);
|
uintptr_t* rbp = (uintptr_t*)__builtin_frame_address(0);
|
||||||
|
|
||||||
for (unsigned int frame=0; frame<max_frames && rbp != NULL; frame++)
|
for (unsigned int frame=0; frame<max_frames && rbp != NULL; frame++) {
|
||||||
{
|
|
||||||
// Return address, 1 word above saved rbp
|
// Return address, 1 word above saved rbp
|
||||||
uintptr_t rip = rbp[1];
|
uintptr_t rip = rbp[1];
|
||||||
uintptr_t offset = 0;
|
uintptr_t offset = 0;
|
||||||
const char* name = debug_find_symbol(rip, &offset);
|
const char* name = debug_find_symbol(rip, &offset);
|
||||||
DEBUG("[%u] <0x%p> (%s+0x%x)", frame, (void*)rip, name, offset);
|
DEBUG("[%u] <0x%p> (%s+0x%x)", frame, (void*)rip, name, offset);
|
||||||
|
|
||||||
|
if (init.terminal) {
|
||||||
|
printf("[%u] <0x%p> (%s+0x%x)\r\n", frame, (void*)rip, name, offset);
|
||||||
|
}
|
||||||
|
|
||||||
uintptr_t* next_rbp = (uintptr_t*)rbp[0];
|
uintptr_t* next_rbp = (uintptr_t*)rbp[0];
|
||||||
|
|
||||||
// invalid rbp or we're at the end
|
// Invalid rbp or we're at the end
|
||||||
if (next_rbp <= rbp || next_rbp == NULL)
|
if (next_rbp <= rbp || next_rbp == NULL) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
rbp = next_rbp;
|
rbp = next_rbp;
|
||||||
}
|
}
|
||||||
|
if (init.terminal) {
|
||||||
|
printf("*** end stack trace ***\x1b[0m");
|
||||||
|
}
|
||||||
DEBUG("*** end stack trace ***");
|
DEBUG("*** end stack trace ***");
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
uint64_t addr;
|
uint64_t addr;
|
||||||
const char *name;
|
const char *name;
|
||||||
} __attribute__((packed)) kernel_symbol_t;
|
} __attribute__((packed)) kernel_symbol_t;
|
||||||
@@ -37,11 +59,19 @@ typedef struct
|
|||||||
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
|
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
|
||||||
__attribute__((weak)) extern uint64_t symbol_count;
|
__attribute__((weak)) extern uint64_t symbol_count;
|
||||||
|
|
||||||
// binary search
|
/*
|
||||||
|
* debug_find_symbol - Finds the symbol name associated to an address
|
||||||
|
* @rip: Pointer to executable code
|
||||||
|
* @offset: Out pointer to reference the offset in the found function, if any
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <symbol name> - symbol name
|
||||||
|
* "???" - no symbol table found
|
||||||
|
* "unknown" - symbol table found, but address isn't in the table
|
||||||
|
*/
|
||||||
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
||||||
{
|
{
|
||||||
if (!symbol_table || symbol_count == 0)
|
if (!symbol_table || symbol_count == 0) {
|
||||||
{
|
|
||||||
if (offset) *offset = 0;
|
if (offset) *offset = 0;
|
||||||
return "???";
|
return "???";
|
||||||
}
|
}
|
||||||
@@ -49,11 +79,9 @@ const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
|||||||
int low = 0, high = (int)symbol_count - 1;
|
int low = 0, high = (int)symbol_count - 1;
|
||||||
int best = -1;
|
int best = -1;
|
||||||
|
|
||||||
while (low <= high)
|
while (low <= high) {
|
||||||
{
|
|
||||||
int mid = (low + high) / 2;
|
int mid = (low + high) / 2;
|
||||||
if (symbol_table[mid].addr <= rip)
|
if (symbol_table[mid].addr <= rip) {
|
||||||
{
|
|
||||||
best = mid;
|
best = mid;
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -61,15 +89,15 @@ const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best != -1)
|
if (best != -1) {
|
||||||
{
|
if (offset) {
|
||||||
if (offset)
|
|
||||||
{
|
|
||||||
*offset = rip - symbol_table[best].addr;
|
*offset = rip - symbol_table[best].addr;
|
||||||
}
|
}
|
||||||
return symbol_table[best].name;
|
return symbol_table[best].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset) *offset = 0;
|
if (offset) {
|
||||||
|
*offset = 0;
|
||||||
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
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
|
|
||||||
+2
-1
@@ -151,12 +151,13 @@ vector_7_handler:
|
|||||||
align 16
|
align 16
|
||||||
vector_8_handler:
|
vector_8_handler:
|
||||||
; No error code, we only push vector number
|
; No error code, we only push vector number
|
||||||
push qword 1
|
push qword 8
|
||||||
jmp interrupt_stub
|
jmp interrupt_stub
|
||||||
|
|
||||||
; Coprocessor Segment Overrun
|
; Coprocessor Segment Overrun
|
||||||
align 16
|
align 16
|
||||||
vector_9_handler:
|
vector_9_handler:
|
||||||
|
push qword 0
|
||||||
push qword 9
|
push qword 9
|
||||||
jmp interrupt_stub
|
jmp interrupt_stub
|
||||||
|
|
||||||
|
|||||||
+81
-41
@@ -22,8 +22,14 @@ struct idtr idt_reg;
|
|||||||
extern char vector_0_handler[];
|
extern char vector_0_handler[];
|
||||||
|
|
||||||
// Timer ticks
|
// Timer ticks
|
||||||
extern uint64_t ticks;
|
extern volatile uint64_t ticks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_set_entry - Sets an Interrupt Descriptor Table entry
|
||||||
|
* @vector: Vector number in the IDT
|
||||||
|
* @handler: Pointer to the executable Interrupt Service Routine
|
||||||
|
* @dpl: Desired privilege level
|
||||||
|
*/
|
||||||
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
||||||
{
|
{
|
||||||
uint64_t handler_addr = (uint64_t)handler;
|
uint64_t handler_addr = (uint64_t)handler;
|
||||||
@@ -42,6 +48,10 @@ void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
|||||||
entry->ist = 0;
|
entry->ist = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_load - Loads the Interrupt Descriptor Table
|
||||||
|
* @idt_addr: Address to the IDT
|
||||||
|
*/
|
||||||
void idt_load(void* idt_addr)
|
void idt_load(void* idt_addr)
|
||||||
{
|
{
|
||||||
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
|
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
|
||||||
@@ -50,12 +60,15 @@ void idt_load(void* idt_addr)
|
|||||||
asm volatile("lidt %0" :: "m"(idt_reg));
|
asm volatile("lidt %0" :: "m"(idt_reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_init - Initializes the Interrupt Descriptor Table
|
||||||
|
*
|
||||||
|
* Sets all IDT entries and their corresponding service routines,
|
||||||
|
* then loads it.
|
||||||
|
*/
|
||||||
void idt_init()
|
void idt_init()
|
||||||
{
|
{
|
||||||
// We set 256 entries, but we have only the first few stubs.
|
for (size_t i=0; i<=KERNEL_IDT_ENTRIES; i++) {
|
||||||
// Undefined behavior?
|
|
||||||
for (size_t i=0; i<256; i++)
|
|
||||||
{
|
|
||||||
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
|
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
|
||||||
idt_set_entry(i, vector_0_handler + (i*16), 0);
|
idt_set_entry(i, vector_0_handler + (i*16), 0);
|
||||||
}
|
}
|
||||||
@@ -63,6 +76,15 @@ void idt_init()
|
|||||||
DEBUG("IDT initialized");
|
DEBUG("IDT initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read_cr2 - Reads the CR2 register
|
||||||
|
*
|
||||||
|
* This function is useful because it gets the address
|
||||||
|
* that the CPU tried to access in the case of a #PF.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %val - CR2 register value
|
||||||
|
*/
|
||||||
static inline uint64_t read_cr2(void)
|
static inline uint64_t read_cr2(void)
|
||||||
{
|
{
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
@@ -70,6 +92,15 @@ static inline uint64_t read_cr2(void)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* page_fault_handler - Handler for #PF
|
||||||
|
* @ctx: CPU context
|
||||||
|
*
|
||||||
|
* Shows detail about a #PF, especially what instruction (RIP)
|
||||||
|
* caused it, and what address access (CR2) caused it.
|
||||||
|
* Also displays an interpretation of the thrown error code.
|
||||||
|
* Then halts the system. We could implement demand paging later.
|
||||||
|
*/
|
||||||
static void page_fault_handler(struct cpu_status_t* ctx)
|
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
|
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
|
||||||
@@ -91,6 +122,13 @@ static void page_fault_handler(struct cpu_status_t* ctx)
|
|||||||
panic(ctx, "page fault");
|
panic(ctx, "page fault");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gp_fault_handler - Handler for #GP
|
||||||
|
* @ctx: CPU context
|
||||||
|
*
|
||||||
|
* Shows detail about a General Protection Fault,
|
||||||
|
* and what may have caused it. Halts the system.
|
||||||
|
*/
|
||||||
static void gp_fault_handler(struct cpu_status_t* ctx)
|
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",
|
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
|
||||||
@@ -99,8 +137,7 @@ static void gp_fault_handler(struct cpu_status_t* ctx)
|
|||||||
(ctx->error_code == 0) ? "NOT_SEGMENT_RELATED" : "SEGMENT_RELATED");
|
(ctx->error_code == 0) ? "NOT_SEGMENT_RELATED" : "SEGMENT_RELATED");
|
||||||
|
|
||||||
// Segment-related
|
// Segment-related
|
||||||
if (ctx->error_code != 0)
|
if (ctx->error_code != 0) {
|
||||||
{
|
|
||||||
bool is_external = CHECK_BIT(ctx->error_code, 0);
|
bool is_external = CHECK_BIT(ctx->error_code, 0);
|
||||||
// is it IDT, GDT, LDT?
|
// is it IDT, GDT, LDT?
|
||||||
uint8_t table = ctx->error_code & 0x6; // 0b110 (isolate table)
|
uint8_t table = ctx->error_code & 0x6; // 0b110 (isolate table)
|
||||||
@@ -117,82 +154,89 @@ static void gp_fault_handler(struct cpu_status_t* ctx)
|
|||||||
panic(ctx, "gp fault");
|
panic(ctx, "gp fault");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* interrupt_dispatch - Interrupt dispatcher
|
||||||
|
* @context: CPU context
|
||||||
|
*
|
||||||
|
* This function is where all interrupt routines go, after they passed
|
||||||
|
* through their corresponding vector handler in the IDT assembly stub.
|
||||||
|
* It catches all exceptions.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <context> - CPU context after interrupt
|
||||||
|
*/
|
||||||
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)
|
if (context == NULL) {
|
||||||
{
|
|
||||||
panic(NULL, "Interrupt dispatch recieved NULL context!");
|
panic(NULL, "Interrupt dispatch recieved NULL context!");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(context->vector_number)
|
switch(context->vector_number) {
|
||||||
{
|
|
||||||
case 0:
|
case 0:
|
||||||
DEBUG("Divide Error!");
|
panic(context, "Divide Error");
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
DEBUG("Debug Exception!");
|
panic(context, "Debug Exception");
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
DEBUG("NMI Interrupt!");
|
panic(context, "NMI Interrupt");
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
DEBUG("Breakpoint Interrupt!");
|
panic(context, "Breakpoint Interrupt");
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
DEBUG("Overflow Trap!");
|
panic(context, "Overflow Trap");
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
DEBUG("BOUND Range Exceeded!");
|
panic(context, "BOUND Range Exceeded");
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
DEBUG("Invalid Opcode!");
|
panic(context, "Invalid Opcode");
|
||||||
panic(context, "Invalid Opcode!");
|
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
DEBUG("Device Not Available!");
|
panic(context, "Device Not Available");
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
DEBUG("Double Fault!");
|
panic(context, "Double Fault");
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
DEBUG("Coprocessor Segment Overrun!");
|
panic(context, "Coprocessor Segment Overrun");
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
DEBUG("Invalid TSS!");
|
panic(context, "Invalid TSS");
|
||||||
break;
|
break;
|
||||||
case 11:
|
case 11:
|
||||||
DEBUG("Segment Not Present!");
|
panic(context, "Segment Not Present");
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
DEBUG("Stack-Segment Fault!");
|
panic(context, "Stack-Segment Fault");
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
gp_fault_handler(context);
|
gp_fault_handler(context);
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
// Better debugging for page faults...
|
|
||||||
page_fault_handler(context);
|
page_fault_handler(context);
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
|
panic(context, "Intel Reserved Interrupt (Achievement unlocked: How Did We Get Here?)");
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
DEBUG("x87 Floating-Point Error!");
|
panic(context, "x87 Floating-Point Error");
|
||||||
break;
|
break;
|
||||||
case 17:
|
case 17:
|
||||||
DEBUG("Alignment Check Fault!");
|
panic(context, "Alignment Check Fault");
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
DEBUG("Machine Check!");
|
panic(context, "Machine Check");
|
||||||
break;
|
break;
|
||||||
case 19:
|
case 19:
|
||||||
DEBUG("SIMD Floating-Point Exception!");
|
panic(context, "SIMD Floating-Point Exception");
|
||||||
break;
|
break;
|
||||||
case 20:
|
case 20:
|
||||||
DEBUG("Virtualization Exception!");
|
panic(context, "Virtualization Exception");
|
||||||
break;
|
break;
|
||||||
case 21:
|
case 21:
|
||||||
DEBUG("Control Protection Exception!");
|
panic(context, "Control Protection Exception");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 32: // Timer Interrupt
|
case 32: // Timer Interrupt
|
||||||
@@ -200,23 +244,19 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
|||||||
// 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)
|
if (ticks % SCHEDULER_QUANTUM == 0) {
|
||||||
{
|
|
||||||
return scheduler_schedule(context);
|
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: // Keyboard Interrupt
|
||||||
DEBUG("Keyboard Interrupt");
|
|
||||||
keyboard_handler();
|
keyboard_handler();
|
||||||
|
outb(0x20, 0x20);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG("Unexpected interrupt");
|
DEBUG("Unexpected Interrupt");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-8
@@ -9,10 +9,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void idt_init();
|
void idt_init(void);
|
||||||
|
|
||||||
struct interrupt_descriptor
|
struct interrupt_descriptor {
|
||||||
{
|
|
||||||
uint16_t address_low;
|
uint16_t address_low;
|
||||||
uint16_t selector;
|
uint16_t selector;
|
||||||
uint8_t ist;
|
uint8_t ist;
|
||||||
@@ -22,8 +21,7 @@ struct interrupt_descriptor
|
|||||||
uint32_t reserved;
|
uint32_t reserved;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct idtr
|
struct idtr {
|
||||||
{
|
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
uint64_t base;
|
uint64_t base;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
@@ -31,8 +29,7 @@ struct idtr
|
|||||||
// All general-purpose registers (except rsp) as stored on the stack,
|
// All general-purpose registers (except rsp) as stored on the stack,
|
||||||
// plus the values we pushed (vector number, error code) and the iret frame
|
// plus the values we pushed (vector number, error code) and the iret frame
|
||||||
// In reverse order because the stack grows downwards.
|
// In reverse order because the stack grows downwards.
|
||||||
struct cpu_status_t
|
struct cpu_status_t {
|
||||||
{
|
|
||||||
uint64_t r15;
|
uint64_t r15;
|
||||||
uint64_t r14;
|
uint64_t r14;
|
||||||
uint64_t r13;
|
uint64_t r13;
|
||||||
@@ -42,7 +39,6 @@ struct cpu_status_t
|
|||||||
uint64_t r9;
|
uint64_t r9;
|
||||||
uint64_t r8;
|
uint64_t r8;
|
||||||
uint64_t rbp;
|
uint64_t rbp;
|
||||||
//uint64_t rsp;
|
|
||||||
uint64_t rdi;
|
uint64_t rdi;
|
||||||
uint64_t rsi;
|
uint64_t rsi;
|
||||||
uint64_t rdx;
|
uint64_t rdx;
|
||||||
|
|||||||
+35
-20
@@ -17,6 +17,8 @@ uint8_t key_status = 0b00000000;
|
|||||||
unsigned char* keymap;
|
unsigned char* keymap;
|
||||||
unsigned char* keymap_shifted;
|
unsigned char* keymap_shifted;
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
unsigned char kbdus[128] =
|
unsigned char kbdus[128] =
|
||||||
{
|
{
|
||||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
|
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
|
||||||
@@ -154,16 +156,22 @@ unsigned char kbdfr_shifted[128] =
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_handler - Keyboard event handler
|
||||||
|
*
|
||||||
|
* Is called from the interrupt dispatcher.
|
||||||
|
* When a key is pressed or released, we get a scancode, and
|
||||||
|
* it is then translated to an ASCII character.
|
||||||
|
* Left Shift, Ctrl, and Alt keys are also taken into consideration.
|
||||||
|
*/
|
||||||
void keyboard_handler()
|
void keyboard_handler()
|
||||||
{
|
{
|
||||||
unsigned char scancode = inb(0x60);
|
unsigned char scancode = inb(0x60);
|
||||||
|
|
||||||
// Key release (bit 7 set)
|
// Key release (bit 7 set)
|
||||||
if (scancode & 0x80)
|
if (scancode & 0x80) {
|
||||||
{
|
|
||||||
unsigned char code = scancode & 0x7F;
|
unsigned char code = scancode & 0x7F;
|
||||||
switch (code)
|
switch (code) {
|
||||||
{
|
|
||||||
// Clear the corresponding bit if corresponding key is released
|
// Clear the corresponding bit if corresponding key is released
|
||||||
case LEFT_SHIFT_PRESSED:
|
case LEFT_SHIFT_PRESSED:
|
||||||
case RIGHT_SHIFT_PRESSED:
|
case RIGHT_SHIFT_PRESSED:
|
||||||
@@ -176,16 +184,10 @@ void keyboard_handler()
|
|||||||
key_status &= ~ALT_PRESSED_BIT;
|
key_status &= ~ALT_PRESSED_BIT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send EOI
|
|
||||||
outb(0x20, 0x20);
|
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Key press
|
// Key press
|
||||||
switch (scancode)
|
switch (scancode) {
|
||||||
{
|
|
||||||
// Set bits for corresponding special key press
|
// Set bits for corresponding special key press
|
||||||
case LEFT_SHIFT_PRESSED:
|
case LEFT_SHIFT_PRESSED:
|
||||||
case RIGHT_SHIFT_PRESSED:
|
case RIGHT_SHIFT_PRESSED:
|
||||||
@@ -200,29 +202,36 @@ void keyboard_handler()
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
// Avoiding buffer overflow from extended keys lol
|
||||||
|
if (scancode < 128) {
|
||||||
// Should we get a SHIFTED char or a regular one?
|
// Should we get a SHIFTED char or a regular one?
|
||||||
unsigned char c = (key_status & SHIFT_PRESSED_BIT) ? keymap_shifted[scancode] : keymap[scancode];
|
unsigned char c = (key_status & SHIFT_PRESSED_BIT) ? keymap_shifted[scancode] : keymap[scancode];
|
||||||
|
|
||||||
if (c)
|
if (c) {
|
||||||
{
|
if (c == '\n') {
|
||||||
|
_putchar('\r');
|
||||||
|
}
|
||||||
// Should probably have a keyboard buffer here... instead of this
|
// Should probably have a keyboard buffer here... instead of this
|
||||||
_putchar(c);
|
_putchar(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// End of Interrupt (to master PIC)
|
|
||||||
outb(0x20, 0x20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_init - Keyboard initialization
|
||||||
|
* @layout: Desired layout
|
||||||
|
*
|
||||||
|
* Prepares the PS/2 keyboard to recieve input.
|
||||||
|
*/
|
||||||
void keyboard_init(unsigned char layout)
|
void keyboard_init(unsigned char layout)
|
||||||
{
|
{
|
||||||
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
|
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
|
||||||
|
|
||||||
// Keyboard layout selection
|
// Keyboard layout selection
|
||||||
switch (layout)
|
switch (layout) {
|
||||||
{
|
|
||||||
case US:
|
case US:
|
||||||
keymap = kbdus;
|
keymap = kbdus;
|
||||||
keymap_shifted = kbdus_shifted;
|
keymap_shifted = kbdus_shifted;
|
||||||
@@ -233,14 +242,20 @@ void keyboard_init(unsigned char layout)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
skputs("Unsupported layout.");
|
panic(NULL, "Unsupported keyboard layout");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush keyboard buffer
|
||||||
|
while (inb(0x64) & 1) {
|
||||||
|
inb(0x60);
|
||||||
|
}
|
||||||
|
|
||||||
// Unmask IRQ1
|
// Unmask IRQ1
|
||||||
uint8_t mask = inb(0x21);
|
uint8_t mask = inb(0x21);
|
||||||
mask &= ~(1 << 1);
|
mask &= ~(1 << 1);
|
||||||
outb(0x21, mask);
|
outb(0x21, mask);
|
||||||
|
|
||||||
DEBUG("PS/2 Keyboard initialized");
|
DEBUG("PS/2 Keyboard initialized");
|
||||||
|
init.keyboard = true;
|
||||||
}
|
}
|
||||||
+4
-7
@@ -7,21 +7,19 @@
|
|||||||
#ifndef PS2_H
|
#ifndef PS2_H
|
||||||
#define PS2_H
|
#define PS2_H
|
||||||
|
|
||||||
void keyboard_handler();
|
void keyboard_handler(void);
|
||||||
|
|
||||||
#define SHIFT_PRESSED_BIT 0b00000001
|
#define SHIFT_PRESSED_BIT 0b00000001
|
||||||
#define ALT_PRESSED_BIT 0b00000010
|
#define ALT_PRESSED_BIT 0b00000010
|
||||||
#define CTRL_PRESSED_BIT 0b00000100
|
#define CTRL_PRESSED_BIT 0b00000100
|
||||||
|
|
||||||
enum SpecialKeys
|
enum SpecialKeys {
|
||||||
{
|
|
||||||
SHIFT = 255,
|
SHIFT = 255,
|
||||||
ALT = 254,
|
ALT = 254,
|
||||||
CTRL = 253
|
CTRL = 253
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SpecialScancodes
|
enum SpecialScancodes {
|
||||||
{
|
|
||||||
LEFT_SHIFT_PRESSED = 0x2A,
|
LEFT_SHIFT_PRESSED = 0x2A,
|
||||||
LEFT_SHIFT_RELEASED = 0xAA,
|
LEFT_SHIFT_RELEASED = 0xAA,
|
||||||
RIGHT_SHIFT_PRESSED = 0x36,
|
RIGHT_SHIFT_PRESSED = 0x36,
|
||||||
@@ -32,8 +30,7 @@ enum SpecialScancodes
|
|||||||
ALT_RELEASED = 0xB8
|
ALT_RELEASED = 0xB8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum KeyboardLayout
|
enum KeyboardLayout {
|
||||||
{
|
|
||||||
US,
|
US,
|
||||||
FR
|
FR
|
||||||
};
|
};
|
||||||
|
|||||||
+46
-10
@@ -7,11 +7,27 @@
|
|||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* outb - Writes a byte to a CPU port
|
||||||
|
* @port: CPU port to write to
|
||||||
|
* @data: Byte to write
|
||||||
|
*
|
||||||
|
* Writes a single byte to the serial interface.
|
||||||
|
*/
|
||||||
void outb(int port, unsigned char data)
|
void outb(int port, unsigned char data)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inb - Gets a byte in through a CPU port
|
||||||
|
* @port: The CPU port to get a byte from
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <data> - byte got from port
|
||||||
|
*/
|
||||||
unsigned char inb(int port)
|
unsigned char inb(int port)
|
||||||
{
|
{
|
||||||
unsigned char data = 0;
|
unsigned char data = 0;
|
||||||
@@ -19,9 +35,13 @@ unsigned char inb(int port)
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// COM1
|
/*
|
||||||
#define PORT 0x3F8
|
* serial_init - Initializes serial interface
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %-EIO - Input/output error
|
||||||
|
* %0 - Success
|
||||||
|
*/
|
||||||
int serial_init()
|
int serial_init()
|
||||||
{
|
{
|
||||||
outb(PORT + 1, 0x00); // Disable all interrupts
|
outb(PORT + 1, 0x00); // Disable all interrupts
|
||||||
@@ -34,36 +54,52 @@ int serial_init()
|
|||||||
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
||||||
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
||||||
|
|
||||||
if (inb(PORT) != 0xAE)
|
if (inb(PORT) != 0xAE) {
|
||||||
{
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set normal operation mode
|
// Set normal operation mode
|
||||||
outb(PORT + 4, 0x0F);
|
outb(PORT + 4, 0x0F);
|
||||||
|
|
||||||
DEBUG("serial initialized");
|
DEBUG("*** Welcome to PepperOS! ***");
|
||||||
|
init.serial = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_transmit_empty - Check if the serial transmit register is empty
|
||||||
|
*
|
||||||
|
* Return: Non-zero if the transmit register is empty and a new
|
||||||
|
* byte can be written to the serial port, 0 otherwise.
|
||||||
|
*/
|
||||||
static int is_transmit_empty()
|
static int is_transmit_empty()
|
||||||
{
|
{
|
||||||
return inb(PORT + 5) & 0x20;
|
return inb(PORT + 5) & 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial kernel putchar
|
/*
|
||||||
|
* skputc - Serial kernel putchar
|
||||||
|
* @c: character to write
|
||||||
|
*
|
||||||
|
* Writes a single character to the serial interface.
|
||||||
|
*/
|
||||||
void skputc(char c)
|
void skputc(char c)
|
||||||
{
|
{
|
||||||
|
// TODO: Spinlock here (serial access)
|
||||||
while (!is_transmit_empty()); // wait for free spot
|
while (!is_transmit_empty()); // wait for free spot
|
||||||
outb(PORT, c);
|
outb(PORT, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial kernel putstring
|
/*
|
||||||
|
* skputs - Serial kernel puts
|
||||||
|
* @str: Message to write
|
||||||
|
*
|
||||||
|
* Writes a non-formatted string to serial output.
|
||||||
|
*/
|
||||||
void skputs(const char* str)
|
void skputs(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int i=0;
|
unsigned int i=0;
|
||||||
while (str[i])
|
while (str[i]) {
|
||||||
{
|
|
||||||
skputc(str[i]);
|
skputc(str[i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,13 @@
|
|||||||
#ifndef SERIAL_H
|
#ifndef SERIAL_H
|
||||||
#define SERIAL_H
|
#define SERIAL_H
|
||||||
|
|
||||||
|
// COM1
|
||||||
|
#define PORT 0x3F8
|
||||||
|
|
||||||
void outb(int port, unsigned char data);
|
void outb(int port, unsigned char data);
|
||||||
unsigned char inb(int port);
|
unsigned char inb(int port);
|
||||||
|
|
||||||
int serial_init();
|
int serial_init(void);
|
||||||
void skputs(const char* str);
|
void skputs(const char* str);
|
||||||
void skputc(char c);
|
void skputc(char c);
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#error "Please do not compile Flanterm as C++ code! Flanterm should be compiled as C99 or newer."
|
#error "Please do not compile Flanterm as C++ code! Flanterm should be compiled as C99 or newer."
|
||||||
#endif
|
#endif
|
||||||
@@ -662,6 +664,17 @@ static void plot_char_unscaled_canvas(struct flanterm_context *_ctx, struct flan
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void plot_char_unscaled_uncanvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
|
static void plot_char_unscaled_uncanvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
|
||||||
|
|
||||||
|
if (_ctx == NULL)
|
||||||
|
{
|
||||||
|
panic(NULL, "plot_char_unscaled_uncanvas: _ctx is NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == NULL)
|
||||||
|
{
|
||||||
|
panic(NULL, "plot_char_unscaled_uncanvas: c is NULL");
|
||||||
|
}
|
||||||
|
|
||||||
struct flanterm_fb_context *ctx = (void *)_ctx;
|
struct flanterm_fb_context *ctx = (void *)_ctx;
|
||||||
|
|
||||||
if (x >= _ctx->cols || y >= _ctx->rows) {
|
if (x >= _ctx->cols || y >= _ctx->rows) {
|
||||||
@@ -953,6 +966,12 @@ static void draw_cursor(struct flanterm_context *_ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void flanterm_fb_double_buffer_flush(struct flanterm_context *_ctx) {
|
static void flanterm_fb_double_buffer_flush(struct flanterm_context *_ctx) {
|
||||||
|
|
||||||
|
if (_ctx == NULL)
|
||||||
|
{
|
||||||
|
panic(NULL, "flanterm_fb_double_buffer_flush: _ctx is NULL");
|
||||||
|
}
|
||||||
|
|
||||||
struct flanterm_fb_context *ctx = (void *)_ctx;
|
struct flanterm_fb_context *ctx = (void *)_ctx;
|
||||||
|
|
||||||
if (_ctx->cursor_enabled) {
|
if (_ctx->cursor_enabled) {
|
||||||
|
|||||||
+62
-94
@@ -11,117 +11,85 @@ because this shitty implementation will be replaced one day by Flanterm
|
|||||||
(once memory management is okay: paging & kernel malloc)
|
(once memory management is okay: paging & kernel malloc)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#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 "config.h"
|
||||||
#include "flanterm.h"
|
#include "flanterm.h"
|
||||||
|
#include "flanterm_backends/fb.h"
|
||||||
extern struct boot_context boot_ctx;
|
#include "mem/heap/kheap.h"
|
||||||
|
#include "limine.h"
|
||||||
// Importing the PSF object file
|
|
||||||
extern unsigned char _binary_zap_light16_psf_start[];
|
|
||||||
extern unsigned char _binary_zap_light16_psf_end[];
|
|
||||||
|
|
||||||
PSF1_Header* font = (PSF1_Header*)_binary_zap_light16_psf_start;
|
|
||||||
uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header);
|
|
||||||
|
|
||||||
#define FONT_WIDTH 8
|
|
||||||
#define FONT_HEIGHT font->characterSize
|
|
||||||
|
|
||||||
extern struct flanterm_context* ft_ctx;
|
extern struct flanterm_context* ft_ctx;
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
// Character cursor
|
/*
|
||||||
typedef struct
|
* _putchar - Writes a character to terminal
|
||||||
{
|
* @character: character to write
|
||||||
size_t x;
|
*/
|
||||||
size_t y;
|
|
||||||
} Cursor;
|
|
||||||
|
|
||||||
static Cursor cursor = {0, 0};
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
// Get framebuffer address from Limine struct
|
|
||||||
|
|
||||||
if (!boot_ctx.fb)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
// AKA they should just be seen here (kind of like private functions in cpp)
|
|
||||||
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
|
|
||||||
size_t pos = x*4 + y*framebuffer->pitch;
|
|
||||||
fb[pos] = color & 255; // blue channel
|
|
||||||
fb[pos+1] = (color >> 8) & 255; // green
|
|
||||||
fb[pos+2] = (color >> 16) & 255; // blue
|
|
||||||
}
|
|
||||||
|
|
||||||
void term_scroll()
|
|
||||||
{
|
|
||||||
// Erase first text line
|
|
||||||
memset(fb, 255, FONT_HEIGHT*framebuffer->pitch);
|
|
||||||
|
|
||||||
// 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++)
|
|
||||||
{
|
|
||||||
lines_length[i - 1] = lines_length[i];
|
|
||||||
}
|
|
||||||
lines_length[max_lines - 1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overhead that could be avoided, right? (for printf)
|
|
||||||
void _putchar(char character)
|
void _putchar(char character)
|
||||||
{
|
{
|
||||||
|
// TODO: Spinlock here (terminal access)
|
||||||
flanterm_write(ft_ctx, &character, 1);
|
flanterm_write(ft_ctx, &character, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug-printing
|
/*
|
||||||
|
* kputs - Kernel puts
|
||||||
|
* @str: String to write
|
||||||
|
*
|
||||||
|
* Writes a non-formatted string to terminal
|
||||||
|
*/
|
||||||
void kputs(const char* str)
|
void kputs(const char* str)
|
||||||
{
|
{
|
||||||
size_t i=0;
|
size_t i=0;
|
||||||
while (str[i] != 0)
|
while (str[i] != 0) {
|
||||||
{
|
|
||||||
_putchar(str[i]);
|
_putchar(str[i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
_putchar('\r');
|
||||||
|
}
|
||||||
|
|
||||||
|
extern struct flanterm_context* ft_ctx;
|
||||||
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flanterm_free_wrapper - free() wrapper for Flanterm
|
||||||
|
* @ptr: pointer to free
|
||||||
|
* @size: amount of bytes to free
|
||||||
|
*
|
||||||
|
* This function exists solely because the Flanterm initialization
|
||||||
|
* function only accepts a free() function with a size parameter,
|
||||||
|
* and the default one doesn't have it.
|
||||||
|
*/
|
||||||
|
void flanterm_free_wrapper(void* ptr, size_t size)
|
||||||
|
{
|
||||||
|
(void)size;
|
||||||
|
kfree(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* term_init - Video output/terminal initialization
|
||||||
|
*
|
||||||
|
* Uses Flanterm and the framebuffer given by Limine.
|
||||||
|
*/
|
||||||
|
void term_init()
|
||||||
|
{
|
||||||
|
uint32_t bgColor = 0x252525;
|
||||||
|
ft_ctx = flanterm_fb_init(
|
||||||
|
kmalloc,
|
||||||
|
flanterm_free_wrapper,
|
||||||
|
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,
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, 0, 0, 1,
|
||||||
|
0, 0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
init.terminal = true;
|
||||||
}
|
}
|
||||||
+1
-19
@@ -7,26 +7,8 @@
|
|||||||
#ifndef TERM_H
|
#ifndef TERM_H
|
||||||
#define TERM_H
|
#define TERM_H
|
||||||
|
|
||||||
int term_init();
|
|
||||||
void kputs(const char* str);
|
void kputs(const char* str);
|
||||||
void _putchar(char character);
|
void _putchar(char character);
|
||||||
|
void term_init(void);
|
||||||
enum TermColors
|
|
||||||
{
|
|
||||||
BLACK = 0x000000,
|
|
||||||
WHITE = 0xffffff
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PSF1_FONT_MAGIC 0x0436
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint16_t magic;
|
|
||||||
uint8_t fontMode;
|
|
||||||
uint8_t characterSize; // height
|
|
||||||
} PSF1_Header;
|
|
||||||
|
|
||||||
// debug
|
|
||||||
void term_scroll();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+17
-7
@@ -7,8 +7,7 @@
|
|||||||
#ifndef KERNEL_H
|
#ifndef KERNEL_H
|
||||||
#define KERNEL_H
|
#define KERNEL_H
|
||||||
|
|
||||||
enum ErrorCodes
|
enum ErrorCodes {
|
||||||
{
|
|
||||||
ENOMEM,
|
ENOMEM,
|
||||||
EIO
|
EIO
|
||||||
};
|
};
|
||||||
@@ -19,9 +18,11 @@ 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"
|
#include "idt/idt.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
|
extern volatile uint64_t ticks;
|
||||||
|
|
||||||
|
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* #define DEBUG(log, ...) \
|
/* #define DEBUG(log, ...) \
|
||||||
printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \
|
printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \
|
||||||
@@ -35,20 +36,29 @@ enum ErrorCodes
|
|||||||
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
|
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
|
||||||
|
|
||||||
void panic(struct cpu_status_t* ctx, const char* str);
|
void panic(struct cpu_status_t* ctx, const char* str);
|
||||||
void hcf();
|
void hcf(void);
|
||||||
void idle();
|
void idle(void);
|
||||||
|
|
||||||
|
/* debug */
|
||||||
void debug_stack_trace(unsigned int max_frames);
|
void debug_stack_trace(unsigned int max_frames);
|
||||||
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset);
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset);
|
||||||
|
void boot_mem_display(void);
|
||||||
|
|
||||||
#define assert(check) do { if(!(check)) hcf(); } while(0)
|
#define assert(check) do { if(!(check)) hcf(); } while(0)
|
||||||
|
|
||||||
struct boot_context
|
struct boot_context {
|
||||||
{
|
|
||||||
struct limine_framebuffer* fb;
|
struct limine_framebuffer* fb;
|
||||||
struct limine_memmap_response* mmap;
|
struct limine_memmap_response* mmap;
|
||||||
struct limine_hhdm_response* hhdm;
|
struct limine_hhdm_response* hhdm;
|
||||||
struct limine_kernel_address_response* kaddr;
|
struct limine_kernel_address_response* kaddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Are these modules initialized yet?
|
||||||
|
struct init_status {
|
||||||
|
bool terminal;
|
||||||
|
bool serial;
|
||||||
|
bool keyboard;
|
||||||
|
bool timer;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+48
-62
@@ -30,20 +30,30 @@
|
|||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile LIMINE_BASE_REVISION(3);
|
volatile LIMINE_BASE_REVISION(3);
|
||||||
|
|
||||||
struct flanterm_context *ft_ctx;
|
/*
|
||||||
|
* hcf - Halt and catch fire
|
||||||
// Halt and catch fire (makes machine stall)
|
*
|
||||||
|
* This function is called only in the case of an unrecoverable
|
||||||
|
* error. It halts interrupts, and stops execution. The machine
|
||||||
|
* will stay in an infinite loop state.
|
||||||
|
*/
|
||||||
void hcf()
|
void hcf()
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS; for (;;)asm("hlt");
|
CLEAR_INTERRUPTS; for (;;)asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doing nothing (can be interrupted)
|
/*
|
||||||
|
* idle - Make the machine idle
|
||||||
|
*
|
||||||
|
* When there is nothing else to do, this function
|
||||||
|
* gets called. It can be interrupted, so it allows
|
||||||
|
* the scheduler, timer, and keyboard to work.
|
||||||
|
*/
|
||||||
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
|
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
|
||||||
|
|
||||||
uint8_t kernel_stack[KERNEL_STACK_SIZE] __attribute__((aligned(16)));
|
struct flanterm_context *ft_ctx;
|
||||||
|
|
||||||
struct boot_context boot_ctx;
|
struct boot_context boot_ctx;
|
||||||
|
struct init_status init = {0};
|
||||||
|
|
||||||
extern volatile struct limine_framebuffer_request framebuffer_request;
|
extern volatile struct limine_framebuffer_request framebuffer_request;
|
||||||
extern volatile struct limine_memmap_request memmap_request;
|
extern volatile struct limine_memmap_request memmap_request;
|
||||||
@@ -52,92 +62,68 @@ extern volatile struct limine_kernel_address_request kerneladdr_request;
|
|||||||
|
|
||||||
extern struct process_t* processes_list;
|
extern struct process_t* processes_list;
|
||||||
extern struct process_t* current_process;
|
extern struct process_t* current_process;
|
||||||
|
struct process_t* idle_proc;
|
||||||
|
|
||||||
|
// Never gets executed although pedicel is scheduled?
|
||||||
void pedicel_main(void* arg)
|
void pedicel_main(void* arg)
|
||||||
{
|
{
|
||||||
//printf("Hello, world from a KERNEL PROCESS!");
|
printf("\n\nWelcome to PepperOS! Pedicel speaking.\r\nNothing left to do, let's go idle!");
|
||||||
}
|
|
||||||
|
|
||||||
void two_main(void* arg)
|
|
||||||
{
|
|
||||||
//printf("...process 2 speaking!!!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void idle_main(void* arg)
|
void idle_main(void* arg)
|
||||||
{
|
{
|
||||||
for(;;)asm("hlt");
|
for (;;) {
|
||||||
|
asm("hlt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is our entry point
|
extern uintptr_t kheap_start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kmain - Kernel entry point
|
||||||
|
*
|
||||||
|
* This is where execution begins at handoff from Limine.
|
||||||
|
* The function fetches all needed information from the
|
||||||
|
* bootloader, initializes all kernel modules and structures,
|
||||||
|
* and then goes in an idle state.
|
||||||
|
*/
|
||||||
void kmain()
|
void kmain()
|
||||||
{
|
{
|
||||||
|
CLEAR_INTERRUPTS;
|
||||||
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
||||||
|
|
||||||
|
serial_init();
|
||||||
|
timer_init();
|
||||||
|
|
||||||
// Populate boot context
|
// Populate boot context
|
||||||
boot_ctx.fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
|
boot_ctx.fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
|
||||||
boot_ctx.mmap = memmap_request.response ? memmap_request.response : NULL;
|
boot_ctx.mmap = memmap_request.response ? memmap_request.response : NULL;
|
||||||
boot_ctx.hhdm = hhdm_request.response ? hhdm_request.response : NULL;
|
boot_ctx.hhdm = hhdm_request.response ? hhdm_request.response : NULL;
|
||||||
boot_ctx.kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
|
boot_ctx.kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
|
||||||
|
|
||||||
pmm_init(boot_ctx.mmap, boot_ctx.hhdm);
|
boot_mem_display();
|
||||||
|
pmm_init(boot_ctx);
|
||||||
|
|
||||||
// Remap kernel , HHDM and framebuffer
|
// Remap kernel , HHDM and framebuffer
|
||||||
paging_init(boot_ctx.kaddr, boot_ctx.fb);
|
paging_init(boot_ctx);
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
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;
|
|
||||||
gdt_init();
|
|
||||||
idt_init();
|
|
||||||
timer_init();
|
|
||||||
|
|
||||||
kheap_init();
|
kheap_init();
|
||||||
|
|
||||||
vmm_init();
|
keyboard_init(FR);
|
||||||
|
|
||||||
struct process_t* idle_proc = process_create("idle", (void*)idle_main, 0);
|
term_init();
|
||||||
|
|
||||||
|
gdt_init();
|
||||||
|
idt_init();
|
||||||
|
|
||||||
|
process_init();
|
||||||
|
idle_proc = process_create("idle", (void*)idle_main, 0);
|
||||||
struct process_t* pedicel = process_create("pedicel", (void*)pedicel_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);
|
process_display_list(processes_list);
|
||||||
|
|
||||||
scheduler_init();
|
scheduler_init();
|
||||||
|
|
||||||
current_process = idle_proc;
|
kputs(PEPPEROS_SPLASH);
|
||||||
current_process->status = RUNNING;
|
|
||||||
|
|
||||||
SET_INTERRUPTS;
|
|
||||||
|
|
||||||
keyboard_init(FR);
|
|
||||||
//term_init();
|
|
||||||
|
|
||||||
//printf(PEPPEROS_SPLASH);
|
|
||||||
|
|
||||||
//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. ");
|
|
||||||
|
|
||||||
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();
|
idle();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,20 @@
|
|||||||
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
||||||
struct GDTR gdtr;
|
struct GDTR gdtr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_load - Loads Global Descriptor Table
|
||||||
|
*/
|
||||||
static void gdt_load()
|
static void gdt_load()
|
||||||
{
|
{
|
||||||
asm("lgdt %0" : : "m"(gdtr));
|
asm("lgdt %0" : : "m"(gdtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_flush - Flushes the Global Descriptor Table
|
||||||
|
*
|
||||||
|
* This function loads new Segment Selectors to make
|
||||||
|
* the GDT changes take effect
|
||||||
|
*/
|
||||||
static void gdt_flush()
|
static void gdt_flush()
|
||||||
{
|
{
|
||||||
// Here, 0x8 is the kernel code selector
|
// Here, 0x8 is the kernel code selector
|
||||||
@@ -42,6 +51,15 @@ static void gdt_flush()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_init - Global Descriptor Table initialization
|
||||||
|
*
|
||||||
|
* This function loads a new GDT in the CPU.
|
||||||
|
* It contains a null descriptor, kernel code and data
|
||||||
|
* segments, and user code and data segments.
|
||||||
|
* However, we do not use segmentation to manage memory on
|
||||||
|
* 64-bit x86, as it's deprecated. Instead, we use paging.
|
||||||
|
*/
|
||||||
void gdt_init()
|
void gdt_init()
|
||||||
{
|
{
|
||||||
// Null descriptor (required)
|
// Null descriptor (required)
|
||||||
|
|||||||
+2
-3
@@ -21,12 +21,11 @@
|
|||||||
#define USER_CODE_SEGMENT 0x18
|
#define USER_CODE_SEGMENT 0x18
|
||||||
#define USER_DATA_SEGMENT 0x20
|
#define USER_DATA_SEGMENT 0x20
|
||||||
|
|
||||||
struct GDTR
|
struct GDTR {
|
||||||
{
|
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
void gdt_init();
|
void gdt_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+72
-59
@@ -23,42 +23,57 @@ static uintptr_t end;
|
|||||||
// Kernel root table (level 4)
|
// Kernel root table (level 4)
|
||||||
extern uint64_t *kernel_pml4;
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
static void kheap_grow(size_t size)
|
/*
|
||||||
{
|
* kheap_init - Kernel heap initialization
|
||||||
size_t pages = ALIGN_UP(size + sizeof(struct heap_block_t), PAGE_SIZE) / PAGE_SIZE;
|
*
|
||||||
|
* This function physically allocates and maps enough pages
|
||||||
if (pages == 0) pages = 1;
|
* of memory for KHEAP_SIZE, which is defined in config.h.
|
||||||
|
*
|
||||||
for (size_t i = 0; i < pages; i++)
|
* It then creates one big heap block, which will be the
|
||||||
{
|
* base for a linked list.
|
||||||
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()
|
void kheap_init()
|
||||||
{
|
{
|
||||||
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
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
|
size_t heap_pages = ALIGN_UP(KHEAP_SIZE, PAGE_SIZE) / PAGE_SIZE;
|
||||||
kheap_map_page();
|
DEBUG("Mapping %d kernel heap pages at 0x%p", heap_pages, kheap_start);
|
||||||
|
|
||||||
|
uintptr_t current_addr = kheap_start;
|
||||||
|
|
||||||
|
// Map/alloc enough pages for heap (KHEAP_SIZE)
|
||||||
|
for (size_t i=0; i<heap_pages; i++) {
|
||||||
|
uintptr_t phys = pmm_alloc();
|
||||||
|
if (phys == 0) {
|
||||||
|
panic(NULL, "Not enough memory available to initialize kernel heap.");
|
||||||
|
}
|
||||||
|
|
||||||
|
paging_map_page(kernel_pml4, current_addr, phys, PTE_PRESENT | PTE_WRITABLE);
|
||||||
|
current_addr += PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = current_addr;
|
||||||
|
|
||||||
// Give linked list head its properties
|
// Give linked list head its properties
|
||||||
head = (struct heap_block_t*)kheap_start;
|
head = (struct heap_block_t*)kheap_start;
|
||||||
head->size = PAGE_SIZE - sizeof(struct heap_block_t);
|
head->size = (end-kheap_start) - sizeof(struct heap_block_t);
|
||||||
head->free = true;
|
head->free = true;
|
||||||
head->next = NULL;
|
head->next = NULL;
|
||||||
DEBUG("kheap initialized, head=0x%p, size=%u", head, head->size);
|
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kmalloc - Kernel memory allocation
|
||||||
|
* @size: number of bytes to allocate
|
||||||
|
*
|
||||||
|
* Looks for a big enough free block and marks it
|
||||||
|
* as taken. Each block of memory is preceded by
|
||||||
|
* the linked list header.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ptr> - Pointer to at least <size> bytes of usable memory
|
||||||
|
* NULL - No more memory, or no valid size given
|
||||||
|
*/
|
||||||
void* kmalloc(size_t size)
|
void* kmalloc(size_t size)
|
||||||
{
|
{
|
||||||
// No size, no memory allocated!
|
// No size, no memory allocated!
|
||||||
@@ -67,18 +82,14 @@ void* kmalloc(size_t size)
|
|||||||
|
|
||||||
struct heap_block_t* curr = head;
|
struct heap_block_t* curr = head;
|
||||||
|
|
||||||
while (curr)
|
while (curr) {
|
||||||
{
|
|
||||||
// Is block free and big enough for us?
|
// Is block free and big enough for us?
|
||||||
if (curr->free && curr->size >= size)
|
if (curr->free && curr->size >= size) {
|
||||||
{
|
|
||||||
// We split the block if it is big enough
|
// We split the block if it is big enough
|
||||||
if (curr->size >= size + BLOCK_MIN_SIZE)
|
if (curr->size >= size + sizeof(struct heap_block_t) + 16) {
|
||||||
{
|
struct heap_block_t* split = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + 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->size = curr->size - size - sizeof(struct heap_block_t);
|
||||||
split->free = true;
|
split->free = true;
|
||||||
split->next = curr->next;
|
split->next = curr->next;
|
||||||
|
|
||||||
@@ -94,27 +105,22 @@ void* kmalloc(size_t size)
|
|||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're here it means we didn't have enough memory
|
// No growing. If we're here it means the initial pool
|
||||||
// for the block allocation. So we will allocate more..
|
// wasn't sufficient. Too bad.
|
||||||
uintptr_t old_end = end;
|
DEBUG("Kernel heap is OUT OF MEMORY!");
|
||||||
kheap_grow(size + sizeof(struct heap_block_t));
|
// if we were terrorists maybe we should panic
|
||||||
|
// or just wait for others to free stuff?
|
||||||
struct heap_block_t* block = (struct heap_block_t*)old_end;
|
return NULL;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kfree - Kernel memory freeing
|
||||||
|
* @ptr: pointer to memory region to free
|
||||||
|
*
|
||||||
|
* Marks the memory block beginning at <ptr>
|
||||||
|
* as free. Also merges adjacent free blocks
|
||||||
|
* to lessen fragmentation.
|
||||||
|
*/
|
||||||
void kfree(void* ptr)
|
void kfree(void* ptr)
|
||||||
{
|
{
|
||||||
// Nothing to free
|
// Nothing to free
|
||||||
@@ -126,10 +132,8 @@ void kfree(void* ptr)
|
|||||||
|
|
||||||
// merge adjacent free blocks (coalescing)
|
// merge adjacent free blocks (coalescing)
|
||||||
struct heap_block_t* curr = head;
|
struct heap_block_t* curr = head;
|
||||||
while (curr && curr->next)
|
while (curr && curr->next) {
|
||||||
{
|
if (curr->free && curr->next->free) {
|
||||||
if (curr->free && curr->next->free)
|
|
||||||
{
|
|
||||||
curr->size += sizeof(*curr) + curr->next->size;
|
curr->size += sizeof(*curr) + curr->next->size;
|
||||||
curr->next = curr->next->next;
|
curr->next = curr->next->next;
|
||||||
continue;
|
continue;
|
||||||
@@ -138,8 +142,17 @@ void kfree(void* ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
* kalloc_stack - Stack memory allocation
|
||||||
|
*
|
||||||
|
* Allocates a memory region of at least PROCESS_STACK_SIZE,
|
||||||
|
* to be used as a stack for a process. The pointer returned
|
||||||
|
* points to the end of the region, as the stack grows downwards.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ptr> - Pointer to a region after at least PROCESS_STACK_SIZE bytes of usable memory
|
||||||
|
* NULL - No more memory
|
||||||
|
*/
|
||||||
void* kalloc_stack()
|
void* kalloc_stack()
|
||||||
{
|
{
|
||||||
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
|
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
|
||||||
|
|||||||
@@ -14,18 +14,19 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
struct heap_block_t
|
struct heap_block_t {
|
||||||
{
|
|
||||||
size_t size;
|
size_t size;
|
||||||
bool free;
|
bool free; // 1byte
|
||||||
|
uint8_t reserved[7]; // (7+1 = 8 bytes)
|
||||||
struct heap_block_t* next;
|
struct heap_block_t* next;
|
||||||
};
|
} __attribute__((aligned(16)));
|
||||||
|
|
||||||
void kheap_init();
|
void kheap_init(void);
|
||||||
void* kmalloc(size_t size);
|
void* kmalloc(size_t size);
|
||||||
void kfree(void* ptr);
|
void kfree(void* ptr);
|
||||||
void* kalloc_stack();
|
void* kalloc_stack(void);
|
||||||
void kheap_map_page();
|
void kheap_map_page(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+59
-65
@@ -16,113 +16,107 @@
|
|||||||
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
||||||
// do more optimization on them (and as it's a much used function, it's good to
|
// do more optimization on them (and as it's a much used function, it's good to
|
||||||
// be able to do that)
|
// be able to do that)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memcpy - Copy memory from one place to another
|
||||||
|
* @dest: pointer to the destination region
|
||||||
|
* @src: pointer to the source region
|
||||||
|
* @n: amount of bytes to copy
|
||||||
|
*
|
||||||
|
* This function copies n bytes of memory from
|
||||||
|
* src to dest.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <dest> - Pointer to destination region
|
||||||
|
*/
|
||||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
||||||
const uint8_t* restrict psrc = (const uint8_t* restrict)src;
|
const uint8_t* restrict psrc = (const uint8_t* restrict)src;
|
||||||
|
|
||||||
for (size_t i=0; i<n; i++)
|
for (size_t i=0; i<n; i++) {
|
||||||
{
|
|
||||||
pdest[i] = psrc[i];
|
pdest[i] = psrc[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memset - Sets a memory region to given byte
|
||||||
|
* @s: pointer to memory region
|
||||||
|
* @c: byte to be written
|
||||||
|
* @n: amount of bytes to write
|
||||||
|
*
|
||||||
|
* This function writes n times the byte c
|
||||||
|
* to the memory region pointed to by s.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <s> - Pointer to memory region
|
||||||
|
*/
|
||||||
void* memset(void* s, int c, size_t n)
|
void* memset(void* s, int c, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* p = (uint8_t*)s;
|
uint8_t* p = (uint8_t*)s;
|
||||||
|
|
||||||
for (size_t i=0; i<n; i++)
|
for (size_t i=0; i<n; i++) {
|
||||||
{
|
|
||||||
p[i] = (uint8_t)c;
|
p[i] = (uint8_t)c;
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memmove - Move memory from one place to another
|
||||||
|
* @dest: pointer to the destination region
|
||||||
|
* @src: pointer to the source region
|
||||||
|
* @n: amount of bytes to move
|
||||||
|
*
|
||||||
|
* This function moves n bytes of memory from
|
||||||
|
* src to dest.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <dest> - Pointer to destination region
|
||||||
|
*/
|
||||||
void* memmove(void *dest, const void* src, size_t n)
|
void* memmove(void *dest, const void* src, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* pdest = (uint8_t*)dest;
|
uint8_t* pdest = (uint8_t*)dest;
|
||||||
const uint8_t* psrc = (uint8_t*)src;
|
const uint8_t* psrc = (uint8_t*)src;
|
||||||
|
|
||||||
if (src > dest)
|
if (src > dest) {
|
||||||
{
|
for (size_t i=0; i<n; i++) {
|
||||||
for (size_t i=0; i<n; i++)
|
|
||||||
{
|
|
||||||
pdest[i] = psrc[i];
|
pdest[i] = psrc[i];
|
||||||
}
|
}
|
||||||
} else if (src < dest)
|
} else if (src < dest) {
|
||||||
{
|
for (size_t i=n; i>0; i--) {
|
||||||
for (size_t i=n; i>0; i--)
|
|
||||||
{
|
|
||||||
pdest[i-1] = psrc[i-1];
|
pdest[i-1] = psrc[i-1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memcmp - Compare two memory regions
|
||||||
|
* @s1: pointer to the first region
|
||||||
|
* @s2: pointer to the second region
|
||||||
|
* @n: amount of bytes to compare
|
||||||
|
*
|
||||||
|
* This function compares n bytes of memory
|
||||||
|
* bewteen regions pointed to by s1 and s2.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %0 - if s1 and s2 are equal
|
||||||
|
* %-1 - if s1 is smaller than s2
|
||||||
|
* %1 - if s1 is greater than s2
|
||||||
|
*/
|
||||||
int memcmp(const void* s1, const void* s2, size_t n)
|
int memcmp(const void* s1, const void* s2, size_t n)
|
||||||
{
|
{
|
||||||
const uint8_t* p1 = (const uint8_t*)s1;
|
const uint8_t* p1 = (const uint8_t*)s1;
|
||||||
const uint8_t* p2 = (const uint8_t*)s2;
|
const uint8_t* p2 = (const uint8_t*)s2;
|
||||||
|
|
||||||
for (size_t i=0; i<n; i++)
|
for (size_t i=0; i<n; i++) {
|
||||||
{
|
if (p1[i] != p2[i]) {
|
||||||
if (p1[i] != p2[i])
|
|
||||||
{
|
|
||||||
return p1[i] < p2[i] ? -1 : 1;
|
return p1[i] < p2[i] ? -1 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
+67
-53
@@ -24,23 +24,46 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
|
|||||||
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* load_cr3 - Load a new value into the CR3 register
|
||||||
|
* @value: the value to load
|
||||||
|
*
|
||||||
|
* This function is used to load the physical address
|
||||||
|
* of the root page table (PML4), to switch the paging
|
||||||
|
* structures the CPU sees and uses.
|
||||||
|
*/
|
||||||
void load_cr3(uint64_t value) {
|
void load_cr3(uint64_t value) {
|
||||||
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// To flush TLB
|
/*
|
||||||
|
* invlpg - Invalidates a Translation Lookaside Buffer entry
|
||||||
|
* @addr: page memory address
|
||||||
|
*
|
||||||
|
* This function is used to flush at least the TLB entrie(s)
|
||||||
|
* for the page that contains the <addr> address.
|
||||||
|
*/
|
||||||
static inline void invlpg(void *addr)
|
static inline void invlpg(void *addr)
|
||||||
{
|
{
|
||||||
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocates a 512-entry 64bit page table/directory/whatever (zeroed)
|
/*
|
||||||
|
* alloc_page_table - Page table allocation
|
||||||
|
*
|
||||||
|
* This function allocates enough memory for a 512-entry
|
||||||
|
* 64-bit page table, for any level (PML4/3/2).
|
||||||
|
*
|
||||||
|
* Memory allocated here is zeroed.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <virt> - Pointer to allocated page table
|
||||||
|
*/
|
||||||
static uint64_t* alloc_page_table()
|
static uint64_t* alloc_page_table()
|
||||||
{
|
{
|
||||||
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
||||||
|
|
||||||
for (size_t i=0; i<512; i++)
|
for (size_t i=0; i<512; i++) {
|
||||||
{
|
|
||||||
virt[i] = 0;
|
virt[i] = 0;
|
||||||
}
|
}
|
||||||
return virt;
|
return virt;
|
||||||
@@ -50,10 +73,19 @@ static uint64_t* alloc_page_table()
|
|||||||
__attribute__((aligned(4096)))
|
__attribute__((aligned(4096)))
|
||||||
uint64_t *kernel_pml4;
|
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
|
* paging_map_page - Mapping a memory page
|
||||||
// page table/directories, and then mapping the correct page table entry with the
|
* @root_table: Address of the PML4
|
||||||
// given physical address + flags
|
* @virt: Virtual address
|
||||||
|
* @phys: Physical address
|
||||||
|
* @flags: Flags to set on page
|
||||||
|
*
|
||||||
|
* This function maps the physical address <phys> to the virtual
|
||||||
|
* address <virt>, using the paging structures beginning at
|
||||||
|
* <root_table>. <flags> can be set according to the PTE_FLAGS enum.
|
||||||
|
*
|
||||||
|
* If a page table/directory entry is not present yet, it creates it.
|
||||||
|
*/
|
||||||
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
||||||
{
|
{
|
||||||
virt = PAGE_ALIGN_DOWN(virt);
|
virt = PAGE_ALIGN_DOWN(virt);
|
||||||
@@ -70,32 +102,26 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
|
|||||||
// PML4
|
// PML4
|
||||||
// If the entry at index is not present, allocate enough space for it
|
// If the entry at index is not present, allocate enough space for it
|
||||||
// then populate the entry with correct addr + flags
|
// then populate the entry with correct addr + flags
|
||||||
if (!(root_table[pml4_i] & PTE_PRESENT))
|
if (!(root_table[pml4_i] & PTE_PRESENT)) {
|
||||||
{
|
|
||||||
pdpt = alloc_page_table();
|
pdpt = alloc_page_table();
|
||||||
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
|
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & PTE_ADDR_MASK);
|
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & PTE_ADDR_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PDPT: same here
|
// PDPT: same here
|
||||||
if (!(pdpt[pdpt_i] & PTE_PRESENT))
|
if (!(pdpt[pdpt_i] & PTE_PRESENT)) {
|
||||||
{
|
|
||||||
pd = alloc_page_table();
|
pd = alloc_page_table();
|
||||||
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
|
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
|
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PD: and here
|
// PD: and here
|
||||||
if (!(pd[pd_i] & PTE_PRESENT))
|
if (!(pd[pd_i] & PTE_PRESENT)) {
|
||||||
{
|
|
||||||
pt = alloc_page_table();
|
pt = alloc_page_table();
|
||||||
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
|
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
|
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +135,16 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
|
|||||||
uint64_t kernel_phys_base;
|
uint64_t kernel_phys_base;
|
||||||
uint64_t kernel_virt_base;
|
uint64_t kernel_virt_base;
|
||||||
|
|
||||||
extern struct boot_context boot_ctx;
|
/*
|
||||||
|
* paging_init - Paging initialization
|
||||||
void paging_init()
|
* @boot_ctx: Boot context structure
|
||||||
|
*
|
||||||
|
* This function initializes new paging structures, to replace
|
||||||
|
* the ones given by the bootloader.
|
||||||
|
*
|
||||||
|
* It maps the kernel, the HHDM space, and the framebuffer.
|
||||||
|
*/
|
||||||
|
void paging_init(struct boot_context boot_ctx)
|
||||||
{
|
{
|
||||||
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
||||||
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
|
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
|
||||||
@@ -129,32 +162,25 @@ void paging_init()
|
|||||||
|
|
||||||
// Find max physical address from limine memmap
|
// Find max physical address from limine memmap
|
||||||
uint64_t max_phys = 0;
|
uint64_t max_phys = 0;
|
||||||
for (uint64_t i=0; i<boot_ctx.mmap->entry_count; i++)
|
for (uint64_t i=0; i<boot_ctx.mmap->entry_count; i++) {
|
||||||
{
|
|
||||||
struct limine_memmap_entry* entry = boot_ctx.mmap->entries[i];
|
struct limine_memmap_entry* entry = boot_ctx.mmap->entries[i];
|
||||||
if (entry->length == 0)
|
if (entry->length == 0) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uint64_t top = entry->base + entry->length;
|
uint64_t top = entry->base + entry->length;
|
||||||
if (top > max_phys)
|
if (top > max_phys) {
|
||||||
{
|
|
||||||
max_phys = top;
|
max_phys = top;
|
||||||
}
|
}
|
||||||
//DEBUG("max_phys=0x%p", max_phys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4GB
|
// 4GB
|
||||||
if (max_phys > 0x100000000)
|
if (max_phys > PAGING_MAX_PHYS) {
|
||||||
{
|
DEBUG("WARNING: max_phys capped to 4GB (%x) (from max_phys=%p)", PAGING_MAX_PHYS, max_phys);
|
||||||
DEBUG("WARNING: max_phys capped to 4GB (0x100000000) (from max_phys=%p)", max_phys);
|
max_phys = PAGING_MAX_PHYS;
|
||||||
max_phys = 0x100000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HHDM map up to max_phys or 4GB, whichever is smaller, using given offset
|
// HHDM map up to max_phys or PAGING_MAX_PHYS, whichever is smaller, using given offset
|
||||||
for (uint64_t i=0; i<max_phys; i += PAGE_SIZE)
|
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);
|
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE | PTE_PRESENT);
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
@@ -164,9 +190,7 @@ void paging_init()
|
|||||||
// SOME DAY when we want a safer kernel we should map .text as Read/Exec
|
// SOME DAY when we want a safer kernel we should map .text as Read/Exec
|
||||||
// .rodata as Read and .data as Read/Write
|
// .rodata as Read and .data as Read/Write
|
||||||
// For now who gives a shit, let's RWX all kernel
|
// For now who gives a shit, let's RWX all kernel
|
||||||
for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE)
|
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);
|
paging_map_page(kernel_pml4, kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
@@ -179,23 +203,13 @@ void paging_init()
|
|||||||
uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE;
|
uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE;
|
||||||
|
|
||||||
// Map the framebuffer (with cache-disable & write-through)
|
// Map the framebuffer (with cache-disable & write-through)
|
||||||
for (uint64_t i=0; i<fb_pages; i++)
|
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);
|
paging_map_page(kernel_pml4, fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
DEBUG("Mapped %u pages for framebuffer", 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
|
// Finally, we load the physical address of our PML4 (root table) into cr3
|
||||||
load_cr3(VIRT_TO_PHYS(kernel_pml4));
|
load_cr3(VIRT_TO_PHYS(kernel_pml4));
|
||||||
DEBUG("cr3 loaded, we're still alive");
|
DEBUG("Loaded kernel PML4 into CR3");
|
||||||
}
|
}
|
||||||
+12
-8
@@ -12,8 +12,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
#include "mem/heap/kheap.h"
|
#include "mem/heap/kheap.h"
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
void paging_init();
|
void paging_init(struct boot_context boot_ctx);
|
||||||
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
|
||||||
|
|
||||||
// To swap root page tables
|
// To swap root page tables
|
||||||
@@ -40,12 +41,15 @@ extern uint64_t hhdm_off;
|
|||||||
|
|
||||||
// Page entry special bits
|
// Page entry special bits
|
||||||
// Bits set on a parent (directory, table) fall back to their children
|
// Bits set on a parent (directory, table) fall back to their children
|
||||||
#define PTE_PRESENT (1ULL << 0)
|
enum PTE_FLAGS
|
||||||
#define PTE_WRITABLE (1ULL << 1)
|
{
|
||||||
#define PTE_USER (1ULL << 2)
|
PTE_PRESENT = (1ULL << 0),
|
||||||
#define PTE_PWT (1ULL << 3)
|
PTE_WRITABLE = (1ULL << 1),
|
||||||
#define PTE_PCD (1ULL << 4)
|
PTE_USER = (1ULL << 2),
|
||||||
#define PTE_HUGE (1ULL << 7)
|
PTE_PWT = (1ULL << 3),
|
||||||
#define PTE_NOEXEC (1ULL << 63)
|
PTE_PCD = (1ULL << 4),
|
||||||
|
PTE_HUGE = (1ULL << 7),
|
||||||
|
PTE_NOEXEC = (1ULL << 63)
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+40
-18
@@ -24,13 +24,16 @@ First we'll have to discover the physical memory layout,
|
|||||||
and for that we can use a Limine request.
|
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;
|
struct limine_memmap_entry* biggest_entry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_find_biggest_usable_region - Finding the biggest free memory region
|
||||||
|
* @memmap: Limine memory map
|
||||||
|
* @hhdm: Limine HHDM offset
|
||||||
|
*
|
||||||
|
* This function uses the memory map provided by the bootloader
|
||||||
|
* to find the single biggest free memory region we can use.
|
||||||
|
*/
|
||||||
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||||
{
|
{
|
||||||
// Max length of a usable memory region
|
// Max length of a usable memory region
|
||||||
@@ -38,12 +41,10 @@ static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap
|
|||||||
uint64_t offset = hhdm->offset;
|
uint64_t offset = hhdm->offset;
|
||||||
|
|
||||||
DEBUG("Usable Memory:");
|
DEBUG("Usable Memory:");
|
||||||
for (size_t i=0; i<memmap->entry_count; i++)
|
for (size_t i=0; i<memmap->entry_count; i++) {
|
||||||
{
|
|
||||||
struct limine_memmap_entry* entry = memmap->entries[i];
|
struct limine_memmap_entry* entry = memmap->entries[i];
|
||||||
|
|
||||||
if (entry->type == LIMINE_MEMMAP_USABLE)
|
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
||||||
{
|
|
||||||
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length,
|
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);
|
entry->base+offset, entry->base+entry->length+offset);
|
||||||
if (entry->length > length_max)
|
if (entry->length > length_max)
|
||||||
@@ -64,10 +65,17 @@ uint64_t hhdm_off;
|
|||||||
|
|
||||||
static uintptr_t g_freelist = 0;
|
static uintptr_t g_freelist = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_alloc - Allocate a physical page
|
||||||
|
*
|
||||||
|
* This function allocates a single physical page (frame)
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <addr> - Address for the allocated page
|
||||||
|
*/
|
||||||
uintptr_t pmm_alloc()
|
uintptr_t pmm_alloc()
|
||||||
{
|
{
|
||||||
if (!g_freelist)
|
if (!g_freelist) {
|
||||||
{
|
|
||||||
panic(NULL, "PMM is out of memory!");
|
panic(NULL, "PMM is out of memory!");
|
||||||
}
|
}
|
||||||
uintptr_t addr = g_freelist;
|
uintptr_t addr = g_freelist;
|
||||||
@@ -75,12 +83,22 @@ uintptr_t pmm_alloc()
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_free - Frees a memory page
|
||||||
|
* @addr: Address to the page
|
||||||
|
*/
|
||||||
void pmm_free(uintptr_t addr)
|
void pmm_free(uintptr_t addr)
|
||||||
{
|
{
|
||||||
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
||||||
g_freelist = addr;
|
g_freelist = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_init_freelist - PMM freelist initialization
|
||||||
|
*
|
||||||
|
* This function marks the biggest memory region as
|
||||||
|
* free, so we can use it in pmm_alloc.
|
||||||
|
*/
|
||||||
static void pmm_init_freelist()
|
static void pmm_init_freelist()
|
||||||
{
|
{
|
||||||
// We simply call pmm_free() on each page that is marked USABLE
|
// We simply call pmm_free() on each page that is marked USABLE
|
||||||
@@ -89,20 +107,24 @@ static void pmm_init_freelist()
|
|||||||
uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE);
|
uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE);
|
||||||
|
|
||||||
uint64_t page_count=0;
|
uint64_t page_count=0;
|
||||||
for (uint64_t addr = base; addr < end; addr += PAGE_SIZE)
|
for (uint64_t addr = base; addr < end; addr += PAGE_SIZE) {
|
||||||
{
|
|
||||||
pmm_free(addr);
|
pmm_free(addr);
|
||||||
//DEBUG("page %u lives at phys 0x%p (virt 0x%p)", page_count, addr, PHYS_TO_VIRT(addr));
|
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
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)
|
/*
|
||||||
|
* pmm_init - Physical memory manager initialization
|
||||||
|
* @boot_ctx: Boot context structure
|
||||||
|
*
|
||||||
|
* This function prepares the PMM for use.
|
||||||
|
* The PMM works with a freelist.
|
||||||
|
*/
|
||||||
|
void pmm_init(struct boot_context boot_ctx)
|
||||||
{
|
{
|
||||||
hhdm_off = hhdm->offset;
|
hhdm_off = boot_ctx.hhdm->offset;
|
||||||
pmm_find_biggest_usable_region(memmap, hhdm);
|
pmm_find_biggest_usable_region(boot_ctx.mmap, boot_ctx.hhdm);
|
||||||
//pmm_allocate_bitmap(hhdm); too complicated for my small brain
|
|
||||||
|
|
||||||
// Now we have biggest USABLE region,
|
// Now we have biggest USABLE region,
|
||||||
// so to populate the free list we just iterate through it
|
// so to populate the free list we just iterate through it
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
#define PAGING_PMM_H
|
#define PAGING_PMM_H
|
||||||
|
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm);
|
void pmm_init(struct boot_context boot_ctx);
|
||||||
void pmm_free(uintptr_t addr);
|
void pmm_free(uintptr_t addr);
|
||||||
uintptr_t pmm_alloc();
|
uintptr_t pmm_alloc(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+10
-1
@@ -24,6 +24,14 @@ void* vmm_pt_root = 0;
|
|||||||
// Linked list head for virtual memory objects
|
// Linked list head for virtual memory objects
|
||||||
struct vm_object* vm_objs = NULL;
|
struct vm_object* vm_objs = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Will have to be rewritten and expanded,
|
||||||
|
* to prepare for userspace.
|
||||||
|
* The platform-agnostic flags will be removed
|
||||||
|
* because as long as the kernel is x86 only,
|
||||||
|
* we don't need over complication.
|
||||||
|
* Plus I don't plan to port to other architectures
|
||||||
|
*/
|
||||||
|
|
||||||
uint64_t convert_x86_vm_flags(size_t flags)
|
uint64_t convert_x86_vm_flags(size_t flags)
|
||||||
{
|
{
|
||||||
@@ -68,5 +76,6 @@ void vmm_setup_pt_root()
|
|||||||
|
|
||||||
void vmm_init()
|
void vmm_init()
|
||||||
{
|
{
|
||||||
vmm_setup_pt_root();
|
// NO U
|
||||||
|
//vmm_setup_pt_root();
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,7 @@ Flags here aren't x86 flags, they are platform-agnostic
|
|||||||
kernel-defined flags.
|
kernel-defined flags.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct vm_object
|
struct vm_object {
|
||||||
{
|
|
||||||
uintptr_t base;
|
uintptr_t base;
|
||||||
size_t length;
|
size_t length;
|
||||||
size_t flags;
|
size_t flags;
|
||||||
@@ -30,6 +29,6 @@ struct vm_object
|
|||||||
#define VM_FLAG_EXEC (1 << 1)
|
#define VM_FLAG_EXEC (1 << 1)
|
||||||
#define VM_FLAG_USER (1 << 2)
|
#define VM_FLAG_USER (1 << 2)
|
||||||
|
|
||||||
void vmm_init();
|
void vmm_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+62
-39
@@ -13,6 +13,9 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
|
|
||||||
|
#include "io/term/flanterm.h"
|
||||||
|
extern struct flanterm_context* ft_ctx;
|
||||||
|
|
||||||
struct process_t* processes_list;
|
struct process_t* processes_list;
|
||||||
struct process_t* current_process;
|
struct process_t* current_process;
|
||||||
|
|
||||||
@@ -20,19 +23,27 @@ extern uint64_t *kernel_pml4;
|
|||||||
|
|
||||||
size_t next_free_pid = 0;
|
size_t next_free_pid = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_init - Initializes process list
|
||||||
|
*/
|
||||||
void process_init()
|
void process_init()
|
||||||
{
|
{
|
||||||
processes_list = NULL;
|
processes_list = NULL;
|
||||||
current_process = NULL;
|
current_process = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only for debug
|
/*
|
||||||
|
* process_display_list - Debug function to display processes
|
||||||
|
* @processes_list: head of the process linked list
|
||||||
|
*
|
||||||
|
* This function prints the linked list of processes
|
||||||
|
* to the DEBUG output.
|
||||||
|
*/
|
||||||
void process_display_list(struct process_t* processes_list)
|
void process_display_list(struct process_t* processes_list)
|
||||||
{
|
{
|
||||||
int process_view_id = 0;
|
int process_view_id = 0;
|
||||||
struct process_t* tmp = processes_list;
|
struct process_t* tmp = processes_list;
|
||||||
while (tmp != NULL)
|
while (tmp != NULL) {
|
||||||
{
|
|
||||||
DEBUG("{%d: %p} -> ", process_view_id, tmp);
|
DEBUG("{%d: %p} -> ", process_view_id, tmp);
|
||||||
tmp = tmp->next;
|
tmp = tmp->next;
|
||||||
process_view_id++;
|
process_view_id++;
|
||||||
@@ -40,11 +51,23 @@ void process_display_list(struct process_t* processes_list)
|
|||||||
DEBUG("NULL");
|
DEBUG("NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_create - Create a process
|
||||||
|
* @name: name of the process
|
||||||
|
* @function: beginning of process executable code
|
||||||
|
* @arg: (optional) argument provided to process
|
||||||
|
*
|
||||||
|
* This function creates a process, gives it all
|
||||||
|
* necessary context and a stack, and adds the
|
||||||
|
* process to the linked list.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <proc> - pointer to created process
|
||||||
|
*/
|
||||||
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
struct process_t* proc = (struct process_t*)kmalloc(sizeof(struct process_t));
|
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));
|
struct cpu_status_t* ctx = (struct cpu_status_t*)kmalloc(sizeof(struct cpu_status_t));
|
||||||
|
|
||||||
// No more memory?
|
// No more memory?
|
||||||
@@ -80,34 +103,40 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
|||||||
return proc;
|
return proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_add - Add a process to the end of the linked list
|
||||||
|
* @processes_list: pointer to the head of the linked list
|
||||||
|
* @process: process to add at the end of the linked list
|
||||||
|
*/
|
||||||
void process_add(struct process_t** processes_list, struct process_t* process)
|
void process_add(struct process_t** processes_list, struct process_t* process)
|
||||||
{
|
{
|
||||||
if (!process) return;
|
if (!process) return;
|
||||||
process->next = NULL;
|
process->next = NULL;
|
||||||
|
|
||||||
if (*processes_list == NULL)
|
if (*processes_list == NULL) {
|
||||||
{
|
|
||||||
// List is empty
|
// List is empty
|
||||||
*processes_list = process;
|
*processes_list = process;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct process_t* tmp = *processes_list;
|
struct process_t* tmp = *processes_list;
|
||||||
while (tmp->next != NULL)
|
while (tmp->next != NULL) {
|
||||||
{
|
|
||||||
tmp = tmp->next;
|
tmp = tmp->next;
|
||||||
}
|
}
|
||||||
// We're at last process before NULL
|
// We're at last process before NULL
|
||||||
tmp->next = process;
|
tmp->next = process;
|
||||||
// process->next = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_delete - Delete a process from the linked list
|
||||||
|
* @processes_list: pointer to head of linked list
|
||||||
|
* @process: the process to delete from the list
|
||||||
|
*/
|
||||||
void process_delete(struct process_t** processes_list, struct process_t* process)
|
void process_delete(struct process_t** processes_list, struct process_t* process)
|
||||||
{
|
{
|
||||||
if (!processes_list || !*processes_list || !process) return;
|
if (!processes_list || !*processes_list || !process) return;
|
||||||
|
|
||||||
if (*processes_list == process)
|
if (*processes_list == process) {
|
||||||
{
|
|
||||||
// process to delete is at head
|
// process to delete is at head
|
||||||
*processes_list = process->next;
|
*processes_list = process->next;
|
||||||
process->next = NULL;
|
process->next = NULL;
|
||||||
@@ -116,13 +145,11 @@ void process_delete(struct process_t** processes_list, struct process_t* process
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct process_t* tmp = *processes_list;
|
struct process_t* tmp = *processes_list;
|
||||||
while (tmp->next && tmp->next != process)
|
while (tmp->next && tmp->next != process) {
|
||||||
{
|
|
||||||
tmp = tmp->next;
|
tmp = tmp->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp->next == NULL)
|
if (tmp->next == NULL) {
|
||||||
{
|
|
||||||
// Didn't find the process
|
// Didn't find the process
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -133,44 +160,40 @@ void process_delete(struct process_t** processes_list, struct process_t* process
|
|||||||
kfree(process);
|
kfree(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_get_next - Get the next process (unused)
|
||||||
|
* @process: pointer to process
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <process->next> - process right after the one specified
|
||||||
|
*/
|
||||||
struct process_t* process_get_next(struct process_t* process)
|
struct process_t* process_get_next(struct process_t* process)
|
||||||
{
|
{
|
||||||
if (!process) return NULL;
|
if (!process) return NULL;
|
||||||
return process->next;
|
return process->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (from gdt) This will switch tasks ONLY in ring 0
|
/*
|
||||||
// KERNEL CS = 0x08
|
* process_exit - Exit from a process
|
||||||
// KERNEL SS = 0x10
|
*
|
||||||
__attribute__((naked, noreturn))
|
* This function is pushed to all process stacks, as a last
|
||||||
void process_switch(uint64_t stack_addr, uint64_t code_addr)
|
* return address. Once the process is done executing, it
|
||||||
{
|
* ends up here.
|
||||||
asm volatile(" \
|
*
|
||||||
push $0x10 \n\
|
* Process is marked as DEAD, and then execution loops.
|
||||||
push %0 \n\
|
* Next time the scheduler sees the process, it will
|
||||||
push $0x202 \n\
|
* automatically delete it from the linked list.
|
||||||
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()
|
void process_exit()
|
||||||
{
|
{
|
||||||
DEBUG("Exiting from process '%s'", current_process->name);
|
DEBUG("Exiting from process '%s'", current_process->name);
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
if (current_process)
|
if (current_process) {
|
||||||
{
|
|
||||||
current_process->status = DEAD;
|
current_process->status = DEAD;
|
||||||
}
|
}
|
||||||
SET_INTERRUPTS;
|
SET_INTERRUPTS;
|
||||||
|
|
||||||
outb(0x20, 0x20);
|
for (;;) {
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-7
@@ -11,15 +11,13 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
|
||||||
READY,
|
READY,
|
||||||
RUNNING,
|
RUNNING,
|
||||||
DEAD
|
DEAD
|
||||||
} status_t;
|
} status_t;
|
||||||
|
|
||||||
struct process_t
|
struct process_t {
|
||||||
{
|
|
||||||
size_t pid;
|
size_t pid;
|
||||||
char name[PROCESS_NAME_MAX];
|
char name[PROCESS_NAME_MAX];
|
||||||
|
|
||||||
@@ -29,13 +27,12 @@ struct process_t
|
|||||||
struct process_t* next;
|
struct process_t* next;
|
||||||
};
|
};
|
||||||
|
|
||||||
void process_init();
|
void process_init(void);
|
||||||
struct process_t* process_create(char* name, void(*function)(void*), void* arg);
|
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_add(struct process_t** processes_list, struct process_t* process);
|
||||||
void process_delete(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);
|
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);
|
||||||
void process_exit();
|
|
||||||
|
|
||||||
void process_display_list(struct process_t* processes_list);
|
void process_display_list(struct process_t* processes_list);
|
||||||
|
|
||||||
|
|||||||
+35
-12
@@ -12,44 +12,67 @@
|
|||||||
|
|
||||||
extern struct process_t* processes_list;
|
extern struct process_t* processes_list;
|
||||||
extern struct process_t* current_process;
|
extern struct process_t* current_process;
|
||||||
|
extern struct process_t* idle_proc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduler_init - Choose the first process
|
||||||
|
*/
|
||||||
void scheduler_init()
|
void scheduler_init()
|
||||||
{
|
{
|
||||||
// Choose first process?
|
|
||||||
current_process = processes_list;
|
current_process = processes_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduler_schedule - Main scheduling routine
|
||||||
|
* @context: CPU context of previous process
|
||||||
|
*
|
||||||
|
* Chooses the next process that we should run.
|
||||||
|
* The routine is executed every SCHEDULER_QUANTUM ticks.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <context> - CPU context for next process
|
||||||
|
*/
|
||||||
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
|
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
|
||||||
{
|
{
|
||||||
|
if (context == NULL) {
|
||||||
|
panic(NULL, "Scheduler called with NULL context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_process == NULL) {
|
||||||
|
// If no more processes, then set IDLE as the current process, that's it.
|
||||||
|
current_process = idle_proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_process == idle_proc && current_process->next == NULL)
|
||||||
|
{
|
||||||
|
return idle_proc->context;
|
||||||
|
}
|
||||||
|
|
||||||
current_process->context = context;
|
current_process->context = context;
|
||||||
//current_process->status = READY;
|
//current_process->status = READY;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct process_t* prev_process = current_process;
|
struct process_t* prev_process = current_process;
|
||||||
if (current_process->next != NULL)
|
if (current_process->next != NULL) {
|
||||||
{
|
|
||||||
current_process = current_process->next;
|
current_process = current_process->next;
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
current_process = processes_list;
|
current_process = processes_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_process != NULL && current_process->status == DEAD)
|
if (current_process != NULL && current_process->status == DEAD) {
|
||||||
{
|
|
||||||
process_delete(&prev_process, current_process);
|
process_delete(&prev_process, current_process);
|
||||||
} else
|
current_process = NULL;
|
||||||
{
|
return idle_proc->context;
|
||||||
|
} else {
|
||||||
current_process->status = RUNNING;
|
current_process->status = RUNNING;
|
||||||
break;
|
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);
|
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));
|
load_cr3(VIRT_TO_PHYS((uint64_t)current_process->root_page_table));
|
||||||
DEBUG("loaded process pml4 into cr3");
|
DEBUG("Loaded process PML4 into CR3");
|
||||||
|
|
||||||
return current_process->context;
|
return current_process->context;
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,6 @@
|
|||||||
#define SCHEDULER_H
|
#define SCHEDULER_H
|
||||||
|
|
||||||
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context);
|
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context);
|
||||||
void scheduler_init();
|
void scheduler_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
; Task (process) switching
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
|
|
||||||
global switch_to_task
|
|
||||||
switch_to_task:
|
|
||||||
|
|
||||||
+45
-5
@@ -6,6 +6,16 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strcpy - copy a NULL-terminated string
|
||||||
|
* @dest: destination buffer where the string is copied
|
||||||
|
* @src: source string to copy from
|
||||||
|
*
|
||||||
|
* Copies the string pointed to by @src (including the terminating
|
||||||
|
* NULL byte) into the buffer pointed to by @dest.
|
||||||
|
*
|
||||||
|
* Return: pointer to the destination string (@dest)
|
||||||
|
*/
|
||||||
char* strcpy(char *dest, const char *src)
|
char* strcpy(char *dest, const char *src)
|
||||||
{
|
{
|
||||||
char *temp = dest;
|
char *temp = dest;
|
||||||
@@ -13,18 +23,48 @@ char* strcpy(char *dest, const char *src)
|
|||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/2488563/strcat-implementation
|
/*
|
||||||
char *strcat(char *dest, const char *src){
|
* strcat - append a NUL-terminated string
|
||||||
|
* @dest: destination buffer containing the initial string
|
||||||
|
* @src: source string to append
|
||||||
|
*
|
||||||
|
* Appends the string pointed to by @src to the end of the string
|
||||||
|
* pointed to by @dest. The terminating NUL byte in @dest is
|
||||||
|
* overwritten and a new terminating NUL byte is added.
|
||||||
|
*
|
||||||
|
* The destination buffer must be large enough to hold the result.
|
||||||
|
*
|
||||||
|
* Taken from: https://stackoverflow.com/questions/2488563/strcat-implementation
|
||||||
|
*
|
||||||
|
* Return: pointer to the destination string (@dest)
|
||||||
|
*/
|
||||||
|
char *strcat(char *dest, const char *src)
|
||||||
|
{
|
||||||
size_t i,j;
|
size_t i,j;
|
||||||
for (i = 0; dest[i] != '\0'; i++)
|
for (i = 0; dest[i] != '\0'; i++);
|
||||||
;
|
|
||||||
for (j = 0; src[j] != '\0'; j++)
|
for (j = 0; src[j] != '\0'; j++)
|
||||||
dest[i+j] = src[j];
|
dest[i+j] = src[j];
|
||||||
|
|
||||||
dest[i+j] = '\0';
|
dest[i+j] = '\0';
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/14159625/implementation-of-strncpy
|
/*
|
||||||
|
* strncpy - copy a string with length limit
|
||||||
|
* @dst: destination buffer
|
||||||
|
* @src: source string
|
||||||
|
* @n: maximum number of bytes to copy
|
||||||
|
*
|
||||||
|
* Copies up to @n bytes from @src to @dst. Copying stops early if a
|
||||||
|
* NULL byte is encountered in @src. If @src is shorter than @n, the
|
||||||
|
* remaining bytes in @dst are left unchanged in this implementation.
|
||||||
|
*
|
||||||
|
* Note: This differs slightly from the standard strncpy behavior,
|
||||||
|
* which pads the remaining bytes with NULL.
|
||||||
|
*
|
||||||
|
* Taken from: https://stackoverflow.com/questions/14159625/implementation-of-strncpy
|
||||||
|
*/
|
||||||
void strncpy(char* dst, const char* src, size_t n)
|
void strncpy(char* dst, const char* src, size_t n)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|||||||
+35
-7
@@ -7,6 +7,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For now, the timer module will be using the PIC.
|
For now, the timer module will be using the PIC.
|
||||||
@@ -18,6 +19,15 @@ interested in multi-core functionnality like SMP)
|
|||||||
|
|
||||||
volatile uint64_t ticks = 0;
|
volatile uint64_t ticks = 0;
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pic_remap - Remap the Programmable Interrupt Controller
|
||||||
|
*
|
||||||
|
* By default, interrupts are mapped at the wrong place.
|
||||||
|
* This function remaps interrupt numbers so interrupts
|
||||||
|
* don't conflict with each other.
|
||||||
|
*/
|
||||||
void pic_remap()
|
void pic_remap()
|
||||||
{
|
{
|
||||||
uint8_t master_mask = inb(0x21);
|
uint8_t master_mask = inb(0x21);
|
||||||
@@ -45,6 +55,12 @@ void pic_remap()
|
|||||||
outb(0xA1, slave_mask);
|
outb(0xA1, slave_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pic_enable - Enable the Programmable Interrupt Controller
|
||||||
|
*
|
||||||
|
* This function enables IRQ0 and IRQ1, which correspond to
|
||||||
|
* the timer and keyboard interrupts, respectively.
|
||||||
|
*/
|
||||||
void pic_enable()
|
void pic_enable()
|
||||||
{
|
{
|
||||||
// Enabling IRQ0 (unmasking it) but not the others
|
// Enabling IRQ0 (unmasking it) but not the others
|
||||||
@@ -55,12 +71,15 @@ void pic_enable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Base frequency = 1.193182 MHz
|
* pit_init - Initialization of the Programmable Interval Timer
|
||||||
1 tick per ms (divide by 1000) = roughly 1193 Hz
|
*
|
||||||
|
* The PIT is the simplest timer we can get working on x86.
|
||||||
|
* It has a base frequency of 1.193182 MHz.
|
||||||
|
* A custom frequency can be set using TIMER_FREQUENCY macro.
|
||||||
*/
|
*/
|
||||||
void pit_init()
|
void pit_init()
|
||||||
{
|
{
|
||||||
uint32_t frequency = 1000; // 1 kHz
|
uint32_t frequency = TIMER_FREQUENCY;
|
||||||
uint32_t divisor = 1193182 / frequency;
|
uint32_t divisor = 1193182 / frequency;
|
||||||
|
|
||||||
// Set PIT to mode 3, channel 0
|
// Set PIT to mode 3, channel 0
|
||||||
@@ -71,17 +90,25 @@ 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
|
* timer_wait - Wait for X ticks
|
||||||
|
*
|
||||||
|
* By default, the timer frequency is 1000Hz, meaning
|
||||||
|
* ticks are equal to milliseconds.
|
||||||
|
*/
|
||||||
void timer_wait(uint64_t wait_ticks)
|
void timer_wait(uint64_t wait_ticks)
|
||||||
{
|
{
|
||||||
uint64_t then = ticks + wait_ticks;
|
uint64_t then = ticks + wait_ticks;
|
||||||
while (ticks < then)
|
while (ticks < then) {
|
||||||
{
|
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* timer_init - Initialization of the timer
|
||||||
|
*
|
||||||
|
* This function wakes the PIT.
|
||||||
|
*/
|
||||||
void timer_init()
|
void timer_init()
|
||||||
{
|
{
|
||||||
// Remapping the PIC, because at startup it conflicts with
|
// Remapping the PIC, because at startup it conflicts with
|
||||||
@@ -91,4 +118,5 @@ void timer_init()
|
|||||||
pic_enable();
|
pic_enable();
|
||||||
pit_init();
|
pit_init();
|
||||||
DEBUG("PIT initialized");
|
DEBUG("PIT initialized");
|
||||||
|
init.timer = true;
|
||||||
}
|
}
|
||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
#ifndef TIMER_H
|
#ifndef TIMER_H
|
||||||
#define TIMER_H
|
#define TIMER_H
|
||||||
|
|
||||||
void timer_init();
|
void timer_init(void);
|
||||||
void timer_wait(unsigned int wait_ticks);
|
void timer_wait(unsigned int wait_ticks);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Binary file not shown.
Reference in New Issue
Block a user