mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2025-12-06 13:35:26 -08:00
Compare commits
19 Commits
9aa1b85ca0
...
3036ee3dfd
| Author | SHA1 | Date | |
|---|---|---|---|
| 3036ee3dfd | |||
| d5906d72de | |||
| 2ab0efdee1 | |||
| 0e011c1682 | |||
| eccf9d7d7c | |||
|
|
62fe09d80d | ||
| f1b0670a15 | |||
| 48fdb348ca | |||
| 6dbd08c808 | |||
| 9ac3a2b862 | |||
| 95f0507c24 | |||
| 70539f72b8 | |||
| 1b046776e0 | |||
| 2609f52dd6 | |||
| f2e75c5142 | |||
| 056d3eb374 | |||
| 98f0f58ce4 | |||
| 7d9d0aeee3 | |||
| 8e5dff4271 |
82
Makefile
82
Makefile
@@ -1,64 +1,58 @@
|
||||
AS = nasm
|
||||
ASFLAGS = -f elf32 -g -F dwarf
|
||||
CC = gcc
|
||||
CFLAGS = -std=c11 -m32 -ffreestanding -c -fno-stack-protector -fno-pie
|
||||
LD = ld
|
||||
QEMU = qemu-system-i386
|
||||
IMG_SIZE = 1440k
|
||||
QEMU= qemu-system-i386
|
||||
|
||||
BUILD_DIR = build
|
||||
|
||||
BOOT_SRC = bootloader/boot.asm
|
||||
BOOT_OBJ = $(BUILD_DIR)/boot.o
|
||||
BOOT_ELF = $(BUILD_DIR)/boot.elf
|
||||
BOOT_IMG = $(BUILD_DIR)/boot.img
|
||||
DISK_IMG = $(BUILD_DIR)/disk.img
|
||||
STAGE2_SIZE = 2048
|
||||
|
||||
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))
|
||||
KERNEL_OBJ += $(BUILD_DIR)/boot1.o
|
||||
KERNEL_ELF = $(BUILD_DIR)/kernel.elf
|
||||
KERNEL_BIN = $(BUILD_DIR)/kernel.bin
|
||||
|
||||
DISK_IMG = $(BUILD_DIR)/disk.img
|
||||
all: $(DISK_IMG)
|
||||
|
||||
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_IMG)
|
||||
.PHONY: stage1 stage2 kernel run gdb clean
|
||||
stage1: $(BUILD_DIR)
|
||||
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/$@.o bootloader/$@.asm
|
||||
$(LD) -Ttext=0x7c00 -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.o
|
||||
objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
|
||||
|
||||
# NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow.
|
||||
# Alternatively, convey the final stage2 size through other means to stage1.
|
||||
stage2: $(BUILD_DIR)
|
||||
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
|
||||
$(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c
|
||||
$(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o
|
||||
objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
|
||||
truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin
|
||||
|
||||
$(BUILD_DIR)/asm_%.o: kernel/%.asm
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: kernel/%.c
|
||||
$(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $@ $<
|
||||
|
||||
kernel: $(KERNEL_OBJ) | $(BUILD_DIR)
|
||||
$(LD) -melf_i386 -Tbootloader/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ)
|
||||
|
||||
$(DISK_IMG): stage1 stage2 kernel
|
||||
dd if=$(BUILD_DIR)/stage1.bin of=$@
|
||||
dd if=$(BUILD_DIR)/stage2.bin of=$@ oflag=append conv=notrunc
|
||||
dd if=$(BUILD_DIR)/kernel.elf of=$@ oflag=append conv=notrunc
|
||||
truncate -s 1M $@
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(BOOT_OBJ): $(BOOT_SRC) | $(BUILD_DIR)
|
||||
$(AS) -f elf32 -g -F dwarf -o $@ $<
|
||||
run:
|
||||
qemu-system-i386 -s -S $(DISK_IMG)
|
||||
|
||||
$(BOOT_ELF): $(BOOT_OBJ)
|
||||
$(LD) -Ttext=0x7c00 -melf_i386 -o $@ $<
|
||||
|
||||
$(BOOT_IMG): $(BOOT_ELF)
|
||||
objcopy -O binary $< $@
|
||||
truncate -s $(IMG_SIZE) $@
|
||||
|
||||
$(BUILD_DIR)/boot1.o: bootloader/boot1.asm
|
||||
$(AS) -f elf32 -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/asm_%.o: kernel/%.asm
|
||||
$(AS) -f elf32 -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: kernel/%.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
$(KERNEL_BIN): $(KERNEL_OBJ) | $(BUILD_DIR)
|
||||
$(LD) -melf_i386 --oformat binary -T bootloader/linker.ld -o $@ $(KERNEL_OBJ)
|
||||
|
||||
$(DISK_IMG): $(BOOT_IMG) $(KERNEL_BIN)
|
||||
dd if=$(BOOT_IMG) of=$@ bs=512 seek=4
|
||||
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200
|
||||
|
||||
run: $(DISK_IMG)
|
||||
$(QEMU) -drive file=$<,format=raw,if=floppy
|
||||
|
||||
.PHONY: stage1 clean
|
||||
|
||||
stage1: $(BOOT_IMG)
|
||||
gdb:
|
||||
gdb -x gdb.txt
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
27
bootloader/README.md
Normal file
27
bootloader/README.md
Normal 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
|
||||
@@ -1,203 +0,0 @@
|
||||
; ==============================================================================
|
||||
; boot1.asm - Second Stage Bootloader (Fixed Real Mode Transition)
|
||||
; ==============================================================================
|
||||
|
||||
[BITS 32]
|
||||
global _start
|
||||
extern kmain
|
||||
|
||||
_start:
|
||||
; Set up segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Stack (must be identity-mapped)
|
||||
mov esp, 0x90000
|
||||
|
||||
; CPU Feature Detection: check CPUID support
|
||||
pushfd ; Save flags
|
||||
pop eax
|
||||
mov ecx, eax
|
||||
xor eax, 1 << 21 ; Flip ID bit
|
||||
push eax
|
||||
popfd
|
||||
pushfd
|
||||
pop eax
|
||||
xor eax, ecx
|
||||
jz .no_cpuid ; CPUID unsupported if no change
|
||||
|
||||
; CPUID supported, verify features
|
||||
mov eax, 1
|
||||
cpuid
|
||||
; Check for paging support (bit 31 of edx)
|
||||
test edx, 1 << 31
|
||||
jz .no_paging_support
|
||||
|
||||
; Additional CPU feature checks could be added here
|
||||
|
||||
jmp .cpuid_check_done
|
||||
|
||||
.no_cpuid:
|
||||
mov si, no_cpuid_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
.no_paging_support:
|
||||
mov si, no_paging_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
.cpuid_check_done:
|
||||
|
||||
; Temporarily switch back to real mode
|
||||
cli
|
||||
mov eax, cr0
|
||||
and eax, 0x7FFFFFFE ; Clear PE & PG bits
|
||||
mov cr0, eax
|
||||
jmp 0x18:real_mode_entry
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 16]
|
||||
real_mode_entry:
|
||||
; Real mode for BIOS access (E820, VESA)
|
||||
xor ax, ax
|
||||
mov es, ax
|
||||
|
||||
; VESA call
|
||||
mov di, VbeControllerInfo
|
||||
mov ax, 0x4F00
|
||||
int 0x10
|
||||
jc vesa_error
|
||||
|
||||
; E820 memory map
|
||||
xor ebx, ebx
|
||||
mov edx, 0x534D4150
|
||||
mov di, MemoryMapBuffer
|
||||
mov [MemoryMapEntries], dword 0
|
||||
|
||||
.e820_loop:
|
||||
mov eax, 0xE820
|
||||
mov ecx, 24
|
||||
int 0x15
|
||||
jc e820_error
|
||||
add di, 24
|
||||
inc dword [MemoryMapEntries]
|
||||
test ebx, ebx
|
||||
jnz .e820_loop
|
||||
jmp e820_done
|
||||
|
||||
e820_error:
|
||||
mov si, e820_error_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
vesa_error:
|
||||
mov si, vesa_error_msg
|
||||
call print_string_16
|
||||
|
||||
; Fallback: set VGA text mode 3 and continue
|
||||
mov ah, 0x00 ; BIOS Set Video Mode function
|
||||
mov al, 0x03 ; VGA 80x25 text mode
|
||||
int 0x10
|
||||
|
||||
; Clear screen
|
||||
mov ah, 0x06 ; Scroll up function
|
||||
mov al, 0 ; Clear entire screen
|
||||
mov bh, 0x07 ; Text attribute (gray on black)
|
||||
mov cx, 0 ; Upper-left corner
|
||||
mov dx, 0x184F ; Lower-right corner
|
||||
int 0x10
|
||||
|
||||
jmp e820_done ; Continue booting without VESA graphics
|
||||
|
||||
e820_done:
|
||||
; Back to protected mode
|
||||
cli
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
jmp 0x08:protected_entry
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 16]
|
||||
print_string_16:
|
||||
.loop:
|
||||
lodsb
|
||||
or al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
int 0x10
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
e820_error_msg db "E820 Failed!", 0
|
||||
vesa_error_msg db "VESA Failed!", 0
|
||||
no_cpuid_msg db "No CPUID support detected!", 0
|
||||
no_paging_msg db "CPU lacks paging support!", 0
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 32]
|
||||
protected_entry:
|
||||
; Paging setup
|
||||
xor eax, eax
|
||||
mov edi, page_directory
|
||||
mov ecx, 1024
|
||||
rep stosd
|
||||
mov edi, page_table
|
||||
rep stosd
|
||||
mov eax, page_table
|
||||
or eax, 0x3
|
||||
mov [page_directory], eax
|
||||
mov ecx, 1024
|
||||
mov edi, page_table
|
||||
mov eax, 0x00000003
|
||||
.fill_pages:
|
||||
mov [edi], eax
|
||||
add eax, 0x1000
|
||||
add edi, 4
|
||||
loop .fill_pages
|
||||
|
||||
mov eax, page_directory
|
||||
mov cr3, eax
|
||||
mov eax, cr0
|
||||
or eax, 0x80000000
|
||||
mov cr0, eax
|
||||
|
||||
jmp kmain
|
||||
|
||||
halt:
|
||||
cli
|
||||
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Data buffers and variables must be appropriately defined in your data section
|
||||
MemoryMapBuffer times 128 db 0 ; 128*24 bytes reserved for E820 memory map (adjust size as needed)
|
||||
MemoryMapEntries dd 0
|
||||
VbeControllerInfo times 512 db 0 ; Buffer for VESA controller info (adjust size as needed)
|
||||
|
||||
; Define page directory and page table aligned as needed (in your data section)
|
||||
align 4096
|
||||
page_directory times 1024 dd 0
|
||||
align 4096
|
||||
page_table times 1024 dd 0
|
||||
|
||||
%assign pad_size 4096
|
||||
%ifdef __SIZE__
|
||||
%define size_current __SIZE__
|
||||
%else
|
||||
%define size_current ($ - $$)
|
||||
%endif
|
||||
|
||||
%if size_current < pad_size
|
||||
times pad_size - size_current db 0
|
||||
%endif
|
||||
|
||||
checksum_byte db 0
|
||||
@@ -2,10 +2,16 @@
|
||||
; boot.asm - First Stage Bootloader (CHS Based)
|
||||
; ==============================================================================
|
||||
|
||||
[BITS 16]
|
||||
; [ORG 0x7C00]
|
||||
; 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
|
||||
|
||||
start:
|
||||
[BITS 16]
|
||||
global _start
|
||||
|
||||
_start:
|
||||
cli ; Disable interrupts
|
||||
|
||||
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
|
||||
@@ -20,29 +26,16 @@ start:
|
||||
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, 4 ; Number of sectors to read
|
||||
mov al, s2_nsect ; Number of sectors to read
|
||||
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
|
||||
call read_chs
|
||||
|
||||
; Load kernel to 0x100000 (1 MB)
|
||||
mov ax, 0xffff
|
||||
mov es, ax ; Set ES for destination address (0xffff << 4 == 0xffff0)
|
||||
mov ax, 5 ; LBA of kernel start (boot1 is 4 sectors: LBA 1–4 → kernel at LBA 5)
|
||||
call lba_to_chs
|
||||
mov al, 16 ; Number of sectors for kernel
|
||||
mov bx, 0x10 ; Destination address offset (0xffff:0x10 = 0xffff << 4 + 0x10 = 0x100000)
|
||||
call read_chs
|
||||
jc disk_error
|
||||
|
||||
; Memory Validation: Verify checksum of second stage bootloader
|
||||
mov si, 0x7E00 ; Start of second stage
|
||||
mov cx, 512 * 4 ; Size in bytes (adjust if more sectors loaded)
|
||||
call verify_checksum
|
||||
jc disk_error ; Jump if checksum fails
|
||||
|
||||
; Enable A20 line
|
||||
call enable_a20
|
||||
jc a20_error ; Jump if A20 enable fails
|
||||
@@ -95,17 +88,32 @@ verify_checksum:
|
||||
pop ax
|
||||
ret
|
||||
|
||||
get_disk_params:
|
||||
mov ah, 08h ; BIOS: Get Drive Parameters
|
||||
int 13h
|
||||
; TODO: error checking
|
||||
|
||||
; CL bits 0–5 contain sectors per track
|
||||
mov al, cl
|
||||
and al, 3Fh ; mask bits 0–5
|
||||
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)
|
||||
; SI = destination offset (Segment:ES already set)
|
||||
; Inputs:
|
||||
; AL = sector count
|
||||
; CH = cylinder
|
||||
; DH = head
|
||||
; CL = sector (1–63, with top 2 bits as high cylinder bits)
|
||||
; SI = destination offset (segment ES must be set)
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Convert LBA to CHS
|
||||
@@ -117,36 +125,29 @@ verify_checksum:
|
||||
; CL = sector (1-63, top 2 bits are upper cylinder bits)
|
||||
|
||||
lba_to_chs:
|
||||
pusha
|
||||
|
||||
; Sector
|
||||
xor dx, dx
|
||||
mov bx, [sectors_per_track]
|
||||
div bx ; AX = LBA / sectors_per_track, DX = remainder (sector number)
|
||||
mov si, ax ; SI = temp quotient (track index)
|
||||
mov cx, [heads_per_cylinder]
|
||||
xor dx, dx
|
||||
div cx ; AX = cylinder, DX = head
|
||||
mov ch, al ; CH = cylinder low byte
|
||||
mov dh, dl ; DH = head
|
||||
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
|
||||
|
||||
; Now take sector number from earlier remainder
|
||||
mov cx, si ; Copy track index to CX to access CL
|
||||
and cl, 0x3F ; Mask to 6 bits (sector number)
|
||||
inc cl ; Sector numbers are 1-based
|
||||
; 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
|
||||
|
||||
; Insert upper 2 bits of cylinder into CL
|
||||
mov ah, al ; AH = cylinder again
|
||||
and ah, 0xC0 ; Get top 2 bits of cylinder
|
||||
or cl, ah ; OR them into sector byte
|
||||
|
||||
popa
|
||||
; Cylinder
|
||||
mov ch, al ; take the quotient, cylinder is in ch
|
||||
ret
|
||||
|
||||
read_chs:
|
||||
pusha
|
||||
push dx
|
||||
|
||||
mov cx, 5
|
||||
.retry:
|
||||
mov ah, 0x02 ; BIOS: Read sectors
|
||||
mov dl, [bootdev] ; Boot device
|
||||
@@ -262,7 +263,7 @@ switch_to_pm:
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
jmp 0x08:0x7E00
|
||||
jmp 0x08:0x7E00 ; jump to S2
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
print_string_16:
|
||||
@@ -284,8 +285,8 @@ halt:
|
||||
hlt
|
||||
|
||||
bootdev db 0
|
||||
sectors_per_track dw 63
|
||||
heads_per_cylinder dw 255
|
||||
sectors_per_track dw 0
|
||||
heads_per_cylinder dw 0
|
||||
|
||||
times 510 - ($ - $$) db 0
|
||||
dw 0xAA55
|
||||
98
bootloader/stage2.asm
Normal file
98
bootloader/stage2.asm
Normal file
@@ -0,0 +1,98 @@
|
||||
[BITS 32]
|
||||
global _start
|
||||
global ata_lba_read
|
||||
|
||||
extern load_kernel
|
||||
|
||||
_start:
|
||||
; Set up segments
|
||||
; Data segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Code segment
|
||||
mov ax, 0x08
|
||||
mov cs, ax
|
||||
|
||||
; Stack (must be identity-mapped)
|
||||
mov esp, 0x90000
|
||||
|
||||
call load_kernel
|
||||
|
||||
jmp eax
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; ATA read sectors (LBA mode)
|
||||
;
|
||||
; sysv32 abi signature:
|
||||
; void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr);
|
||||
; ----------------------------------------------------------------------------
|
||||
ata_lba_read:
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
push ebx
|
||||
push ecx
|
||||
push edx
|
||||
push edi
|
||||
|
||||
mov eax, [ebp+8] ; arg #1 = LBA
|
||||
mov cl, [ebp+12] ; arg #2 = # of sectors
|
||||
mov edi, [ebp+16] ; arg #3 = buffer address
|
||||
and eax, 0x0FFFFFFF
|
||||
|
||||
mov ebx, eax ; Save LBA in RBX
|
||||
|
||||
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
|
||||
shr eax, 24 ; Get bit 24 - 27 in al
|
||||
or al, 11100000b ; Set bit 6 in al for LBA mode
|
||||
out dx, al
|
||||
|
||||
mov edx, 0x01F2 ; Port to send number of sectors
|
||||
mov al, cl ; Get number of sectors from CL
|
||||
out dx, al
|
||||
|
||||
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
|
||||
mov eax, ebx ; Get LBA from EBX
|
||||
out dx, al
|
||||
|
||||
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
|
||||
mov eax, ebx ; Get LBA from EBX
|
||||
shr eax, 8 ; Get bit 8 - 15 in AL
|
||||
out dx, al
|
||||
|
||||
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
|
||||
mov eax, ebx ; Get LBA from EBX
|
||||
shr eax, 16 ; Get bit 16 - 23 in AL
|
||||
out dx, al
|
||||
|
||||
mov edx, 0x1F7 ; Command port
|
||||
mov al, 0x20 ; Read with retry.
|
||||
out dx, al
|
||||
|
||||
mov bl, cl ; Save # of sectors in BL
|
||||
|
||||
.wait_drq:
|
||||
mov edx, 0x1F7
|
||||
.do_wait_drq:
|
||||
in al, dx
|
||||
test al, 8 ; the sector buffer requires servicing.
|
||||
jz .do_wait_drq ; keep polling until the sector buffer is ready.
|
||||
|
||||
mov edx, 0x1F0 ; Data port, in and out
|
||||
mov ecx, 256
|
||||
rep insw ; in to [RDI]
|
||||
|
||||
dec bl ; are we...
|
||||
jnz .wait_drq ; ...done?
|
||||
|
||||
pop edi
|
||||
pop edx
|
||||
pop ecx
|
||||
pop ebx
|
||||
pop ebp
|
||||
ret
|
||||
14
bootloader/stage2.ld
Normal file
14
bootloader/stage2.ld
Normal file
@@ -0,0 +1,14 @@
|
||||
SECTIONS {
|
||||
. = 0x7e00;
|
||||
|
||||
.text : { *(.text*) }
|
||||
.rodata : { *(.rodata*) }
|
||||
.data : { *(.data*) }
|
||||
|
||||
.bss : {
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
read_buf = .;
|
||||
}
|
||||
118
bootloader/stage2_load.c
Normal file
118
bootloader/stage2_load.c
Normal file
@@ -0,0 +1,118 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// ELF Ident indexes
|
||||
#define EI_NIDENT 16
|
||||
|
||||
// Program header types
|
||||
#define PT_NULL 0
|
||||
#define PT_LOAD 1
|
||||
|
||||
// ELF Header (32-bit)
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry; // Entry point
|
||||
uint32_t e_phoff; // Program header table offset
|
||||
uint32_t e_shoff; // Section header table offset
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} __attribute__((packed)) Elf32_Ehdr;
|
||||
|
||||
// Program Header (32-bit)
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
} __attribute__((packed)) Elf32_Phdr;
|
||||
|
||||
// Load an ELF executable into memory.
|
||||
static int elf_load(const void* data, void (*load_segment)(uint8_t *vaddr, uint32_t src, uint32_t size)) {
|
||||
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
|
||||
const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff);
|
||||
|
||||
for (int i = 0; i < header->e_phnum; i++) {
|
||||
if (ph[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
uint32_t offset = ph[i].p_offset;
|
||||
uint32_t vaddr = ph[i].p_vaddr;
|
||||
uint32_t filesz = ph[i].p_filesz;
|
||||
uint32_t memsz = ph[i].p_memsz;
|
||||
|
||||
// Copy data segment
|
||||
//load_segment((uint8_t *)vaddr, offset, filesz);
|
||||
load_segment((uint8_t *)vaddr, offset, filesz);
|
||||
|
||||
// Zero remaining BSS (if any)
|
||||
if (memsz > filesz) {
|
||||
uint8_t* bss_start = (uint8_t*)(vaddr + filesz);
|
||||
for (uint32_t j = 0; j < memsz - filesz; j++) {
|
||||
bss_start[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return header->e_entry;
|
||||
}
|
||||
|
||||
#define KERN_START_SECT 5
|
||||
#define MAX(a, b) ((a)>(b) ? (a) : (b))
|
||||
|
||||
extern void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr);
|
||||
extern uint8_t read_buf[];
|
||||
|
||||
static uint32_t
|
||||
total_header_size(const Elf32_Ehdr *header) {
|
||||
uint32_t phend = header->e_phoff + header->e_phentsize*header->e_phnum;
|
||||
|
||||
// Align to 512
|
||||
return (phend + 511) & ~511;
|
||||
}
|
||||
|
||||
static void read_sectors(uint8_t *vaddr, uint32_t offset, uint32_t size) {
|
||||
// # of sectors to read
|
||||
uint32_t rem_nsect = ((size + 511) & ~511) / 512;
|
||||
|
||||
// Current lba address, offset by the first sector already read
|
||||
uint32_t lba = KERN_START_SECT + offset / 512;
|
||||
|
||||
// Max 255 sectors at a time
|
||||
while (rem_nsect) {
|
||||
uint8_t nsect = rem_nsect > 255 ? 255 : rem_nsect;
|
||||
ata_lba_read(lba, nsect, vaddr);
|
||||
|
||||
vaddr += nsect * 512;
|
||||
rem_nsect -= nsect;
|
||||
lba += nsect;
|
||||
}
|
||||
}
|
||||
|
||||
void *load_kernel(void) {
|
||||
// Read the first sector
|
||||
ata_lba_read(KERN_START_SECT, 1, read_buf);
|
||||
|
||||
const Elf32_Ehdr* header = (const Elf32_Ehdr*)read_buf;
|
||||
|
||||
// Remaining data size, subtract the first 512B already read
|
||||
uint32_t rem = total_header_size(header) - 512;
|
||||
|
||||
// Read the rest if necessary
|
||||
if (rem)
|
||||
read_sectors(read_buf+512, 512, rem);
|
||||
|
||||
elf_load(read_buf, read_sectors);
|
||||
|
||||
return (void *)header->e_entry;
|
||||
}
|
||||
6
gdb.txt
Normal file
6
gdb.txt
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
55
kernel/irq.c
55
kernel/irq.c
@@ -1,5 +1,56 @@
|
||||
#include "irq.h"
|
||||
#include "io.h"
|
||||
#include "isr.h"
|
||||
|
||||
void irq_init() {
|
||||
// IRQ initialization code
|
||||
#define PIC1_CMD 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_CMD 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef IRQ_H
|
||||
#define IRQ_H
|
||||
|
||||
void irq_init();
|
||||
void irq_remap(void);
|
||||
void irq_install(void);
|
||||
void irq_handler(uint32_t int_num);
|
||||
|
||||
#endif // IRQ_H
|
||||
#endif
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
#include "terminal.h"
|
||||
|
||||
#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;
|
||||
|
||||
// Basic US QWERTY keymap (scancode to ASCII)
|
||||
@@ -21,39 +25,41 @@ static const char scancode_map[128] = {
|
||||
|
||||
// Interrupt handler for IRQ1
|
||||
void keyboard_callback(void) {
|
||||
uint8_t scancode = inb(0x60);
|
||||
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
|
||||
|
||||
if (scancode & 0x80) return; // Ignore key release
|
||||
|
||||
// Only handle key press (ignore key release)
|
||||
if (!(scancode & 0x80)) {
|
||||
char c = scancode_map[scancode];
|
||||
if (c && buffer_index < sizeof(key_buffer) - 1) {
|
||||
key_buffer[buffer_index++] = c;
|
||||
if (!c) return;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Send End of Interrupt (EOI) to the PIC
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
|
||||
|
||||
void keyboard_init() {
|
||||
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
|
||||
}
|
||||
|
||||
// Blocking read (returns one char)
|
||||
char keyboard_get_char() {
|
||||
while (buffer_index == 0); // Busy wait
|
||||
char keyboard_get_char(void) {
|
||||
while (buffer_count == 0) {
|
||||
__asm__ __volatile__("hlt"); // Better than busy loop
|
||||
}
|
||||
|
||||
char c;
|
||||
__asm__ __volatile__("cli");
|
||||
c = key_buffer[0];
|
||||
for (uint8_t i = 1; i < buffer_index; i++) {
|
||||
key_buffer[i - 1] = key_buffer[i];
|
||||
}
|
||||
buffer_index--;
|
||||
c = key_buffer[buffer_tail];
|
||||
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
|
||||
buffer_count--;
|
||||
__asm__ __volatile__("sti");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "timer.h"
|
||||
#include "utils.h"
|
||||
#include "keyboard.h"
|
||||
#include "irq.h"
|
||||
|
||||
#define LPT1 0x378
|
||||
|
||||
@@ -41,6 +42,9 @@ void kmain(void) {
|
||||
idt_init();
|
||||
serial_write("IDT initialized.\n");
|
||||
|
||||
irq_install();
|
||||
__asm__ __volatile__ ("sti");
|
||||
|
||||
terminal_write("Enabling paging...\n");
|
||||
paging_init();
|
||||
serial_write("Paging initialized.\n");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "kmalloc.h"
|
||||
#include "terminal.h" // Optional: for debug output
|
||||
|
||||
#define HEAP_END 0xC0100000
|
||||
#define HEAP_END 0xC0500000
|
||||
|
||||
static uint32_t current_heap = 0;
|
||||
|
||||
|
||||
138
kernel/memory.c
Normal file
138
kernel/memory.c
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "memory.h"
|
||||
|
||||
/* note: this is a stub, please use care as theres duplicate functions in utils implementation
|
||||
/* --------------------------------------------------------------------- *
|
||||
* Helper: copy a single byte (used by both memcpy and memmove)
|
||||
* --------------------------------------------------------------------- */
|
||||
static inline void byte_copy_forward(uint8_t *dst, const uint8_t *src, size_t n)
|
||||
{
|
||||
while (n--) *dst++ = *src++;
|
||||
}
|
||||
|
||||
static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n)
|
||||
{
|
||||
dst += n; src += n;
|
||||
while (n--) *--dst = *--src;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- *
|
||||
* memcpy – no overlap allowed (behaviour undefined if overlap)
|
||||
* --------------------------------------------------------------------- */
|
||||
void *memcpy(void *restrict dst, const void *restrict src, size_t n)
|
||||
{
|
||||
uint8_t *d = (uint8_t *)dst;
|
||||
const uint8_t *s = (const uint8_t *)src;
|
||||
|
||||
#if defined(MEMORY_OPTIMIZED)
|
||||
/* Align destination to 4-byte boundary */
|
||||
size_t align = (uintptr_t)d & 3U;
|
||||
if (align) {
|
||||
size_t head = 4 - align;
|
||||
if (head > n) head = n;
|
||||
byte_copy_forward(d, s, head);
|
||||
d += head; s += head; n -= head;
|
||||
}
|
||||
|
||||
/* 32-bit word copy – safe because we already aligned dst */
|
||||
{
|
||||
uint32_t *d32 = (uint32_t *)d;
|
||||
const uint32_t *s32 = (const uint32_t *)s;
|
||||
size_t words = n / 4;
|
||||
while (words--) *d32++ = *s32++;
|
||||
d = (uint8_t *)d32;
|
||||
s = (const uint8_t *)s32;
|
||||
n &= 3;
|
||||
}
|
||||
#endif
|
||||
|
||||
byte_copy_forward(d, s, n);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- *
|
||||
* memmove – handles overlapping regions correctly
|
||||
* --------------------------------------------------------------------- */
|
||||
void *memmove(void *dst, const void *src, size_t n)
|
||||
{
|
||||
uint8_t *d = (uint8_t *)dst;
|
||||
const uint8_t *s = (const uint8_t *)src;
|
||||
|
||||
if (n == 0 || dst == src)
|
||||
return dst;
|
||||
|
||||
if (d < s) { /* copy forward */
|
||||
#if defined(MEMORY_OPTIMIZED)
|
||||
/* Same fast path as memcpy when no overlap */
|
||||
size_t align = (uintptr_t)d & 3U;
|
||||
if (align) {
|
||||
size_t head = 4 - align;
|
||||
if (head > n) head = n;
|
||||
byte_copy_forward(d, s, head);
|
||||
d += head; s += head; n -= head;
|
||||
}
|
||||
{
|
||||
uint32_t *d32 = (uint32_t *)d;
|
||||
const uint32_t *s32 = (const uint32_t *)s;
|
||||
size_t words = n / 4;
|
||||
while (words--) *d32++ = *s32++;
|
||||
d = (uint8_t *)d32;
|
||||
s = (const uint8_t *)s32;
|
||||
n &= 3;
|
||||
}
|
||||
#endif
|
||||
byte_copy_forward(d, s, n);
|
||||
} else { /* copy backward */
|
||||
byte_copy_backward(d, s, n);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- *
|
||||
* memcmp – lexicographical compare
|
||||
* --------------------------------------------------------------------- */
|
||||
int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
const uint8_t *a = (const uint8_t *)s1;
|
||||
const uint8_t *b = (const uint8_t *)s2;
|
||||
|
||||
#if defined(MEMORY_OPTIMIZED)
|
||||
/* Align to 4-byte boundary */
|
||||
size_t align = (uintptr_t)a & 3U;
|
||||
if (align && align == ((uintptr_t)b & 3U)) {
|
||||
size_t head = 4 - align;
|
||||
if (head > n) head = n;
|
||||
while (head--) {
|
||||
int diff = *a++ - *b++;
|
||||
if (diff) return diff;
|
||||
}
|
||||
n -= head;
|
||||
}
|
||||
|
||||
{
|
||||
const uint32_t *a32 = (const uint32_t *)a;
|
||||
const uint32_t *b32 = (const uint32_t *)b;
|
||||
size_t words = n / 4;
|
||||
while (words--) {
|
||||
uint32_t va = *a32++, vb = *b32++;
|
||||
if (va != vb) {
|
||||
/* byte-wise fallback for the differing word */
|
||||
const uint8_t *pa = (const uint8_t *)(a32 - 1);
|
||||
const uint8_t *pb = (const uint8_t *)(b32 - 1);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int diff = pa[i] - pb[i];
|
||||
if (diff) return diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
a = (const uint8_t *)a32;
|
||||
b = (const uint8_t *)b32;
|
||||
n &= 3;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (n--) {
|
||||
int diff = *a++ - *b++;
|
||||
if (diff) return diff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
25
kernel/memory.h
Normal file
25
kernel/memory.h
Normal 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 */
|
||||
@@ -3,9 +3,9 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x100000;
|
||||
page_table_entry_t *page_table = (page_table_entry_t *)0x101000;
|
||||
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x102000; // Located right after the page directory
|
||||
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x200000;
|
||||
page_table_entry_t *page_table = (page_table_entry_t *)0x201000;
|
||||
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x202000;
|
||||
|
||||
// Helper function to set up the page directory entry
|
||||
void set_page_directory(page_directory_entry_t *dir) {
|
||||
|
||||
@@ -12,6 +12,7 @@ static uint16_t* const vga_buffer = (uint16_t*) VGA_ADDRESS;
|
||||
static uint8_t cursor_x = 0;
|
||||
static uint8_t cursor_y = 0;
|
||||
static uint8_t current_color = WHITE_ON_BLACK;
|
||||
static uint16_t last_cursor_pos = 0xFFFF;
|
||||
|
||||
void terminal_initialize(void) {
|
||||
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
|
||||
@@ -96,8 +97,10 @@ void terminal_clear(void) {
|
||||
update_cursor();
|
||||
}
|
||||
|
||||
void update_cursor() {
|
||||
void update_cursor(void) {
|
||||
uint16_t pos = cursor_y * VGA_WIDTH + cursor_x;
|
||||
if (pos == last_cursor_pos) return;
|
||||
last_cursor_pos = pos;
|
||||
|
||||
outb(0x3D4, 0x0F);
|
||||
outb(0x3D5, (uint8_t)(pos & 0xFF));
|
||||
|
||||
Reference in New Issue
Block a user