Compare commits

...

7 Commits

Author SHA1 Message Date
nub31
01f16ce210 ... 2025-08-24 21:00:04 +02:00
nub31
bffea5b0c1 irq improvements 2025-08-24 20:39:23 +02:00
nub31
bbd4b62697 ... 2025-08-24 20:14:27 +02:00
nub31
01e6022fa7 idt 2025-08-24 20:09:11 +02:00
nub31
57ef424e00 interrupts 2025-08-24 18:52:51 +02:00
nub31
1402a227f1 64 bit support 2025-08-24 18:29:02 +02:00
nub31
ff664b0687 ... 2025-08-24 17:44:11 +02:00
12 changed files with 561 additions and 23 deletions

View File

@@ -5,8 +5,8 @@
- `make` - `make`
- `grub` - `grub`
- `mtools` - `mtools`
- `gcc` - `x86_64-elf-gcc`
- `ld` - `x86_64-elf-ld`
## Building ## Building

View File

@@ -1,8 +1,15 @@
CC = gcc CC = x86_64-elf-gcc
LD = ld LD = x86_64-elf-ld
CFLAGS = -m32 -ffreestanding -fno-pie -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Wshadow -std=c23 CFLAGS = -m64 -ffreestanding -fno-builtin -Wall -Wextra -Wshadow -std=c23
LDFLAGS = -m elf_i386 -Ttext 0x100000 -nostdlib LDFLAGS =
SRC_C := src/kernel.c src/vga.c src/idt.c src/string.c
SRC_ASM := src/boot.asm src/idt_stub.asm
OBJ_C := $(SRC_C:src/%.c=.build/%.o)
OBJ_ASM := $(SRC_ASM:src/%.asm=.build/%.o)
OBJS := $(OBJ_C) $(OBJ_ASM)
all: .build/nub-os.iso all: .build/nub-os.iso
@@ -12,20 +19,17 @@ clean:
build-dir: build-dir:
mkdir .build 2>/dev/null || true mkdir .build 2>/dev/null || true
.build/nub-os.iso: build-dir .build/kernel .build/nub-os.iso: .build/kernel
mkdir -p .build/iso/boot/grub mkdir -p .build/iso/boot/grub
cp grub.cfg .build/iso/boot/grub cp grub.cfg .build/iso/boot/grub
cp .build/kernel .build/iso/boot/ cp .build/kernel .build/iso/boot/
grub-mkrescue -o .build/nub-os.iso .build/iso/ grub-mkrescue -o .build/nub-os.iso .build/iso/
.build/kernel: build-dir .build/boot.o .build/kernel.o .build/vga.o .build/kernel: $(OBJS)
$(LD) $(LDFLAGS) -Ttext 0x100000 -o .build/kernel .build/boot.o .build/kernel.o .build/vga.o $(LD) $(LDFLAGS) -T src/boot.ld -o $@ $^
.build/kernel.o: build-dir src/kernel.c .build/%.o: src/%.c | build-dir
$(CC) $(CFLAGS) -c -o .build/kernel.o src/kernel.c $(CC) $(CFLAGS) -c -o $@ $<
.build/vga.o: build-dir src/vga.c .build/%.o: src/%.asm | build-dir
$(CC) $(CFLAGS) -c -o .build/vga.o src/vga.c nasm -f elf64 -o $@ $<
.build/boot.o: build-dir src/boot.asm
nasm -f elf32 -o .build/boot.o src/boot.asm

View File

@@ -3,6 +3,7 @@
%define LEN header_end - header_start %define LEN header_end - header_start
%define CHECKSUM -(MAGIC + ARCH + LEN) %define CHECKSUM -(MAGIC + ARCH + LEN)
section .multiboot2
header_start: header_start:
align 4 align 4
dd MAGIC dd MAGIC
@@ -18,14 +19,186 @@ align 16
resb 16384 resb 16384
stack_top: stack_top:
extern kernel_main align 4096
pml4_table:
resb 4096
pdp_table:
resb 4096
pd_table:
resb 4096
section .text section .text
bits 32
global _start global _start
_start: _start:
mov esp, stack_top
call kernel_main
hang:
cli 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 hlt
jmp hang 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

28
src/boot.ld Normal file
View File

@@ -0,0 +1,28 @@
ENTRY(_start)
SECTIONS
{
. = 2M;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot2)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}

74
src/idt.c Normal file
View File

@@ -0,0 +1,74 @@
#include "idt.h"
#include "vga.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef struct
{
uint16_t address_low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t address_mid;
uint32_t address_high;
uint32_t reserved;
} __attribute__((packed)) interrupt_descriptor;
typedef struct
{
uint16_t limit;
uint64_t base;
} __attribute__((packed)) idtr_t;
typedef struct
{
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax;
uint64_t int_no;
uint64_t err_code;
uint64_t rip, cs, rflags, rsp, ss;
} __attribute__((packed)) isr_frame_t;
#define IDT_SIZE 256
extern void* isr_stub_table[];
static interrupt_descriptor idt[IDT_SIZE];
void idt_set_descriptor(uint8_t vector, void* handler, uint8_t dpl)
{
interrupt_descriptor* entry = &idt[vector];
entry->address_low = (uint64_t)handler & 0xFFFF;
entry->address_mid = ((uint64_t)handler >> 16) & 0xFFFF;
entry->address_high = (uint64_t)handler >> 32;
entry->selector = 0x08;
entry->flags = 0b1110 | ((dpl & 0b11) << 5) | (1 << 7);
entry->ist = 0;
entry->reserved = 0;
}
void exception_handler(isr_frame_t* frame)
{
vga_print("Interrupt\n");
vga_print(" int_no: ");
vga_print_uint(frame->int_no);
vga_print("\n");
vga_print(" err_code: ");
vga_print_uint(frame->err_code);
vga_print("\n");
__asm__ volatile("cli; hlt");
}
void idt_init(void)
{
for (int i = 0; i < 32; i++)
{
idt_set_descriptor(i, isr_stub_table[i], 0);
}
idtr_t idtr = { .base = (uintptr_t)&idt[0], .limit = (uint16_t)sizeof(interrupt_descriptor) * IDT_SIZE - 1 };
__asm__ volatile("lidt %0" : : "m"(idtr));
}

6
src/idt.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
void idt_init(void);
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags);

121
src/idt_stub.asm Normal file
View File

@@ -0,0 +1,121 @@
extern exception_handler
%macro ISR_NOERR 1
isr_stub_%1:
push qword 0
push qword %1
jmp isr_common
%endmacro
%macro ISR_ERR 1
isr_stub_%1:
push qword %1
jmp isr_common
%endmacro
isr_common:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rdi, rsp
call exception_handler
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
add rsp, 16
iretq
ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
ISR_NOERR 3
ISR_NOERR 4
ISR_NOERR 5
ISR_NOERR 6
ISR_NOERR 7
ISR_ERR 8
ISR_NOERR 9
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
ISR_NOERR 15
ISR_NOERR 16
ISR_ERR 17
ISR_NOERR 18
ISR_NOERR 19
ISR_NOERR 20
ISR_NOERR 21
ISR_NOERR 22
ISR_NOERR 23
ISR_NOERR 24
ISR_NOERR 25
ISR_NOERR 26
ISR_NOERR 27
ISR_NOERR 28
ISR_NOERR 29
ISR_ERR 30
ISR_NOERR 31
global isr_stub_table
isr_stub_table:
dq isr_stub_0
dq isr_stub_1
dq isr_stub_2
dq isr_stub_3
dq isr_stub_4
dq isr_stub_5
dq isr_stub_6
dq isr_stub_7
dq isr_stub_8
dq isr_stub_9
dq isr_stub_10
dq isr_stub_11
dq isr_stub_12
dq isr_stub_13
dq isr_stub_14
dq isr_stub_15
dq isr_stub_16
dq isr_stub_17
dq isr_stub_18
dq isr_stub_19
dq isr_stub_20
dq isr_stub_21
dq isr_stub_22
dq isr_stub_23
dq isr_stub_24
dq isr_stub_25
dq isr_stub_26
dq isr_stub_27
dq isr_stub_28
dq isr_stub_29
dq isr_stub_30
dq isr_stub_31

View File

@@ -1,7 +1,18 @@
#include "idt.h"
#include "vga.h" #include "vga.h"
void kernel_main(void) void kernel_main(void)
{ {
vga_clear(); vga_clear();
vga_print("Welcome to nub OS\n"); vga_print_success();
vga_print(" VGA intialzied\n");
idt_init();
vga_print_success();
vga_print(" IDT intialzied\n");
vga_print("\nWelcome to nub OS\n");
vga_print(">");
int x = 1 / 0;
} }

View File

@@ -1,4 +1,5 @@
#include "string.h" #include "string.h"
#include <stdbool.h>
int strcmp(const char* a, const char* b) int strcmp(const char* a, const char* b)
{ {
@@ -10,3 +11,79 @@ int strcmp(const char* a, const char* b)
return (*a == *b) ? 0 : (*a > *b) ? 1 : -1; return (*a == *b) ? 0 : (*a > *b) ? 1 : -1;
} }
void reverse(char* str, size_t length)
{
int start = 0;
int end = length - 1;
while (start < end)
{
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
int uitoa(uint64_t value, char* buffer)
{
int i = 0;
if (value == 0)
{
buffer[i++] = '0';
buffer[i] = '\0';
return i;
}
while (value > 0)
{
buffer[i++] = (value % 10) + '0';
value /= 10;
}
buffer[i] = '\0';
reverse(buffer, i);
return i;
}
int itoa(int64_t value, char* buffer)
{
int i = 0;
if (value == 0)
{
buffer[i++] = '0';
buffer[i] = '\0';
return i;
}
bool negative = false;
uint64_t v;
if (value < 0)
{
negative = true;
v = (uint64_t)(-value);
}
else
{
v = (uint64_t)value;
}
while (v > 0)
{
buffer[i++] = (v % 10) + '0';
v /= 10;
}
if (negative)
{
buffer[i++] = '-';
}
buffer[i] = '\0';
reverse(buffer, i);
return i;
}

View File

@@ -1,3 +1,9 @@
#pragma once #pragma once
int strcmp(const char* a, const char* b); #include <stddef.h>
#include <stdint.h>
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);

View File

@@ -1,4 +1,8 @@
#include "vga.h" #include "vga.h"
#include "string.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define ROWS 25 #define ROWS 25
#define COLUMNS 80 #define COLUMNS 80
@@ -108,4 +112,32 @@ void vga_print_colored(const char* string, vga_color_t color)
void vga_print(const char* string) void vga_print(const char* string)
{ {
vga_print_colored(string, vga_default_color()); vga_print_colored(string, vga_default_color());
}
void vga_print_success(void)
{
vga_print("[ ");
vga_print_colored("success", VGA_GREEN);
vga_print(" ]");
}
void vga_print_error(void)
{
vga_print("[ ");
vga_print_colored("error", VGA_RED);
vga_print(" ]");
}
void vga_print_uint(uint64_t value)
{
char buffer[11];
uitoa(value, buffer);
vga_print(buffer);
}
void vga_print_int(int64_t value)
{
char buffer[12];
itoa(value, buffer);
vga_print(buffer);
} }

View File

@@ -27,6 +27,12 @@ void vga_set_cursor_position(uint8_t row, uint8_t col);
void vga_print_colored(const char* string, vga_color_t color); void vga_print_colored(const char* string, vga_color_t color);
void vga_print(const char* string); void vga_print(const char* string);
void vga_print_success(void);
void vga_print_error(void);
void vga_print_uint(uint64_t value);
void vga_print_int(int64_t value);
static inline vga_color_t vga_color(vga_color_t fg_color, vga_color_t bg_color) static inline vga_color_t vga_color(vga_color_t fg_color, vga_color_t bg_color)
{ {
return fg_color | bg_color << 4; return fg_color | bg_color << 4;