48 Commits

Author SHA1 Message Date
xamidev 5e0bd98874 Merge pull request 'should be right?' (#20) from sys_fix into main
Reviewed-on: #20
2026-05-08 12:59:05 +02:00
xamidev 8c5911bef9 Update README.md 2026-05-08 12:58:08 +02:00
xamidev eb8a03facd Load raw C binary + docs 2026-05-08 12:38:16 +02:00
xamidev 9a1a0e428a tar_list 2026-05-06 14:04:28 +02:00
xamidev c061da4d81 sys_read/open/close 2026-05-06 13:29:35 +02:00
xamidev 63e9a761a3 should be right? 2026-05-06 11:26:33 +02:00
xamidev 935564c4b2 Merge pull request 'Tar filesystem' (#19) from tarfs into main
Reviewed-on: #19
2026-05-06 09:24:41 +02:00
xamidev c00a247ead Kshell: load executable command 2026-05-04 20:38:10 +02:00
xamidev ccb6ca89f1 Load TAR archive + run raw user program 2026-05-04 20:24:18 +02:00
xamidev e399ec6a46 alpha 0.1.121 2026-04-10 15:04:52 +02:00
xamidev dd9315f2f1 Update docs/MANUAL.md 2026-04-06 14:54:07 +02:00
xamidev f91831616c Merge pull request 'user-scheduler' (#18) from user-scheduler into main
Reviewed-on: #18
2026-04-05 19:37:15 +02:00
xamidev 0240220796 Scheduler fix, User RR 2026-04-03 19:18:08 +02:00
xamidev 437bd0e751 process_create_user 2026-04-03 18:45:12 +02:00
xamidev 1fe5eb2d38 Merge pull request 'syscall' (#17) from syscall into main
Reviewed-on: xamidev/pepperOS#17
2026-04-02 19:16:34 +02:00
xamidev dbffb7f5fa userland HELLO WORLD 2026-04-02 19:11:37 +02:00
xamidev aa30d9c6b5 user program (still many #PF) 2026-04-02 17:05:51 +02:00
xamidev 11a9dd4adb Load limine module + alloc user stack 2026-04-01 15:51:04 +02:00
xamidev d644126901 TSS setup 2026-04-01 13:04:33 +02:00
xamidev 8be2a744b4 Refactor GDT entry filling 2026-04-01 11:44:57 +02:00
xamidev e9b57f70b1 End the _t nonsense 2026-04-01 09:15:59 +02:00
xamidev e8a0a36889 Enable FPU 2026-03-31 21:04:44 +02:00
xamidev 1fc5225fd2 kheap info 2026-03-31 17:48:11 +02:00
xamidev 2f1eef9e15 UBSan conditional compilation 2026-03-29 09:38:24 +02:00
xamidev 65371077d9 more UBsan 2026-03-28 21:50:19 +01:00
xamidev 3cd38f654c Undefined Behavior Sanitization 2026-03-28 21:09:08 +01:00
xamidev 89259ec9b2 Stack Smashing Protection 2026-03-26 20:14:56 +01:00
xamidev 0fbaf6d26e Date functions (get current time) 2026-03-26 17:59:02 +01:00
xamidev 532953da4d CPU Name identification 2026-03-26 16:53:39 +01:00
xamidev ac788c55d3 Upgrade VMM for processes 2026-03-22 09:03:43 +01:00
xamidev 3ae56bbad5 Kernel debug shell 2026-03-21 11:34:46 +01:00
xamidev db36899152 Add kprintf for DEBUG(); differentiated from printf() 2026-03-21 10:36:54 +01:00
xamidev 7d03a0090b Merge pull request 'real-hw-fix' (#16) from real-hw-fix into main
Reviewed-on: xamidev/pepperOS#16
2026-03-20 16:58:08 +01:00
xamidev 03f87723d1 Splash 2026-03-20 10:04:16 +01:00
xamidev 3607a7179c printf spinlock + remove DEPRECATED stuff + begin separating x86 stuff 2026-03-20 09:01:57 +01:00
xamidev 424b4c4632 Use MSR to map framebuffer as WC (write-combining) = huge speed diff on real HW 2026-03-19 19:34:31 +01:00
xamidev 6a82d581fb Fix PMM for real HW + serial lock 2026-03-19 16:54:23 +01:00
xamidev b77c53ae99 Keyboard buffer + getline 2026-03-18 13:07:26 +01:00
xamidev f7735eb3a4 Move headers to include/ 2026-03-18 11:48:33 +01:00
xamidev a1e8aacd01 improve README.md 2026-03-17 10:33:10 +01:00
xamidev ccc8985d4c Merge pull request 'Improve Makefile' (#15) from furtest/pepperOS:makefile into main
Reviewed-on: xamidev/pepperOS#15
2026-03-15 21:17:44 +01:00
xamidev 0482f594ef Flanterm back to bump alloc (allows earlier use for real hw debugging) 2026-03-15 21:11:47 +01:00
furtest b02a4b5284 Fix build-iso prerequisites 2026-03-15 18:05:24 +01:00
furtest 32f3889565 Move PHONY tags and fix clean
Move the PHONY tags to make them clearer to read.
Fix the clean rule so it deletes the build directory.
2026-03-15 18:01:35 +01:00
furtest 803ac0879b Auto find source files check for changes
Previously the build process removed everything and did all the build
again on each make invocation.
This fixes this behaviour with two changes.
First dynamically find the list of files to build using find instead of
a manually written list.
Then use implicit rules to only build files that need to be built again
instead of recompiling everything.
2026-03-15 17:56:26 +01:00
furtest 9fc55f98d8 Use variables for build and pepperk and rename build target.
Instead of hardcoding the names set them using a variable.
Also rename the target build to the name of the file it builds which is
in the ELFFILE variable.
2026-03-15 16:58:04 +01:00
furtest 11bd628821 Extract CC and LD to variables.
This allows to change the name of the compiler or linker when calling
make.
2026-03-15 16:57:29 +01:00
xamidev 80d8b49560 Merge pull request 'spinlock' (#14) from spinlock into main
Reviewed-on: xamidev/pepperOS#14
2026-03-15 09:55:45 +01:00
78 changed files with 2841 additions and 664 deletions
+50 -19
View File
@@ -1,33 +1,60 @@
SOURCES = src/sched/spinlock.c 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/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c USER_PROGRAMS := pedicel.raw apex.raw
USER_FILES := wow.txt
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 BUILDDIR := build
CC_PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable ELFFILE := pepperk
SRC := src
.PHONY: build build-iso debug debug2 run clean CC_PROBLEMATIC_FLAGS:=-Wno-unused-parameter -Wno-unused-variable
build: ifeq ($(UBSAN),true)
mkdir -p build SOURCES := $(shell find src -name '*.c')
rm -f *.o build/*.o CC_PROBLEMATIC_FLAGS:= -fsanitize=undefined
x86_64-elf-gcc -g -c -Isrc $(SOURCES) $(CC_PROBLEMATIC_FLAGS) $(CC_FLAGS) else
mv *.o build/ SOURCES := $(shell find src -name '*.c')
nasm -f elf64 src/idt/idt.S -o build/idt_stub.o SOURCES := $(filter-out src/security/ubsan.c, $(SOURCES))
x86_64-elf-ld -o pepperk -T linker.ld build/*.o endif
nm -n pepperk | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
OBJFILES := $(patsubst $(SRC)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
CC := x86_64-elf-gcc
CC_FLAGS=-Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fstack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel -mno-red-zone
LD := x86_64-elf-ld
$(ELFFILE): $(BUILDDIR) $(OBJFILES)
nasm -f elf64 src/arch/x86/idt.S -o $(BUILDDIR)/idt_stub.o
$(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o
# Get the symbols for debugging
nm -n $(ELFFILE) | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
python3 symbols.py python3 symbols.py
nasm -f elf64 symbols.S -o build/symbols.o nasm -f elf64 symbols.S -o $(BUILDDIR)/symbols.o
x86_64-elf-ld -o pepperk -T linker.ld build/*.o $(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o $(BUILDDIR)/symbols.o
$(BUILDDIR):
@mkdir -p $(BUILDDIR)
$(BUILDDIR)/%.o: $(SRC)/%.c
mkdir -p $(dir $@)
$(CC) -g -c -Iinclude $< $(CC_PROBLEMATIC_FLAGS) $(CC_FLAGS) -o $@
limine/limine: limine/limine:
rm -rf limine rm -rf limine
git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1
$(MAKE) -C limine $(MAKE) -C limine
build-iso: limine/limine build .PHONY: user
user:
$(MAKE) -C user
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) $(USER_PROGRAMS) -C ../user $(USER_FILES)
build-iso: limine/limine $(ELFFILE)
rm -rf iso_root rm -rf iso_root
mkdir -p iso_root/boot mkdir -p iso_root/boot
cp -v pepperk iso_root/boot cp -v $(ELFFILE) iso_root/boot
mkdir -p iso_root/boot/limine mkdir -p iso_root/boot/limine
cp -v limine.conf iso_root/boot/limine cp -v limine.conf iso_root/boot/limine
cp $(BUILDDIR)/initfs.tar iso_root/boot/
mkdir -p iso_root/EFI/BOOT mkdir -p iso_root/EFI/BOOT
cp -v limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin iso_root/boot/limine/ cp -v limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin iso_root/boot/limine/
cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/ cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/
@@ -39,16 +66,20 @@ build-iso: limine/limine build
iso_root -o pepper.iso iso_root -o pepper.iso
./limine/limine bios-install pepper.iso ./limine/limine bios-install pepper.iso
.PHONY: debug
debug: debug:
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -D qemu.log -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 $(ELFFILE) --command=debug.gdb
.PHONY: debug2
debug2: debug2:
/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 -no-reboot -no-shutdown &
pwndbg pepperk --command=debug.gdb pwndbg $(ELFFILE) --command=debug.gdb
.PHONY: run
run: build-iso 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
.PHONY: clean
clean: clean:
rm -rf *.o symbols.map symbols.S pepperk iso_root pepper.iso limine build/*.o rm -rf $(BUILDDIR) symbols.map symbols.S $(ELFFILE) iso_root pepper.iso limine
+41 -24
View File
@@ -1,50 +1,64 @@
# <img width="40" height="40" alt="red-pepper" src="https://i.ibb.co/mrHH6d1m/pixil-frame-0-4.png" /> pepperOS: "will never be done" # <img width="40" height="40" alt="red-pepper" src="https://i.ibb.co/mrHH6d1m/pixil-frame-0-4.png" /> pepperOS: "will never be done"
<a href="https://ibb.co/KxbfxmFB"><img src="https://i.ibb.co/GQn8QFcG/pepper.png" alt="pepper" border="0"></a>
## Description
## Trying the kernel PepperOS is a 64-bit freely-licensed monolithic kernel for x86 processors, with round-robin preemptive scheduling and 4-level paging. See the [manual](docs/MANUAL.md) for more.
First install the dependencies: `sudo apt install python3 xorriso make qemu-system` ## Trying the kernel in QEMU
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: ### Debian-based distributions
First, install the dependencies: `sudo apt install nasm python3 xorriso make qemu-system`
Then, you can 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 brew install x86_64-elf-gcc
``` ```
Then, to compile the kernel and make an ISO image file: `make build-iso` If you're already on a 64-bit machine (which you probably are), and don't want to install a cross-compiler, you can just override `CC` and `LD` variables in the Makefile, like so:
To run it with QEMU, `make run`
## TODO ```
CC := gcc
LD := ld
```
The basics that I'm targeting are: Then, to compile the kernel and make an ISO image file, run: `make`.
To build the user programs and the initial filesystem, do `make user`.
To run it with QEMU, do: `make run`.
### Basic utility of what we call a "kernel" ## Trying the kernel on real hardware
- Implement tasks, and task switching + context switching and spinlock acquire/release Compile the kernel and generate an ISO image like described above, then burn the image to a USB stick, `/dev/sdX` being the device name (you can get it using `lsblk`):
- Load an executable
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
- Getting to userspace (ring 3 switching, syscall interface)
- Porting musl libc or equivalent
### Scalability/maintenance/expansion features ```
sudo dd if=pepper.iso of=/dev/sdX
```
- Documentation ## Compilation options
- SOME error handling in functions
- Unit tests
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
### Optional features By default, PepperOS is compiled without the following features:
In the future, maybe? ```
- SMP support (Limine provides functionality to make this easier) UBSAN - undefined behavior sanitization
- Parsing the ACPI tables and using them for something ```
- Replacing the PIT timer with APIC
These features can be activated by setting them to "true" at the end of the make command, for example:
```
make UBSAN=true
```
## Writing software for PepperOS
If you want to write software for PepperOS, take a look at the [Software Developer's guide](docs/SOFTWARE.md).
## Thanks ## Thanks
PepperOS wouldn't be possible without the following freely-licensed software: 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) - Charles Nicholson's [nanoprintf](https://github.com/charlesnicholson/nanoprintf)
- Mintuski's [Flanterm](https://codeberg.org/Mintsuki/Flanterm) terminal emulator - Mintuski's [Flanterm](https://codeberg.org/Mintsuki/Flanterm) terminal emulator
...and without these amazing resources: ...and without these amazing resources:
@@ -52,3 +66,6 @@ PepperOS wouldn't be possible without the following freely-licensed software:
- the [OSDev](https://osdev.org) wiki & forums - the [OSDev](https://osdev.org) wiki & forums
- Intel 64 and IA-32 Architectures Software Developer's Manual - Intel 64 and IA-32 Architectures Software Developer's Manual
- Documentation for the [GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc/) - Documentation for the [GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc/)
- dreamos82's [OSDev Notes](https://github.com/dreamportdev/Osdev-Notes/tree/master)
- the [Sortix UBsan hook implementations](https://gitlab.com/sortix/sortix/-/blob/main/libc/ubsan/ubsan.c)
- the [CSC 395](https://curtsinger.cs.grinnell.edu/teaching/2022S/CSC395/kernel/) Kernel Development course from Grinnell College
+52
View File
@@ -0,0 +1,52 @@
# PepperOS Manual
# Table of Contents
- [Overview](#i-overview)
- [Supported Hardware](#a-supported-hardware)
- [Features](#b-features)
- [Kernel architecture](#ii-kernel-architecture)
- [Boot process](#a-boot-process)
- [Memory management](#b-memory-management)
- [Scheduling](#c-scheduling)
- [Input/output](#d-inputoutput)
- [Syscall table](#iii-syscall-table)
## I. Overview
## a. Supported Hardware
The recommended hardware to run PepperOS is the following:
- UEFI/BIOS
- Any x86 processor, 64-bits only
- PS/2 Keyboard
- Minimum 128MB of memory
## b. Features
- Round robin preemptive scheduling
- Coexistence of ring 0 and ring 3 processes
## II. Kernel architecture
### a. Boot process
### b. Memory management
### c. Scheduling
### d. Input/Output
## III. Syscall table
The syscall interface in the Pepper kernel uses the System V ABI convention for argument order.
It vaguely mimics Unix-like systems.
Name | Number (%rax) | arg0 (%rdi) | arg1 (%rsi) | arg2 (%rdx) |
|---|---|---|---|---|
| sys_read | 0 | unsigned int fd | char* buf | size_t count |
| sys_write | 1 | unsigned int fd | const char* buf | size_t count |
| sys_open | 2 | const char* filename | int flags | |
| sys_close | 3 | unsigned int fd | | |
| sys_exit | 60 | int error_code | | |
+57
View File
@@ -0,0 +1,57 @@
# Writing software for PepperOS
## Why would you want to do that?
Honestly I have no idea. Maybe you have too much free time.
Keep in mind that the Pepper kernel is a personal project and it's full of bugs, inconsistencies, weird ways of doing things (and I don't care because it's my toy).
Now if you still want to write something for this OS, thank you. Follow along.
## 1. Write the source code
PepperOS is able to run programs written in x86 assembly, and C programs.
### x86 Assembly
Start your assembly file with the `bits 64` instruction, to emit 64-bit code.
You can add sections `.text`, `.data`, `.bss` as you need.
The three things to take in consideration here are:
- PepperOS does not use the `syscall` instruction, instead it uses the old-fashioned `int 0x80` to trigger a system call.
- The entry point should be labelled as `_start`.
- At the end of the file, there should be an exit system call followed by a loop, like so:
```nasm
.end:
mov rax, 0x3C
mov rdi, 0x0
int 0x80
.loop:
jmp .loop
```
For an example, look at the file [pedicel.S](../user/pedicel.S).
### C program
You will find relevant headers in the `libc` directory. They contain system call wrappers, utility functions, and more. See what's implemented there and what's not.
To invoke a system call you can use the functions defined in `libc/syscall.h`.
## 2. Add the Makefile rule and variable
Now that your code is complete, add a Makefile rule to `user/Makefile` with your program name. You can just copy-paste the rule that applies to you (either from an Assembly source or C source) and change the name of the files (.raw, .elf, etc...) in the rule.
For clarity, raw binaries have the `.raw` extension, and ELF ones have `.elf`.
You also now have to add the name of the executable to the `USER_PROGRAMS` variable at the top of the global Makefile.
Finally, do `make user` to compile your program.
## 3. Run your program
You can now boot up PepperOS, in a VM or on real hardware, and use the kernel's shell to `list` files in the filesystem (to see if your executable was properly added), and then, run it with the `load` command. Congratulations, you made a program for a random hobby OS!
## 4. (Optional) debugging
Use GDB with the `make debug` rule!
For your information, user programs are loaded at `0x400000`. Can be good to know to set breakpoints.
## 5. (Optional) contribute!
If you like what you've done and you think it could be nice to add it to PepperOS, send it to me by e-mail: `xamidev (at) riseup (dot) net`. It may or may not be added in a future release... who knows?
+8
View File
@@ -2,6 +2,14 @@
This document describes the coding style for the Pepper kernel. It is used as a guideline across all source files. This document describes the coding style for the Pepper kernel. It is used as a guideline across all source files.
## Setting up a language server (optional)
Before you do anything you might want to setup a language server with your editor. This will save you lots of time correcting errors and stuff. I use `clangd`, and generate my `compile_commands.json` like so:
```
bear -- make
```
## Indentation ## Indentation
Indentations should be 4 characters long. Indentations should be 4 characters long.
+15 -2
View File
@@ -1,6 +1,6 @@
/* /*
* @author xamidev <xamidev@riseup.net> * @author xamidev <xamidev@riseup.net>
* @brief Global Descriptor Table (for legacy reasons) * @brief Global Descriptor Table
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
@@ -13,19 +13,32 @@
// we'll only use this as a requirement for paging, not more. // we'll only use this as a requirement for paging, not more.
// This means base 0 and no limit (whole address space) // This means base 0 and no limit (whole address space)
#define NUM_GDT_ENTRIES 5 #define NUM_GDT_ENTRIES 7
#define NULL_SELECTOR 0x00 #define NULL_SELECTOR 0x00
#define KERNEL_CODE_SEGMENT 0x08 #define KERNEL_CODE_SEGMENT 0x08
#define KERNEL_DATA_SEGMENT 0x10 #define KERNEL_DATA_SEGMENT 0x10
#define USER_CODE_SEGMENT 0x18 #define USER_CODE_SEGMENT 0x18
#define USER_DATA_SEGMENT 0x20 #define USER_DATA_SEGMENT 0x20
#define TSS_SEGMENT 0x28
struct GDTR { struct GDTR {
uint16_t limit; uint16_t limit;
uint64_t address; uint64_t address;
} __attribute__((packed)); } __attribute__((packed));
struct tss {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved1;
uint64_t ist[7];
uint64_t reserved2;
uint16_t reserved3;
uint16_t iopb;
} __attribute__((packed));
void gdt_init(void); void gdt_init(void);
#endif #endif
+17 -9
View File
@@ -1,14 +1,20 @@
/* #ifndef X86_H
* @author xamidev <xamidev@riseup.net> #define X86_H
* @brief Interrupt Descriptor Table setup and dispatching
* @license GPL-3.0-only
*/
#ifndef IDT_H
#define IDT_H
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
uint64_t rdmsr(uint32_t msr);
void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx);
void wrmsr(uint32_t msr, uint64_t value);
bool x86_has_msr();
void x86_arch_init();
void x86_cpu_identification();
int cpuid_get_vendor_string(char* str);
/* Interrupt Descriptor Table */
void idt_init(void); void idt_init(void);
struct interrupt_descriptor { struct interrupt_descriptor {
@@ -29,7 +35,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 {
uint64_t r15; uint64_t r15;
uint64_t r14; uint64_t r14;
uint64_t r13; uint64_t r13;
@@ -56,4 +62,6 @@ struct cpu_status_t {
uint64_t iret_ss; uint64_t iret_ss;
}; };
struct cpu_status* syscall_handler(struct cpu_status* regs);
#endif #endif
+14
View File
@@ -0,0 +1,14 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Boot routines
* @license GPL-3.0-only
*/
#ifndef BOOT_H
#define BOOT_H
#include <kernel.h>
void populate_boot_context(struct boot_context* boot_ctx);
#endif
+70
View File
@@ -0,0 +1,70 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PepperOS configuration file
* @license GPL-3.0-only
*/
#ifndef CONFIG_H
#define CONFIG_H
/* version */
#define PEPPEROS_VERSION_MAJOR "0"
#define PEPPEROS_VERSION_MINOR "1"
#define PEPPEROS_VERSION_PATCH "121"
#define PEPPEROS_SPLASH \
"\x1b[38;5;196m \x1b[38;5;231m____ _____\r\n\x1b[0m"\
"\x1b[38;5;196m ____ ___ ____ ____ ___ _____\x1b[38;5;231m/ __ \\/ ___/\r\n\x1b[0m"\
"\x1b[38;5;196m / __ \\/ _ \\/ __ \\/ __ \\/ _ \\/ ___\x1b[38;5;231m/ / / /\\__ \\ \r\n\x1b[0m"\
"\x1b[38;5;196m / /_/ / __/ /_/ / /_/ / __/ / \x1b[38;5;231m/ /_/ /___/ / \r\n\x1b[0m"\
"\x1b[38;5;196m / .___/\\___/ .___/ .___/\\___/_/ \x1b[38;5;231m\\____//____/ \r\n\x1b[0m"\
"\x1b[38;5;196m/_/ /_/ /_/ \r\n\x1b[0m"\
" --- version \x1b[38;5;220m"PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\x1b[0m built on \x1b[38;5;40m"__DATE__" "__TIME__"\x1b[0m\r\n"
/* pedicel */
#define PEDICEL_PROMPT "pedicel$ "
#define PEDICEL_INPUT_SIZE 128
/* process */
#define PROCESS_NAME_MAX 64
#define PROCESS_STACK_SIZE 0x10000 // 64kb
#define PROCESS_STACK_TOP 0x80000000
/* sched */
// 1 tick = 1 ms => quantum = 10ms
#define SCHEDULER_QUANTUM 10
/* kernel */
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
#define KERNEL_SIZE 0x200000
#define KERNEL_STACK_SIZE 65536
#define KERNEL_IDT_ENTRIES 33
/* user */
#define USER_STACK_TOP 0x80000000
#define USER_STACK_PAGES 16 // 16*4096 = 64kb
#define USER_CODE_START 0x400000 // like linux
/* paging */
#define PAGING_MAX_PHYS 0x200000000
/* heap */
#define KHEAP_SIZE (32*1024*1024)
/* term */
#define TERM_HISTORY_MAX_LINES 256
/* kbd */
#define KBD_BUFFER_MAX 256
/* time */
#define TIMER_FREQUENCY 1000
/* ssp */
#define STACK_CHK_GUARD 0x7ABA5C007ABA5C00
/* fs */
#define FDT_MAX 8 // Maximum amount of file descriptors per process
#endif
+18
View File
@@ -0,0 +1,18 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PS/2 Keyboard driver
* @license GPL-3.0-only
*/
#ifndef INITFS_H
#define INITFS_H
#include <limine.h>
int initfs_init(struct limine_file* tar_file);
int tar_lookup(unsigned char* archive, char* filename, char** out);
int tar_exists(const char* filename);
int tar_read(char* filename, char* out, int count, int offset);
void tar_list();
#endif
@@ -7,7 +7,12 @@
#ifndef PS2_H #ifndef PS2_H
#define PS2_H #define PS2_H
#include <stddef.h>
void keyboard_handler(void); void keyboard_handler(void);
char keyboard_getchar();
int keyboard_putchar(char c);
int keyboard_getline(char* output, size_t size);
#define SHIFT_PRESSED_BIT 0b00000001 #define SHIFT_PRESSED_BIT 0b00000001
#define ALT_PRESSED_BIT 0b00000010 #define ALT_PRESSED_BIT 0b00000010
@@ -8,8 +8,9 @@
#define TERM_H #define TERM_H
void kputs(const char* str); void kputs(const char* str);
void _putchar(char character);
void term_init(void); void term_init(void);
int printf(const char* fmt, ...); int printf(const char* fmt, ...);
void internal_putc(int c, void *_);
int kprintf(const char* fmt, ...);
#endif #endif
+20 -7
View File
@@ -7,21 +7,27 @@
#ifndef KERNEL_H #ifndef KERNEL_H
#define KERNEL_H #define KERNEL_H
#include "limine.h"
enum ErrorCodes { enum ErrorCodes {
ENOMEM, ENOMEM, // No memory
EIO EIO, // Input/output error
ENOENT, // No entry
EBADFD, // Bad file descriptor
EMFILE, // Too many open files
EINVAL // Invalid argument
}; };
#define CLEAR_INTERRUPTS __asm__ volatile("cli") #define CLEAR_INTERRUPTS __asm__ volatile("cli")
#define SET_INTERRUPTS __asm__ volatile("sti") #define SET_INTERRUPTS __asm__ volatile("sti")
#include "io/serial/serial.h" #include <io/serial/serial.h>
#include "io/term/term.h" #include <io/term/term.h>
#include "idt/idt.h" #include <arch/x86.h>
#include <stdbool.h> #include <stdbool.h>
extern volatile uint64_t ticks; extern volatile uint64_t ticks;
#define DEBUG(log, ...) printf("[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__) #define DEBUG(log, ...) kprintf("[%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__); \
@@ -34,15 +40,19 @@ extern volatile uint64_t ticks;
// 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* ctx, const char* str);
void hcf(void); void hcf(void);
void idle(void); void idle(void);
void pedicel_main(void* arg);
/* debug */ /* 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); void boot_mem_display(void);
int loader_load_raw();
#define assert(check) do { if(!(check)) hcf(); } while(0) #define assert(check) do { if(!(check)) hcf(); } while(0)
struct boot_context { struct boot_context {
@@ -50,6 +60,8 @@ struct boot_context {
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;
struct limine_boot_time_response* bootdate;
struct limine_module_response* module;
}; };
// Are these modules initialized yet? // Are these modules initialized yet?
@@ -58,6 +70,7 @@ struct init_status {
bool serial; bool serial;
bool keyboard; bool keyboard;
bool timer; bool timer;
bool all;
}; };
#endif #endif
View File
+3 -2
View File
@@ -16,11 +16,11 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
struct heap_block_t { struct heap_block {
size_t size; size_t size;
bool free; // 1byte bool free; // 1byte
uint8_t reserved[7]; // (7+1 = 8 bytes) uint8_t reserved[7]; // (7+1 = 8 bytes)
struct heap_block_t* next; struct heap_block* next;
} __attribute__((aligned(16))); } __attribute__((aligned(16)));
void kheap_init(void); void kheap_init(void);
@@ -28,5 +28,6 @@ void* kmalloc(size_t size);
void kfree(void* ptr); void kfree(void* ptr);
void* kalloc_stack(void); void* kalloc_stack(void);
void kheap_map_page(void); void kheap_map_page(void);
void kheap_info();
#endif #endif
@@ -11,14 +11,16 @@
#include <stdint.h> #include <stdint.h>
#include <limine.h> #include <limine.h>
#include "mem/heap/kheap.h" #include <mem/kheap.h>
#include <kernel.h> #include <kernel.h>
void paging_init(struct boot_context boot_ctx); 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);
uint64_t* alloc_page_table();
// To swap root page tables // To swap root page tables
void load_cr3(uint64_t value); void load_cr3(uint64_t value);
void invlpg(void *addr);
extern uint64_t hhdm_off; extern uint64_t hhdm_off;
@@ -32,7 +34,7 @@ extern uint64_t hhdm_off;
#define PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK) #define PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK)
#define ALIGN(size) ALIGN_UP(size, 16) #define ALIGN(size) ALIGN_UP(size, 16)
#define BLOCK_MIN_SIZE (sizeof(struct heap_block_t) + 16) #define BLOCK_MIN_SIZE (sizeof(struct heap_block) + 16)
#define PML4_INDEX(x) (((x) >> 39) & 0x1FF) #define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF) #define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
@@ -8,6 +8,7 @@
#define MEM_UTILS_H #define MEM_UTILS_H
#include <stddef.h> #include <stddef.h>
#include <limine.h>
void* memcpy(void* restrict dest, const void* restrict src, size_t n); void* memcpy(void* restrict dest, const void* restrict src, size_t n);
void* memset(void* s, int c, size_t n); void* memset(void* s, int c, size_t n);
+31
View File
@@ -0,0 +1,31 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Virtual memory manager
* @license GPL-3.0-only
*/
#ifndef VMM_H
#define VMM_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
struct vmm_context {
uint64_t* pml4;
};
void vmm_init(void);
void* vmm_alloc_region(uint64_t* pml4, size_t pages, uint64_t flags);
bool vmm_is_mapped(uint64_t* pml4, uint64_t virt);
void vmm_unmap(uint64_t* pml4, uint64_t virt);
void* vmm_map(uint64_t* pml4, uint64_t virt, uint64_t flags);
uint64_t* vmm_create_address_space();
uint64_t vmm_virt_to_phys(uint64_t* pml4, uint64_t virt);
uintptr_t vmm_alloc_user_stack(uint64_t* pml4);
uintptr_t vmm_alloc_user_code(uint64_t* pml4, void* code_addr, uint64_t code_size);
#define VMM_USER_SPACE_START 0x0000000000001000
#define VMM_USER_SPACE_END 0x00007FFFFFFFF000
#endif
+56
View File
@@ -0,0 +1,56 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Process definition
* @license GPL-3.0-only
*/
#ifndef PROCESS_H
#define PROCESS_H
#include <stddef.h>
#include <config.h>
#include <stdint.h>
#include <limine.h>
#include <stdbool.h>
typedef enum {
READY,
RUNNING,
DEAD
} status_t;
struct fd {
int fd;
char filename[PROCESS_NAME_MAX]; // File opened
uint64_t cursor; // Cursor position in file
bool open;
};
struct process {
size_t pid;
char name[PROCESS_NAME_MAX];
status_t status;
struct cpu_status* context;
void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511]
void* kernel_stack; // Used for interrupts (syscall: int 0x80), defines the TSS RSP0
struct fd fdt[FDT_MAX]; // File Descriptor Table
size_t next_free_fd;
struct process* next;
};
void process_init(void);
struct process* process_create(char* name, void(*function)(void*), void* arg);
void process_add(struct process** processes_list, struct process* process);
void process_delete(struct process** processes_list, struct process* process);
struct process* process_get_next(struct process* process);
void process_exit(void);
void process_display_list(struct process* processes_list);
void process_create_user_raw(char* file, int size, char* name);
#endif
@@ -7,7 +7,7 @@
#ifndef SCHEDULER_H #ifndef SCHEDULER_H
#define SCHEDULER_H #define SCHEDULER_H
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context); struct cpu_status* scheduler_schedule(struct cpu_status* context);
void scheduler_init(void); void scheduler_init(void);
#endif #endif
@@ -1,4 +1,4 @@
/* /*
* @author xamidev <xamidev@riseup.net> * @author xamidev <xamidev@riseup.net>
* @brief Spinlock implementation * @brief Spinlock implementation
* @license GPL-3.0-only * @license GPL-3.0-only
@@ -10,13 +10,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
struct spinlock_t struct spinlock
{ {
bool locked; bool locked;
uint64_t rflags; uint64_t rflags;
}; };
void spinlock_acquire(struct spinlock_t* lock); void spinlock_acquire(struct spinlock* lock);
void spinlock_release(struct spinlock_t* lock); void spinlock_release(struct spinlock* lock);
#endif #endif
+65
View File
@@ -0,0 +1,65 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Undefined behavior sanitization hooks
* @license GPL-3.0-only
*/
#ifndef UBSAN_H
#define UBSAN_H
#include <stdint.h>
struct ubsan_source_location
{
const char* filename;
uint32_t line;
uint32_t column;
};
struct ubsan_type_descriptor
{
uint16_t type_kind;
uint16_t type_info;
char type_name[];
};
struct ubsan_type_mismatch_v1_data
{
struct ubsan_source_location location;
struct ubsan_type_descriptor* type;
unsigned char log_alignment;
unsigned char type_check_kind;
};
struct ubsan_pointer_overflow_data
{
struct ubsan_source_location location;
};
struct ubsan_shift_out_of_bounds_data
{
struct ubsan_source_location location;
struct ubsan_type_descriptor* lhs_type;
struct ubsan_type_descriptor* rhs_type;
};
struct ubsan_invalid_value_data
{
struct ubsan_source_location location;
struct ubsan_type_descriptor* type;
};
struct ubsan_out_of_bounds_data
{
struct ubsan_source_location location;
struct ubsan_type_descriptor* array_type;
struct ubsan_type_descriptor* index_type;
};
struct ubsan_overflow_data
{
struct ubsan_source_location location;
struct ubsan_type_descriptor* type;
};
#endif
@@ -12,5 +12,7 @@
char *strcpy(char *dest, const char *src); char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src); char *strcat(char *dest, const char *src);
void strncpy(char* dst, const char* src, size_t n); void strncpy(char* dst, const char* src, size_t n);
int strncmp(const char* s1, const char* s2, size_t n);
size_t strlen(const char* str);
#endif #endif
+25
View File
@@ -0,0 +1,25 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Date helper functions
* @license GPL-3.0-only
*/
#ifndef DATE_H
#define DATE_H
#include <stdint.h>
struct date {
uint64_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
struct date date_timestamp_to_date(uint64_t timestamp);
struct date date_now();
#endif
+3
View File
@@ -1,6 +1,9 @@
timeout: 3 timeout: 3
interface_branding: Welcome to the PepperOS disk!
/PepperOS /PepperOS
protocol: limine protocol: limine
comment: Default configuration (warning: spicy)
path: boot():/boot/pepperk path: boot():/boot/pepperk
module_path: boot():/boot/initfs.tar
+48
View File
@@ -0,0 +1,48 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief x86 CPU identification
* @license GPL-3.0-only
*/
#include <stdint.h>
#include <stddef.h>
#include <kernel.h>
#include <string/string.h>
/*
* cpuid - Wrapper for CPUID instruction
* @leaf: Requested leaf (input EAX)
* @eax: EAX register value (output)
* @ebx: EBX register value (output)
* @ecx: ECX register value (output)
* @edx: EDX register value (output)
*/
void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx)
{
__asm__ volatile("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(leaf));
}
/*
* cpuid_get_vendor_string - Get the CPU vendor string
* @str: String at least 13 bytes long (for output)
*
* Return:
* %0 - on success
*/
int cpuid_get_vendor_string(char* str)
{
uint32_t eax, ebx, ecx, edx;
cpuid(0, &eax, &ebx, &ecx, &edx);
char output[13] = {0};
uint32_t regs[3] = {ebx, edx, ecx};
for (unsigned int j=0; j<3; j++) {
for (unsigned int i=0; i<4; i++) {
output[4*j+i] = (char)((regs[j] >> 8*i) & 0xff);
}
}
strncpy(str, output, 13);
return 0;
}
+138
View File
@@ -0,0 +1,138 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Global Descriptor Table and Task State Segment setup
* @license GPL-3.0-only
*/
#include <arch/gdt.h>
#include <stdint.h>
#include <io/serial/serial.h>
#include <kernel.h>
// Descriptors are 8-byte wide (64bits)
// So the selectors will be (in bytes): 0x0, 0x8, 0x10, 0x18, etc..
uint64_t gdt_entries[NUM_GDT_ENTRIES];
struct GDTR gdtr;
struct tss tss = {0};
/*
* gdt_load - Loads Global Descriptor Table
*/
static void gdt_load()
{
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()
{
// Here, 0x8 is the kernel code selector
// and 0x10 is the kernel data selector
asm volatile (
"mov $0x10, %%ax \n" // Reload segments with kernel data selector
"mov %%ax, %%ds \n"
"mov %%ax, %%es \n"
"mov %%ax, %%fs \n"
"mov %%ax, %%gs \n"
"mov %%ax, %%ss \n"
"pushq $0x8 \n" // CS reload
"lea 1f(%%rip), %%rax \n"
"push %%rax \n"
"lretq \n"
"1: \n" // Execution continues here after CS reload
:
:
: "rax", "memory"
);
}
/*
* get_set_entry - Sets a GDT entry
* @num: Number of the entry (index in GDT)
* @flags: Flags (Granularity, Size, Long mode)
* @access: Access byte (contains Descriptor Privilege Level)
*
* This function fills a GDT entry with the specified @flags
* and @access byte. The base and limit fields are left to zero
* because we don't use segmentation for memory management.
*/
static void gdt_set_entry(int num, uint8_t flags, uint8_t access)
{
uint64_t gdt_entry = 0;
gdt_entry |= (access << 8);
gdt_entry |= (flags << 20);
// Rest (base, limit) is always zero
gdt_entries[num] = gdt_entry << 32;
}
/*
* gdt_set_tss - Setup the TSS entry in the GDT
* @num: Number of the entry (index in GDT)
*
* This function sets up a Task State Segment entry
* in the Global Descriptor Table.
*
* The entry is 128-bit long, so it actually takes
* two 64-bit GDT entries.
*/
static void gdt_set_tss(int num)
{
uint64_t tss_base = (uint64_t)&tss;
uint64_t tss_limit = sizeof(struct tss) - 1;
tss.iopb = sizeof(struct tss);
uint64_t tss_low = 0;
tss_low |= (tss_limit & 0xFFFFULL);
tss_low |= (tss_base & 0xFFFFFFULL) << 16;
tss_low |= 0x89ULL << 40;
tss_low |= ((tss_limit >> 16) & 0xFULL) << 48;
tss_low |= ((tss_base >> 24) & 0xFFULL) << 56;
uint64_t tss_high = (tss_base >> 32) & 0xFFFFFFFFULL;
gdt_entries[num] = tss_low;
gdt_entries[num + 1] = tss_high;
}
/*
* 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()
{
gdt_set_entry(0, 0, 0); // Null descriptor (0x0)
gdt_set_entry(1, 0xA, 0x9B); // Kernel code (0x8)
gdt_set_entry(2, 0xC, 0x93); // Kernel data (0x10)
gdt_set_entry(3, 0xA, 0xFB); // User code (0x18)
gdt_set_entry(4, 0xC, 0xF3); // User data (0x20)
gdt_set_tss(5); // TSS (0x28)
// The -1 subtraction is some wizardry explained in the OSDev wiki -> GDT
gdtr.limit = NUM_GDT_ENTRIES * sizeof(uint64_t) - 1;
gdtr.address = (uint64_t)gdt_entries;
// Load the GDT we created, flush the old one
gdt_load();
gdt_flush();
// Load task register with new TSS
asm volatile("ltr %%ax" : : "a"(TSS_SEGMENT) : "memory");
DEBUG("GDT initialized");
}
+9
View File
@@ -32,6 +32,8 @@ global vector_19_handler
global vector_20_handler global vector_20_handler
global vector_21_handler global vector_21_handler
global vector_128_handler
interrupt_stub: interrupt_stub:
; We'll push all general-purpose registers to the stack, ; We'll push all general-purpose registers to the stack,
; so they're intact and don't bother the code that was ; so they're intact and don't bother the code that was
@@ -313,3 +315,10 @@ vector_33_handler:
push qword 0 push qword 0
push qword 33 push qword 33
jmp interrupt_stub jmp interrupt_stub
; Syscall Interrupt (0x80)
align 16
vector_128_handler:
push qword 0
push qword 128
jmp interrupt_stub
+34 -16
View File
@@ -4,16 +4,16 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "idt.h" #include <arch/x86.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "io/serial/serial.h" #include <io/serial/serial.h>
#include "io/kbd/ps2.h" #include <io/kbd/ps2.h>
#include <kernel.h> #include <kernel.h>
#include <stdbool.h> #include <stdbool.h>
#include "sched/scheduler.h" #include <sched/scheduler.h>
#include "config.h" #include <config.h>
#include "sched/process.h" #include <sched/process.h>
struct interrupt_descriptor idt[256]; struct interrupt_descriptor idt[256];
struct idtr idt_reg; struct idtr idt_reg;
@@ -21,9 +21,13 @@ struct idtr idt_reg;
// Address to our first interrupt handler // Address to our first interrupt handler
extern char vector_0_handler[]; extern char vector_0_handler[];
extern char vector_128_handler[];
// Timer ticks // Timer ticks
extern volatile uint64_t ticks; extern volatile uint64_t ticks;
extern struct init_status init;
/* /*
* idt_set_entry - Sets an Interrupt Descriptor Table entry * idt_set_entry - Sets an Interrupt Descriptor Table entry
* @vector: Vector number in the IDT * @vector: Vector number in the IDT
@@ -72,6 +76,9 @@ void idt_init()
// 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);
} }
idt_set_entry(0x80, vector_128_handler, 3);
idt_load(&idt); idt_load(&idt);
DEBUG("IDT initialized"); DEBUG("IDT initialized");
} }
@@ -101,7 +108,7 @@ static inline uint64_t read_cr2(void)
* Also displays an interpretation of the thrown error code. * Also displays an interpretation of the thrown error code.
* Then halts the system. We could implement demand paging later. * 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* 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
// interested in is getting more info out of those numbers cause i'm lost each time i have // interested in is getting more info out of those numbers cause i'm lost each time i have
@@ -119,6 +126,19 @@ static void page_fault_handler(struct cpu_status_t* ctx)
CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "", CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "",
cr2); cr2);
if (init.all) {
printf("\x1b[38;5;231mPage Fault at rip=0x%p, err=%u (%s%s%s%s%s%s%s%s) when accessing addr=0x%p\x1b[0m", ctx->iret_rip, ctx->error_code,
CHECK_BIT(ctx->error_code, 0) ? "PAGE_PROTECTION_VIOLATION " : "PAGE_NOT_PRESENT ",
CHECK_BIT(ctx->error_code, 1) ? "ON_WRITE " : "ON_READ ",
CHECK_BIT(ctx->error_code, 2) ? "IN_USER_MODE" : "IN_KERNEL_MODE",
CHECK_BIT(ctx->error_code, 3) ? " WAS_RESERVED" : "",
CHECK_BIT(ctx->error_code, 4) ? " ON_INSTRUCTION_FETCH" : "",
CHECK_BIT(ctx->error_code, 5) ? " PK_VIOLATION" : "",
CHECK_BIT(ctx->error_code, 6) ? " ON_SHADOWSTACK_ACCESS" : "",
CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "",
cr2);
}
panic(ctx, "page fault"); panic(ctx, "page fault");
} }
@@ -129,7 +149,7 @@ static void page_fault_handler(struct cpu_status_t* ctx)
* Shows detail about a General Protection Fault, * Shows detail about a General Protection Fault,
* and what may have caused it. Halts the system. * 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* 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",
ctx->iret_rip, ctx->iret_rip,
@@ -154,12 +174,6 @@ static void gp_fault_handler(struct cpu_status_t* ctx)
panic(ctx, "gp fault"); panic(ctx, "gp fault");
} }
// DEBUG
void kbdproc_main(void* arg)
{
printf("Key pressed/released.\r\n");
}
/* /*
* interrupt_dispatch - Interrupt dispatcher * interrupt_dispatch - Interrupt dispatcher
* @context: CPU context * @context: CPU context
@@ -171,7 +185,7 @@ void kbdproc_main(void* arg)
* Return: * Return:
* <context> - CPU context after interrupt * <context> - CPU context after interrupt
*/ */
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) struct cpu_status* interrupt_dispatch(struct cpu_status* context)
{ {
if (context == NULL) { if (context == NULL) {
panic(NULL, "Interrupt dispatch recieved NULL context!"); panic(NULL, "Interrupt dispatch recieved NULL context!");
@@ -258,10 +272,14 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
case 33: // Keyboard Interrupt case 33: // Keyboard Interrupt
keyboard_handler(); keyboard_handler();
process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG //process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG
outb(0x20, 0x20); outb(0x20, 0x20);
break; break;
case 128: // Syscall Interrupt (0x80)
syscall_handler(context);
break;
default: default:
DEBUG("Unexpected Interrupt"); DEBUG("Unexpected Interrupt");
break; break;
+114
View File
@@ -0,0 +1,114 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief x86 architecture-dependant initialization
* @license GPL-3.0-only
*/
#include <arch/gdt.h>
#include <stdint.h>
#include <arch/x86.h>
#include <kernel.h>
#include <mem/utils.h>
/*
* x86_overwrite_pat - Set PAT to WC
*
* This function overwrites the 1st Page Attribute
* Table entry, to enable the Write-Combining property
* when we map memory regions later on.
* The framebuffer will be mapped with WC, which makes
* memory access significantly faster by using burst
* operations.
*/
static void x86_overwrite_pat()
{
uint64_t pat = rdmsr(0x277);
pat &= ~(0xFFULL << 8); // Clear PAT1
pat |= (0x01ULL << 8); // PAT1 = 0x01 (WC)
wrmsr(0x277, pat);
}
/*
* x86_enable_fpu - Enable Floating Point Unit
*
* This function enables the Floating Point Unit,
* which allows the CPU to do floating point
* operations.
*
* Here we do not check for FPU support but we
* should. However most processors support it.
*/
static void x86_enable_fpu()
{
size_t cr4;
__asm__ volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= 0x200;
__asm__ volatile("mov %0, %%cr4" :: "r"(cr4));
uint16_t cw = 0x37F; // control word
asm volatile("fldcw %0" :: "m"(cw));
}
/*
* x86_arch_init - Initialize x86 CPU structures
*
* This function is responsible for overriding a PAT entry
* (to put the framebuffer area in WC mode) only.
*
* Later, all architecture-dependant init (GDT, IDT, TSS, ...)
* should be initialized here, and separate function pointers
* should be set up for each arch.
*/
void x86_arch_init()
{
x86_overwrite_pat();
x86_enable_fpu();
x86_cpu_identification();
idt_init();
gdt_init();
}
/*
* cpu_supports_brandstring - Does the CPU support brand strings?
*
* Return:
* true - if it does
* false - if it doesn't
*/
bool cpu_supports_brandstring() {
uint32_t eax, ebx, ecx, edx;
cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
if (eax < 0x80000004) {
return false;
} else {
return true;
}
}
/*
* x86_cpu_idenfitication - get info about the CPU
*
* This function displays the CPU vendor name or the
* extended "brand string" if it's supported, on
* debug output.
*/
void x86_cpu_identification()
{
if (cpu_supports_brandstring()) {
uint32_t regs[12];
// Some CPUs don't return null-terminated values so we do it as a failsafe default
char str[sizeof(regs)+1] = {0};
cpuid(0x80000002, &regs[0], &regs[1], &regs[2], &regs[3]);
cpuid(0x80000003, &regs[4], &regs[5], &regs[6], &regs[7]);
cpuid(0x80000004, &regs[8], &regs[9], &regs[10], &regs[11]);
memcpy(str, regs, sizeof(regs));
str[sizeof(regs)] = '\0';
DEBUG("CPU: %s", str);
} else {
char vendor_string[13] = {0};
cpuid_get_vendor_string(vendor_string);
DEBUG("CPU vendor is: %s", vendor_string);
}
}
+66
View File
@@ -0,0 +1,66 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief x86 MSR C wrappers
* @description
* Wrapper functions to access Model Specific Registers
*
* @license GPL-3.0-only
*/
#include <stdint.h>
#include <stdbool.h>
#include <arch/x86.h>
/*
* rdmsr - Read from MSR
* @msr: model specific register number
*
* Read a 64-bit word from a Model Specific Register.
* Wrapper for the "rdmsr" instruction. It originally
* outputs to two 32-bit registers (EDX:EAX), so the
* function does the job of uniting them as a 64-bit
* value for us.
*
* Return:
* <value> - value read from MSR
*/
uint64_t rdmsr(uint32_t msr)
{
uint32_t low;
uint32_t high;
__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
return ((uint64_t)high << 32) | low;
}
/*
* wrmsr - Write to MSR
* @msr: model specific register number
*
* Write a 64-bit value to a Model Specific Register.
*/
void wrmsr(uint32_t msr, uint64_t value)
{
uint32_t low = (uint32_t)(value & 0xFFFFFFFF);
uint32_t high = (uint32_t)(value >> 32);
__asm__ volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high) : "memory");
}
/*
* x86_has_msr - Test for MSR support
*
* Checks if CPU supports Model Specific Registers
* using CPUID.01h:EDX[bit 5].
*
* Return:
* true - MSR are supported
* false - MSR are not supported
*/
bool x86_has_msr()
{
uint32_t eax, ebx, ecx, edx;
cpuid(1, &eax, &ebx, &ecx, &edx);
return (edx & (1 << 5)) != 0;
}
+160
View File
@@ -0,0 +1,160 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief System call handling
* @license GPL-3.0-only
*/
#include "config.h"
#include "sched/scheduler.h"
#include <arch/x86.h>
#include <kernel.h>
#include <stddef.h>
#include <io/term/term.h>
#include <sched/process.h>
#include <io/kbd/ps2.h>
#include <fs/initfs.h>
#include <string/string.h>
extern struct process* current_process;
// Return fd on success, -errno on error
int sys_open(const char* filename, int flags)
{
if (tar_exists(filename) < 0) {
return -ENOENT; // file doesn't exist..
}
// file exists here!
if (current_process->next_free_fd >= FDT_MAX) {
return -EMFILE;
}
int fd = current_process->next_free_fd++;
current_process->fdt[fd].fd = fd;
current_process->fdt[fd].open = true;
current_process->fdt[fd].cursor = 0;
strncpy(current_process->fdt[fd].filename, filename, PROCESS_NAME_MAX - 1);
return fd;
}
// Return 0 on success, -EBADFD if invalid FD
int sys_close(int fd)
{
if (fd < 0 || fd >= FDT_MAX) {
return -EBADFD;
}
if (!current_process->fdt[fd].open) {
return -EBADFD; // FD not opened in the first place
}
current_process->fdt[fd].open = false;
current_process->fdt[fd].filename[0] = '\0';
current_process->fdt[fd].cursor = 0;
return 0;
}
// Should return the number of bytes read
int sys_read(unsigned int fd, char* buf, size_t count)
{
size_t i;
switch (fd) {
case 0: //read from stdin (keyboard)
for (i=0; i<count; i++) {
buf[i] = keyboard_getchar();
}
return i;
case 1: // from stdout
case 2: // from stderr
return -EBADFD;
default: // from an open file?
if (current_process->fdt[fd].open == false) {
return -EBADFD; // File descriptor wasn't open
}
// Here fd refers to a valid opened file..
int sz = tar_read(current_process->fdt[fd].filename, buf, count,
current_process->fdt[fd].cursor);
if (sz == 0) {
return -ENOENT;
} else {
current_process->fdt[fd].cursor += sz;
return sz;
}
}
return -EBADFD;
}
// TODO: Should have a return value: number of bytes written on success, -1 on error (errno set)
int sys_write(unsigned int fd, const char* buf, size_t count)
{
switch (fd) {
case 1: //stdout
case 2: //stderr
for (size_t i=0; i<count; i++) {
internal_putc(buf[i], NULL);
}
return count;
default:
return -EBADFD;
}
}
int sys_exit(int error_code)
{
current_process->status = DEAD;
DEBUG("(pid=%u, name=%s)", current_process->pid, current_process->name);
return error_code;
}
/*
* syscall_handler - System call dispatcher
* @regs: CPU state
*
* This function is called from the interrupt dispatcher,
* when an interrupt 0x80 is emitted from userland.
*
* It switches control to the syscall number provided
* in %rax.
*
* We try to follow the System V convention here:
* - syscall number in %rax
* - args in %rdi, %rsi, %rdx, %r10, %r8, %r9
* - return value (if any) in %rax
*
* Return:
* <regs> - CPU state after system call
*/
struct cpu_status* syscall_handler(struct cpu_status* regs)
{
switch (regs->rax)
{
case 0:
DEBUG("sys_read(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_read(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 1:
DEBUG("sys_write(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_write(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 2:
DEBUG("sys_open(filename=%s, flags=%u)", regs->rdi, regs->rsi);
regs->rax = sys_open((const char*)regs->rdi, regs->rsi);
break;
case 3:
DEBUG("sys_close(fd=%u)", regs->rdi);
regs->rax = sys_close(regs->rdi);
break;
case 60:
DEBUG("sys_exit(error_code=%d)", regs->rdi);
regs->rax = sys_exit(regs->rdi);
break;
default:
DEBUG("Bad syscall! (rax=%p, rdi=%p, rsi=%p, rdx=%p)",
regs->rax, regs->rdi, regs->rsi, regs->rdx);
regs->rax = 0xbad515ca11;
break;
}
DEBUG("returned rax=%p (%u)", regs->rax, regs->rax);
return regs;
}
+28 -2
View File
@@ -9,7 +9,12 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include <kernel.h>
#include <limine.h> #include <limine.h>
#include <stddef.h>
__attribute__((used, section(".limine_requests_start")))
volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests"))) __attribute__((used, section(".limine_requests")))
volatile struct limine_framebuffer_request framebuffer_request = { volatile struct limine_framebuffer_request framebuffer_request = {
@@ -35,8 +40,29 @@ volatile struct limine_kernel_address_request kerneladdr_request = {
.revision = 0 .revision = 0
}; };
__attribute__((used, section(".limine_requests_start"))) __attribute__((used, section(".limine_requests")))
volatile LIMINE_REQUESTS_START_MARKER; volatile struct limine_boot_time_request date_request = {
.id = LIMINE_BOOT_TIME_REQUEST,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_module_request module_request = {
.id = LIMINE_MODULE_REQUEST,
.revision = 0
};
__attribute__((used, section(".limine_requests_end"))) __attribute__((used, section(".limine_requests_end")))
volatile LIMINE_REQUESTS_END_MARKER; volatile LIMINE_REQUESTS_END_MARKER;
void populate_boot_context(struct boot_context* ctx)
{
// Populate boot context
// This stays valid only if the BOOTLOADER_RECLAIMABLE regions are preserved
ctx->fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
ctx->mmap = memmap_request.response ? memmap_request.response : NULL;
ctx->hhdm = hhdm_request.response ? hhdm_request.response : NULL;
ctx->kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
ctx->bootdate = date_request.response ? date_request.response : NULL;
ctx->module = module_request.response ? module_request.response : NULL;
}
-45
View File
@@ -1,45 +0,0 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PepperOS configuration file
* @license GPL-3.0-only
*/
#ifndef CONFIG_H
#define CONFIG_H
/* version */
#define PEPPEROS_VERSION_MAJOR "0"
#define PEPPEROS_VERSION_MINOR "0"
#define PEPPEROS_VERSION_PATCH "58"
#define PEPPEROS_SPLASH "\x1b[38;5;196mPepperOS\x1b[0m version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\n"
/* process */
#define PROCESS_NAME_MAX 64
#define PROCESS_STACK_SIZE 0x10000 // 64kb
#define PROCESS_BASE 0x400000
#define PROCESS_STACK_BASE 0x1000000
/* sched */
// 1 tick = 1 ms => quantum = 10ms
#define SCHEDULER_QUANTUM 10
/* kernel */
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
#define KERNEL_SIZE 0x200000
#define KERNEL_STACK_SIZE 65536
#define KERNEL_IDT_ENTRIES 33
/* paging */
#define PAGING_MAX_PHYS 0x100000000
/* heap */
#define KHEAP_SIZE (32*1024*1024)
/* term */
#define TERM_HISTORY_MAX_LINES 256
/* time */
#define TIMER_FREQUENCY 1000
#endif
+2 -2
View File
@@ -5,8 +5,8 @@
*/ */
#include <kernel.h> #include <kernel.h>
#include "limine.h" #include <limine.h>
#include "string/string.h" #include <string/string.h>
#include <stddef.h> #include <stddef.h>
extern struct boot_context boot_ctx; extern struct boot_context boot_ctx;
+5 -26
View File
@@ -5,9 +5,9 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include "idt/idt.h" #include <arch/x86.h>
#include "io/serial/serial.h" #include <io/serial/serial.h>
#include "kernel.h" #include <kernel.h>
extern struct init_status init; extern struct init_status init;
extern int panic_count; extern int panic_count;
@@ -18,7 +18,7 @@ extern int panic_count;
*/ */
void read_rflags(uint64_t rflags) void read_rflags(uint64_t rflags)
{ {
DEBUG("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m", printf("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m",
CHECK_BIT(rflags, 0) ? "CF " : "", /*carry flag*/ CHECK_BIT(rflags, 0) ? "CF " : "", /*carry flag*/
CHECK_BIT(rflags, 2) ? "PF " : "", /*parity flag*/ CHECK_BIT(rflags, 2) ? "PF " : "", /*parity flag*/
CHECK_BIT(rflags, 4) ? "AF " : "", /*auxiliary carry flag*/ CHECK_BIT(rflags, 4) ? "AF " : "", /*auxiliary carry flag*/
@@ -36,27 +36,6 @@ void read_rflags(uint64_t rflags)
CHECK_BIT(rflags, 19) ? "VIF " : "", /*virtual interrupt flag*/ CHECK_BIT(rflags, 19) ? "VIF " : "", /*virtual interrupt flag*/
CHECK_BIT(rflags, 20) ? "VIP " : "", /*virtual interrupt pending*/ CHECK_BIT(rflags, 20) ? "VIP " : "", /*virtual interrupt pending*/
CHECK_BIT(rflags, 21) ? "ID " : ""); /*id flag*/ CHECK_BIT(rflags, 21) ? "ID " : ""); /*id flag*/
if (init.terminal) {
printf("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m\r\n",
CHECK_BIT(rflags, 0) ? "CF " : "",
CHECK_BIT(rflags, 2) ? "PF " : "",
CHECK_BIT(rflags, 4) ? "AF " : "",
CHECK_BIT(rflags, 6) ? "ZF " : "",
CHECK_BIT(rflags, 7) ? "SF " : "",
CHECK_BIT(rflags, 8) ? "TF " : "",
CHECK_BIT(rflags, 9) ? "IF " : "",
CHECK_BIT(rflags, 10) ? "DF " : "",
CHECK_BIT(rflags, 11) ? "OF " : "",
(CHECK_BIT(rflags, 12) && CHECK_BIT(rflags, 13)) ? "IOPL3 " : "IOPL0 ",
CHECK_BIT(rflags, 14) ? "NT " : "",
CHECK_BIT(rflags, 16) ? "RF " : "",
CHECK_BIT(rflags, 17) ? "VM " : "",
CHECK_BIT(rflags, 18) ? "AC " : "",
CHECK_BIT(rflags, 19) ? "VIF " : "",
CHECK_BIT(rflags, 20) ? "VIP " : "",
CHECK_BIT(rflags, 21) ? "ID " : "");
}
} }
/* /*
@@ -68,7 +47,7 @@ void read_rflags(uint64_t rflags)
* Will display to terminal if it is initialized, otherwise serial only. * Will display to terminal if it is initialized, otherwise serial only.
* Can be called with or without a CPU context. * Can be called with or without a CPU context.
*/ */
void panic(struct cpu_status_t* ctx, const char* str) void panic(struct cpu_status* ctx, const char* str)
{ {
CLEAR_INTERRUPTS; CLEAR_INTERRUPTS;
panic_count += 1; panic_count += 1;
+1 -1
View File
@@ -5,7 +5,7 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include "kernel.h" #include <kernel.h>
#include <stddef.h> #include <stddef.h>
extern struct init_status init; extern struct init_status init;
+147
View File
@@ -0,0 +1,147 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Initial TAR filesystem (read-only)
* @license GPL-3.0-only
*/
#include <sched/process.h>
#include <limine.h>
#include <fs/initfs.h>
#include <kernel.h>
#include <mem/utils.h>
#include <string/string.h>
void* archive_start_addr;
uint64_t archive_size;
/*
* tar_oct2bin - convert octal size string to an integer
* @str: octal size string
* @size: size of string
*
* Return:
* $n - file size as an integer
*/
int tar_oct2bin(unsigned char* str, int size)
{
int n = 0;
unsigned char* c = str;
while (size-- > 0) {
n *= 8;
n += *c - '0';
c++;
}
return n;
}
/*
* tar_lookup - lookup a file in the TAR file
* @archive: pointer to beginning of the archive
* @filename: file to lookup (absolute path)
* @out: where to store file data if found
*
* Return:
* $filesize - size of the file, if found
* $-ENOENT - file not found
*/
int tar_lookup(unsigned char* archive, char* filename, char** out)
{
unsigned char *ptr = archive;
while (!memcmp(ptr + 257, "ustar", 5)) {
int filesize = tar_oct2bin(ptr + 0x7c, 11);
if (!memcmp(ptr, filename, strlen(filename) + 1)) {
*out = (char*)(ptr + 512);
return filesize;
}
ptr += (((filesize + 511) / 512) + 1) * 512;
}
return -ENOENT;
}
/*
* tar_list - list all files present in archive
*/
void tar_list()
{
printf("++ Contents of initial filesystem ++\r\n\r\n");
unsigned char *ptr = archive_start_addr;
while (!memcmp(ptr + 257, "ustar", 5)) {
int filesize = tar_oct2bin(ptr + 0x7c, 11);
char* filename = (char*)ptr;
printf("file: %s\r\n", filename);
ptr += (((filesize + 511) / 512) + 1) * 512;
}
}
/*
* tar_read - read a file in the TAR archive
* @filename: file to read (absolute path)
* @out: where to store file data if found
* @count: amount of bytes to read
* @offset: read from byte offset (0 for none)
*
* Return:
* $filesize - size of the file, if found
* $-ENOENT - file not found
*/
int tar_read(char* filename, char* out, int count, int offset)
{
char* file_data;
int filesize = tar_lookup(archive_start_addr, filename, &file_data);
if (filesize <= 0) {
return filesize;
}
if (offset >= filesize) {
return -EINVAL;
}
int remaining = filesize - offset;
int to_read = remaining < count ? remaining : count;
memcpy(out, file_data + offset, to_read);
return to_read;
}
/*
* tar_exists - check if a file exists in the TAR archive
* @filename: file to check (absolute path)
*
* Return:
* $filesize - size of the file, if found
* $-ENOENT - file not found
*/
int tar_exists(const char* filename)
{
unsigned char* ptr = archive_start_addr;
while (!memcmp(ptr + 257, "ustar", 5)) {
int filesize = tar_oct2bin(ptr + 0x7c, 11);
if (!memcmp(ptr, filename, strlen(filename) + 1)) {
return filesize;
}
ptr += (((filesize + 511) / 512) + 1) * 512;
}
return -ENOENT;
}
/*
* initfs_init - initialize the TAR initial filesystem
* @tar_file: pointer to the Limine-loaded archive
*
* Return:
* $0 - on success
*/
int initfs_init(struct limine_file* tar_file)
{
archive_start_addr = tar_file->address;
archive_size = tar_file->size;
DEBUG("Loaded TAR initial filesystem (initfs.tar)");
return 0;
}
+92 -6
View File
@@ -4,10 +4,11 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "io/serial/serial.h" #include "config.h"
#include "ps2.h" #include <io/serial/serial.h>
#include <io/kbd/ps2.h>
#include <stdint.h> #include <stdint.h>
#include "io/term/term.h" #include <io/term/term.h>
#include <kernel.h> #include <kernel.h>
#include <stddef.h> #include <stddef.h>
@@ -18,6 +19,11 @@ uint8_t key_status = 0b00000000;
unsigned char* keymap; unsigned char* keymap;
unsigned char* keymap_shifted; unsigned char* keymap_shifted;
// Keyboard buffer
char keyboard_buffer[KBD_BUFFER_MAX] = {0};
int write_index = 0;
int read_index = 0;
extern struct init_status init; extern struct init_status init;
unsigned char kbdus[128] = unsigned char kbdus[128] =
@@ -210,10 +216,11 @@ void keyboard_handler()
if (c) { if (c) {
if (c == '\n') { if (c == '\n') {
_putchar('\r'); internal_putc('\r', NULL);
} }
// Should probably have a keyboard buffer here... instead of this
_putchar(c); internal_putc(c, NULL);
keyboard_putchar(c);
} }
} }
} }
@@ -221,6 +228,85 @@ void keyboard_handler()
} }
} }
/*
* keyboard_getchar - Get a character from keyboard
*
* This function reads one character from the keyboard buffer.
* If the keyboard buffer is empty, it will block until a key
* is pressed.
*
* Return:
* <char> - character from keyboard buffer
*/
char keyboard_getchar()
{
while (read_index == write_index); // Empty buffer
char c = keyboard_buffer[read_index];
read_index = (read_index+1) % KBD_BUFFER_MAX;
return c;
}
/*
* keyboard_putchar - Puts a character in the keyboard buffer
* @c: character to add
*
* This function is used in the keyboard handler to add new
* characters to the keyboard buffer.
*
* Return:
* %-1 - keyboard buffer is full
* %0 - operation completed successfully
*/
int keyboard_putchar(char c)
{
if ((write_index+1) % KBD_BUFFER_MAX == read_index) {
// Full buffer
return -1;
}
keyboard_buffer[write_index] = c;
write_index = (write_index+1) % KBD_BUFFER_MAX;
return 0;
}
/*
* keyboard_getline - Gets a line of input from keyboard
* @output: Output string
* @size: Size of output string
*
* Read a line of characters from the keyboard, until the
* buffer fills or a newline character is read.
* The output string is NULL-terminated.
*
* Return:
* <num> - the number of characters read
*/
int keyboard_getline(char* output, size_t size)
{
char c;
size_t index = 0;
// Read until Enter is pressed
while ((c = keyboard_getchar()) != 0x0A) {
if (c == '\b') {
if (index > 0) {
index--;
output[index] = '\0';
printf(" \b");
}
continue;
}
if (index >= size-1) {
continue;
}
output[index++] = c;
}
output[index] = '\0';
return index;
}
/* /*
* keyboard_init - Keyboard initialization * keyboard_init - Keyboard initialization
* @layout: Desired layout * @layout: Desired layout
+13 -3
View File
@@ -5,10 +5,14 @@
*/ */
#include <kernel.h> #include <kernel.h>
#include "serial.h" #include <io/serial/serial.h>
#include <sched/spinlock.h>
extern struct init_status init; extern struct init_status init;
extern int panic_count;
struct spinlock serial_lock = {0};
/* /*
* outb - Writes a byte to a CPU port * outb - Writes a byte to a CPU port
* @port: CPU port to write to * @port: CPU port to write to
@@ -61,8 +65,8 @@ int serial_init()
// Set normal operation mode // Set normal operation mode
outb(PORT + 4, 0x0F); outb(PORT + 4, 0x0F);
DEBUG("*** Welcome to PepperOS! ***");
init.serial = true; init.serial = true;
DEBUG("*** Welcome to PepperOS! (built @ %s %s) ***", __DATE__, __TIME__);
return 0; return 0;
} }
@@ -85,9 +89,15 @@ static int is_transmit_empty()
*/ */
void skputc(char c) void skputc(char c)
{ {
// TODO: Spinlock here (serial access) if (panic_count == 0) {
spinlock_acquire(&serial_lock);
while (!is_transmit_empty()); // wait for free spot while (!is_transmit_empty()); // wait for free spot
outb(PORT, c); outb(PORT, c);
spinlock_release(&serial_lock);
} else {
while (!is_transmit_empty());
outb(PORT, c);
}
} }
/* /*
+1 -1
View File
@@ -41,7 +41,7 @@
#define FLANTERM_IN_FLANTERM #define FLANTERM_IN_FLANTERM
#endif #endif
#include "flanterm.h" #include <io/term/flanterm.h>
// Tries to implement this standard for terminfo // Tries to implement this standard for terminfo
// https://man7.org/linux/man-pages/man4/console_codes.4.html // https://man7.org/linux/man-pages/man4/console_codes.4.html
+2 -2
View File
@@ -51,8 +51,8 @@
#define FLANTERM_IN_FLANTERM #define FLANTERM_IN_FLANTERM
#endif #endif
#include "../flanterm.h" #include <io/term/flanterm.h>
#include "fb.h" #include <io/term/flanterm_backends/fb.h>
void *memset(void *, int, size_t); void *memset(void *, int, size_t);
void *memcpy(void *, const void *, size_t); void *memcpy(void *, const void *, size_t);
+90 -39
View File
@@ -13,36 +13,27 @@ because this shitty implementation will be replaced one day by Flanterm
#include <stddef.h> #include <stddef.h>
#include <kernel.h> #include <kernel.h>
#include "term.h" #include <io/term/term.h>
#include "config.h" #include <config.h>
#include "flanterm.h" #include <io/term/flanterm.h>
#include "flanterm_backends/fb.h" #include <io/term/flanterm_backends/fb.h>
#include "mem/heap/kheap.h" #include <mem/kheap.h>
#include "limine.h" #include <limine.h>
#include <stdarg.h> #include <stdarg.h>
#include "sched/spinlock.h" #include <sched/spinlock.h>
#include "io/serial/serial.h" #include <io/serial/serial.h>
#define NANOPRINTF_IMPLEMENTATION #define NANOPRINTF_IMPLEMENTATION
#include "nanoprintf.h" #include <io/term/nanoprintf.h>
extern struct flanterm_context* ft_ctx; extern struct flanterm_context* ft_ctx;
extern struct init_status init; extern struct init_status init;
struct spinlock_t term_lock = {0}; struct spinlock term_lock = {0};
struct spinlock printf_lock = {0};
extern int panic_count; extern int panic_count;
/*
* _putchar - Writes a character to terminal (DEPRECATED)
* @character: character to write
*/
void _putchar(char character)
{
// TODO: Spinlock here (terminal access)
flanterm_write(ft_ctx, &character, 1);
}
/* /*
* internal_putc - Internal putchar function * internal_putc - Internal putchar function
* @c: char to print * @c: char to print
@@ -74,6 +65,38 @@ void internal_putc(int c, void *_)
} }
} }
/*
* debug_putc - Internal DEBUG putchar function
* @c: char to print
* @_: (unused, for nanoprintf)
*
* Prints a character to the terminal if it's ready and if
* the kernel is still initializing, and also always to the
* serial interface if it's ready.
*/
void debug_putc(int c, void *_)
{
(void)_;
char ch = (char)c;
if (init.terminal && (!init.all || panic_count > 0)) {
if (panic_count == 0) {
spinlock_acquire(&term_lock);
flanterm_write(ft_ctx, &ch, 1);
spinlock_release(&term_lock);
} else {
flanterm_write(ft_ctx, &ch, 1);
}
}
if (init.serial) {
if (ch == '\n') {
skputc('\r');
}
skputc(ch);
}
}
/* /*
* printf - Fromatted printing * printf - Fromatted printing
* @fmt: format string * @fmt: format string
@@ -83,14 +106,58 @@ void internal_putc(int c, void *_)
* *
* Return: * Return:
* <ret> - number of characters sent to the callback * <ret> - number of characters sent to the callback
* %-1 - error
*/ */
int printf(const char* fmt, ...) int printf(const char* fmt, ...)
{ {
if (panic_count == 0) {
spinlock_acquire(&printf_lock);
va_list args;
va_start(args, fmt);
int ret = npf_vpprintf(internal_putc, NULL, fmt, args);
va_end(args);
spinlock_release(&printf_lock);
return ret;
} else {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
int ret = npf_vpprintf(internal_putc, NULL, fmt, args); int ret = npf_vpprintf(internal_putc, NULL, fmt, args);
va_end(args); va_end(args);
return ret; return ret;
}
return -1;
}
/*
* kprintf - Fromatted DEBUG printing
* @fmt: format string
* @...: variadic arguments
*
* Wrapper for nanoprintf; to be used only for
* kernel/debug messages.
*
* Return:
* <ret> - number of characters sent to the callback
* %-1 - error
*/
int kprintf(const char* fmt, ...)
{
if (panic_count == 0) {
spinlock_acquire(&printf_lock);
va_list args;
va_start(args, fmt);
int ret = npf_vpprintf(debug_putc, NULL, fmt, args);
va_end(args);
spinlock_release(&printf_lock);
return ret;
} else {
va_list args;
va_start(args, fmt);
int ret = npf_vpprintf(debug_putc, NULL, fmt, args);
va_end(args);
return ret;
}
return -1;
} }
/* /*
@@ -103,30 +170,14 @@ void kputs(const char* str)
{ {
size_t i=0; size_t i=0;
while (str[i] != 0) { while (str[i] != 0) {
_putchar(str[i]); internal_putc(str[i], NULL);
i++; i++;
} }
_putchar('\r');
} }
extern struct flanterm_context* ft_ctx; extern struct flanterm_context* ft_ctx;
extern struct boot_context boot_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 * term_init - Video output/terminal initialization
* *
@@ -136,8 +187,8 @@ void term_init()
{ {
uint32_t bgColor = 0x252525; uint32_t bgColor = 0x252525;
ft_ctx = flanterm_fb_init( ft_ctx = flanterm_fb_init(
kmalloc, NULL,
flanterm_free_wrapper, NULL,
boot_ctx.fb->address, boot_ctx.fb->width, boot_ctx.fb->height, boot_ctx.fb->pitch, 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->red_mask_size, boot_ctx.fb->red_mask_shift,
boot_ctx.fb->green_mask_size, boot_ctx.fb->green_mask_shift, boot_ctx.fb->green_mask_size, boot_ctx.fb->green_mask_shift,
+112
View File
@@ -0,0 +1,112 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PepperOS kernel shell
* @license GPL-3.0-only
*/
#include "fs/initfs.h"
#include <io/term/term.h>
#include <config.h>
#include <io/kbd/ps2.h>
#include <string/string.h>
#include <stdint.h>
#include <kernel.h>
#include <time/date.h>
#include <mem/kheap.h>
__attribute__((noinline))
void smash_it()
{
char buf[16]; (void)buf;
for (size_t i=0; i<256; i++) {
buf[i] = (char)i;
}
}
/*
* pedicel_main - Kernel shell main function
* @arg: argument (optional)
*
* This is the entry point for the kernel shell process.
* It is used to start programs and to test different things
* on different real hardware easily.
*
* Named after the root part of the pepper.
*/
void pedicel_main(void* arg)
{
printf("Welcome to the kernel shell!\r\nType 'help' for a list of commands.\r\n");
for (;;) {
char input_buf[PEDICEL_INPUT_SIZE] = {0};
printf(PEDICEL_PROMPT);
keyboard_getline(input_buf, PEDICEL_INPUT_SIZE);
if (strncmp(input_buf, "help", 4) == 0) {
printf("++ shell builtins ++\r\n\r\n"
"\tclear - clear the screen\r\n"
"\tpanic - trigger a test panic\r\n"
"\tsyscall - trigger int 0x80\r\n"
"\tpf - trigger a page fault\r\n"
"\tnow - get current date\r\n"
"\tsmash - smash the stack\r\n"
"\tmem - get used heap info\r\n"
"\tload - load an user executable\r\n"
"\tlist - list initfs.tar contents\r\n");
continue;
}
if (strncmp(input_buf, "", 1) == 0) {
continue;
}
if (strncmp(input_buf, "clear", 5) == 0) {
printf("\x1b[2J\x1b[H");
continue;
}
if (strncmp(input_buf, "panic", 5) == 0) {
panic(NULL, "test panic");
}
if (strncmp(input_buf, "syscall", 7) == 0) {
__asm__ volatile("mov $0x00, %rdi");
__asm__ volatile("int $0x80");
continue;
}
if (strncmp(input_buf, "pf", 2) == 0) {
volatile uint64_t* fault = (uint64_t*)0xdeadbeef;
fault[0] = 1;
}
if (strncmp(input_buf, "now", 3) == 0) {
struct date now = date_now();
printf("Now is %02u:%02u:%02u on %u/%u/%u\r\n", now.hour, now.minute,
now.second, now.day, now.month, now.year);
continue;
}
if (strncmp(input_buf, "smash", 5) == 0) {
smash_it();
continue;
}
if (strncmp(input_buf, "mem", 3) == 0) {
kheap_info();
continue;
}
if (strncmp(input_buf, "load", 4) == 0) {
loader_load_raw();
continue;
}
if (strncmp(input_buf, "list", 4) == 0) {
tar_list();
continue;
}
printf("%s: command not found\r\n", input_buf);
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Executable loader
* @license GPL-3.0-only
*/
#include <stddef.h>
#include <fs/initfs.h>
#include <kernel.h>
#include <sched/process.h>
#include <io/kbd/ps2.h>
#include <string/string.h>
extern void* archive_start_addr;
int loader_load_raw()
{
char input_buf[PEDICEL_INPUT_SIZE] = {0};
do {
printf("file> ");
keyboard_getline(input_buf, PEDICEL_INPUT_SIZE);
} while (strncmp(input_buf, "", 1) == 0);
char* data = NULL;
int sz = tar_lookup(archive_start_addr, input_buf,&data);
if (sz > 0) {
process_create_user_raw(data, sz, input_buf);
return 0; // TODO: should return something else on error
}
printf("Couldn't load file '%s'\r\n", input_buf);
return 1;
}
+39 -40
View File
@@ -7,24 +7,25 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <limine.h> #include <limine.h>
#include "io/term/term.h" #include <io/term/term.h>
#include "io/term/term.h" #include <io/serial/serial.h>
#include "io/serial/serial.h" #include <arch/gdt.h>
#include "mem/gdt/gdt.h" #include <mem/utils.h>
#include "mem/misc/utils.h" #include <kernel.h>
#include "idt/idt.h" #include <time/timer.h>
#include "kernel.h" #include <io/kbd/ps2.h>
#include "time/timer.h" #include <mem/pmm.h>
#include "io/kbd/ps2.h" #include <mem/paging.h>
#include "mem/paging/pmm.h" #include <mem/vmm.h>
#include "mem/paging/paging.h" #include <mem/kheap.h>
#include "mem/paging/vmm.h" #include <sched/process.h>
#include "mem/heap/kheap.h" #include <sched/scheduler.h>
#include "sched/process.h" #include <config.h>
#include "sched/scheduler.h" #include <io/term/flanterm.h>
#include "config.h" #include <io/term/flanterm_backends/fb.h>
#include "io/term/flanterm.h" #include <arch/x86.h>
#include "io/term/flanterm_backends/fb.h" #include <boot/boot.h>
#include <fs/initfs.h>
// Limine version used // Limine version used
__attribute__((used, section(".limine_requests"))) __attribute__((used, section(".limine_requests")))
@@ -61,16 +62,14 @@ extern volatile struct limine_framebuffer_request framebuffer_request;
extern volatile struct limine_memmap_request memmap_request; extern volatile struct limine_memmap_request memmap_request;
extern volatile struct limine_hhdm_request hhdm_request; extern volatile struct limine_hhdm_request hhdm_request;
extern volatile struct limine_kernel_address_request kerneladdr_request; extern volatile struct limine_kernel_address_request kerneladdr_request;
extern volatile struct limine_boot_time_request date_request;
extern volatile struct limine_module_request module_request;
extern struct process_t* processes_list; struct limine_file* file;
extern struct process_t* current_process;
struct process_t* idle_proc;
// Never gets executed although pedicel is scheduled? extern struct process* processes_list;
void pedicel_main(void* arg) extern struct process* current_process;
{ struct process* idle_proc;
printf("\n\nWelcome to PepperOS! Pedicel speaking.\r\nNothing left to do, let's go idle!");
}
void idle_main(void* arg) void idle_main(void* arg)
{ {
@@ -94,37 +93,37 @@ void kmain()
CLEAR_INTERRUPTS; CLEAR_INTERRUPTS;
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf(); if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
populate_boot_context(&boot_ctx);
term_init();
serial_init(); serial_init();
timer_init(); timer_init();
// Populate boot context x86_arch_init();
boot_ctx.fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
boot_ctx.mmap = memmap_request.response ? memmap_request.response : NULL;
boot_ctx.hhdm = hhdm_request.response ? hhdm_request.response : NULL;
boot_ctx.kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
boot_mem_display(); boot_mem_display();
pmm_init(boot_ctx); pmm_init(boot_ctx);
// Remap kernel , HHDM and framebuffer
paging_init(boot_ctx); paging_init(boot_ctx);
kheap_init(); kheap_init();
keyboard_init(FR); keyboard_init(FR);
term_init();
gdt_init();
idt_init();
process_init(); process_init();
idle_proc = process_create("idle", (void*)idle_main, 0); idle_proc = process_create("idle", (void*)idle_main, 0);
struct process_t* pedicel = process_create("pedicel", (void*)pedicel_main, 0);
process_display_list(processes_list); if (!boot_ctx.module) {
panic(NULL, "could not load initfs.tar :(");
}
if (boot_ctx.module->module_count == 1) {
initfs_init(boot_ctx.module->modules[0]);
}
process_create("kshell", (void*)pedicel_main, 0);
scheduler_init(); scheduler_init();
kputs(PEPPEROS_SPLASH); printf(PEPPEROS_SPLASH);
init.all = true;
idle(); idle();
} }
-107
View File
@@ -1,107 +0,0 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Global Descriptor Table (for legacy reasons)
* @license GPL-3.0-only
*/
#include "gdt.h"
#include <stdint.h>
#include "io/serial/serial.h"
#include <kernel.h>
// Descriptors are 8-byte wide (64bits)
// So the selectors will be (in bytes): 0x0, 0x8, 0x10, 0x18, etc..
uint64_t gdt_entries[NUM_GDT_ENTRIES];
struct GDTR gdtr;
/*
* gdt_load - Loads Global Descriptor Table
*/
static void gdt_load()
{
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()
{
// Here, 0x8 is the kernel code selector
// and 0x10 is the kernel data selector
asm volatile (
"mov $0x10, %%ax \n" // Reload segments with kernel data selector
"mov %%ax, %%ds \n"
"mov %%ax, %%es \n"
"mov %%ax, %%fs \n"
"mov %%ax, %%gs \n"
"mov %%ax, %%ss \n"
"pushq $0x8 \n" // CS reload
"lea 1f(%%rip), %%rax \n"
"push %%rax \n"
"lretq \n"
"1: \n" // Execution continues here after CS reload
:
:
: "rax", "memory"
);
}
/*
* 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()
{
// Null descriptor (required)
gdt_entries[0] = 0;
// Kernel code segment
uint64_t kernel_code = 0;
kernel_code |= 0b1101 << 8; // Selector type: accessed, read-enable, no conforming
kernel_code |= 1 << 12; // not a system descriptor
kernel_code |= 0 << 13; // DPL field = 0
kernel_code |= 1 << 15; // Present
kernel_code |= 1 << 21; // Long mode
// Left shift 32 bits so we place our stuff in the upper 32 bits of the descriptor.
// The lower 32 bits contain limit and part of base and therefore are ignored in Long Mode
// (because we'll use paging; segmentation is used only for legacy)
gdt_entries[1] = kernel_code << 32;
uint64_t kernel_data = 0;
kernel_data |= 0b0011 << 8;
kernel_data |= 1 << 12;
kernel_data |= 0 << 13;
kernel_data |= 1 << 15;
kernel_data |= 1 << 21;
gdt_entries[2] = kernel_data << 32;
// We re-use the kernel descriptors here, and just update their DPL fields
// (Descriptor privilege level) from ring 0 -> to ring 3 (userspace)
uint64_t user_code = kernel_code | (3 << 13);
gdt_entries[3] = user_code;
uint64_t user_data = kernel_data | (3 << 13);
gdt_entries[4] = user_data;
// The -1 subtraction is some wizardry explained in the OSDev wiki -> GDT
gdtr.limit = NUM_GDT_ENTRIES * sizeof(uint64_t) - 1;
gdtr.address = (uint64_t)gdt_entries;
// Load the GDT we created, flush the old one
gdt_load();
gdt_flush();
DEBUG("GDT initialized");
}
+44 -15
View File
@@ -4,20 +4,20 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "kheap.h" #include <mem/kheap.h>
#include "mem/paging/paging.h" #include <mem/paging.h>
#include "mem/paging/pmm.h" #include <mem/pmm.h>
#include <stddef.h> #include <stddef.h>
#include <kernel.h> #include <kernel.h>
#include "sched/process.h" #include <sched/process.h>
#include "config.h" #include <config.h>
extern uint64_t kernel_phys_base; extern uint64_t kernel_phys_base;
extern uint64_t kernel_virt_base; extern uint64_t kernel_virt_base;
uintptr_t kheap_start; uintptr_t kheap_start;
static struct heap_block_t* head = NULL; static struct heap_block* head = NULL;
static uintptr_t end; static uintptr_t end;
// Kernel root table (level 4) // Kernel root table (level 4)
@@ -55,8 +55,8 @@ void kheap_init()
end = current_addr; 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*)kheap_start;
head->size = (end-kheap_start) - sizeof(struct heap_block_t); head->size = (end-kheap_start) - sizeof(struct heap_block);
head->free = true; head->free = true;
head->next = NULL; head->next = NULL;
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size); DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
@@ -80,16 +80,16 @@ void* kmalloc(size_t size)
if (!size) return NULL; if (!size) return NULL;
size = ALIGN(size); size = ALIGN(size);
struct heap_block_t* curr = head; struct heap_block* 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 + sizeof(struct heap_block_t) + 16) { if (curr->size >= size + sizeof(struct heap_block) + 16) {
struct heap_block_t* split = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size); struct heap_block* split = (struct heap_block*)((uintptr_t)curr + sizeof(struct heap_block) + size);
split->size = curr->size - size - sizeof(struct heap_block_t); split->size = curr->size - size - sizeof(struct heap_block);
split->free = true; split->free = true;
split->next = curr->next; split->next = curr->next;
@@ -99,7 +99,7 @@ void* kmalloc(size_t size)
// Found a good block, we return it // Found a good block, we return it
curr->free = false; curr->free = false;
return (void*)((uintptr_t)curr + sizeof(struct heap_block_t)); return (void*)((uintptr_t)curr + sizeof(struct heap_block));
} }
// Continue browsing the list if nothing good was found yet // Continue browsing the list if nothing good was found yet
curr = curr->next; curr = curr->next;
@@ -127,11 +127,11 @@ void kfree(void* ptr)
if (!ptr) return; if (!ptr) return;
// Set it free! // Set it free!
struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t)); struct heap_block* block = (struct heap_block*)((uintptr_t)ptr - sizeof(struct heap_block));
block->free = true; block->free = true;
// merge adjacent free blocks (coalescing) // merge adjacent free blocks (coalescing)
struct heap_block_t* curr = head; struct heap_block* 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;
@@ -158,3 +158,32 @@ 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
return ptr ? ptr+PROCESS_STACK_SIZE : NULL; return ptr ? ptr+PROCESS_STACK_SIZE : NULL;
} }
/*
* kheap_info - Display heap info
*
* This function writes the size of the heap (total),
* the number of allocated bytes, and the number of
* free bytes to the standard output.
*/
void kheap_info()
{
uint64_t free_bytes = 0;
struct heap_block* curr = (struct heap_block*)kheap_start;
while (curr) {
if (curr->free == true) {
free_bytes += curr->size;
}
curr = curr->next;
}
uint64_t total = end-kheap_start;
printf("total=% 8u bytes (%u kB)\r\n"
"alloc=% 8u bytes (%u kB)\r\n"
" free=% 8u bytes (%u kB)\r\n",
total, (total)/1000,
total-free_bytes, (total-free_bytes)/1000,
free_bytes, free_bytes/1000);
}
+28 -12
View File
@@ -4,12 +4,12 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "paging.h" #include <mem/paging.h>
#include "pmm.h" #include <mem/pmm.h>
#include <kernel.h> #include <kernel.h>
#include <stddef.h> #include <stddef.h>
#include <limine.h> #include <limine.h>
#include "config.h" #include <config.h>
/* /*
Paging on x86 uses four different page table levels: Paging on x86 uses four different page table levels:
@@ -43,7 +43,7 @@ void load_cr3(uint64_t value) {
* This function is used to flush at least the TLB entrie(s) * This function is used to flush at least the TLB entrie(s)
* for the page that contains the <addr> address. * for the page that contains the <addr> address.
*/ */
static inline void invlpg(void *addr) void invlpg(void *addr)
{ {
asm volatile("invlpg (%0)" :: "r"(addr) : "memory"); asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
} }
@@ -59,7 +59,7 @@ static inline void invlpg(void *addr)
* Return: * Return:
* <virt> - Pointer to allocated page table * <virt> - Pointer to allocated page table
*/ */
static uint64_t* alloc_page_table() 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());
@@ -99,30 +99,46 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
uint64_t *pdpt, *pd, *pt; uint64_t *pdpt, *pd, *pt;
// Any parent entry on a userspace mapping must also carry PTE_USER,
// otherwise CPL3 accesses fault even if the final PTE is user.
uint64_t parent_flags = PTE_PRESENT | PTE_WRITABLE;
if (flags & PTE_USER) {
parent_flags |= PTE_USER;
}
// 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) | parent_flags;
} 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);
if (flags & PTE_USER) {
root_table[pml4_i] |= PTE_USER;
}
} }
// 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) | parent_flags;
} 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);
if (flags & PTE_USER) {
pdpt[pdpt_i] |= PTE_USER;
}
} }
// 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) | parent_flags;
} 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);
if (flags & PTE_USER) {
pd[pd_i] |= PTE_USER;
}
} }
// PT: finally, populate the page table entry // PT: finally, populate the page table entry
@@ -173,9 +189,9 @@ void paging_init(struct boot_context boot_ctx)
} }
} }
// 4GB // 8GB
if (max_phys > PAGING_MAX_PHYS) { 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 PAGING_MAX_PHYS (from max_phys=%p)", max_phys);
max_phys = PAGING_MAX_PHYS; max_phys = PAGING_MAX_PHYS;
} }
@@ -202,9 +218,9 @@ void paging_init(struct boot_context boot_ctx)
uint64_t fb_size = fb->pitch * fb->height; uint64_t fb_size = fb->pitch * fb->height;
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 (PWT set, and no PCD means PAT1 [Write-Combining] for this region)
for (uint64_t i=0; i<fb_pages; i++) { for (uint64_t i=0; i<fb_pages; i++) {
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_PWT);
page_count++; page_count++;
} }
DEBUG("Mapped %u pages for framebuffer", page_count); DEBUG("Mapped %u pages for framebuffer", page_count);
-81
View File
@@ -1,81 +0,0 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Virtual memory manager
* @license GPL-3.0-only
*/
/*
The VMM (virtual memory manager) will have two roles:
- mapping pages
- unmapping pages
in a specified virtual space
compared to the PMM which allocs/frees 4kb frames ("physical pages").
*/
#include "vmm.h"
#include "paging.h"
#include <stddef.h>
#include "pmm.h"
#include <kernel.h>
void* vmm_pt_root = 0;
// Linked list head for virtual memory objects
struct vm_object* vm_objs = NULL;
/*
* 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 value = 0;
if (flags & VM_FLAG_WRITE)
{
value |= PTE_WRITABLE;
}
if (flags & VM_FLAG_USER)
{
value |= PTE_USER;
}
if ((flags & VM_FLAG_EXEC) == 0)
{
value |= PTE_NOEXEC;
}
return value;
}
extern uint64_t *kernel_pml4;
void vmm_setup_pt_root()
{
// We alloc a physical page (frame) for the pointer, then map it
// to virt (pointer)
uintptr_t phys = pmm_alloc();
vmm_pt_root = (void*)kernel_pml4;
paging_map_page(kernel_pml4, (uint64_t)vmm_pt_root, phys, convert_x86_vm_flags(VM_FLAG_WRITE | VM_FLAG_EXEC));
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
}
/* void* vmm_alloc(size_t length, size_t flags)
{
// We will try to allocate at least length bytes, which have to be rounded UP to
// the next page so its coherent with the PMM
size_t len = ALIGN_UP(length, PAGE_SIZE);
// Need to implement this (as linked list)
// but for now kernel heap is sufficient
// The VMM will prob be more useful when we have userspace
} */
void vmm_init()
{
// NO U
//vmm_setup_pt_root();
}
-34
View File
@@ -1,34 +0,0 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Virtual memory manager
* @license GPL-3.0-only
*/
#ifndef VMM_H
#define VMM_H
#include <stdint.h>
#include <stddef.h>
/*
This will be our linked list of virtual memory objects.
Flags here aren't x86 flags, they are platform-agnostic
kernel-defined flags.
*/
struct vm_object {
uintptr_t base;
size_t length;
size_t flags;
struct vm_object* next;
};
// Flags bitfield
#define VM_FLAG_NONE 0
#define VM_FLAG_WRITE (1 << 0)
#define VM_FLAG_EXEC (1 << 1)
#define VM_FLAG_USER (1 << 2)
void vmm_init(void);
#endif
+27 -49
View File
@@ -11,55 +11,20 @@ it will probably need to get some info from Limine,
to see which pages are used by kernel/bootloader/mmio/fb etc. to see which pages are used by kernel/bootloader/mmio/fb etc.
*/ */
#include "paging.h" #include "config.h"
#include <mem/paging.h>
#include <limine.h> #include <limine.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <kernel.h> #include <kernel.h>
#include "mem/misc/utils.h" #include <mem/utils.h>
#include "pmm.h" #include <mem/pmm.h>
/* /*
First we'll have to discover the physical memory layout, 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.
*/ */
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)
{
// Max length of a usable memory region
uint64_t length_max = 0;
uint64_t offset = hhdm->offset;
DEBUG("Usable Memory:");
for (size_t i=0; i<memmap->entry_count; i++) {
struct limine_memmap_entry* entry = memmap->entries[i];
if (entry->type == LIMINE_MEMMAP_USABLE) {
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length,
entry->base+offset, entry->base+entry->length+offset);
if (entry->length > length_max)
{
length_max = entry->length;
biggest_entry = entry;
}
}
}
DEBUG("Biggest usable memory region:");
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", biggest_entry->base, biggest_entry->base + biggest_entry->length,
biggest_entry->base+offset, biggest_entry->base+biggest_entry->length+offset);
}
// Offset from Higher Half Direct Map // Offset from Higher Half Direct Map
uint64_t hhdm_off; uint64_t hhdm_off;
@@ -99,19 +64,32 @@ void pmm_free(uintptr_t addr)
* This function marks the biggest memory region as * This function marks the biggest memory region as
* free, so we can use it in pmm_alloc. * free, so we can use it in pmm_alloc.
*/ */
static void pmm_init_freelist() static void pmm_init_freelist(struct limine_memmap_response* memmap)
{ {
// We simply call pmm_free() on each page that is marked USABLE uint64_t total_pages = 0;
// in our big memory region.
uint64_t base = ALIGN_UP(biggest_entry->base, PAGE_SIZE); for (size_t i=0; i<memmap->entry_count; i++) {
uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE); struct limine_memmap_entry* entry = memmap->entries[i];
if (entry->type == LIMINE_MEMMAP_USABLE) {
uint64_t base = ALIGN_UP(entry->base, PAGE_SIZE);
uint64_t end = ALIGN_DOWN(entry->base + entry->length, PAGE_SIZE);
if (end > PAGING_MAX_PHYS) {
end = PAGING_MAX_PHYS;
}
// Region above PAGING_MAX_PHYS
if (base >= end) continue;
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);
page_count++; total_pages++;
} }
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE); }
}
DEBUG("%u frames in freelist, %u bytes available (%u MB)", total_pages, total_pages*PAGE_SIZE, total_pages*PAGE_SIZE/1000000);
} }
/* /*
@@ -124,9 +102,9 @@ static void pmm_init_freelist()
void pmm_init(struct boot_context boot_ctx) void pmm_init(struct boot_context boot_ctx)
{ {
hhdm_off = boot_ctx.hhdm->offset; hhdm_off = boot_ctx.hhdm->offset;
pmm_find_biggest_usable_region(boot_ctx.mmap, boot_ctx.hhdm); //pmm_find_biggest_usable_region(boot_ctx.mmap, boot_ctx.hhdm);
// 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
pmm_init_freelist(); pmm_init_freelist(boot_ctx.mmap);
} }
+2 -2
View File
@@ -7,8 +7,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <limine.h> #include <limine.h>
#include "kernel.h" #include <kernel.h>
#include "string/string.h" #include <string/string.h>
// We won't be linked to standard library, but still need the basic mem* functions // We won't be linked to standard library, but still need the basic mem* functions
// so everything goes allright with the compiler // so everything goes allright with the compiler
+270
View File
@@ -0,0 +1,270 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Virtual memory manager
* @license GPL-3.0-only
*/
/*
The VMM (virtual memory manager) will have two roles:
- mapping pages
- unmapping pages
in a specified virtual space
compared to the PMM which allocs/frees 4kb frames ("physical pages").
*/
#include "config.h"
#include <mem/vmm.h>
#include <mem/paging.h>
#include <stddef.h>
#include <mem/pmm.h>
#include <kernel.h>
extern uint64_t *kernel_pml4;
/*
* vmm_switch_to - Switch to a different VMM context
* @ctx: VMM context to switch to
*
* This function makes the CPU switch to another
* virtual memory context, by using the PML4 address
* specified in the VMM context pointed to by @ctx.
*/
void vmm_switch_to(struct vmm_context* ctx)
{
if (!ctx || !ctx->pml4) {
panic(NULL, "Attempted to switch to bad PML4!");
}
uint64_t pml4 = VIRT_TO_PHYS(ctx->pml4);
asm volatile ("mov %0, %%cr3" :: "r"(pml4) : "memory");
}
/*
* vmm_virt_to_phys - Translate from virtual to physical address
* @pml4: virtual address of the Page Map Level 4 (root page table)
* @virt: virtual address to translate
*
* This function goes through page table structures, beginning at
* the root page table which lives at @pml4, and translates @virt
* to a physical address, if it's found in the tables.
*
* Return:
* <phys> - physical address
* %-1 - address is not present in page tables pointed to by @pml4
*/
uint64_t vmm_virt_to_phys(uint64_t* pml4, uint64_t virt)
{
uint64_t pml4_i = PML4_INDEX(virt);
uint64_t pdpt_i = PDPT_INDEX(virt);
uint64_t pd_i = PD_INDEX(virt);
uint64_t pt_i = PT_INDEX(virt);
if (!(pml4[pml4_i] & PTE_PRESENT)) return -1;
uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK);
if (!(pdpt[pdpt_i] & PTE_PRESENT)) return -1;
uint64_t* pd = (uint64_t*)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
if (!(pd[pd_i] & PTE_PRESENT)) return -1;
uint64_t* pt = (uint64_t*)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
if (!(pt[pt_i] & PTE_PRESENT)) return -1;
uint64_t phys = (pt[pt_i] & PTE_ADDR_MASK) + (virt & 0xFFF);
return phys;
}
/*
* vmm_create_address_space - Create a new address space
*
* This function allocates a PML4, and then copies the kernel
* page tables into it.
*
* Return:
* <pml4> - address of the new PML4
* NULL - on error (couldn't allocate a page table)
*/
uint64_t* vmm_create_address_space()
{
uint64_t* pml4 = alloc_page_table();
if (!pml4) return NULL;
for (size_t i=256; i<512; i++) {
pml4[i] = kernel_pml4[i];
}
return pml4;
}
/*
* vmm_map - Map & allocate a page
* @pml4: Page Map Level 4 (root table)
* @virt: Virtual address to map
* @flags: Flags to apply on page
*
* This function allocates a page frame with the PMM,
* and maps this page to the provided @virt address,
* with the corresponding @flags.
*
* Return:
* <virt> - virtual address
*/
void* vmm_map(uint64_t* pml4, uint64_t virt, uint64_t flags)
{
uint64_t phys = pmm_alloc();
if (!phys) {
panic(NULL, "VMM/PMM out of memory!");
}
paging_map_page(pml4, virt, phys, flags | PTE_PRESENT);
return (void*)virt;
}
/*
* vmm_unmap - Unmap & free a page
* @pml4: Page Map Level 4 (root table)
* @virt: Virtual address to unmap
*
* This function frees a page frame with the PMM,
* and unmaps the virtual page at @virt.
*/
void vmm_unmap(uint64_t* pml4, uint64_t virt)
{
uint64_t pml4_i = PML4_INDEX(virt);
uint64_t pdpt_i = PDPT_INDEX(virt);
uint64_t pd_i = PD_INDEX(virt);
uint64_t pt_i = PT_INDEX(virt);
if (!(pml4[pml4_i] & PTE_PRESENT)) return;
uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK);
if (!(pdpt[pdpt_i] & PTE_PRESENT)) return;
uint64_t* pd = (uint64_t*)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
if (!(pd[pd_i] & PTE_PRESENT)) return;
uint64_t* pt = (uint64_t*)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
if (!(pt[pt_i] & PTE_PRESENT)) return;
uint64_t phys = pt[pt_i] & PTE_ADDR_MASK;
pmm_free(phys);
pt[pt_i] = 0;
invlpg((void*)virt);
}
/*
* vmm_is_mapped - Check if an address is mapped
* @pml4: Page Map Level 4 (root table)
* @virt: Virtual address to check
*
* This function checks if the @virt address is
* mapped in the tables pointed to by @pml4.
*
* Return:
* true - @virt is mapped in tables of @pml4
* false - @virt is not mapped there
*/
bool vmm_is_mapped(uint64_t* pml4, uint64_t virt)
{
uint64_t pml4_i = PML4_INDEX(virt);
uint64_t pdpt_i = PDPT_INDEX(virt);
uint64_t pd_i = PD_INDEX(virt);
uint64_t pt_i = PT_INDEX(virt);
if (!(pml4[pml4_i] & PTE_PRESENT)) return false;
uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK);
if (!(pdpt[pdpt_i] & PTE_PRESENT)) return false;
uint64_t* pd = (uint64_t*)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
if (!(pd[pd_i] & PTE_PRESENT)) return false;
uint64_t* pt = (uint64_t*)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
return (pt[pt_i] & PTE_PRESENT);
}
/*
* vmm_alloc_range - Map and allocate a memory range
* @pml4: Page Map Level 4 (root table)
* @pages: Amount of pages to allocate/map
* @flags: Flags to put on mapped pages
*
* This function looks for enough space in page tables
* to map @pages pages, then maps them into the provided
* @pml4 with the provided @flags and allocates them.
*
* Return:
* <start_virt> - the starting virtual address for the mapped range
*/
void* vmm_alloc_region(uint64_t* pml4, size_t pages, uint64_t flags)
{
uint64_t found_pages = 0;
uint64_t start_virt = VMM_USER_SPACE_START;
for (uint64_t curr = VMM_USER_SPACE_START; curr < VMM_USER_SPACE_END; curr += PAGE_SIZE) {
if (!vmm_is_mapped(pml4, curr)) {
if (found_pages == 0) start_virt = curr;
found_pages++;
} else {
found_pages = 0;
}
if (found_pages == pages) {
for (size_t i = 0; i < pages; i++) {
uint64_t addr_to_map = start_virt + (i * PAGE_SIZE);
if (!vmm_map(pml4, addr_to_map, flags)) {
panic(NULL, "VMM out of memory!");
}
}
return (void*)start_virt;
}
}
panic(NULL, "VMM out of memory!");
return NULL;
}
/*
* vmm_map_user_stack - Map a user stack
* @pml4: the user process's PML4
*
* This function maps and allocates a userspace
* stack in the user @pml4 provided, according
* to constants USER_STACK_TOP and USER_STACK_PAGES.
*
* Return:
* <addr> - User stack top address
*/
uintptr_t vmm_alloc_user_stack(uint64_t* pml4)
{
uintptr_t stack_top = USER_STACK_TOP;
size_t stack_size = USER_STACK_PAGES*PAGE_SIZE;
for (size_t i=stack_top; i>stack_top-stack_size; i-=PAGE_SIZE) {
vmm_map(pml4, i, PTE_PRESENT | PTE_WRITABLE | PTE_USER);
}
return stack_top;
}
uintptr_t vmm_alloc_user_code(uint64_t* pml4, void* code_addr, uint64_t code_size)
{
uintptr_t code_start = USER_CODE_START;
// Round code_size up to next page boundary
uint64_t code_size_aligned = (code_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
for (uint64_t i=code_start; i<code_start+code_size_aligned; i+=PAGE_SIZE) {
vmm_map(pml4, i, PTE_PRESENT | PTE_WRITABLE | PTE_USER);
}
return code_start;
}
void vmm_init()
{
// NO U
//vmm_setup_pt_root();
}
+119 -20
View File
@@ -4,20 +4,23 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "mem/paging.h"
#include "mem/vmm.h"
#include <stddef.h> #include <stddef.h>
#include "process.h" #include <sched/process.h>
#include "mem/heap/kheap.h" #include <mem/kheap.h>
#include "kernel.h" #include <kernel.h>
#include "string/string.h" #include <string/string.h>
#include "mem/gdt/gdt.h" #include <arch/gdt.h>
#include "config.h" #include <config.h>
#include "io/serial/serial.h" #include <io/serial/serial.h>
#include <io/term/flanterm.h>
#include <mem/utils.h>
#include "io/term/flanterm.h"
extern struct flanterm_context* ft_ctx; extern struct flanterm_context* ft_ctx;
struct process_t* processes_list; struct process* processes_list;
struct process_t* current_process; struct process* current_process;
extern uint64_t *kernel_pml4; extern uint64_t *kernel_pml4;
@@ -39,10 +42,10 @@ void process_init()
* This function prints the linked list of processes * This function prints the linked list of processes
* to the DEBUG output. * to the DEBUG output.
*/ */
void process_display_list(struct process_t* processes_list) void process_display_list(struct process* processes_list)
{ {
int process_view_id = 0; int process_view_id = 0;
struct process_t* tmp = processes_list; struct process* 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;
@@ -64,11 +67,11 @@ void process_display_list(struct process_t* processes_list)
* Return: * Return:
* <proc> - pointer to created process * <proc> - pointer to created process
*/ */
struct process_t* process_create(char* name, void(*function)(void*), void* arg) struct process* 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* proc = (struct process*)kmalloc(sizeof(struct process));
struct cpu_status_t* ctx = (struct cpu_status_t*)kmalloc(sizeof(struct cpu_status_t)); struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status));
// No more memory? // No more memory?
if (!proc) return NULL; if (!proc) return NULL;
@@ -95,6 +98,8 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg)
// Kernel PML4 as it already maps code/stack (when switching to userland we'll have to change that) // Kernel PML4 as it already maps code/stack (when switching to userland we'll have to change that)
proc->root_page_table = kernel_pml4; proc->root_page_table = kernel_pml4;
proc->kernel_stack = kalloc_stack();
proc->next = 0; proc->next = 0;
process_add(&processes_list, proc); process_add(&processes_list, proc);
@@ -108,7 +113,7 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg)
* @processes_list: pointer to the head 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 * @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** processes_list, struct process* process)
{ {
if (!process) return; if (!process) return;
process->next = NULL; process->next = NULL;
@@ -119,7 +124,7 @@ void process_add(struct process_t** processes_list, struct process_t* process)
return; return;
} }
struct process_t* tmp = *processes_list; struct process* tmp = *processes_list;
while (tmp->next != NULL) { while (tmp->next != NULL) {
tmp = tmp->next; tmp = tmp->next;
} }
@@ -132,7 +137,7 @@ void process_add(struct process_t** processes_list, struct process_t* process)
* @processes_list: pointer to head of linked list * @processes_list: pointer to head of linked list
* @process: the process to delete from the 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** processes_list, struct process* process)
{ {
if (!processes_list || !*processes_list || !process) return; if (!processes_list || !*processes_list || !process) return;
@@ -144,7 +149,7 @@ void process_delete(struct process_t** processes_list, struct process_t* process
return; return;
} }
struct process_t* tmp = *processes_list; struct process* tmp = *processes_list;
while (tmp->next && tmp->next != process) { while (tmp->next && tmp->next != process) {
tmp = tmp->next; tmp = tmp->next;
} }
@@ -167,7 +172,7 @@ void process_delete(struct process_t** processes_list, struct process_t* process
* Return: * Return:
* <process->next> - process right after the one specified * <process->next> - process right after the one specified
*/ */
struct process_t* process_get_next(struct process_t* process) struct process* process_get_next(struct process* process)
{ {
if (!process) return NULL; if (!process) return NULL;
return process->next; return process->next;
@@ -197,3 +202,97 @@ void process_exit()
asm("hlt"); asm("hlt");
} }
} }
/*
* process_jump_to_user - Jump to userland
* @stack_top: Address of the top of the user stack
* @user_code: Address of the first instruction of user code
*/
void process_jump_to_user(uintptr_t stack_top, uintptr_t user_code)
{
// 0x20 | 3 = 0x23 (user data segment | 3)
// 0x18 | 3 = 0x1B (user code segment | 3)
asm volatile(" \
push $0x23 \n\
push %0 \n\
push $0x202 \n\
push $0x1B \n\
push %1 \n\
iretq \n\
" :: "r"(stack_top), "r"(user_code));
}
extern struct tss tss;
/*
* process_create_user_raw - Create a new user process from raw binary
* @file: pointer to beginning of binary
* @size: size of the binary
* @name: name for the new process
*
* This function takes an executable loaded in memory
* and maps its code, a user stack, sets the TSS RSP0
* for interrupts, and finally jumps to the user code.
*/
void process_create_user_raw(char* file, int size, char* name)
{
CLEAR_INTERRUPTS;
struct process* proc = (struct process*)kmalloc(sizeof(struct process));
struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status));
if (!proc || !ctx) panic(NULL, "out of memory while creating user process");
memset(proc, 0, sizeof(struct process));
memset(ctx, 0, sizeof(struct cpu_status));
strncpy(proc->name, name, PROCESS_NAME_MAX);
proc->pid = next_free_pid++;
proc->status = READY;
proc->next = 0;
proc->context = ctx;
proc->context->iret_ss = USER_DATA_SEGMENT | 3;
proc->context->iret_cs = USER_CODE_SEGMENT | 3;
proc->context->iret_flags = 0x202; // Interrupt Flag set
/* Set basic entries for the process's File Descriptor Table */
proc->fdt[0].fd = 0;
proc->fdt[0].open = true;
proc->fdt[0].cursor = 0;
strncpy(proc->fdt[0].filename, "stdin", PROCESS_NAME_MAX - 1);
proc->fdt[1].fd = 1;
proc->fdt[1].open = true;
proc->fdt[1].cursor = 0;
strncpy(proc->fdt[1].filename, "stdout", PROCESS_NAME_MAX - 1);
proc->fdt[2].fd = 2;
proc->fdt[2].open = true;
proc->fdt[2].cursor = 0;
strncpy(proc->fdt[2].filename, "stderr", PROCESS_NAME_MAX - 1);
proc->next_free_fd = 3; // file descriptors are also bump-allocated
void* exec_addr = (void*)file;
uint64_t exec_size = size;
uint64_t* user_pml4 = vmm_create_address_space();
if (!user_pml4) panic(NULL, "failed to create user address space");
proc->root_page_table = user_pml4;
uintptr_t stack_top = vmm_alloc_user_stack(user_pml4);
uint64_t code = vmm_alloc_user_code(user_pml4, exec_addr, exec_size);
proc->context->iret_rsp = stack_top;
proc->context->iret_rip = code;
proc->kernel_stack = kalloc_stack();
if (!proc->kernel_stack) panic(NULL, "failed to allocate kernel stack");
// Copy code into user pages; for that we need to temporarily switch to the user pml4
load_cr3(VIRT_TO_PHYS((uint64_t)user_pml4));
memcpy((uint64_t*)code, exec_addr, exec_size);
load_cr3(VIRT_TO_PHYS((uint64_t)kernel_pml4));
process_add(&processes_list, proc);
DEBUG("user process '%s' (pid=%u) enqueued for scheduling", name, proc->pid);
SET_INTERRUPTS;
}
-39
View File
@@ -1,39 +0,0 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Process definition
* @license GPL-3.0-only
*/
#ifndef PROCESS_H
#define PROCESS_H
#include <stddef.h>
#include "config.h"
#include <stdint.h>
typedef enum {
READY,
RUNNING,
DEAD
} status_t;
struct process_t {
size_t pid;
char name[PROCESS_NAME_MAX];
status_t status;
struct cpu_status_t* context;
void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511]
struct process_t* next;
};
void process_init(void);
struct process_t* process_create(char* name, void(*function)(void*), void* arg);
void process_add(struct process_t** processes_list, struct process_t* process);
void process_delete(struct process_t** processes_list, struct process_t* process);
struct process_t* process_get_next(struct process_t* process);
void process_exit(void);
void process_display_list(struct process_t* processes_list);
#endif
+45 -25
View File
@@ -4,15 +4,18 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "kernel.h" #include <kernel.h>
#include "process.h" #include <sched/process.h>
#include "mem/paging/paging.h" #include <mem/paging.h>
#include <stdint.h> #include <stdint.h>
#include "io/serial/serial.h" #include <io/serial/serial.h>
#include <arch/gdt.h>
extern struct process_t* processes_list; extern struct process* processes_list;
extern struct process_t* current_process; extern struct process* current_process;
extern struct process_t* idle_proc; extern struct process* idle_proc;
extern struct tss tss;
/* /*
* scheduler_init - Choose the first process * scheduler_init - Choose the first process
@@ -20,6 +23,7 @@ extern struct process_t* idle_proc;
void scheduler_init() void scheduler_init()
{ {
current_process = processes_list; current_process = processes_list;
DEBUG("scheduler starting with: pid=%u, name='%s', context=%p", current_process->pid, current_process->name, current_process->context);
} }
/* /*
@@ -32,47 +36,63 @@ void scheduler_init()
* Return: * Return:
* <context> - CPU context for next process * <context> - CPU context for next process
*/ */
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context) struct cpu_status* scheduler_schedule(struct cpu_status* context)
{ {
if (context == NULL) { if (context == NULL) {
panic(NULL, "Scheduler called with NULL context"); panic(NULL, "Scheduler called with NULL context");
} }
if (current_process == NULL) { if (current_process == NULL) {
// If no more processes, then set IDLE as the current process, that's it. panic(NULL, "current_process is NULL");
current_process = idle_proc;
} }
if (current_process == idle_proc && current_process->next == NULL) if (current_process->context == NULL) {
{ panic(NULL, "current_process->context is NULL");
return idle_proc->context;
} }
current_process->context = context; current_process->context = context;
//current_process->status = READY;
for (;;) { if (current_process->status == DEAD) {
struct process_t* prev_process = current_process; struct process* dead_process = current_process;
if (current_process->next != NULL) { struct process* next_process = (dead_process->next != NULL) ? dead_process->next : processes_list;
process_delete(&processes_list, dead_process);
if (processes_list == NULL || next_process == dead_process) {
current_process = idle_proc;
return idle_proc->context;
}
current_process = next_process;
} else 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) { for (;;) {
process_delete(&prev_process, current_process); if (current_process->status == DEAD) {
current_process = NULL; struct process* dead_process = current_process;
struct process* next_process = (current_process->next != NULL) ? current_process->next : processes_list;
process_delete(&processes_list, dead_process);
if (processes_list == NULL || next_process == dead_process) {
current_process = idle_proc;
return idle_proc->context; return idle_proc->context;
} else { }
current_process = next_process;
continue;
}
current_process->status = RUNNING; current_process->status = RUNNING;
break; break;
} }
}
DEBUG("current_process={pid=%u, name='%s', root_page_table[virt]=%p}", current_process->pid, current_process->name, current_process->root_page_table);
// Here, we chose next running process so we load its kernel stack & page tables
tss.rsp0 = (uint64_t)current_process->kernel_stack;
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");
return current_process->context; return current_process->context;
} }
+4 -4
View File
@@ -6,8 +6,8 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include "kernel.h" #include <kernel.h>
#include "spinlock.h" #include <sched/spinlock.h>
/* /*
* spinlock_acquire - Lock a lock * spinlock_acquire - Lock a lock
@@ -16,7 +16,7 @@
* Saves the RFLAGS register, then acquires a lock. * Saves the RFLAGS register, then acquires a lock.
* Pause instruction is used to ease the CPU. * Pause instruction is used to ease the CPU.
*/ */
void spinlock_acquire(struct spinlock_t* lock) void spinlock_acquire(struct spinlock* lock)
{ {
uint64_t rflags; uint64_t rflags;
asm volatile("pushfq ; pop %0 ; cli" : "=rm"(rflags) : : "memory"); asm volatile("pushfq ; pop %0 ; cli" : "=rm"(rflags) : : "memory");
@@ -36,7 +36,7 @@ void spinlock_acquire(struct spinlock_t* lock)
* unlocks it (clears locked state). * unlocks it (clears locked state).
* RFLAGS is then restored. * RFLAGS is then restored.
*/ */
void spinlock_release(struct spinlock_t* lock) void spinlock_release(struct spinlock* lock)
{ {
uint64_t rflags = lock->rflags; uint64_t rflags = lock->rflags;
__atomic_clear(&lock->locked, __ATOMIC_RELEASE); __atomic_clear(&lock->locked, __ATOMIC_RELEASE);
+17
View File
@@ -0,0 +1,17 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Stack Smashing Protection feature
* @license GPL-3.0-only
*/
#include <config.h>
#include <stdint.h>
#include <kernel.h>
#include <stddef.h>
uint64_t __stack_chk_guard = STACK_CHK_GUARD;
void __stack_chk_fail(void)
{
panic(NULL, "SSP: Stask Smashing Detected!!! (very spicy)");
}
+293
View File
@@ -0,0 +1,293 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Undefined behavior sanitization hooks
* @license GPL-3.0-only
*/
#include <stdint.h>
#include <security/ubsan.h>
#include <kernel.h>
#include <stddef.h>
/*
* Hooks were inspired from the Sortix implementation:
* https://gitlab.com/sortix/sortix/-/blob/main/libc/ubsan/ubsan.c
*
* Not all hooks are implemented here.
*/
extern struct init_status init;
/*
* __ubsan_handle_type_mismatch_v1 - Hook for UBSan
* @data_raw: pointer to error data
* @pointer_raw: faulty pointer
*
* This function is executed when the UBSan library detects
* following undefined behavior: type mismatch, null pointer
* access, and unaligned access. It halts the system and
* gives the location of the code that triggered it.
*/
void __ubsan_handle_type_mismatch_v1(void* data_raw, void* pointer_raw)
{
struct ubsan_type_mismatch_v1_data* data = (struct ubsan_type_mismatch_v1_data*) data_raw;
uintptr_t pointer = (uintptr_t)pointer_raw;
uintptr_t alignment = (uintptr_t)1UL << data->log_alignment;
const char* violation = "type mismatch";
if ( !pointer ) {
violation = "null pointer access";
}
else if ( alignment && (pointer & (alignment - 1)) ) {
violation = "unaligned access";
}
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: %s (ptr=%p) at %s:%u:%u\x1b[0m", violation, pointer, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: %s (ptr=%p) at %s:%u:%u\x1b[0m\r\n", violation, pointer, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error");
}
/*
* __ubsan_handle_pointer_overflow - Hook for UBSan
* @data_raw: pointer to error data
* @base_raw: base pointer
* @result_raw: pointer after faulty operation
*
* This function is executed when the UBSan library detects
* following undefined behavior: pointer overflow.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_pointer_overflow(void* data_raw, void* base_raw, void* result_raw)
{
struct ubsan_pointer_overflow_data* data = (struct ubsan_pointer_overflow_data*) data_raw;
uintptr_t base = (uintptr_t)base_raw;
uintptr_t result = (uintptr_t)result_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: pointer overflow (base=%p, result=%p) at %s:%u:%u\x1b[0m", base, result, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: pointer overflow (base=%p, result=%p) at %s:%u:%u\x1b[0m", base, result, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: pointer overflow");
}
/*
* __ubsan_handle_shift_out_of_bounds - Hook for UBSan
* @data_raw: pointer to error data
* @lhs_raw: left hand side (value being shifted)
* @rhs_raw: right hand side (shift amount)
*
* This function is executed when the UBSan library detects
* following undefined behavior: shift out of bounds.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_shift_out_of_bounds(void* data_raw, void* lhs_raw, void* rhs_raw)
{
struct ubsan_shift_out_of_bounds_data* data = (struct ubsan_shift_out_of_bounds_data*) data_raw;
uintptr_t lhs = (uintptr_t) lhs_raw;
uintptr_t rhs = (uintptr_t) rhs_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: shift out of bounds (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: shift out of bounds (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: shift out of bounds");
}
/*
* __ubsan_handle_load_invalid_value - Hook for UBSan
* @data_raw: pointer to error data
* @value_raw: value loaded
*
* This function is executed when the UBSan library detects
* following undefined behavior: invalid value load.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_load_invalid_value(void* data_raw, void* value_raw)
{
struct ubsan_invalid_value_data* data = (struct ubsan_invalid_value_data*) data_raw;
uintptr_t value = (uintptr_t) value_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: invalid value load (value=%p) at %s:%u:%u\x1b[0m", value, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: invalid value load (value=%p) at %s:%u:%u\x1b[0m", value, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: invalid value load");
}
/*
* __ubsan_handle_out_of_bounds - Hook for UBSan
* @data_raw: pointer to error data
* @index_raw: out-of-bounds index in array
*
* This function is executed when the UBSan library detects
* following undefined behavior: access out of bounds.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_out_of_bounds(void* data_raw, void* index_raw)
{
struct ubsan_out_of_bounds_data* data = (struct ubsan_out_of_bounds_data*) data_raw;
uintptr_t index = (uintptr_t) index_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: out of bounds (index=%p) at %s:%u:%u\x1b[0m", index, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: out of bounds (index=%p) at %s:%u:%u\x1b[0m", index, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: out of bounds");
}
/*
* __ubsan_handle_divrem_overflow - Hook for UBSan
* @data_raw: pointer to error data
* @lhs_raw: left hand side operator
* @rhs_raw: right hand side operator
*
* This function is executed when the UBSan library detects
* following undefined behavior: division remainder overflow.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_divrem_overflow(void* data_raw, void* lhs_raw, void* rhs_raw)
{
struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw;
uintptr_t lhs = (uintptr_t) lhs_raw;
uintptr_t rhs = (uintptr_t) rhs_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: division remainder overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: division remainder overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: division remainder overflow");
}
/*
* __ubsan_handle_sub_overflow - Hook for UBSan
* @data_raw: pointer to error data
* @lhs_raw: left hand side operator
* @rhs_raw: right hand side operator
*
* This function is executed when the UBSan library detects
* following undefined behavior: subtraction overflow.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_sub_overflow(void* data_raw, void* lhs_raw, void* rhs_raw)
{
struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw;
uintptr_t lhs = (uintptr_t) lhs_raw;
uintptr_t rhs = (uintptr_t) rhs_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: subtraction overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: subtraction overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: subtraction overflow");
}
/*
* __ubsan_handle_add_overflow - Hook for UBSan
* @data_raw: pointer to error data
* @lhs_raw: left hand side operator
* @rhs_raw: right hand side operator
*
* This function is executed when the UBSan library detects
* following undefined behavior: addition overflow.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_add_overflow(void* data_raw, void* lhs_raw, void* rhs_raw)
{
struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw;
uintptr_t lhs = (uintptr_t) lhs_raw;
uintptr_t rhs = (uintptr_t) rhs_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: addition overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: addition overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: addition overflow");
}
/*
* __ubsan_handle_mul_overflow - Hook for UBSan
* @data_raw: pointer to error data
* @lhs_raw: left hand side operator
* @rhs_raw: right hand side operator
*
* This function is executed when the UBSan library detects
* following undefined behavior: multiplication overflow.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_mul_overflow(void* data_raw, void* lhs_raw, void* rhs_raw)
{
struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw;
uintptr_t lhs = (uintptr_t) lhs_raw;
uintptr_t rhs = (uintptr_t) rhs_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: multiplication overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: multiplication overflow (lhs=%p, rhs=%p) at %s:%u:%u\x1b[0m", lhs, rhs, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: multiplication overflow");
}
/*
* __ubsan_handle_negate_overflow - Hook for UBSan
* @data_raw: pointer to error data
* @old_value_raw: value before overflow
*
* This function is executed when the UBSan library detects
* following undefined behavior: negation overflow.
* It halts the system and gives the location of the code
* that triggered it.
*/
void __ubsan_handle_negate_overflow(void* data_raw, void* old_value_raw)
{
struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw;
uintptr_t old_value = (uintptr_t) old_value_raw;
struct ubsan_source_location* loc = &data->location;
DEBUG("\x1b[38;5;231mUBSan: negation overflow (old_value=%p) at %s:%u:%u\x1b[0m", old_value, loc->filename, loc->line, loc->column);
if (init.all) {
printf("\x1b[38;5;231mUBSan: negation overflow (old_value=%p) at %s:%u:%u\x1b[0m", old_value, loc->filename, loc->line, loc->column);
}
panic(NULL, "Undefined Behavior Sanitization error: negation overflow");
}
+37
View File
@@ -70,3 +70,40 @@ void strncpy(char* dst, const char* src, size_t n)
size_t i = 0; size_t i = 0;
while(i++ != n && (*dst++ = *src++)); while(i++ != n && (*dst++ = *src++));
} }
/*
* strncmp - compare two strings up to n characters
* @s1: first string
* @s2: second string
* @n: number of bytes to compare
*
* Taken from: https://github.com/DevSolar/pdclib/blob/master/functions/string/strncmp.c
*
* Return:
* $0 - @s1 and @s2 are equal
* $<0 - @s1 is less than @s2
* $>0 - @s1 is greater than @s2
*/
int strncmp(const char* s1, const char* s2, size_t n)
{
while ( n && *s1 && ( *s1 == *s2 ) ) {
++s1;
++s2;
--n;
}
if ( n == 0 ) {
return 0;
}
else {
return ( *(unsigned char *)s1 - *(unsigned char *)s2 );
}
}
// BSD implementation
size_t strlen(const char* str)
{
const char* s;
for (s = str; *s; ++s);
return (s - str);
}
+89
View File
@@ -0,0 +1,89 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Date helper functions
* @license GPL-3.0-only
*/
#include <stdint.h>
#include <time/date.h>
#include <mem/utils.h>
#include <kernel.h>
extern struct boot_context boot_ctx;
// Unix epoch used as reference: Jan 1st 1970, 00:00:00 UTC
struct date epoch = {
.year = 1970,
.month = 1,
.day = 1,
.hour = 0,
.minute = 0,
.second = 0
};
/*
* date_timestamp_to_date - Convert UNIX timestamp to a date structure
* @timestamp: UNIX timestamp
*
* Return:
* <date> - date structure
*/
struct date date_timestamp_to_date(uint64_t timestamp)
{
struct date result;
memcpy(&result, &epoch, sizeof(struct date));
uint64_t nr_days = timestamp / 86400;
while (nr_days > 0) {
unsigned int nr_month = 0;
int leap_year = 0;
if (result.year % 4 == 0 && (result.year % 100 != 0 || result.year % 400 == 0)) {
leap_year = 1;
} else {
leap_year = 0;
}
if (result.month == 2) {
if (leap_year != 0) {
nr_month = 29;
} else {
nr_month = 28;
}
} else {
nr_month = 31 - ((result.month -1) % 7 % 2);
}
if (nr_days >= nr_month) {
nr_days -= nr_month;
result.month++;
if (result.month > 12) {
result.month = 1;
result.year++;
}
} else {
result.day += nr_days;
nr_days = 0;
}
}
result.second = timestamp % 60;
timestamp /= 60;
result.minute = timestamp % 60;
timestamp /= 60;
result.hour = timestamp % 24;
return result;
}
/*
* date_now - Get the current date (time at boot + timer ticks)
*
* Return:
* <date> - date structure
*/
struct date date_now()
{
uint64_t timestamp_now = boot_ctx.bootdate->boot_time + (ticks/1000);
return date_timestamp_to_date(timestamp_now);
}
+2 -2
View File
@@ -5,9 +5,9 @@
*/ */
#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" #include <config.h>
/* /*
For now, the timer module will be using the PIC. For now, the timer module will be using the PIC.
+18
View File
@@ -0,0 +1,18 @@
CC := x86_64-elf-gcc
CC_FLAGS := -ffreestanding -nostdlib -fno-pic -mno-red-zone -Ilibc
LD := x86_64-elf-ld
BUILDDIR := ../build
LIBDIR := libc
all: pedicel apex
pedicel:
nasm -f bin pedicel.S -o $(BUILDDIR)/pedicel.raw
apex:
$(CC) $(CC_FLAGS) -c apex.c -o $(BUILDDIR)/apex.o
nasm -f elf64 $(LIBDIR)/crt0.S -o $(BUILDDIR)/crt0.o
$(LD) -T $(LIBDIR)/linker.ld $(BUILDDIR)/crt0.o $(BUILDDIR)/apex.o -o $(BUILDDIR)/apex.elf
objcopy -O binary $(BUILDDIR)/apex.elf $(BUILDDIR)/apex.raw
+7
View File
@@ -0,0 +1,7 @@
#include <syscall.h>
int main() {
const char* msg = "hi from C userland\r\n";
write(1, msg, 21);
return 42;
}
+18
View File
@@ -0,0 +1,18 @@
bits 64
global _start
extern main
section .text
; Begin the program with main() function
_start:
call main
; Exit the program by exit() syscall
.exit:
mov rdi, rax ; put the value of "return X;" (rax) as arg1 (error_code)
mov rax, 60 ; sys_exit
int 0x80
.loop:
jmp .loop
+22
View File
@@ -0,0 +1,22 @@
ENTRY(_start)
SECTIONS
{
. = 0x400000;
.text : {
*(.text*)
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
*(.bss*)
}
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
// 3 because 3 arguments to the call, get it??
static inline long syscall3(long n, long a, long b, long c) {
long ret;
__asm__ volatile (
"int $0x80"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c)
: "memory"
);
return ret;
}
static inline void write(int fd, const char* buf, long len) {
syscall3(1, fd, (long)buf, len);
}
static inline void exit(int code) {
__asm__ volatile (
"int $0x80"
:
: "a"(60), "D"(code)
: "memory"
);
for (;;);
}
+51
View File
@@ -0,0 +1,51 @@
bits 64
section .data
hello db 0x0A, 0x0D, "TEST PROGRAM...", 0x0A, 0x0D, 0
filename db "wow.txt", 0
section .text
_start:
mov rax, 0x1 ;sys_write
mov rdi, 0x1 ;stdout
lea rsi, [rel hello]
mov rdx, 19 ;count
int 0x80
; Open a file
mov rax, 0x2 ;sys_open
lea rdi, [rel filename] ;filename
mov rsi, 0x0 ;flags
int 0x80
mov rdi, rax ;fd
mov rax, 0x0 ;sys_read
lea rsi, [rel buf] ;buf
mov rdx, 33 ;count
int 0x80
mov rax, 0x1 ;sys_write
mov rdi, 0x1 ;stdout
lea rsi, [rel buf] ;buf
mov rdx, 33 ;count
int 0x80
mov rax, 0x3 ;sys_close
mov rdi, 0x3 ;fd
int 0x80
; when we are ready to have an os specific toolchain,
; this bit (exit & loop) should be appended at the end of every
; C program we compile.
.end:
mov rax, 0x3C
mov rdi, 0x0
int 0x80
.loop:
jmp .loop
section .bss
buf resb 10
+1
View File
@@ -0,0 +1 @@
hi from a file opened in usermode