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 *.o
*.bin *.bin
*.iso *.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 KERNEL := hhhos.elf
ARCH = i386 ISO := hhhos.iso
CC = $(TARGET)-gcc
STRIP = $(TARGET)-strip CC = x86_64-elf-gcc
NASM = nasm 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 # Targets that do not actually build a file of the same name.
LDFLAGS += -ffreestanding -O2 -nostdlib -lgcc .PHONY: all clean run
NASMFLAGS += -felf32
BIN = sysroot/boot/hhhos.bin run: $(ISO)
ISO = build/HhhOS.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 \ # Default target.
build/drivers/idt/isr.o build/std/util.o build/drivers/device/device.o build/drivers/keyboard/keyboard.o build/drivers/pic/pic.o \ all: $(KERNEL)
build/drivers/ide/ide.o build/std/stdio.o
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: font.o:
${MKDIR_P} ${DIRS} objcopy -O elf64-x86-64 -B i386 -I binary font.psfu font.o
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 $@ $<
# Remove object files and the final executable.
clean: 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 <stddef.h>
#include <stivale2.h>
#include <std/inline.h>
#include <std/util.h>
#include <stdbool.h>
#include <std/stdio.h> #include <std/stdio.h>
#include <drivers/idt/isr.h> #include <drivers/idt/isr.h>
#include <drivers/keyboard/keyboard.h> #include <drivers/keyboard/keyboard.h>
#include <drivers/pic/pic.h> #include <drivers/pic/pic.h>
#include <drivers/device/device.h> #include <drivers/device/device.h>
#include <drivers/ide/ide.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 keyboard_descriptor = 0;
int terminal_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() { void shell() {
printf("hello yes"); printf("hello yes");
@ -18,7 +121,7 @@ void shell() {
printf("\n> "); printf("\n> ");
while (true) { while (true) {
unsigned char scancode = read(keyboard_descriptor, NULL); uint32_t scancode = (uint32_t)read(keyboard_descriptor, NULL);
if (!scancode) continue; if (!scancode) continue;
bool special = true; bool special = true;
@ -56,46 +159,55 @@ extern void enable_paging();
uint32_t page_directory[1024] __attribute__((aligned(4096))); uint32_t page_directory[1024] __attribute__((aligned(4096)));
uint32_t first_page_table[1024] __attribute__((aligned(4096))); uint32_t first_page_table[1024] __attribute__((aligned(4096)));
uint32_t second_page_table[1024] __attribute__((aligned(4096)));
void kmain() { #define PAGE_PRESENT 1 << 0
//set each entry to not present #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++) { for (uint32_t i = 0; i < 1024; i++) {
// This sets the following flags to the pages: page_directory[i] = 0;
// 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;
} }
// i holds the physical address where we want to start mapping these pages to. // 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. // 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 // we will fill all 1024 entries in the table, mapping 4 megabytes
for (uint32_t i = 0; i < 1024; i++) { for (uint32_t i = 0; i < 1024; i++) {
// As the address is page aligned, it will always leave 12 bits zeroed. first_page_table[i] = (i * 0x1000) | PAGE_PRESENT | PAGE_WRITABLE;
// Those bits are used by the attributes ;)
first_page_table[i] = (i * 0x1000) | 3; // attributes: supervisor level, read/write, present.
} }
// attributes: supervisor level, read/write, present page_directory[0] = ((uint32_t)first_page_table) | PAGE_PRESENT | PAGE_WRITABLE;
page_directory[0] = ((uint32_t)first_page_table) | 3;
load_page_directory(page_directory); load_page_directory((uint32_t)page_directory);
enable_paging(); enable_paging();
}
*/
void kmain() {
//setup_paging();
terminal_descriptor = terminal_initialize();
printf("Terminal initialized, descriptor: %d\n", terminal_descriptor);
printf("Preparing interrupts... "); printf("Preparing interrupts... ");
pic_init(); pic_init();
isr_install(); isr_install();
printf("done\n"); 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(); keyboard_descriptor = keyboard_init();
printf("Keyboard ready, descriptor: %d\n", keyboard_descriptor); 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? "); printf("Are interrupts enabled? ");
if (are_interrupts_enabled() == 1) { if (are_interrupts_enabled() == 1) {
printf("yes\n"); printf("yes\n");
@ -103,8 +215,8 @@ void kmain() {
printf("no\n"); printf("no\n");
} }
printf("kmain memory address: %p\n", kmain); printf("kmain memory address: %#X\n", (unsigned int)kmain);
printf("----------\n"); printf("----------\n");
shell(); 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) if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); 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) if (reg < 0x08)
insl(channels[channel].base + reg - 0x00, buffer, quads); 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) else if (reg < 0x16)
insl(channels[channel].bmide + reg - 0x0E, buffer, quads); insl(channels[channel].bmide + reg - 0x0E, buffer, quads);
asm("popw %es;"); //asm("popw %es;");
if (reg > 0x07 && reg < 0x0C) if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);

View file

@ -58,7 +58,6 @@ unsigned char keyboard_buffer_pop_key() {
return scancode; return scancode;
} }
unsigned char keyboard_get_key_from_scancode(unsigned char scancode) { unsigned char keyboard_get_key_from_scancode(unsigned char scancode) {
return kbdus[scancode]; return kbdus[scancode];
} }
@ -80,12 +79,12 @@ uint8_t keyboard_enabled(void) {
return __kbd_enabled; return __kbd_enabled;
} }
void* keyboard_read(void* data) { void* keyboard_read() {
return keyboard_buffer_pop_key(); return (void*) keyboard_buffer_pop_key();
} }
void* keyboard_write(void* data) { void* keyboard_write() {
return -1; return (void*) -1;
} }
int keyboard_init(void) { 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 <drivers/tar/tar.h>
#include <std/util.h> #include <std/util.h>
#include <drivers/device/device.h> #include <drivers/device/device.h>
/*
struct tar_header* tar_seek(int fd, char filename[100]) { struct tar_header* tar_seek(int fd, char filename[100]) {
unsigned int i; unsigned int i;
unsigned int addr; unsigned int addr;
@ -15,4 +15,5 @@ struct tar_header* tar_seek(int fd, char filename[100]) {
addr += ((size / 512) + 1); addr += ((size / 512) + 1);
if (size % 512) addr++; if (size % 512) addr++;
} }
} }
*/

View file

@ -4,9 +4,10 @@
size_t terminal_row; size_t terminal_row;
size_t terminal_column; size_t terminal_column;
uint8_t terminal_color; 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 y = 0; y < VGA_HEIGHT; y++) {
for (size_t x = 0; x < VGA_WIDTH; x++) { for (size_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x; const size_t index = y * VGA_WIDTH + x;
@ -35,10 +36,11 @@ void* terminal_devwrite(void* data) {
return NULL; return NULL;
} }
int terminal_initialize(void) { int terminal_initialize(framebuffer_t *terminal_fb) {
terminal_framebuffer = terminal_fb;
terminal_row = 0; terminal_row = 0;
terminal_column = 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_buffer = (uint16_t*) 0xB8000;
terminal_clear(); 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; size_t temp = terminal_row * VGA_WIDTH + terminal_column;
outportb(0x3D4, 14); outportb(0x3D4, 14);
outportb(0x3D5, temp >> 8); outportb(0x3D5, temp >> 8);
@ -78,7 +80,7 @@ void terminal_updatecursor(void) {
outportb(0x3D5, temp); outportb(0x3D5, temp);
} }
void terminal_scrollup(void) { void terminal_scrollup() {
for (size_t index = 0; index < VGA_WIDTH * (VGA_HEIGHT - 1); index++) { for (size_t index = 0; index < VGA_WIDTH * (VGA_HEIGHT - 1); index++) {
terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH]; terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH];
} }
@ -92,17 +94,19 @@ void terminal_scrollup(void) {
terminal_updatecursor(); terminal_updatecursor();
} }
void terminal_checknewline(void) { void terminal_checknewline() {
if (terminal_row >= VGA_HEIGHT - 1) { if (terminal_row >= VGA_HEIGHT - 1) {
terminal_scrollup(); terminal_scrollup();
} }
} }
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) { 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) { void terminal_putchar(char c) {
outportb(0x3F8, c); // log terminal to serial.log when running on qemu
switch (c) { switch (c) {
case '\b': case '\b':
if (--terminal_column < 0) { 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); int keyboard_init(void);
uint8_t keyboard_enabled(void); uint8_t keyboard_enabled(void);
unsigned char keyboard_get_key_from_scancode(unsigned char scancode);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <std/string.h> #include <std/string.h>
#include <std/inline.h> #include <std/inline.h>
#include <drivers/ide/ide.h>
#include <psf.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -29,11 +31,11 @@ enum vga_color {
VGA_COLOR_WHITE = 15, VGA_COLOR_WHITE = 15,
}; };
static inline uint8_t vga_entry_color(enum vga_color const fg, enum vga_color const bg) { static inline uint8_t vga_entry_color(const enum vga_color fg, const enum vga_color bg) {
return fg | bg << 4; 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; 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_row;
extern size_t terminal_column; extern size_t terminal_column;
extern uint8_t terminal_color; extern uint8_t terminal_color;
extern uint16_t* terminal_buffer; extern uint16_t *terminal_buffer;
void terminal_clear(void); void terminal_clear();
int terminal_initialize(void); int terminal_initialize(framebuffer_t *terminal_fb);
void terminal_setcolor(uint8_t color); void terminal_setcolor(uint8_t color);
void terminal_clearline(size_t line); void terminal_clearline(size_t line);
void terminal_clearlines(size_t from, size_t to); void terminal_clearlines(size_t from, size_t to);
void terminal_updatecursor(void); void terminal_updatecursor();
void terminal_scrollup(void); void terminal_scrollup();
void terminal_checknewline(void); void terminal_checknewline();
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y); void terminal_putentryat(char c, uint8_t color, size_t x, size_t y);
void terminal_putchar(char c); void terminal_putchar(char c);
void terminal_write(const char* data, size_t size); 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)); 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_writestring(data);
terminal_putchar('\n'); 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" { extern "C" {
#endif #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) ); 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) ); 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) ); 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)); 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; uint8_t ret;
asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) ); asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
return ret; return ret;
} }
static inline uint16_t inportw(uint16_t const port) { static inline uint16_t inportw(const uint16_t port) {
uint16_t ret; uint16_t ret;
asm volatile ( "inw %1, %0" : "=a"(ret) : "Nd"(port) ); asm volatile ( "inw %1, %0" : "=a"(ret) : "Nd"(port) );
return ret; return ret;
} }
static inline uint32_t inportl(uint16_t const port) { static inline uint32_t inportl(const uint16_t port) {
uint32_t ret; uint32_t ret;
asm volatile ( "inl %1, %0" : "=a"(ret) : "Nd"(port) ); asm volatile ( "inl %1, %0" : "=a"(ret) : "Nd"(port) );
return ret; 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 /* Tell the linker that we want the symbol _start to be our entry point */
designated as the entry point. */
ENTRY(_start) ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS SECTIONS
{ {
/* Begin putting sections at 1 MiB, a conventional place for kernels to be /* We wanna be placed in the higher half, 2MiB above 0 in physical memory. */
loaded at by the bootloader. */ /* Since we are going to use PIE, this is just the base load address, but the */
. = 1M; /* 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 /* We place the .stivale2hdr section containing the header in its own section, */
early in the image or the bootloader won't recognize the file format. /* and we use the KEEP directive on it to make sure it doesn't get discarded. */
Next we'll put the .text section. */ .stivale2hdr : {
.text BLOCK(4K) : ALIGN(4K) KEEP(*(.stivale2hdr))
{ }
*(.multiboot)
*(.text)
}
/* Read-only data. */ /* Then let's place all the other traditional executable sections afterwards. */
.rodata BLOCK(4K) : ALIGN(4K) .text : {
{ *(.text*)
*(.rodata) }
}
/* Read-write data (initialized) */ .rodata : {
.data BLOCK(4K) : ALIGN(4K) *(.rodata*)
{ }
*(.data)
}
/* Read-write data (uninitialized) and stack */ .data : {
.bss BLOCK(4K) : ALIGN(4K) *(.data*)
{ }
*(COMMON)
*(.bss)
}
/* The compiler may produce other sections, by default it will put them in .bss : {
a segment with the same name. Simply add stuff here as needed. */ *(COMMON)
*(.bss*)
}
} }

View file

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