29 Commits

Author SHA1 Message Date
4047bc3936 Update display.c
Added the 95% completely wired up display driver implementation file
2025-11-26 16:02:07 -08:00
7e54f0de66 Update display.h
updated header for display driver display.c and display.h this will need to be finished wired up. Old display driver would have done nothing.
2025-11-26 15:53:58 -08:00
940b2810cb Update io.h
adding the missing io
2025-11-20 10:07:01 -08:00
01f85f97ec Update fat12.h
better header for FAT12 kernel driver
2025-11-19 09:31:22 -08:00
fd2c567d29 Update fat12.c
implementation of kernel space fat12 kernel driver for fat12
2025-11-19 09:29:04 -08:00
9de9cc6523 Update scheduler.h 2025-11-19 08:44:15 -08:00
e9a78c835a Create context_switch.s
new context_switch.s for x86 IA32.
must confirm nasm.
2025-11-19 08:43:11 -08:00
77400d8f5a Update scheduler.c
old scheduler might not work on x86 IA-32 32 bit
2025-11-19 08:41:03 -08:00
cdf5676085 Merge pull request #70 from vmttmv/main
Kernel build fixes
2025-11-18 18:11:18 -08:00
vmttmv
8743fa9e24 Multiple changes:
- Makefile: fix linker script path
- irq.c: `irqN()` stubs
- irq.h: fix missing header
- isr.h/isr.c extern `interrupt_handlers`
- utils.c: remove duplicate `memcmp`
2025-11-19 03:32:06 +02:00
3036ee3dfd Delete bootloader/linker.ld
delete linker.ld as moved to kernel space
2025-11-14 14:18:54 -08:00
d5906d72de Move linker.ld
Move to kernel
2025-11-14 14:17:27 -08:00
2ab0efdee1 Delete bootloader/Makefile
Remove Makefile in bootloader
2025-11-14 14:15:51 -08:00
0e011c1682 Update README.md 2025-11-13 14:45:56 -08:00
eccf9d7d7c Merge pull request #69 from vmttmv/bootloader
BL implementation
2025-11-13 14:36:09 -08:00
vmttmv
62fe09d80d multiple changes: BL1/BL2/kernel separation (build system, etc.) BL2 implementation. BL documentation 2025-11-13 23:35:54 +02:00
f1b0670a15 Update kmain.c
Adding isr stuff
2025-11-10 05:59:22 -08:00
48fdb348ca Update irq.h
add implementation for irq handles to header
2025-11-10 05:49:36 -08:00
6dbd08c808 Update irq.c
Implement the irq handles
2025-11-10 05:48:02 -08:00
9ac3a2b862 Update terminal.c
Fixed minor issue with terminal
2025-11-10 05:19:25 -08:00
95f0507c24 Update keyboard.c
some issues with keyboard buffer fixed and interrupt greater than 32 would cause EOI to get sent to PIC 2x
2025-11-10 05:09:30 -08:00
70539f72b8 Update Makefile
This Makefile is for i686-elf cross compilation  only
2025-11-10 03:42:17 -08:00
1b046776e0 Update boot1.asm
remove duplicate print
2025-11-10 03:29:17 -08:00
2609f52dd6 Update paging.c
Fixed page table entry so it doesnt clobber kernel
2025-11-10 03:12:34 -08:00
f2e75c5142 Update kmalloc.c
Safer 1MB heap. Original value would have caused a heap overflow
2025-11-10 02:51:56 -08:00
056d3eb374 Update framebuffer.h
Added the stub graphics framebuffer stub
2025-11-04 01:21:35 -08:00
98f0f58ce4 Add stub code for the graphics franebuffer 2025-11-04 01:13:36 -08:00
7d9d0aeee3 Create memory.c
This is the implementation for memory.c memory.h pair to house the 
memset. memcmp, memcpy, memmove etc careful as there are now duplicates in utils implementation
2025-11-02 17:39:31 -08:00
8e5dff4271 Add memory.h with memcpy and memmove declarations
Define memory management functions and include guards.

Adding a home for memory functions memset, memcpy, memcmp, memmove

This is the header
2025-11-02 17:32:53 -08:00
33 changed files with 1154 additions and 409 deletions

View File

@@ -1,64 +1,58 @@
AS = nasm AS = nasm
ASFLAGS = -f elf32 -g -F dwarf
CC = gcc CC = gcc
CFLAGS = -std=c11 -m32 -ffreestanding -c -fno-stack-protector -fno-pie
LD = ld LD = ld
QEMU = qemu-system-i386 QEMU= qemu-system-i386
IMG_SIZE = 1440k
BUILD_DIR = build BUILD_DIR = build
DISK_IMG = $(BUILD_DIR)/disk.img
BOOT_SRC = bootloader/boot.asm STAGE2_SIZE = 2048
BOOT_OBJ = $(BUILD_DIR)/boot.o
BOOT_ELF = $(BUILD_DIR)/boot.elf
BOOT_IMG = $(BUILD_DIR)/boot.img
KERNEL_C_SRC = $(wildcard kernel/*.c) KERNEL_C_SRC = $(wildcard kernel/*.c)
KERNEL_ASM_SRC = $(wildcard kernel/*.asm) KERNEL_ASM_SRC = $(wildcard kernel/*.asm)
KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC)) KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC))
KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC)) KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC))
KERNEL_OBJ += $(BUILD_DIR)/boot1.o
KERNEL_ELF = $(BUILD_DIR)/kernel.elf
KERNEL_BIN = $(BUILD_DIR)/kernel.bin
DISK_IMG = $(BUILD_DIR)/disk.img all: $(DISK_IMG)
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_IMG) .PHONY: stage1 stage2 kernel run gdb clean
stage1: $(BUILD_DIR)
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/$@.o bootloader/$@.asm
$(LD) -Ttext=0x7c00 -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.o
objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
# NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow.
# Alternatively, convey the final stage2 size through other means to stage1.
stage2: $(BUILD_DIR)
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
$(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c
$(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o
objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin
$(BUILD_DIR)/asm_%.o: kernel/%.asm
$(AS) $(ASFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: kernel/%.c
$(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $@ $<
kernel: $(KERNEL_OBJ) | $(BUILD_DIR)
$(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ)
$(DISK_IMG): stage1 stage2 kernel
dd if=$(BUILD_DIR)/stage1.bin of=$@
dd if=$(BUILD_DIR)/stage2.bin of=$@ oflag=append conv=notrunc
dd if=$(BUILD_DIR)/kernel.elf of=$@ oflag=append conv=notrunc
truncate -s 1M $@
$(BUILD_DIR): $(BUILD_DIR):
mkdir -p $@ mkdir -p $@
$(BOOT_OBJ): $(BOOT_SRC) | $(BUILD_DIR) run:
$(AS) -f elf32 -g -F dwarf -o $@ $< qemu-system-i386 -s -S $(DISK_IMG)
$(BOOT_ELF): $(BOOT_OBJ) gdb:
$(LD) -Ttext=0x7c00 -melf_i386 -o $@ $< gdb -x gdb.txt
$(BOOT_IMG): $(BOOT_ELF)
objcopy -O binary $< $@
truncate -s $(IMG_SIZE) $@
$(BUILD_DIR)/boot1.o: bootloader/boot1.asm
$(AS) -f elf32 -o $@ $<
$(BUILD_DIR)/asm_%.o: kernel/%.asm
$(AS) -f elf32 -o $@ $<
$(BUILD_DIR)/%.o: kernel/%.c
$(CC) $(CFLAGS) $< -o $@
$(KERNEL_BIN): $(KERNEL_OBJ) | $(BUILD_DIR)
$(LD) -melf_i386 --oformat binary -T bootloader/linker.ld -o $@ $(KERNEL_OBJ)
$(DISK_IMG): $(BOOT_IMG) $(KERNEL_BIN)
dd if=$(BOOT_IMG) of=$@ bs=512 seek=4
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200
run: $(DISK_IMG)
$(QEMU) -drive file=$<,format=raw,if=floppy
.PHONY: stage1 clean
stage1: $(BOOT_IMG)
clean: clean:
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)

View File

27
bootloader/README.md Normal file
View File

@@ -0,0 +1,27 @@
# ClassicOS 2-stage bootloader
Bootloader documentation for ClassicOS
## Disk image organization:
```
[ 512 B ] [ 2048 B ] [ Unspecified ]
Stage 1 Stage 2 Kernel
```
## Stage 1 (`stage1.asm`)
Responsible for loading the second stage using BIOS routines, and switching to protected mode.
- Queries CHS parameters from BIOS
- Loads the second stage bootloader (2048 B) to `0x7c00`
- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space
- Enables A20
- Set CR0.PE (enable protected mode) and jump to stage 2
## Stage 2 (`stage2.asm, stage2_load.c`)
- Set up segment registers
- Load the kernel ELF header
- Parse the program headers, and load all `PT_LOAD` segments from disk
- Jump to the kernel entry

View File

@@ -1,203 +0,0 @@
; ==============================================================================
; boot1.asm - Second Stage Bootloader (Fixed Real Mode Transition)
; ==============================================================================
[BITS 32]
global _start
extern kmain
_start:
; Set up segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Stack (must be identity-mapped)
mov esp, 0x90000
; CPU Feature Detection: check CPUID support
pushfd ; Save flags
pop eax
mov ecx, eax
xor eax, 1 << 21 ; Flip ID bit
push eax
popfd
pushfd
pop eax
xor eax, ecx
jz .no_cpuid ; CPUID unsupported if no change
; CPUID supported, verify features
mov eax, 1
cpuid
; Check for paging support (bit 31 of edx)
test edx, 1 << 31
jz .no_paging_support
; Additional CPU feature checks could be added here
jmp .cpuid_check_done
.no_cpuid:
mov si, no_cpuid_msg
call print_string_16
jmp halt
.no_paging_support:
mov si, no_paging_msg
call print_string_16
jmp halt
.cpuid_check_done:
; Temporarily switch back to real mode
cli
mov eax, cr0
and eax, 0x7FFFFFFE ; Clear PE & PG bits
mov cr0, eax
jmp 0x18:real_mode_entry
; ----------------------------------------------------------------
[BITS 16]
real_mode_entry:
; Real mode for BIOS access (E820, VESA)
xor ax, ax
mov es, ax
; VESA call
mov di, VbeControllerInfo
mov ax, 0x4F00
int 0x10
jc vesa_error
; E820 memory map
xor ebx, ebx
mov edx, 0x534D4150
mov di, MemoryMapBuffer
mov [MemoryMapEntries], dword 0
.e820_loop:
mov eax, 0xE820
mov ecx, 24
int 0x15
jc e820_error
add di, 24
inc dword [MemoryMapEntries]
test ebx, ebx
jnz .e820_loop
jmp e820_done
e820_error:
mov si, e820_error_msg
call print_string_16
jmp halt
vesa_error:
mov si, vesa_error_msg
call print_string_16
; Fallback: set VGA text mode 3 and continue
mov ah, 0x00 ; BIOS Set Video Mode function
mov al, 0x03 ; VGA 80x25 text mode
int 0x10
; Clear screen
mov ah, 0x06 ; Scroll up function
mov al, 0 ; Clear entire screen
mov bh, 0x07 ; Text attribute (gray on black)
mov cx, 0 ; Upper-left corner
mov dx, 0x184F ; Lower-right corner
int 0x10
jmp e820_done ; Continue booting without VESA graphics
e820_done:
; Back to protected mode
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:protected_entry
; ----------------------------------------------------------------
[BITS 16]
print_string_16:
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp .loop
.done:
ret
e820_error_msg db "E820 Failed!", 0
vesa_error_msg db "VESA Failed!", 0
no_cpuid_msg db "No CPUID support detected!", 0
no_paging_msg db "CPU lacks paging support!", 0
; ----------------------------------------------------------------
[BITS 32]
protected_entry:
; Paging setup
xor eax, eax
mov edi, page_directory
mov ecx, 1024
rep stosd
mov edi, page_table
rep stosd
mov eax, page_table
or eax, 0x3
mov [page_directory], eax
mov ecx, 1024
mov edi, page_table
mov eax, 0x00000003
.fill_pages:
mov [edi], eax
add eax, 0x1000
add edi, 4
loop .fill_pages
mov eax, page_directory
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
jmp kmain
halt:
cli
.hang:
hlt
jmp .hang
; ----------------------------------------------------------------
; Data buffers and variables must be appropriately defined in your data section
MemoryMapBuffer times 128 db 0 ; 128*24 bytes reserved for E820 memory map (adjust size as needed)
MemoryMapEntries dd 0
VbeControllerInfo times 512 db 0 ; Buffer for VESA controller info (adjust size as needed)
; Define page directory and page table aligned as needed (in your data section)
align 4096
page_directory times 1024 dd 0
align 4096
page_table times 1024 dd 0
%assign pad_size 4096
%ifdef __SIZE__
%define size_current __SIZE__
%else
%define size_current ($ - $$)
%endif
%if size_current < pad_size
times pad_size - size_current db 0
%endif
checksum_byte db 0

View File

@@ -2,10 +2,16 @@
; boot.asm - First Stage Bootloader (CHS Based) ; boot.asm - First Stage Bootloader (CHS Based)
; ============================================================================== ; ==============================================================================
[BITS 16] ; Params for stage2
; [ORG 0x7C00] %define s2_addr 1 ; stage2 disk offset, in sectors
%define s2_laddr 0x7e00 ; stage2 load address
%define s2_size 2048 ; stage2 size
%define s2_nsect s2_size / 512 ; stage2 size in sectors
start: [BITS 16]
global _start
_start:
cli ; Disable interrupts cli ; Disable interrupts
mov [bootdev], dl ; Save boot device number (from BIOS in DL) mov [bootdev], dl ; Save boot device number (from BIOS in DL)
@@ -20,29 +26,16 @@ start:
mov ds, ax mov ds, ax
mov es, ax mov es, ax
; Query bios for disk parameters
call get_disk_params
; Load second-stage bootloader (boot1.asm) to 0x7E00 ; Load second-stage bootloader (boot1.asm) to 0x7E00
mov ax, 1 ; LBA of boot1.asm (starts at sector 1) mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
call lba_to_chs call lba_to_chs
mov al, 4 ; Number of sectors to read mov al, s2_nsect ; Number of sectors to read
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00) mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
call read_chs call read_chs
; Load kernel to 0x100000 (1 MB)
mov ax, 0xffff
mov es, ax ; Set ES for destination address (0xffff << 4 == 0xffff0)
mov ax, 5 ; LBA of kernel start (boot1 is 4 sectors: LBA 14 → kernel at LBA 5)
call lba_to_chs
mov al, 16 ; Number of sectors for kernel
mov bx, 0x10 ; Destination address offset (0xffff:0x10 = 0xffff << 4 + 0x10 = 0x100000)
call read_chs
jc disk_error
; Memory Validation: Verify checksum of second stage bootloader
mov si, 0x7E00 ; Start of second stage
mov cx, 512 * 4 ; Size in bytes (adjust if more sectors loaded)
call verify_checksum
jc disk_error ; Jump if checksum fails
; Enable A20 line ; Enable A20 line
call enable_a20 call enable_a20
jc a20_error ; Jump if A20 enable fails jc a20_error ; Jump if A20 enable fails
@@ -95,17 +88,32 @@ verify_checksum:
pop ax pop ax
ret ret
get_disk_params:
mov ah, 08h ; BIOS: Get Drive Parameters
int 13h
; TODO: error checking
; CL bits 05 contain sectors per track
mov al, cl
and al, 3Fh ; mask bits 05
mov ah, 0
mov [sectors_per_track], ax
; DH = maximum head number (0-based)
mov al, dh
inc ax ; convert to count (heads = maxhead + 1)
mov [heads_per_cylinder], ax
ret
; ---------------------------------------------------------------- ; ----------------------------------------------------------------
; CHS Disk Read Routine ; CHS Disk Read Routine
; AL = number of sectors ; AL = number of sectors
; CL = starting sector (1-based) ; CL = starting sector (1-based)
; SI = destination offset (Segment:ES already set)
; Inputs: ; Inputs:
; AL = sector count ; AL = sector count
; CH = cylinder ; CH = cylinder
; DH = head ; DH = head
; CL = sector (163, with top 2 bits as high cylinder bits) ; CL = sector (163, with top 2 bits as high cylinder bits)
; SI = destination offset (segment ES must be set)
; ---------------------------------------------------------------- ; ----------------------------------------------------------------
; Convert LBA to CHS ; Convert LBA to CHS
@@ -117,36 +125,29 @@ verify_checksum:
; CL = sector (1-63, top 2 bits are upper cylinder bits) ; CL = sector (1-63, top 2 bits are upper cylinder bits)
lba_to_chs: lba_to_chs:
pusha ; Sector
xor dx, dx xor dx, dx
mov bx, [sectors_per_track] mov bx, ax
div bx ; AX = LBA / sectors_per_track, DX = remainder (sector number) div word [sectors_per_track] ; divide lba with max sectors
mov si, ax ; SI = temp quotient (track index) add dl, 1 ; take the remainder, sectors start at 1
mov cx, [heads_per_cylinder] mov cl, dl ; sector is in cl
xor dx, dx
div cx ; AX = cylinder, DX = head
mov ch, al ; CH = cylinder low byte
mov dh, dl ; DH = head
; Now take sector number from earlier remainder ; Head
mov cx, si ; Copy track index to CX to access CL mov ax, bx
and cl, 0x3F ; Mask to 6 bits (sector number) mov dx, 0
inc cl ; Sector numbers are 1-based div word [sectors_per_track] ; divide lba with max sectors
mov dx, 0
div word [heads_per_cylinder] ; divide quotient with heads
mov dh, dl ; take the remainder, head is in dh
; Insert upper 2 bits of cylinder into CL ; Cylinder
mov ah, al ; AH = cylinder again mov ch, al ; take the quotient, cylinder is in ch
and ah, 0xC0 ; Get top 2 bits of cylinder
or cl, ah ; OR them into sector byte
popa
ret ret
read_chs: read_chs:
pusha pusha
push dx push dx
mov cx, 5
.retry: .retry:
mov ah, 0x02 ; BIOS: Read sectors mov ah, 0x02 ; BIOS: Read sectors
mov dl, [bootdev] ; Boot device mov dl, [bootdev] ; Boot device
@@ -262,7 +263,7 @@ switch_to_pm:
mov eax, cr0 mov eax, cr0
or eax, 1 or eax, 1
mov cr0, eax mov cr0, eax
jmp 0x08:0x7E00 jmp 0x08:0x7E00 ; jump to S2
; ---------------------------------------------------------------- ; ----------------------------------------------------------------
print_string_16: print_string_16:
@@ -284,8 +285,8 @@ halt:
hlt hlt
bootdev db 0 bootdev db 0
sectors_per_track dw 63 sectors_per_track dw 0
heads_per_cylinder dw 255 heads_per_cylinder dw 0
times 510 - ($ - $$) db 0 times 510 - ($ - $$) db 0
dw 0xAA55 dw 0xAA55

98
bootloader/stage2.asm Normal file
View File

@@ -0,0 +1,98 @@
[BITS 32]
global _start
global ata_lba_read
extern load_kernel
_start:
; Set up segments
; Data segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Code segment
mov ax, 0x08
mov cs, ax
; Stack (must be identity-mapped)
mov esp, 0x90000
call load_kernel
jmp eax
; ----------------------------------------------------------------------------
; ATA read sectors (LBA mode)
;
; sysv32 abi signature:
; void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr);
; ----------------------------------------------------------------------------
ata_lba_read:
push ebp
mov ebp, esp
push ebx
push ecx
push edx
push edi
mov eax, [ebp+8] ; arg #1 = LBA
mov cl, [ebp+12] ; arg #2 = # of sectors
mov edi, [ebp+16] ; arg #3 = buffer address
and eax, 0x0FFFFFFF
mov ebx, eax ; Save LBA in RBX
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
shr eax, 24 ; Get bit 24 - 27 in al
or al, 11100000b ; Set bit 6 in al for LBA mode
out dx, al
mov edx, 0x01F2 ; Port to send number of sectors
mov al, cl ; Get number of sectors from CL
out dx, al
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
mov eax, ebx ; Get LBA from EBX
out dx, al
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 8 ; Get bit 8 - 15 in AL
out dx, al
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 16 ; Get bit 16 - 23 in AL
out dx, al
mov edx, 0x1F7 ; Command port
mov al, 0x20 ; Read with retry.
out dx, al
mov bl, cl ; Save # of sectors in BL
.wait_drq:
mov edx, 0x1F7
.do_wait_drq:
in al, dx
test al, 8 ; the sector buffer requires servicing.
jz .do_wait_drq ; keep polling until the sector buffer is ready.
mov edx, 0x1F0 ; Data port, in and out
mov ecx, 256
rep insw ; in to [RDI]
dec bl ; are we...
jnz .wait_drq ; ...done?
pop edi
pop edx
pop ecx
pop ebx
pop ebp
ret

14
bootloader/stage2.ld Normal file
View File

@@ -0,0 +1,14 @@
SECTIONS {
. = 0x7e00;
.text : { *(.text*) }
.rodata : { *(.rodata*) }
.data : { *(.data*) }
.bss : {
*(.bss*)
*(COMMON)
}
read_buf = .;
}

118
bootloader/stage2_load.c Normal file
View File

@@ -0,0 +1,118 @@
#include <stdint.h>
// ELF Ident indexes
#define EI_NIDENT 16
// Program header types
#define PT_NULL 0
#define PT_LOAD 1
// ELF Header (32-bit)
typedef struct {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry; // Entry point
uint32_t e_phoff; // Program header table offset
uint32_t e_shoff; // Section header table offset
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} __attribute__((packed)) Elf32_Ehdr;
// Program Header (32-bit)
typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} __attribute__((packed)) Elf32_Phdr;
// Load an ELF executable into memory.
static int elf_load(const void* data, void (*load_segment)(uint8_t *vaddr, uint32_t src, uint32_t size)) {
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff);
for (int i = 0; i < header->e_phnum; i++) {
if (ph[i].p_type != PT_LOAD)
continue;
uint32_t offset = ph[i].p_offset;
uint32_t vaddr = ph[i].p_vaddr;
uint32_t filesz = ph[i].p_filesz;
uint32_t memsz = ph[i].p_memsz;
// Copy data segment
//load_segment((uint8_t *)vaddr, offset, filesz);
load_segment((uint8_t *)vaddr, offset, filesz);
// Zero remaining BSS (if any)
if (memsz > filesz) {
uint8_t* bss_start = (uint8_t*)(vaddr + filesz);
for (uint32_t j = 0; j < memsz - filesz; j++) {
bss_start[j] = 0;
}
}
}
return header->e_entry;
}
#define KERN_START_SECT 5
#define MAX(a, b) ((a)>(b) ? (a) : (b))
extern void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr);
extern uint8_t read_buf[];
static uint32_t
total_header_size(const Elf32_Ehdr *header) {
uint32_t phend = header->e_phoff + header->e_phentsize*header->e_phnum;
// Align to 512
return (phend + 511) & ~511;
}
static void read_sectors(uint8_t *vaddr, uint32_t offset, uint32_t size) {
// # of sectors to read
uint32_t rem_nsect = ((size + 511) & ~511) / 512;
// Current lba address, offset by the first sector already read
uint32_t lba = KERN_START_SECT + offset / 512;
// Max 255 sectors at a time
while (rem_nsect) {
uint8_t nsect = rem_nsect > 255 ? 255 : rem_nsect;
ata_lba_read(lba, nsect, vaddr);
vaddr += nsect * 512;
rem_nsect -= nsect;
lba += nsect;
}
}
void *load_kernel(void) {
// Read the first sector
ata_lba_read(KERN_START_SECT, 1, read_buf);
const Elf32_Ehdr* header = (const Elf32_Ehdr*)read_buf;
// Remaining data size, subtract the first 512B already read
uint32_t rem = total_header_size(header) - 512;
// Read the rest if necessary
if (rem)
read_sectors(read_buf+512, 512, rem);
elf_load(read_buf, read_sectors);
return (void *)header->e_entry;
}

6
gdb.txt Normal file
View File

@@ -0,0 +1,6 @@
target remote :1234
add-symbol-file build/stage1.elf
add-symbol-file build/stage2.elf
add-symbol-file build/kernel.elf
hbreak *0x7c00
c

25
kernel/context_switch.s Normal file
View File

@@ -0,0 +1,25 @@
.global ctx_switch
; void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp);
; Arguments on stack (cdecl convention):
; [ESP + 4] -> old_sp_ptr (pointer to the 'stack_ptr' field of current task)
; [ESP + 8] -> new_sp (value of 'stack_ptr' of the next task)
ctx_switch:
; 1. Save the context of the CURRENT task
pushf ; Save EFLAGS (CPU status flags)
pusha ; Save all General Purpose Regs (EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI)
; 2. Save the current stack pointer (ESP) into the pointer passed as 1st arg
mov eax, [esp + 40] ; Get 1st argument (old_sp_ptr). Offset 40 = 36 (regs) + 4 (ret addr)
mov [eax], esp ; *old_sp_ptr = ESP
; 3. Load the stack pointer of the NEW task
mov esp, [esp + 44] ; Get 2nd argument (new_sp). Offset 44 = 40 + 4
; 4. Restore the context of the NEW task
popa ; Restore all General Purpose Regs
popf ; Restore EFLAGS
; 5. Jump to the new task (The 'ret' pops EIP from the new stack)
ret

View File

@@ -1,36 +1,79 @@
#include "display.h" #include "display.h"
#include "io.h" // Include your I/O header for port access #include "io.h"
#include "vga.h" #include "vga.h"
// Initialize the display // Initialize the display
void init_display(void) { void init_display(void) {
// Initialize VGA settings, if necessary // Initialize the VGA driver. This typically sets up the 80x25 text mode,
// This could involve setting up the VGA mode, etc. // clears the screen, and sets the cursor.
set_display_mode(0x13); // Example: Set to 320x200 256-color mode vga_init();
} }
// Enumerate connected displays // Enumerate connected displays
void enumerate_displays(void) { void enumerate_displays(void) {
// This is a simplified example. Actual enumeration may require // This function is often a complex operation in a real driver.
// reading from specific VGA registers or using BIOS interrupts. // In this simplified kernel/VGA text mode environment, we use printf
// to output a message and rely on the fact that VGA is present.
// For demonstration, we will just print a message // Clear the display before printing a message
// In a real driver, you would check the VGA registers vga_clear(vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK));
// to determine connected displays.
clear_display(); // Output a simplified enumeration message
// Here you would typically read from VGA registers to find connected displays vga_printf("Display: Standard VGA Text Mode (80x25) Detected.\n");
// For example, using inb() to read from VGA ports
// In a real driver, you would use inb() and outb() with specific VGA ports
// to read information (e.g., from the CRTC registers 0x3D4/0x3D5)
// to check for display presence or configuration.
} }
// Set the display mode // Set the display mode
// NOTE: Setting arbitrary VGA modes (like 0x13 for 320x200) is very complex
// and requires writing hundreds of register values, often done via BIOS in
// real mode. Since we are in protected mode and have a simple text driver,
// this function is kept simple or treated as a placeholder for full mode changes.
void set_display_mode(uint8_t mode) { void set_display_mode(uint8_t mode) {
// Set the VGA mode by writing to the appropriate registers // Check if the requested mode is a known mode (e.g., VGA Text Mode 3)
// For this example, we simply acknowledge the call.
// A true mode set would involve complex register sequencing.
// The provided vga.c is a Text Mode driver, so a graphical mode set
// like 0x13 (320x200 256-color) would break the existing vga_printf functionality.
// A simplified text-mode-specific response:
if (mode == 0x03) { // Mode 3 is standard 80x25 text mode
vga_printf("Display mode set to 80x25 Text Mode (Mode 0x03).\n");
vga_init(); // Re-initialize the text mode
} else {
// Simple I/O example based on the original structure (Caution: Incomplete for full mode set)
outb(VGA_PORT, mode); // Example function to write to a port outb(VGA_PORT, mode); // Example function to write to a port
vga_printf("Attempting to set display mode to 0x%x. (Warning: May break current display)\n", mode);
}
} }
// Clear the display // Clear the display
void clear_display(void) { void clear_display(void) {
// Clear the display by filling it with a color // Use the VGA driver's clear function, typically clearing to black on light grey
// This is a placeholder for actual clearing logic // or black on black. We'll use the black on light grey from vga_init for consistency.
// You would typically write to video memory here vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY));
// Reset cursor to 0, 0
vga_set_cursor_position(0, 0);
}
// Helper function to write a string
void display_write_string(const char* str) {
// Use the VGA driver's string writing function
vga_write_string(str, my_strlen(str));
}
// Helper function to print a formatted string
void display_printf(const char* format, ...) {
// Use the VGA driver's printf function
va_list args;
va_start(args, format);
// The vga_printf function already handles the va_list internally,
// so we can just call it directly.
vga_printf(format, args);
va_end(args);
} }

View File

@@ -2,13 +2,21 @@
#define DISPLAY_H #define DISPLAY_H
#include <stdint.h> #include <stdint.h>
#include "vga.h" // Include VGA functions
#define VGA_PORT 0x3C0 // Base port for VGA #define VGA_PORT 0x3C0 // Base port for VGA (Often used for general control, though 0x3D4/0x3D5 are used for cursor)
// Function prototypes // Function prototypes
void init_display(void); void init_display(void);
void enumerate_displays(void); void enumerate_displays(void);
void set_display_mode(uint8_t mode); void set_display_mode(uint8_t mode); // In this context, modes are typically BIOS or VESA modes, which are complex.
// We'll treat this as a placeholder/simple mode call.
void clear_display(void); void clear_display(void);
// New function to write a string using the VGA driver
void display_write_string(const char* str);
// New function to print a formatted string using the VGA driver
void display_printf(const char* format, ...);
#endif // DISPLAY_H #endif // DISPLAY_H

View File

@@ -1,5 +1,184 @@
#include "fat12.h" #include "fat12.h"
#include <stddef.h> // for NULL
// --- Globals for Filesystem State ---
static fat12_bpb_t bpb;
static uint32_t fat_start_lba;
static uint32_t root_dir_lba;
static uint32_t data_start_lba;
static uint32_t root_dir_sectors;
// Scratch buffer to read sectors (avoids large stack usage)
static uint8_t g_sector_buffer[FAT12_SECTOR_SIZE];
// --- Utils (Since we don't have string.h) ---
static int k_memcmp(const void *s1, const void *s2, uint32_t n) {
const uint8_t *p1 = (const uint8_t *)s1;
const uint8_t *p2 = (const uint8_t *)s2;
for (uint32_t i = 0; i < n; i++) {
if (p1[i] != p2[i]) return p1[i] - p2[i];
}
return 0;
}
// Converts "file.txt" to "FILE TXT" for comparison
static void to_fat_name(const char *src, char *dest) {
// Initialize with spaces
for(int i=0; i<11; i++) dest[i] = ' ';
int i = 0, j = 0;
// Copy Name
while (src[i] != '\0' && src[i] != '.' && j < 8) {
// Convert to uppercase (simple version)
char c = src[i];
if (c >= 'a' && c <= 'z') c -= 32;
dest[j++] = c;
i++;
}
// Skip extension dot
if (src[i] == '.') i++;
// Copy Extension
j = 8;
while (src[i] != '\0' && j < 11) {
char c = src[i];
if (c >= 'a' && c <= 'z') c -= 32;
dest[j++] = c;
i++;
}
}
// --- Core Logic ---
void fat12_init() { void fat12_init() {
// Filesystem initialization code // 1. Read Boot Sector (LBA 0)
disk_read_sector(0, g_sector_buffer);
// 2. Copy BPB data safely
// We cast the buffer to our struct
fat12_bpb_t *boot_sector = (fat12_bpb_t*)g_sector_buffer;
bpb = *boot_sector;
// 3. Calculate System Offsets
fat_start_lba = bpb.reserved_sectors;
// Root Dir starts after FATs
// LBA = Reserved + (FatCount * SectorsPerFat)
root_dir_lba = fat_start_lba + (bpb.fat_count * bpb.sectors_per_fat);
// Calculate size of Root Directory in sectors
// (Entries * 32 bytes) / 512
root_dir_sectors = (bpb.dir_entries_count * 32 + FAT12_SECTOR_SIZE - 1) / FAT12_SECTOR_SIZE;
// Data starts after Root Directory
data_start_lba = root_dir_lba + root_dir_sectors;
}
// Helper: Read the FAT table to find the NEXT cluster
static uint16_t fat12_get_next_cluster(uint16_t current_cluster) {
// FAT12 Offset Calculation:
// Offset = Cluster + (Cluster / 2)
uint32_t fat_offset = current_cluster + (current_cluster / 2);
uint32_t fat_sector = fat_start_lba + (fat_offset / FAT12_SECTOR_SIZE);
uint32_t ent_offset = fat_offset % FAT12_SECTOR_SIZE;
// Read the sector containing the FAT entry
disk_read_sector(fat_sector, g_sector_buffer);
// Read 16 bits (2 bytes)
// Note: If ent_offset == 511, the entry spans two sectors.
// For simplicity in this snippet, we ignore that edge case (rare).
// A robust kernel would check if(ent_offset == 511) and read next sector.
uint16_t val = *(uint16_t*)&g_sector_buffer[ent_offset];
if (current_cluster & 1) {
return val >> 4; // Odd: High 12 bits
} else {
return val & 0x0FFF; // Even: Low 12 bits
}
}
file_t fat12_open(const char *filename) {
file_t file = {0};
char target_name[11];
to_fat_name(filename, target_name);
// Search Root Directory
for (uint32_t i = 0; i < root_dir_sectors; i++) {
disk_read_sector(root_dir_lba + i, g_sector_buffer);
fat12_entry_t *entry = (fat12_entry_t*)g_sector_buffer;
// Check all 16 entries in this sector (512 / 32 = 16)
for (int j = 0; j < 16; j++) {
if (entry[j].filename[0] == 0x00) return file; // End of Dir
// Check if filename matches
if (k_memcmp(entry[j].filename, target_name, 11) == 0) {
// Found it!
file.start_cluster = entry[j].low_cluster_num;
file.size = entry[j].file_size;
// Initialize file cursor
file.current_cluster = file.start_cluster;
file.bytes_read = 0;
return file;
}
}
}
// Not found (file.start_cluster will be 0)
return file;
}
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read) {
if (file->start_cluster == 0) return 0; // File not open
uint32_t total_read = 0;
while (bytes_to_read > 0) {
// Check for EOF marker in FAT12 (>= 0xFF8)
if (file->current_cluster >= 0xFF8) break;
// Calculate Physical LBA of current cluster
// LBA = DataStart + ((Cluster - 2) * SectorsPerCluster)
uint32_t lba = data_start_lba + ((file->current_cluster - 2) * bpb.sectors_per_cluster);
// Read the cluster
// NOTE: Assumes SectorsPerCluster = 1 (Standard Floppy)
disk_read_sector(lba, g_sector_buffer);
// Determine how much to copy from this sector
uint32_t chunk_size = FAT12_SECTOR_SIZE;
// If the file is smaller than a sector, or we are at the end
if (chunk_size > bytes_to_read) chunk_size = bytes_to_read;
// Check if we are reading past file size
if (file->bytes_read + chunk_size > file->size) {
chunk_size = file->size - file->bytes_read;
}
// Copy to user buffer
for (uint32_t i = 0; i < chunk_size; i++) {
buffer[total_read + i] = g_sector_buffer[i];
}
total_read += chunk_size;
file->bytes_read += chunk_size;
bytes_to_read -= chunk_size;
// If we finished this cluster, move to the next one
if (chunk_size == FAT12_SECTOR_SIZE) { // Or strictly logic based on position
file->current_cluster = fat12_get_next_cluster(file->current_cluster);
} else {
// We finished the file or the request
break;
}
}
return total_read;
} }

View File

@@ -1,47 +1,67 @@
#ifndef FAT12_H #ifndef FAT12_H
#define FAT12_H #define FAT12_H
#include <stdint.h> /* Include standard integer types */ #include <stdint.h>
#include <stdio.h> /* Include standard I/O library */
#include <stdlib.h> /* Include standard library */
#define FAT12_SECTOR_SIZE 512 /* Sector size for FAT12 */ // --- Configuration ---
#define FAT12_MAX_FILES 128 /* Maximum number of files in root directory */ #define FAT12_SECTOR_SIZE 512
#define FAT12_ROOT_DIR_SECTORS 1 /* Number of sectors for root directory */
// --- On-Disk Structures (Must be Packed) ---
// BIOS Parameter Block (Start of Boot Sector)
typedef struct { typedef struct {
uint8_t jump[3]; /* Jump instruction for boot */ uint8_t jump[3];
char oem[8]; /* OEM name */ char oem[8];
uint16_t bytes_per_sector; /* Bytes per sector */ uint16_t bytes_per_sector; // 512
uint8_t sectors_per_cluster; /* Sectors per cluster */ uint8_t sectors_per_cluster; // 1
uint16_t reserved_sectors; /* Reserved sectors count */ uint16_t reserved_sectors; // 1 (Boot sector)
uint8_t num_fats; /* Number of FATs */ uint8_t fat_count; // 2
uint16_t max_root_dir_entries; /* Max entries in root directory */ uint16_t dir_entries_count; // 224
uint16_t total_sectors; /* Total sectors */ uint16_t total_sectors; // 2880
uint8_t media_descriptor; /* Media descriptor */ uint8_t media_descriptor; // 0xF0
uint16_t fat_size; /* Size of each FAT */ uint16_t sectors_per_fat; // 9
uint16_t sectors_per_track; /* Sectors per track */ uint16_t sectors_per_track; // 18
uint16_t num_heads; /* Number of heads */ uint16_t heads; // 2
uint32_t hidden_sectors; /* Hidden sectors count */ uint32_t hidden_sectors;
uint32_t total_sectors_large; /* Total sectors for large disks */ uint32_t total_sectors_large;
} __attribute__((packed)) FAT12_BootSector; /* Packed structure for boot sector */ } __attribute__((packed)) fat12_bpb_t;
// Directory Entry (32 bytes)
typedef struct { typedef struct {
char name[11]; /* File name (8.3 format) */ char filename[8];
uint8_t attr; /* File attributes */ char ext[3];
uint16_t reserved; /* Reserved */ uint8_t attributes;
uint16_t time; /* Time of last write */ uint8_t reserved;
uint16_t date; /* Date of last write */ uint8_t creation_ms;
uint16_t start_cluster; /* Starting cluster number */ uint16_t creation_time;
uint32_t file_size; /* File size in bytes */ uint16_t creation_date;
} __attribute__((packed)) FAT12_DirEntry; /* Directory entry structure */ uint16_t last_access_date;
uint16_t high_cluster_num; // Always 0 in FAT12
uint16_t last_mod_time;
uint16_t last_mod_date;
uint16_t low_cluster_num; // The starting cluster
uint32_t file_size; // Size in bytes
} __attribute__((packed)) fat12_entry_t;
void initialize_fat12(const char *disk_image); /* Function to initialize FAT12 */ // --- Kernel File Handle ---
void read_fat12(const char *disk_image); /* Function to read FAT12 */ // This is what your kernel uses to track an open file
void write_fat12(const char *disk_image); /* Function to write FAT12 */ typedef struct {
void list_files(const char *disk_image); /* Function to list files in root directory */ char name[11];
void read_file(const char *disk_image, const char *filename); /* Function to read a file */ uint32_t size;
void write_file(const char *disk_image, const char *filename, const uint8_t *data, size_t size); /* Function to write a file */ uint16_t start_cluster;
uint16_t current_cluster;
uint32_t current_sector_in_cluster;
uint32_t bytes_read;
} file_t;
#endif // --- Public API ---
/* FAT12_H */
// You must implement this in your disk driver (e.g., floppy.c)
// Returns 0 on success, non-zero on error.
extern int disk_read_sector(uint32_t lba, uint8_t *buffer);
void fat12_init();
file_t fat12_open(const char *filename);
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read);
#endif // FAT12_H

View File

@@ -0,0 +1,92 @@
#include "framebuffer.h"
#include <stddef.h>
#include <stdint.h>
// Simple init
void framebuffer_init(framebuffer_t *fb, void *base, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp) {
fb->base = base;
fb->width = width;
fb->height = height;
fb->pitch = pitch;
fb->bpp = bpp;
fb->initialized = true;
}
// Pack color into 32-bit value. Format: 0xAARRGGBB
uint32_t framebuffer_pack_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
}
void framebuffer_put_pixel(framebuffer_t *fb, uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
if (!fb->initialized) return;
if (x >= fb->width || y >= fb->height) return;
if (fb->bpp != 32) return; // only 32bpp implemented here
uint8_t *line = (uint8_t*)fb->base + (size_t)y * fb->pitch;
uint32_t *pixel = (uint32_t*)(line + x * 4);
*pixel = framebuffer_pack_color(r, g, b, a);
}
void framebuffer_clear(framebuffer_t *fb, uint8_t r, uint8_t g, uint8_t b) {
if (!fb->initialized) return;
if (fb->bpp != 32) return;
uint32_t color = framebuffer_pack_color(r,g,b,0xFF);
for (uint32_t y = 0; y < fb->height; ++y) {
uint32_t *row = (uint32_t*)((uint8_t*)fb->base + (size_t)y * fb->pitch);
for (uint32_t x = 0; x < fb->width; ++x) {
row[x] = color;
}
}
}
void framebuffer_fill_rect(framebuffer_t *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t r, uint8_t g, uint8_t b) {
if (!fb->initialized) return;
if (fb->bpp != 32) return;
if (x >= fb->width || y >= fb->height) return;
if (x + w > fb->width) w = fb->width - x;
if (y + h > fb->height) h = fb->height - y;
uint32_t color = framebuffer_pack_color(r,g,b,0xFF);
for (uint32_t yy = 0; yy < h; ++yy) {
uint32_t *row = (uint32_t*)((uint8_t*)fb->base + (size_t)(y + yy) * fb->pitch) + x;
for (uint32_t xx = 0; xx < w; ++xx) {
row[xx] = color;
}
}
}
// Simple blit from a source buffer with 32bpp pixels and given pitch (bytes per line)
void framebuffer_blit(framebuffer_t *fb, uint32_t dst_x, uint32_t dst_y, const void *src, uint32_t src_w, uint32_t src_h, uint32_t src_pitch) {
if (!fb->initialized) return;
if (fb->bpp != 32) return;
if (dst_x >= fb->width || dst_y >= fb->height) return;
uint32_t copy_w = src_w;
uint32_t copy_h = src_h;
if (dst_x + copy_w > fb->width) copy_w = fb->width - dst_x;
if (dst_y + copy_h > fb->height) copy_h = fb->height - dst_y;
const uint8_t *s = (const uint8_t*)src;
for (uint32_t yy = 0; yy < copy_h; ++yy) {
uint32_t *dst_row = (uint32_t*)((uint8_t*)fb->base + (size_t)(dst_y + yy) * fb->pitch) + dst_x;
const uint32_t *src_row = (const uint32_t*)(s + (size_t)yy * src_pitch);
for (uint32_t xx = 0; xx < copy_w; ++xx) {
dst_row[xx] = src_row[xx];
}
}
}
void framebuffer_test_pattern(framebuffer_t *fb) {
if (!fb->initialized) return;
// simple color bars
uint32_t band_h = fb->height / 6;
framebuffer_fill_rect(fb, 0, 0, fb->width, band_h, 0xFF, 0x00, 0x00); // red
framebuffer_fill_rect(fb, 0, band_h, fb->width, band_h, 0x00, 0xFF, 0x00); // green
framebuffer_fill_rect(fb, 0, band_h*2, fb->width, band_h, 0x00, 0x00, 0xFF); // blue
framebuffer_fill_rect(fb, 0, band_h*3, fb->width, band_h, 0xFF, 0xFF, 0x00); // yellow
framebuffer_fill_rect(fb, 0, band_h*4, fb->width, band_h, 0xFF, 0x00, 0xFF); // magenta
framebuffer_fill_rect(fb, 0, band_h*5, fb->width, fb->height - band_h*5, 0x00, 0xFF, 0xFF); // cyan
}

View File

@@ -0,0 +1,25 @@
#ifndef FRAMEBUFFER_H
#define FRAMEBUFFER_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct {
void *base;
uint32_t width;
uint32_t height;
uint32_t pitch;
uint8_t bpp;
bool initialized;
} framebuffer_t;
void framebuffer_init(framebuffer_t *fb, void *base, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp);
uint32_t framebuffer_pack_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
void framebuffer_put_pixel(framebuffer_t *fb, uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
void framebuffer_clear(framebuffer_t *fb, uint8_t r, uint8_t g, uint8_t b);
void framebuffer_fill_rect(framebuffer_t *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t r, uint8_t g, uint8_t b);
void framebuffer_blit(framebuffer_t *fb, uint32_t dst_x, uint32_t dst_y, const void *src, uint32_t src_w, uint32_t src_h, uint32_t src_pitch);
void framebuffer_test_pattern(framebuffer_t *fb);
#endif /* FRAMEBUFFER_H */

View File

@@ -13,4 +13,24 @@ static inline uint8_t inb(uint16_t port) {
return ret; return ret;
} }
static inline void outw(uint16_t port, uint16_t val) {
__asm__("outw %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t ret;
__asm__("inw %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outl(uint16_t port, uint32_t val) {
__asm__("outl %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint32_t inl(uint16_t port) {
uint32_t ret;
__asm__("inl %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
#endif #endif

View File

@@ -1,5 +1,76 @@
#include "idt.h"
#include "irq.h" #include "irq.h"
#include "io.h"
#include "isr.h"
void irq_init() { #define PIC1_CMD 0x20
// IRQ initialization code #define PIC1_DATA 0x21
#define PIC2_CMD 0xA0
#define PIC2_DATA 0xA1
// FIXME: stubs
void irq0() {}
void irq1() {}
void irq2() {}
void irq3() {}
void irq4() {}
void irq5() {}
void irq6() {}
void irq7() {}
void irq8() {}
void irq9() {}
void irq10() {}
void irq11() {}
void irq12() {}
void irq13() {}
void irq14() {}
void irq15() {}
// --- stubs end
void irq_remap(void)
{
outb(PIC1_CMD, 0x11); // ICW1 edge triggered, cascade, need ICW4
outb(PIC2_CMD, 0x11);
outb(PIC1_DATA, 0x20); // ICW2 master base vector
outb(PIC2_DATA, 0x28); // ICW2 slave base vector
outb(PIC1_DATA, 0x04); // ICW3 slave on IRQ2
outb(PIC2_DATA, 0x02); // ICW3 cascade identity
outb(PIC1_DATA, 0x01); // ICW4 8086 mode
outb(PIC2_DATA, 0x01);
// Mask everything except IRQ0 (timer) and IRQ1 (keyboard) for now
outb(PIC1_DATA, 0b11111001);
outb(PIC2_DATA, 0xFF);
}
void irq_install(void)
{
irq_remap();
/* Fill IRQ entries in the IDT (0x20 … 0x2F) */
//extern void irq0(), irq1(), irq2(), irq3(), irq4(), irq5(), irq6(), irq7();
//extern void irq8(), irq9(), irq10(), irq11(), irq12(), irq13(), irq14(), irq15();
idt_set_gate(0x20, (uint32_t)irq0);
idt_set_gate(0x21, (uint32_t)irq1);
/* … repeat for the rest or loop … */
for (int i = 2; i < 16; ++i)
idt_set_gate(0x20 + i, (uint32_t)irq0 + i * 8); // crude but works
}
/* Called from the assembly stubs (see irq.asm below) */
void irq_handler(uint32_t int_num)
{
/* int_num is the *remapped* vector, e.g. 0x21 for keyboard */
if (interrupt_handlers[int_num]) {
interrupt_handlers[int_num]();
}
/* ---- EOI ---- */
if (int_num >= 0x28) // slave PIC
outb(PIC2_CMD, 0x20);
outb(PIC1_CMD, 0x20); // always master
} }

View File

@@ -1,6 +1,10 @@
#ifndef IRQ_H #ifndef IRQ_H
#define IRQ_H #define IRQ_H
void irq_init(); #include "types.h"
#endif // IRQ_H void irq_remap(void);
void irq_install(void);
void irq_handler(uint32_t int_num);
#endif

View File

@@ -4,7 +4,7 @@
#include "io.h" #include "io.h"
#include "print.h" #include "print.h"
static isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 }; isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
void isr_handler(uint32_t int_num, uint32_t err_code) { void isr_handler(uint32_t int_num, uint32_t err_code) {
terminal_write("Interrupt occurred: "); terminal_write("Interrupt occurred: ");

View File

@@ -6,6 +6,7 @@
#define MAX_INTERRUPTS 256 #define MAX_INTERRUPTS 256
typedef void (*isr_callback_t)(void); typedef void (*isr_callback_t)(void);
extern isr_callback_t interrupt_handlers[MAX_INTERRUPTS];
void isr_handler(uint32_t int_num, uint32_t err_code); void isr_handler(uint32_t int_num, uint32_t err_code);
void register_interrupt_handler(uint8_t n, isr_callback_t handler); void register_interrupt_handler(uint8_t n, isr_callback_t handler);

View File

@@ -4,8 +4,12 @@
#include "terminal.h" #include "terminal.h"
#define KEYBOARD_DATA_PORT 0x60 #define KEYBOARD_DATA_PORT 0x60
#define KEY_BUFFER_SIZE 256
static char key_buffer[256]; static char key_buffer[KEY_BUFFER_SIZE];
static uint8_t buffer_head = 0; // Write position (interrupt)
static uint8_t buffer_tail = 0; // Read position (get_char)
static uint8_t buffer_count = 0;
static uint8_t buffer_index = 0; static uint8_t buffer_index = 0;
// Basic US QWERTY keymap (scancode to ASCII) // Basic US QWERTY keymap (scancode to ASCII)
@@ -21,39 +25,41 @@ static const char scancode_map[128] = {
// Interrupt handler for IRQ1 // Interrupt handler for IRQ1
void keyboard_callback(void) { void keyboard_callback(void) {
uint8_t scancode = inb(0x60); uint8_t scancode = inb(KEYBOARD_DATA_PORT);
if (scancode & 0x80) return; // Ignore key release
// Only handle key press (ignore key release)
if (!(scancode & 0x80)) {
char c = scancode_map[scancode]; char c = scancode_map[scancode];
if (c && buffer_index < sizeof(key_buffer) - 1) { if (!c) return;
key_buffer[buffer_index++] = c;
uint8_t next_head = (buffer_head + 1) % KEY_BUFFER_SIZE;
// Drop key if buffer full
if (next_head == buffer_tail) return;
key_buffer[buffer_head] = c;
buffer_head = next_head;
buffer_count++;
terminal_putchar(c); terminal_putchar(c);
}
}
// Send End of Interrupt (EOI) to the PIC
outb(0x20, 0x20);
} }
void keyboard_init() { void keyboard_init() {
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21) register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
} }
// Blocking read (returns one char) // Blocking read (returns one char)
char keyboard_get_char() { char keyboard_get_char(void) {
while (buffer_index == 0); // Busy wait while (buffer_count == 0) {
__asm__ __volatile__("hlt"); // Better than busy loop
}
char c; char c;
__asm__ __volatile__("cli"); __asm__ __volatile__("cli");
c = key_buffer[0]; c = key_buffer[buffer_tail];
for (uint8_t i = 1; i < buffer_index; i++) { buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
key_buffer[i - 1] = key_buffer[i]; buffer_count--;
}
buffer_index--;
__asm__ __volatile__("sti"); __asm__ __volatile__("sti");
return c; return c;
} }

View File

@@ -12,6 +12,7 @@
#include "timer.h" #include "timer.h"
#include "utils.h" #include "utils.h"
#include "keyboard.h" #include "keyboard.h"
#include "irq.h"
#define LPT1 0x378 #define LPT1 0x378
@@ -41,6 +42,9 @@ void kmain(void) {
idt_init(); idt_init();
serial_write("IDT initialized.\n"); serial_write("IDT initialized.\n");
irq_install();
__asm__ __volatile__ ("sti");
terminal_write("Enabling paging...\n"); terminal_write("Enabling paging...\n");
paging_init(); paging_init();
serial_write("Paging initialized.\n"); serial_write("Paging initialized.\n");

View File

@@ -1,7 +1,7 @@
#include "kmalloc.h" #include "kmalloc.h"
#include "terminal.h" // Optional: for debug output #include "terminal.h" // Optional: for debug output
#define HEAP_END 0xC0100000 #define HEAP_END 0xC0500000
static uint32_t current_heap = 0; static uint32_t current_heap = 0;

138
kernel/memory.c Normal file
View File

@@ -0,0 +1,138 @@
#include "memory.h"
/* note: this is a stub, please use care as theres duplicate functions in utils implementation
/* --------------------------------------------------------------------- *
* Helper: copy a single byte (used by both memcpy and memmove)
* --------------------------------------------------------------------- */
static inline void byte_copy_forward(uint8_t *dst, const uint8_t *src, size_t n)
{
while (n--) *dst++ = *src++;
}
static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n)
{
dst += n; src += n;
while (n--) *--dst = *--src;
}
/* --------------------------------------------------------------------- *
* memcpy no overlap allowed (behaviour undefined if overlap)
* --------------------------------------------------------------------- */
void *memcpy(void *restrict dst, const void *restrict src, size_t n)
{
uint8_t *d = (uint8_t *)dst;
const uint8_t *s = (const uint8_t *)src;
#if defined(MEMORY_OPTIMIZED)
/* Align destination to 4-byte boundary */
size_t align = (uintptr_t)d & 3U;
if (align) {
size_t head = 4 - align;
if (head > n) head = n;
byte_copy_forward(d, s, head);
d += head; s += head; n -= head;
}
/* 32-bit word copy safe because we already aligned dst */
{
uint32_t *d32 = (uint32_t *)d;
const uint32_t *s32 = (const uint32_t *)s;
size_t words = n / 4;
while (words--) *d32++ = *s32++;
d = (uint8_t *)d32;
s = (const uint8_t *)s32;
n &= 3;
}
#endif
byte_copy_forward(d, s, n);
return dst;
}
/* --------------------------------------------------------------------- *
* memmove handles overlapping regions correctly
* --------------------------------------------------------------------- */
void *memmove(void *dst, const void *src, size_t n)
{
uint8_t *d = (uint8_t *)dst;
const uint8_t *s = (const uint8_t *)src;
if (n == 0 || dst == src)
return dst;
if (d < s) { /* copy forward */
#if defined(MEMORY_OPTIMIZED)
/* Same fast path as memcpy when no overlap */
size_t align = (uintptr_t)d & 3U;
if (align) {
size_t head = 4 - align;
if (head > n) head = n;
byte_copy_forward(d, s, head);
d += head; s += head; n -= head;
}
{
uint32_t *d32 = (uint32_t *)d;
const uint32_t *s32 = (const uint32_t *)s;
size_t words = n / 4;
while (words--) *d32++ = *s32++;
d = (uint8_t *)d32;
s = (const uint8_t *)s32;
n &= 3;
}
#endif
byte_copy_forward(d, s, n);
} else { /* copy backward */
byte_copy_backward(d, s, n);
}
return dst;
}
/* --------------------------------------------------------------------- *
* memcmp lexicographical compare
* --------------------------------------------------------------------- */
int memcmp(const void *s1, const void *s2, size_t n)
{
const uint8_t *a = (const uint8_t *)s1;
const uint8_t *b = (const uint8_t *)s2;
#if defined(MEMORY_OPTIMIZED)
/* Align to 4-byte boundary */
size_t align = (uintptr_t)a & 3U;
if (align && align == ((uintptr_t)b & 3U)) {
size_t head = 4 - align;
if (head > n) head = n;
while (head--) {
int diff = *a++ - *b++;
if (diff) return diff;
}
n -= head;
}
{
const uint32_t *a32 = (const uint32_t *)a;
const uint32_t *b32 = (const uint32_t *)b;
size_t words = n / 4;
while (words--) {
uint32_t va = *a32++, vb = *b32++;
if (va != vb) {
/* byte-wise fallback for the differing word */
const uint8_t *pa = (const uint8_t *)(a32 - 1);
const uint8_t *pb = (const uint8_t *)(b32 - 1);
for (int i = 0; i < 4; ++i) {
int diff = pa[i] - pb[i];
if (diff) return diff;
}
}
}
a = (const uint8_t *)a32;
b = (const uint8_t *)b32;
n &= 3;
}
#endif
while (n--) {
int diff = *a++ - *b++;
if (diff) return diff;
}
return 0;
}

25
kernel/memory.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef MEMORY_H
#define MEMORY_H
#include <stddef.h> /* size_t, NULL */
#include <stdint.h> /* uint8_t */
#ifdef __cplusplus
extern "C" {
#endif
/* C11 / POSIX-2004 signatures */
void *memcpy(void *restrict dst, const void *restrict src, size_t n);
void *memmove(void *dst, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
/* Optional fast-path using 32-bit loads (x86 only) */
#if defined(__i386__) && !defined(MEMORY_NO_OPT)
# define MEMORY_OPTIMIZED 1
#endif
#ifdef __cplusplus
}
#endif
#endif /* MEMORY_H */

View File

@@ -3,9 +3,9 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x100000; page_directory_entry_t *page_directory = (page_directory_entry_t *)0x200000;
page_table_entry_t *page_table = (page_table_entry_t *)0x101000; page_table_entry_t *page_table = (page_table_entry_t *)0x201000;
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x102000; // Located right after the page directory page_table_entry_t *heap_page_table = (page_table_entry_t *)0x202000;
// Helper function to set up the page directory entry // Helper function to set up the page directory entry
void set_page_directory(page_directory_entry_t *dir) { void set_page_directory(page_directory_entry_t *dir) {

View File

@@ -1,7 +1,12 @@
#include "scheduler.h" #include "scheduler.h"
#include <stddef.h> #include <stddef.h>
// Defined in context_switch.s
extern void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp);
static task_t tasks[MAX_TASKS]; static task_t tasks[MAX_TASKS];
// Stack memory area. Note: x86 Stacks grow DOWN from high to low addresses.
static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)]; static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)];
static int task_count = 0; static int task_count = 0;
@@ -9,7 +14,6 @@ static task_t *task_list = NULL;
static task_t *current_task = NULL; static task_t *current_task = NULL;
void scheduler_init() { void scheduler_init() {
// Initialize task list, etc.
task_list = NULL; task_list = NULL;
current_task = NULL; current_task = NULL;
task_count = 0; task_count = 0;
@@ -20,16 +24,42 @@ void scheduler_add_task(void (*entry)(void)) {
task_t *new_task = &tasks[task_count]; task_t *new_task = &tasks[task_count];
new_task->id = task_count; new_task->id = task_count;
new_task->entry = entry;
// Simulate a stack pointer pointing to the "top" of the stack // 1. Calculate the top of the stack (High Address)
new_task->stack_ptr = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t) - 1]; // We point to the very end of the array.
uint32_t *sp = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t)];
// 2. "Forge" the stack frame to look like ctx_switch saved it.
// We push values onto the stack by decrementing the pointer and writing.
// --- Return Address (EIP) ---
sp--;
*sp = (uint32_t)entry; // When ctx_switch does 'ret', it pops this and jumps to 'entry'
// --- EFLAGS ---
sp--;
*sp = 0x00000202; // Reserved bit set, Interrupts Enabled (IF=1). Important!
// --- General Purpose Registers (PUSHA/POPA layout) ---
// Order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
// We initialize them to 0 or meaningful values.
sp--; *sp = 0; // EAX
sp--; *sp = 0; // ECX
sp--; *sp = 0; // EDX
sp--; *sp = 0; // EBX
sp--; *sp = 0; // ESP (Ignored by POPA)
sp--; *sp = 0; // EBP
sp--; *sp = 0; // ESI
sp--; *sp = 0; // EDI
// Save this final stack location to the TCB
new_task->stack_ptr = sp;
new_task->next = NULL; new_task->next = NULL;
// Add to task list // 3. Add to linked list
if (task_list == NULL) { if (task_list == NULL) {
task_list = new_task; task_list = new_task;
current_task = new_task; // Make sure we have a current task to start
} else { } else {
task_t *tail = task_list; task_t *tail = task_list;
while (tail->next) { while (tail->next) {
@@ -42,21 +72,25 @@ void scheduler_add_task(void (*entry)(void)) {
} }
void scheduler_schedule() { void scheduler_schedule() {
// Very basic round-robin switch if (!current_task) return;
if (current_task && current_task->next) {
task_t *prev = current_task;
// Round-robin logic
if (current_task->next) {
current_task = current_task->next; current_task = current_task->next;
} else { } else {
current_task = task_list; // Loop back current_task = task_list;
} }
// Call context switch or simulate yielding to current_task // Perform the ACTUAL context switch
// In real system: context_switch_to(current_task) // We pass the address of the previous task's stack pointer storage
if (current_task && current_task->entry) { // and the value of the new task's stack pointer.
current_task->entry(); // Simulate switching by calling if (prev != current_task) {
ctx_switch(&prev->stack_ptr, current_task->stack_ptr);
} }
} }
void scheduler_yield() { void scheduler_yield() {
// Stub: manually call schedule for cooperative multitasking
scheduler_schedule(); scheduler_schedule();
} }

View File

@@ -4,18 +4,21 @@
#include <stdint.h> #include <stdint.h>
#define MAX_TASKS 8 #define MAX_TASKS 8
#define STACK_SIZE 1024 #define STACK_SIZE 1024 // in bytes
typedef struct task { typedef struct task {
uint32_t id; uint32_t id;
void (*entry)(void);
// The most important field:
// Where was the stack pointer when we last left this task?
uint32_t *stack_ptr; uint32_t *stack_ptr;
struct task *next; struct task *next;
} task_t; } task_t;
void scheduler_init(); void scheduler_init();
void scheduler_add_task(void (*entry)(void)); void scheduler_add_task(void (*entry)(void));
void scheduler_schedule(); void scheduler_schedule();
void scheduler_yield(); // Optional for cooperative scheduling void scheduler_yield();
#endif // SCHEDULER_H #endif // SCHEDULER_H

View File

@@ -12,6 +12,7 @@ static uint16_t* const vga_buffer = (uint16_t*) VGA_ADDRESS;
static uint8_t cursor_x = 0; static uint8_t cursor_x = 0;
static uint8_t cursor_y = 0; static uint8_t cursor_y = 0;
static uint8_t current_color = WHITE_ON_BLACK; static uint8_t current_color = WHITE_ON_BLACK;
static uint16_t last_cursor_pos = 0xFFFF;
void terminal_initialize(void) { void terminal_initialize(void) {
for (uint16_t y = 0; y < VGA_HEIGHT; y++) { for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
@@ -96,8 +97,10 @@ void terminal_clear(void) {
update_cursor(); update_cursor();
} }
void update_cursor() { void update_cursor(void) {
uint16_t pos = cursor_y * VGA_WIDTH + cursor_x; uint16_t pos = cursor_y * VGA_WIDTH + cursor_x;
if (pos == last_cursor_pos) return;
last_cursor_pos = pos;
outb(0x3D4, 0x0F); outb(0x3D4, 0x0F);
outb(0x3D5, (uint8_t)(pos & 0xFF)); outb(0x3D5, (uint8_t)(pos & 0xFF));

View File

@@ -77,16 +77,6 @@ char* utoa(unsigned int value, char* str, int base) {
return str; return str;
} }
int memcmp(const void *ptr1, const void *ptr2, size_t num) {
const uint8_t *p1 = ptr1, *p2 = ptr2;
for (size_t i = 0; i < num; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
void *memset(void *dest, int value, size_t len) { void *memset(void *dest, int value, size_t len) {
unsigned char *ptr = (unsigned char *)dest; unsigned char *ptr = (unsigned char *)dest;
while (len-- > 0) while (len-- > 0)

View File

@@ -9,7 +9,6 @@ char* itoa(int value, char* str, int base);
// Convert unsigned integer to string (base is typically 10, 16, etc.) // Convert unsigned integer to string (base is typically 10, 16, etc.)
char* utoa(unsigned int value, char* str, int base); char* utoa(unsigned int value, char* str, int base);
int memcmp(const void *ptr1, const void *ptr2, size_t num);
void *memset(void *dest, int value, size_t len); void *memset(void *dest, int value, size_t len);
#endif // UTILS_H #endif // UTILS_H