Compare commits
61 Commits
memory
...
real-hw-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
03f87723d1
|
|||
|
3607a7179c
|
|||
|
424b4c4632
|
|||
|
6a82d581fb
|
|||
|
b77c53ae99
|
|||
|
f7735eb3a4
|
|||
|
a1e8aacd01
|
|||
| ccc8985d4c | |||
|
0482f594ef
|
|||
|
b02a4b5284
|
|||
|
32f3889565
|
|||
|
803ac0879b
|
|||
|
9fc55f98d8
|
|||
|
11bd628821
|
|||
| 80d8b49560 | |||
|
22fea378b4
|
|||
|
5eaf193d42
|
|||
|
af3a9e27fd
|
|||
|
6a3abb0f55
|
|||
| 6ceccb2374 | |||
|
e5c296238c
|
|||
|
5c0d02579b
|
|||
|
8026c33639
|
|||
|
8e2a612d88
|
|||
|
9d409317e2
|
|||
|
1dd4e728d4
|
|||
|
b9c77a316a
|
|||
| 6fc28806e2 | |||
|
3f9b78b05e
|
|||
|
42c7a55d3f
|
|||
|
5e9c582833
|
|||
| 77d9df6f48 | |||
|
90dc26ee11
|
|||
|
c8a72244b1
|
|||
|
b9f55d89f6
|
|||
|
a7d9e70a61
|
|||
|
9df33b49d8
|
|||
|
1f055ab31c
|
|||
| 95c801b991 | |||
|
70f19ab299
|
|||
|
9470dedb61
|
|||
|
4cf4fb0dda
|
|||
|
ac7216d84a
|
|||
|
458ba375f3
|
|||
| b920c87bab | |||
|
4fbd9b3987
|
|||
|
8aad1235c3
|
|||
|
38710653be
|
|||
|
7f997f6611
|
|||
|
7bb542d901
|
|||
|
4a90de9521
|
|||
|
c46157fad0
|
|||
| 6e633b44b7 | |||
|
b8a155fada
|
|||
|
091f94f89e
|
|||
|
b469952d91
|
|||
|
9cbecc1689
|
|||
|
12ab12f1b2
|
|||
|
0f72987bc1
|
|||
|
d9dfd4c749
|
|||
| be1be41a64 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -7,3 +7,10 @@ iso_root
|
|||||||
*.gch
|
*.gch
|
||||||
*/*.gch
|
*/*.gch
|
||||||
*/*/*.gch
|
*/*/*.gch
|
||||||
|
.gdb_history
|
||||||
|
symbols.map
|
||||||
|
symbols.S
|
||||||
|
*.log
|
||||||
|
build/
|
||||||
|
compile_commands.json
|
||||||
|
.cache/
|
||||||
54
Makefile
54
Makefile
@@ -1,21 +1,41 @@
|
|||||||
SOURCES = src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c
|
|
||||||
|
|
||||||
build:
|
BUILDDIR := build
|
||||||
rm -f *.o
|
ELFFILE := pepperk
|
||||||
x86_64-elf-gcc -g -c -Isrc $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel
|
SRC := src
|
||||||
objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o
|
SOURCES := $(shell find src -name '*.c')
|
||||||
nasm -f elf64 src/idt/idt.S -o idt_stub.o
|
OBJFILES := $(patsubst $(SRC)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
|
||||||
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
|
||||||
|
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_PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable
|
||||||
|
|
||||||
|
LD := x86_64-elf-ld
|
||||||
|
|
||||||
|
$(ELFFILE): $(BUILDDIR) $(OBJFILES)
|
||||||
|
nasm -f elf64 src/arch/x86/idt.S -o $(BUILDDIR)/idt_stub.o
|
||||||
|
$(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o
|
||||||
|
# Get the symbols for debugging
|
||||||
|
nm -n $(ELFFILE) | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
|
||||||
|
python3 symbols.py
|
||||||
|
nasm -f elf64 symbols.S -o $(BUILDDIR)/symbols.o
|
||||||
|
$(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o $(BUILDDIR)/symbols.o
|
||||||
|
|
||||||
|
$(BUILDDIR):
|
||||||
|
@mkdir -p $(BUILDDIR)
|
||||||
|
|
||||||
|
$(BUILDDIR)/%.o: $(SRC)/%.c
|
||||||
|
mkdir -p $(dir $@)
|
||||||
|
$(CC) -g -c -Iinclude $< $(CC_PROBLEMATIC_FLAGS) $(CC_FLAGS) -o $@
|
||||||
|
|
||||||
limine/limine:
|
limine/limine:
|
||||||
rm -rf limine
|
rm -rf limine
|
||||||
git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1
|
git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1
|
||||||
$(MAKE) -C limine
|
$(MAKE) -C limine
|
||||||
|
|
||||||
build-iso: limine/limine build
|
build-iso: limine/limine $(ELFFILE)
|
||||||
rm -rf iso_root
|
rm -rf iso_root
|
||||||
mkdir -p iso_root/boot
|
mkdir -p iso_root/boot
|
||||||
cp -v pepperk iso_root/boot
|
cp -v $(ELFFILE) iso_root/boot
|
||||||
mkdir -p iso_root/boot/limine
|
mkdir -p iso_root/boot/limine
|
||||||
cp -v limine.conf iso_root/boot/limine
|
cp -v limine.conf iso_root/boot/limine
|
||||||
mkdir -p iso_root/EFI/BOOT
|
mkdir -p iso_root/EFI/BOOT
|
||||||
@@ -29,12 +49,20 @@ build-iso: limine/limine build
|
|||||||
iso_root -o pepper.iso
|
iso_root -o pepper.iso
|
||||||
./limine/limine bios-install pepper.iso
|
./limine/limine bios-install pepper.iso
|
||||||
|
|
||||||
|
.PHONY: debug
|
||||||
debug:
|
debug:
|
||||||
qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
|
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -D qemu.log -no-reboot -no-shutdown &
|
||||||
gdb pepperk --command=debug.gdb
|
gdb $(ELFFILE) --command=debug.gdb
|
||||||
|
|
||||||
|
.PHONY: debug2
|
||||||
|
debug2:
|
||||||
|
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
|
||||||
|
pwndbg $(ELFFILE) --command=debug.gdb
|
||||||
|
|
||||||
|
.PHONY: run
|
||||||
run: build-iso
|
run: build-iso
|
||||||
qemu-system-x86_64 -cdrom pepper.iso -serial stdio
|
/usr/bin/qemu-system-x86_64 -cdrom pepper.iso -serial stdio
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o pepperk iso_root pepper.iso limine
|
rm -rf $(BUILDDIR) symbols.map symbols.S $(ELFFILE) iso_root pepper.iso limine
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -1,28 +1,62 @@
|
|||||||
# <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"
|
||||||
|
|
||||||
## Trying the kernel
|
## Description
|
||||||
|
|
||||||
First install the dependencies: `sudo apt install xorriso make qemu-system`
|
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.
|
||||||
|
|
||||||
Then, to compile the kernel and make an ISO image file: `make build-iso`
|
## Trying the kernel in QEMU
|
||||||
To run it with QEMU, `make run`
|
|
||||||
|
### Debian-based distributions
|
||||||
|
|
||||||
|
First, install the dependencies: `sudo apt install nasm python3 xorriso make qemu-system`
|
||||||
|
|
||||||
|
Then, you can get an x86_64 toolchain for compilation. The easiest way to do that on most systems is to install it from Homebrew:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew install x86_64-elf-gcc
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're already on a 64-bit machine (which you probably are), and don't want to install a cross-compiler, you can just override `CC` and `LD` variables in the Makefile, like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
CC := gcc
|
||||||
|
LD := ld
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, to compile the kernel and make an ISO image file, run: `make build-iso`
|
||||||
|
To run it with QEMU, do: `make run`
|
||||||
|
|
||||||
|
## Trying the kernel on real hardware
|
||||||
|
|
||||||
|
Compile the kernel and generate an ISO image like described above, then burn the image to a USB stick, `/dev/sdX` being the device name (you can get it using `lsblk`):
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo dd if=pepper.iso of=/dev/sdX
|
||||||
|
```
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
The basics that I'm targeting are:
|
The basics that I'm targeting are:
|
||||||
|
|
||||||
- Fix terminal driver (backspace issues, scrolling) OR add Flanterm or equivalent
|
### Basic utility of what we call a "kernel"
|
||||||
- Implement paging / see what Limine does at boot with memory management
|
|
||||||
- Implement tasks, and task switching
|
- Implement tasks, and task switching + context switching and spinlock acquire/release
|
||||||
- Load an executable
|
- Load an executable
|
||||||
- Scheduler (round-robin using the PIT timer interrupt)
|
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
|
||||||
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32
|
- Getting to userspace (ring 3 switching, syscall interface)
|
||||||
- Getting to userspace (syscalls)
|
|
||||||
- Porting musl libc or equivalent
|
- Porting musl libc or equivalent
|
||||||
|
|
||||||
In the future, maybe?
|
### Scalability/maintenance/expansion features
|
||||||
|
|
||||||
- SMP support
|
- Documentation
|
||||||
|
- SOME error handling in functions
|
||||||
|
- Unit tests
|
||||||
|
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
|
||||||
|
|
||||||
|
### Optional features
|
||||||
|
|
||||||
|
In the future, maybe?
|
||||||
|
- SMP support (Limine provides functionality to make this easier)
|
||||||
- Parsing the ACPI tables and using them for something
|
- Parsing the ACPI tables and using them for something
|
||||||
- Replacing the PIT timer with APIC
|
- Replacing the PIT timer with APIC
|
||||||
|
|
||||||
@@ -31,9 +65,12 @@ In the future, maybe?
|
|||||||
PepperOS wouldn't be possible without the following freely-licensed software:
|
PepperOS wouldn't be possible without the following freely-licensed software:
|
||||||
|
|
||||||
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
|
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
|
||||||
- Marco Paland's freestanding [printf implementation](https://github.com/mpaland)
|
- Charles Nicholson's [nanoprintf](https://github.com/charlesnicholson/nanoprintf)
|
||||||
- the [ZAP](https://www.zap.org.au/projects/console-fonts-zap/) PSF console fonts
|
- Mintuski's [Flanterm](https://codeberg.org/Mintsuki/Flanterm) terminal emulator
|
||||||
|
|
||||||
...and without these amazing resources:
|
...and without these amazing resources:
|
||||||
|
|
||||||
- the [OSDev](https://osdev.org) wiki & forums
|
- the [OSDev](https://osdev.org) wiki & forums
|
||||||
|
- Intel 64 and IA-32 Architectures Software Developer's Manual
|
||||||
|
- Documentation for the [GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc/)
|
||||||
|
- dreamos82's [OSDev Notes](https://github.com/dreamportdev/Osdev-Notes/tree/master)
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
target remote localhost:1234
|
target remote localhost:1234
|
||||||
set disassembly-flavor intel
|
set disassembly-flavor intel
|
||||||
display/8i $rip
|
display/4i $rip
|
||||||
|
|
||||||
|
# Trying to debug that flanterm page fault
|
||||||
|
|
||||||
|
# b plot_char_unscaled_uncanvas if $rdi == 0 || $rsi == 0 || $rdx == 0 || $r10 == 0
|
||||||
40
docs/MANUAL.md
Normal file
40
docs/MANUAL.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# PepperOS Manual
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
- [Overview](#i-overview)
|
||||||
|
- [Supported Hardware](#a-supported-hardware)
|
||||||
|
- [Features](#b-features)
|
||||||
|
- [Kernel architecture](#ii-kernel-architecture)
|
||||||
|
- [Boot process](#a-boot-process)
|
||||||
|
- [Memory management](#b-memory-management)
|
||||||
|
- [Scheduling](#c-scheduling)
|
||||||
|
- [Input/output](#d-inputoutput)
|
||||||
|
- [Syscall table](#iii-syscall-table)
|
||||||
|
|
||||||
|
## I. Overview
|
||||||
|
|
||||||
|
## a. Supported Hardware
|
||||||
|
|
||||||
|
The recommended hardware to run PepperOS is the following:
|
||||||
|
|
||||||
|
- UEFI/BIOS
|
||||||
|
- Any x86 processor, 64-bits only
|
||||||
|
- PS/2 Keyboard
|
||||||
|
- Minimum 128MB of memory
|
||||||
|
|
||||||
|
## b. Features
|
||||||
|
|
||||||
|
## II. Kernel architecture
|
||||||
|
|
||||||
|
### a. Boot process
|
||||||
|
|
||||||
|
### b. Memory management
|
||||||
|
|
||||||
|
### c. Scheduling
|
||||||
|
|
||||||
|
### d. Input/Output
|
||||||
|
|
||||||
|
## III. Syscall table
|
||||||
|
|
||||||
|
Not yet implemented.
|
||||||
93
docs/STYLE.md
Normal file
93
docs/STYLE.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# Pepper kernel coding style
|
||||||
|
|
||||||
|
This document describes the coding style for the Pepper kernel. It is used as a guideline across all source files.
|
||||||
|
|
||||||
|
## Indentation
|
||||||
|
|
||||||
|
Indentations should be 4 characters long.
|
||||||
|
|
||||||
|
## Line length
|
||||||
|
|
||||||
|
Lines should not be more than 100 characters long. Exceptions is made for printing strings.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Variables should be declared at most once per line.
|
||||||
|
|
||||||
|
## Braces
|
||||||
|
|
||||||
|
Non-function statement blocks should have an opening brace last on the line, and a closing brace first. Exception is made for `else`, `else if` statements and the like:
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (something) {
|
||||||
|
do_something();
|
||||||
|
} else if (something_else) {
|
||||||
|
do_something_else();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Having no braces for a single statement structure is fine.
|
||||||
|
|
||||||
|
Functions should have their opening brace on a separate line, and the same goes for the closing brace:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void function()
|
||||||
|
{
|
||||||
|
do_something();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Spaces
|
||||||
|
|
||||||
|
Use a space after `if, switch, case, for, do, while` keywords, but not for `sizeof, typeof, alignof, __attribute__` and the like.
|
||||||
|
|
||||||
|
For pointers, the asterisk should always be placed adjacent to the type name, like `char* str;`.
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
Functions should be named with whole words, beginning with the corresponding name of the module in the kernel (the parent folder). Words should be spaced with underscores, like so:
|
||||||
|
|
||||||
|
```c
|
||||||
|
serial_init(void* ptr, char* str, int foo);
|
||||||
|
```
|
||||||
|
|
||||||
|
Constants should be named in all caps, separated by underscores:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define MAX_HEAP_SIZE 0x1000
|
||||||
|
```
|
||||||
|
|
||||||
|
Global variables need to have descriptive names. Local variables can be kept short (especially for loop counters).
|
||||||
|
|
||||||
|
## Typedefs
|
||||||
|
|
||||||
|
Structures should not be `typedef`'d. However using `typedef` for an enumeration is fine.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
Functions should be short, simple, and only do one thing.
|
||||||
|
|
||||||
|
Function prototypes should include parameter names and their data types.
|
||||||
|
|
||||||
|
## Commenting
|
||||||
|
|
||||||
|
Comments should describe what a function does and why, not how it does it. The preferred way of commenting functions is the following:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/*
|
||||||
|
* function_name - Function brief description
|
||||||
|
* @arg1: Argument 1 description
|
||||||
|
* @arg2: Argument 2 description
|
||||||
|
*
|
||||||
|
* A longer description can be featured here, explaining more
|
||||||
|
* in detail what the function does and why it does it.
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kernel messages
|
||||||
|
|
||||||
|
When printing kernel messages with the `DEBUG` macro, they should start with a capital letter.
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
|
||||||
|
Some of the elements here are inspired by the [Linux kernel coding style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html).
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
#ifndef IDT_H
|
#ifndef X86_H
|
||||||
#define IDT_H
|
#define X86_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void idt_init();
|
uint64_t rdmsr(uint32_t msr);
|
||||||
|
void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx);
|
||||||
|
void wrmsr(uint32_t msr, uint64_t value);
|
||||||
|
bool x86_has_msr();
|
||||||
|
void x86_arch_init();
|
||||||
|
|
||||||
struct interrupt_descriptor
|
/* Interrupt Descriptor Table */
|
||||||
{
|
|
||||||
|
void idt_init(void);
|
||||||
|
|
||||||
|
struct interrupt_descriptor {
|
||||||
uint16_t address_low;
|
uint16_t address_low;
|
||||||
uint16_t selector;
|
uint16_t selector;
|
||||||
uint8_t ist;
|
uint8_t ist;
|
||||||
@@ -16,8 +24,7 @@ struct interrupt_descriptor
|
|||||||
uint32_t reserved;
|
uint32_t reserved;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct idtr
|
struct idtr {
|
||||||
{
|
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
uint64_t base;
|
uint64_t base;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
@@ -25,8 +32,7 @@ struct idtr
|
|||||||
// All general-purpose registers (except rsp) as stored on the stack,
|
// All general-purpose registers (except rsp) as stored on the stack,
|
||||||
// plus the values we pushed (vector number, error code) and the iret frame
|
// plus the values we pushed (vector number, error code) and the iret frame
|
||||||
// In reverse order because the stack grows downwards.
|
// In reverse order because the stack grows downwards.
|
||||||
struct cpu_status_t
|
struct cpu_status_t {
|
||||||
{
|
|
||||||
uint64_t r15;
|
uint64_t r15;
|
||||||
uint64_t r14;
|
uint64_t r14;
|
||||||
uint64_t r13;
|
uint64_t r13;
|
||||||
@@ -36,7 +42,6 @@ struct cpu_status_t
|
|||||||
uint64_t r9;
|
uint64_t r9;
|
||||||
uint64_t r8;
|
uint64_t r8;
|
||||||
uint64_t rbp;
|
uint64_t rbp;
|
||||||
//uint64_t rsp;
|
|
||||||
uint64_t rdi;
|
uint64_t rdi;
|
||||||
uint64_t rsi;
|
uint64_t rsi;
|
||||||
uint64_t rdx;
|
uint64_t rdx;
|
||||||
55
include/config.h
Normal file
55
include/config.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PepperOS configuration file
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
/* version */
|
||||||
|
#define PEPPEROS_VERSION_MAJOR "0"
|
||||||
|
#define PEPPEROS_VERSION_MINOR "0"
|
||||||
|
#define PEPPEROS_VERSION_PATCH "58"
|
||||||
|
#define PEPPEROS_SPLASH \
|
||||||
|
"\x1b[38;5;196m \x1b[38;5;231m____ _____\r\n\x1b[0m"\
|
||||||
|
"\x1b[38;5;196m ____ ___ ____ ____ ___ _____\x1b[38;5;231m/ __ \\/ ___/\r\n\x1b[0m"\
|
||||||
|
"\x1b[38;5;196m / __ \\/ _ \\/ __ \\/ __ \\/ _ \\/ ___\x1b[38;5;231m/ / / /\\__ \\ \r\n\x1b[0m"\
|
||||||
|
"\x1b[38;5;196m / /_/ / __/ /_/ / /_/ / __/ / \x1b[38;5;231m/ /_/ /___/ / \r\n\x1b[0m"\
|
||||||
|
"\x1b[38;5;196m / .___/\\___/ .___/ .___/\\___/_/ \x1b[38;5;231m\\____//____/ \r\n\x1b[0m"\
|
||||||
|
"\x1b[38;5;196m/_/ /_/ /_/ \r\n\x1b[0m"\
|
||||||
|
" --- version \x1b[38;5;220m"PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\x1b[0m built on \x1b[38;5;40m"__DATE__" "__TIME__"\x1b[0m\r\n"
|
||||||
|
|
||||||
|
/* process */
|
||||||
|
#define PROCESS_NAME_MAX 64
|
||||||
|
#define PROCESS_STACK_SIZE 0x10000 // 64kb
|
||||||
|
#define PROCESS_BASE 0x400000
|
||||||
|
#define PROCESS_STACK_BASE 0x1000000
|
||||||
|
|
||||||
|
/* sched */
|
||||||
|
// 1 tick = 1 ms => quantum = 10ms
|
||||||
|
#define SCHEDULER_QUANTUM 10
|
||||||
|
|
||||||
|
/* kernel */
|
||||||
|
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
|
||||||
|
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
|
||||||
|
#define KERNEL_SIZE 0x200000
|
||||||
|
#define KERNEL_STACK_SIZE 65536
|
||||||
|
#define KERNEL_IDT_ENTRIES 33
|
||||||
|
|
||||||
|
/* paging */
|
||||||
|
#define PAGING_MAX_PHYS 0x200000000
|
||||||
|
|
||||||
|
/* heap */
|
||||||
|
#define KHEAP_SIZE (32*1024*1024)
|
||||||
|
|
||||||
|
/* term */
|
||||||
|
#define TERM_HISTORY_MAX_LINES 256
|
||||||
|
|
||||||
|
/* kbd */
|
||||||
|
#define KBD_BUFFER_MAX 256
|
||||||
|
|
||||||
|
/* time */
|
||||||
|
#define TIMER_FREQUENCY 1000
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,21 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PS/2 Keyboard driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef PS2_H
|
#ifndef PS2_H
|
||||||
#define PS2_H
|
#define PS2_H
|
||||||
|
|
||||||
void keyboard_handler();
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void keyboard_handler(void);
|
||||||
|
char keyboard_getchar();
|
||||||
|
int keyboard_putchar(char c);
|
||||||
|
int keyboard_getline(char* output, size_t size);
|
||||||
|
|
||||||
#define SHIFT_PRESSED_BIT 0b00000001
|
#define SHIFT_PRESSED_BIT 0b00000001
|
||||||
#define ALT_PRESSED_BIT 0b00000010
|
#define ALT_PRESSED_BIT 0b00000010
|
||||||
#define CTRL_PRESSED_BIT 0b00000100
|
#define CTRL_PRESSED_BIT 0b00000100
|
||||||
|
|
||||||
enum SpecialKeys
|
enum SpecialKeys {
|
||||||
{
|
|
||||||
SHIFT = 255,
|
SHIFT = 255,
|
||||||
ALT = 254,
|
ALT = 254,
|
||||||
CTRL = 253
|
CTRL = 253
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SpecialScancodes
|
enum SpecialScancodes {
|
||||||
{
|
|
||||||
LEFT_SHIFT_PRESSED = 0x2A,
|
LEFT_SHIFT_PRESSED = 0x2A,
|
||||||
LEFT_SHIFT_RELEASED = 0xAA,
|
LEFT_SHIFT_RELEASED = 0xAA,
|
||||||
RIGHT_SHIFT_PRESSED = 0x36,
|
RIGHT_SHIFT_PRESSED = 0x36,
|
||||||
@@ -26,8 +35,7 @@ enum SpecialScancodes
|
|||||||
ALT_RELEASED = 0xB8
|
ALT_RELEASED = 0xB8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum KeyboardLayout
|
enum KeyboardLayout {
|
||||||
{
|
|
||||||
US,
|
US,
|
||||||
FR
|
FR
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Debug serial driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef SERIAL_H
|
#ifndef SERIAL_H
|
||||||
#define SERIAL_H
|
#define SERIAL_H
|
||||||
|
|
||||||
|
// COM1
|
||||||
|
#define PORT 0x3F8
|
||||||
|
|
||||||
void outb(int port, unsigned char data);
|
void outb(int port, unsigned char data);
|
||||||
unsigned char inb(int port);
|
unsigned char inb(int port);
|
||||||
|
|
||||||
int serial_init();
|
int serial_init(void);
|
||||||
void skputs(const char* str);
|
void skputs(const char* str);
|
||||||
void skputc(char c);
|
void skputc(char c);
|
||||||
|
|
||||||
72
include/io/term/flanterm.h
Normal file
72
include/io/term/flanterm.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_H
|
||||||
|
#define FLANTERM_H 1
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_CB_DEC 10
|
||||||
|
#define FLANTERM_CB_BELL 20
|
||||||
|
#define FLANTERM_CB_PRIVATE_ID 30
|
||||||
|
#define FLANTERM_CB_STATUS_REPORT 40
|
||||||
|
#define FLANTERM_CB_POS_REPORT 50
|
||||||
|
#define FLANTERM_CB_KBD_LEDS 60
|
||||||
|
#define FLANTERM_CB_MODE 70
|
||||||
|
#define FLANTERM_CB_LINUX 80
|
||||||
|
#define FLANTERM_CB_OSC 90
|
||||||
|
|
||||||
|
#ifdef FLANTERM_IN_FLANTERM
|
||||||
|
|
||||||
|
#include "flanterm_private.h"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct flanterm_context;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count);
|
||||||
|
void flanterm_flush(struct flanterm_context *ctx);
|
||||||
|
void flanterm_full_refresh(struct flanterm_context *ctx);
|
||||||
|
void flanterm_deinit(struct flanterm_context *ctx, void (*_free)(void *ptr, size_t size));
|
||||||
|
|
||||||
|
void flanterm_get_dimensions(struct flanterm_context *ctx, size_t *cols, size_t *rows);
|
||||||
|
void flanterm_set_autoflush(struct flanterm_context *ctx, bool state);
|
||||||
|
void flanterm_set_callback(struct flanterm_context *ctx, void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t));
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
79
include/io/term/flanterm_backends/fb.h
Normal file
79
include/io/term/flanterm_backends/fb.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_FB_H
|
||||||
|
#define FLANTERM_FB_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../flanterm.h"
|
||||||
|
|
||||||
|
#ifdef FLANTERM_IN_FLANTERM
|
||||||
|
|
||||||
|
#include "fb_private.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_FB_ROTATE_0 0
|
||||||
|
#define FLANTERM_FB_ROTATE_90 1
|
||||||
|
#define FLANTERM_FB_ROTATE_180 2
|
||||||
|
#define FLANTERM_FB_ROTATE_270 3
|
||||||
|
|
||||||
|
struct flanterm_context *flanterm_fb_init(
|
||||||
|
/* If _malloc and _free are nulled, use the bump allocated instance (1 use only). */
|
||||||
|
void *(*_malloc)(size_t size),
|
||||||
|
void (*_free)(void *ptr, size_t size),
|
||||||
|
uint32_t *framebuffer, size_t width, size_t height, size_t pitch,
|
||||||
|
uint8_t red_mask_size, uint8_t red_mask_shift,
|
||||||
|
uint8_t green_mask_size, uint8_t green_mask_shift,
|
||||||
|
uint8_t blue_mask_size, uint8_t blue_mask_shift,
|
||||||
|
uint32_t *canvas, /* If nulled, no canvas. */
|
||||||
|
uint32_t *ansi_colours, uint32_t *ansi_bright_colours, /* If nulled, default. */
|
||||||
|
uint32_t *default_bg, uint32_t *default_fg, /* If nulled, default. */
|
||||||
|
uint32_t *default_bg_bright, uint32_t *default_fg_bright, /* If nulled, default. */
|
||||||
|
/* If font is null, use default font and font_width and font_height ignored. */
|
||||||
|
void *font, size_t font_width, size_t font_height, size_t font_spacing,
|
||||||
|
/* If scale_x and scale_y are 0, automatically scale font based on resolution. */
|
||||||
|
size_t font_scale_x, size_t font_scale_y,
|
||||||
|
size_t margin,
|
||||||
|
/* One of FLANTERM_FB_ROTATE_* values. */
|
||||||
|
int rotation
|
||||||
|
);
|
||||||
|
|
||||||
|
void flanterm_fb_set_flush_callback(struct flanterm_context *ctx, void (*flush_callback)(volatile void *address, size_t length));
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
127
include/io/term/flanterm_backends/fb_private.h
Normal file
127
include/io/term/flanterm_backends/fb_private.h
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_FB_PRIVATE_H
|
||||||
|
#define FLANTERM_FB_PRIVATE_H 1
|
||||||
|
|
||||||
|
#ifndef FLANTERM_IN_FLANTERM
|
||||||
|
#error "Do not use fb_private.h. Use interfaces defined in fb.h only."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_FB_FONT_GLYPHS 256
|
||||||
|
|
||||||
|
struct flanterm_fb_char {
|
||||||
|
uint32_t c;
|
||||||
|
uint32_t fg;
|
||||||
|
uint32_t bg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flanterm_fb_queue_item {
|
||||||
|
size_t x, y;
|
||||||
|
struct flanterm_fb_char c;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flanterm_fb_context {
|
||||||
|
struct flanterm_context term;
|
||||||
|
|
||||||
|
void (*plot_char)(struct flanterm_context *ctx, struct flanterm_fb_char *c, size_t x, size_t y);
|
||||||
|
void (*flush_callback)(volatile void *address, size_t length);
|
||||||
|
|
||||||
|
size_t font_width;
|
||||||
|
size_t font_height;
|
||||||
|
size_t glyph_width;
|
||||||
|
size_t glyph_height;
|
||||||
|
|
||||||
|
size_t font_scale_x;
|
||||||
|
size_t font_scale_y;
|
||||||
|
|
||||||
|
size_t offset_x, offset_y;
|
||||||
|
|
||||||
|
volatile uint32_t *framebuffer;
|
||||||
|
size_t pitch;
|
||||||
|
size_t width;
|
||||||
|
size_t height;
|
||||||
|
size_t phys_height;
|
||||||
|
size_t bpp;
|
||||||
|
|
||||||
|
uint8_t red_mask_size, red_mask_shift;
|
||||||
|
uint8_t green_mask_size, green_mask_shift;
|
||||||
|
uint8_t blue_mask_size, blue_mask_shift;
|
||||||
|
|
||||||
|
int rotation;
|
||||||
|
|
||||||
|
size_t font_bits_size;
|
||||||
|
uint8_t *font_bits;
|
||||||
|
size_t font_bool_size;
|
||||||
|
bool *font_bool;
|
||||||
|
|
||||||
|
uint32_t ansi_colours[8];
|
||||||
|
uint32_t ansi_bright_colours[8];
|
||||||
|
uint32_t default_fg, default_bg;
|
||||||
|
uint32_t default_fg_bright, default_bg_bright;
|
||||||
|
|
||||||
|
size_t canvas_size;
|
||||||
|
uint32_t *canvas;
|
||||||
|
|
||||||
|
size_t grid_size;
|
||||||
|
size_t queue_size;
|
||||||
|
size_t map_size;
|
||||||
|
|
||||||
|
struct flanterm_fb_char *grid;
|
||||||
|
|
||||||
|
struct flanterm_fb_queue_item *queue;
|
||||||
|
size_t queue_i;
|
||||||
|
|
||||||
|
struct flanterm_fb_queue_item **map;
|
||||||
|
|
||||||
|
uint32_t text_fg;
|
||||||
|
uint32_t text_bg;
|
||||||
|
size_t cursor_x;
|
||||||
|
size_t cursor_y;
|
||||||
|
|
||||||
|
uint32_t saved_state_text_fg;
|
||||||
|
uint32_t saved_state_text_bg;
|
||||||
|
size_t saved_state_cursor_x;
|
||||||
|
size_t saved_state_cursor_y;
|
||||||
|
|
||||||
|
size_t old_cursor_x;
|
||||||
|
size_t old_cursor_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
133
include/io/term/flanterm_private.h
Normal file
133
include/io/term/flanterm_private.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
|
||||||
|
/* Copyright (C) 2022-2026 Mintsuki and contributors.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLANTERM_PRIVATE_H
|
||||||
|
#define FLANTERM_PRIVATE_H 1
|
||||||
|
|
||||||
|
#ifndef FLANTERM_IN_FLANTERM
|
||||||
|
#error "Do not use flanterm_private.h. Use interfaces defined in flanterm.h only."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FLANTERM_MAX_ESC_VALUES 16
|
||||||
|
|
||||||
|
struct flanterm_context {
|
||||||
|
/* internal use */
|
||||||
|
|
||||||
|
size_t tab_size;
|
||||||
|
bool autoflush;
|
||||||
|
bool cursor_enabled;
|
||||||
|
bool scroll_enabled;
|
||||||
|
bool wrap_enabled;
|
||||||
|
bool origin_mode;
|
||||||
|
bool control_sequence;
|
||||||
|
bool escape;
|
||||||
|
bool osc;
|
||||||
|
bool osc_escape;
|
||||||
|
size_t osc_buf_i;
|
||||||
|
uint8_t osc_buf[256];
|
||||||
|
bool rrr;
|
||||||
|
bool discard_next;
|
||||||
|
bool bold;
|
||||||
|
bool bg_bold;
|
||||||
|
bool reverse_video;
|
||||||
|
bool dec_private;
|
||||||
|
bool insert_mode;
|
||||||
|
bool csi_unhandled;
|
||||||
|
uint64_t code_point;
|
||||||
|
size_t unicode_remaining;
|
||||||
|
uint8_t g_select;
|
||||||
|
uint8_t charsets[2];
|
||||||
|
size_t current_charset;
|
||||||
|
size_t escape_offset;
|
||||||
|
size_t esc_values_i;
|
||||||
|
size_t saved_cursor_x;
|
||||||
|
size_t saved_cursor_y;
|
||||||
|
size_t current_primary;
|
||||||
|
size_t current_bg;
|
||||||
|
size_t scroll_top_margin;
|
||||||
|
size_t scroll_bottom_margin;
|
||||||
|
uint32_t esc_values[FLANTERM_MAX_ESC_VALUES];
|
||||||
|
uint8_t last_printed_char;
|
||||||
|
bool last_was_graphic;
|
||||||
|
bool saved_state_bold;
|
||||||
|
bool saved_state_bg_bold;
|
||||||
|
bool saved_state_reverse_video;
|
||||||
|
bool saved_state_origin_mode;
|
||||||
|
bool saved_state_wrap_enabled;
|
||||||
|
size_t saved_state_current_charset;
|
||||||
|
uint8_t saved_state_charsets[2];
|
||||||
|
size_t saved_state_current_primary;
|
||||||
|
size_t saved_state_current_bg;
|
||||||
|
|
||||||
|
/* to be set by backend */
|
||||||
|
|
||||||
|
size_t rows, cols;
|
||||||
|
|
||||||
|
void (*raw_putchar)(struct flanterm_context *, uint8_t c);
|
||||||
|
void (*clear)(struct flanterm_context *, bool move);
|
||||||
|
void (*set_cursor_pos)(struct flanterm_context *, size_t x, size_t y);
|
||||||
|
void (*get_cursor_pos)(struct flanterm_context *, size_t *x, size_t *y);
|
||||||
|
void (*set_text_fg)(struct flanterm_context *, size_t fg);
|
||||||
|
void (*set_text_bg)(struct flanterm_context *, size_t bg);
|
||||||
|
void (*set_text_fg_bright)(struct flanterm_context *, size_t fg);
|
||||||
|
void (*set_text_bg_bright)(struct flanterm_context *, size_t bg);
|
||||||
|
void (*set_text_fg_rgb)(struct flanterm_context *, uint32_t fg);
|
||||||
|
void (*set_text_bg_rgb)(struct flanterm_context *, uint32_t bg);
|
||||||
|
void (*set_text_fg_default)(struct flanterm_context *);
|
||||||
|
void (*set_text_bg_default)(struct flanterm_context *);
|
||||||
|
void (*set_text_fg_default_bright)(struct flanterm_context *);
|
||||||
|
void (*set_text_bg_default_bright)(struct flanterm_context *);
|
||||||
|
void (*move_character)(struct flanterm_context *, size_t new_x, size_t new_y, size_t old_x, size_t old_y);
|
||||||
|
void (*scroll)(struct flanterm_context *);
|
||||||
|
void (*revscroll)(struct flanterm_context *);
|
||||||
|
void (*swap_palette)(struct flanterm_context *);
|
||||||
|
void (*save_state)(struct flanterm_context *);
|
||||||
|
void (*restore_state)(struct flanterm_context *);
|
||||||
|
void (*double_buffer_flush)(struct flanterm_context *);
|
||||||
|
void (*full_refresh)(struct flanterm_context *);
|
||||||
|
void (*deinit)(struct flanterm_context *, void (*)(void *, size_t));
|
||||||
|
|
||||||
|
/* to be set by client */
|
||||||
|
|
||||||
|
void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
void flanterm_context_reinit(struct flanterm_context *ctx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
1597
include/io/term/nanoprintf.h
Normal file
1597
include/io/term/nanoprintf.h
Normal file
File diff suppressed because it is too large
Load Diff
15
include/io/term/term.h
Normal file
15
include/io/term/term.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Framebuffer-based terminal driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TERM_H
|
||||||
|
#define TERM_H
|
||||||
|
|
||||||
|
void kputs(const char* str);
|
||||||
|
void term_init(void);
|
||||||
|
int printf(const char* fmt, ...);
|
||||||
|
void internal_putc(int c, void *_);
|
||||||
|
|
||||||
|
#endif
|
||||||
63
include/kernel.h
Normal file
63
include/kernel.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel global macros
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KERNEL_H
|
||||||
|
#define KERNEL_H
|
||||||
|
|
||||||
|
enum ErrorCodes {
|
||||||
|
ENOMEM,
|
||||||
|
EIO
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CLEAR_INTERRUPTS __asm__ volatile("cli")
|
||||||
|
#define SET_INTERRUPTS __asm__ volatile("sti")
|
||||||
|
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
#include <io/term/term.h>
|
||||||
|
#include <arch/x86.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern volatile uint64_t ticks;
|
||||||
|
#define DEBUG(log, ...) printf("[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/* #define DEBUG(log, ...) \
|
||||||
|
printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \
|
||||||
|
fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DIE_DEBUG(str) printf(str)
|
||||||
|
|
||||||
|
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
|
||||||
|
|
||||||
|
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
|
||||||
|
|
||||||
|
void panic(struct cpu_status_t* ctx, const char* str);
|
||||||
|
void hcf(void);
|
||||||
|
void idle(void);
|
||||||
|
|
||||||
|
/* debug */
|
||||||
|
void debug_stack_trace(unsigned int max_frames);
|
||||||
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset);
|
||||||
|
void boot_mem_display(void);
|
||||||
|
|
||||||
|
#define assert(check) do { if(!(check)) hcf(); } while(0)
|
||||||
|
|
||||||
|
struct boot_context {
|
||||||
|
struct limine_framebuffer* fb;
|
||||||
|
struct limine_memmap_response* mmap;
|
||||||
|
struct limine_hhdm_response* hhdm;
|
||||||
|
struct limine_kernel_address_response* kaddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Are these modules initialized yet?
|
||||||
|
struct init_status {
|
||||||
|
bool terminal;
|
||||||
|
bool serial;
|
||||||
|
bool keyboard;
|
||||||
|
bool timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Global Descriptor Table (for legacy reasons)
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef GDT_H
|
#ifndef GDT_H
|
||||||
#define GDT_H
|
#define GDT_H
|
||||||
|
|
||||||
@@ -15,12 +21,11 @@
|
|||||||
#define USER_CODE_SEGMENT 0x18
|
#define USER_CODE_SEGMENT 0x18
|
||||||
#define USER_DATA_SEGMENT 0x20
|
#define USER_DATA_SEGMENT 0x20
|
||||||
|
|
||||||
struct GDTR
|
struct GDTR {
|
||||||
{
|
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
void gdt_init();
|
void gdt_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel heap
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef KHEAP_H
|
#ifndef KHEAP_H
|
||||||
#define KHEAP_H
|
#define KHEAP_H
|
||||||
|
|
||||||
@@ -6,21 +12,21 @@
|
|||||||
// When the kernel heap is ready, we can alloc our VM object linked list
|
// When the kernel heap is ready, we can alloc our VM object linked list
|
||||||
// and then continue working on the VMM.
|
// and then continue working on the VMM.
|
||||||
|
|
||||||
// 16MB should be enough for some linked lists
|
|
||||||
#define KHEAP_SIZE (16*1024*1024)
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
struct heap_block_t
|
struct heap_block_t {
|
||||||
{
|
|
||||||
size_t size;
|
size_t size;
|
||||||
bool free;
|
bool free; // 1byte
|
||||||
|
uint8_t reserved[7]; // (7+1 = 8 bytes)
|
||||||
struct heap_block_t* next;
|
struct heap_block_t* next;
|
||||||
};
|
} __attribute__((aligned(16)));
|
||||||
|
|
||||||
void kheap_init();
|
void kheap_init(void);
|
||||||
void* kmalloc(size_t size);
|
void* kmalloc(size_t size);
|
||||||
void kfree(void* ptr);
|
void kfree(void* ptr);
|
||||||
|
void* kalloc_stack(void);
|
||||||
|
void kheap_map_page(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,24 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x64 4-level paging implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef PAGING_H
|
#ifndef PAGING_H
|
||||||
#define PAGING_H
|
#define PAGING_H
|
||||||
|
|
||||||
#define PAGE_SIZE 4096
|
#define PAGE_SIZE 4096
|
||||||
#define BITS_PER_ROW 64
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
|
#include <mem/kheap.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb);
|
void paging_init(struct boot_context boot_ctx);
|
||||||
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
|
||||||
|
|
||||||
|
// To swap root page tables
|
||||||
|
void load_cr3(uint64_t value);
|
||||||
|
|
||||||
extern uint64_t hhdm_off;
|
extern uint64_t hhdm_off;
|
||||||
|
|
||||||
#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off))
|
#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off))
|
||||||
#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off)
|
#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off)
|
||||||
|
|
||||||
|
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000
|
||||||
// Stole it
|
// Stole it
|
||||||
#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
|
#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
|
||||||
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
|
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
|
||||||
#define PAGE_ALIGN_DOWN(x) ((x) & ~0xFFFULL)
|
#define PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK)
|
||||||
|
|
||||||
|
#define ALIGN(size) ALIGN_UP(size, 16)
|
||||||
|
#define BLOCK_MIN_SIZE (sizeof(struct heap_block_t) + 16)
|
||||||
|
|
||||||
#define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
|
#define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
|
||||||
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
|
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
|
||||||
@@ -27,18 +41,15 @@ extern uint64_t hhdm_off;
|
|||||||
|
|
||||||
// Page entry special bits
|
// Page entry special bits
|
||||||
// Bits set on a parent (directory, table) fall back to their children
|
// Bits set on a parent (directory, table) fall back to their children
|
||||||
#define PTE_PRESENT (1ULL << 0)
|
enum PTE_FLAGS
|
||||||
#define PTE_WRITABLE (1ULL << 1)
|
{
|
||||||
#define PTE_USER (1ULL << 2)
|
PTE_PRESENT = (1ULL << 0),
|
||||||
#define PTE_PWT (1ULL << 3)
|
PTE_WRITABLE = (1ULL << 1),
|
||||||
#define PTE_PCD (1ULL << 4)
|
PTE_USER = (1ULL << 2),
|
||||||
#define PTE_HUGE (1ULL << 7)
|
PTE_PWT = (1ULL << 3),
|
||||||
#define PTE_NOEXEC (1ULL << 63)
|
PTE_PCD = (1ULL << 4),
|
||||||
|
PTE_HUGE = (1ULL << 7),
|
||||||
// Specified in linker.ld
|
PTE_NOEXEC = (1ULL << 63)
|
||||||
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
|
};
|
||||||
|
|
||||||
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
|
|
||||||
#define KERNEL_SIZE 0x200000
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
17
include/mem/pmm.h
Normal file
17
include/mem/pmm.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Physical memory manager from freelist
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PAGING_PMM_H
|
||||||
|
#define PAGING_PMM_H
|
||||||
|
|
||||||
|
#include <limine.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
void pmm_init(struct boot_context boot_ctx);
|
||||||
|
void pmm_free(uintptr_t addr);
|
||||||
|
uintptr_t pmm_alloc(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Common memory utilities
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MEM_UTILS_H
|
#ifndef MEM_UTILS_H
|
||||||
#define MEM_UTILS_H
|
#define MEM_UTILS_H
|
||||||
|
|
||||||
@@ -8,6 +14,7 @@ void* memset(void* s, int c, size_t n);
|
|||||||
void* memmove(void *dest, const void* src, size_t n);
|
void* memmove(void *dest, const void* src, size_t n);
|
||||||
int memcmp(const void* s1, const void* s2, size_t n);
|
int memcmp(const void* s1, const void* s2, size_t n);
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
void memmap_display(struct limine_memmap_response* response);
|
void memmap_display(struct limine_memmap_response* response);
|
||||||
void hhdm_display(struct limine_hhdm_response* hhdm);
|
void hhdm_display(struct limine_hhdm_response* hhdm);
|
||||||
|
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Virtual memory manager
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef VMM_H
|
#ifndef VMM_H
|
||||||
#define VMM_H
|
#define VMM_H
|
||||||
|
|
||||||
@@ -10,8 +16,7 @@ Flags here aren't x86 flags, they are platform-agnostic
|
|||||||
kernel-defined flags.
|
kernel-defined flags.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct vm_object
|
struct vm_object {
|
||||||
{
|
|
||||||
uintptr_t base;
|
uintptr_t base;
|
||||||
size_t length;
|
size_t length;
|
||||||
size_t flags;
|
size_t flags;
|
||||||
@@ -24,6 +29,6 @@ struct vm_object
|
|||||||
#define VM_FLAG_EXEC (1 << 1)
|
#define VM_FLAG_EXEC (1 << 1)
|
||||||
#define VM_FLAG_USER (1 << 2)
|
#define VM_FLAG_USER (1 << 2)
|
||||||
|
|
||||||
void vmm_init();
|
void vmm_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
39
include/sched/process.h
Normal file
39
include/sched/process.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Process definition
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROCESS_H
|
||||||
|
#define PROCESS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
READY,
|
||||||
|
RUNNING,
|
||||||
|
DEAD
|
||||||
|
} status_t;
|
||||||
|
|
||||||
|
struct process_t {
|
||||||
|
size_t pid;
|
||||||
|
char name[PROCESS_NAME_MAX];
|
||||||
|
|
||||||
|
status_t status;
|
||||||
|
struct cpu_status_t* context;
|
||||||
|
void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511]
|
||||||
|
struct process_t* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void process_init(void);
|
||||||
|
struct process_t* process_create(char* name, void(*function)(void*), void* arg);
|
||||||
|
void process_add(struct process_t** processes_list, struct process_t* process);
|
||||||
|
void process_delete(struct process_t** processes_list, struct process_t* process);
|
||||||
|
struct process_t* process_get_next(struct process_t* process);
|
||||||
|
void process_exit(void);
|
||||||
|
|
||||||
|
void process_display_list(struct process_t* processes_list);
|
||||||
|
|
||||||
|
#endif
|
||||||
13
include/sched/scheduler.h
Normal file
13
include/sched/scheduler.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Round-robin scheduler
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCHEDULER_H
|
||||||
|
#define SCHEDULER_H
|
||||||
|
|
||||||
|
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context);
|
||||||
|
void scheduler_init(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
22
include/sched/spinlock.h
Normal file
22
include/sched/spinlock.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Spinlock implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPINLOCK_H
|
||||||
|
#define SPINLOCK_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct spinlock_t
|
||||||
|
{
|
||||||
|
bool locked;
|
||||||
|
uint64_t rflags;
|
||||||
|
};
|
||||||
|
|
||||||
|
void spinlock_acquire(struct spinlock_t* lock);
|
||||||
|
void spinlock_release(struct spinlock_t* lock);
|
||||||
|
|
||||||
|
#endif
|
||||||
16
include/string/string.h
Normal file
16
include/string/string.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief String manipulation functions
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STRING_H
|
||||||
|
#define STRING_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
char *strcpy(char *dest, const char *src);
|
||||||
|
char *strcat(char *dest, const char *src);
|
||||||
|
void strncpy(char* dst, const char* src, size_t n);
|
||||||
|
|
||||||
|
#endif
|
||||||
13
include/time/timer.h
Normal file
13
include/time/timer.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PIT functions
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
void timer_init(void);
|
||||||
|
void timer_wait(unsigned int wait_ticks);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
timeout: 3
|
timeout: 3
|
||||||
|
interface_branding: Welcome to the PepperOS disk!
|
||||||
|
|
||||||
/PepperOS
|
/PepperOS
|
||||||
protocol: limine
|
protocol: limine
|
||||||
|
|
||||||
|
comment: Default configuration (warning: spicy)
|
||||||
path: boot():/boot/pepperk
|
path: boot():/boot/pepperk
|
||||||
|
|||||||
21
src/arch/x86/cpuid.c
Normal file
21
src/arch/x86/cpuid.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x86 CPU identification
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cpuid - Wrapper for CPUID instruction
|
||||||
|
* @leaf: Requested leaf (input EAX)
|
||||||
|
* @eax: EAX register value (output)
|
||||||
|
* @ebx: EBX register value (output)
|
||||||
|
* @ecx: ECX register value (output)
|
||||||
|
* @edx: EDX register value (output)
|
||||||
|
*/
|
||||||
|
void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx)
|
||||||
|
{
|
||||||
|
__asm__ volatile("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(leaf));
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
; Assembly stub for the IDT
|
;
|
||||||
|
; @author xamidev <xamidev@riseup.net>
|
||||||
|
; @brief Stub for Interrupt Descriptor Table handlers
|
||||||
|
; @license GPL-3.0-only
|
||||||
|
;
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
|
|
||||||
@@ -147,12 +151,13 @@ vector_7_handler:
|
|||||||
align 16
|
align 16
|
||||||
vector_8_handler:
|
vector_8_handler:
|
||||||
; No error code, we only push vector number
|
; No error code, we only push vector number
|
||||||
push qword 1
|
push qword 8
|
||||||
jmp interrupt_stub
|
jmp interrupt_stub
|
||||||
|
|
||||||
; Coprocessor Segment Overrun
|
; Coprocessor Segment Overrun
|
||||||
align 16
|
align 16
|
||||||
vector_9_handler:
|
vector_9_handler:
|
||||||
|
push qword 0
|
||||||
push qword 9
|
push qword 9
|
||||||
jmp interrupt_stub
|
jmp interrupt_stub
|
||||||
|
|
||||||
271
src/arch/x86/idt.c
Normal file
271
src/arch/x86/idt.c
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Interrupt Descriptor Table setup and dispatching
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <arch/x86.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
#include <io/kbd/ps2.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sched/scheduler.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <sched/process.h>
|
||||||
|
|
||||||
|
struct interrupt_descriptor idt[256];
|
||||||
|
struct idtr idt_reg;
|
||||||
|
|
||||||
|
// Address to our first interrupt handler
|
||||||
|
extern char vector_0_handler[];
|
||||||
|
|
||||||
|
// Timer ticks
|
||||||
|
extern volatile uint64_t ticks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_set_entry - Sets an Interrupt Descriptor Table entry
|
||||||
|
* @vector: Vector number in the IDT
|
||||||
|
* @handler: Pointer to the executable Interrupt Service Routine
|
||||||
|
* @dpl: Desired privilege level
|
||||||
|
*/
|
||||||
|
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
||||||
|
{
|
||||||
|
uint64_t handler_addr = (uint64_t)handler;
|
||||||
|
|
||||||
|
struct interrupt_descriptor* entry = &idt[vector];
|
||||||
|
// Address is split in three parts so we right-shift progressively to get it all
|
||||||
|
entry->address_low = handler_addr & 0xFFFF;
|
||||||
|
entry->address_mid = (handler_addr >> 16) & 0xFFFF;
|
||||||
|
entry->address_high = handler_addr >> 32;
|
||||||
|
|
||||||
|
// Kernel code selector (as set in GDT)
|
||||||
|
entry->selector = 0x8;
|
||||||
|
// Interrupt gate, present, DPL (having: max DPL = 3)
|
||||||
|
entry->flags = 0b1110 | ((dpl & 0b11) << 5) | (1 << 7);
|
||||||
|
// We won't use IST for now
|
||||||
|
entry->ist = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_load - Loads the Interrupt Descriptor Table
|
||||||
|
* @idt_addr: Address to the IDT
|
||||||
|
*/
|
||||||
|
void idt_load(void* idt_addr)
|
||||||
|
{
|
||||||
|
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
|
||||||
|
idt_reg.limit = 0xFFF;
|
||||||
|
idt_reg.base = (uint64_t)idt_addr;
|
||||||
|
asm volatile("lidt %0" :: "m"(idt_reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_init - Initializes the Interrupt Descriptor Table
|
||||||
|
*
|
||||||
|
* Sets all IDT entries and their corresponding service routines,
|
||||||
|
* then loads it.
|
||||||
|
*/
|
||||||
|
void idt_init()
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<=KERNEL_IDT_ENTRIES; i++) {
|
||||||
|
// 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_load(&idt);
|
||||||
|
DEBUG("IDT initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read_cr2 - Reads the CR2 register
|
||||||
|
*
|
||||||
|
* This function is useful because it gets the address
|
||||||
|
* that the CPU tried to access in the case of a #PF.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %val - CR2 register value
|
||||||
|
*/
|
||||||
|
static inline uint64_t read_cr2(void)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
asm volatile ("mov %%cr2, %0" : "=r"(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* page_fault_handler - Handler for #PF
|
||||||
|
* @ctx: CPU context
|
||||||
|
*
|
||||||
|
* Shows detail about a #PF, especially what instruction (RIP)
|
||||||
|
* caused it, and what address access (CR2) caused it.
|
||||||
|
* Also displays an interpretation of the thrown error code.
|
||||||
|
* Then halts the system. We could implement demand paging later.
|
||||||
|
*/
|
||||||
|
static void page_fault_handler(struct cpu_status_t* ctx)
|
||||||
|
{
|
||||||
|
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
|
||||||
|
// interested in is getting more info out of those numbers cause i'm lost each time i have
|
||||||
|
// to read all this mess
|
||||||
|
uint64_t cr2 = read_cr2();
|
||||||
|
|
||||||
|
DEBUG("\x1b[38;5;231mPage Fault at rip=0x%p, err=%u (%s%s%s%s%s%s%s%s) when accessing addr=0x%p\x1b[0m", ctx->iret_rip, ctx->error_code,
|
||||||
|
CHECK_BIT(ctx->error_code, 0) ? "PAGE_PROTECTION_VIOLATION " : "PAGE_NOT_PRESENT ",
|
||||||
|
CHECK_BIT(ctx->error_code, 1) ? "ON_WRITE " : "ON_READ ",
|
||||||
|
CHECK_BIT(ctx->error_code, 2) ? "IN_USER_MODE" : "IN_KERNEL_MODE",
|
||||||
|
CHECK_BIT(ctx->error_code, 3) ? " WAS_RESERVED" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 4) ? " ON_INSTRUCTION_FETCH" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 5) ? " PK_VIOLATION" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 6) ? " ON_SHADOWSTACK_ACCESS" : "",
|
||||||
|
CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "",
|
||||||
|
cr2);
|
||||||
|
|
||||||
|
panic(ctx, "page fault");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gp_fault_handler - Handler for #GP
|
||||||
|
* @ctx: CPU context
|
||||||
|
*
|
||||||
|
* Shows detail about a General Protection Fault,
|
||||||
|
* and what may have caused it. Halts the system.
|
||||||
|
*/
|
||||||
|
static void gp_fault_handler(struct cpu_status_t* ctx)
|
||||||
|
{
|
||||||
|
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
|
||||||
|
ctx->iret_rip,
|
||||||
|
ctx->error_code,
|
||||||
|
(ctx->error_code == 0) ? "NOT_SEGMENT_RELATED" : "SEGMENT_RELATED");
|
||||||
|
|
||||||
|
// Segment-related
|
||||||
|
if (ctx->error_code != 0) {
|
||||||
|
bool is_external = CHECK_BIT(ctx->error_code, 0);
|
||||||
|
// is it IDT, GDT, LDT?
|
||||||
|
uint8_t table = ctx->error_code & 0x6; // 0b110 (isolate table)
|
||||||
|
uint16_t index = ctx->error_code & 0xFFF8; // 13*1 1111111111111 + 000 = 1111111111111000
|
||||||
|
|
||||||
|
char* table_names[4] = {"GDT", "IDT", "LDT", "IDT"};
|
||||||
|
|
||||||
|
DEBUG("\x1b[38;5;231m%s in %s index %u\x1b[0m",
|
||||||
|
is_external ? "EXTERNAL" : "INTERNAL",
|
||||||
|
table_names[table],
|
||||||
|
index);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(ctx, "gp fault");
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
void kbdproc_main(void* arg)
|
||||||
|
{
|
||||||
|
printf("Key pressed/released.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* interrupt_dispatch - Interrupt dispatcher
|
||||||
|
* @context: CPU context
|
||||||
|
*
|
||||||
|
* This function is where all interrupt routines go, after they passed
|
||||||
|
* through their corresponding vector handler in the IDT assembly stub.
|
||||||
|
* It catches all exceptions.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <context> - CPU context after interrupt
|
||||||
|
*/
|
||||||
|
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
||||||
|
{
|
||||||
|
if (context == NULL) {
|
||||||
|
panic(NULL, "Interrupt dispatch recieved NULL context!");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(context->vector_number) {
|
||||||
|
case 0:
|
||||||
|
panic(context, "Divide Error");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
panic(context, "Debug Exception");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
panic(context, "NMI Interrupt");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
panic(context, "Breakpoint Interrupt");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
panic(context, "Overflow Trap");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
panic(context, "BOUND Range Exceeded");
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
panic(context, "Invalid Opcode");
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
panic(context, "Device Not Available");
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
panic(context, "Double Fault");
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
panic(context, "Coprocessor Segment Overrun");
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
panic(context, "Invalid TSS");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
panic(context, "Segment Not Present");
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
panic(context, "Stack-Segment Fault");
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
gp_fault_handler(context);
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
page_fault_handler(context);
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
panic(context, "Intel Reserved Interrupt (Achievement unlocked: How Did We Get Here?)");
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
panic(context, "x87 Floating-Point Error");
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
panic(context, "Alignment Check Fault");
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
panic(context, "Machine Check");
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
panic(context, "SIMD Floating-Point Exception");
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
panic(context, "Virtualization Exception");
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
panic(context, "Control Protection Exception");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32: // Timer Interrupt
|
||||||
|
ticks++;
|
||||||
|
// Send an EOI so that we can continue having interrupts
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
|
||||||
|
if (ticks % SCHEDULER_QUANTUM == 0) {
|
||||||
|
return scheduler_schedule(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 33: // Keyboard Interrupt
|
||||||
|
keyboard_handler();
|
||||||
|
process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG("Unexpected Interrupt");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
46
src/arch/x86/init.c
Normal file
46
src/arch/x86/init.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x86 architecture-dependant initialization
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mem/gdt.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <arch/x86.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* x86_overwrite_pat - Set PAT to WC
|
||||||
|
*
|
||||||
|
* This function overwrites the 1st Page Attribute
|
||||||
|
* Table entry, to enable the Write-Combining property
|
||||||
|
* when we map memory regions later on.
|
||||||
|
* The framebuffer will be mapped with WC, which makes
|
||||||
|
* memory access significantly faster by using burst
|
||||||
|
* operations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void x86_overwrite_pat()
|
||||||
|
{
|
||||||
|
uint64_t pat = rdmsr(0x277);
|
||||||
|
pat &= ~(0xFFULL << 8); // Clear PAT1
|
||||||
|
pat |= (0x01ULL << 8); // PAT1 = 0x01 (WC)
|
||||||
|
wrmsr(0x277, pat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* x86_arch_init - Initialize x86 CPU structures
|
||||||
|
*
|
||||||
|
* This function is responsible for overriding a PAT entry
|
||||||
|
* (to put the framebuffer area in WC mode) only.
|
||||||
|
*
|
||||||
|
* Later, all architecture-dependant init (GDT, IDT, TSS, ...)
|
||||||
|
* should be initialized here, and separate function pointers
|
||||||
|
* should be set up for each arch.
|
||||||
|
*/
|
||||||
|
void x86_arch_init()
|
||||||
|
{
|
||||||
|
x86_overwrite_pat();
|
||||||
|
idt_init();
|
||||||
|
gdt_init();
|
||||||
|
}
|
||||||
66
src/arch/x86/msr.c
Normal file
66
src/arch/x86/msr.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x86 MSR C wrappers
|
||||||
|
* @description
|
||||||
|
* Wrapper functions to access Model Specific Registers
|
||||||
|
*
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <arch/x86.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rdmsr - Read from MSR
|
||||||
|
* @msr: model specific register number
|
||||||
|
*
|
||||||
|
* Read a 64-bit word from a Model Specific Register.
|
||||||
|
* Wrapper for the "rdmsr" instruction. It originally
|
||||||
|
* outputs to two 32-bit registers (EDX:EAX), so the
|
||||||
|
* function does the job of uniting them as a 64-bit
|
||||||
|
* value for us.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <value> - value read from MSR
|
||||||
|
*/
|
||||||
|
uint64_t rdmsr(uint32_t msr)
|
||||||
|
{
|
||||||
|
uint32_t low;
|
||||||
|
uint32_t high;
|
||||||
|
|
||||||
|
__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
|
||||||
|
|
||||||
|
return ((uint64_t)high << 32) | low;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wrmsr - Write to MSR
|
||||||
|
* @msr: model specific register number
|
||||||
|
*
|
||||||
|
* Write a 64-bit value to a Model Specific Register.
|
||||||
|
*/
|
||||||
|
void wrmsr(uint32_t msr, uint64_t value)
|
||||||
|
{
|
||||||
|
uint32_t low = (uint32_t)(value & 0xFFFFFFFF);
|
||||||
|
uint32_t high = (uint32_t)(value >> 32);
|
||||||
|
|
||||||
|
__asm__ volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* x86_has_msr - Test for MSR support
|
||||||
|
*
|
||||||
|
* Checks if CPU supports Model Specific Registers
|
||||||
|
* using CPUID.01h:EDX[bit 5].
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* true - MSR are supported
|
||||||
|
* false - MSR are not supported
|
||||||
|
*/
|
||||||
|
bool x86_has_msr()
|
||||||
|
{
|
||||||
|
uint32_t eax, ebx, ecx, edx;
|
||||||
|
cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||||
|
return (edx & (1 << 5)) != 0;
|
||||||
|
}
|
||||||
42
src/boot/boot.c
Normal file
42
src/boot/boot.c
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Limine requests for boot
|
||||||
|
* @description
|
||||||
|
* The kernel makes a few requests to the Limine bootloader
|
||||||
|
* in order to get precious information about the system.
|
||||||
|
* We get a framebuffer, a memory map, the address of the
|
||||||
|
* kernel in memory, and the Higher Half Direct Map offset.
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limine.h>
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_framebuffer_request framebuffer_request = {
|
||||||
|
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_memmap_request memmap_request = {
|
||||||
|
.id = LIMINE_MEMMAP_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_hhdm_request hhdm_request = {
|
||||||
|
.id = LIMINE_HHDM_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile struct limine_kernel_address_request kerneladdr_request = {
|
||||||
|
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests_start")))
|
||||||
|
volatile LIMINE_REQUESTS_START_MARKER;
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests_end")))
|
||||||
|
volatile LIMINE_REQUESTS_END_MARKER;
|
||||||
79
src/debug/misc.c
Normal file
79
src/debug/misc.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Miscellaneous debug features
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include <string/string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memmap_display - displays a memory map
|
||||||
|
* @response: Limine memory map response
|
||||||
|
*
|
||||||
|
* Displays the memory map we get from Limine
|
||||||
|
* to see different regions, their sizes, and
|
||||||
|
* how the memory is laid out at handoff.
|
||||||
|
*/
|
||||||
|
void memmap_display(struct limine_memmap_response* response)
|
||||||
|
{
|
||||||
|
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
|
||||||
|
|
||||||
|
for (size_t i=0; i<response->entry_count; i++) {
|
||||||
|
struct limine_memmap_entry* entry = response->entries[i];
|
||||||
|
char type[32] = {0};
|
||||||
|
switch(entry->type) {
|
||||||
|
case LIMINE_MEMMAP_USABLE:
|
||||||
|
strcpy(type, "USABLE");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_RESERVED:
|
||||||
|
strcpy(type, "RESERVED");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
||||||
|
strcpy(type, "ACPI_RECLAIMABLE");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_ACPI_NVS:
|
||||||
|
strcpy(type, "ACPI_NVS");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_BAD_MEMORY:
|
||||||
|
strcpy(type, "BAD_MEMORY");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
||||||
|
strcpy(type, "BOOTLOADER_RECLAIMABLE");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
|
||||||
|
strcpy(type, "KERNEL_AND_MODULES");
|
||||||
|
break;
|
||||||
|
case LIMINE_MEMMAP_FRAMEBUFFER:
|
||||||
|
strcpy(type, "FRAMEBUFFER");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strcpy(type, "UNKNOWN");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUG("Entry %02u: [0x%016x | %016u bytes] - %s", i, entry->base, entry->length, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hhdm_display - displays the HHDM offset
|
||||||
|
* @hhdm: Limine HHDM offset response
|
||||||
|
*/
|
||||||
|
void hhdm_display(struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* boot_mem_display - displays all memory info
|
||||||
|
*/
|
||||||
|
void boot_mem_display()
|
||||||
|
{
|
||||||
|
memmap_display(boot_ctx.mmap);
|
||||||
|
hhdm_display(boot_ctx.hhdm);
|
||||||
|
DEBUG("Kernel is at phys_base=0x%p virt_base=0x%p", boot_ctx.kaddr->physical_base, boot_ctx.kaddr->virtual_base);
|
||||||
|
}
|
||||||
71
src/debug/panic.c
Normal file
71
src/debug/panic.c
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel panic
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <arch/x86.h>
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
extern int panic_count;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reaf_rflags - provide easy reading of the RFLAGS register
|
||||||
|
* @rflags: RFLAGS register value
|
||||||
|
*/
|
||||||
|
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",
|
||||||
|
CHECK_BIT(rflags, 0) ? "CF " : "", /*carry flag*/
|
||||||
|
CHECK_BIT(rflags, 2) ? "PF " : "", /*parity flag*/
|
||||||
|
CHECK_BIT(rflags, 4) ? "AF " : "", /*auxiliary carry flag*/
|
||||||
|
CHECK_BIT(rflags, 6) ? "ZF " : "", /*zero flag*/
|
||||||
|
CHECK_BIT(rflags, 7) ? "SF " : "", /*sign flag*/
|
||||||
|
CHECK_BIT(rflags, 8) ? "TF " : "", /*trap flag*/
|
||||||
|
CHECK_BIT(rflags, 9) ? "IF " : "", /*interrupt enable flag*/
|
||||||
|
CHECK_BIT(rflags, 10) ? "DF " : "", /*direction flag*/
|
||||||
|
CHECK_BIT(rflags, 11) ? "OF " : "", /*overflow flag*/
|
||||||
|
(CHECK_BIT(rflags, 12) && CHECK_BIT(rflags, 13)) ? "IOPL3 " : "IOPL0 ", /*io privilege lvl*/
|
||||||
|
CHECK_BIT(rflags, 14) ? "NT " : "", /*nested task*/
|
||||||
|
CHECK_BIT(rflags, 16) ? "RF " : "", /*resume flag*/
|
||||||
|
CHECK_BIT(rflags, 17) ? "VM " : "", /*virtual 8086 mode*/
|
||||||
|
CHECK_BIT(rflags, 18) ? "AC " : "", /*alignment check/access control*/
|
||||||
|
CHECK_BIT(rflags, 19) ? "VIF " : "", /*virtual interrupt flag*/
|
||||||
|
CHECK_BIT(rflags, 20) ? "VIP " : "", /*virtual interrupt pending*/
|
||||||
|
CHECK_BIT(rflags, 21) ? "ID " : ""); /*id flag*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* panic - Kernel panic
|
||||||
|
* @ctx: CPU context (optional)
|
||||||
|
* @str: Error message
|
||||||
|
*
|
||||||
|
* Ends execution of the kernel in case of an unrecoverable error.
|
||||||
|
* Will display to terminal if it is initialized, otherwise serial only.
|
||||||
|
* Can be called with or without a CPU context.
|
||||||
|
*/
|
||||||
|
void panic(struct cpu_status_t* ctx, const char* str)
|
||||||
|
{
|
||||||
|
CLEAR_INTERRUPTS;
|
||||||
|
panic_count += 1;
|
||||||
|
if (ctx == NULL) {
|
||||||
|
printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232m Something went horribly wrong! (no cpu ctx)");
|
||||||
|
printf("\r\n%s\r\n\x1b[38;5;231m\x1b[0m", str);
|
||||||
|
debug_stack_trace(100);
|
||||||
|
hcf();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232mat rip=%p\r\nSomething went horribly wrong! (%s) vect=0x%.2x errcode=0x%x\n\rrax=%p rbx=%p rcx=%p rdx=%p\n\rrsi=%p rdi=%p r8=%p r9=%p\n\rr10=%p r11=%p r12=%p r13=%p\n\rr14=%p r15=%p\n\n\rflags=%p ",
|
||||||
|
ctx->iret_rip,
|
||||||
|
str,
|
||||||
|
ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi,
|
||||||
|
ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags);
|
||||||
|
|
||||||
|
read_rflags(ctx->iret_flags);
|
||||||
|
debug_stack_trace(100);
|
||||||
|
|
||||||
|
hcf();
|
||||||
|
}
|
||||||
96
src/debug/stacktrace.c
Normal file
96
src/debug/stacktrace.c
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Stack trace tools
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* debug_stack_trace - Prints the stack trace
|
||||||
|
* @max_frames: Maximum amount of stack frames to walk
|
||||||
|
*
|
||||||
|
* Walks back the stack and gets all return values (RIP)
|
||||||
|
* and prints them to the DEBUG interface.
|
||||||
|
*/
|
||||||
|
void debug_stack_trace(unsigned int max_frames)
|
||||||
|
{
|
||||||
|
printf("\r\n\x1b[48;5;232m\x1b[38;5;231m*** begin stack trace ***\r\n");
|
||||||
|
|
||||||
|
// Thanks GCC :)
|
||||||
|
uintptr_t* rbp = (uintptr_t*)__builtin_frame_address(0);
|
||||||
|
|
||||||
|
for (unsigned int frame=0; frame<max_frames && rbp != NULL; frame++) {
|
||||||
|
// Return address, 1 word above saved rbp
|
||||||
|
uintptr_t rip = rbp[1];
|
||||||
|
uintptr_t offset = 0;
|
||||||
|
const char* name = debug_find_symbol(rip, &offset);
|
||||||
|
printf("[%u] <0x%p> (%s+0x%x)\r\n", frame, (void*)rip, name, offset);
|
||||||
|
|
||||||
|
uintptr_t* next_rbp = (uintptr_t*)rbp[0];
|
||||||
|
|
||||||
|
// Invalid rbp or we're at the end
|
||||||
|
if (next_rbp <= rbp || next_rbp == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rbp = next_rbp;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("*** end stack trace ***\r\n[end Kernel panic]\r\nHalting system...\x1b[0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t addr;
|
||||||
|
const char *name;
|
||||||
|
} __attribute__((packed)) kernel_symbol_t;
|
||||||
|
|
||||||
|
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
|
||||||
|
__attribute__((weak)) extern uint64_t symbol_count;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* debug_find_symbol - Finds the symbol name associated to an address
|
||||||
|
* @rip: Pointer to executable code
|
||||||
|
* @offset: Out pointer to reference the offset in the found function, if any
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <symbol name> - symbol name
|
||||||
|
* "???" - no symbol table found
|
||||||
|
* "unknown" - symbol table found, but address isn't in the table
|
||||||
|
*/
|
||||||
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
||||||
|
{
|
||||||
|
if (!symbol_table || symbol_count == 0) {
|
||||||
|
if (offset) *offset = 0;
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
int low = 0, high = (int)symbol_count - 1;
|
||||||
|
int best = -1;
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
int mid = (low + high) / 2;
|
||||||
|
if (symbol_table[mid].addr <= rip) {
|
||||||
|
best = mid;
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best != -1) {
|
||||||
|
if (offset) {
|
||||||
|
*offset = rip - symbol_table[best].addr;
|
||||||
|
}
|
||||||
|
return symbol_table[best].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
*offset = 0;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
144
src/idt/idt.c
144
src/idt/idt.c
@@ -1,144 +0,0 @@
|
|||||||
#include "idt.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include "io/serial/serial.h"
|
|
||||||
#include "io/kbd/ps2.h"
|
|
||||||
#include <kernel.h>
|
|
||||||
|
|
||||||
struct interrupt_descriptor idt[256];
|
|
||||||
struct idtr idt_reg;
|
|
||||||
|
|
||||||
// Address to our first interrupt handler
|
|
||||||
extern char vector_0_handler[];
|
|
||||||
|
|
||||||
// Timer ticks
|
|
||||||
extern uint64_t ticks;
|
|
||||||
|
|
||||||
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
|
||||||
{
|
|
||||||
uint64_t handler_addr = (uint64_t)handler;
|
|
||||||
|
|
||||||
struct interrupt_descriptor* entry = &idt[vector];
|
|
||||||
// Address is split in three parts so we right-shift progressively to get it all
|
|
||||||
entry->address_low = handler_addr & 0xFFFF;
|
|
||||||
entry->address_mid = (handler_addr >> 16) & 0xFFFF;
|
|
||||||
entry->address_high = handler_addr >> 32;
|
|
||||||
|
|
||||||
// Kernel code selector (as set in GDT)
|
|
||||||
entry->selector = 0x8;
|
|
||||||
// Interrupt gate, present, DPL (having: max DPL = 3)
|
|
||||||
entry->flags = 0b1110 | ((dpl & 0b11) << 5) | (1 << 7);
|
|
||||||
// We won't use IST for now
|
|
||||||
entry->ist = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void idt_load(void* idt_addr)
|
|
||||||
{
|
|
||||||
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
|
|
||||||
idt_reg.limit = 0xFFF;
|
|
||||||
idt_reg.base = (uint64_t)idt_addr;
|
|
||||||
asm volatile("lidt %0" :: "m"(idt_reg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void idt_init()
|
|
||||||
{
|
|
||||||
// We set 256 entries, but we have only the first few stubs.
|
|
||||||
// Undefined behavior?
|
|
||||||
for (size_t i=0; i<256; i++)
|
|
||||||
{
|
|
||||||
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
|
|
||||||
idt_set_entry(i, vector_0_handler + (i*16), 0);
|
|
||||||
}
|
|
||||||
idt_load(&idt);
|
|
||||||
DEBUG("IDT initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
|
||||||
{
|
|
||||||
switch(context->vector_number)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
DEBUG("Divide Error!");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
DEBUG("Debug Exception!");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
DEBUG("NMI Interrupt!");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
DEBUG("Breakpoint Interrupt!");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
DEBUG("Overflow Trap!");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
DEBUG("BOUND Range Exceeded!");
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
DEBUG("Invalid Opcode!");
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
DEBUG("Device Not Available!");
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
DEBUG("Double Fault!");
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
DEBUG("Coprocessor Segment Overrun!");
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
DEBUG("Invalid TSS!");
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
DEBUG("Segment Not Present!");
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
DEBUG("Stack-Segment Fault!");
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
DEBUG("General Protection Fault!");
|
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
DEBUG("Page Fault!");
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
DEBUG("x87 Floating-Point Error!");
|
|
||||||
break;
|
|
||||||
case 17:
|
|
||||||
DEBUG("Alignment Check Fault!");
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
DEBUG("Machine Check!");
|
|
||||||
break;
|
|
||||||
case 19:
|
|
||||||
DEBUG("SIMD Floating-Point Exception!");
|
|
||||||
break;
|
|
||||||
case 20:
|
|
||||||
DEBUG("Virtualization Exception!");
|
|
||||||
break;
|
|
||||||
case 21:
|
|
||||||
DEBUG("Control Protection Exception!");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 32:
|
|
||||||
//DEBUG("Tick!");
|
|
||||||
ticks++;
|
|
||||||
// Send an EOI so that we can continue having interrupts
|
|
||||||
outb(0x20, 0x20);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 33:
|
|
||||||
keyboard_handler();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DEBUG("Unexpected interrupt");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
159
src/io/kbd/ps2.c
159
src/io/kbd/ps2.c
@@ -1,10 +1,16 @@
|
|||||||
// PS/2 Keyboard support
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PS/2 Keyboard driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include "io/serial/serial.h"
|
#include "config.h"
|
||||||
#include "ps2.h"
|
#include <io/serial/serial.h>
|
||||||
|
#include <io/kbd/ps2.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "io/term/term.h"
|
#include <io/term/term.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
// The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed
|
// The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed
|
||||||
uint8_t key_status = 0b00000000;
|
uint8_t key_status = 0b00000000;
|
||||||
@@ -13,6 +19,13 @@ uint8_t key_status = 0b00000000;
|
|||||||
unsigned char* keymap;
|
unsigned char* keymap;
|
||||||
unsigned char* keymap_shifted;
|
unsigned char* keymap_shifted;
|
||||||
|
|
||||||
|
// Keyboard buffer
|
||||||
|
char keyboard_buffer[KBD_BUFFER_MAX] = {0};
|
||||||
|
int write_index = 0;
|
||||||
|
int read_index = 0;
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
unsigned char kbdus[128] =
|
unsigned char kbdus[128] =
|
||||||
{
|
{
|
||||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
|
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
|
||||||
@@ -150,16 +163,22 @@ unsigned char kbdfr_shifted[128] =
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_handler - Keyboard event handler
|
||||||
|
*
|
||||||
|
* Is called from the interrupt dispatcher.
|
||||||
|
* When a key is pressed or released, we get a scancode, and
|
||||||
|
* it is then translated to an ASCII character.
|
||||||
|
* Left Shift, Ctrl, and Alt keys are also taken into consideration.
|
||||||
|
*/
|
||||||
void keyboard_handler()
|
void keyboard_handler()
|
||||||
{
|
{
|
||||||
unsigned char scancode = inb(0x60);
|
unsigned char scancode = inb(0x60);
|
||||||
|
|
||||||
// Key release (bit 7 set)
|
// Key release (bit 7 set)
|
||||||
if (scancode & 0x80)
|
if (scancode & 0x80) {
|
||||||
{
|
|
||||||
unsigned char code = scancode & 0x7F;
|
unsigned char code = scancode & 0x7F;
|
||||||
switch (code)
|
switch (code) {
|
||||||
{
|
|
||||||
// Clear the corresponding bit if corresponding key is released
|
// Clear the corresponding bit if corresponding key is released
|
||||||
case LEFT_SHIFT_PRESSED:
|
case LEFT_SHIFT_PRESSED:
|
||||||
case RIGHT_SHIFT_PRESSED:
|
case RIGHT_SHIFT_PRESSED:
|
||||||
@@ -172,16 +191,10 @@ void keyboard_handler()
|
|||||||
key_status &= ~ALT_PRESSED_BIT;
|
key_status &= ~ALT_PRESSED_BIT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send EOI
|
|
||||||
outb(0x20, 0x20);
|
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Key press
|
// Key press
|
||||||
switch (scancode)
|
switch (scancode) {
|
||||||
{
|
|
||||||
// Set bits for corresponding special key press
|
// Set bits for corresponding special key press
|
||||||
case LEFT_SHIFT_PRESSED:
|
case LEFT_SHIFT_PRESSED:
|
||||||
case RIGHT_SHIFT_PRESSED:
|
case RIGHT_SHIFT_PRESSED:
|
||||||
@@ -196,30 +209,110 @@ void keyboard_handler()
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
// Avoiding buffer overflow from extended keys lol
|
||||||
|
if (scancode < 128) {
|
||||||
// Should we get a SHIFTED char or a regular one?
|
// Should we get a SHIFTED char or a regular one?
|
||||||
unsigned char c = (key_status & SHIFT_PRESSED_BIT) ? keymap_shifted[scancode] : keymap[scancode];
|
unsigned char c = (key_status & SHIFT_PRESSED_BIT) ? keymap_shifted[scancode] : keymap[scancode];
|
||||||
|
|
||||||
if (c)
|
if (c) {
|
||||||
|
if (c == '\n') {
|
||||||
|
internal_putc('\r', NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal_putc(c, NULL);
|
||||||
|
keyboard_putchar(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_getchar - Get a character from keyboard
|
||||||
|
*
|
||||||
|
* This function reads one character from the keyboard buffer.
|
||||||
|
* If the keyboard buffer is empty, it will block until a key
|
||||||
|
* is pressed.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <char> - character from keyboard buffer
|
||||||
|
*/
|
||||||
|
char keyboard_getchar()
|
||||||
{
|
{
|
||||||
putchar(c);
|
while (read_index == write_index); // Empty buffer
|
||||||
}
|
|
||||||
}
|
char c = keyboard_buffer[read_index];
|
||||||
|
read_index = (read_index+1) % KBD_BUFFER_MAX;
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
skputs("key pressed!\n");
|
/*
|
||||||
|
* keyboard_putchar - Puts a character in the keyboard buffer
|
||||||
|
* @c: character to add
|
||||||
|
*
|
||||||
|
* This function is used in the keyboard handler to add new
|
||||||
|
* characters to the keyboard buffer.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %-1 - keyboard buffer is full
|
||||||
|
* %0 - operation completed successfully
|
||||||
|
*/
|
||||||
|
int keyboard_putchar(char c)
|
||||||
|
{
|
||||||
|
if ((write_index+1) % KBD_BUFFER_MAX == read_index) {
|
||||||
|
// Full buffer
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End of Interrupt (to master PIC)
|
keyboard_buffer[write_index] = c;
|
||||||
outb(0x20, 0x20);
|
write_index = (write_index+1) % KBD_BUFFER_MAX;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_getline - Gets a line of input from keyboard
|
||||||
|
* @output: Output string
|
||||||
|
* @size: Size of output string
|
||||||
|
*
|
||||||
|
* Read a line of characters from the keyboard, until the
|
||||||
|
* buffer fills or a newline character is read.
|
||||||
|
* The output string is NULL-terminated.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <num> - the number of characters read
|
||||||
|
*/
|
||||||
|
int keyboard_getline(char* output, size_t size)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
// Read until Enter is pressed
|
||||||
|
while ((c = keyboard_getchar()) != 0x0A) {
|
||||||
|
if (index == size-1) {
|
||||||
|
output[index] = c;
|
||||||
|
output[index+1] = '\0';
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
output[index] = c;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
output[index+1] = '\0';
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_init - Keyboard initialization
|
||||||
|
* @layout: Desired layout
|
||||||
|
*
|
||||||
|
* Prepares the PS/2 keyboard to recieve input.
|
||||||
|
*/
|
||||||
void keyboard_init(unsigned char layout)
|
void keyboard_init(unsigned char layout)
|
||||||
{
|
{
|
||||||
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
|
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
|
||||||
|
|
||||||
// Keyboard layout selection
|
// Keyboard layout selection
|
||||||
switch (layout)
|
switch (layout) {
|
||||||
{
|
|
||||||
case US:
|
case US:
|
||||||
keymap = kbdus;
|
keymap = kbdus;
|
||||||
keymap_shifted = kbdus_shifted;
|
keymap_shifted = kbdus_shifted;
|
||||||
@@ -230,8 +323,20 @@ void keyboard_init(unsigned char layout)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
skputs("Unsupported layout.");
|
panic(NULL, "Unsupported keyboard layout");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DEBUG("PS/2 Keyboard initialized");
|
|
||||||
|
// Flush keyboard buffer
|
||||||
|
while (inb(0x64) & 1) {
|
||||||
|
inb(0x60);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmask IRQ1
|
||||||
|
uint8_t mask = inb(0x21);
|
||||||
|
mask &= ~(1 << 1);
|
||||||
|
outb(0x21, mask);
|
||||||
|
|
||||||
|
DEBUG("PS/2 Keyboard initialized");
|
||||||
|
init.keyboard = true;
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,37 @@
|
|||||||
#include <kernel.h>
|
/*
|
||||||
#include "serial.h"
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Debug serial driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
#include <sched/spinlock.h>
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
extern int panic_count;
|
||||||
|
struct spinlock_t serial_lock = {0};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* outb - Writes a byte to a CPU port
|
||||||
|
* @port: CPU port to write to
|
||||||
|
* @data: Byte to write
|
||||||
|
*
|
||||||
|
* Writes a single byte to the serial interface.
|
||||||
|
*/
|
||||||
void outb(int port, unsigned char data)
|
void outb(int port, unsigned char data)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inb - Gets a byte in through a CPU port
|
||||||
|
* @port: The CPU port to get a byte from
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <data> - byte got from port
|
||||||
|
*/
|
||||||
unsigned char inb(int port)
|
unsigned char inb(int port)
|
||||||
{
|
{
|
||||||
unsigned char data = 0;
|
unsigned char data = 0;
|
||||||
@@ -13,9 +39,13 @@ unsigned char inb(int port)
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// COM1
|
/*
|
||||||
#define PORT 0x3F8
|
* serial_init - Initializes serial interface
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %-EIO - Input/output error
|
||||||
|
* %0 - Success
|
||||||
|
*/
|
||||||
int serial_init()
|
int serial_init()
|
||||||
{
|
{
|
||||||
outb(PORT + 1, 0x00); // Disable all interrupts
|
outb(PORT + 1, 0x00); // Disable all interrupts
|
||||||
@@ -28,36 +58,58 @@ int serial_init()
|
|||||||
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
||||||
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
||||||
|
|
||||||
if (inb(PORT) != 0xAE)
|
if (inb(PORT) != 0xAE) {
|
||||||
{
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set normal operation mode
|
// Set normal operation mode
|
||||||
outb(PORT + 4, 0x0F);
|
outb(PORT + 4, 0x0F);
|
||||||
|
|
||||||
DEBUG("serial initialized");
|
init.serial = true;
|
||||||
|
DEBUG("*** Welcome to PepperOS! (built @ %s %s) ***", __DATE__, __TIME__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_transmit_empty - Check if the serial transmit register is empty
|
||||||
|
*
|
||||||
|
* Return: Non-zero if the transmit register is empty and a new
|
||||||
|
* byte can be written to the serial port, 0 otherwise.
|
||||||
|
*/
|
||||||
static int is_transmit_empty()
|
static int is_transmit_empty()
|
||||||
{
|
{
|
||||||
return inb(PORT + 5) & 0x20;
|
return inb(PORT + 5) & 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial kernel putchar
|
/*
|
||||||
|
* skputc - Serial kernel putchar
|
||||||
|
* @c: character to write
|
||||||
|
*
|
||||||
|
* Writes a single character to the serial interface.
|
||||||
|
*/
|
||||||
void skputc(char c)
|
void skputc(char c)
|
||||||
{
|
{
|
||||||
|
if (panic_count == 0) {
|
||||||
|
spinlock_acquire(&serial_lock);
|
||||||
while (!is_transmit_empty()); // wait for free spot
|
while (!is_transmit_empty()); // wait for free spot
|
||||||
outb(PORT, c);
|
outb(PORT, c);
|
||||||
|
spinlock_release(&serial_lock);
|
||||||
|
} else {
|
||||||
|
while (!is_transmit_empty());
|
||||||
|
outb(PORT, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial kernel putstring
|
/*
|
||||||
|
* skputs - Serial kernel puts
|
||||||
|
* @str: Message to write
|
||||||
|
*
|
||||||
|
* Writes a non-formatted string to serial output.
|
||||||
|
*/
|
||||||
void skputs(const char* str)
|
void skputs(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int i=0;
|
unsigned int i=0;
|
||||||
while (str[i])
|
while (str[i]) {
|
||||||
{
|
|
||||||
skputc(str[i]);
|
skputc(str[i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|||||||
2129
src/io/term/flanterm.c
Normal file
2129
src/io/term/flanterm.c
Normal file
File diff suppressed because it is too large
Load Diff
1460
src/io/term/flanterm_backends/fb.c
Normal file
1460
src/io/term/flanterm_backends/fb.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,914 +0,0 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// \author (c) Marco Paland (info@paland.com)
|
|
||||||
// 2014-2019, PALANDesign Hannover, Germany
|
|
||||||
//
|
|
||||||
// \license The MIT License (MIT)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
|
|
||||||
// embedded systems with a very limited resources. These routines are thread
|
|
||||||
// safe and reentrant!
|
|
||||||
// Use this instead of the bloated standard/newlib printf cause these use
|
|
||||||
// malloc for printf (and may not be thread safe).
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
|
|
||||||
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
|
|
||||||
// printf_config.h header file
|
|
||||||
// default: undefined
|
|
||||||
#ifdef PRINTF_INCLUDE_CONFIG_H
|
|
||||||
#include "printf_config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
|
|
||||||
// numeric number including padded zeros (dynamically created on stack)
|
|
||||||
// default: 32 byte
|
|
||||||
#ifndef PRINTF_NTOA_BUFFER_SIZE
|
|
||||||
#define PRINTF_NTOA_BUFFER_SIZE 32U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
|
|
||||||
// float number including padded zeros (dynamically created on stack)
|
|
||||||
// default: 32 byte
|
|
||||||
#ifndef PRINTF_FTOA_BUFFER_SIZE
|
|
||||||
#define PRINTF_FTOA_BUFFER_SIZE 32U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// support for the floating point type (%f)
|
|
||||||
// default: activated
|
|
||||||
#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
|
|
||||||
#define PRINTF_SUPPORT_FLOAT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// support for exponential floating point notation (%e/%g)
|
|
||||||
// default: activated
|
|
||||||
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
|
|
||||||
#define PRINTF_SUPPORT_EXPONENTIAL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// define the default floating point precision
|
|
||||||
// default: 6 digits
|
|
||||||
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
|
|
||||||
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// define the largest float suitable to print with %f
|
|
||||||
// default: 1e9
|
|
||||||
#ifndef PRINTF_MAX_FLOAT
|
|
||||||
#define PRINTF_MAX_FLOAT 1e9
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// support for the long long types (%llu or %p)
|
|
||||||
// default: activated
|
|
||||||
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
|
|
||||||
#define PRINTF_SUPPORT_LONG_LONG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// support for the ptrdiff_t type (%t)
|
|
||||||
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
|
|
||||||
// default: activated
|
|
||||||
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
|
|
||||||
#define PRINTF_SUPPORT_PTRDIFF_T
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// internal flag definitions
|
|
||||||
#define FLAGS_ZEROPAD (1U << 0U)
|
|
||||||
#define FLAGS_LEFT (1U << 1U)
|
|
||||||
#define FLAGS_PLUS (1U << 2U)
|
|
||||||
#define FLAGS_SPACE (1U << 3U)
|
|
||||||
#define FLAGS_HASH (1U << 4U)
|
|
||||||
#define FLAGS_UPPERCASE (1U << 5U)
|
|
||||||
#define FLAGS_CHAR (1U << 6U)
|
|
||||||
#define FLAGS_SHORT (1U << 7U)
|
|
||||||
#define FLAGS_LONG (1U << 8U)
|
|
||||||
#define FLAGS_LONG_LONG (1U << 9U)
|
|
||||||
#define FLAGS_PRECISION (1U << 10U)
|
|
||||||
#define FLAGS_ADAPT_EXP (1U << 11U)
|
|
||||||
|
|
||||||
|
|
||||||
// import float.h for DBL_MAX
|
|
||||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
|
||||||
#include <float.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// output function type
|
|
||||||
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
|
|
||||||
|
|
||||||
|
|
||||||
// wrapper (used as buffer) for output function type
|
|
||||||
typedef struct {
|
|
||||||
void (*fct)(char character, void* arg);
|
|
||||||
void* arg;
|
|
||||||
} out_fct_wrap_type;
|
|
||||||
|
|
||||||
|
|
||||||
// internal buffer output
|
|
||||||
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
|
|
||||||
{
|
|
||||||
if (idx < maxlen) {
|
|
||||||
((char*)buffer)[idx] = character;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal null output
|
|
||||||
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
|
|
||||||
{
|
|
||||||
(void)character; (void)buffer; (void)idx; (void)maxlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal _putchar wrapper
|
|
||||||
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
|
|
||||||
{
|
|
||||||
(void)buffer; (void)idx; (void)maxlen;
|
|
||||||
if (character) {
|
|
||||||
_putchar(character);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal output function wrapper
|
|
||||||
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
|
|
||||||
{
|
|
||||||
(void)idx; (void)maxlen;
|
|
||||||
if (character) {
|
|
||||||
// buffer is the output fct pointer
|
|
||||||
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal secure strlen
|
|
||||||
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
|
|
||||||
static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
|
|
||||||
{
|
|
||||||
const char* s;
|
|
||||||
for (s = str; *s && maxsize--; ++s);
|
|
||||||
return (unsigned int)(s - str);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal test if char is a digit (0-9)
|
|
||||||
// \return true if char is a digit
|
|
||||||
static inline bool _is_digit(char ch)
|
|
||||||
{
|
|
||||||
return (ch >= '0') && (ch <= '9');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal ASCII string to unsigned int conversion
|
|
||||||
static unsigned int _atoi(const char** str)
|
|
||||||
{
|
|
||||||
unsigned int i = 0U;
|
|
||||||
while (_is_digit(**str)) {
|
|
||||||
i = i * 10U + (unsigned int)(*((*str)++) - '0');
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// output the specified string in reverse, taking care of any zero-padding
|
|
||||||
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
|
|
||||||
{
|
|
||||||
const size_t start_idx = idx;
|
|
||||||
|
|
||||||
// pad spaces up to given width
|
|
||||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
|
||||||
for (size_t i = len; i < width; i++) {
|
|
||||||
out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reverse string
|
|
||||||
while (len) {
|
|
||||||
out(buf[--len], buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append pad spaces up to given width
|
|
||||||
if (flags & FLAGS_LEFT) {
|
|
||||||
while (idx - start_idx < width) {
|
|
||||||
out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal itoa format
|
|
||||||
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
|
|
||||||
{
|
|
||||||
// pad leading zeros
|
|
||||||
if (!(flags & FLAGS_LEFT)) {
|
|
||||||
if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
|
|
||||||
width--;
|
|
||||||
}
|
|
||||||
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
|
||||||
buf[len++] = '0';
|
|
||||||
}
|
|
||||||
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
|
||||||
buf[len++] = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle hash
|
|
||||||
if (flags & FLAGS_HASH) {
|
|
||||||
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
|
|
||||||
len--;
|
|
||||||
if (len && (base == 16U)) {
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
|
||||||
buf[len++] = 'x';
|
|
||||||
}
|
|
||||||
else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
|
||||||
buf[len++] = 'X';
|
|
||||||
}
|
|
||||||
else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
|
||||||
buf[len++] = 'b';
|
|
||||||
}
|
|
||||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
|
||||||
buf[len++] = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
|
||||||
if (negative) {
|
|
||||||
buf[len++] = '-';
|
|
||||||
}
|
|
||||||
else if (flags & FLAGS_PLUS) {
|
|
||||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
|
||||||
}
|
|
||||||
else if (flags & FLAGS_SPACE) {
|
|
||||||
buf[len++] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal itoa for 'long' type
|
|
||||||
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
|
|
||||||
{
|
|
||||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
|
||||||
size_t len = 0U;
|
|
||||||
|
|
||||||
// no hash for 0 values
|
|
||||||
if (!value) {
|
|
||||||
flags &= ~FLAGS_HASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write if precision != 0 and value is != 0
|
|
||||||
if (!(flags & FLAGS_PRECISION) || value) {
|
|
||||||
do {
|
|
||||||
const char digit = (char)(value % base);
|
|
||||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
|
||||||
value /= base;
|
|
||||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// internal itoa for 'long long' type
|
|
||||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
|
||||||
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
|
|
||||||
{
|
|
||||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
|
||||||
size_t len = 0U;
|
|
||||||
|
|
||||||
// no hash for 0 values
|
|
||||||
if (!value) {
|
|
||||||
flags &= ~FLAGS_HASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write if precision != 0 and value is != 0
|
|
||||||
if (!(flags & FLAGS_PRECISION) || value) {
|
|
||||||
do {
|
|
||||||
const char digit = (char)(value % base);
|
|
||||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
|
||||||
value /= base;
|
|
||||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
|
||||||
}
|
|
||||||
#endif // PRINTF_SUPPORT_LONG_LONG
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
|
||||||
|
|
||||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
||||||
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
|
|
||||||
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// internal ftoa for fixed decimal floating point
|
|
||||||
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
|
|
||||||
{
|
|
||||||
char buf[PRINTF_FTOA_BUFFER_SIZE];
|
|
||||||
size_t len = 0U;
|
|
||||||
double diff = 0.0;
|
|
||||||
|
|
||||||
// powers of 10
|
|
||||||
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
|
||||||
|
|
||||||
// test for special values
|
|
||||||
if (value != value)
|
|
||||||
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
|
|
||||||
if (value < -DBL_MAX)
|
|
||||||
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
|
|
||||||
if (value > DBL_MAX)
|
|
||||||
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
|
|
||||||
|
|
||||||
// test for very large values
|
|
||||||
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
|
|
||||||
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
|
|
||||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
||||||
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
|
|
||||||
#else
|
|
||||||
return 0U;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// test for negative
|
|
||||||
bool negative = false;
|
|
||||||
if (value < 0) {
|
|
||||||
negative = true;
|
|
||||||
value = 0 - value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set default precision, if not set explicitly
|
|
||||||
if (!(flags & FLAGS_PRECISION)) {
|
|
||||||
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
|
||||||
}
|
|
||||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
|
||||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
|
|
||||||
buf[len++] = '0';
|
|
||||||
prec--;
|
|
||||||
}
|
|
||||||
|
|
||||||
int whole = (int)value;
|
|
||||||
double tmp = (value - whole) * pow10[prec];
|
|
||||||
unsigned long frac = (unsigned long)tmp;
|
|
||||||
diff = tmp - frac;
|
|
||||||
|
|
||||||
if (diff > 0.5) {
|
|
||||||
++frac;
|
|
||||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
|
||||||
if (frac >= pow10[prec]) {
|
|
||||||
frac = 0;
|
|
||||||
++whole;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (diff < 0.5) {
|
|
||||||
}
|
|
||||||
else if ((frac == 0U) || (frac & 1U)) {
|
|
||||||
// if halfway, round up if odd OR if last digit is 0
|
|
||||||
++frac;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prec == 0U) {
|
|
||||||
diff = value - (double)whole;
|
|
||||||
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
|
|
||||||
// exactly 0.5 and ODD, then round up
|
|
||||||
// 1.5 -> 2, but 2.5 -> 2
|
|
||||||
++whole;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
unsigned int count = prec;
|
|
||||||
// now do fractional part, as an unsigned number
|
|
||||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
|
||||||
--count;
|
|
||||||
buf[len++] = (char)(48U + (frac % 10U));
|
|
||||||
if (!(frac /= 10U)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add extra 0s
|
|
||||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
|
|
||||||
buf[len++] = '0';
|
|
||||||
}
|
|
||||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
|
||||||
// add decimal
|
|
||||||
buf[len++] = '.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do whole part, number is reversed
|
|
||||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
|
||||||
buf[len++] = (char)(48 + (whole % 10));
|
|
||||||
if (!(whole /= 10)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad leading zeros
|
|
||||||
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
|
|
||||||
if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
|
|
||||||
width--;
|
|
||||||
}
|
|
||||||
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
|
|
||||||
buf[len++] = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
|
||||||
if (negative) {
|
|
||||||
buf[len++] = '-';
|
|
||||||
}
|
|
||||||
else if (flags & FLAGS_PLUS) {
|
|
||||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
|
||||||
}
|
|
||||||
else if (flags & FLAGS_SPACE) {
|
|
||||||
buf[len++] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
||||||
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
|
|
||||||
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
|
|
||||||
{
|
|
||||||
// check for NaN and special values
|
|
||||||
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
|
|
||||||
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine the sign
|
|
||||||
const bool negative = value < 0;
|
|
||||||
if (negative) {
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// default precision
|
|
||||||
if (!(flags & FLAGS_PRECISION)) {
|
|
||||||
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine the decimal exponent
|
|
||||||
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
|
|
||||||
union {
|
|
||||||
uint64_t U;
|
|
||||||
double F;
|
|
||||||
} conv;
|
|
||||||
|
|
||||||
conv.F = value;
|
|
||||||
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
|
|
||||||
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
|
|
||||||
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
|
|
||||||
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
|
|
||||||
// now we want to compute 10^expval but we want to be sure it won't overflow
|
|
||||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
|
||||||
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
|
||||||
const double z2 = z * z;
|
|
||||||
conv.U = (uint64_t)(exp2 + 1023) << 52U;
|
|
||||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
|
||||||
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
|
||||||
// correct for rounding errors
|
|
||||||
if (value < conv.F) {
|
|
||||||
expval--;
|
|
||||||
conv.F /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
|
|
||||||
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
|
|
||||||
|
|
||||||
// in "%g" mode, "prec" is the number of *significant figures* not decimals
|
|
||||||
if (flags & FLAGS_ADAPT_EXP) {
|
|
||||||
// do we want to fall-back to "%f" mode?
|
|
||||||
if ((value >= 1e-4) && (value < 1e6)) {
|
|
||||||
if ((int)prec > expval) {
|
|
||||||
prec = (unsigned)((int)prec - expval - 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
prec = 0;
|
|
||||||
}
|
|
||||||
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
|
|
||||||
// no characters in exponent
|
|
||||||
minwidth = 0U;
|
|
||||||
expval = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// we use one sigfig for the whole part
|
|
||||||
if ((prec > 0) && (flags & FLAGS_PRECISION)) {
|
|
||||||
--prec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// will everything fit?
|
|
||||||
unsigned int fwidth = width;
|
|
||||||
if (width > minwidth) {
|
|
||||||
// we didn't fall-back so subtract the characters required for the exponent
|
|
||||||
fwidth -= minwidth;
|
|
||||||
} else {
|
|
||||||
// not enough characters, so go back to default sizing
|
|
||||||
fwidth = 0U;
|
|
||||||
}
|
|
||||||
if ((flags & FLAGS_LEFT) && minwidth) {
|
|
||||||
// if we're padding on the right, DON'T pad the floating part
|
|
||||||
fwidth = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rescale the float value
|
|
||||||
if (expval) {
|
|
||||||
value /= conv.F;
|
|
||||||
}
|
|
||||||
|
|
||||||
// output the floating part
|
|
||||||
const size_t start_idx = idx;
|
|
||||||
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
|
|
||||||
|
|
||||||
// output the exponent part
|
|
||||||
if (minwidth) {
|
|
||||||
// output the exponential symbol
|
|
||||||
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
|
|
||||||
// output the exponent value
|
|
||||||
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
|
|
||||||
// might need to right-pad spaces
|
|
||||||
if (flags & FLAGS_LEFT) {
|
|
||||||
while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
#endif // PRINTF_SUPPORT_EXPONENTIAL
|
|
||||||
#endif // PRINTF_SUPPORT_FLOAT
|
|
||||||
|
|
||||||
|
|
||||||
// internal vsnprintf
|
|
||||||
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
|
|
||||||
{
|
|
||||||
unsigned int flags, width, precision, n;
|
|
||||||
size_t idx = 0U;
|
|
||||||
|
|
||||||
if (!buffer) {
|
|
||||||
// use null output function
|
|
||||||
out = _out_null;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*format)
|
|
||||||
{
|
|
||||||
// format specifier? %[flags][width][.precision][length]
|
|
||||||
if (*format != '%') {
|
|
||||||
// no
|
|
||||||
out(*format, buffer, idx++, maxlen);
|
|
||||||
format++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// yes, evaluate it
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate flags
|
|
||||||
flags = 0U;
|
|
||||||
do {
|
|
||||||
switch (*format) {
|
|
||||||
case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
|
|
||||||
case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
|
|
||||||
case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
|
|
||||||
case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
|
|
||||||
case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
|
|
||||||
default : n = 0U; break;
|
|
||||||
}
|
|
||||||
} while (n);
|
|
||||||
|
|
||||||
// evaluate width field
|
|
||||||
width = 0U;
|
|
||||||
if (_is_digit(*format)) {
|
|
||||||
width = _atoi(&format);
|
|
||||||
}
|
|
||||||
else if (*format == '*') {
|
|
||||||
const int w = va_arg(va, int);
|
|
||||||
if (w < 0) {
|
|
||||||
flags |= FLAGS_LEFT; // reverse padding
|
|
||||||
width = (unsigned int)-w;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
width = (unsigned int)w;
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate precision field
|
|
||||||
precision = 0U;
|
|
||||||
if (*format == '.') {
|
|
||||||
flags |= FLAGS_PRECISION;
|
|
||||||
format++;
|
|
||||||
if (_is_digit(*format)) {
|
|
||||||
precision = _atoi(&format);
|
|
||||||
}
|
|
||||||
else if (*format == '*') {
|
|
||||||
const int prec = (int)va_arg(va, int);
|
|
||||||
precision = prec > 0 ? (unsigned int)prec : 0U;
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate length field
|
|
||||||
switch (*format) {
|
|
||||||
case 'l' :
|
|
||||||
flags |= FLAGS_LONG;
|
|
||||||
format++;
|
|
||||||
if (*format == 'l') {
|
|
||||||
flags |= FLAGS_LONG_LONG;
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'h' :
|
|
||||||
flags |= FLAGS_SHORT;
|
|
||||||
format++;
|
|
||||||
if (*format == 'h') {
|
|
||||||
flags |= FLAGS_CHAR;
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
|
|
||||||
case 't' :
|
|
||||||
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case 'j' :
|
|
||||||
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
case 'z' :
|
|
||||||
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate specifier
|
|
||||||
switch (*format) {
|
|
||||||
case 'd' :
|
|
||||||
case 'i' :
|
|
||||||
case 'u' :
|
|
||||||
case 'x' :
|
|
||||||
case 'X' :
|
|
||||||
case 'o' :
|
|
||||||
case 'b' : {
|
|
||||||
// set the base
|
|
||||||
unsigned int base;
|
|
||||||
if (*format == 'x' || *format == 'X') {
|
|
||||||
base = 16U;
|
|
||||||
}
|
|
||||||
else if (*format == 'o') {
|
|
||||||
base = 8U;
|
|
||||||
}
|
|
||||||
else if (*format == 'b') {
|
|
||||||
base = 2U;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
base = 10U;
|
|
||||||
flags &= ~FLAGS_HASH; // no hash for dec format
|
|
||||||
}
|
|
||||||
// uppercase
|
|
||||||
if (*format == 'X') {
|
|
||||||
flags |= FLAGS_UPPERCASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no plus or space flag for u, x, X, o, b
|
|
||||||
if ((*format != 'i') && (*format != 'd')) {
|
|
||||||
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore '0' flag when precision is given
|
|
||||||
if (flags & FLAGS_PRECISION) {
|
|
||||||
flags &= ~FLAGS_ZEROPAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the integer
|
|
||||||
if ((*format == 'i') || (*format == 'd')) {
|
|
||||||
// signed
|
|
||||||
if (flags & FLAGS_LONG_LONG) {
|
|
||||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
|
||||||
const long long value = va_arg(va, long long);
|
|
||||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (flags & FLAGS_LONG) {
|
|
||||||
const long value = va_arg(va, long);
|
|
||||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
|
|
||||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// unsigned
|
|
||||||
if (flags & FLAGS_LONG_LONG) {
|
|
||||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
|
||||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (flags & FLAGS_LONG) {
|
|
||||||
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
|
|
||||||
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
|
||||||
case 'f' :
|
|
||||||
case 'F' :
|
|
||||||
if (*format == 'F') flags |= FLAGS_UPPERCASE;
|
|
||||||
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
case 'g':
|
|
||||||
case 'G':
|
|
||||||
if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
|
|
||||||
if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
|
|
||||||
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
#endif // PRINTF_SUPPORT_EXPONENTIAL
|
|
||||||
#endif // PRINTF_SUPPORT_FLOAT
|
|
||||||
case 'c' : {
|
|
||||||
unsigned int l = 1U;
|
|
||||||
// pre padding
|
|
||||||
if (!(flags & FLAGS_LEFT)) {
|
|
||||||
while (l++ < width) {
|
|
||||||
out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// char output
|
|
||||||
out((char)va_arg(va, int), buffer, idx++, maxlen);
|
|
||||||
// post padding
|
|
||||||
if (flags & FLAGS_LEFT) {
|
|
||||||
while (l++ < width) {
|
|
||||||
out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 's' : {
|
|
||||||
const char* p = va_arg(va, char*);
|
|
||||||
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
|
|
||||||
// pre padding
|
|
||||||
if (flags & FLAGS_PRECISION) {
|
|
||||||
l = (l < precision ? l : precision);
|
|
||||||
}
|
|
||||||
if (!(flags & FLAGS_LEFT)) {
|
|
||||||
while (l++ < width) {
|
|
||||||
out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// string output
|
|
||||||
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
|
|
||||||
out(*(p++), buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
// post padding
|
|
||||||
if (flags & FLAGS_LEFT) {
|
|
||||||
while (l++ < width) {
|
|
||||||
out(' ', buffer, idx++, maxlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'p' : {
|
|
||||||
width = sizeof(void*) * 2U;
|
|
||||||
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
|
|
||||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
|
||||||
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
|
|
||||||
if (is_ll) {
|
|
||||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#endif
|
|
||||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
|
|
||||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '%' :
|
|
||||||
out('%', buffer, idx++, maxlen);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default :
|
|
||||||
out(*format, buffer, idx++, maxlen);
|
|
||||||
format++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// termination
|
|
||||||
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
|
||||||
|
|
||||||
// return written chars without terminating \0
|
|
||||||
return (int)idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
int printf_(const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list va;
|
|
||||||
va_start(va, format);
|
|
||||||
char buffer[1];
|
|
||||||
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
|
|
||||||
va_end(va);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int sprintf_(char* buffer, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list va;
|
|
||||||
va_start(va, format);
|
|
||||||
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
|
|
||||||
va_end(va);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int snprintf_(char* buffer, size_t count, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list va;
|
|
||||||
va_start(va, format);
|
|
||||||
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
|
|
||||||
va_end(va);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int vprintf_(const char* format, va_list va)
|
|
||||||
{
|
|
||||||
char buffer[1];
|
|
||||||
return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
|
|
||||||
{
|
|
||||||
return _vsnprintf(_out_buffer, buffer, count, format, va);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list va;
|
|
||||||
va_start(va, format);
|
|
||||||
const out_fct_wrap_type out_fct_wrap = { out, arg };
|
|
||||||
const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
|
|
||||||
va_end(va);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// \author (c) Marco Paland (info@paland.com)
|
|
||||||
// 2014-2019, PALANDesign Hannover, Germany
|
|
||||||
//
|
|
||||||
// \license The MIT License (MIT)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
|
|
||||||
// embedded systems with a very limited resources.
|
|
||||||
// Use this instead of bloated standard/newlib printf.
|
|
||||||
// These routines are thread safe and reentrant.
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef _PRINTF_H_
|
|
||||||
#define _PRINTF_H_
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output a character to a custom device like UART, used by the printf() function
|
|
||||||
* This function is declared here only. You have to write your custom implementation somewhere
|
|
||||||
* \param character Character to output
|
|
||||||
*/
|
|
||||||
void _putchar(char character);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tiny printf implementation
|
|
||||||
* You have to implement _putchar if you use printf()
|
|
||||||
* To avoid conflicts with the regular printf() API it is overridden by macro defines
|
|
||||||
* and internal underscore-appended functions like printf_() are used
|
|
||||||
* \param format A string that specifies the format of the output
|
|
||||||
* \return The number of characters that are written into the array, not counting the terminating null character
|
|
||||||
*/
|
|
||||||
#define printf printf_
|
|
||||||
int printf_(const char* format, ...);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tiny sprintf implementation
|
|
||||||
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
|
|
||||||
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
|
|
||||||
* \param format A string that specifies the format of the output
|
|
||||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
|
|
||||||
*/
|
|
||||||
#define sprintf sprintf_
|
|
||||||
int sprintf_(char* buffer, const char* format, ...);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tiny snprintf/vsnprintf implementation
|
|
||||||
* \param buffer A pointer to the buffer where to store the formatted string
|
|
||||||
* \param count The maximum number of characters to store in the buffer, including a terminating null character
|
|
||||||
* \param format A string that specifies the format of the output
|
|
||||||
* \param va A value identifying a variable arguments list
|
|
||||||
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
|
|
||||||
* null character. A value equal or larger than count indicates truncation. Only when the returned value
|
|
||||||
* is non-negative and less than count, the string has been completely written.
|
|
||||||
*/
|
|
||||||
#define snprintf snprintf_
|
|
||||||
#define vsnprintf vsnprintf_
|
|
||||||
int snprintf_(char* buffer, size_t count, const char* format, ...);
|
|
||||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tiny vprintf implementation
|
|
||||||
* \param format A string that specifies the format of the output
|
|
||||||
* \param va A value identifying a variable arguments list
|
|
||||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
|
|
||||||
*/
|
|
||||||
#define vprintf vprintf_
|
|
||||||
int vprintf_(const char* format, va_list va);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* printf with output function
|
|
||||||
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
|
|
||||||
* \param out An output function which takes one character and an argument pointer
|
|
||||||
* \param arg An argument pointer for user data passed to output function
|
|
||||||
* \param format A string that specifies the format of the output
|
|
||||||
* \return The number of characters that are sent to the output function, not counting the terminating null character
|
|
||||||
*/
|
|
||||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _PRINTF_H_
|
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Framebuffer-based terminal driver
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
// Terminal output
|
// Terminal output
|
||||||
/*
|
/*
|
||||||
There are a couple of bugs here and there but for now I don't care too much
|
There are a couple of bugs here and there but for now I don't care too much
|
||||||
@@ -5,145 +11,132 @@ because this shitty implementation will be replaced one day by Flanterm
|
|||||||
(once memory management is okay: paging & kernel malloc)
|
(once memory management is okay: paging & kernel malloc)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <limine.h>
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include "term.h"
|
#include <io/term/term.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <io/term/flanterm.h>
|
||||||
|
#include <io/term/flanterm_backends/fb.h>
|
||||||
|
#include <mem/kheap.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <sched/spinlock.h>
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
|
||||||
extern struct limine_framebuffer* framebuffer;
|
#define NANOPRINTF_IMPLEMENTATION
|
||||||
|
#include <io/term/nanoprintf.h>
|
||||||
|
|
||||||
// Importing the PSF object file
|
extern struct flanterm_context* ft_ctx;
|
||||||
extern unsigned char _binary_zap_light16_psf_start[];
|
extern struct init_status init;
|
||||||
extern unsigned char _binary_zap_light16_psf_end[];
|
|
||||||
|
|
||||||
PSF1_Header* font = (PSF1_Header*)_binary_zap_light16_psf_start;
|
struct spinlock_t term_lock = {0};
|
||||||
uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header);
|
struct spinlock_t printf_lock = {0};
|
||||||
|
|
||||||
#define FONT_WIDTH 8
|
extern int panic_count;
|
||||||
#define FONT_HEIGHT font->characterSize
|
|
||||||
|
|
||||||
// Character cursor
|
/*
|
||||||
typedef struct
|
* internal_putc - Internal putchar function
|
||||||
|
* @c: char to print
|
||||||
|
* @_: (unused, for nanoprintf)
|
||||||
|
*
|
||||||
|
* Prints a character to the terminal if it's ready,
|
||||||
|
* and also to the serial interface if it's ready.
|
||||||
|
*/
|
||||||
|
void internal_putc(int c, void *_)
|
||||||
{
|
{
|
||||||
unsigned int x;
|
(void)_;
|
||||||
unsigned int y;
|
char ch = (char)c;
|
||||||
} Cursor;
|
|
||||||
|
|
||||||
Cursor cursor = {0};
|
if (init.terminal) {
|
||||||
|
if (panic_count == 0) {
|
||||||
unsigned char* fb;
|
spinlock_acquire(&term_lock);
|
||||||
|
flanterm_write(ft_ctx, &ch, 1);
|
||||||
int term_init()
|
spinlock_release(&term_lock);
|
||||||
{
|
} else {
|
||||||
// Get framebuffer address from Limine struct
|
flanterm_write(ft_ctx, &ch, 1);
|
||||||
|
|
||||||
if (framebuffer)
|
|
||||||
{
|
|
||||||
fb = framebuffer->address;
|
|
||||||
DEBUG("terminal initialized");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are marked "static" because we don't wanna expose them all around
|
|
||||||
// AKA they should just be seen here (kind of like private functions in cpp)
|
|
||||||
static void putpixel(int x, int y, int color)
|
|
||||||
{
|
|
||||||
// Depth isn't part of limine_framebuffer attributes so it will be 4
|
|
||||||
unsigned pos = x*4 + y*framebuffer->pitch;
|
|
||||||
fb[pos] = color & 255; // blue channel
|
|
||||||
fb[pos+1] = (color >> 8) & 255; // green
|
|
||||||
fb[pos+2] = (color >> 16) & 255; // blue
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_char(char c, int px, int py, int fg, int bg)
|
|
||||||
{
|
|
||||||
uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT);
|
|
||||||
|
|
||||||
for (size_t y=0; y<FONT_HEIGHT; y++)
|
|
||||||
{
|
|
||||||
uint8_t row = glyph[y];
|
|
||||||
for (size_t x=0; x<8; x++)
|
|
||||||
{
|
|
||||||
int color = (row & (0x80 >> x)) ? fg : bg;
|
|
||||||
putpixel(px+x, py+y, color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void erase_char(int px, int py)
|
if (init.serial) {
|
||||||
{
|
if (ch == '\n') {
|
||||||
for (size_t y=0; y<FONT_HEIGHT; y++)
|
skputc('\r');
|
||||||
{
|
|
||||||
for (size_t x=0; x<8; x++)
|
|
||||||
{
|
|
||||||
// Black
|
|
||||||
putpixel(px+x, py+y, 0);
|
|
||||||
}
|
}
|
||||||
|
skputc(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void putchar(char c)
|
/*
|
||||||
|
* printf - Fromatted printing
|
||||||
|
* @fmt: format string
|
||||||
|
* @...: variadic arguments
|
||||||
|
*
|
||||||
|
* Wrapper for nanoprintf
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ret> - number of characters sent to the callback
|
||||||
|
* %-1 - error
|
||||||
|
*/
|
||||||
|
int printf(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (c == '\n')
|
if (panic_count == 0) {
|
||||||
{
|
spinlock_acquire(&printf_lock);
|
||||||
cursor.x = 0;
|
va_list args;
|
||||||
cursor.y++;
|
va_start(args, fmt);
|
||||||
return;
|
int ret = npf_vpprintf(internal_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(internal_putc, NULL, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Improperly handled.
|
/*
|
||||||
// When we're on an empty line it should get to the upper line's last character
|
* kputs - Kernel puts
|
||||||
// NOT just the last position possible; we would need to track the last line's character amount for that
|
* @str: String to write
|
||||||
if (c == '\b')
|
*
|
||||||
{
|
* Writes a non-formatted string to terminal
|
||||||
if (cursor.x == 0 && cursor.y == 0)
|
*/
|
||||||
{
|
|
||||||
// Top-left corner
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.x == 0)
|
|
||||||
{
|
|
||||||
cursor.y--;
|
|
||||||
cursor.x = (framebuffer->width / FONT_WIDTH) -1; // here
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cursor.x--;
|
|
||||||
}
|
|
||||||
|
|
||||||
int px = cursor.x * FONT_WIDTH;
|
|
||||||
int py = cursor.y * FONT_HEIGHT;
|
|
||||||
erase_char(px, py);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((cursor.x+1)*FONT_WIDTH >= framebuffer->width)
|
|
||||||
{
|
|
||||||
cursor.x = 0;
|
|
||||||
cursor.y++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int px = cursor.x * FONT_WIDTH;
|
|
||||||
int py = cursor.y * FONT_HEIGHT;
|
|
||||||
draw_char(c, px, py, WHITE, BLACK);
|
|
||||||
cursor.x++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overhead that could be avoided, right? (for printf)
|
|
||||||
void _putchar(char character)
|
|
||||||
{
|
|
||||||
putchar(character);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug-printing
|
|
||||||
void kputs(const char* str)
|
void kputs(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int i=0;
|
size_t i=0;
|
||||||
while (str[i] != 0)
|
while (str[i] != 0) {
|
||||||
{
|
internal_putc(str[i], NULL);
|
||||||
putchar(str[i]);
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern struct flanterm_context* ft_ctx;
|
||||||
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* term_init - Video output/terminal initialization
|
||||||
|
*
|
||||||
|
* Uses Flanterm and the framebuffer given by Limine.
|
||||||
|
*/
|
||||||
|
void term_init()
|
||||||
|
{
|
||||||
|
uint32_t bgColor = 0x252525;
|
||||||
|
ft_ctx = flanterm_fb_init(
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
boot_ctx.fb->address, boot_ctx.fb->width, boot_ctx.fb->height, boot_ctx.fb->pitch,
|
||||||
|
boot_ctx.fb->red_mask_size, boot_ctx.fb->red_mask_shift,
|
||||||
|
boot_ctx.fb->green_mask_size, boot_ctx.fb->green_mask_shift,
|
||||||
|
boot_ctx.fb->blue_mask_size, boot_ctx.fb->blue_mask_shift,
|
||||||
|
NULL,
|
||||||
|
NULL, NULL,
|
||||||
|
&bgColor, NULL,
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, 0, 0, 1,
|
||||||
|
0, 0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
init.terminal = true;
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#ifndef TERM_H
|
|
||||||
#define TERM_H
|
|
||||||
|
|
||||||
int term_init();
|
|
||||||
void kputs(const char* str);
|
|
||||||
void putchar(char c);
|
|
||||||
|
|
||||||
enum TermColors
|
|
||||||
{
|
|
||||||
BLACK = 0x000000,
|
|
||||||
WHITE = 0xffffff
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PSF1_FONT_MAGIC 0x0436
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint16_t magic;
|
|
||||||
uint8_t fontMode;
|
|
||||||
uint8_t characterSize; // height
|
|
||||||
} PSF1_Header;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
23
src/kernel.h
23
src/kernel.h
@@ -1,23 +0,0 @@
|
|||||||
#ifndef KERNEL_H
|
|
||||||
#define KERNEL_H
|
|
||||||
|
|
||||||
enum ErrorCodes
|
|
||||||
{
|
|
||||||
ENOMEM,
|
|
||||||
EIO
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CLEAR_INTERRUPTS __asm__ volatile("cli")
|
|
||||||
#define SET_INTERRUPTS __asm__ volatile("sti")
|
|
||||||
|
|
||||||
#include "io/serial/serial.h"
|
|
||||||
#include "io/term/printf.h"
|
|
||||||
|
|
||||||
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
|
|
||||||
|
|
||||||
void hcf();
|
|
||||||
#define assert(check) do { if(!(check)) hcf(); } while(0)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
199
src/kmain.c
199
src/kmain.c
@@ -1,119 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief PepperOS kernel entry point
|
||||||
|
* @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/term/printf.h"
|
#include <io/serial/serial.h>
|
||||||
#include "io/serial/serial.h"
|
#include <mem/gdt.h>
|
||||||
#include "mem/gdt/gdt.h"
|
#include <mem/utils.h>
|
||||||
#include "mem/misc/utils.h"
|
#include <kernel.h>
|
||||||
#include "idt/idt.h"
|
#include <time/timer.h>
|
||||||
#include "kernel.h"
|
#include <io/kbd/ps2.h>
|
||||||
#include "time/timer.h"
|
#include <mem/pmm.h>
|
||||||
#include "io/kbd/ps2.h"
|
#include <mem/paging.h>
|
||||||
#include "mem/paging/pmm.h"
|
#include <mem/vmm.h>
|
||||||
#include "mem/paging/paging.h"
|
#include <mem/kheap.h>
|
||||||
#include "mem/paging/vmm.h"
|
#include <sched/process.h>
|
||||||
#include "mem/heap/kheap.h"
|
#include <sched/scheduler.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <io/term/flanterm.h>
|
||||||
|
#include <io/term/flanterm_backends/fb.h>
|
||||||
|
#include <arch/x86.h>
|
||||||
|
|
||||||
// Limine version used
|
// Limine version used
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
static volatile LIMINE_BASE_REVISION(3);
|
volatile LIMINE_BASE_REVISION(3);
|
||||||
|
|
||||||
// Framebuffer request
|
int panic_count = 0;
|
||||||
__attribute__((used, section(".limine_requests")))
|
|
||||||
static volatile struct limine_framebuffer_request framebuffer_request = {
|
|
||||||
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
|
||||||
.revision = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Memory map request
|
/*
|
||||||
__attribute__((used, section(".limine_requests")))
|
* hcf - Halt and catch fire
|
||||||
static volatile struct limine_memmap_request memmap_request = {
|
*
|
||||||
.id = LIMINE_MEMMAP_REQUEST,
|
* This function is called only in the case of an unrecoverable
|
||||||
.revision = 0
|
* error. It halts interrupts, and stops execution. The machine
|
||||||
};
|
* will stay in an infinite loop state.
|
||||||
|
*/
|
||||||
// Higher Half Direct Map
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
|
||||||
static volatile struct limine_hhdm_request hhdm_request = {
|
|
||||||
.id = LIMINE_HHDM_REQUEST,
|
|
||||||
.revision = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Executable Address/Kernel Address (find base phys/virt address of kernel)
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
|
||||||
static volatile struct limine_kernel_address_request kerneladdr_request = {
|
|
||||||
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
|
||||||
.revision = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests_start")))
|
|
||||||
static volatile LIMINE_REQUESTS_START_MARKER;
|
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests_end")))
|
|
||||||
static volatile LIMINE_REQUESTS_END_MARKER;
|
|
||||||
|
|
||||||
struct limine_framebuffer* framebuffer;
|
|
||||||
|
|
||||||
// Panic (should dump registers etc. in the future)
|
|
||||||
void hcf()
|
void hcf()
|
||||||
{
|
{
|
||||||
for (;;)
|
CLEAR_INTERRUPTS; for (;;)asm("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idle - Make the machine idle
|
||||||
|
*
|
||||||
|
* When there is nothing else to do, this function
|
||||||
|
* gets called. It can be interrupted, so it allows
|
||||||
|
* the scheduler, timer, and keyboard to work.
|
||||||
|
*/
|
||||||
|
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
|
||||||
|
|
||||||
|
struct flanterm_context *ft_ctx;
|
||||||
|
struct boot_context boot_ctx;
|
||||||
|
struct init_status init = {0};
|
||||||
|
|
||||||
|
extern volatile struct limine_framebuffer_request framebuffer_request;
|
||||||
|
extern volatile struct limine_memmap_request memmap_request;
|
||||||
|
extern volatile struct limine_hhdm_request hhdm_request;
|
||||||
|
extern volatile struct limine_kernel_address_request kerneladdr_request;
|
||||||
|
|
||||||
|
extern struct process_t* processes_list;
|
||||||
|
extern struct process_t* current_process;
|
||||||
|
struct process_t* idle_proc;
|
||||||
|
|
||||||
|
// Never gets executed although pedicel is scheduled?
|
||||||
|
void pedicel_main(void* arg)
|
||||||
{
|
{
|
||||||
|
printf("\n\n\rWelcome to PepperOS! Pedicel speaking.\r\nNothing left to do, let's go idle!\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void idle_main(void* arg)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is our entry point
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kmain - Kernel entry point
|
||||||
|
*
|
||||||
|
* This is where execution begins at handoff from Limine.
|
||||||
|
* The function fetches all needed information from the
|
||||||
|
* bootloader, initializes all kernel modules and structures,
|
||||||
|
* and then goes in an idle state.
|
||||||
|
*/
|
||||||
void kmain()
|
void kmain()
|
||||||
{
|
{
|
||||||
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
|
||||||
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) hcf();
|
|
||||||
|
|
||||||
// We should probably grab all the boot info in a boot context struct
|
|
||||||
// that would be a bit cleaner than this mess
|
|
||||||
|
|
||||||
// Get the first framebuffer from the response
|
|
||||||
framebuffer = framebuffer_request.response->framebuffers[0];
|
|
||||||
|
|
||||||
serial_init();
|
|
||||||
|
|
||||||
if (memmap_request.response == NULL) hcf();
|
|
||||||
memmap_display(memmap_request.response);
|
|
||||||
|
|
||||||
if (hhdm_request.response == NULL) hcf();
|
|
||||||
hhdm_display(hhdm_request.response);
|
|
||||||
|
|
||||||
if (kerneladdr_request.response == NULL) hcf();
|
|
||||||
DEBUG("kernel: phys_base=0x%p virt_base=0x%p", kerneladdr_request.response->physical_base, kerneladdr_request.response->virtual_base);
|
|
||||||
|
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
gdt_init();
|
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
|
||||||
idt_init();
|
|
||||||
|
// Populate boot context
|
||||||
|
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();
|
||||||
|
serial_init();
|
||||||
timer_init();
|
timer_init();
|
||||||
SET_INTERRUPTS;
|
|
||||||
|
|
||||||
pmm_init(memmap_request.response, hhdm_request.response);
|
x86_arch_init();
|
||||||
|
|
||||||
// Remap kernel , HHDM and framebuffer
|
boot_mem_display();
|
||||||
paging_init(kerneladdr_request.response, framebuffer);
|
pmm_init(boot_ctx);
|
||||||
|
|
||||||
|
paging_init(boot_ctx);
|
||||||
kheap_init();
|
kheap_init();
|
||||||
|
|
||||||
void* ptr = kmalloc(10); DEBUG("(KMALLOC TEST) Allocated 10 bytes at 0x%p", ptr);
|
|
||||||
void* ptr2 = kmalloc(200); DEBUG("(KMALLOC TEST) Allocated 200 bytes at 0x%p", ptr2);
|
|
||||||
kfree(ptr);
|
|
||||||
void* ptr3 = kmalloc(5); DEBUG("(KMALLOC TEST) Allocated 5 bytes at 0x%p", ptr3);
|
|
||||||
|
|
||||||
vmm_init();
|
|
||||||
|
|
||||||
keyboard_init(FR);
|
keyboard_init(FR);
|
||||||
|
|
||||||
term_init();
|
process_init();
|
||||||
// Draw something
|
idle_proc = process_create("idle", (void*)idle_main, 0);
|
||||||
printf("%s, %s!\n", "Hello", "world");
|
process_create("pedicel", (void*)pedicel_main, 0);
|
||||||
// Yoohoooooo!
|
process_create("thing", thing_main, NULL);
|
||||||
//DEBUG("kernel initialized successfully! hanging... wow=%d", 42);
|
|
||||||
printf("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non justo a magna bibendum auctor viverra rutrum diam. In hac habitasse platea dictumst. Vestibulum suscipit ipsum eget tortor maximus lobortis. Donec vel ipsum id lacus fringilla bibendum id eget risus. Fusce vestibulum diam sit amet nunc ultricies, nec rutrum nibh congue. Donec fringilla a dui sit amet ullamcorper. Donec pharetra quis tortor id congue. Aliquam erat volutpat. Duis suscipit nulla vel ligula iaculis, in gravida mauris pellentesque. Vestibulum nunc nisl, posuere eu eros et, dictum molestie dolor. Donec posuere laoreet hendrerit. Suspendisse potenti. Proin fringilla vehicula malesuada. Quisque a dui est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec aliquam lacus, at lacinia enim. ");
|
scheduler_init();
|
||||||
hcf();
|
|
||||||
|
printf(PEPPEROS_SPLASH);
|
||||||
|
idle();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
#include "gdt.h"
|
/*
|
||||||
|
* @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 <stdint.h>
|
||||||
#include "io/serial/serial.h"
|
#include <io/serial/serial.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
|
||||||
// Descriptors are 8-byte wide (64bits)
|
// Descriptors are 8-byte wide (64bits)
|
||||||
@@ -8,11 +14,20 @@
|
|||||||
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
||||||
struct GDTR gdtr;
|
struct GDTR gdtr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_load - Loads Global Descriptor Table
|
||||||
|
*/
|
||||||
static void gdt_load()
|
static void gdt_load()
|
||||||
{
|
{
|
||||||
asm("lgdt %0" : : "m"(gdtr));
|
asm("lgdt %0" : : "m"(gdtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_flush - Flushes the Global Descriptor Table
|
||||||
|
*
|
||||||
|
* This function loads new Segment Selectors to make
|
||||||
|
* the GDT changes take effect
|
||||||
|
*/
|
||||||
static void gdt_flush()
|
static void gdt_flush()
|
||||||
{
|
{
|
||||||
// Here, 0x8 is the kernel code selector
|
// Here, 0x8 is the kernel code selector
|
||||||
@@ -36,6 +51,15 @@ static void gdt_flush()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_init - Global Descriptor Table initialization
|
||||||
|
*
|
||||||
|
* This function loads a new GDT in the CPU.
|
||||||
|
* It contains a null descriptor, kernel code and data
|
||||||
|
* segments, and user code and data segments.
|
||||||
|
* However, we do not use segmentation to manage memory on
|
||||||
|
* 64-bit x86, as it's deprecated. Instead, we use paging.
|
||||||
|
*/
|
||||||
void gdt_init()
|
void gdt_init()
|
||||||
{
|
{
|
||||||
// Null descriptor (required)
|
// Null descriptor (required)
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
#include "kheap.h"
|
|
||||||
#include "mem/paging/paging.h"
|
|
||||||
#include "mem/paging/pmm.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <kernel.h>
|
|
||||||
|
|
||||||
extern uint64_t kernel_phys_base;
|
|
||||||
extern uint64_t kernel_virt_base;
|
|
||||||
|
|
||||||
uintptr_t kheap_start;
|
|
||||||
|
|
||||||
static struct heap_block_t* head = NULL;
|
|
||||||
static uintptr_t end;
|
|
||||||
|
|
||||||
// Kernel root table (level 4)
|
|
||||||
extern uint64_t *kernel_pml4;
|
|
||||||
|
|
||||||
static void kheap_map_page()
|
|
||||||
{
|
|
||||||
uintptr_t phys = pmm_alloc();
|
|
||||||
paging_map_page(kernel_pml4, end, phys, PTE_PRESENT | PTE_WRITABLE | PTE_NOEXEC);
|
|
||||||
end += PAGE_SIZE;
|
|
||||||
DEBUG("Mapped first kheap page");
|
|
||||||
}
|
|
||||||
|
|
||||||
void kheap_init()
|
|
||||||
{
|
|
||||||
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
|
||||||
end = kheap_start;
|
|
||||||
|
|
||||||
// At least 1 page must be mapped for it to work
|
|
||||||
kheap_map_page();
|
|
||||||
|
|
||||||
// Give linked list head its properties
|
|
||||||
head = (struct heap_block_t*)kheap_start;
|
|
||||||
head->size = PAGE_SIZE - sizeof(struct heap_block_t);
|
|
||||||
head->free = true;
|
|
||||||
head->next = NULL;
|
|
||||||
DEBUG("kheap initialized, head=0x%p, size=%u", head, head->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* kmalloc(size_t size)
|
|
||||||
{
|
|
||||||
// No size, no memory allocated!
|
|
||||||
if (!size) return NULL;
|
|
||||||
|
|
||||||
struct heap_block_t* curr = head;
|
|
||||||
|
|
||||||
while (curr)
|
|
||||||
{
|
|
||||||
// Is block free and big enough for us?
|
|
||||||
if (curr->free && curr->size >= size)
|
|
||||||
{
|
|
||||||
// We split the block if it is big enough
|
|
||||||
if (curr->size > size + sizeof(struct heap_block_t))
|
|
||||||
{
|
|
||||||
struct heap_block_t* new_block = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
|
|
||||||
// We have to subtract the size of our block struct
|
|
||||||
new_block->size = curr->size - size - sizeof(struct heap_block_t);
|
|
||||||
new_block->free = true;
|
|
||||||
|
|
||||||
// Then we chain up the block in the list
|
|
||||||
new_block->next = curr->next;
|
|
||||||
curr->next = new_block;
|
|
||||||
|
|
||||||
curr->size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found a good block, we return it
|
|
||||||
curr->free = false;
|
|
||||||
return (void*)((uintptr_t)curr + sizeof(struct heap_block_t));
|
|
||||||
}
|
|
||||||
// Continue browsing the list if nothing good was found yet
|
|
||||||
curr = curr->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're hear it means we didn't have enough memory
|
|
||||||
// for the block allocation. So we will allocate more..
|
|
||||||
uintptr_t old_end = end;
|
|
||||||
kheap_map_page();
|
|
||||||
|
|
||||||
struct heap_block_t* block = (struct heap_block_t*)old_end;
|
|
||||||
block->size = PAGE_SIZE - sizeof(struct heap_block_t);
|
|
||||||
block->free = false;
|
|
||||||
block->next = NULL;
|
|
||||||
|
|
||||||
// Put the block at the end of the list
|
|
||||||
curr = head;
|
|
||||||
while (curr->next)
|
|
||||||
{
|
|
||||||
curr = curr->next;
|
|
||||||
}
|
|
||||||
curr->next = block;
|
|
||||||
|
|
||||||
return (void*)((uintptr_t)block + sizeof(struct heap_block_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void kfree(void* ptr)
|
|
||||||
{
|
|
||||||
// Nothing to free
|
|
||||||
if (!ptr) return;
|
|
||||||
|
|
||||||
// Set it free!
|
|
||||||
struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t));
|
|
||||||
block->free = true;
|
|
||||||
}
|
|
||||||
160
src/mem/kheap.c
Normal file
160
src/mem/kheap.c
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Kernel heap
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mem/kheap.h>
|
||||||
|
#include <mem/paging.h>
|
||||||
|
#include <mem/pmm.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <sched/process.h>
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
extern uint64_t kernel_phys_base;
|
||||||
|
extern uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
uintptr_t kheap_start;
|
||||||
|
|
||||||
|
static struct heap_block_t* head = NULL;
|
||||||
|
static uintptr_t end;
|
||||||
|
|
||||||
|
// Kernel root table (level 4)
|
||||||
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kheap_init - Kernel heap initialization
|
||||||
|
*
|
||||||
|
* This function physically allocates and maps enough pages
|
||||||
|
* of memory for KHEAP_SIZE, which is defined in config.h.
|
||||||
|
*
|
||||||
|
* It then creates one big heap block, which will be the
|
||||||
|
* base for a linked list.
|
||||||
|
*/
|
||||||
|
void kheap_init()
|
||||||
|
{
|
||||||
|
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
||||||
|
|
||||||
|
size_t heap_pages = ALIGN_UP(KHEAP_SIZE, PAGE_SIZE) / PAGE_SIZE;
|
||||||
|
DEBUG("Mapping %d kernel heap pages at 0x%p", heap_pages, kheap_start);
|
||||||
|
|
||||||
|
uintptr_t current_addr = kheap_start;
|
||||||
|
|
||||||
|
// Map/alloc enough pages for heap (KHEAP_SIZE)
|
||||||
|
for (size_t i=0; i<heap_pages; i++) {
|
||||||
|
uintptr_t phys = pmm_alloc();
|
||||||
|
if (phys == 0) {
|
||||||
|
panic(NULL, "Not enough memory available to initialize kernel heap.");
|
||||||
|
}
|
||||||
|
|
||||||
|
paging_map_page(kernel_pml4, current_addr, phys, PTE_PRESENT | PTE_WRITABLE);
|
||||||
|
current_addr += PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = current_addr;
|
||||||
|
|
||||||
|
// Give linked list head its properties
|
||||||
|
head = (struct heap_block_t*)kheap_start;
|
||||||
|
head->size = (end-kheap_start) - sizeof(struct heap_block_t);
|
||||||
|
head->free = true;
|
||||||
|
head->next = NULL;
|
||||||
|
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kmalloc - Kernel memory allocation
|
||||||
|
* @size: number of bytes to allocate
|
||||||
|
*
|
||||||
|
* Looks for a big enough free block and marks it
|
||||||
|
* as taken. Each block of memory is preceded by
|
||||||
|
* the linked list header.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ptr> - Pointer to at least <size> bytes of usable memory
|
||||||
|
* NULL - No more memory, or no valid size given
|
||||||
|
*/
|
||||||
|
void* kmalloc(size_t size)
|
||||||
|
{
|
||||||
|
// No size, no memory allocated!
|
||||||
|
if (!size) return NULL;
|
||||||
|
size = ALIGN(size);
|
||||||
|
|
||||||
|
struct heap_block_t* curr = head;
|
||||||
|
|
||||||
|
while (curr) {
|
||||||
|
// Is block free and big enough for us?
|
||||||
|
if (curr->free && curr->size >= size) {
|
||||||
|
// We split the block if it is big enough
|
||||||
|
if (curr->size >= size + sizeof(struct heap_block_t) + 16) {
|
||||||
|
struct heap_block_t* split = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
|
||||||
|
|
||||||
|
split->size = curr->size - size - sizeof(struct heap_block_t);
|
||||||
|
split->free = true;
|
||||||
|
split->next = curr->next;
|
||||||
|
|
||||||
|
curr->next = split;
|
||||||
|
curr->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found a good block, we return it
|
||||||
|
curr->free = false;
|
||||||
|
return (void*)((uintptr_t)curr + sizeof(struct heap_block_t));
|
||||||
|
}
|
||||||
|
// Continue browsing the list if nothing good was found yet
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No growing. If we're here it means the initial pool
|
||||||
|
// wasn't sufficient. Too bad.
|
||||||
|
DEBUG("Kernel heap is OUT OF MEMORY!");
|
||||||
|
// if we were terrorists maybe we should panic
|
||||||
|
// or just wait for others to free stuff?
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kfree - Kernel memory freeing
|
||||||
|
* @ptr: pointer to memory region to free
|
||||||
|
*
|
||||||
|
* Marks the memory block beginning at <ptr>
|
||||||
|
* as free. Also merges adjacent free blocks
|
||||||
|
* to lessen fragmentation.
|
||||||
|
*/
|
||||||
|
void kfree(void* ptr)
|
||||||
|
{
|
||||||
|
// Nothing to free
|
||||||
|
if (!ptr) return;
|
||||||
|
|
||||||
|
// Set it free!
|
||||||
|
struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t));
|
||||||
|
block->free = true;
|
||||||
|
|
||||||
|
// merge adjacent free blocks (coalescing)
|
||||||
|
struct heap_block_t* curr = head;
|
||||||
|
while (curr && curr->next) {
|
||||||
|
if (curr->free && curr->next->free) {
|
||||||
|
curr->size += sizeof(*curr) + curr->next->size;
|
||||||
|
curr->next = curr->next->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kalloc_stack - Stack memory allocation
|
||||||
|
*
|
||||||
|
* Allocates a memory region of at least PROCESS_STACK_SIZE,
|
||||||
|
* to be used as a stack for a process. The pointer returned
|
||||||
|
* points to the end of the region, as the stack grows downwards.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ptr> - Pointer to a region after at least PROCESS_STACK_SIZE bytes of usable memory
|
||||||
|
* NULL - No more memory
|
||||||
|
*/
|
||||||
|
void* kalloc_stack()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <limine.h>
|
|
||||||
#include "kernel.h"
|
|
||||||
#include "string/string.h"
|
|
||||||
|
|
||||||
// We won't be linked to standard library, but still need the basic mem* functions
|
|
||||||
// so everything goes allright with the compiler
|
|
||||||
|
|
||||||
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
|
||||||
// do more optimization on them (and as it's a much used function, it's good to
|
|
||||||
// be able to do that)
|
|
||||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
|
||||||
const uint8_t* restrict psrc = (const uint8_t* restrict)src;
|
|
||||||
|
|
||||||
for (size_t i=0; i<n; i++)
|
|
||||||
{
|
|
||||||
pdest[i] = psrc[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* memset(void* s, int c, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t* p = (uint8_t*)s;
|
|
||||||
|
|
||||||
for (size_t i=0; i<n; i++)
|
|
||||||
{
|
|
||||||
p[i] = (uint8_t)c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* memmove(void *dest, const void* src, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t* pdest = (uint8_t*)dest;
|
|
||||||
const uint8_t* psrc = (uint8_t*)src;
|
|
||||||
|
|
||||||
if (src > dest)
|
|
||||||
{
|
|
||||||
for (size_t i=0; i<n; i++)
|
|
||||||
{
|
|
||||||
pdest[i] = psrc[i];
|
|
||||||
}
|
|
||||||
} else if (src < dest)
|
|
||||||
{
|
|
||||||
for (size_t i=n; i>0; i--)
|
|
||||||
{
|
|
||||||
pdest[i-1] = psrc[i-1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
int memcmp(const void* s1, const void* s2, size_t n)
|
|
||||||
{
|
|
||||||
const uint8_t* p1 = (const uint8_t*)s1;
|
|
||||||
const uint8_t* p2 = (const uint8_t*)s2;
|
|
||||||
|
|
||||||
for (size_t i=0; i<n; i++)
|
|
||||||
{
|
|
||||||
if (p1[i] != p2[i])
|
|
||||||
{
|
|
||||||
return p1[i] < p2[i] ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the memmap so we see how the memory is laid out at handoff
|
|
||||||
void memmap_display(struct limine_memmap_response* response)
|
|
||||||
{
|
|
||||||
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
|
|
||||||
|
|
||||||
for (size_t i=0; i<response->entry_count; i++)
|
|
||||||
{
|
|
||||||
struct limine_memmap_entry* entry = response->entries[i];
|
|
||||||
char type[32] = {0};
|
|
||||||
switch(entry->type)
|
|
||||||
{
|
|
||||||
case LIMINE_MEMMAP_USABLE:
|
|
||||||
strcpy(type, "USABLE");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_RESERVED:
|
|
||||||
strcpy(type, "RESERVED");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
|
||||||
strcpy(type, "ACPI_RECLAIMABLE");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_ACPI_NVS:
|
|
||||||
strcpy(type, "ACPI_NVS");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_BAD_MEMORY:
|
|
||||||
strcpy(type, "BAD_MEMORY");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
|
||||||
strcpy(type, "BOOTLOADER_RECLAIMABLE");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
|
|
||||||
strcpy(type, "KERNEL_AND_MODULES");
|
|
||||||
break;
|
|
||||||
case LIMINE_MEMMAP_FRAMEBUFFER:
|
|
||||||
strcpy(type, "FRAMEBUFFER");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
strcpy(type, "UNKNOWN");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
DEBUG("entry %02u: [0x%016x | %016u bytes] - %s", i, entry->base, entry->length, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the HHDM
|
|
||||||
void hhdm_display(struct limine_hhdm_response* hhdm)
|
|
||||||
{
|
|
||||||
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
#include "paging.h"
|
/*
|
||||||
#include "pmm.h"
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief x64 4-level paging implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mem/paging.h>
|
||||||
|
#include <mem/pmm.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Paging on x86 uses four different page table levels:
|
Paging on x86 uses four different page table levels:
|
||||||
@@ -17,23 +24,46 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
|
|||||||
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void load_cr3(uint64_t value) {
|
/*
|
||||||
|
* load_cr3 - Load a new value into the CR3 register
|
||||||
|
* @value: the value to load
|
||||||
|
*
|
||||||
|
* This function is used to load the physical address
|
||||||
|
* of the root page table (PML4), to switch the paging
|
||||||
|
* structures the CPU sees and uses.
|
||||||
|
*/
|
||||||
|
void load_cr3(uint64_t value) {
|
||||||
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// To flush TLB
|
/*
|
||||||
|
* invlpg - Invalidates a Translation Lookaside Buffer entry
|
||||||
|
* @addr: page memory address
|
||||||
|
*
|
||||||
|
* This function is used to flush at least the TLB entrie(s)
|
||||||
|
* for the page that contains the <addr> address.
|
||||||
|
*/
|
||||||
static inline void invlpg(void *addr)
|
static inline void invlpg(void *addr)
|
||||||
{
|
{
|
||||||
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocates a 512-entry 64bit page table/directory/whatever (zeroed)
|
/*
|
||||||
|
* alloc_page_table - Page table allocation
|
||||||
|
*
|
||||||
|
* This function allocates enough memory for a 512-entry
|
||||||
|
* 64-bit page table, for any level (PML4/3/2).
|
||||||
|
*
|
||||||
|
* Memory allocated here is zeroed.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <virt> - Pointer to allocated page table
|
||||||
|
*/
|
||||||
static uint64_t* alloc_page_table()
|
static uint64_t* alloc_page_table()
|
||||||
{
|
{
|
||||||
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
||||||
|
|
||||||
for (size_t i=0; i<512; i++)
|
for (size_t i=0; i<512; i++) {
|
||||||
{
|
|
||||||
virt[i] = 0;
|
virt[i] = 0;
|
||||||
}
|
}
|
||||||
return virt;
|
return virt;
|
||||||
@@ -43,10 +73,19 @@ static uint64_t* alloc_page_table()
|
|||||||
__attribute__((aligned(4096)))
|
__attribute__((aligned(4096)))
|
||||||
uint64_t *kernel_pml4;
|
uint64_t *kernel_pml4;
|
||||||
|
|
||||||
// Map a page, taking virt and phys address. This will go through the paging structures
|
/*
|
||||||
// beginning at the given root table, translate the virtual address in indexes in
|
* paging_map_page - Mapping a memory page
|
||||||
// page table/directories, and then mapping the correct page table entry with the
|
* @root_table: Address of the PML4
|
||||||
// given physical address + flags
|
* @virt: Virtual address
|
||||||
|
* @phys: Physical address
|
||||||
|
* @flags: Flags to set on page
|
||||||
|
*
|
||||||
|
* This function maps the physical address <phys> to the virtual
|
||||||
|
* address <virt>, using the paging structures beginning at
|
||||||
|
* <root_table>. <flags> can be set according to the PTE_FLAGS enum.
|
||||||
|
*
|
||||||
|
* If a page table/directory entry is not present yet, it creates it.
|
||||||
|
*/
|
||||||
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
||||||
{
|
{
|
||||||
virt = PAGE_ALIGN_DOWN(virt);
|
virt = PAGE_ALIGN_DOWN(virt);
|
||||||
@@ -63,33 +102,27 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
|
|||||||
// PML4
|
// PML4
|
||||||
// If the entry at index is not present, allocate enough space for it
|
// If the entry at index is not present, allocate enough space for it
|
||||||
// then populate the entry with correct addr + flags
|
// then populate the entry with correct addr + flags
|
||||||
if (!(root_table[pml4_i] & PTE_PRESENT))
|
if (!(root_table[pml4_i] & PTE_PRESENT)) {
|
||||||
{
|
|
||||||
pdpt = alloc_page_table();
|
pdpt = alloc_page_table();
|
||||||
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
|
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
}
|
} else {
|
||||||
else {
|
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & PTE_ADDR_MASK);
|
||||||
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & ~0xFFFULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PDPT: same here
|
// PDPT: same here
|
||||||
if (!(pdpt[pdpt_i] & PTE_PRESENT))
|
if (!(pdpt[pdpt_i] & PTE_PRESENT)) {
|
||||||
{
|
|
||||||
pd = alloc_page_table();
|
pd = alloc_page_table();
|
||||||
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
|
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
}
|
} else {
|
||||||
else {
|
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
|
||||||
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PD: and here
|
// PD: and here
|
||||||
if (!(pd[pd_i] & PTE_PRESENT))
|
if (!(pd[pd_i] & PTE_PRESENT)) {
|
||||||
{
|
|
||||||
pt = alloc_page_table();
|
pt = alloc_page_table();
|
||||||
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
|
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
}
|
} else {
|
||||||
else {
|
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
|
||||||
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PT: finally, populate the page table entry
|
// PT: finally, populate the page table entry
|
||||||
@@ -102,13 +135,23 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
|
|||||||
uint64_t kernel_phys_base;
|
uint64_t kernel_phys_base;
|
||||||
uint64_t kernel_virt_base;
|
uint64_t kernel_virt_base;
|
||||||
|
|
||||||
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb)
|
/*
|
||||||
|
* paging_init - Paging initialization
|
||||||
|
* @boot_ctx: Boot context structure
|
||||||
|
*
|
||||||
|
* This function initializes new paging structures, to replace
|
||||||
|
* the ones given by the bootloader.
|
||||||
|
*
|
||||||
|
* It maps the kernel, the HHDM space, and the framebuffer.
|
||||||
|
*/
|
||||||
|
void paging_init(struct boot_context boot_ctx)
|
||||||
{
|
{
|
||||||
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
||||||
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
|
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
|
||||||
|
|
||||||
kernel_phys_base = kaddr->physical_base;
|
kernel_phys_base = boot_ctx.kaddr->physical_base;
|
||||||
kernel_virt_base = kaddr->virtual_base;
|
kernel_virt_base = boot_ctx.kaddr->virtual_base;
|
||||||
|
struct limine_framebuffer* fb = boot_ctx.fb;
|
||||||
|
|
||||||
DEBUG("Kernel lives at virt=0x%p phys=0x%p", kernel_virt_base, kernel_phys_base);
|
DEBUG("Kernel lives at virt=0x%p phys=0x%p", kernel_virt_base, kernel_phys_base);
|
||||||
|
|
||||||
@@ -117,22 +160,37 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra
|
|||||||
// for debug
|
// for debug
|
||||||
uint64_t page_count = 0;
|
uint64_t page_count = 0;
|
||||||
|
|
||||||
// HHDM map first 1 GB using given offset
|
// Find max physical address from limine memmap
|
||||||
for (uint64_t i=0; i<0x40000000; i += PAGE_SIZE)
|
uint64_t max_phys = 0;
|
||||||
{
|
for (uint64_t i=0; i<boot_ctx.mmap->entry_count; i++) {
|
||||||
//paging_kmap_page(i+hhdm_off, i, PTE_WRITABLE);
|
struct limine_memmap_entry* entry = boot_ctx.mmap->entries[i];
|
||||||
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE);
|
if (entry->length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint64_t top = entry->base + entry->length;
|
||||||
|
if (top > max_phys) {
|
||||||
|
max_phys = top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8GB
|
||||||
|
if (max_phys > PAGING_MAX_PHYS) {
|
||||||
|
DEBUG("WARNING: max_phys capped to PAGING_MAX_PHYS (from max_phys=%p)", max_phys);
|
||||||
|
max_phys = PAGING_MAX_PHYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HHDM map up to max_phys or PAGING_MAX_PHYS, whichever is smaller, using given offset
|
||||||
|
for (uint64_t i=0; i<max_phys; i += PAGE_SIZE) {
|
||||||
|
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE | PTE_PRESENT);
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); page_count = 0;
|
DEBUG("Mapped %u pages up to 0x%p (HHDM)", page_count, max_phys); page_count = 0;
|
||||||
|
|
||||||
// Map the kernel (according to virt/phys_base given by Limine)
|
// Map the kernel (according to virt/phys_base given by Limine)
|
||||||
// SOME DAY when we want a safer kernel we should map .text as Read/Exec
|
// SOME DAY when we want a safer kernel we should map .text as Read/Exec
|
||||||
// .rodata as Read and .data as Read/Write
|
// .rodata as Read and .data as Read/Write
|
||||||
// For now who gives a shit, let's RWX all kernel
|
// For now who gives a shit, let's RWX all kernel
|
||||||
for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE)
|
for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE) {
|
||||||
{
|
|
||||||
//paging_kmap_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
|
||||||
paging_map_page(kernel_pml4, kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
paging_map_page(kernel_pml4, kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
@@ -144,16 +202,14 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra
|
|||||||
uint64_t fb_size = fb->pitch * fb->height;
|
uint64_t fb_size = fb->pitch * fb->height;
|
||||||
uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE;
|
uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE;
|
||||||
|
|
||||||
// Map the framebuffer (with cache-disable & write-through)
|
// Map the framebuffer (PWT set, and no PCD means PAT1 [Write-Combining] for this region)
|
||||||
for (uint64_t i=0; i<fb_pages; i++)
|
for (uint64_t i=0; i<fb_pages; i++) {
|
||||||
{
|
paging_map_page(kernel_pml4, fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PWT);
|
||||||
//paging_kmap_page(fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
|
||||||
paging_map_page(kernel_pml4, fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
|
||||||
page_count++;
|
page_count++;
|
||||||
}
|
}
|
||||||
DEBUG("Mapped %u pages for framebuffer", page_count);
|
DEBUG("Mapped %u pages for framebuffer", page_count);
|
||||||
|
|
||||||
// Finally, we load the physical address of our PML4 (root table) into cr3
|
// Finally, we load the physical address of our PML4 (root table) into cr3
|
||||||
load_cr3(VIRT_TO_PHYS(kernel_pml4));
|
load_cr3(VIRT_TO_PHYS(kernel_pml4));
|
||||||
DEBUG("cr3 loaded, we're still alive");
|
DEBUG("Loaded kernel PML4 into CR3");
|
||||||
}
|
}
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
// OMG here we are. I'm cooked.
|
|
||||||
|
|
||||||
/*
|
|
||||||
pmm - Physical Memory Manager
|
|
||||||
will manage 4kb pages physically
|
|
||||||
it will probably need to get some info from Limine,
|
|
||||||
to see which pages are used by kernel/bootloader/mmio/fb etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "paging.h"
|
|
||||||
#include <limine.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <kernel.h>
|
|
||||||
#include "mem/misc/utils.h"
|
|
||||||
#include "pmm.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
First we'll have to discover the physical memory layout,
|
|
||||||
and for that we can use a Limine request.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
We will look for the biggest usable physical memory region
|
|
||||||
and use this for the bitmap. The reserved memory will be ignored.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct limine_memmap_entry* biggest_entry;
|
|
||||||
|
|
||||||
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
|
||||||
{
|
|
||||||
// Max length of a usable memory region
|
|
||||||
uint64_t length_max = 0;
|
|
||||||
uint64_t offset = hhdm->offset;
|
|
||||||
|
|
||||||
DEBUG("Usable Memory:");
|
|
||||||
for (size_t i=0; i<memmap->entry_count; i++)
|
|
||||||
{
|
|
||||||
struct limine_memmap_entry* entry = memmap->entries[i];
|
|
||||||
|
|
||||||
if (entry->type == LIMINE_MEMMAP_USABLE)
|
|
||||||
{
|
|
||||||
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length,
|
|
||||||
entry->base+offset, entry->base+entry->length+offset);
|
|
||||||
if (entry->length > length_max)
|
|
||||||
{
|
|
||||||
length_max = entry->length;
|
|
||||||
biggest_entry = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("Biggest usable memory region:");
|
|
||||||
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", biggest_entry->base, biggest_entry->base + biggest_entry->length,
|
|
||||||
biggest_entry->base+offset, biggest_entry->base+biggest_entry->length+offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offset from Higher Half Direct Map
|
|
||||||
uint64_t hhdm_off;
|
|
||||||
|
|
||||||
static uintptr_t g_freelist = 0;
|
|
||||||
|
|
||||||
uintptr_t pmm_alloc()
|
|
||||||
{
|
|
||||||
if (!g_freelist) return 0;
|
|
||||||
uintptr_t addr = g_freelist;
|
|
||||||
g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist);
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pmm_free(uintptr_t addr)
|
|
||||||
{
|
|
||||||
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
|
||||||
g_freelist = addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pmm_init_freelist()
|
|
||||||
{
|
|
||||||
// We simply call pmm_free() on each page that is marked USABLE
|
|
||||||
// in our big memory region.
|
|
||||||
uint64_t base = ALIGN_UP(biggest_entry->base, PAGE_SIZE);
|
|
||||||
uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE);
|
|
||||||
|
|
||||||
uint64_t page_count=0;
|
|
||||||
for (uint64_t addr = base; addr < end; addr += PAGE_SIZE)
|
|
||||||
{
|
|
||||||
pmm_free(addr);
|
|
||||||
//DEBUG("page %u lives at phys 0x%p (virt 0x%p)", page_count, addr, PHYS_TO_VIRT(addr));
|
|
||||||
page_count++;
|
|
||||||
}
|
|
||||||
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
|
||||||
{
|
|
||||||
hhdm_off = hhdm->offset;
|
|
||||||
pmm_find_biggest_usable_region(memmap, hhdm);
|
|
||||||
//pmm_allocate_bitmap(hhdm); too complicated for my small brain
|
|
||||||
|
|
||||||
// Now we have biggest USABLE region,
|
|
||||||
// so to populate the free list we just iterate through it
|
|
||||||
pmm_init_freelist();
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef PAGING_PMM_H
|
|
||||||
#define PAGING_PMM_H
|
|
||||||
|
|
||||||
#include <limine.h>
|
|
||||||
|
|
||||||
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm);
|
|
||||||
void pmm_free(uintptr_t addr);
|
|
||||||
uintptr_t pmm_alloc();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
110
src/mem/pmm.c
Normal file
110
src/mem/pmm.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Physical memory manager from freelist
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
pmm - Physical Memory Manager
|
||||||
|
will manage 4kb pages physically
|
||||||
|
it will probably need to get some info from Limine,
|
||||||
|
to see which pages are used by kernel/bootloader/mmio/fb etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <mem/paging.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <mem/utils.h>
|
||||||
|
#include <mem/pmm.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
First we'll have to discover the physical memory layout,
|
||||||
|
and for that we can use a Limine request.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Offset from Higher Half Direct Map
|
||||||
|
uint64_t hhdm_off;
|
||||||
|
|
||||||
|
static uintptr_t g_freelist = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_alloc - Allocate a physical page
|
||||||
|
*
|
||||||
|
* This function allocates a single physical page (frame)
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <addr> - Address for the allocated page
|
||||||
|
*/
|
||||||
|
uintptr_t pmm_alloc()
|
||||||
|
{
|
||||||
|
if (!g_freelist) {
|
||||||
|
panic(NULL, "PMM is out of memory!");
|
||||||
|
}
|
||||||
|
uintptr_t addr = g_freelist;
|
||||||
|
g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_free - Frees a memory page
|
||||||
|
* @addr: Address to the page
|
||||||
|
*/
|
||||||
|
void pmm_free(uintptr_t addr)
|
||||||
|
{
|
||||||
|
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
||||||
|
g_freelist = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_init_freelist - PMM freelist initialization
|
||||||
|
*
|
||||||
|
* This function marks the biggest memory region as
|
||||||
|
* free, so we can use it in pmm_alloc.
|
||||||
|
*/
|
||||||
|
static void pmm_init_freelist(struct limine_memmap_response* memmap)
|
||||||
|
{
|
||||||
|
uint64_t total_pages = 0;
|
||||||
|
|
||||||
|
for (size_t i=0; i<memmap->entry_count; i++) {
|
||||||
|
struct limine_memmap_entry* entry = memmap->entries[i];
|
||||||
|
|
||||||
|
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
||||||
|
uint64_t base = ALIGN_UP(entry->base, PAGE_SIZE);
|
||||||
|
uint64_t end = ALIGN_DOWN(entry->base + entry->length, PAGE_SIZE);
|
||||||
|
|
||||||
|
if (end > PAGING_MAX_PHYS) {
|
||||||
|
end = PAGING_MAX_PHYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Region above PAGING_MAX_PHYS
|
||||||
|
if (base >= end) continue;
|
||||||
|
|
||||||
|
for (uint64_t addr = base; addr < end; addr += PAGE_SIZE) {
|
||||||
|
pmm_free(addr);
|
||||||
|
total_pages++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("%u frames in freelist, %u bytes available (%u MB)", total_pages, total_pages*PAGE_SIZE, total_pages*PAGE_SIZE/1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_init - Physical memory manager initialization
|
||||||
|
* @boot_ctx: Boot context structure
|
||||||
|
*
|
||||||
|
* This function prepares the PMM for use.
|
||||||
|
* The PMM works with a freelist.
|
||||||
|
*/
|
||||||
|
void pmm_init(struct boot_context boot_ctx)
|
||||||
|
{
|
||||||
|
hhdm_off = boot_ctx.hhdm->offset;
|
||||||
|
//pmm_find_biggest_usable_region(boot_ctx.mmap, boot_ctx.hhdm);
|
||||||
|
|
||||||
|
// Now we have biggest USABLE region,
|
||||||
|
// so to populate the free list we just iterate through it
|
||||||
|
pmm_init_freelist(boot_ctx.mmap);
|
||||||
|
}
|
||||||
122
src/mem/utils.c
Normal file
122
src/mem/utils.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Common memory utilities
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <string/string.h>
|
||||||
|
|
||||||
|
// We won't be linked to standard library, but still need the basic mem* functions
|
||||||
|
// so everything goes allright with the compiler
|
||||||
|
|
||||||
|
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
||||||
|
// do more optimization on them (and as it's a much used function, it's good to
|
||||||
|
// be able to do that)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memcpy - Copy memory from one place to another
|
||||||
|
* @dest: pointer to the destination region
|
||||||
|
* @src: pointer to the source region
|
||||||
|
* @n: amount of bytes to copy
|
||||||
|
*
|
||||||
|
* This function copies n bytes of memory from
|
||||||
|
* src to dest.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <dest> - Pointer to destination region
|
||||||
|
*/
|
||||||
|
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
||||||
|
const uint8_t* restrict psrc = (const uint8_t* restrict)src;
|
||||||
|
|
||||||
|
for (size_t i=0; i<n; i++) {
|
||||||
|
pdest[i] = psrc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memset - Sets a memory region to given byte
|
||||||
|
* @s: pointer to memory region
|
||||||
|
* @c: byte to be written
|
||||||
|
* @n: amount of bytes to write
|
||||||
|
*
|
||||||
|
* This function writes n times the byte c
|
||||||
|
* to the memory region pointed to by s.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <s> - Pointer to memory region
|
||||||
|
*/
|
||||||
|
void* memset(void* s, int c, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t* p = (uint8_t*)s;
|
||||||
|
|
||||||
|
for (size_t i=0; i<n; i++) {
|
||||||
|
p[i] = (uint8_t)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memmove - Move memory from one place to another
|
||||||
|
* @dest: pointer to the destination region
|
||||||
|
* @src: pointer to the source region
|
||||||
|
* @n: amount of bytes to move
|
||||||
|
*
|
||||||
|
* This function moves n bytes of memory from
|
||||||
|
* src to dest.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <dest> - Pointer to destination region
|
||||||
|
*/
|
||||||
|
void* memmove(void *dest, const void* src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t* pdest = (uint8_t*)dest;
|
||||||
|
const uint8_t* psrc = (uint8_t*)src;
|
||||||
|
|
||||||
|
if (src > dest) {
|
||||||
|
for (size_t i=0; i<n; i++) {
|
||||||
|
pdest[i] = psrc[i];
|
||||||
|
}
|
||||||
|
} else if (src < dest) {
|
||||||
|
for (size_t i=n; i>0; i--) {
|
||||||
|
pdest[i-1] = psrc[i-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memcmp - Compare two memory regions
|
||||||
|
* @s1: pointer to the first region
|
||||||
|
* @s2: pointer to the second region
|
||||||
|
* @n: amount of bytes to compare
|
||||||
|
*
|
||||||
|
* This function compares n bytes of memory
|
||||||
|
* bewteen regions pointed to by s1 and s2.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %0 - if s1 and s2 are equal
|
||||||
|
* %-1 - if s1 is smaller than s2
|
||||||
|
* %1 - if s1 is greater than s2
|
||||||
|
*/
|
||||||
|
int memcmp(const void* s1, const void* s2, size_t n)
|
||||||
|
{
|
||||||
|
const uint8_t* p1 = (const uint8_t*)s1;
|
||||||
|
const uint8_t* p2 = (const uint8_t*)s2;
|
||||||
|
|
||||||
|
for (size_t i=0; i<n; i++) {
|
||||||
|
if (p1[i] != p2[i]) {
|
||||||
|
return p1[i] < p2[i] ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Virtual memory manager
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The VMM (virtual memory manager) will have two roles:
|
The VMM (virtual memory manager) will have two roles:
|
||||||
- mapping pages
|
- mapping pages
|
||||||
@@ -7,10 +13,10 @@ 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 "vmm.h"
|
#include <mem/vmm.h>
|
||||||
#include "paging.h"
|
#include <mem/paging.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "pmm.h"
|
#include <mem/pmm.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
|
||||||
void* vmm_pt_root = 0;
|
void* vmm_pt_root = 0;
|
||||||
@@ -18,6 +24,14 @@ void* vmm_pt_root = 0;
|
|||||||
// Linked list head for virtual memory objects
|
// Linked list head for virtual memory objects
|
||||||
struct vm_object* vm_objs = NULL;
|
struct vm_object* vm_objs = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Will have to be rewritten and expanded,
|
||||||
|
* to prepare for userspace.
|
||||||
|
* The platform-agnostic flags will be removed
|
||||||
|
* because as long as the kernel is x86 only,
|
||||||
|
* we don't need over complication.
|
||||||
|
* Plus I don't plan to port to other architectures
|
||||||
|
*/
|
||||||
|
|
||||||
uint64_t convert_x86_vm_flags(size_t flags)
|
uint64_t convert_x86_vm_flags(size_t flags)
|
||||||
{
|
{
|
||||||
@@ -49,7 +63,7 @@ void vmm_setup_pt_root()
|
|||||||
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
|
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* vmm_alloc(size_t length, size_t flags)
|
/* void* vmm_alloc(size_t length, size_t flags)
|
||||||
{
|
{
|
||||||
// We will try to allocate at least length bytes, which have to be rounded UP to
|
// We will try to allocate at least length bytes, which have to be rounded UP to
|
||||||
// the next page so its coherent with the PMM
|
// the next page so its coherent with the PMM
|
||||||
@@ -58,9 +72,10 @@ void* vmm_alloc(size_t length, size_t flags)
|
|||||||
// Need to implement this (as linked list)
|
// Need to implement this (as linked list)
|
||||||
// but for now kernel heap is sufficient
|
// but for now kernel heap is sufficient
|
||||||
// The VMM will prob be more useful when we have userspace
|
// The VMM will prob be more useful when we have userspace
|
||||||
}
|
} */
|
||||||
|
|
||||||
void vmm_init()
|
void vmm_init()
|
||||||
{
|
{
|
||||||
vmm_setup_pt_root();
|
// NO U
|
||||||
|
//vmm_setup_pt_root();
|
||||||
}
|
}
|
||||||
199
src/sched/process.c
Normal file
199
src/sched/process.c
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Process linked list implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sched/process.h>
|
||||||
|
#include <mem/kheap.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <string/string.h>
|
||||||
|
#include <mem/gdt.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
|
||||||
|
#include <io/term/flanterm.h>
|
||||||
|
extern struct flanterm_context* ft_ctx;
|
||||||
|
|
||||||
|
struct process_t* processes_list;
|
||||||
|
struct process_t* current_process;
|
||||||
|
|
||||||
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
size_t next_free_pid = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_init - Initializes process list
|
||||||
|
*/
|
||||||
|
void process_init()
|
||||||
|
{
|
||||||
|
processes_list = NULL;
|
||||||
|
current_process = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_display_list - Debug function to display processes
|
||||||
|
* @processes_list: head of the process linked list
|
||||||
|
*
|
||||||
|
* This function prints the linked list of processes
|
||||||
|
* to the DEBUG output.
|
||||||
|
*/
|
||||||
|
void process_display_list(struct process_t* processes_list)
|
||||||
|
{
|
||||||
|
int process_view_id = 0;
|
||||||
|
struct process_t* tmp = processes_list;
|
||||||
|
while (tmp != NULL) {
|
||||||
|
DEBUG("{%d: %p} -> ", process_view_id, tmp);
|
||||||
|
tmp = tmp->next;
|
||||||
|
process_view_id++;
|
||||||
|
}
|
||||||
|
DEBUG("NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_create - Create a process
|
||||||
|
* @name: name of the process
|
||||||
|
* @function: beginning of process executable code
|
||||||
|
* @arg: (optional) argument provided to process
|
||||||
|
*
|
||||||
|
* This function creates a process, gives it all
|
||||||
|
* necessary context and a stack, and adds the
|
||||||
|
* process to the linked list.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <proc> - pointer to created process
|
||||||
|
*/
|
||||||
|
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
||||||
|
{
|
||||||
|
CLEAR_INTERRUPTS;
|
||||||
|
struct process_t* proc = (struct process_t*)kmalloc(sizeof(struct process_t));
|
||||||
|
struct cpu_status_t* ctx = (struct cpu_status_t*)kmalloc(sizeof(struct cpu_status_t));
|
||||||
|
|
||||||
|
// No more memory?
|
||||||
|
if (!proc) return NULL;
|
||||||
|
if (!ctx) return NULL;
|
||||||
|
|
||||||
|
strncpy(proc->name, name, PROCESS_NAME_MAX);
|
||||||
|
proc->pid = next_free_pid++;
|
||||||
|
proc->status = READY;
|
||||||
|
|
||||||
|
uint64_t* stack_top = (uint64_t*)kalloc_stack();
|
||||||
|
// push return address to the stack so when "ret" hits we jmp to exit instead of idk what
|
||||||
|
// stack grows DOWNWARDS!!
|
||||||
|
*(--stack_top) = (uint64_t)process_exit;
|
||||||
|
|
||||||
|
proc->context = ctx;
|
||||||
|
proc->context->iret_ss = KERNEL_DATA_SEGMENT; // process will live in kernel mode
|
||||||
|
proc->context->iret_rsp = (uint64_t)stack_top;
|
||||||
|
proc->context->iret_flags = 0x202; //bit 2 and 9 set (Interrupt Flag)
|
||||||
|
proc->context->iret_cs = KERNEL_CODE_SEGMENT;
|
||||||
|
proc->context->iret_rip = (uint64_t)function; // beginning of executable code
|
||||||
|
proc->context->rdi = (uint64_t)arg; // 1st arg is in rdi (as per x64 calling convention)
|
||||||
|
proc->context->rbp = 0;
|
||||||
|
|
||||||
|
// Kernel PML4 as it already maps code/stack (when switching to userland we'll have to change that)
|
||||||
|
proc->root_page_table = kernel_pml4;
|
||||||
|
|
||||||
|
proc->next = 0;
|
||||||
|
|
||||||
|
process_add(&processes_list, proc);
|
||||||
|
|
||||||
|
SET_INTERRUPTS;
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_add - Add a process to the end of the linked list
|
||||||
|
* @processes_list: pointer to the head of the linked list
|
||||||
|
* @process: process to add at the end of the linked list
|
||||||
|
*/
|
||||||
|
void process_add(struct process_t** processes_list, struct process_t* process)
|
||||||
|
{
|
||||||
|
if (!process) return;
|
||||||
|
process->next = NULL;
|
||||||
|
|
||||||
|
if (*processes_list == NULL) {
|
||||||
|
// List is empty
|
||||||
|
*processes_list = process;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct process_t* tmp = *processes_list;
|
||||||
|
while (tmp->next != NULL) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
// We're at last process before NULL
|
||||||
|
tmp->next = process;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_delete - Delete a process from the linked list
|
||||||
|
* @processes_list: pointer to head of linked list
|
||||||
|
* @process: the process to delete from the list
|
||||||
|
*/
|
||||||
|
void process_delete(struct process_t** processes_list, struct process_t* process)
|
||||||
|
{
|
||||||
|
if (!processes_list || !*processes_list || !process) return;
|
||||||
|
|
||||||
|
if (*processes_list == process) {
|
||||||
|
// process to delete is at head
|
||||||
|
*processes_list = process->next;
|
||||||
|
process->next = NULL;
|
||||||
|
kfree(process);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct process_t* tmp = *processes_list;
|
||||||
|
while (tmp->next && tmp->next != process) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp->next == NULL) {
|
||||||
|
// Didn't find the process
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're at process before the one we want to delete
|
||||||
|
tmp->next = process->next;
|
||||||
|
process->next = NULL;
|
||||||
|
kfree(process);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_get_next - Get the next process (unused)
|
||||||
|
* @process: pointer to process
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <process->next> - process right after the one specified
|
||||||
|
*/
|
||||||
|
struct process_t* process_get_next(struct process_t* process)
|
||||||
|
{
|
||||||
|
if (!process) return NULL;
|
||||||
|
return process->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_exit - Exit from a process
|
||||||
|
*
|
||||||
|
* This function is pushed to all process stacks, as a last
|
||||||
|
* return address. Once the process is done executing, it
|
||||||
|
* ends up here.
|
||||||
|
*
|
||||||
|
* Process is marked as DEAD, and then execution loops.
|
||||||
|
* Next time the scheduler sees the process, it will
|
||||||
|
* automatically delete it from the linked list.
|
||||||
|
*/
|
||||||
|
void process_exit()
|
||||||
|
{
|
||||||
|
DEBUG("Exiting from process '%s'", current_process->name);
|
||||||
|
CLEAR_INTERRUPTS;
|
||||||
|
if (current_process) {
|
||||||
|
current_process->status = DEAD;
|
||||||
|
}
|
||||||
|
SET_INTERRUPTS;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
asm("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/sched/scheduler.c
Normal file
80
src/sched/scheduler.c
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Round-robin scheduler
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <sched/process.h>
|
||||||
|
#include <mem/paging.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <io/serial/serial.h>
|
||||||
|
|
||||||
|
extern struct process_t* processes_list;
|
||||||
|
extern struct process_t* current_process;
|
||||||
|
extern struct process_t* idle_proc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduler_init - Choose the first process
|
||||||
|
*/
|
||||||
|
void scheduler_init()
|
||||||
|
{
|
||||||
|
current_process = processes_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduler_schedule - Main scheduling routine
|
||||||
|
* @context: CPU context of previous process
|
||||||
|
*
|
||||||
|
* Chooses the next process that we should run.
|
||||||
|
* The routine is executed every SCHEDULER_QUANTUM ticks.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <context> - CPU context for next process
|
||||||
|
*/
|
||||||
|
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
|
||||||
|
{
|
||||||
|
if (context == NULL) {
|
||||||
|
panic(NULL, "Scheduler called with NULL context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_process == NULL) {
|
||||||
|
// If no more processes, then set IDLE as the current process, that's it.
|
||||||
|
current_process = idle_proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_process == idle_proc && current_process->next == NULL)
|
||||||
|
{
|
||||||
|
return idle_proc->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_process->context = context;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct process_t* prev_process = current_process;
|
||||||
|
if (current_process->next != NULL) {
|
||||||
|
current_process = current_process->next;
|
||||||
|
} else {
|
||||||
|
current_process = processes_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_process != NULL && current_process->status == DEAD) {
|
||||||
|
process_delete(&prev_process, current_process);
|
||||||
|
current_process = NULL;
|
||||||
|
return idle_proc->context;
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//DEBUG("current_process={pid=%u, name='%s', root_page_table[virt]=%p}", current_process->pid, current_process->name, current_process->root_page_table);
|
||||||
|
|
||||||
|
load_cr3(VIRT_TO_PHYS((uint64_t)current_process->root_page_table));
|
||||||
|
//DEBUG("Loaded process PML4 into CR3");
|
||||||
|
|
||||||
|
return current_process->context;
|
||||||
|
}
|
||||||
44
src/sched/spinlock.c
Normal file
44
src/sched/spinlock.c
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Spinlock implementation
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <sched/spinlock.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* spinlock_acquire - Lock a lock
|
||||||
|
* @lock: pointer to desired spinlock
|
||||||
|
*
|
||||||
|
* Saves the RFLAGS register, then acquires a lock.
|
||||||
|
* Pause instruction is used to ease the CPU.
|
||||||
|
*/
|
||||||
|
void spinlock_acquire(struct spinlock_t* lock)
|
||||||
|
{
|
||||||
|
uint64_t rflags;
|
||||||
|
asm volatile("pushfq ; pop %0 ; cli" : "=rm"(rflags) : : "memory");
|
||||||
|
|
||||||
|
while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE)) {
|
||||||
|
__builtin_ia32_pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock->rflags = rflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* spinlock_release - Unlock a lock
|
||||||
|
* @lock: pointer to desired spinlock
|
||||||
|
*
|
||||||
|
* Gets saved RFLAGS register from the lock and
|
||||||
|
* unlocks it (clears locked state).
|
||||||
|
* RFLAGS is then restored.
|
||||||
|
*/
|
||||||
|
void spinlock_release(struct spinlock_t* lock)
|
||||||
|
{
|
||||||
|
uint64_t rflags = lock->rflags;
|
||||||
|
__atomic_clear(&lock->locked, __ATOMIC_RELEASE);
|
||||||
|
asm volatile("push %0 ; popfq" : : "rm"(rflags) : "memory");
|
||||||
|
}
|
||||||
@@ -1,6 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief String manipulation utilities
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strcpy - copy a NULL-terminated string
|
||||||
|
* @dest: destination buffer where the string is copied
|
||||||
|
* @src: source string to copy from
|
||||||
|
*
|
||||||
|
* Copies the string pointed to by @src (including the terminating
|
||||||
|
* NULL byte) into the buffer pointed to by @dest.
|
||||||
|
*
|
||||||
|
* Return: pointer to the destination string (@dest)
|
||||||
|
*/
|
||||||
char* strcpy(char *dest, const char *src)
|
char* strcpy(char *dest, const char *src)
|
||||||
{
|
{
|
||||||
char *temp = dest;
|
char *temp = dest;
|
||||||
while((*dest++ = *src++));
|
while((*dest++ = *src++));
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strcat - append a NUL-terminated string
|
||||||
|
* @dest: destination buffer containing the initial string
|
||||||
|
* @src: source string to append
|
||||||
|
*
|
||||||
|
* Appends the string pointed to by @src to the end of the string
|
||||||
|
* pointed to by @dest. The terminating NUL byte in @dest is
|
||||||
|
* overwritten and a new terminating NUL byte is added.
|
||||||
|
*
|
||||||
|
* The destination buffer must be large enough to hold the result.
|
||||||
|
*
|
||||||
|
* Taken from: https://stackoverflow.com/questions/2488563/strcat-implementation
|
||||||
|
*
|
||||||
|
* Return: pointer to the destination string (@dest)
|
||||||
|
*/
|
||||||
|
char *strcat(char *dest, const char *src)
|
||||||
|
{
|
||||||
|
size_t i,j;
|
||||||
|
for (i = 0; dest[i] != '\0'; i++);
|
||||||
|
|
||||||
|
for (j = 0; src[j] != '\0'; j++)
|
||||||
|
dest[i+j] = src[j];
|
||||||
|
|
||||||
|
dest[i+j] = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strncpy - copy a string with length limit
|
||||||
|
* @dst: destination buffer
|
||||||
|
* @src: source string
|
||||||
|
* @n: maximum number of bytes to copy
|
||||||
|
*
|
||||||
|
* Copies up to @n bytes from @src to @dst. Copying stops early if a
|
||||||
|
* NULL byte is encountered in @src. If @src is shorter than @n, the
|
||||||
|
* remaining bytes in @dst are left unchanged in this implementation.
|
||||||
|
*
|
||||||
|
* Note: This differs slightly from the standard strncpy behavior,
|
||||||
|
* which pads the remaining bytes with NULL.
|
||||||
|
*
|
||||||
|
* Taken from: https://stackoverflow.com/questions/14159625/implementation-of-strncpy
|
||||||
|
*/
|
||||||
|
void strncpy(char* dst, const char* src, size_t n)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
while(i++ != n && (*dst++ = *src++));
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef STRING_H
|
|
||||||
#define STRING_H
|
|
||||||
|
|
||||||
char *strcpy(char *dest, const char *src);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @author xamidev <xamidev@riseup.net>
|
||||||
|
* @brief Programmable Interval Timer init and enabling
|
||||||
|
* @license GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "io/serial/serial.h"
|
#include <io/serial/serial.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For now, the timer module will be using the PIC.
|
For now, the timer module will be using the PIC.
|
||||||
@@ -12,6 +19,15 @@ interested in multi-core functionnality like SMP)
|
|||||||
|
|
||||||
volatile uint64_t ticks = 0;
|
volatile uint64_t ticks = 0;
|
||||||
|
|
||||||
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pic_remap - Remap the Programmable Interrupt Controller
|
||||||
|
*
|
||||||
|
* By default, interrupts are mapped at the wrong place.
|
||||||
|
* This function remaps interrupt numbers so interrupts
|
||||||
|
* don't conflict with each other.
|
||||||
|
*/
|
||||||
void pic_remap()
|
void pic_remap()
|
||||||
{
|
{
|
||||||
uint8_t master_mask = inb(0x21);
|
uint8_t master_mask = inb(0x21);
|
||||||
@@ -39,22 +55,31 @@ void pic_remap()
|
|||||||
outb(0xA1, slave_mask);
|
outb(0xA1, slave_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pic_enable - Enable the Programmable Interrupt Controller
|
||||||
|
*
|
||||||
|
* This function enables IRQ0 and IRQ1, which correspond to
|
||||||
|
* the timer and keyboard interrupts, respectively.
|
||||||
|
*/
|
||||||
void pic_enable()
|
void pic_enable()
|
||||||
{
|
{
|
||||||
// Enabling IRQ0 (unmasking it) but not the others
|
// Enabling IRQ0 (unmasking it) but not the others
|
||||||
uint8_t mask = inb(0x21);
|
uint8_t mask = inb(0x21);
|
||||||
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
|
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
|
||||||
mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
|
//mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
|
||||||
outb(0x21, mask);
|
outb(0x21, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Base frequency = 1.193182 MHz
|
* pit_init - Initialization of the Programmable Interval Timer
|
||||||
1 tick per ms (divide by 1000) = roughly 1193 Hz
|
*
|
||||||
|
* The PIT is the simplest timer we can get working on x86.
|
||||||
|
* It has a base frequency of 1.193182 MHz.
|
||||||
|
* A custom frequency can be set using TIMER_FREQUENCY macro.
|
||||||
*/
|
*/
|
||||||
void pit_init()
|
void pit_init()
|
||||||
{
|
{
|
||||||
uint32_t frequency = 1000; // 1 kHz
|
uint32_t frequency = TIMER_FREQUENCY;
|
||||||
uint32_t divisor = 1193182 / frequency;
|
uint32_t divisor = 1193182 / frequency;
|
||||||
|
|
||||||
// Set PIT to mode 3, channel 0
|
// Set PIT to mode 3, channel 0
|
||||||
@@ -65,6 +90,25 @@ void pit_init()
|
|||||||
outb(0x40, (divisor >> 8) & 0xFF);
|
outb(0x40, (divisor >> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* timer_wait - Wait for X ticks
|
||||||
|
*
|
||||||
|
* By default, the timer frequency is 1000Hz, meaning
|
||||||
|
* ticks are equal to milliseconds.
|
||||||
|
*/
|
||||||
|
void timer_wait(uint64_t wait_ticks)
|
||||||
|
{
|
||||||
|
uint64_t then = ticks + wait_ticks;
|
||||||
|
while (ticks < then) {
|
||||||
|
asm("hlt");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* timer_init - Initialization of the timer
|
||||||
|
*
|
||||||
|
* This function wakes the PIT.
|
||||||
|
*/
|
||||||
void timer_init()
|
void timer_init()
|
||||||
{
|
{
|
||||||
// Remapping the PIC, because at startup it conflicts with
|
// Remapping the PIC, because at startup it conflicts with
|
||||||
@@ -74,4 +118,5 @@ void timer_init()
|
|||||||
pic_enable();
|
pic_enable();
|
||||||
pit_init();
|
pit_init();
|
||||||
DEBUG("PIT initialized");
|
DEBUG("PIT initialized");
|
||||||
|
init.timer = true;
|
||||||
}
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef TIMER_H
|
|
||||||
#define TIMER_H
|
|
||||||
|
|
||||||
void timer_init();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
33
symbols.py
Normal file
33
symbols.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Make assembly file from ELF symbols map
|
||||||
|
# Then link it to kernel so it's aware of symbol names
|
||||||
|
# then we can use that for the stack trace.
|
||||||
|
|
||||||
|
print("Extracting symbols from map file to assembly...")
|
||||||
|
|
||||||
|
with open("symbols.map", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
symbols = []
|
||||||
|
for line in lines:
|
||||||
|
parts = line.split()
|
||||||
|
# output is formed like "address name"
|
||||||
|
symbols.append((parts[0], parts[1]))
|
||||||
|
|
||||||
|
with open("symbols.S", "w") as f:
|
||||||
|
f.write("section .rodata\n")
|
||||||
|
f.write("global symbol_table\n")
|
||||||
|
f.write("global symbol_count\n")
|
||||||
|
f.write("symbol_table:\n")
|
||||||
|
|
||||||
|
for i, (addr, name) in enumerate(symbols):
|
||||||
|
f.write(f" dq 0x{addr}\n")
|
||||||
|
f.write(f" dq sym_name_{i}\n")
|
||||||
|
|
||||||
|
f.write("\nsymbol_count: dq " + str(len(symbols)) + "\n\n")
|
||||||
|
|
||||||
|
for i, (addr, name) in enumerate(symbols):
|
||||||
|
# escaping quotes
|
||||||
|
safe_name = name.replace('"', '\\"')
|
||||||
|
f.write(f'sym_name_{i}: db "{safe_name}", 0\n')
|
||||||
|
|
||||||
|
print("Done!")
|
||||||
BIN
zap-light16.psf
BIN
zap-light16.psf
Binary file not shown.
Reference in New Issue
Block a user