switch bootloader protocol to stivale2, os now 64-bit (very buggy because i still havent updated the GDT and IDT) and on the higher half, framebuffer, drawing text on the framebuffer with a font loaded from a psf file

This commit is contained in:
Tunacan 2021-07-02 21:49:20 +03:00
parent e46c2dbf0a
commit ef1ee78320
23 changed files with 687 additions and 292 deletions

5
.gitignore vendored
View file

@ -3,4 +3,7 @@ workspace.code-workspace
*.o
*.bin
*.iso
disk.img
*.elf
*.img
sysroot/
*.log

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule "limine"]
path = limine
url = https://github.com/limine-bootloader/limine.git
branch = v2.0-branch-binary

View file

@ -1,47 +1,68 @@
TARGET = i686-elf
ARCH = i386
CC = $(TARGET)-gcc
STRIP = $(TARGET)-strip
KERNEL := hhhos.elf
ISO := hhhos.iso
CC = x86_64-elf-gcc
NASM = nasm
QEMU = qemu-system-i386
MKDIR_P = mkdir -p
# User controllable CFLAGS.
CFLAGS = -Wall -Wextra -O3 -pipe
NASMFLAGS = -felf64
# Internal link flags that should not be changed by the user.
INTERNALLDFLAGS := \
-fno-pic -fpie \
-Wl,-static,-pie,--no-dynamic-linker,-ztext \
-static-pie \
-nostdlib \
-Tlinker.ld \
-z max-page-size=0x1000
# Internal C flags that should not be changed by the user.
INTERNALCFLAGS := \
-Iinclude \
-std=gnu11 \
-ffreestanding \
-fno-stack-protector \
-fno-pic -fpie \
-mno-80387 \
-mno-mmx \
-mno-3dnow \
-mno-sse \
-mno-sse2 \
-mno-red-zone
# Use find to glob all *.c files in the directory and extract the object names.
CFILES := $(shell find ./arch -type f -name '*.c')
OBJ := $(CFILES:.c=.o)
OBJ += font.o
CFLAGS += -std=gnu18 -ffreestanding -O2 -Wall -Wextra -mgeneral-regs-only
LDFLAGS += -ffreestanding -O2 -nostdlib -lgcc
NASMFLAGS += -felf32
# Targets that do not actually build a file of the same name.
.PHONY: all clean run
BIN = sysroot/boot/hhhos.bin
ISO = build/HhhOS.iso
run: $(ISO)
qemu-system-x86_64 -cdrom $(ISO) -serial file:serial.log -drive id=disk,file=build/disk.img,if=none,format=raw -device ide-hd,drive=disk,bus=ide.0
DIRS = build build/boot build/std build/drivers build/drivers/terminal build/drivers/idt build/drivers/device build/drivers/keyboard build/drivers/pic build/drivers/ide
$(ISO): $(KERNEL)
mkdir -p sysroot
cp -v $(KERNEL) limine.cfg limine/limine.sys limine/limine-cd.bin limine/limine-eltorito-efi.bin sysroot/
xorriso -as mkisofs -b limine-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table \
--efi-boot limine-eltorito-efi.bin -efi-boot-part --efi-boot-image --protective-msdos-label \
sysroot -o $(ISO)
OBJS = build/boot/boot.o build/boot/kernel.o build/std/string.o build/drivers/terminal/terminal.o build/drivers/idt/idt.o \
build/drivers/idt/isr.o build/std/util.o build/drivers/device/device.o build/drivers/keyboard/keyboard.o build/drivers/pic/pic.o \
build/drivers/ide/ide.o build/std/stdio.o
# Default target.
all: $(KERNEL)
ARCHDIR = arch/$(ARCH)/
# Link rules for the final kernel executable.
$(KERNEL): $(OBJ)
$(CC) $(OBJ) -o $@ $(INTERNALLDFLAGS)
.PHONY: all run clean build
# Compilation rules for *.c files.
%.o: %.c
$(CC) $(CFLAGS) $(INTERNALCFLAGS) -c $< -o $@
dirs:
${MKDIR_P} ${DIRS}
all: dirs $(BIN)
run: $(ISO)
$(QEMU) -cdrom $(ISO) -drive id=disk,file=build/disk.img,if=none,format=raw -device ide-hd,drive=disk,bus=ide.0
$(ISO): dirs $(BIN)
grub-mkrescue -o $(ISO) sysroot
$(BIN): $(OBJS)
$(CC) -T linker.ld -o $@ $^ $(LDFLAGS)
build/%.o: $(ARCHDIR)%.c
$(CC) -c $(CFLAGS) -Iinclude -o $@ $<
build/%.o: $(ARCHDIR)%.asm
$(NASM) $(NASMFLAGS) -o $@ $<
font.o:
objcopy -O elf64-x86-64 -B i386 -I binary font.psfu font.o
# Remove object files and the final executable.
clean:
rm -rf $(OBJS) $(BIN) $(ISO)
rm -rf $(KERNEL) $(OBJ) $(ISO)

41
arch/i386/boot/asm.asm Normal file
View file

@ -0,0 +1,41 @@
.intel_syntax noprefix
.rodata
gdt:
.null:
dq 0x0
.code:
dw 0xffff
dw 0x0000
db 0x00
db 10011010b
db 11001111b
db 0x00
.data:
dw 0xffff
dw 0x0000
db 0x00
db 10010010b
db 11001111b
db 0x00
.end:
.descriptor:
dw .end - gdt - 1
dd gdt
.text
.global gdt_load
gdt_load:
push eax
lgdt [gdt.descriptor]
jmp 0x08:.loaded_cs
.loaded_cs:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
pop eax
ret

View file

@ -1,160 +0,0 @@
; Declare constants for the multiboot header.
MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
MEMINFO equ 1 << 1 ; provide memory map
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
; Declare a multiboot header that marks the program as a kernel. These are magic
; values that are documented in the multiboot standard. The bootloader will
; search for this signature in the first 8 KiB of the kernel file, aligned at a
; 32-bit boundary. The signature is in its own section so the header can be
; forced to be within the first 8 KiB of the kernel file.
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
; The multiboot standard does not define the value of the stack pointer register
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
; small stack by creating a symbol at the bottom of it, then allocating 16384
; bytes for it, and finally creating a symbol at the top. The stack grows
; downwards on x86. The stack is in its own section so it can be marked nobits,
; which means the kernel file is smaller because it does not contain an
; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
; System V ABI standard and de-facto extensions. The compiler will assume the
; stack is properly aligned and failure to align the stack will result in
; undefined behavior.
section .bss
align 16
stack_bottom:
resb 16384 ; 16 KiB
stack_top:
section .rodata
gdt:
.null:
dq 0x0
.code:
dw 0xffff
dw 0x0000
db 0x00
db 10011010b
db 11001111b
db 0x00
.data:
dw 0xffff
dw 0x0000
db 0x00
db 10010010b
db 11001111b
db 0x00
.end:
.descriptor:
dw .end - gdt - 1
dd gdt
code_segment equ gdt.code - gdt
data_segment equ gdt.data - gdt
; The linker script specifies _start as the entry point to the kernel and the
; bootloader will jump to this position once the kernel has been loaded. It
; doesn't make sense to return from this function as the bootloader is gone.
; Declare _start as a function symbol with the given symbol size.
section .text
global _start:function (_start.end - _start)
gdt_load:
push eax
lgdt [gdt.descriptor]
jmp 0x08:.loaded_cs
.loaded_cs:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
pop eax
ret
global load_page_directory
load_page_directory:
push ebp
mov ebp, esp
mov eax, [esp + 8]
mov cr3, eax
mov esp, ebp
pop ebp
ret
global enable_paging
enable_paging:
push ebp
mov ebp, esp
mov eax, cr0
or eax, 80000001h
mov cr0, eax
mov esp, ebp
pop ebp
ret
_start:
; The bootloader has loaded us into 32-bit protected mode on a x86
; machine. Interrupts are disabled. Paging is disabled. The processor
; state is as defined in the multiboot standard. The kernel has full
; control of the CPU. The kernel can only make use of hardware features
; and any code it provides as part of itself. There's no printf
; function, unless the kernel provides its own <stdio.h> header and a
; printf implementation. There are no security restrictions, no
; safeguards, no debugging mechanisms, only what the kernel provides
; itself. It has absolute and complete power over the machine.
; To set up a stack, we set the esp register to point to the top of our
; stack (as it grows downwards on x86 systems). This is necessarily done
; in assembly as languages such as C cannot function without a stack.
mov esp, stack_top
;now enable SSE and the like
mov eax, cr0
and ax, 0xFFFB ;clear coprocessor emulation CR0.EM
or ax, 0x2 ;set coprocessor monitoring CR0.MP
mov cr0, eax
mov eax, cr4
or ax, 3 << 9 ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
mov cr4, eax
; This is a good place to initialize crucial processor state before the
; high-level kernel is entered. It's best to minimize the early
; environment where crucial features are offline. Note that the
; processor is not fully initialized yet: Features such as floating
; point instructions and instruction set extensions are not initialized
; yet. The GDT should be loaded here. Paging should be enabled here.
; C++ features such as global constructors and exceptions will require
; runtime support to work as well.
call gdt_load
; Enter the high-level kernel. The ABI requires the stack is 16-byte
; aligned at the time of the call instruction (which afterwards pushes
; the return pointer of size 4 bytes). The stack was originally 16-byte
; aligned above and we've since pushed a multiple of 16 bytes to the
; stack since (pushed 0 bytes so far) and the alignment is thus
; preserved and the call is well defined.
; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kmain
extern kmain
call kmain
; If the system has nothing more to do, put the computer into an
; infinite loop. To do that:
; 1) Disable interrupts with cli (clear interrupt enable in eflags).
; They are already disabled by the bootloader, so this is not needed.
; Mind that you might later enable interrupts and return from
; kmain (which is sort of nonsensical to do).
; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
; Since they are disabled, this will lock up the computer.
; 3) Jump to the hlt instruction if it ever wakes up due to a
; non-maskable interrupt occurring or due to system management mode.
cli
.hang: hlt
jmp .hang
.end:

View file

@ -1,16 +1,119 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stivale2.h>
#include <std/inline.h>
#include <std/util.h>
#include <stdbool.h>
#include <std/stdio.h>
#include <drivers/idt/isr.h>
#include <drivers/keyboard/keyboard.h>
#include <drivers/pic/pic.h>
#include <drivers/device/device.h>
#include <drivers/ide/ide.h>
#include <framebuffer.h>
#include <psf.h>
uint8_t is_running = 1;
// We need to tell the stivale bootloader where we want our stack to be.
// We are going to allocate our stack as an uninitialised array in .bss.
static uint8_t stack[4096];
void kmain();
bool is_running = true;
int keyboard_descriptor = 0;
int terminal_descriptor = 0;
// stivale2 uses a linked list of tags for both communicating TO the
// bootloader, or receiving info FROM it. More information about these tags
// is found in the stivale2 specification.
// We are now going to define a framebuffer header tag, which is mandatory when
// using the stivale2 terminal.
// This tag tells the bootloader that we want a graphical framebuffer instead
// of a CGA-compatible text mode. Omitting this tag will make the bootloader
// default to text mode, if available.
static struct stivale2_header_tag_framebuffer framebuffer_hdr_tag = {
// Same as above.
.tag = {
.identifier = STIVALE2_HEADER_TAG_FRAMEBUFFER_ID,
.next = 0
},
// set all the framebuffer specifics to 0 for it to pick the best it can.
.framebuffer_width = 800,
.framebuffer_height = 600,
.framebuffer_bpp = 32
};
// The stivale2 specification says we need to define a "header structure".
// This structure needs to reside in the .stivale2hdr ELF section in order
// for the bootloader to find it. We use this __attribute__ directive to
// tell the compiler to put the following structure in said section.
__attribute__((section(".stivale2hdr"), used))
static struct stivale2_header stivale_hdr = {
// The entry_point member is used to specify an alternative entry
// point that the bootloader should jump to instead of the executable's
// ELF entry point. We do not care about that so we leave it zeroed.
.entry_point = 0,
// Let's tell the bootloader where our stack is.
// We need to add the sizeof(stack) since in x86(_64) the stack grows
// downwards.
.stack = (uintptr_t)stack + sizeof(stack),
// Bit 1, if set, causes the bootloader to return to us pointers in the
// higher half, which we likely want.
.flags = (1 << 1),
// This header structure is the root of the linked list of header tags and
// points to the first one in the linked list.
.tags = (uintptr_t)&framebuffer_hdr_tag
};
// We will now write a helper function which will allow us to scan for tags
// that we want FROM the bootloader (structure tags).
void *stivale2_get_tag(struct stivale2_struct *stivale2_struct, uint64_t id) {
struct stivale2_tag *current_tag = (void *)stivale2_struct->tags;
for (;;) {
// If the tag pointer is NULL (end of linked list), we did not find
// the tag. Return NULL to signal this.
if (current_tag == NULL) {
return NULL;
}
// Check whether the identifier matches. If it does, return a pointer
// to the matching tag.
if (current_tag->identifier == id) {
return current_tag;
}
// Get a pointer to the next tag in the linked list and repeat.
current_tag = (void *)current_tag->next;
}
}
framebuffer_t *framebuffer;
// The following will be our kernel's entry point.
void _start(struct stivale2_struct *stivale2_struct) {
struct stivale2_struct_tag_framebuffer *tagfb = stivale2_get_tag(stivale2_struct, STIVALE2_STRUCT_TAG_FRAMEBUFFER_ID);
if (tagfb == NULL) {
printf("Requested stivale2 tags were not found, hanging...");
for (;;) {
asm ("hlt");
}
}
framebuffer->address = (uint8_t*)tagfb->framebuffer_addr;
framebuffer->width = tagfb->framebuffer_width;
framebuffer->height = tagfb->framebuffer_height;
framebuffer->depth = tagfb->framebuffer_bpp;
framebuffer->pitch = tagfb->framebuffer_pitch;
framebuffer->pixelwidth = tagfb->framebuffer_bpp / 8;
terminal_descriptor = terminal_initialize(framebuffer);
printf("Terminal initialized, descriptor: %d\n", terminal_descriptor);
printf("Framebuffer | addr: %#X, width: %d, height: %d, depth: %d, pitch: %d\n", framebuffer->address, framebuffer->width, framebuffer->height, framebuffer->depth, framebuffer->pitch, framebuffer->pixelwidth);
kmain();
}
void shell() {
printf("hello yes");
@ -18,7 +121,7 @@ void shell() {
printf("\n> ");
while (true) {
unsigned char scancode = read(keyboard_descriptor, NULL);
uint32_t scancode = (uint32_t)read(keyboard_descriptor, NULL);
if (!scancode) continue;
bool special = true;
@ -56,46 +159,55 @@ extern void enable_paging();
uint32_t page_directory[1024] __attribute__((aligned(4096)));
uint32_t first_page_table[1024] __attribute__((aligned(4096)));
uint32_t second_page_table[1024] __attribute__((aligned(4096)));
void kmain() {
//set each entry to not present
#define PAGE_PRESENT 1 << 0
#define PAGE_WRITABLE 1 << 1
#define PAGE_USER 1 << 2
#define PAGE_WRITE_THROUGH 1 << 3
#define PAGE_CACHE_DISABLE 1 << 4
#define PAGE_ACCESSED 1 << 5
#define PAGE_DIRTY 1 << 6
#define PAGE_LARGE 1 << 7
#define PAGE_GLOBAL 1 << 8
/*
void setup_paging() {
// set each entry to not present
for (uint32_t i = 0; i < 1024; i++) {
// This sets the following flags to the pages:
// Supervisor: Only kernel-mode can access them
// Write Enabled: It can be both read from and written to
// Not Present: The page table is not present
page_directory[i] = 0x00000002;
page_directory[i] = 0;
}
// i holds the physical address where we want to start mapping these pages to.
// in this case, we want to map these pages to the very beginning of memory.
// we will fill all 1024 entries in the table, mapping 4 megabytes
for (uint32_t i = 0; i < 1024; i++) {
// As the address is page aligned, it will always leave 12 bits zeroed.
// Those bits are used by the attributes ;)
first_page_table[i] = (i * 0x1000) | 3; // attributes: supervisor level, read/write, present.
first_page_table[i] = (i * 0x1000) | PAGE_PRESENT | PAGE_WRITABLE;
}
// attributes: supervisor level, read/write, present
page_directory[0] = ((uint32_t)first_page_table) | 3;
page_directory[0] = ((uint32_t)first_page_table) | PAGE_PRESENT | PAGE_WRITABLE;
load_page_directory(page_directory);
load_page_directory((uint32_t)page_directory);
enable_paging();
}
*/
void kmain() {
//setup_paging();
terminal_descriptor = terminal_initialize();
printf("Terminal initialized, descriptor: %d\n", terminal_descriptor);
printf("Preparing interrupts... ");
pic_init();
isr_install();
printf("done\n");
printf("Preparing IDE driver... \n");
printf("Searching for IDE devices... \n");
ide_init();
fillrect(framebuffer, 500, 200, 100, 100, 0x00FFD5FF);
fillrect(framebuffer, 600, 300, 100, 100, 0xFF00D5FF);
keyboard_descriptor = keyboard_init();
printf("Keyboard ready, descriptor: %d\n", keyboard_descriptor);
printf("Preparing IDE driver... \n");
printf("Searching for IDE devices... \n");
ide_init();
printf("Are interrupts enabled? ");
if (are_interrupts_enabled() == 1) {
printf("yes\n");
@ -103,8 +215,8 @@ void kmain() {
printf("no\n");
}
printf("kmain memory address: %p\n", kmain);
printf("kmain memory address: %#X\n", (unsigned int)kmain);
printf("----------\n");
shell();
}
}

View file

@ -0,0 +1,24 @@
#include <framebuffer.h>
#include <std/stdio.h>
void putpixel(framebuffer_t *framebuffer, uint16_t x, uint16_t y, uint32_t color) {
if (color & 255 == 0) return;
uint64_t where = x * framebuffer->pixelwidth + y * framebuffer->pitch;
framebuffer->address[where] = (color >> 8) & 255; // BLUE
framebuffer->address[where + 1] = (color >> 16) & 255; // GREEN
framebuffer->address[where + 2] = (color >> 24) & 255; // RED
}
void fillrect(framebuffer_t *framebuffer, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t color) {
if (color & 255 == 0) return;
uint8_t* where;
uint32_t pw = framebuffer->pixelwidth;
for (uint16_t i = y; i < h + y; i++) {
where = (uint8_t*)((uint64_t)framebuffer->address + framebuffer->pitch * i);
for (uint16_t j = x; j < w + x; j++) {
where[j * pw] = (color >> 8) & 255; // BLUE
where[j * pw + 1] = (color >> 16) & 255; // GREEN
where[j * pw + 2] = (color >> 24) & 255; // RED
}
}
}

View file

@ -88,7 +88,7 @@ void ide_read_buffer(unsigned char channel, unsigned char reg,
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
asm("pushw %es; movw %ds, %ax; movw %ax, %es");
//asm("pushw %es; movw %ds, %ax; movw %ax, %es");
if (reg < 0x08)
insl(channels[channel].base + reg - 0x00, buffer, quads);
@ -99,7 +99,7 @@ void ide_read_buffer(unsigned char channel, unsigned char reg,
else if (reg < 0x16)
insl(channels[channel].bmide + reg - 0x0E, buffer, quads);
asm("popw %es;");
//asm("popw %es;");
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);

View file

@ -58,7 +58,6 @@ unsigned char keyboard_buffer_pop_key() {
return scancode;
}
unsigned char keyboard_get_key_from_scancode(unsigned char scancode) {
return kbdus[scancode];
}
@ -80,12 +79,12 @@ uint8_t keyboard_enabled(void) {
return __kbd_enabled;
}
void* keyboard_read(void* data) {
return keyboard_buffer_pop_key();
void* keyboard_read() {
return (void*) keyboard_buffer_pop_key();
}
void* keyboard_write(void* data) {
return -1;
void* keyboard_write() {
return (void*) -1;
}
int keyboard_init(void) {

32
arch/i386/drivers/psf.c Normal file
View file

@ -0,0 +1,32 @@
#include <psf.h>
// c is a unicode character, cx and cy are cursor position in characters
void draw_psf_char(framebuffer_t *framebuffer, uint32_t c, uint16_t cx, uint16_t cy, uint32_t fg, uint32_t bg) {
PSF_font *font = (PSF_font*)&_binary_font_psfu_start;
// we need to know how many bytes encode one row
int bytesperline = (font->width + 7) / 8;
// get the glyph for the character. If there's no
// glyph for a given character, we'll display the first glyph.
uint8_t *glyph = (uint8_t*)&_binary_font_psfu_start + font->headersize +
(c > 0 && c < font->numglyph ? c : 0) * font->bytesperglyph;
// calculate the upper left corner on screen where we want to display.
// we only do this once, and adjust the whereet later. This is faster.
int where = (cy * font->height * framebuffer->pitch) + (cx * (font->width + 1) * 4);
// finally display pixels according to the bitmap
int x, y, line, mask;
for (y = 0; y < font->height; y++) {
// save the starting position of the line
line = where;
mask = 1 << (font->width - 1);
// display a row
for (x = 0; x < font->width; x++) {
*((uint32_t*)(framebuffer->address + line)) = *((uint32_t*)glyph) & mask ? fg : bg;
// adjust to the next pixel
mask >>= 1;
line += 4;
}
// adjust to the next line
glyph += bytesperline;
where += framebuffer->pitch;
}
}

View file

@ -1,7 +1,7 @@
#include <drivers/tar/tar.h>
#include <std/util.h>
#include <drivers/device/device.h>
/*
struct tar_header* tar_seek(int fd, char filename[100]) {
unsigned int i;
unsigned int addr;
@ -15,4 +15,5 @@ struct tar_header* tar_seek(int fd, char filename[100]) {
addr += ((size / 512) + 1);
if (size % 512) addr++;
}
}
}
*/

View file

@ -4,9 +4,10 @@
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;
uint16_t *terminal_buffer;
framebuffer_t *terminal_framebuffer;
void terminal_clear(void) {
void terminal_clear() {
for (size_t y = 0; y < VGA_HEIGHT; y++) {
for (size_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x;
@ -35,10 +36,11 @@ void* terminal_devwrite(void* data) {
return NULL;
}
int terminal_initialize(void) {
int terminal_initialize(framebuffer_t *terminal_fb) {
terminal_framebuffer = terminal_fb;
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
terminal_color = vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE);
terminal_buffer = (uint16_t*) 0xB8000;
terminal_clear();
@ -70,7 +72,7 @@ void terminal_clearlines(size_t from, size_t to) {
}
}
void terminal_updatecursor(void) {
void terminal_updatecursor() {
size_t temp = terminal_row * VGA_WIDTH + terminal_column;
outportb(0x3D4, 14);
outportb(0x3D5, temp >> 8);
@ -78,7 +80,7 @@ void terminal_updatecursor(void) {
outportb(0x3D5, temp);
}
void terminal_scrollup(void) {
void terminal_scrollup() {
for (size_t index = 0; index < VGA_WIDTH * (VGA_HEIGHT - 1); index++) {
terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH];
}
@ -92,17 +94,19 @@ void terminal_scrollup(void) {
terminal_updatecursor();
}
void terminal_checknewline(void) {
void terminal_checknewline() {
if (terminal_row >= VGA_HEIGHT - 1) {
terminal_scrollup();
}
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
terminal_buffer[y * VGA_WIDTH + x] = vga_entry(c, color);
//terminal_buffer[y * VGA_WIDTH + x] = vga_entry(c, color);
draw_psf_char(terminal_framebuffer, c, x, y, 0xFFFFFFFF, 0);
}
void terminal_putchar(char c) {
outportb(0x3F8, c); // log terminal to serial.log when running on qemu
switch (c) {
case '\b':
if (--terminal_column < 0) {

BIN
font.psfu Normal file

Binary file not shown.

View file

@ -13,6 +13,7 @@ extern "C" {
int keyboard_init(void);
uint8_t keyboard_enabled(void);
unsigned char keyboard_get_key_from_scancode(unsigned char scancode);
#ifdef __cplusplus
}

View file

@ -4,6 +4,8 @@
#include <stdint.h>
#include <std/string.h>
#include <std/inline.h>
#include <drivers/ide/ide.h>
#include <psf.h>
#ifdef __cplusplus
extern "C" {
@ -29,11 +31,11 @@ enum vga_color {
VGA_COLOR_WHITE = 15,
};
static inline uint8_t vga_entry_color(enum vga_color const fg, enum vga_color const bg) {
return fg | bg << 4;
static inline uint8_t vga_entry_color(const enum vga_color fg, const enum vga_color bg) {
return fg | (bg << 4);
}
static inline uint16_t vga_entry(unsigned char const uc, uint8_t const color) {
static inline uint16_t vga_entry(const uint8_t uc, const uint8_t color) {
return (uint16_t) uc | (uint16_t) color << 8;
}
@ -43,25 +45,25 @@ static const size_t VGA_HEIGHT = 25;
extern size_t terminal_row;
extern size_t terminal_column;
extern uint8_t terminal_color;
extern uint16_t* terminal_buffer;
extern uint16_t *terminal_buffer;
void terminal_clear(void);
int terminal_initialize(void);
void terminal_clear();
int terminal_initialize(framebuffer_t *terminal_fb);
void terminal_setcolor(uint8_t color);
void terminal_clearline(size_t line);
void terminal_clearlines(size_t from, size_t to);
void terminal_updatecursor(void);
void terminal_scrollup(void);
void terminal_checknewline(void);
void terminal_updatecursor();
void terminal_scrollup();
void terminal_checknewline();
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y);
void terminal_putchar(char c);
void terminal_write(const char* data, size_t size);
static inline void terminal_writestring(const char* data) {
static inline void terminal_writestring(const char *data) {
terminal_write(data, strlen(data));
}
static inline void terminal_writeline(const char* data) {
static inline void terminal_writeline(const char *data) {
terminal_writestring(data);
terminal_putchar('\n');
}

24
include/framebuffer.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint8_t *address;
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t pitch;
uint32_t pixelwidth;
} framebuffer_t;
void putpixel(framebuffer_t *framebuffer, uint16_t x, uint16_t y, uint32_t color);
void fillrect(framebuffer_t *framebuffer, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t color);
#ifdef __cplusplus
}
#endif

33
include/psf.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <framebuffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PSF_FONT_MAGIC 0x864ab572
typedef struct {
uint32_t magic; /* magic bytes to identify PSF */
uint32_t version; /* zero */
uint32_t headersize; /* whereet of bitmaps in file, 32 */
uint32_t flags; /* 0 if there's no unicode table */
uint32_t numglyph; /* number of glyphs */
uint32_t bytesperglyph; /* size of each glyph */
uint32_t height; /* height in pixels */
uint32_t width; /* width in pixels */
} PSF_font;
// these are linked using objcopy
extern char _binary_font_psfu_start;
extern char _binary_font_psfu_end;
// c is a unicode character, cx and cy are cursor position in characters
void draw_psf_char(framebuffer_t *framebuffer, uint32_t c, uint16_t cx, uint16_t cy, uint32_t fg, uint32_t bg);
#ifdef __cplusplus
}
#endif

View file

@ -7,15 +7,15 @@
extern "C" {
#endif
static inline void outportb(uint16_t const port, uint8_t const val) {
static inline void outportb(const uint16_t port, const uint8_t val) {
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}
static inline void outportw(uint16_t const port, uint16_t const val) {
static inline void outportw(const uint16_t port, const uint16_t val) {
asm volatile ( "outw %0, %1" : : "a"(val), "Nd"(port) );
}
static inline void outportl(uint16_t const port, uint32_t const val) {
static inline void outportl(const uint16_t port, const uint32_t val) {
asm volatile ( "outl %0, %1" : : "a"(val), "Nd"(port) );
}
@ -23,19 +23,19 @@ static inline void outportsm(unsigned short port, unsigned char* data, unsigned
asm volatile ("rep outsw" : "+S" (data), "+c" (size) : "d" (port));
}
static inline uint8_t inportb(uint16_t const port) {
static inline uint8_t inportb(const uint16_t port) {
uint8_t ret;
asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
return ret;
}
static inline uint16_t inportw(uint16_t const port) {
static inline uint16_t inportw(const uint16_t port) {
uint16_t ret;
asm volatile ( "inw %1, %0" : "=a"(ret) : "Nd"(port) );
return ret;
}
static inline uint32_t inportl(uint16_t const port) {
static inline uint32_t inportl(const uint16_t port) {
uint32_t ret;
asm volatile ( "inl %1, %0" : "=a"(ret) : "Nd"(port) );
return ret;

251
include/stivale2.h Normal file
View file

@ -0,0 +1,251 @@
#ifndef __STIVALE__STIVALE2_H__
#define __STIVALE__STIVALE2_H__
#include <stdint.h>
struct stivale2_tag {
uint64_t identifier;
uint64_t next;
} __attribute__((__packed__));
/* --- Header --------------------------------------------------------------- */
/* Information passed from the kernel to the bootloader */
struct stivale2_header {
uint64_t entry_point;
uint64_t stack;
uint64_t flags;
uint64_t tags;
} __attribute__((__packed__));
#define STIVALE2_HEADER_TAG_FRAMEBUFFER_ID 0x3ecc1bc43d0f7971
struct stivale2_header_tag_framebuffer {
struct stivale2_tag tag;
uint16_t framebuffer_width;
uint16_t framebuffer_height;
uint16_t framebuffer_bpp;
} __attribute__((__packed__));
#define STIVALE2_HEADER_TAG_FB_MTRR_ID 0x4c7bb07731282e00
#define STIVALE2_HEADER_TAG_TERMINAL_ID 0xa85d499b1823be72
struct stivale2_header_tag_terminal {
struct stivale2_tag tag;
uint64_t flags;
} __attribute__((__packed__));
#define STIVALE2_HEADER_TAG_SMP_ID 0x1ab015085f3273df
struct stivale2_header_tag_smp {
struct stivale2_tag tag;
uint64_t flags;
} __attribute__((__packed__));
#define STIVALE2_HEADER_TAG_5LV_PAGING_ID 0x932f477032007e8f
#define STIVALE2_HEADER_TAG_UNMAP_NULL_ID 0x92919432b16fe7e7
/* --- Struct --------------------------------------------------------------- */
/* Information passed from the bootloader to the kernel */
struct stivale2_struct {
#define STIVALE2_BOOTLOADER_BRAND_SIZE 64
char bootloader_brand[STIVALE2_BOOTLOADER_BRAND_SIZE];
#define STIVALE2_BOOTLOADER_VERSION_SIZE 64
char bootloader_version[STIVALE2_BOOTLOADER_VERSION_SIZE];
uint64_t tags;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_CMDLINE_ID 0xe5e76a1b4597a781
struct stivale2_struct_tag_cmdline {
struct stivale2_tag tag;
uint64_t cmdline;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_MEMMAP_ID 0x2187f79e8612de07
#define STIVALE2_MMAP_USABLE 1
#define STIVALE2_MMAP_RESERVED 2
#define STIVALE2_MMAP_ACPI_RECLAIMABLE 3
#define STIVALE2_MMAP_ACPI_NVS 4
#define STIVALE2_MMAP_BAD_MEMORY 5
#define STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE 0x1000
#define STIVALE2_MMAP_KERNEL_AND_MODULES 0x1001
#define STIVALE2_MMAP_FRAMEBUFFER 0x1002
struct stivale2_mmap_entry {
uint64_t base;
uint64_t length;
uint32_t type;
uint32_t unused;
} __attribute__((__packed__));
struct stivale2_struct_tag_memmap {
struct stivale2_tag tag;
uint64_t entries;
struct stivale2_mmap_entry memmap[];
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_FRAMEBUFFER_ID 0x506461d2950408fa
#define STIVALE2_FBUF_MMODEL_RGB 1
struct stivale2_struct_tag_framebuffer {
struct stivale2_tag tag;
uint64_t framebuffer_addr;
uint16_t framebuffer_width;
uint16_t framebuffer_height;
uint16_t framebuffer_pitch;
uint16_t framebuffer_bpp;
uint8_t memory_model;
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;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_EDID_ID 0x968609d7af96b845
struct stivale2_struct_tag_edid {
struct stivale2_tag tag;
uint64_t edid_size;
uint8_t edid_information[];
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_FB_MTRR_ID 0x6bc1a78ebe871172
#define STIVALE2_STRUCT_TAG_TERMINAL_ID 0xc2b3f4c3233b0974
struct stivale2_struct_tag_terminal {
struct stivale2_tag tag;
uint32_t flags;
uint16_t cols;
uint16_t rows;
uint64_t term_write;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_MODULES_ID 0x4b6fe466aade04ce
struct stivale2_module {
uint64_t begin;
uint64_t end;
#define STIVALE2_MODULE_STRING_SIZE 128
char string[STIVALE2_MODULE_STRING_SIZE];
} __attribute__((__packed__));
struct stivale2_struct_tag_modules {
struct stivale2_tag tag;
uint64_t module_count;
struct stivale2_module modules[];
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_RSDP_ID 0x9e1786930a375e78
struct stivale2_struct_tag_rsdp {
struct stivale2_tag tag;
uint64_t rsdp;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_EPOCH_ID 0x566a7bed888e1407
struct stivale2_struct_tag_epoch {
struct stivale2_tag tag;
uint64_t epoch;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_FIRMWARE_ID 0x359d837855e3858c
#define STIVALE2_FIRMWARE_BIOS (1 << 0)
struct stivale2_struct_tag_firmware {
struct stivale2_tag tag;
uint64_t flags;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_EFI_SYSTEM_TABLE_ID 0x4bc5ec15845b558e
struct stivale2_struct_tag_efi_system_table {
struct stivale2_tag tag;
uint64_t system_table;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_KERNEL_FILE_ID 0xe599d90c2975584a
struct stivale2_struct_tag_kernel_file {
struct stivale2_tag tag;
uint64_t kernel_file;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_KERNEL_SLIDE_ID 0xee80847d01506c57
struct stivale2_struct_tag_kernel_slide {
struct stivale2_tag tag;
uint64_t kernel_slide;
} __attribute__((packed));
#define STIVALE2_STRUCT_TAG_SMBIOS_ID 0x274bd246c62bf7d1
struct stivale2_struct_tag_smbios {
struct stivale2_tag tag;
uint64_t flags;
uint64_t smbios_entry_32;
uint64_t smbios_entry_64;
} __attribute__((packed));
#define STIVALE2_STRUCT_TAG_SMP_ID 0x34d1d96339647025
struct stivale2_smp_info {
uint32_t processor_id;
uint32_t lapic_id;
uint64_t target_stack;
uint64_t goto_address;
uint64_t extra_argument;
} __attribute__((__packed__));
struct stivale2_struct_tag_smp {
struct stivale2_tag tag;
uint64_t flags;
uint32_t bsp_lapic_id;
uint32_t unused;
uint64_t cpu_count;
struct stivale2_smp_info smp_info[];
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_PXE_SERVER_INFO 0x29d1e96239247032
struct stivale2_struct_tag_pxe_server_info {
struct stivale2_tag tag;
uint32_t server_ip;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_MMIO32_UART 0xb813f9b8dbc78797
struct stivale2_struct_tag_mmio32_uart {
struct stivale2_tag tag;
uint64_t addr;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_DTB 0xabb29bd49a2833fa
struct stivale2_struct_tag_dtb {
struct stivale2_tag tag;
uint64_t addr;
uint64_t size;
} __attribute__((__packed__));
#define STIVALE2_STRUCT_TAG_VMAP 0xb0ed257db18cb58f
struct stivale2_struct_vmap {
struct stivale2_tag tag;
uint64_t addr;
} __attribute__((__packed__));
#endif

1
limine Submodule

@ -0,0 +1 @@
Subproject commit 2862a261450af7cb2b1504b5a3ed15c32759deaa

14
limine.cfg Normal file
View file

@ -0,0 +1,14 @@
# Timeout in seconds that Limine will use before automatically booting.
TIMEOUT=5
# The entry name that will be displayed in the boot menu
:HhhOS
# Change the protocol line depending on the used protocol.
PROTOCOL=stivale2
# Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located.
KERNEL_PATH=boot:///hhhos.elf
# Remove the following line to enable kernel address layout randomisation.
KASLR=no

View file

@ -1,43 +1,34 @@
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
/* Tell the linker that we want the symbol _start to be our entry point */
ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
loaded at by the bootloader. */
. = 1M;
/* We wanna be placed in the higher half, 2MiB above 0 in physical memory. */
/* Since we are going to use PIE, this is just the base load address, but the */
/* bootloader will be able to relocate us as it sees fit. */
. = 0xffffffff80200000;
/* First put the multiboot header, as it is required to be put very early
early in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
/* We place the .stivale2hdr section containing the header in its own section, */
/* and we use the KEEP directive on it to make sure it doesn't get discarded. */
.stivale2hdr : {
KEEP(*(.stivale2hdr))
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Then let's place all the other traditional executable sections afterwards. */
.text : {
*(.text*)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.rodata : {
*(.rodata*)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
.data : {
*(.data*)
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
.bss : {
*(COMMON)
*(.bss*)
}
}

View file

@ -1,3 +0,0 @@
menuentry "HhhOS" {
multiboot /boot/hhhos.bin
}