This commit is contained in:
nub31
2025-08-30 20:39:59 +02:00
parent f7342ff6e4
commit bd3137067d
12 changed files with 144 additions and 254 deletions

View File

@@ -2,16 +2,29 @@
## Dependencies
### Building the kernel
- `make`
- `grub`
- `mtools`
- `x86_64-elf-gcc`
- `x86_64-elf-ld`
### Creating a disk image
- `grub`
## Building
### Kernel
```sh
make
make kernel
```
### Disk image
```sh
make iso
```
## Running
@@ -19,5 +32,5 @@ make
After building, run the following:
```sh
qemu-system-x86_64 -cdrom .build/nub-os.iso
qemu-system-i386 -kernel .build/kernel
```

View File

@@ -1,7 +1,6 @@
menuentry "nub-os" {
multiboot2 /boot/kernel
boot
multiboot /boot/kernel
}
set default="nub-os"
set default=0
set timeout=0

View File

@@ -6,7 +6,7 @@ SECTIONS
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot2)
*(.multiboot)
*(.text)
}

View File

@@ -1,16 +1,21 @@
CC = x86_64-elf-gcc
LD = x86_64-elf-ld
CC = i386-elf-gcc
AS = i386-elf-as
LD = i386-elf-ld
CFLAGS = -m64 -ffreestanding -fno-builtin -Wall -Wextra -Wshadow -std=c23
CFLAGS = -m32 -ffreestanding -fno-builtin -Wall -Wextra -Wshadow -std=c23
LDFLAGS =
ASFLAGS =
SRC_C := src/kernel.c src/vga.c src/idt.c src/string.c src/keyboard.c
SRC_ASM := src/boot.asm src/idt_stub.asm
SRC_C := src/kernel.c src/string.c src/vga.c
SRC_ASM := src/boot.s
OBJ_C := $(SRC_C:src/%.c=.build/%.o)
OBJ_ASM := $(SRC_ASM:src/%.asm=.build/%.o)
OBJ_ASM := $(SRC_ASM:src/%.s=.build/%.o)
OBJS := $(OBJ_C) $(OBJ_ASM)
kernel: .build/kernel
@echo "Kernel created at '.build/kernel'"
iso: .build/nub-os.iso
@echo "ISO created at '.build/nub-os.iso'"
@@ -20,17 +25,17 @@ clean:
build-dir:
mkdir .build 2>/dev/null || true
.build/nub-os.iso: .build/kernel
.build/nub-os.iso: .build/kernel grub.cfg
mkdir -p .build/nub-os/boot/grub
cp grub.cfg .build/nub-os/boot/grub
cp .build/kernel .build/nub-os/boot/
grub-mkrescue -o .build/nub-os.iso .build/nub-os/
.build/kernel: $(OBJS)
$(LD) $(LDFLAGS) -T src/boot.ld -o $@ $^
.build/kernel: $(OBJS) | linker.ld
$(LD) $(LDFLAGS) -m elf_i386 -T linker.ld -o $@ $^
.build/%.o: src/%.c | build-dir
$(CC) $(CFLAGS) -c -o $@ $<
.build/%.o: src/%.asm | build-dir
nasm -f elf64 -o $@ $<
.build/%.o: src/%.s | build-dir
$(AS) $(ASFLAGS) -o $@ $<

View File

@@ -1,204 +0,0 @@
%define MAGIC 0xe85250d6
%define ARCH 0
%define LEN header_end - header_start
%define CHECKSUM -(MAGIC + ARCH + LEN)
section .multiboot2
header_start:
align 4
dd MAGIC
dd ARCH
dd LEN
dd CHECKSUM
dw 0
dd 8
header_end:
section .bss
align 16
resb 16384
stack_top:
align 4096
pml4_table:
resb 4096
pdp_table:
resb 4096
pd_table:
resb 4096
section .text
bits 32
global _start
_start:
cli
mov esp, stack_top
; Check if CPU supports 64-bit mode
call check_multiboot
call check_cpuid
call check_long_mode
; Set up paging for 64-bit mode
call set_up_page_tables
call enable_paging
; Load GDT and jump to 64-bit mode
lgdt [gdt64.pointer]
jmp gdt64.code:long_mode_start
check_multiboot:
cmp eax, 0x36d76289
jne .no_multiboot
ret
.no_multiboot:
mov al, 'M'
jmp error
check_cpuid:
; Copy flags to eax through the stack
pushfd
pop eax
; Copy flags to ecx for a later comparison
mov ecx, eax
; Flit the cpuid bit
xor eax, 1 << 21
; Copy eax (with the flipped cpuid bit) back to flags
push eax
popfd
; Copy flags back to eax. The cpiuid bit will be flipped if cpuid is supported
pushfd
pop eax
; Restore flags from the original flags stored in ecx
push ecx
popfd
; Compare eax and ecx. If thry are equal, cpuid is not supported
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, 'I'
jmp error
check_long_mode:
; Test if extended processor info is available
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .no_long_mode
; Test if long mode is available
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz .no_long_mode
ret
.no_long_mode:
mov al, 'L'
jmp error
; todo(nub): This is copy pasted. I will research hat it does later
set_up_page_tables:
; Map first PML4 entry to PDP table
mov eax, pdp_table
or eax, 0b11 ; present + writable
mov [pml4_table], eax
; Map first PDP entry to PD table
mov eax, pd_table
or eax, 0b11 ; present + writable
mov [pdp_table], eax
; Map each PD entry to a huge 2MiB page
mov ecx, 0 ; counter variable
.map_pd_table:
; Map ecx-th PD entry to a huge page that starts at address (2MiB * ecx)
mov eax, 0x200000 ; 2MiB
mul ecx ; Start address of ecx-th page
or eax, 0b10000011 ; present + writable + huge
mov [pd_table + ecx * 8], eax ; Map ecx-th entry
inc ecx ; Increase counter
cmp ecx, 512 ; If counter == 512, the whole PD table is mapped
jne .map_pd_table ; Else map the next entry
ret
; todo(nub): This is copy pasted. I will research hat it does later
enable_paging:
; Load PML4 to cr3 register (cpu uses this to access the PML4 table)
mov eax, pml4_table
mov cr3, eax
; Enable PAE-flag in cr4 (Physical Address Extension)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; Set the long mode bit in the EFER MSR (model specific register)
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
; Enable paging in the cr0 register
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
error:
mov byte [0xb8000], '['
mov byte [0xb8002], ' '
mov byte [0xb8004], 'E'
mov byte [0xb8005], 4
mov byte [0xb8006], 'R'
mov byte [0xb8007], 4
mov byte [0xb8008], 'R'
mov byte [0xb8009], 4
mov byte [0xb800a], ' '
mov byte [0xb800c], ']'
mov byte [0xb800e], ':'
mov byte [0xb8010], ' '
mov byte [0xb8012], al
cli
.hang:
hlt
jmp .hang
section .rodata
gdt64:
dq 0
.code: equ $ - gdt64
dq (1<<43) | (1<<44) | (1<<47) | (1<<53)
.pointer:
dw $ - gdt64 - 1
dq gdt64
section .text
bits 64
long_mode_start:
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
extern kernel_main
call kernel_main
cli
.hang:
hlt
jmp .hang

37
src/boot.s Normal file
View File

@@ -0,0 +1,37 @@
.intel_syntax noprefix
.set ALIGN, 1<<0
.set MEMINFO, 1<<1
.set FLAGS, ALIGN | MEMINFO
.set MAGIC, 0x1BADB002
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .bss
.align 16
stack_bottom:
.skip 16384
stack_top:
.set BOOTLOADER_MAGIC, 0x2BADB002
.section .text
.global _start
_start:
mov stack_top, esp
cmp eax, BOOTLOADER_MAGIC
jne hang
push ebx
call kernel_main
add esp, 4
hang:
cli
hlt
jmp hang

View File

@@ -1,35 +1,17 @@
#include "kernel.h"
#include "idt.h"
#include "keyboard.h"
#include "multiboot.h"
#include "vga.h"
#include <stdbool.h>
#include <stdint.h>
void keyboard_handler(const keyboard_event_t* event)
{
if (event->pressed && event->ascii)
{
vga_print_char(event->ascii);
}
}
void kernel_main(void)
void kernel_main(multiboot_info_t* multiboot_info)
{
vga_clear();
vga_print_success();
vga_print(" VGA intialzied\n");
init_idt();
vga_print_success();
vga_print(" IDT intialzied\n");
init_keyboard();
vga_print_success();
vga_print(" Keyboard driver intialzied\n");
vga_print("\nWelcome to nub OS\n");
register_keypress_handler(keyboard_handler);
while (true)
{
}

58
src/multiboot.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include <stdint.h>
typedef struct multiboot_info
{
uint32_t flags; // required
// Available if flags[0] is set
uint32_t mem_lower;
uint32_t mem_upper;
// Available if flags[1] is set
uint32_t boot_device;
// Available if flags[2] is set
uint32_t cmdline;
// Available if flags[3] is set
uint32_t mods_count;
uint32_t mods_addr;
// Available if flags[4] or flags[5] is set
uint8_t syms[16]; // 28 - 40 bytes, exact layout may differ depending on a.out or ELF
// Available if flags[6] is set
uint32_t mmap_length;
uint32_t mmap_addr;
// Available if flags[7] is set
uint32_t drives_length;
uint32_t drives_addr;
// Available if flags[8] is set
uint32_t config_table;
// Available if flags[9] is set
uint32_t boot_loader_name;
// Available if flags[10] is set
uint32_t apm_table;
// Available if flags[11] is set
uint32_t vbe_control_info;
uint32_t vbe_mode_info;
uint16_t vbe_mode;
uint16_t vbe_interface_seg;
uint16_t vbe_interface_off;
uint16_t vbe_interface_len;
// Available if flags[12] is set
uint64_t framebuffer_addr; // 64-bit for large memory addresses
uint32_t framebuffer_pitch;
uint32_t framebuffer_width;
uint32_t framebuffer_height;
uint8_t framebuffer_bpp;
uint8_t framebuffer_type;
uint8_t color_info[6]; // 110-115 bytes
} __attribute__((packed)) multiboot_info_t;

View File

@@ -26,7 +26,7 @@ void reverse(char* str, size_t length)
}
}
int uitoa(uint64_t value, char* buffer)
int uitoa(uint32_t value, char* buffer)
{
int i = 0;
if (value == 0)
@@ -47,7 +47,7 @@ int uitoa(uint64_t value, char* buffer)
return i;
}
int itoa(int64_t value, char* buffer)
int itoa(int32_t value, char* buffer)
{
int i = 0;
@@ -59,16 +59,16 @@ int itoa(int64_t value, char* buffer)
}
bool negative = false;
uint64_t v;
uint32_t v;
if (value < 0)
{
negative = true;
v = (uint64_t)(-value);
v = (uint32_t)(-value);
}
else
{
v = (uint64_t)value;
v = (uint32_t)value;
}
while (v > 0)

View File

@@ -5,5 +5,5 @@
int strcmp(const char* a, const char* b);
void reverse(char* str, size_t length);
int uitoa(uint64_t value, char* buffer);
int itoa(int64_t value, char* buffer);
int uitoa(uint32_t value, char* buffer);
int itoa(int32_t value, char* buffer);

View File

@@ -138,14 +138,14 @@ void vga_print_error(void)
vga_print(" ]");
}
void vga_print_uint(uint64_t value)
void vga_print_uint(uint32_t value)
{
char buffer[11];
uitoa(value, buffer);
vga_print(buffer);
}
void vga_print_int(int64_t value)
void vga_print_int(int32_t value)
{
char buffer[12];
itoa(value, buffer);

View File

@@ -30,8 +30,8 @@ void vga_print_colored(const char* string, vga_color_t color);
void vga_print_success(void);
void vga_print_error(void);
void vga_print_uint(uint64_t value);
void vga_print_int(int64_t value);
void vga_print_uint(uint32_t value);
void vga_print_int(int32_t value);
static inline vga_color_t vga_color(vga_color_t fg_color, vga_color_t bg_color)
{