74 Commits

Author SHA1 Message Date
19f7c7b213 Merge pull request #93 from vmttmv/fix/91-header-cleanup
Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid()
2026-01-13 07:40:03 -08:00
bc9d84a93e Merge branch 'main' into fix/91-header-cleanup 2026-01-13 07:39:46 -08:00
9066ceaddb Merge pull request #94 from vmttmv/fix/92-paging
Fix PDE/PTE definitions, header cleanup
2026-01-13 07:29:27 -08:00
vmttmv
3b67e81ed0 Fix PDE/PTE definitions, header cleanup
- Fixes PDE/PTE definitions in kernel/paging.h
- removes memset declaration from kernel/utils.h, uses klibc string.h as
needed
2026-01-12 04:02:15 +02:00
vmttmv
841892398a Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid() 2026-01-12 02:43:37 +02:00
86608ef48c Merge pull request #90 from gbowne1/gbowne1-patch-2
Fix extern declaration for disk_read_sector function
2026-01-10 21:31:34 -08:00
785c8920d8 Merge pull request #86 from gbowne1/gbowne1-cpuidfix-1
IImplement CPUID support check and CPU info printing
2026-01-09 11:59:52 -08:00
c0e7ab6be0 Fix k_memcmp return logic and add disk_read_sector
Refactor k_memcmp to return correct difference and add disk_read_sector function.
2026-01-08 21:10:35 -08:00
f78bc27f35 Fix extern declaration for disk_read_sector function 2026-01-08 21:03:10 -08:00
507b4f5511 Merge pull request #89 from vmttmv/fix/bl-bounds
Establish well-defined read buffers for bl, implement error printing
2026-01-07 21:00:00 -08:00
Borna Šoštarić
12046ce96b fix vga clear section in on_error 2026-01-08 05:20:42 +01:00
vmttmv
a9b8ac7066 Establish well-defined read buffers for bl, implement error printing 2026-01-07 02:33:46 +02:00
d6ab8c91f8 Merge pull request #87 from vmttmv/fix/bl-nonaligned-reads
Fix non-aligned disk reads in bootloader
2026-01-05 17:45:37 -08:00
vmttmv
35ebd5fd72 Fix non-aligned disk reads in bootloader 2026-01-06 00:44:55 +02:00
10d3761be1 Enhance cpu.h with Intel model definitions and struct
Added Intel model definitions and CPU info structure.
2026-01-05 00:46:14 -08:00
cc2e967a4d Implement CPUID support check and CPU info printing
Added functions to check CPUID support and print CPU details.
2026-01-05 00:42:05 -08:00
9eae2e1005 Merge pull request #84 from shoshta73/RWXPerms
[fix] LOAD segment with RWX permissions
2025-12-30 17:19:16 -08:00
Borna Šoštarić
bd4236ad9b fix RWX perms warnings in link step 2025-12-31 01:39:18 +01:00
5292808934 Merge pull request #83 from vmttmv/main
Fix BL disk read status polls
2025-12-30 16:13:42 -08:00
vmttmv
09c48c2f50 fix stage2.asm: disk reads wait for BSY 2025-12-30 05:08:54 +02:00
caea475daf Merge pull request #79 from shoshta73/klibc
Initial implementation of klibc
2025-12-27 11:14:45 -08:00
Borna Šoštarić
f30be3ddd5 initial implementation of klibc
fix linker error about ctx_switch
2025-12-27 19:44:54 +01:00
d83e247bbd Merge pull request #76 from shoshta73/configure-script
Configure script
2025-12-19 15:22:02 -08:00
Borna Šoštarić
a1a6fd2aa9 lessen indirection in the makefile 2025-12-19 23:46:03 +01:00
Borna Šoštarić
66f9056406 update readme 2025-12-19 23:44:00 +01:00
Borna Šoštarić
45acbb5c04 generate .build.env as part of configure script 2025-12-19 23:41:32 +01:00
Borna Šoštarić
649a227e41 add configure script for setting up cross compilation tools 2025-12-19 22:47:34 +01: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
9aa1b85ca0 Merge pull request #62 from vmttmv/main
begin fixing build errors for stage2
2025-10-25 17:42:46 -07:00
vmttmv
9216673b18 begin fixing build errors 2025-10-26 03:03:20 +03:00
ed07e2cd9c Delete kernel/linker.ld
Removing linker.ld for the grub legacy stuff I was gonna try
2025-10-25 15:10:55 -07:00
a4318d3c79 Delete kernel/multiboot.h
Remove empty multiboot.h for grub legacy
2025-10-25 15:09:32 -07:00
9cde2e708d Merge pull request #61 from vmttmv/main
stage1: fix load addresses for stage2/kernel
2025-10-25 14:53:23 -07:00
vmttmv
c22f6b6f14 stage1: fix load addresses for stage2/kernel 2025-10-26 00:18:45 +03:00
6267863939 Merge pull request #60 from vmttmv/main
build: debug symbols for stage1
2025-10-25 11:42:52 -07:00
vmttmv
49114214cb build: debug symbols for stage1 2025-10-25 21:26:52 +03:00
e58abdae1c Merge pull request #59 from vmttmv/main
Makefile target organization
2025-10-25 10:28:29 -07:00
vmttmv
dd37ba8ed6 make: explicit build dir, separate stage1 target so it can be called easier 2025-10-25 19:52:39 +03:00
dd68fd805f fixing bootloader again but should work using nasm now 2025-08-06 01:42:17 -07:00
16309bc306 added checksum to boot1 2025-08-03 10:27:25 -07:00
4a189f482f fixed bootloader by adding lba conversion from chs 2025-08-02 21:48:51 -07:00
267130281a fixing the keyboard and bootloader so that its 2 stage again 2025-08-02 20:06:15 -07:00
e1e30b511a mostly improvements to malloc 2025-07-01 11:20:04 -07:00
109e554524 fixing the remaining issues in the kernel directory 2025-06-16 15:13:37 -07:00
69762b6650 adding stub usb and mouse code 2025-05-18 02:49:17 -07:00
49361a98be fixing minor bugs with single unit compilation in gcc with flags on 2025-05-16 01:08:12 -07:00
50efcc13fe minor additions to the kernel heap and adding acpi 2025-05-15 04:22:55 -07:00
a9f2826014 addind more important kernel files and also fixing bugs 2025-05-15 02:37:06 -07:00
83 changed files with 3387 additions and 265 deletions

4
.gitignore vendored
View File

@@ -1 +1,5 @@
.build.env
build build
cross
.cache/
compile_commands.json

View File

@@ -1,6 +1,7 @@
{ {
"files.associations": { "files.associations": {
".fantomasignore": "ignore", ".fantomasignore": "ignore",
"stddef.h": "c" "stddef.h": "c",
"io.h": "c"
} }
} }

View File

@@ -1,35 +1,73 @@
AS = nasm AS = nasm
CC = gcc ASFLAGS = -f elf32 -g -F dwarf
LD = ld CC = i386-elf-gcc
QEMU = qemu-system-i386 LD = i386-elf-ld
IMG_SIZE = 1440k QEMU= qemu-system-i386
OBJCOPY = i386-elf-objcopy
BOOT_SRC = bootloader/boot.asm BUILD_DIR = build
BOOT_BIN = build/boot.bin CROSS_DIR = cross
BOOT_IMG = build/boot.img DISK_IMG = $(BUILD_DIR)/disk.img
KERNEL_SRC = kernel/kmain.c STAGE2_SIZE = 2048
KERNEL_BIN = build/kernel.bin
DISK_IMG = build/disk.img
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_IMG) KERNEL_C_SRC = $(wildcard kernel/*.c)
KERNEL_ASM_SRC = $(wildcard kernel/*.asm)
KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC))
KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC))
$(BOOT_BIN): $(BOOT_SRC) KLIBC_SRC = $(wildcard klibc/src/*.c)
$(AS) -f bin -o $@ $< KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC))
$(BOOT_IMG): $(BOOT_BIN) all: $(DISK_IMG)
cp $(BOOT_BIN) $@
truncate -s $(IMG_SIZE) $@
$(KERNEL_BIN): $(KERNEL_SRC) .PHONY: stage1 stage2 kernel run gdb clean
$(CC) -ffreestanding -c $< -o build/kernel.o stage1: $(BUILD_DIR)
$(LD) -T bootloader/linker.ld -o $@ build/kernel.o $(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
$(DISK_IMG): $(BOOT_IMG) $(KERNEL_BIN) # NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow.
dd if=$(BOOT_IMG) of=$@ bs=512 seek=4 # Alternatively, convey the final stage2 size through other means to stage1.
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200 stage2: $(BUILD_DIR)
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -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
run: $(DISK_IMG) $(BUILD_DIR)/asm_%.o: kernel/%.asm
$(QEMU) -drive file=$<,format=raw,if=floppy $(AS) $(ASFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: kernel/%.c
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
$(BUILD_DIR)/klibc/%.o: klibc/src/%.c
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
kernel: $(KERNEL_OBJ) | $(BUILD_DIR) $(KLIBC_OBJ)
$(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) $(KLIBC_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):
mkdir -p $@
mkdir -p $(BUILD_DIR)/klibc
run:
qemu-system-i386 -s -S $(DISK_IMG)
gdb:
gdb -x gdb.txt
clean: clean:
rm -rf build rm -rf $(BUILD_DIR)
clean-cross:
rm -rf $(CROSS_DIR)
rm -rf .build.env
clean-all: clean clean-cross

View File

@@ -35,6 +35,7 @@ Youll need the following tools installed:
- `qemu-system-i386` - `qemu-system-i386`
Optional: Optional:
- `gdb` - `gdb`
- `vncviewer` (TigerVNC or similar) - `vncviewer` (TigerVNC or similar)
@@ -42,10 +43,27 @@ Optional:
## 🛠️ Building ClassicOS ## 🛠️ Building ClassicOS
Clone and build: Clone repository:
```bash ```sh
git clone https://github.com/gbowne1/ClassicOS.git git clone https://github.com/gbowne1/ClassicOS.git
cd ClassicOS cd ClassicOS
```
Run `configure` script to build a cross-compiler toolchain for `i386-elf`:
```sh
./configure
```
Source the `.build.env` file to add the cross-compiler toolchain to your PATH:
```sh
source .build.env
```
Build the kernel:
```sh
make make
``` ```

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,67 +0,0 @@
[BITS 16]
[ORG 0x7C00]
start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
call enable_a20
call setup_gdt
call switch_to_pm
; ----------------------
; A20 Gate Enable (Fast method)
enable_a20:
in al, 0x92
or al, 0x02
out 0x92, al
ret
; ----------------------
; Set up a minimal GDT
gdt_start:
dq 0x0000000000000000 ; null descriptor
dq 0x00CF9A000000FFFF ; code segment descriptor
dq 0x00CF92000000FFFF ; data segment descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size of GDT
dd gdt_start ; address of GDT
gdt_end:
setup_gdt:
lgdt [gdt_descriptor]
ret
; ----------------------
; Switch to Protected Mode
switch_to_pm:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:protected_mode_entry
; ----------------------
; 32-bit Protected Mode Entry Point
[BITS 32]
protected_mode_entry:
mov ax, 0x10 ; data segment selector
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x90000
; Kernel is assumed to be loaded at 0x100000
jmp 0x08:0x100000
; ----------------------
; Boot signature
times 510 - ($ - $$) db 0
dw 0xAA55

Binary file not shown.

292
bootloader/stage1.asm Normal file
View File

@@ -0,0 +1,292 @@
; ==============================================================================
; boot.asm - First Stage Bootloader (CHS Based)
; ==============================================================================
; Params for stage2
%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
[BITS 16]
global _start
_start:
cli ; Disable interrupts
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
; Setup stack safely below EBDA area (choose 0x0000:0x7A00)
xor ax, ax ; AX = 0
mov ss, ax ; Stack segment = 0x0000
mov sp, 0x7A00 ; Stack offset = 0x7A00
; Initialize DS, ES for zero-based segments
xor ax, ax
mov ds, ax
mov es, ax
; Query bios for disk parameters
call get_disk_params
; Load second-stage bootloader (boot1.asm) to 0x7E00
mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
call lba_to_chs
mov al, s2_nsect ; Number of sectors to read
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
call read_chs
; Enable A20 line
call enable_a20
jc a20_error ; Jump if A20 enable fails
; Setup Global Descriptor Table
call setup_gdt
; Switch to protected mode and jump to second stage at 0x08:0x7E00
call switch_to_pm
disk_error:
mov si, disk_error_msg
call print_string_16
jmp halt
a20_error:
mov si, a20_error_msg
call print_string_16
jmp halt
; ----------------------------------------------------------------
; Verify Checksum Routine
; Uses SI = start address, CX = byte count
; Simple XOR checksum over bytes, expects result 0
verify_checksum:
push ax
push bx
push di
mov di, si
xor al, al
xor bx, bx
.verify_loop:
lodsb
xor bl, al
loop .verify_loop
test bl, bl
jz .checksum_ok
stc ; Set carry on checksum error
jmp .done
.checksum_ok:
clc ; Clear carry on success
.done:
pop di
pop bx
pop ax
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
; AL = number of sectors
; CL = starting sector (1-based)
; Inputs:
; AL = sector count
; CH = cylinder
; DH = head
; CL = sector (163, with top 2 bits as high cylinder bits)
; ----------------------------------------------------------------
; Convert LBA to CHS
; Inputs:
; AX = LBA sector number (0-based)
; Outputs:
; CH = cylinder
; DH = head
; CL = sector (1-63, top 2 bits are upper cylinder bits)
lba_to_chs:
; Sector
xor dx, dx
mov bx, ax
div word [sectors_per_track] ; divide lba with max sectors
add dl, 1 ; take the remainder, sectors start at 1
mov cl, dl ; sector is in cl
; Head
mov ax, bx
mov dx, 0
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
; Cylinder
mov ch, al ; take the quotient, cylinder is in ch
ret
read_chs:
pusha
push dx
.retry:
mov ah, 0x02 ; BIOS: Read sectors
mov dl, [bootdev] ; Boot device
; Assume CH, DH, CL already set before this call
int 0x13
jc .error
pop dx
popa
ret
.error:
dec cx
jz disk_error
xor ah, ah
int 0x13
jmp .retry
; ----------------------------------------------------------------
enable_a20:
; Try fast A20 gate method
in al, 0x92
or al, 0x02
and al, 0xFE ; Clear bit 0 to avoid fast A20 bugs
out 0x92, al
; Verify A20
call check_a20
jnc .done ; Success
; Fallback: use keyboard controller method
call .fallback
.done:
ret
.fallback:
mov al, 0xAD ; Disable keyboard
out 0x64, al
call .wait_input_clear
mov al, 0xD0 ; Command: read output port
out 0x64, al
call .wait_output_full
in al, 0x60
or al, 0x02 ; Set A20 enable bit
mov bl, al
call .wait_input_clear
mov al, 0xD1 ; Command: write output port
out 0x64, al
call .wait_input_clear
mov al, bl
out 0x60, al
call .wait_input_clear
mov al, 0xAE ; Enable keyboard
out 0x64, al
ret
.wait_input_clear:
in al, 0x64
test al, 0x02
jnz .wait_input_clear
ret
.wait_output_full:
in al, 0x64
test al, 0x01
jz .wait_output_full
ret
check_a20:
in al, 0x64 ; Read keyboard controller status
test al, 0x02 ; Check if input buffer is full
jnz check_a20 ; Wait until empty
mov al, 0xD0 ; Command: read output port
out 0x64, al
.wait_output:
in al, 0x60 ; Read output port value
test al, 0x02 ; Check A20 gate bit (bit 1)
jnz .a20_enabled ; If set, A20 enabled
xor al, al ; Clear carry to indicate failure
stc ; Set carry for failure, jc will jump
ret
.a20_enabled:
clc ; Clear carry flag to indicate success
ret
; ----------------------------------------------------------------
gdt_start:
dq 0x0000000000000000 ; Null descriptor
dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08)
dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10)
dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18)
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
gdt_end:
setup_gdt:
lgdt [gdt_descriptor]
ret
; ----------------------------------------------------------------
switch_to_pm:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:0x7E00 ; jump to S2
; ----------------------------------------------------------------
print_string_16:
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp .loop
.done:
ret
disk_error_msg db "Disk error!", 0
a20_error_msg db "A20 error!", 0
halt:
cli
hlt
bootdev db 0
sectors_per_track dw 0
heads_per_cylinder dw 0
times 510 - ($ - $$) db 0
dw 0xAA55

26
bootloader/stage2.asm Normal file
View File

@@ -0,0 +1,26 @@
[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

View File

@@ -1,15 +1,10 @@
ENTRY(start)
SECTIONS { SECTIONS {
. = 1M; . = 0x7e00;
.text : {
*(.multiboot)
*(.text*)
}
.text : { *(.text*) }
.rodata : { *(.rodata*) } .rodata : { *(.rodata*) }
.data : { *(.data*) } .data : { *(.data*) }
.bss : { .bss : {
*(.bss*) *(.bss*)
*(COMMON) *(COMMON)

235
bootloader/stage2_load.c Normal file
View File

@@ -0,0 +1,235 @@
#include <stddef.h>
#include <stdint.h>
// ATA IO Ports
#define ATA_PRIMARY_DATA 0x1F0
#define ATA_PRIMARY_ERR_FEATURES 0x1F1
#define ATA_PRIMARY_SEC_COUNT 0x1F2
#define ATA_PRIMARY_LBA_LOW 0x1F3
#define ATA_PRIMARY_LBA_MID 0x1F4
#define ATA_PRIMARY_LBA_HIGH 0x1F5
#define ATA_PRIMARY_DRIVE_SEL 0x1F6
#define ATA_PRIMARY_COMM_STAT 0x1F7
// ATA Commands
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_WRITE_PIO 0x30
// ELF Ident indexes
#define EI_NIDENT 16
// Program header types
#define PT_NULL 0
#define PT_LOAD 1
// Disk sector size
#define SECTOR_SIZE 512
#define PH_PER_SECTOR (SECTOR_SIZE / sizeof(Elf32_Phdr))
// Kernel start LBA
#define KERN_START_SECT 5
// VGA
// Expects bios initialization for text mode (3), buffer at 0xb8000
#define VGA_ADDRESS 0xB8000
#define VGA_COLS 80
#define VGA_ROWS 25
// 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;
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
__asm__ volatile ("inb %1, %0"
: "=a"(ret)
: "Nd"(port));
return ret;
}
static inline void outb(uint16_t port, uint8_t val)
{
__asm__ volatile ("outb %0, %1"
:
: "a"(val), "Nd"(port));
}
static inline uint16_t inw(uint16_t port)
{
uint16_t ret;
__asm__ volatile ("inw %1, %0"
: "=a"(ret)
: "Nd"(port));
return ret;
}
static inline void ata_wait_bsy() {
while (inb(ATA_PRIMARY_COMM_STAT) & 0x80);
}
static inline void ata_wait_drq() {
while (!(inb(ATA_PRIMARY_COMM_STAT) & 0x08));
}
static void ata_read_sector(void *addr, uint32_t lba) {
ata_wait_bsy();
outb(ATA_PRIMARY_DRIVE_SEL, 0xE0 | ((lba >> 24) & 0x0F));
outb(ATA_PRIMARY_SEC_COUNT, 1);
outb(ATA_PRIMARY_LBA_LOW, (uint8_t)lba);
outb(ATA_PRIMARY_LBA_MID, (uint8_t)(lba >> 8));
outb(ATA_PRIMARY_LBA_HIGH, (uint8_t)(lba >> 16));
outb(ATA_PRIMARY_COMM_STAT, ATA_CMD_READ_PIO);
uint16_t* ptr = (uint16_t*)addr;
ata_wait_bsy();
ata_wait_drq();
for (int i = 0; i < 256; i++) {
*ptr++ = inw(ATA_PRIMARY_DATA);
}
}
static void ata_read_sectors(uint8_t *addr, uint32_t offset, uint32_t size)
{
// Reads are offset from the starting sector of the kernel
uint32_t lba = KERN_START_SECT + offset / SECTOR_SIZE;
uint32_t off = offset % 512;
uint8_t data[512];
while (size > 0) {
ata_read_sector(data, lba);
uint32_t copy = 512 - off;
if (copy > size) {
copy = size;
}
for (uint32_t i = 0; i < copy; i++) {
addr[i] = data[off + i];
}
addr += copy;
size -= copy;
lba++;
off = 0;
}
}
static void on_error(const char *msg)
{
uint16_t *ptr = (uint16_t *)VGA_ADDRESS;
// Clear
uint16_t val = 0x0f00 | (uint8_t)' ';
for (size_t i = 0; i < VGA_COLS * VGA_ROWS; i++) {
ptr[i] = val;
}
// Print error
for (size_t i = 0; msg[i]; i++) {
ptr[i] = 0xf00 | (uint8_t)msg[i];
}
// Halt
while (1) {
__asm__("hlt");
}
}
// Load an ELF executable into memory.
// NOTE: Only 32-byte program headers are supported.
// Returns the entry point to the program.
static void *elf_load(const void *data) {
const Elf32_Ehdr *header = (const Elf32_Ehdr*)data;
if (header->e_phentsize != sizeof(Elf32_Phdr)) {
// The bootloader only handles 32-byte program header entries
on_error("ERROR: Unsupported program header entry size, halting...");
}
// Buffer for the program headers
uint8_t file_buf[SECTOR_SIZE];
// Current file offset to the next program header
uint32_t file_offset = header->e_phoff;
for (int i = 0; i < header->e_phnum; i++) {
// Check for sector boundary.
// Program headers are read in a sector at a time
// 512 / 32 = 16 PH per sector
if (i % PH_PER_SECTOR == 0) {
uint32_t count = (header->e_phnum - i) * sizeof(Elf32_Phdr);
if (count > SECTOR_SIZE) {
count = SECTOR_SIZE;
}
// Reads
ata_read_sectors(file_buf, file_offset, count);
file_offset += count;
}
// PH being processed currently, index mod 16 as headers
// are being loaded in sector by sector.
const Elf32_Phdr *ph = (const Elf32_Phdr *)file_buf + (i % PH_PER_SECTOR);
// Discard non-load segments
if (ph->p_type != PT_LOAD)
continue;
// Load in the segment
uint32_t offset = ph->p_offset;
uint32_t filesz = ph->p_filesz;
uint32_t memsz = ph->p_memsz;
uint8_t *vaddr = (uint8_t *)ph->p_vaddr;
ata_read_sectors(vaddr, offset, filesz);
// Zero remaining BSS (if any)
if (memsz > filesz) {
uint8_t* bss_start = vaddr + filesz;
for (uint32_t j = 0; j < memsz - filesz; j++) {
bss_start[j] = 0;
}
}
}
// Return the entry point
return (void *)header->e_entry;
}
void *load_kernel(void) {
// ELF header buffer
uint8_t header_buf[SECTOR_SIZE];
// Read the first sector (contains the ELF header)
ata_read_sector(header_buf, KERN_START_SECT);
// `elf_load()` returns the entry point
return elf_load(header_buf);
}

169
configure vendored Executable file
View File

@@ -0,0 +1,169 @@
#!/usr/bin/env bash
set -euo pipefail
# Configuration
TARGET="i386-elf"
BINUTILS_VERSION="2.45"
GCC_VERSION="15.2.0"
# Paths
SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")"
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
PREFIX="$SCRIPT_DIR/cross"
SRC_DIR="$PREFIX/src"
BINUTILS_SRC="$SRC_DIR/binutils-$BINUTILS_VERSION"
BINUTILS_BUILD="$PREFIX/build-binutils"
GCC_SRC="$SRC_DIR/gcc-$GCC_VERSION"
GCC_BUILD="$PREFIX/build-gcc"
# Flags
DEBUG=0
HELP=0
# Parse arguments
for arg in "$@"; do
case "$arg" in
-h|--help)
HELP=1
;;
-d|--debug)
DEBUG=1
;;
*)
echo "Unknown option: $arg"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
# Show help
if [[ "$HELP" -eq 1 ]]; then
cat << EOF
Usage: $0 [OPTIONS]
Build a cross-compiler toolchain for $TARGET.
OPTIONS:
-h, --help Show this help message
-d, --debug Enable debug mode (set -x)
This script will:
1. Download binutils $BINUTILS_VERSION and GCC $GCC_VERSION
2. Build and install them to: $PREFIX
EOF
exit 0
fi
# Enable debug mode
if [[ "$DEBUG" -eq 1 ]]; then
set -x
fi
# Print configuration
cat << EOF
=== Build Configuration ===
Target : $TARGET
Prefix : $PREFIX
Binutils : $BINUTILS_VERSION
GCC : $GCC_VERSION
===========================
EOF
# Create directory structure
echo "Setting up directories..."
mkdir -p "$SRC_DIR"
# Download sources
cd "$SRC_DIR"
if [[ ! -d "$BINUTILS_SRC" ]]; then
echo "Downloading binutils $BINUTILS_VERSION..."
wget "https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.gz"
echo "Extracting binutils..."
tar xf "binutils-$BINUTILS_VERSION.tar.gz"
rm "binutils-$BINUTILS_VERSION.tar.gz"
else
echo "Binutils source already exists, skipping download"
fi
if [[ ! -d "$GCC_SRC" ]]; then
echo "Downloading GCC $GCC_VERSION..."
wget "https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz"
echo "Extracting GCC..."
tar xf "gcc-$GCC_VERSION.tar.gz"
rm "gcc-$GCC_VERSION.tar.gz"
else
echo "GCC source already exists, skipping download"
fi
# Download GCC prerequisites
if [[ ! -d "$GCC_SRC/gmp" ]]; then
echo "Downloading GCC prerequisites..."
cd "$GCC_SRC"
./contrib/download_prerequisites
cd "$SRC_DIR"
else
echo "GCC prerequisites already downloaded, skipping"
fi
# Build binutils
if [[ ! -f "$PREFIX/bin/$TARGET-ld" ]]; then
echo "Building binutils..."
mkdir -p "$BINUTILS_BUILD"
cd "$BINUTILS_BUILD"
"$BINUTILS_SRC/configure" \
--target="$TARGET" \
--prefix="$PREFIX" \
--with-sysroot \
--disable-nls \
--disable-werror
make -j"$(nproc)"
make install
else
echo "Binutils already installed, skipping build"
fi
# Build GCC
if [[ ! -f "$PREFIX/bin/$TARGET-gcc" ]]; then
echo "Building GCC..."
mkdir -p "$GCC_BUILD"
cd "$GCC_BUILD"
"$GCC_SRC/configure" \
--target="$TARGET" \
--prefix="$PREFIX" \
--disable-nls \
--enable-languages=c \
--without-headers
make all-gcc -j"$(nproc)"
make all-target-libgcc -j"$(nproc)"
make install-gcc
make install-target-libgcc
else
echo "GCC already installed, skipping build"
fi
cd "$SCRIPT_DIR"
# Generate .build.env file
cat > .build.env << EOF
# Generated by configure on $(date)
# Source this file to add the cross-compiler toolchain to your PATH
export PATH="$PREFIX/bin:\$PATH"
EOF
echo ""
echo "=== Build Complete ==="
echo "Toolchain installed to: $PREFIX"
echo ""
echo "To use the toolchain, run:"
echo " source .build.env"
echo "======================"

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

54
kernel/acpi.c Normal file
View File

@@ -0,0 +1,54 @@
#include "acpi.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
// Function to find the RSDP (Root System Description Pointer)
acpi_rsdp_t* acpi_find_rsdp() {
// Search memory from 0x000E0000 to 0x00100000 (BIOS)
for (uint32_t addr = 0x000E0000; addr < 0x00100000; addr += 16) {
acpi_rsdp_t* rsdp = (acpi_rsdp_t*)addr;
if (memcmp(rsdp->signature, "RSD PTR ", 8) == 0) {
uint8_t checksum = 0;
for (size_t i = 0; i < sizeof(acpi_rsdp_t); i++) { // Change int to size_t
checksum += ((uint8_t*)rsdp)[i];
}
if (checksum == 0) {
return rsdp; // Valid RSDP found
}
}
}
return NULL; // RSDP not found
}
// Function to get the RSDT or XSDT based on the RSDP revision
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp) {
if (rsdp->revision >= 2) {
return (void*)rsdp->xsdt_addr; // ACPI 2.0+ uses XSDT
} else {
return (void*)rsdp->rsdt_addr; // ACPI 1.0 uses RSDT
}
}
// Function to find the FADT table within the RSDT or XSDT
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt) {
acpi_rsdt_t* rsdt = (acpi_rsdt_t*)rsdt_or_xsdt;
uint32_t num_tables = (rsdt->length - sizeof(acpi_rsdt_t)) / sizeof(uint32_t);
for (size_t i = 0; i < num_tables; i++) {
uint32_t table_addr = rsdt->tables[i];
acpi_fadt_t* fadt = (acpi_fadt_t*)table_addr;
if (fadt->signature == 0x50434146) {
uint8_t checksum = 0;
for (size_t j = 0; j < fadt->length; j++) {
checksum += ((uint8_t*)fadt)[j];
}
if (checksum == 0) {
return fadt;
}
}
}
return NULL;
}

47
kernel/acpi.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef ACPI_H
#define ACPI_H
#include <stdint.h>
// ACPI base address (replace with actual value)
#define ACPI_BASE 0xE0000000
#ifndef NULL
#define NULL ((void*)0)
#endif
// ACPI RSDP Structure (Root System Description Pointer)
typedef struct {
uint8_t signature[8]; // Should be "RSD PTR "
uint8_t checksum; // Checksum for the RSDP structure
uint8_t oem_id[6]; // OEM ID string
uint8_t revision; // ACPI revision
uint32_t rsdt_addr; // 32-bit RSDT address (ACPI 1.0)
uint32_t xsdt_addr; // 64-bit XSDT address (ACPI 2.0+)
} __attribute__((packed)) acpi_rsdp_t;
// ACPI RSDT Structure (Root System Description Table)
typedef struct {
uint32_t signature; // Should be "RSDT"
uint32_t length; // Length of the table
uint8_t revision; // Revision of the RSDT table
uint8_t checksum; // Checksum for the RSDT table
uint32_t tables[]; // Array of pointers to other tables (RSDT/XSDT entries)
} __attribute__((packed)) acpi_rsdt_t;
// ACPI FADT Structure (Fixed ACPI Description Table)
typedef struct {
uint32_t signature; // Should be "FACP"
uint32_t length; // Length of the table
uint8_t revision; // Revision of the FADT table
uint8_t checksum; // Checksum for the FADT table
uint32_t pm_tmr_address; // Power Management Timer Address
// ... other FADT fields
} __attribute__((packed)) acpi_fadt_t;
// Function prototypes
acpi_rsdp_t* acpi_find_rsdp();
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp);
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt);
#endif /* ACPI_H */

25
kernel/context_switch.asm 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

107
kernel/cpu.c Normal file
View File

@@ -0,0 +1,107 @@
#include "cpu.h"
#include "serial.h"
#include "terminal.h"
#include "utils.h"
void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
__asm__(
"cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(leaf)
);
}
// Helper to print a labeled decimal value
void print_val(const char* label, uint32_t val) {
char buf[12];
utoa(val, buf, 10);
terminal_write(label);
terminal_write(buf);
terminal_write(" ");
}
// Safely check if CPUID is supported by attempting to flip bit 21 of EFLAGS
int check_cpuid_supported() {
uint32_t f1, f2;
__asm__ volatile (
"pushfl\n\t"
"pushfl\n\t"
"popl %0\n\t"
"movl %0, %1\n\t"
"xorl $0x200000, %0\n\t"
"pushl %0\n\t"
"popfl\n\t"
"pushfl\n\t"
"popl %0\n\t"
"popfl\n\t"
: "=&r" (f1), "=&r" (f2));
return ((f1 ^ f2) & 0x200000) != 0;
}
void identify_cpu() {
if (!check_cpuid_supported()) {
terminal_write("CPUID not supported. Likely a 386 or early 486.\n");
return;
}
uint32_t eax, ebx, ecx, edx;
char vendor[13];
// Leaf 0: Vendor String & Max Leaf
cpuid(0, &eax, &ebx, &ecx, &edx);
uint32_t max_leaf = eax;
*(uint32_t *)&vendor[0] = ebx;
*(uint32_t *)&vendor[4] = edx;
*(uint32_t *)&vendor[8] = ecx;
vendor[12] = '\0';
terminal_write("Vendor: ");
terminal_write(vendor);
terminal_write("\n");
// Leaf 1: Family, Model, Stepping
if (max_leaf >= 1) {
cpuid(1, &eax, &ebx, &ecx, &edx);
uint32_t stepping = eax & 0xF;
uint32_t model = (eax >> 4) & 0xF;
uint32_t family = (eax >> 8) & 0xF;
uint32_t type = (eax >> 12) & 0x3;
// Handle Extended Family/Model (Required for Pentium 4 and newer)
if (family == 0xF) {
family += (eax >> 20) & 0xFF;
model += ((eax >> 16) & 0xF) << 4;
}
print_val("Family:", family);
print_val("Model:", model);
print_val("Step:", stepping);
terminal_write("\n");
}
// Leaf 2: Cache Descriptors
if (max_leaf >= 2) {
cpuid(2, &eax, &ebx, &ecx, &edx);
terminal_write("Cache Descriptors: ");
// Note: Leaf 2 returns a list of 1-byte descriptors in the registers.
// We look for common Intel ones:
uint32_t regs[4] = {eax, ebx, ecx, edx};
for (int i = 0; i < 4; i++) {
if (regs[i] & 0x80000000) continue; // Reserved bit
for (int j = 0; j < 4; j++) {
uint8_t desc = (regs[i] >> (j * 8)) & 0xFF;
if (desc == 0) continue;
// Example decoding for specific chips you mentioned:
if (desc == 0x06) terminal_write("8KB L1 I-Cache ");
if (desc == 0x0A) terminal_write("8KB L1 D-Cache ");
if (desc == 0x41) terminal_write("128KB L2 ");
if (desc == 0x43) terminal_write("512KB L2 ");
if (desc == 0x2C) terminal_write("32KB L1 D-Cache ");
}
}
terminal_write("\n");
}
}

43
kernel/cpu.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef CPU_H
#define CPU_H
#include <stdint.h>
#include <stdbool.h>
// Specific Intel Model Definitions for your targets
#define INTEL_FAM4_486_DX 0x00 // Also 0x01
#define INTEL_FAM4_486_SX 0x02
#define INTEL_FAM4_486_DX2 0x03
#define INTEL_FAM4_486_DX4 0x08
#define INTEL_FAM5_PENTIUM 0x01 // P5
#define INTEL_FAM5_PENTIUM_MMX 0x04 // P55C
#define INTEL_FAM6_PENTIUM_PRO 0x01 // P6
#define INTEL_FAM6_PENTIUM_II 0x05 // Deschutes
#define INTEL_FAM6_PENTIUM_III 0x07 // Katmai/Coppermine
#define INTEL_FAM15_P4_WILLY 0x00 // Willamette
#define INTEL_FAM15_P4_NORTH 0x02 // Northwood
#define INTEL_FAM15_P4_PRES 0x03 // Prescott
typedef struct {
char vendor[13];
uint32_t family;
uint32_t model;
uint32_t stepping;
uint32_t type;
uint32_t max_leaf;
// Feature flags (optional, but very helpful later)
bool has_fpu;
bool has_mmx;
bool has_sse;
} cpu_info_t;
// Function Prototypes
void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
bool cpu_check_cpuid_support(void);
void identify_cpu(void);
// Helper to get the current CPU info after identification
cpu_info_t* cpu_get_info(void);
#endif // CPU_H

View File

@@ -1,5 +1,45 @@
#include "debug.h" #include "debug.h"
#include "vga.h"
#include <stdint.h>
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
#define VGA_MEMORY 0xB8000
// VGA text mode color attributes
#define COLOR_WHITE 0x07
// Pointer to the VGA memory
volatile uint16_t* vga_buffer = (uint16_t*)VGA_MEMORY;
// Function to print a string to the VGA text buffer
void debug_print(const char *str) { void debug_print(const char *str) {
// Implementation for printing debug messages while (*str) {
if (*str == '\n') {
// Handle new line
// Move to the next line (not implemented here)
// You can implement line wrapping if needed
str++;
continue;
}
// Calculate the position in the VGA buffer
static int cursor_x = 0;
static int cursor_y = 0;
// Write the character and its attribute to the VGA buffer
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = (COLOR_WHITE << 8) | *str;
// Move the cursor to the right
cursor_x++;
if (cursor_x >= VGA_WIDTH) {
cursor_x = 0;
cursor_y++;
if (cursor_y >= VGA_HEIGHT) {
cursor_y = 0; // Scroll up (not implemented here)
}
}
str++;
}
} }

36
kernel/display.c Normal file
View File

@@ -0,0 +1,36 @@
#include "display.h"
#include "io.h" // Include your I/O header for port access
#include "vga.h"
// Initialize the display
void init_display(void) {
// Initialize VGA settings, if necessary
// This could involve setting up the VGA mode, etc.
set_display_mode(0x13); // Example: Set to 320x200 256-color mode
}
// Enumerate connected displays
void enumerate_displays(void) {
// This is a simplified example. Actual enumeration may require
// reading from specific VGA registers or using BIOS interrupts.
// For demonstration, we will just print a message
// In a real driver, you would check the VGA registers
// to determine connected displays.
clear_display();
// Here you would typically read from VGA registers to find connected displays
// For example, using inb() to read from VGA ports
}
// Set the display mode
void set_display_mode(uint8_t mode) {
// Set the VGA mode by writing to the appropriate registers
outb(VGA_PORT, mode); // Example function to write to a port
}
// Clear the display
void clear_display(void) {
// Clear the display by filling it with a color
// This is a placeholder for actual clearing logic
// You would typically write to video memory here
}

14
kernel/display.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <stdint.h>
#define VGA_PORT 0x3C0 // Base port for VGA
// Function prototypes
void init_display(void);
void enumerate_displays(void);
void set_display_mode(uint8_t mode);
void clear_display(void);
#endif // DISPLAY_H

50
kernel/elf.c Normal file
View File

@@ -0,0 +1,50 @@
#include "elf.h"
#include <stddef.h>
#include <string.h>
// This assumes that the elf is ELF32 and little-endian Intel X86 architecture.
// Check if a binary is a valid ELF executable.
int elf_validate(const void* data) {
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
if (*(uint32_t*)header->e_ident != ELF_MAGIC)
return 0;
if (header->e_type != ET_EXEC && header->e_type != ET_DYN)
return 0;
if (header->e_machine != EM_386)
return 0;
return 1;
}
// Load an ELF executable into memory.
int elf_load(const void* data, void (*load_segment)(uint32_t vaddr, const void* 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;
const void* src = (uint8_t*)data + 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(vaddr, src, 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;
}

52
kernel/elf.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef ELF_H
#define ELF_H
#include <stdint.h>
#define ELF_MAGIC 0x464C457F // "\x7FELF" in little-endian
// ELF Types
#define ET_EXEC 2
#define ET_DYN 3
// ELF Machine
#define EM_386 3
// 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;
#endif // ELF_H

196
kernel/fat12.c Normal file
View File

@@ -0,0 +1,196 @@
#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]) {
// Correct way to return the difference:
// If p1[i] > p2[i], returns positive.
// If p1[i] < p2[i], returns negative.
return (int)p1[i] - (int)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() {
// 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;
}
int disk_read_sector(uint32_t lba, uint8_t *buffer) {
// For now, do nothing and return success
return 0;
}

67
kernel/fat12.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef FAT12_H
#define FAT12_H
#include <stdint.h>
// --- Configuration ---
#define FAT12_SECTOR_SIZE 512
// --- On-Disk Structures (Must be Packed) ---
// BIOS Parameter Block (Start of Boot Sector)
typedef struct {
uint8_t jump[3];
char oem[8];
uint16_t bytes_per_sector; // 512
uint8_t sectors_per_cluster; // 1
uint16_t reserved_sectors; // 1 (Boot sector)
uint8_t fat_count; // 2
uint16_t dir_entries_count; // 224
uint16_t total_sectors; // 2880
uint8_t media_descriptor; // 0xF0
uint16_t sectors_per_fat; // 9
uint16_t sectors_per_track; // 18
uint16_t heads; // 2
uint32_t hidden_sectors;
uint32_t total_sectors_large;
} __attribute__((packed)) fat12_bpb_t;
// Directory Entry (32 bytes)
typedef struct {
char filename[8];
char ext[3];
uint8_t attributes;
uint8_t reserved;
uint8_t creation_ms;
uint16_t creation_time;
uint16_t creation_date;
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;
// --- Kernel File Handle ---
// This is what your kernel uses to track an open file
typedef struct {
char name[11];
uint32_t size;
uint16_t start_cluster;
uint16_t current_cluster;
uint32_t current_sector_in_cluster;
uint32_t bytes_read;
} file_t;
// --- Public API ---
// You must implement this in your disk driver (e.g., floppy.c)
// Returns 0 on success, non-zero on error.
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

92
kernel/framebuffer.c Normal file
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
}

25
kernel/framebuffer.h Normal file
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

@@ -1,5 +0,0 @@
#include "fs.h"
void fs_init() {
// Filesystem initialization code
}

View File

@@ -1,6 +0,0 @@
#ifndef FS_H
#define FS_H
void fs_init();
#endif // FS_H

View File

@@ -1,6 +1,63 @@
#include "heap.h" #include "heap.h"
#include <stdint.h>
typedef struct heap_block {
size_t size;
struct heap_block *next;
int is_free;
} heap_block_t;
static heap_block_t *free_list = NULL;
static void *heap_start_ptr = NULL;
static void *heap_end_ptr = NULL;
void heap_init(void *heap_start, void *heap_end) {
heap_start_ptr = heap_start;
heap_end_ptr = heap_end;
free_list = (heap_block_t *)heap_start;
free_list->size = (uintptr_t)heap_end - (uintptr_t)heap_start - sizeof(heap_block_t);
free_list->next = NULL;
free_list->is_free = 1;
}
void *heap_alloc(size_t size) { void *heap_alloc(size_t size) {
// Heap allocation code heap_block_t *current = free_list;
return NULL;
while (current != NULL) {
if (current->is_free && current->size >= size) {
// If it's a large block, split it
if (current->size > size + sizeof(heap_block_t)) {
heap_block_t *new_block = (heap_block_t *)((uintptr_t)current + sizeof(heap_block_t) + size);
new_block->size = current->size - size - sizeof(heap_block_t);
new_block->next = current->next;
new_block->is_free = 1;
current->next = new_block;
current->size = size;
}
current->is_free = 0;
return (void *)((uintptr_t)current + sizeof(heap_block_t));
}
current = current->next;
}
return NULL; // Out of memory
}
void heap_free(void *ptr) {
if (ptr == NULL) return;
heap_block_t *block = (heap_block_t *)((uintptr_t)ptr - sizeof(heap_block_t));
block->is_free = 1;
// Coalesce with next block
if (block->next && block->next->is_free) {
block->size += block->next->size + sizeof(heap_block_t);
block->next = block->next->next;
}
// TODO: Coalesce with previous block for better compaction
} }

View File

@@ -3,6 +3,9 @@
#include <stddef.h> #include <stddef.h>
void heap_init(void *heap_start, void *heap_end);
void *heap_alloc(size_t size); void *heap_alloc(size_t size);
void heap_free(void *ptr);
#endif // HEAP_H #endif // HEAP_H

View File

@@ -52,7 +52,7 @@ void idt_set_gate(int n, uint32_t handler) {
// Load IDT via lidt // Load IDT via lidt
static void idt_load() { static void idt_load() {
asm volatile("lidt (%0)" : : "r" (&idt_ptr)); __asm__("lidt (%0)" : : "r" (&idt_ptr));
} }
// IDT initialization // IDT initialization

View File

@@ -1,13 +1,35 @@
#ifndef IO_H #ifndef IO_H
#define IO_H #define IO_H
#include <stdint.h>
static inline void outb(uint16_t port, uint8_t val) { static inline void outb(uint16_t port, uint8_t val) {
asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); __asm__("outb %0, %1" : : "a"(val), "Nd"(port));
} }
static inline uint8_t inb(uint16_t port) { static inline uint8_t inb(uint16_t port) {
uint8_t ret; uint8_t ret;
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); __asm__("inb %1, %0" : "=a"(ret) : "Nd"(port));
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; return ret;
} }

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 <stdint.h>
#endif // IRQ_H void irq_remap(void);
void irq_install(void);
void irq_handler(uint32_t int_num);
#endif

View File

@@ -1,8 +1,8 @@
[BITS 32] [BITS 32]
[GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9] GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9
[GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19] GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19
[GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29] GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29
[GLOBAL isr30, isr31, isr_default] GLOBAL isr30, isr31, isr_default
[EXTERN isr_handler] [EXTERN isr_handler]

View File

@@ -1,14 +1,24 @@
#include <stdbool.h>
#include "terminal.h" #include "terminal.h"
#include "serial.h" #include "serial.h"
#include "isr.h" #include "isr.h"
#include "io.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: ");
// Here you can add a basic itoa to print int_num
print_hex(int_num, true, false);
terminal_write("\n");
serial_write("INT triggered\n"); serial_write("INT triggered\n");
terminal_write("Error code: ");
print_hex(err_code, true, false);
terminal_write("\n");
if (interrupt_handlers[int_num]) { if (interrupt_handlers[int_num]) {
interrupt_handlers[int_num](); // Call registered handler interrupt_handlers[int_num](); // Call registered handler
} else { } else {
@@ -24,9 +34,19 @@ void isr_handler(uint32_t int_num, uint32_t err_code) {
// Halt CPU // Halt CPU
while (1) { while (1) {
asm volatile ("hlt"); __asm__("hlt");
} }
} }
// === Send End Of Interrupt to PIC(s) ===
if (int_num >= 40) {
// Send reset signal to slave PIC
outb(0xA0, 0x20);
}
if (int_num >= 32) {
// Send reset signal to master PIC
outb(0x20, 0x20);
}
} }
void register_interrupt_handler(uint8_t n, isr_callback_t handler) { void register_interrupt_handler(uint8_t n, isr_callback_t handler) {

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)
@@ -20,18 +24,24 @@ static const char scancode_map[128] = {
}; };
// Interrupt handler for IRQ1 // Interrupt handler for IRQ1
void keyboard_callback() { void keyboard_callback(void) {
uint8_t scancode = inb(KEYBOARD_DATA_PORT); uint8_t scancode = inb(KEYBOARD_DATA_PORT);
// Ignore key releases (scancodes with high bit set) if (scancode & 0x80) return; // Ignore key release
if (scancode & 0x80)
return;
char c = scancode_map[scancode]; char c = scancode_map[scancode];
if (c && buffer_index < sizeof(key_buffer)) { if (!c) return;
key_buffer[buffer_index++] = c;
terminal_putchar(c); // Echo to terminal (optional) 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);
} }
void keyboard_init() { void keyboard_init() {
@@ -39,15 +49,17 @@ void keyboard_init() {
} }
// Blocking read (returns one char) // Blocking read (returns one char)
char keyboard_get_char() { char keyboard_get_char(void) {
while (buffer_index == 0); while (buffer_count == 0) {
char c = key_buffer[0]; __asm__ __volatile__("hlt"); // Better than busy loop
// Shift buffer left
for (uint8_t i = 1; i < buffer_index; i++) {
key_buffer[i - 1] = key_buffer[i];
} }
buffer_index--;
char c;
__asm__ __volatile__("cli");
c = key_buffer[buffer_tail];
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
buffer_count--;
__asm__ __volatile__("sti");
return c; return c;
} }

View File

@@ -1,5 +1,4 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "io.h" #include "io.h"
#include "serial.h" #include "serial.h"
#include "terminal.h" #include "terminal.h"
@@ -7,6 +6,13 @@
#include "paging.h" #include "paging.h"
#include "memmap.h" #include "memmap.h"
#include "gdt.h" #include "gdt.h"
#include "cpu.h"
#include "kmalloc.h"
#include "print.h"
#include "timer.h"
#include "utils.h"
#include "keyboard.h"
#include "irq.h"
#define LPT1 0x378 #define LPT1 0x378
@@ -22,6 +28,10 @@ void kmain(void) {
serial_init(); serial_init();
serial_write("Serial port initialized.\n"); serial_write("Serial port initialized.\n");
terminal_write("Identifying CPU...\n");
identify_cpu();
serial_write("CPU identification complete.\n");
lpt_write('L'); // Send 'L' to LPT1 to test lpt_write('L'); // Send 'L' to LPT1 to test
terminal_write("Initializing GDT...\n"); terminal_write("Initializing GDT...\n");
@@ -32,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");
@@ -40,30 +53,39 @@ void kmain(void) {
kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!) kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!)
serial_write("kmalloc initialized.\n"); serial_write("kmalloc initialized.\n");
void* ptr = kmalloc(128); // Allocation test
serial_write("Allocated 128 bytes.\n"); serial_write("Allocated 128 bytes.\n");
terminal_write("Initializing timer...\n"); terminal_write("Initializing timer...\n");
timer_init(100); // 100 Hz (10 ms interval) timer_init(100); // 100 Hz (10 ms interval)
serial_write("Timer initialized.\n"); serial_write("Timer initialized.\n");
terminal_write("Initializing keyboard...\n");
keyboard_init();
serial_write("Keyboard initialized.\n");
terminal_write("Getting memory map...\n"); terminal_write("Getting memory map...\n");
memory_map_entry_t mmap[32]; memory_map_entry_t mmap[32];
uint32_t mmap_size = get_memory_map(mmap, 32); uint32_t mmap_size = get_memory_map(mmap, 32);
serial_write("Memory map retrieved.\n"); serial_write("Memory map retrieved.\n");
terminal_write("Memory Regions:\n"); terminal_write("Memory Regions:\n");
char buf[32];
for (uint32_t i = 0; i < mmap_size; i++) { for (uint32_t i = 0; i < mmap_size; i++) {
terminal_write(" - Region: "); terminal_write(" - Base: ");
// You would format and print base/length/type here print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF), true, false); // Lower 32 bits
// (e.g., with a basic itoa and print_hex helper) terminal_write(", Length: ");
serial_write("Memory region entry\n"); print_hex((uint32_t)(mmap[i].length & 0xFFFFFFFF), true, false); // Lower 32 bits
terminal_write(", Type: ");
itoa(mmap[i].type, buf, 10);
terminal_write(buf);
terminal_write("\n");
} }
terminal_write("System initialized. Halting.\n"); terminal_write("System initialized. Halting.\n");
// Halt CPU in loop // Halt CPU in loop
while (1) { while (1) {
asm volatile("hlt"); __asm__("hlt");
} }
} }

View File

@@ -1,6 +1,8 @@
#include "kmalloc.h" #include "kmalloc.h"
#include "terminal.h" // Optional: for debug output #include "terminal.h" // Optional: for debug output
#define HEAP_END 0xC0500000
static uint32_t current_heap = 0; static uint32_t current_heap = 0;
// Initialize the allocator with a starting heap address // Initialize the allocator with a starting heap address
@@ -32,7 +34,18 @@ void* kmalloc_aligned(size_t size, uint32_t alignment) {
current_heap = (current_heap + alignment) & ~(alignment - 1); current_heap = (current_heap + alignment) & ~(alignment - 1);
} }
if (current_heap + size > HEAP_END) {
terminal_write("kmalloc_aligned: Out of memory!\n");
return 0;
}
void* addr = (void*)current_heap; void* addr = (void*)current_heap;
current_heap += size; current_heap += size;
return addr; return addr;
} }
void kfree(void* ptr) {
// In a bump allocator, we cannot free individual blocks.
// We can reset the allocator to the initial state.
current_heap = 0; // Reset the heap pointer
}

View File

@@ -1,31 +1,38 @@
ENTRY(_start) ENTRY(kmain)
PHDRS {
text PT_LOAD FLAGS(5); /* Read + Execute */
rodata PT_LOAD FLAGS(4); /* Read only */
data PT_LOAD FLAGS(6); /* Read + Write */
}
SECTIONS { SECTIONS {
. = 1M; . = 1M;
.multiboot : {
*(.multiboot)
}
.text : { .text : {
*(.text) *(.text*)
} } :text
.rodata : { .rodata : {
*(.rodata) *(.rodata*)
} } :rodata
.data : { .data : {
*(.data) *(.data*)
} } :data
.bss : { .bss : {
*(.bss) *(.bss*)
*(COMMON) *(COMMON)
} :data
.stack (NOLOAD) : {
. = ALIGN(4);
. = . + 0x1000;
} }
. = ALIGN(4096); .heap (NOLOAD) : {
__stack_top = .; . = ALIGN(4);
. += 128K; . = . + 0x10000;
__stack_bottom = .; }
} }

105
kernel/malloc.c Normal file
View File

@@ -0,0 +1,105 @@
#include "malloc.h"
#include <stdint.h>
static void *heap_start; // Start of the heap
static void *heap_end; // End of the heap
static struct memory_block *free_blocks; // List of free blocks
void init_heap(void *start, void *end)
{
heap_start = start;
heap_end = end;
// Initialize the heap with a single large free block
free_blocks = (struct memory_block *)start;
free_blocks->size = (uintptr_t)end - (uintptr_t)start - sizeof(struct memory_block);
free_blocks->next = NULL;
free_blocks->is_free = 1;
}
void *find_free_block(size_t size) {
struct memory_block *current = free_blocks;
while (current != NULL) {
if (current->is_free && current->size >= size) {
return current;
}
current = current->next;
}
// No suitable block found
return NULL;
}
void mark_as_used(void *ptr, size_t size) {
struct memory_block *block = (struct memory_block *)ptr;
block->is_free = 0;
// If the block is larger than needed, split it
if (block->size > size + sizeof(struct memory_block)) {
struct memory_block *new_block = (struct memory_block *)((uintptr_t)ptr + size + sizeof(struct memory_block));
new_block->size = block->size - size - sizeof(struct memory_block);
new_block->next = block->next;
new_block->is_free = 1;
block->size = size;
block->next = new_block;
}
}
void mark_as_free(void *ptr) {
struct memory_block *block = (struct memory_block *)ptr;
block->is_free = 1;
// Coalesce with next block if it's free
if (block->next && block->next->is_free) {
block->size += block->next->size + sizeof(struct memory_block);
block->next = block->next->next;
}
// Coalesce with previous block if it's free
struct memory_block *prev = free_blocks;
while (prev && prev->next != block) {
prev = prev->next;
}
if (prev && prev->is_free) {
prev->size += block->size + sizeof(struct memory_block);
prev->next = block->next;
}
}
void *malloc(size_t size)
{
if (heap_start == NULL || heap_end == NULL)
{
// Heap not initialized, cannot allocate
return NULL;
}
// Align the size to the word size for efficiency
size = (size + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1);
// Search for a free block of sufficient size
void *block = find_free_block(size);
if (block != NULL)
{
// Mark the block as used
mark_as_used(block, size);
return (void *)((uintptr_t)block + sizeof(struct memory_block));
}
// No suitable block found, out of memory
return NULL;
}
void free(void *ptr)
{
if (ptr == NULL)
{
return;
}
// Adjust pointer to the start of the memory block
struct memory_block *block = (struct memory_block *)((uintptr_t)ptr - sizeof(struct memory_block));
// Mark the block as free
mark_as_free(block);
}

27
kernel/malloc.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef MALLOC_H
#define MALLOC_H
#include <stddef.h> // For size_t
// Define the memory block structure
struct memory_block {
size_t size;
struct memory_block *next;
int is_free;
};
// Function prototypes
void init_heap(void *start, void *end);
void *malloc(size_t size);
void free(void *ptr);
// Helper function prototypes
void *find_free_block(size_t size);
void mark_as_used(void *ptr, size_t size);
void mark_as_free(void *ptr);
// External heap boundaries
extern void *user_heap_start;
extern void *user_heap_end;
#endif // MALLOC_H

View File

@@ -1,14 +1,21 @@
#include "memmap.h" #include "memmap.h"
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) { uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
// Fill with dummy values for now uint32_t count = 0;
map[0].base_addr = 0x00000000;
map[0].length = 0x0009FC00;
map[0].type = 1;
map[1].base_addr = 0x00100000; if (max_entries >= 1) {
map[1].length = 0x1FF00000; map[count].base_addr = 0x00000000;
map[1].type = 1; map[count].length = 0x0009FC00;
map[count].type = 1;
count++;
}
return 2; // 2 regions if (max_entries >= 2) {
map[count].base_addr = 0x00100000;
map[count].length = 0x1FF00000;
map[count].type = 1;
count++;
}
return count;
} }

17
kernel/memory.c Normal file
View File

@@ -0,0 +1,17 @@
#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;
}

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 */

27
kernel/mouse.c Normal file
View File

@@ -0,0 +1,27 @@
// mouse.c
#include "mouse.h"
#include "usb.h"
#include <stdint.h>
#include <stdbool.h>
// Mouse buffer
static mouse_data_t mouse_data;
// Read USB mouse data
mouse_data_t usb_read_mouse(void) {
uint8_t buffer[3]; // USB HID Mouse reports typically use 3 bytes
if (usb_interrupt_transfer()) { // Ensure buffer is filled
// Process the received data
mouse_data.x += buffer[1]; // X movement
mouse_data.y += buffer[2]; // Y movement
mouse_data.left_button = buffer[0] & 0x01;
mouse_data.right_button = buffer[0] & 0x02;
} else {
// Handle the case where no data was received
// You can choose to reset mouse_data or leave it unchanged
// For example, you might want to set movement to zero if no data is received
// mouse_data.x = 0;
// mouse_data.y = 0;
}
return mouse_data;
}

26
kernel/mouse.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef MOUSE_H
#define MOUSE_H
#include <stdint.h>
#include <stdbool.h>
// Mouse data structure
typedef struct {
int x;
int y;
bool left_button;
bool right_button;
} mouse_data_t;
// Function declarations for USB 1.x HID mouse support
bool usb_mouse_init(void);
bool usb_mouse_detected(void);
bool usb_mouse_received(void);
mouse_data_t usb_read_mouse(void);
// USB Host Controller Interface functions (USB 1.x support)
bool uhci_init(void);
bool ohci_init(void);
#endif // MOUSE_H

View File

@@ -1,21 +1,17 @@
#include "paging.h"
#include "io.h"
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <string.h>
#include "io.h"
#include "paging.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
// 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) {
for (int i = 0; i < PAGE_DIRECTORY_SIZE; i++) { // Set first PDE
dir[i].present = 0;
}
dir[0].present = 1; dir[0].present = 1;
dir[0].rw = 1; dir[0].rw = 1;
dir[0].user = 0; dir[0].addr = (uint32_t)page_table >> 12;
dir[0].frame = (uint32_t)page_table >> 12;
} }
// Helper function to set up the page table entry // Helper function to set up the page table entry
@@ -23,12 +19,8 @@ void set_page_table(page_table_entry_t *table) {
for (int i = 0; i < PAGE_TABLE_SIZE; i++) { for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
// Set up page table entries with identity mapping // Set up page table entries with identity mapping
table[i].present = 1; table[i].present = 1;
table[i].rw = 1; // Read/Write table[i].rw = 1; // Read/Write
table[i].user = 0; // Kernel mode table[i].addr = i; // Identity mapping
table[i].write_through = 0;
table[i].cache_disabled = 0;
table[i].accessed = 0;
table[i].frame = i; // Identity mapping
} }
} }
@@ -37,36 +29,23 @@ void enable_paging() {
uint32_t cr0; uint32_t cr0;
// Load page directory into CR3 // Load page directory into CR3
asm volatile("mov %0, %%cr3" : : "r"(page_directory)); __asm__("mov %0, %%cr3" : : "r"(page_directory));
// Enable paging (set the PG bit in CR0) // Enable paging (set the PG bit in CR0)
asm volatile("mov %%cr0, %0" : "=r"(cr0)); __asm__("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 0x80000000; // Set the PG (paging) bit cr0 |= 0x80000000; // Set the PG (paging) bit
asm volatile("mov %0, %%cr0" : : "r"(cr0)); __asm__("mov %0, %%cr0" : : "r"(cr0));
} }
// Initialize paging: set up the page directory and enable paging // Initialize paging: set up the page directory and enable paging
void paging_init() { void paging_init() {
// Zero out the tables
memset(page_directory, 0x00, PAGE_DIRECTORY_SIZE * sizeof *page_directory);
memset(page_table, 0x00, PAGE_TABLE_SIZE * sizeof *page_table);
// Set up identity-mapped page directory + table // Set up identity-mapped page directory + table
set_page_directory(page_directory); set_page_directory(page_directory);
set_page_table(page_table); set_page_table(page_table);
// === Set up heap mapping at 0xC0100000 ===
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
heap_page_table[i].present = 1;
heap_page_table[i].rw = 1;
heap_page_table[i].user = 0;
heap_page_table[i].write_through = 0;
heap_page_table[i].cache_disabled = 0;
heap_page_table[i].accessed = 0;
heap_page_table[i].frame = (256 + i); // Start physical heap at 1MB (256*4KB = 1MB)
}
// Index 772 = 0xC0100000 / 4MB
page_directory[772].present = 1;
page_directory[772].rw = 1;
page_directory[772].user = 0;
page_directory[772].frame = (uint32_t)heap_page_table >> 12;
enable_paging(); enable_paging();
} }

View File

@@ -10,31 +10,31 @@
// Page Directory and Page Table structure // Page Directory and Page Table structure
typedef struct { typedef struct {
uint32_t present : 1; // Present bit (1: page is present in memory) uint32_t present : 1; // Present bit (1: page is present in memory)
uint32_t rw : 1; // Read-Write bit (1: page is read-write) uint32_t rw : 1; // Read-Write bit (1: page is read-write)
uint32_t user : 1; // User-supervisor bit (1: user mode access) uint32_t user : 1; // User-supervisor bit (1: user mode access)
uint32_t write_through : 1; // Write-through cache uint32_t write_through : 1; // Write-through cache
uint32_t cache_disabled : 1; // Cache disabled uint32_t cache_disabled : 1; // Cache disabled
uint32_t accessed : 1; // Accessed bit uint32_t accessed : 1; // Accessed bit
uint32_t reserved : 1; // Reserved bit uint32_t dirty : 1; // Dirty bit
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB) uint32_t attribute : 1; // Page size (0: 4KB, 1: 4MB)
uint32_t global : 1; // Global page (can be used across different processes) uint32_t global : 1; // Global page (can be used across different processes)
uint32_t available : 3; // Available bits for the system uint32_t reserved : 3; // Unused
uint32_t frame : 20; // Frame address (physical address) uint32_t addr : 20; // Page frame address (physical address)
} __attribute__((packed)) page_table_entry_t; } __attribute__((packed)) page_table_entry_t;
// Define page directory entry // Define page directory entry
typedef struct { typedef struct {
uint32_t present : 1; uint32_t present : 1; // Present bit (1: PTE is present in memory)
uint32_t rw : 1; uint32_t rw : 1; // Read-Write bit (1: pages are read-write)
uint32_t user : 1; uint32_t user : 1; // User-supervisor bit (1: user mode access)
uint32_t write_through : 1; uint32_t write_through : 1; // Write-through cache
uint32_t cache_disabled : 1; uint32_t cache_disabled : 1; // Cache disabled
uint32_t accessed : 1; uint32_t accessed : 1; // Accessed bit
uint32_t reserved : 1; uint32_t available : 1; // Unused
uint32_t zero : 5; // Must be zero for page directory uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
uint32_t reserved_2 : 7; // Reserved bits uint32_t available_2 : 4; // Unused
uint32_t frame : 20; // Frame address of the page table uint32_t addr : 20; // Page table address
} __attribute__((packed)) page_directory_entry_t; } __attribute__((packed)) page_directory_entry_t;
extern page_directory_entry_t *page_directory; extern page_directory_entry_t *page_directory;

View File

@@ -1,5 +1,19 @@
#include "panic.h" #include "panic.h"
#include "terminal.h"
#include "serial.h"
#include <stdbool.h>
void panic(const char *message) { void panic(const char *message) {
// Panic handling code terminal_write("KERNEL PANIC: ");
terminal_write(message);
terminal_write("\nSystem halted.\n");
serial_write("KERNEL PANIC: ");
serial_write(message);
serial_write("\nSystem halted.\n");
// Halt the system
while (true) {
__asm__("cli; hlt");
}
} }

102
kernel/print.c Normal file
View File

@@ -0,0 +1,102 @@
#include <stdarg.h>
#include "print.h"
#include "serial.h"
#include "terminal.h"
void my_putchar(char ch) {
// Write a single character to standard output
// In a freestanding environment, you might need to implement this differently
// For now, we will use the standard putchar for demonstration
// Replace this with your own implementation if needed
terminal_putchar(ch);
}
void print_string(const char *str) {
// Simple implementation to print a string
while (*str) {
my_putchar(*str++);
}
}
void my_printf(const char *format, ...) {
va_list args;
va_start(args, format);
while (*format) {
if (*format == '%') {
format++; // Move to the next character after '%'
switch (*format) {
case 's': { // String
char *str = va_arg(args, char *);
print_string(str);
break;
}
case 'd': { // Integer
int num = va_arg(args, int);
char buffer[20]; // Buffer to hold the string representation
//TODO: implement `snprintf()`
//snprintf(buffer, sizeof(buffer), "%d", num);
print_string(buffer);
break;
}
case 'c': { // Character
char ch = (char)va_arg(args, int); // Promote char to int
my_putchar(ch);
break;
}
default:
my_putchar('%'); // Print the '%' if no valid format specifier
my_putchar(*format);
break;
}
} else {
my_putchar(*format);
}
format++;
}
va_end(args);
}
void print_hex(uint32_t val, int include_prefix, int suppress_leading_zeros) {
char hex_chars[] = "0123456789ABCDEF";
char buffer[11]; // 8 hex digits + "0x" + null terminator
int pos = 10; // Start from end of buffer (null terminator)
// Null-terminate the buffer
buffer[pos--] = '\0';
// Convert value to hex digits
for (int i = 7; i >= 0; i--) {
int digit = val & 0xF; // Get last 4 bits
buffer[pos--] = hex_chars[digit];
val >>= 4; // Shift right by 4 bits
}
// Add "0x" prefix if requested
if (include_prefix) {
buffer[pos--] = 'x';
buffer[pos--] = '0';
}
// Determine start of output (skip leading zeros if requested)
int start = include_prefix ? 0 : 2; // Start after "0x" if prefix included
if (suppress_leading_zeros && !include_prefix) {
int i = start;
while (i < 9 && buffer[i] == '0') {
i++;
}
if (i == 10) {
// All zeros, output single '0'
terminal_write("0");
serial_write("0");
return;
}
start = i;
}
// Output the result
terminal_write(buffer + start);
serial_write(buffer + start);
}

11
kernel/print.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef PRINT_H
#define PRINT_H
#include <stdint.h>
void print_string(const char *str);
void my_printf(const char *format, ...);
void print_hex(uint32_t val, int include_prefix, int suppress_leading_zeros);
void my_putchar(char ch);
#endif

View File

@@ -1,5 +1,96 @@
#include "scheduler.h" #include "scheduler.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];
// 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 int task_count = 0;
static task_t *task_list = NULL;
static task_t *current_task = NULL;
void scheduler_init() { void scheduler_init() {
// Scheduler initialization code task_list = NULL;
current_task = NULL;
task_count = 0;
}
void scheduler_add_task(void (*entry)(void)) {
if (task_count >= MAX_TASKS || entry == NULL) return;
task_t *new_task = &tasks[task_count];
new_task->id = task_count;
// 1. Calculate the top of the stack (High Address)
// 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;
// 3. Add to linked list
if (task_list == NULL) {
task_list = new_task;
current_task = new_task; // Make sure we have a current task to start
} else {
task_t *tail = task_list;
while (tail->next) {
tail = tail->next;
}
tail->next = new_task;
}
task_count++;
}
void scheduler_schedule() {
if (!current_task) return;
task_t *prev = current_task;
// Round-robin logic
if (current_task->next) {
current_task = current_task->next;
} else {
current_task = task_list;
}
// Perform the ACTUAL context switch
// We pass the address of the previous task's stack pointer storage
// and the value of the new task's stack pointer.
if (prev != current_task) {
ctx_switch(&prev->stack_ptr, current_task->stack_ptr);
}
}
void scheduler_yield() {
scheduler_schedule();
} }

View File

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

View File

@@ -1,5 +1,6 @@
#include "io.h" #include "io.h"
#include "serial.h" #include "serial.h"
#include <stdint.h>
#define COM1 0x3F8 #define COM1 0x3F8
#define COM2 0x2F8 #define COM2 0x2F8

60
kernel/shell.c Normal file
View File

@@ -0,0 +1,60 @@
#include "shell.h"
#include "keyboard.h"
#include "terminal.h"
#include "print.h"
#include "string_utils.h"
void execute(char *input) {
if (my_strcmp(input, "help") == 0) {
my_printf("Available commands: help, clear, exit\n");
} else if (my_strcmp(input, "clear") == 0) {
terminal_clear();
} else {
my_printf("Unknown command: %s\n", input);
}
}
void shell_loop()
{
char input[256];
size_t index = 0;
char c;
while (1)
{
my_printf("> ");
index = 0;
while (1)
{
c = keyboard_get_char(); // Waits for input
if (c == '\n' || c == '\r') // Enter key
{
input[index] = '\0';
my_printf("\n");
break;
}
else if (c == '\b' || c == 127) // Backspace
{
if (index > 0)
{
index--;
my_printf("\b \b"); // Erase last char on screen
}
}
else
{
if (index < sizeof(input) - 1) {
input[index++] = c;
terminal_putchar(c);
}
}
}
if (my_strcmp(input, "exit") == 0)
break;
execute(input);
}
}

7
kernel/shell.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef SHELL_H
#define SHELL_H
void shell_loop(void);
void execute(char *input);
#endif

80
kernel/string_utils.c Normal file
View File

@@ -0,0 +1,80 @@
#include "string_utils.h"
#include <stddef.h>
#include <stdarg.h>
size_t my_strlen(const char *str) {
const char *s = str;
while (*s) s++;
return s - str;
}
// Forward declaration of my_itoa
static size_t my_itoa(int value, char *str, size_t size);
int my_vsnprintf(char *str, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
size_t written = 0; // Change to size_t
for (const char *p = format; *p != '\0' && written < size - 1; p++) {
if (*p == '%') {
p++;
if (*p == 's') {
const char *s = va_arg(args, const char *);
while (*s && written < size - 1) {
str[written++] = *s++;
}
} else if (*p == 'd') {
// Handle integer formatting
written += my_itoa(va_arg(args, int), str + written, size - written);
} else {
// Handle other formats as needed
str[written++] = *p; // Just copy the character
}
} else {
str[written++] = *p;
}
}
str[written] = '\0'; // Null-terminate the string
va_end(args);
return written;
}
static size_t my_itoa(int value, char *str, size_t size) {
size_t written = 0; // Change to size_t
if (value < 0) {
if (written < size - 1) {
str[written++] = '-';
}
value = -value;
}
// Convert integer to string
int temp = value;
int digits = 0;
do {
digits++;
temp /= 10;
} while (temp);
if (written + digits >= size) {
digits = size - written - 1; // Prevent overflow
}
str += written + digits; // Move pointer to the end
*str-- = '\0'; // Null-terminate the string
do {
*str-- = (value % 10) + '0';
value /= 10;
} while (value && str >= str - digits);
return written + digits; // Return total written characters
}
int my_strcmp(const char *str1, const char *str2) {
while (*str1 && (*str1 == *str2)) {
str1++;
str2++;
}
return *(unsigned char *)str1 - *(unsigned char *)str2;
}

11
kernel/string_utils.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include <stddef.h>
#include <stdarg.h> // Include for va_list and related macros
size_t my_strlen(const char *str); // Renamed to avoid conflict
int my_vsnprintf(char *str, size_t size, const char *format, ...); // Renamed to avoid conflict
int my_strcmp(const char *str1, const char *str2);
#endif // STRING_UTILS_H

View File

@@ -1,5 +1,29 @@
#include "syscalls.h" #include "syscalls.h"
#include "scheduler.h"
#include <stdarg.h>
void syscall_handler() { void syscall_handler(int code, va_list args) {
// Syscall handling code switch (code) {
case SYSCALL_INIT:
scheduler_init();
break;
case SYSCALL_SPAWN: {
void (*entry)(void) = va_arg(args, void (*)(void));
scheduler_add_task(entry);
break;
}
case SYSCALL_YIELD:
scheduler_yield();
break;
default:
// Unknown syscall
break;
}
}
void syscall(int code, ...) {
va_list args;
va_start(args, code);
syscall_handler(code, args);
va_end(args);
} }

View File

@@ -1,6 +1,17 @@
#ifndef SYSCALLS_H #ifndef SYSCALLS_H
#define SYSCALLS_H #define SYSCALLS_H
void syscall_handler(); #include <stdarg.h>
// Syscall numbers
typedef enum {
SYSCALL_INIT = 0,
SYSCALL_SPAWN,
SYSCALL_YIELD
} syscall_code_t;
// Syscall dispatcher
void syscall_handler(int code, va_list args);
// Syscall interface
void syscall(int code, ...);
#endif // SYSCALLS_H #endif // SYSCALLS_H

View File

@@ -1,6 +1,7 @@
#include <stdint.h> #include <stdint.h>
#include "io.h" #include "io.h"
#include "terminal.h" #include "terminal.h"
#include "vga.h"
#define VGA_ADDRESS 0xB8000 #define VGA_ADDRESS 0xB8000
#define VGA_WIDTH 80 #define VGA_WIDTH 80
@@ -11,10 +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;
static uint16_t vga_entry(char c, uint8_t color) {
return (uint16_t) color << 8 | (uint8_t) c;
}
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++) {
@@ -99,8 +97,10 @@ void terminal_clear(void) {
update_cursor(); update_cursor();
} }
static 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

@@ -1,5 +1,118 @@
#include <stdbool.h>
#include <string.h>
#include "malloc.h"
#include "print.h"
#include "threading.h" #include "threading.h"
void threading_init() { #define MAX_THREADS 16 // Maximum number of threads
// Threading initialization code #define THREAD_STACK_SIZE 8192 // Stack size for each thread
// The thread table stores information about all threads
static Thread thread_table[MAX_THREADS];
static uint32_t current_thread = 0; // Index of the currently running thread
static uint32_t num_threads = 0; // Number of active threads
// A simple mutex spinlock
static volatile int mutex_locked = 0;
// Function declaration for context_switch
void context_switch(Thread *next);
// Initialize the threading system
void thread_init(void) {
memset(thread_table, 0, sizeof(thread_table));
num_threads = 0;
}
// Create a new thread
void thread_create(Thread *thread __attribute__((unused)), void (*start_routine)(void *), void *arg) {
if (num_threads >= MAX_THREADS) {
my_printf("Error: Maximum thread count reached.\n");
return;
}
// Find an empty slot for the new thread
int index = num_threads++;
thread_table[index] = (Thread){0};
// Set up the new thread
thread_table[index].start_routine = start_routine;
thread_table[index].arg = arg;
thread_table[index].stack_size = THREAD_STACK_SIZE;
thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE);
thread_table[index].stack_top = thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t);
// Initialize the stack (simulate pushing the function's return address)
uint32_t *stack_top = thread_table[index].stack_top;
*(--stack_top) = (uint32_t)start_routine; // Return address (the thread's entry point)
*(--stack_top) = (uint32_t)arg; // Argument to pass to the thread
// Set the thread's state to ready
thread_table[index].state = THREAD_READY;
// If this is the first thread, switch to it
if (index == 0) {
scheduler();
}
}
// Yield the CPU to another thread
void thread_yield(void) {
// Find the next thread in a round-robin manner
uint32_t next_thread = (current_thread + 1) % num_threads;
while (next_thread != current_thread && thread_table[next_thread].state != THREAD_READY) {
next_thread = (next_thread + 1) % num_threads;
}
if (next_thread != current_thread) {
current_thread = next_thread;
scheduler();
}
}
// Exit the current thread
void thread_exit(void) {
thread_table[current_thread].state = THREAD_BLOCKED; // Mark the thread as blocked (finished)
free(thread_table[current_thread].stack); // Free the thread's stack
num_threads--; // Decrease thread count
// Yield to the next thread
thread_yield();
}
// Scheduler: This function selects the next thread to run
void scheduler(void) {
// Find the next ready thread
uint32_t next_thread = (current_thread + 1) % num_threads;
while (thread_table[next_thread].state != THREAD_READY) {
next_thread = (next_thread + 1) % num_threads;
}
if (next_thread != current_thread) {
current_thread = next_thread;
context_switch(&thread_table[current_thread]);
}
}
// Context switch to the next thread (assembly would go here to save/load registers)
void context_switch(Thread *next) {
// For simplicity, context switching in this example would involve saving/restoring registers.
// In a real system, you would need to save the CPU state (registers) and restore the next thread's state.
my_printf("Switching to thread...\n");
next->start_routine(next->arg); // Start running the next thread
}
// Simple mutex functions (spinlock)
void mutex_init(void) {
mutex_locked = 0;
}
void mutex_lock(void) {
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
// Busy wait (spinlock)
}
}
void mutex_unlock(void) {
__sync_lock_release(&mutex_locked);
} }

View File

@@ -1,6 +1,36 @@
#ifndef THREADING_H #ifndef THREADING_H
#define THREADING_H #define THREADING_H
void threading_init(); #include <stdint.h>
#include <stddef.h>
// Define a basic thread structure (Thread Control Block - TCB)
typedef struct Thread {
void (*start_routine)(void *); // Function pointer to the thread function
void *arg; // Argument to be passed to the thread function
uint32_t *stack; // Pointer to the thread's stack
uint32_t *stack_top; // Top of the stack (where the thread will start)
uint32_t stack_size; // Size of the stack
uint32_t state; // Thread state (running, ready, blocked)
} Thread;
// Thread states
#define THREAD_RUNNING 0
#define THREAD_READY 1
#define THREAD_BLOCKED 2
// Thread management functions
void thread_init(void);
void thread_create(Thread *thread, void (*start_routine)(void *), void *arg);
void thread_yield(void);
void thread_exit(void);
// Scheduler function
void scheduler(void);
// Synchronization functions (mutex spinlocks)
void mutex_init(void);
void mutex_lock(void);
void mutex_unlock(void);
#endif // THREADING_H #endif // THREADING_H

View File

@@ -2,15 +2,23 @@
#include "io.h" #include "io.h"
#include "isr.h" #include "isr.h"
#include "terminal.h" #include "terminal.h"
#include "stdio.h"
#include "utils.h"
static uint32_t tick = 0; static volatile uint32_t tick = 0;
void timer_callback(void) { void timer_callback(void) {
tick++; tick++;
// Optional: Print every 100 ticks
// if (tick % 100 == 0) { if (tick % 100 == 0) {
// terminal_write("Tick\n"); char buf[16];
// } itoa(tick, buf, 10);
terminal_write("Tick count: ");
terminal_write(buf);
terminal_write("\n");
}
outb(0x20, 0x20); // EOI to PIC
} }
void timer_init(uint32_t frequency) { void timer_init(uint32_t frequency) {

64
kernel/usb.c Normal file
View File

@@ -0,0 +1,64 @@
#include "usb.h"
#include <stdint.h>
#include <stdbool.h>
// USB version detection
bool usb_detect_version(uint16_t *version) {
if (!version) return false;
*version = 0x0110; // Example: USB 1.1
return true;
}
// USB initialization for 1.x
bool usb_init(void) {
// Detect controller type (UHCI or OHCI)
bool uhci_supported = uhci_init();
bool ohci_supported = ohci_init();
if (!uhci_supported && !ohci_supported) {
return false; // No supported controllers found
}
return true;
}
// USB device enumeration (1.x)
bool usb_enumerate_devices(void) {
// Implementation for detecting devices on USB 1.x ports
return true;
}
// HID initialization for USB 1.x
bool usb_hid_init(void) {
// Ensure USB is initialized
if (!usb_init()) return false;
return usb_enumerate_devices();
}
// USB transfers (stubs for 1.x)
bool usb_control_transfer(/* parameters */) {
// Implement control transfer for USB 1.x
return true;
}
bool usb_interrupt_transfer(/* parameters */) {
// Implement interrupt transfer for USB 1.x
return true;
}
bool usb_bulk_transfer(/* parameters */) {
// Implement bulk transfer for USB 1.x
return true;
}
// USB host controller initialization (UHCI & OHCI)
bool uhci_init(void) {
// Initialize UHCI controller (USB 1.x)
return true;
}
bool ohci_init(void) {
// Initialize OHCI controller (USB 1.x)
return true;
}

24
kernel/usb.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef USB_H
#define USB_H
#include <stdint.h>
#include <stdbool.h>
// USB initialization and management functions
bool usb_init(void);
bool usb_detect_version(uint16_t *version);
bool usb_enumerate_devices(void);
// HID-specific functions
bool usb_hid_init(void);
// USB transfer functions
bool usb_control_transfer(/* parameters */);
bool usb_interrupt_transfer(/* parameters */);
bool usb_bulk_transfer(/* parameters */);
// USB host controller initialization (USB 1.x only)
bool uhci_init(void); // UHCI support
bool ohci_init(void); // OHCI support
#endif // USB_H

View File

@@ -1,5 +1,78 @@
#include "utils.h" #include "utils.h"
void util_function() { static void reverse(char* str, int len) {
// Utility function code int start = 0;
int end = len - 1;
while (start < end) {
char temp = str[start];
str[start++] = str[end];
str[end--] = temp;
}
}
// Integer to ASCII for signed ints
char* itoa(int value, char* str, int base) {
int i = 0;
int isNegative = 0;
unsigned int uval;
if (base < 2 || base > 36) {
str[0] = '\0';
return str;
}
// Handle zero explicitly
if (value == 0) {
str[i++] = '0';
str[i] = '\0';
return str;
}
// Handle negative numbers (only for base 10)
if (value < 0 && base == 10) {
isNegative = 1;
uval = (unsigned int)(-value);
} else {
uval = (unsigned int)value;
}
while (uval != 0) {
int rem = uval % base;
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
uval /= base;
}
if (isNegative) {
str[i++] = '-';
}
str[i] = '\0';
reverse(str, i);
return str;
}
// Integer to ASCII for unsigned ints
char* utoa(unsigned int value, char* str, int base) {
int i = 0;
if (base < 2 || base > 36) {
str[0] = '\0';
return str;
}
if (value == 0) {
str[i++] = '0';
str[i] = '\0';
return str;
}
while (value != 0) {
int rem = value % base;
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
value /= base;
}
str[i] = '\0';
reverse(str, i);
return str;
} }

View File

@@ -1,6 +1,12 @@
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H #define UTILS_H
void util_function(); #include <stddef.h>
// Convert integer to string (base is typically 10, 16, etc.)
char* itoa(int value, char* str, int base);
// Convert unsigned integer to string (base is typically 10, 16, etc.)
char* utoa(unsigned int value, char* str, int base);
#endif // UTILS_H #endif // UTILS_H

View File

@@ -1,4 +1,20 @@
#include "vga.h" #include "vga.h"
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "string_utils.h"
void outb(uint16_t port, uint8_t value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
// Read a byte from the specified port
uint8_t inb(uint16_t port) {
uint8_t value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
uint8_t vga_entry_color(vga_color fg, vga_color bg) { uint8_t vga_entry_color(vga_color fg, vga_color bg) {
return fg | bg << 4; return fg | bg << 4;
@@ -7,3 +23,129 @@ uint8_t vga_entry_color(vga_color fg, vga_color bg) {
uint16_t vga_entry(unsigned char uc, uint8_t color) { uint16_t vga_entry(unsigned char uc, uint8_t color) {
return (uint16_t)uc | (uint16_t)color << 8; return (uint16_t)uc | (uint16_t)color << 8;
} }
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y) {
const size_t index = y * 80 + x;
((uint16_t*) 0xB8000)[index] = vga_entry(c, color);
}
void vga_clear(uint8_t color) {
uint16_t blank = vga_entry(' ', color);
for (size_t i = 0; i < 80 * 25; ++i) {
((uint16_t*) 0xB8000)[i] = blank;
}
}
void vga_write_string(const char* data, size_t size) {
size_t x = 0;
size_t y = 0;
for (size_t i = 0; i < size; ++i) {
if (x >= 80) { // If we reach the end of a line, move to the next line
x = 0;
y++;
}
if (y >= 25) { // If we reach the bottom of the screen, scroll
vga_scroll();
y = 24; // Reset to the last row
}
vga_put_entry_at(data[i], vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), x, y);
x++; // Move to the next column
}
}
void vga_scroll(void) {
uint16_t* video_memory = (uint16_t*)0xB8000;
// Shift all lines up by one row (80 columns per row)
for (size_t i = 0; i < 24 * 80; ++i) {
video_memory[i] = video_memory[i + 80]; // Move one row up
}
// Clear the last row (bottom row)
for (size_t i = 24 * 80; i < 25 * 80; ++i) {
video_memory[i] = vga_entry(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE));
}
}
void vga_set_cursor_position(size_t x, size_t y) {
uint16_t position = y * 80 + x; // Calculate linear index (y * 80 + x)
// Set the high byte of the cursor position
outb(VGA_PORT_INDEX, 0x0E); // Cursor high byte register
outb(VGA_PORT_DATA, position >> 8);
// Set the low byte of the cursor position
outb(VGA_PORT_INDEX, 0x0F); // Cursor low byte register
outb(VGA_PORT_DATA, position & 0xFF);
}
size_t vga_get_cursor_position(void) {
outb(VGA_PORT_INDEX, 0x0E); // Cursor high byte register
uint8_t high = inb(VGA_PORT_DATA); // Read high byte
outb(VGA_PORT_INDEX, 0x0F); // Cursor low byte register
uint8_t low = inb(VGA_PORT_DATA); // Read low byte
return (high << 8) | low; // Combine the high and low bytes
}
void vga_set_cursor_blinking(bool enable) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
if (enable) {
cursor |= 0x01; // Enable blinking
} else {
cursor &= ~0x01; // Disable blinking
}
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
outb(VGA_PORT_DATA, cursor);
}
void vga_set_cursor_shape(uint8_t start, uint8_t end) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
cursor = (cursor & 0xC0) | (start & 0x3F); // Set start of cursor shape
outb(VGA_PORT_DATA, cursor);
outb(VGA_PORT_INDEX, 0x0B); // Cursor start register
outb(VGA_PORT_DATA, end & 0x3F); // Set end of cursor shape
}
void vga_set_cursor_color(uint8_t color) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
cursor = (cursor & 0xC0) | (color & 0x3F); // Set cursor color
outb(VGA_PORT_DATA, cursor);
}
void vga_set_cursor_blink_rate(uint8_t rate) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
cursor = (cursor & 0xC0) | (rate & 0x3F); // Set cursor blink rate
outb(VGA_PORT_DATA, cursor);
}
void vga_printf(const char* format, ...) {
char buffer[256]; // Buffer to store the formatted string
va_list args;
va_start(args, format);
my_vsnprintf(buffer, sizeof(buffer), format, args); // Use my_vsnprintf instead of vsnprintf
va_end(args);
// Now you can use the buffer with vga_write_string
vga_write_string(buffer, my_strlen(buffer)); // Use my_strlen instead of strlen
}
void vga_init(void) {
// Clear the screen
vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY));
// Set the cursor to the top-left corner
vga_set_cursor_position(0, 0);
// Optionally, set cursor blinking and shape
vga_set_cursor_blinking(true);
vga_set_cursor_shape(0x20, 0x3F); // Default shape
}

View File

@@ -2,7 +2,11 @@
#define VGA_H #define VGA_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h> // For va_list, va_start, etc.
// VGA color definitions
typedef enum { typedef enum {
VGA_COLOR_BLACK = 0, VGA_COLOR_BLACK = 0,
VGA_COLOR_BLUE = 1, VGA_COLOR_BLUE = 1,
@@ -22,7 +26,28 @@ typedef enum {
VGA_COLOR_WHITE = 15, VGA_COLOR_WHITE = 15,
} vga_color; } vga_color;
// VGA port addresses
typedef enum {
VGA_PORT_INDEX = 0x3D4, // Index register for VGA
VGA_PORT_DATA = 0x3D5 // Data register for VGA
} vga_io_port_t;
// Function prototypes
uint8_t vga_entry_color(vga_color fg, vga_color bg); uint8_t vga_entry_color(vga_color fg, vga_color bg);
uint16_t vga_entry(unsigned char uc, uint8_t color); uint16_t vga_entry(unsigned char uc, uint8_t color);
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y);
void vga_clear(uint8_t color);
void vga_write_string(const char* data, size_t size);
void vga_scroll(void);
void vga_set_cursor_position(size_t x, size_t y);
size_t vga_get_cursor_position(void);
void vga_set_cursor_blinking(bool enable);
void vga_set_cursor_shape(uint8_t start, uint8_t end);
void vga_set_cursor_color(uint8_t color);
void vga_set_cursor_blink_rate(uint8_t rate);
void vga_printf(const char* format, ...);
#endif #endif

14
klibc/include/stdarg.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef CLASSICOS_KLIBC_STDARG_H
#define CLASSICOS_KLIBC_STDARG_H
typedef __builtin_va_list va_list;
#ifndef va_start
#define va_start(ap, param) __builtin_va_start(ap, param)
#endif
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
#define va_copy(dest, src) __builtin_va_copy(dest, src)
#endif // CLASSICOS_KLIBC_STDARG_H

12
klibc/include/stdbool.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef CLASSICOS_KLIBC_STDBOOL_H
#define CLASSICOS_KLIBC_STDBOOL_H
#ifndef __cplusplus
#define bool _Bool
#define true 1
#define false 0
#endif
#define __bool_true_false_are_defined 1
#endif // CLASSICOS_KLIBC_STDBOOL_H

10
klibc/include/stddef.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef CLASSICOS_KLIBC_STDDEF_H
#define CLASSICOS_KLIBC_STDDEF_H
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
#undef NULL
#define NULL ((void*)0)
#endif // CLASSICOS_KLIBC_STDDEF_H

16
klibc/include/stdint.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef CLASSICOS_KLIBC_STDINT_H
#define CLASSICOS_KLIBC_STDINT_H
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
typedef long long int int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long int uint64_t;
typedef unsigned int uintptr_t;
#endif // CLASSICOS_KLIBC_STDINT_H

4
klibc/include/stdio.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef CLASSICOS_KLIBC_STDIO_H
#define CLASSICOS_KLIBC_STDIO_H
#endif // CLASSICOS_KLIBC_STDIO_H

4
klibc/include/stdlib.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef CLASSICOS_KLIBC_STDLIB_H
#define CLASSICOS_KLIBC_STDLIB_H
#endif // CLASSICOS_KLIBC_STDLIB_H

14
klibc/include/string.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef CLASSICOS_KLIBC_STRING_H
#define CLASSICOS_KLIBC_STRING_H
#include <stddef.h>
extern int memcmp(const void* s1, const void* s2, size_t n);
extern void* memmove(void* dst, const void* src, size_t n);
extern void* memcpy(void* dst, const void* src, size_t n);
extern void* memset(void* dst, int c, size_t n);
extern size_t strlen(const char* s);
extern int strcmp(const char* s1, const char* s2);
#endif // CLASSICOS_KLIBC_STRING_H

107
klibc/src/string.c Normal file
View File

@@ -0,0 +1,107 @@
#include <string.h>
int memcmp(const void* s1, const void* s2, size_t n) {
const unsigned char* c1 = s1;
const unsigned char* c2 = s2;
int d = 0;
while (n--) {
d = (int)*c1++ - (int)*c2++;
if (d) break;
}
return d;
}
void* memmove(void* dst, const void* src, size_t n) {
const char* p = src;
char* q = dst;
#if defined(__i386__) || defined(__x86_64__)
if (q < p) {
__asm__ volatile("cld; rep; movsb" : "+c"(n), "+S"(p), "+D"(q));
} else {
p += (n - 1);
q += (n - 1);
__asm__ volatile("std; rep; movsb; cld" : "+c"(n), "+S"(p), "+D"(q));
}
#else
if (q < p) {
while (n--) {
*q++ = *p++;
}
} else {
p += n;
q += n;
while (n--) {
*--q = *--p;
}
}
#endif
return dst;
}
void* memcpy(void* dst, const void* src, size_t n) {
const char* p = src;
char* q = dst;
#if defined(__i386__)
size_t nl = n >> 2;
__asm__ volatile("cld ; rep ; movsl ; movl %3,%0 ; rep ; movsb"
: "+c"(nl), "+S"(p), "+D"(q)
: "r"(n & 3));
#elif defined(__x86_64__)
size_t nq = n >> 3;
__asm__ volatile("cld ; rep ; movsq ; movl %3,%%ecx ; rep ; movsb"
: "+c"(nq), "+S"(p), "+D"(q)
: "r"((uint32_t)(n & 7)));
#else
while (n--) {
*q++ = *p++;
}
#endif
return dst;
}
void* memset(void* dst, int c, size_t n) {
char* q = dst;
#if defined(__i386__)
size_t nl = n >> 2;
__asm__ volatile("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb"
: "+c"(nl), "+D"(q)
: "a"((unsigned char)c * 0x01010101U), "r"(n & 3));
#elif defined(__x86_64__)
size_t nq = n >> 3;
__asm__ volatile("cld ; rep ; stosq ; movl %3,%%ecx ; rep ; stosb"
: "+c"(nq), "+D"(q)
: "a"((unsigned char)c * 0x0101010101010101U),
"r"((uint32_t)n & 7));
#else
while (n--) {
*q++ = c;
}
#endif
return dst;
}
size_t strlen(const char* s) {
const char* ss = s;
while (*ss) ss++;
return ss - s;
}
int strcmp(const char* s1, const char* s2) {
const unsigned char* c1 = (const unsigned char*)s1;
const unsigned char* c2 = (const unsigned char*)s2;
unsigned char ch;
int d = 0;
while (1) {
d = (int)(ch = *c1++) - (int)*c2++;
if (d || !ch) break;
}
return d;
}