33 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
56 changed files with 2243 additions and 335 deletions
+19 -2
View File
@@ -1,13 +1,24 @@
USER_PROGRAMS := pedicel.raw apex.raw
USER_FILES := wow.txt
BUILDDIR := build BUILDDIR := build
ELFFILE := pepperk ELFFILE := pepperk
SRC := src SRC := src
CC_PROBLEMATIC_FLAGS:=-Wno-unused-parameter -Wno-unused-variable
ifeq ($(UBSAN),true)
SOURCES := $(shell find src -name '*.c') SOURCES := $(shell find src -name '*.c')
CC_PROBLEMATIC_FLAGS:= -fsanitize=undefined
else
SOURCES := $(shell find src -name '*.c')
SOURCES := $(filter-out src/security/ubsan.c, $(SOURCES))
endif
OBJFILES := $(patsubst $(SRC)/%.c, $(BUILDDIR)/%.o, $(SOURCES)) OBJFILES := $(patsubst $(SRC)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
CC := x86_64-elf-gcc CC := x86_64-elf-gcc
CC_FLAGS=-Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel CC_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
CC_PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable
LD := x86_64-elf-ld LD := x86_64-elf-ld
@@ -32,12 +43,18 @@ limine/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
.PHONY: user
user:
$(MAKE) -C user
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) $(USER_PROGRAMS) -C ../user $(USER_FILES)
build-iso: limine/limine $(ELFFILE) 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 $(ELFFILE) 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/
+17 -22
View File
@@ -1,5 +1,5 @@
# <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 ## Description
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. 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.
@@ -23,8 +23,9 @@ CC := gcc
LD := ld LD := ld
``` ```
Then, to compile the kernel and make an ISO image file, run: `make build-iso` Then, to compile the kernel and make an ISO image file, run: `make`.
To run it with QEMU, do: `make run` To build the user programs and the initial filesystem, do `make user`.
To run it with QEMU, do: `make run`.
## Trying the kernel on real hardware ## Trying the kernel on real hardware
@@ -34,31 +35,23 @@ Compile the kernel and generate an ISO image like described above, then burn the
sudo dd if=pepper.iso of=/dev/sdX sudo dd if=pepper.iso of=/dev/sdX
``` ```
## TODO ## Compilation options
The basics that I'm targeting are: By default, PepperOS is compiled without the following features:
### Basic utility of what we call a "kernel" ```
UBSAN - undefined behavior sanitization
```
- Implement tasks, and task switching + context switching and spinlock acquire/release These features can be activated by setting them to "true" at the end of the make command, for example:
- 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 ```
make UBSAN=true
```
- Documentation ## Writing software for PepperOS
- SOME error handling in functions
- Unit tests
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
### Optional features If you want to write software for PepperOS, take a look at the [Software Developer's guide](docs/SOFTWARE.md).
In the future, maybe?
- SMP support (Limine provides functionality to make this easier)
- Parsing the ACPI tables and using them for something
- Replacing the PIT timer with APIC
## Thanks ## Thanks
@@ -74,3 +67,5 @@ PepperOS wouldn't be possible without the following freely-licensed software:
- 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) - 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
+13 -1
View File
@@ -25,6 +25,9 @@ The recommended hardware to run PepperOS is the following:
## b. Features ## b. Features
- Round robin preemptive scheduling
- Coexistence of ring 0 and ring 3 processes
## II. Kernel architecture ## II. Kernel architecture
### a. Boot process ### a. Boot process
@@ -37,4 +40,13 @@ The recommended hardware to run PepperOS is the following:
## III. Syscall table ## III. Syscall table
Not yet implemented. 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
+6 -1
View File
@@ -10,6 +10,9 @@ void wrmsr(uint32_t msr, uint64_t value);
bool x86_has_msr(); bool x86_has_msr();
void x86_arch_init(); void x86_arch_init();
void x86_cpu_identification();
int cpuid_get_vendor_string(char* str);
/* Interrupt Descriptor Table */ /* Interrupt Descriptor Table */
void idt_init(void); void idt_init(void);
@@ -32,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;
@@ -59,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
+19 -4
View File
@@ -9,8 +9,8 @@
/* version */ /* version */
#define PEPPEROS_VERSION_MAJOR "0" #define PEPPEROS_VERSION_MAJOR "0"
#define PEPPEROS_VERSION_MINOR "0" #define PEPPEROS_VERSION_MINOR "1"
#define PEPPEROS_VERSION_PATCH "58" #define PEPPEROS_VERSION_PATCH "121"
#define PEPPEROS_SPLASH \ #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"\
@@ -20,11 +20,14 @@
"\x1b[38;5;196m/_/ /_/ /_/ \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" " --- 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 */ /* process */
#define PROCESS_NAME_MAX 64 #define PROCESS_NAME_MAX 64
#define PROCESS_STACK_SIZE 0x10000 // 64kb #define PROCESS_STACK_SIZE 0x10000 // 64kb
#define PROCESS_BASE 0x400000 #define PROCESS_STACK_TOP 0x80000000
#define PROCESS_STACK_BASE 0x1000000
/* sched */ /* sched */
// 1 tick = 1 ms => quantum = 10ms // 1 tick = 1 ms => quantum = 10ms
@@ -37,6 +40,11 @@
#define KERNEL_STACK_SIZE 65536 #define KERNEL_STACK_SIZE 65536
#define KERNEL_IDT_ENTRIES 33 #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 */ /* paging */
#define PAGING_MAX_PHYS 0x200000000 #define PAGING_MAX_PHYS 0x200000000
@@ -52,4 +60,11 @@
/* time */ /* time */
#define TIMER_FREQUENCY 1000 #define TIMER_FREQUENCY 1000
/* ssp */
#define STACK_CHK_GUARD 0x7ABA5C007ABA5C00
/* fs */
#define FDT_MAX 8 // Maximum amount of file descriptors per process
#endif #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
+1
View File
@@ -11,5 +11,6 @@ void kputs(const char* str);
void term_init(void); void term_init(void);
int printf(const char* fmt, ...); int printf(const char* fmt, ...);
void internal_putc(int c, void *_); void internal_putc(int c, void *_);
int kprintf(const char* fmt, ...);
#endif #endif
+17 -4
View File
@@ -7,9 +7,15 @@
#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")
@@ -21,7 +27,7 @@ enum ErrorCodes {
#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
+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
+3 -1
View File
@@ -16,9 +16,11 @@
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)
+1
View File
@@ -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);
+14 -17
View File
@@ -9,26 +9,23 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
/* struct vmm_context {
This will be our linked list of virtual memory objects. uint64_t* pml4;
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); 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 #endif
+25 -8
View File
@@ -10,6 +10,8 @@
#include <stddef.h> #include <stddef.h>
#include <config.h> #include <config.h>
#include <stdint.h> #include <stdint.h>
#include <limine.h>
#include <stdbool.h>
typedef enum { typedef enum {
READY, READY,
@@ -17,23 +19,38 @@ typedef enum {
DEAD DEAD
} status_t; } status_t;
struct process_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; size_t pid;
char name[PROCESS_NAME_MAX]; char name[PROCESS_NAME_MAX];
status_t status; status_t status;
struct cpu_status_t* context; struct cpu_status* context;
void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511] void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511]
struct process_t* next; 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); void process_init(void);
struct process_t* process_create(char* name, void(*function)(void*), void* arg); struct process* process_create(char* name, void(*function)(void*), void* arg);
void process_add(struct process_t** processes_list, struct process_t* process); void process_add(struct process** processes_list, struct process* process);
void process_delete(struct process_t** processes_list, struct process_t* process); void process_delete(struct process** processes_list, struct process* process);
struct process_t* process_get_next(struct process_t* process); struct process* process_get_next(struct process* process);
void process_exit(void); void process_exit(void);
void process_display_list(struct process_t* processes_list); void process_display_list(struct process* processes_list);
void process_create_user_raw(char* file, int size, char* name);
#endif #endif
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
+2
View File
@@ -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
+1
View File
@@ -6,3 +6,4 @@ interface_branding: Welcome to the PepperOS disk!
comment: Default configuration (warning: spicy) comment: Default configuration (warning: spicy)
path: boot():/boot/pepperk path: boot():/boot/pepperk
module_path: boot():/boot/initfs.tar
+27
View File
@@ -6,6 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <kernel.h>
#include <string/string.h>
/* /*
* cpuid - Wrapper for CPUID instruction * cpuid - Wrapper for CPUID instruction
@@ -19,3 +21,28 @@ void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t*
{ {
__asm__ volatile("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(leaf)); __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
+28 -10
View File
@@ -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;
+69 -1
View File
@@ -4,10 +4,11 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include <mem/gdt.h> #include <arch/gdt.h>
#include <stdint.h> #include <stdint.h>
#include <arch/x86.h> #include <arch/x86.h>
#include <kernel.h> #include <kernel.h>
#include <mem/utils.h>
/* /*
* x86_overwrite_pat - Set PAT to WC * x86_overwrite_pat - Set PAT to WC
@@ -28,6 +29,26 @@ static void x86_overwrite_pat()
wrmsr(0x277, pat); 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 * x86_arch_init - Initialize x86 CPU structures
* *
@@ -41,6 +62,53 @@ static void x86_overwrite_pat()
void x86_arch_init() void x86_arch_init()
{ {
x86_overwrite_pat(); x86_overwrite_pat();
x86_enable_fpu();
x86_cpu_identification();
idt_init(); idt_init();
gdt_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);
}
}
+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;
}
+2 -2
View File
@@ -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*/
@@ -47,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;
+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;
}
+13 -7
View File
@@ -289,15 +289,21 @@ int keyboard_getline(char* output, size_t size)
// Read until Enter is pressed // Read until Enter is pressed
while ((c = keyboard_getchar()) != 0x0A) { while ((c = keyboard_getchar()) != 0x0A) {
if (index == size-1) { if (c == '\b') {
output[index] = c; if (index > 0) {
output[index+1] = '\0'; index--;
return index; output[index] = '\0';
printf(" \b");
} }
output[index] = c; continue;
index++;
} }
output[index+1] = '\0';
if (index >= size-1) {
continue;
}
output[index++] = c;
}
output[index] = '\0';
return index; return index;
} }
+1 -1
View File
@@ -11,7 +11,7 @@
extern struct init_status init; extern struct init_status init;
extern int panic_count; extern int panic_count;
struct spinlock_t serial_lock = {0}; struct spinlock serial_lock = {0};
/* /*
* outb - Writes a byte to a CPU port * outb - Writes a byte to a CPU port
+66 -2
View File
@@ -29,8 +29,8 @@ because this shitty implementation will be replaced one day by Flanterm
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_t printf_lock = {0}; struct spinlock printf_lock = {0};
extern int panic_count; extern int panic_count;
@@ -65,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
@@ -96,6 +128,38 @@ int printf(const char* fmt, ...)
return -1; 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;
}
/* /*
* kputs - Kernel puts * kputs - Kernel puts
* @str: String to write * @str: String to write
+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;
}
+20 -25
View File
@@ -4,13 +4,12 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "arch/x86.h"
#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/serial/serial.h> #include <io/serial/serial.h>
#include <mem/gdt.h> #include <arch/gdt.h>
#include <mem/utils.h> #include <mem/utils.h>
#include <kernel.h> #include <kernel.h>
#include <time/timer.h> #include <time/timer.h>
@@ -25,6 +24,8 @@
#include <io/term/flanterm.h> #include <io/term/flanterm.h>
#include <io/term/flanterm_backends/fb.h> #include <io/term/flanterm_backends/fb.h>
#include <arch/x86.h> #include <arch/x86.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\n\rWelcome to PepperOS! Pedicel speaking.\r\nNothing left to do, let's go idle!\r\n");
}
void idle_main(void* arg) void idle_main(void* arg)
{ {
@@ -79,14 +78,6 @@ void idle_main(void* arg)
} }
} }
void thing_main(void* arg)
{
printf("What's your name, pal? ");
char name[10];
keyboard_getline(name, 10);
printf("\r\n{%s} is such a nice name!\r\n", name);
}
extern uintptr_t kheap_start; extern uintptr_t kheap_start;
/* /*
@@ -102,11 +93,7 @@ void kmain()
CLEAR_INTERRUPTS; CLEAR_INTERRUPTS;
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf(); if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
// Populate boot context populate_boot_context(&boot_ctx);
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;
term_init(); term_init();
serial_init(); serial_init();
@@ -124,11 +111,19 @@ void kmain()
process_init(); process_init();
idle_proc = process_create("idle", (void*)idle_main, 0); idle_proc = process_create("idle", (void*)idle_main, 0);
process_create("pedicel", (void*)pedicel_main, 0);
process_create("thing", thing_main, NULL);
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();
printf(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 <mem/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");
}
+39 -10
View File
@@ -17,7 +17,7 @@ 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);
}
+21 -5
View File
@@ -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
+236 -47
View File
@@ -13,66 +13,255 @@ in a specified virtual space
compared to the PMM which allocs/frees 4kb frames ("physical pages"). compared to the PMM which allocs/frees 4kb frames ("physical pages").
*/ */
#include "config.h"
#include <mem/vmm.h> #include <mem/vmm.h>
#include <mem/paging.h> #include <mem/paging.h>
#include <stddef.h> #include <stddef.h>
#include <mem/pmm.h> #include <mem/pmm.h>
#include <kernel.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; extern uint64_t *kernel_pml4;
void vmm_setup_pt_root() /*
* 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)
{ {
// We alloc a physical page (frame) for the pointer, then map it if (!ctx || !ctx->pml4) {
// to virt (pointer) panic(NULL, "Attempted to switch to bad PML4!");
uintptr_t phys = pmm_alloc(); }
vmm_pt_root = (void*)kernel_pml4; uint64_t pml4 = VIRT_TO_PHYS(ctx->pml4);
paging_map_page(kernel_pml4, (uint64_t)vmm_pt_root, phys, convert_x86_vm_flags(VM_FLAG_WRITE | VM_FLAG_EXEC)); asm volatile ("mov %0, %%cr3" :: "r"(pml4) : "memory");
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
} }
/* void* vmm_alloc(size_t length, size_t flags) /*
* 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)
{ {
// We will try to allocate at least length bytes, which have to be rounded UP to uint64_t pml4_i = PML4_INDEX(virt);
// the next page so its coherent with the PMM uint64_t pdpt_i = PDPT_INDEX(virt);
size_t len = ALIGN_UP(length, PAGE_SIZE); uint64_t pd_i = PD_INDEX(virt);
uint64_t pt_i = PT_INDEX(virt);
// Need to implement this (as linked list) if (!(pml4[pml4_i] & PTE_PRESENT)) return -1;
// but for now kernel heap is sufficient uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK);
// The VMM will prob be more useful when we have userspace
} */ 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() void vmm_init()
{ {
+113 -14
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 <sched/process.h> #include <sched/process.h>
#include <mem/kheap.h> #include <mem/kheap.h>
#include <kernel.h> #include <kernel.h>
#include <string/string.h> #include <string/string.h>
#include <mem/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 <io/term/flanterm.h>
#include <mem/utils.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;
}
+41 -23
View File
@@ -9,10 +9,13 @@
#include <mem/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,49 +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;
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;
/* if (prev_process != current_process) {
DEBUG("Changed from {pid=%u, name=%s} to {pid=%u, name=%s}", prev_process->pid, prev_process->name, current_process->pid, current_process->name);
} */
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;
} }
+2 -2
View File
@@ -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);
}
+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