mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2025-06-27 07:25:22 -07:00
new OS
This commit is contained in:
parent
a464e109cb
commit
ecfa54e225
72
.gitignore
vendored
72
.gitignore
vendored
@ -1,73 +1 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# Binaries
|
||||
*.bin
|
||||
|
||||
# Build system
|
||||
Makefile
|
||||
CMakeLists.txt
|
||||
|
||||
# source control and editor
|
||||
.vscode
|
||||
.vscode/
|
||||
.github
|
||||
.github/
|
||||
|
||||
*.patch
|
||||
|
||||
/build
|
||||
build
|
||||
./build
|
||||
build/
|
||||
build/**
|
||||
|
@ -1,49 +0,0 @@
|
||||
cmake_minimum_required(VERSION 4.0.0)
|
||||
project(ClassicOS VERSION 0.0.1 LANGUAGES C ASM SH)
|
||||
|
||||
set(CMAKE_C_COMPILER "gcc")
|
||||
set(CMAKE_CXX_COMPILER "g++")
|
||||
set(CMAKE_ASM_COMPilER "nasm")
|
||||
set(cmake_SH_COMPILER "bash")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Werror")
|
||||
|
||||
# Define the executable name
|
||||
set(EXECUTABLE_NAME "${PROJECT_NAME}")
|
||||
|
||||
# Set the repository URL
|
||||
set(REPOSITORY_URL "https://github.com/gbowne1/ClassicOS/")
|
||||
|
||||
# Add the assembly and C source files
|
||||
add_executable(${EXECUTABLE_NAME}
|
||||
boot.asm
|
||||
kernel.c
|
||||
)
|
||||
|
||||
# Configure compiler flags for assembly
|
||||
set(ASM_FLAGS "-f elf32")
|
||||
set_property(SOURCE boot.asm PROPERTY COMPILE_OPTIONS "${ASM_FLAGS} -o boot.o")
|
||||
|
||||
# Configure compiler flags for C
|
||||
set(C_FLAGS "-m32 -ffreestanding -nostdlib -fno-pie -std=c11")
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES COMPILE_FLAGS "${C_FLAGS}")
|
||||
|
||||
# Link the object files together
|
||||
target_link_libraries(${EXECUTABLE_NAME} PRIVATE boot.o kernel.bin)
|
||||
|
||||
# Specify the output format and entry point
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_FORMAT "binary")
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES ENTRYPOINT "boot")
|
||||
|
||||
# Include the linker script
|
||||
include_directories(linker.ld)
|
||||
|
||||
# Add a custom message
|
||||
message(STATUS "Building ${PROJECT_NAME} version ${PROJECT_VERSION}")
|
||||
message(STATUS "Repository: ${REPOSITORY_URL}")
|
35
Makefile
35
Makefile
@ -0,0 +1,35 @@
|
||||
AS = nasm
|
||||
CC = gcc
|
||||
LD = ld
|
||||
QEMU = qemu-system-i386
|
||||
IMG_SIZE = 1440k
|
||||
|
||||
BOOT_SRC = bootloader/boot.asm
|
||||
BOOT_BIN = build/boot.bin
|
||||
BOOT_IMG = build/boot.img
|
||||
KERNEL_SRC = kernel/kmain.c
|
||||
KERNEL_BIN = build/kernel.bin
|
||||
DISK_IMG = build/disk.img
|
||||
|
||||
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_IMG)
|
||||
|
||||
$(BOOT_BIN): $(BOOT_SRC)
|
||||
$(AS) -f bin -o $@ $<
|
||||
|
||||
$(BOOT_IMG): $(BOOT_BIN)
|
||||
cp $(BOOT_BIN) $@
|
||||
truncate -s $(IMG_SIZE) $@
|
||||
|
||||
$(KERNEL_BIN): $(KERNEL_SRC)
|
||||
$(CC) -ffreestanding -c $< -o build/kernel.o
|
||||
$(LD) -T bootloader/linker.ld -o $@ build/kernel.o
|
||||
|
||||
$(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
|
||||
|
||||
clean:
|
||||
rm -rf build
|
232
README.md
232
README.md
@ -1,211 +1,51 @@
|
||||
# ClassicOS
|
||||
|
||||
This operating system uses standard operating system concepts used in the 32 bit environment. It will eventually be ported to 64 bit including IA64.
|
||||
This ClassicOS operating system, aims to support major hardware and software technology existing from when the first 32 bit systems appeared on the market through the early 2000's and most of those have been listed below.
|
||||
[](https://github.com/gbowne1/ClassicOS/actions)
|
||||
[](LICENSE)
|
||||
[](https://en.wikipedia.org/wiki/IA-32)
|
||||
[](#)
|
||||
|
||||
## Programming
|
||||
> **ClassicOS** is a 32-bit Intel x86 operating system built from scratch using C, NASM, and GCC.
|
||||
> Designed for 386, 486, and Pentium-class CPUs, it runs in protected mode, outputs to VGA text mode and serial ports, and supports floppy/HDD boot with basic FAT support.
|
||||
|
||||
This project uses the C library and Assembly language.
|
||||
---
|
||||
|
||||
## Toolchain
|
||||
## ✨ Features
|
||||
|
||||
GNU Make 4.2.1
|
||||
CMake 3.13.4
|
||||
GNU 8.3.0
|
||||
gcc (Debian 8.3.0-6) 8.3.0
|
||||
nasm 2.14
|
||||
GNU ld (GNU Binutils for Debian) 2.31.1
|
||||
binutils
|
||||
- MBR bootloader at `0x7C00`
|
||||
- Switch to protected mode with GDT
|
||||
- A20 gate enabling
|
||||
- Simple FAT12/FAT16 disk loader stub
|
||||
- VGA text output (`0xB8000`)
|
||||
- Serial COM1 support (`0x3F8`)
|
||||
- Basic kernel (`kmain`) written in C
|
||||
- Makefile-based build system
|
||||
- Bootable floppy image for testing in QEMU
|
||||
|
||||
For testing, QEMU i386 and TigerVNC/VNCViewer on ::1:5900
|
||||
---
|
||||
|
||||
## Development (Team, etc)
|
||||
## ⚙️ Requirements
|
||||
|
||||
This project will use MIT or the GPL license and will be fully open source.
|
||||
You’ll need the following tools installed:
|
||||
|
||||
Ideal situations aside, My goal has always been:
|
||||
- `nasm`
|
||||
- `gcc` (targeting i386)
|
||||
- `ld`
|
||||
- `make`
|
||||
- `qemu-system-i386`
|
||||
|
||||
1-2 person working on bootloader
|
||||
1-2 person working on kernel
|
||||
1-2 person working on drivers
|
||||
1-2 person working on issues/bugs
|
||||
1-2 people working on applications/user-space
|
||||
1-2 people working on Networking
|
||||
1-2 people working on memory issues, stack issues, etc.
|
||||
Optional:
|
||||
- `gdb`
|
||||
- `vncviewer` (TigerVNC or similar)
|
||||
|
||||
at least one person doing hardware and software testing and writing tests in a test framework
|
||||
---
|
||||
|
||||
At least one major bug fix a week
|
||||
At least one minor buf fix a week
|
||||
At least one new feature a month (or more)
|
||||
## 🛠️ Building ClassicOS
|
||||
|
||||
## CPU Processor Support
|
||||
Clone and build:
|
||||
|
||||
This project initially aims to support all 32 bit Intel and AMD processors Including;
|
||||
|
||||
-- Intel --
|
||||
i386 SX and DX Processors
|
||||
i486 SX and DX Processors
|
||||
Pentium Processors (60 to 120MHz)
|
||||
Pentium Pro Processors
|
||||
Pentium II Processors
|
||||
Pentium II Xeon Processors
|
||||
Pentium III Processors
|
||||
Pentium III Xeon Processors
|
||||
early Pentium 4 Processors (Willamette, Prescott, Northwood)
|
||||
|
||||
-- AMD --
|
||||
|
||||
AMD k5
|
||||
AMD K6
|
||||
AMD am386
|
||||
AMD am486
|
||||
Athlon
|
||||
Duron
|
||||
Sempron
|
||||
|
||||
## Device Support
|
||||
|
||||
USB 1.0
|
||||
USB 1.1
|
||||
USB 2.0
|
||||
USB 2.1
|
||||
SATA 1
|
||||
UDMA
|
||||
UltraATA 66/100/133
|
||||
SCSI-1
|
||||
SCSI-2
|
||||
SCSI-3
|
||||
Ultra-2 SCSI
|
||||
Ultra-3 SCSI
|
||||
ESDI
|
||||
MFM/RLL
|
||||
|
||||
## Booting
|
||||
|
||||
BIOS from
|
||||
|
||||
- primary hard disk partition
|
||||
- primary floppy
|
||||
- ISO 9660 CD-ROM, CD-R,CD-RW,
|
||||
- DVD
|
||||
- Removable media (Zip, Jaz, USB, Tape, Syquest, Bernoulli, CF, SmartMedia, SD etc)
|
||||
|
||||
Might eventually support GRUB/GRUB2 and/or UEFI/EFI.
|
||||
|
||||
Include support for AHCI and ACPI
|
||||
|
||||
## Bus Support
|
||||
|
||||
ISA
|
||||
EISA
|
||||
VESA/VESA Local Bus (VLB)
|
||||
PCI
|
||||
PCI-X
|
||||
PCIe 1.0, 1.1, 2.0
|
||||
AGP
|
||||
DIN 41416/NuBUS
|
||||
|
||||
## Hardware support
|
||||
|
||||
This OS aims to support major hardware existing from 1985 to early 2k's.
|
||||
|
||||
## Memory Support
|
||||
|
||||
up to 4GB
|
||||
|
||||
## Features
|
||||
|
||||
Has a GUI
|
||||
Has a IDE
|
||||
Has a text editor
|
||||
Has compilers for compiled lanugages (C, C++, C#, Go, Java, Fortran, Pascal, Objective C, Haskell, ADA, Scala, Rust, Zig, Ocaml, Julia, Dart, Erlang, Elixir)
|
||||
Has a Web Browser
|
||||
Has a shell, tty, console, terminal
|
||||
Has interpreters for interpreted languages like python 2 and python 3, JavaScript, BASIC, PHP, etc.
|
||||
Has a git client
|
||||
Has a video, audio editing and playing suite
|
||||
Has a file browser (in GUI)
|
||||
Has debuggers for languages/compilers, etc that output symbols, etc.
|
||||
|
||||
## Video Support
|
||||
|
||||
-- Modes --
|
||||
CGA
|
||||
EGA
|
||||
VGA
|
||||
SVGA
|
||||
MCGA
|
||||
XGA
|
||||
HGA / Hercules
|
||||
XGA-2
|
||||
SXGA
|
||||
UXGA
|
||||
WXGA
|
||||
8514/a
|
||||
VESA SVGA
|
||||
VESA/VLB
|
||||
AGP (1.0, 2.0, 3.0, 3.5, Pro) - 66MHz - aka AGP 1X, 2X, 4X, 8X
|
||||
PCI Graphics
|
||||
|
||||
-- Resolutions --
|
||||
|
||||
## Networking
|
||||
|
||||
Novell NE1000
|
||||
Novell NE2000
|
||||
|
||||
## Drivers
|
||||
|
||||
-- Video card(s)
|
||||
|
||||
3dFx Voodoo2, Voodoo3 cards
|
||||
NVIDIA NV1
|
||||
NVIDIA Riva 128
|
||||
ATI VGA Wonder
|
||||
NVIDIA GeForce 256
|
||||
NVIDIA GeForce 2 GTS
|
||||
NVIDIA GeForce 3 Ti500
|
||||
ATI Radeon DDR
|
||||
ATI Radeon 9700 Pro
|
||||
ATI Radeon 9800 Pro
|
||||
Matrox Millennium and MGA Millennium
|
||||
Matrox G400
|
||||
Matrox Mystique
|
||||
Matrox G200
|
||||
Matrox G400
|
||||
ATI Mach8
|
||||
ATI Mach32
|
||||
ATI Mach 64
|
||||
ATI 3D Rage
|
||||
ATI Rage Pro
|
||||
ATI Rage 128 Pro
|
||||
ATI Rage Wonder
|
||||
|
||||
-- NIC's (3Com, Intel, etc.)
|
||||
|
||||
Intel EtherExpress Pro/100
|
||||
3Com EtherLink I, II & III cards in the 3c5xx - 3c9xx series model range.
|
||||
Linksys LNE series cards
|
||||
Netgear FA3xx/FA4xx series and GA series cards
|
||||
|
||||
-- Audio
|
||||
|
||||
## Filesystems
|
||||
|
||||
- FAT12
|
||||
- FAT16/FAT16B/FAT16X
|
||||
- FAT32/FAT32X
|
||||
- NTFS
|
||||
- HPFS
|
||||
- HFS / HFS+
|
||||
- ext / ext2 / ext3
|
||||
- exFAT
|
||||
- ZFS
|
||||
- JFS
|
||||
|
||||
## Build
|
||||
|
||||
nasm -f elf32 boot.asm -o boot.o ; gcc -m32 -ffreestanding -nostdlib -fno-pic -fno-pie -std=c11 kernel.c boot.o -o kernel.bin -T linker.ld
|
||||
nasm -f elf32 boot.asm -o boot.o ; gcc -m32 -ffreestanding -nostdlib -fno-pic kernel.c boot.o -o kernel.bin -T linker.ld
|
||||
nasm -f elf32 boot.asm -o boot.o ; gcc -m32 -ffreestanding -nostdlib -fno-pie kernel.c boot.o -o kernel.bin -T linker.ld
|
||||
```bash
|
||||
git clone https://github.com/gbowne1/ClassicOS.git
|
||||
cd ClassicOS
|
||||
make
|
||||
```
|
100
boot.asm
100
boot.asm
@ -1,100 +0,0 @@
|
||||
section .boot align=512
|
||||
bits 16
|
||||
global boot
|
||||
boot:
|
||||
mov ax, 0x2401
|
||||
int 0x15
|
||||
|
||||
mov ax, 0x3
|
||||
int 0x10
|
||||
|
||||
mov [disk],dl
|
||||
|
||||
mov ah, 0x2 ;read sectors
|
||||
mov al, 64 ;sectors to read
|
||||
mov ch, 0 ;cylinder idx
|
||||
mov dh, 0 ;head idx
|
||||
mov cl, 2 ;sector idx
|
||||
mov dl, [disk] ;disk idx
|
||||
mov bx, copy_target;target pointer
|
||||
int 0x13
|
||||
cli
|
||||
lgdt [gdt_pointer]
|
||||
mov eax, cr0
|
||||
or eax,0x1
|
||||
mov cr0, eax
|
||||
mov ax, DATA_SEG
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
jmp CODE_SEG:boot2
|
||||
|
||||
; access(8)
|
||||
; P:present(1) DPL:descriptor_privilege_level(2)
|
||||
; S:descriptor_type(1) E:executable(1) DC:direction/conforming(1)
|
||||
; RW:readable/writable(1) A:accessed(1)
|
||||
; flags(4)
|
||||
; G:granularity(1) DB:size(1) L:long_mode_code(1) Reserved(1)
|
||||
|
||||
gdt_start:
|
||||
dq 0x0000000000000000 ; null descriptor
|
||||
|
||||
gdt_code:
|
||||
dw 0xFFFF ; limit_0_15(16)
|
||||
dw 0x0000 ; base_0_15(16)
|
||||
db 0x00 ; base_16_23(8)
|
||||
db 10011010b ; P(1)=1=present DPL(2)=00=ring0 S(1)=1=non_system E(1)=1=code
|
||||
; DC(1)=0=same_ring RW(1)=1=readable A(1)=0=not_accessed
|
||||
db 11001111b ; G(1)=1=page, DB(1)=1=32b, L(1)=0=non_64b Reserved(1)=0=_
|
||||
; limit_16_19(4)=1111
|
||||
db 0x00 ; base_24_31(8)
|
||||
|
||||
gdt_data:
|
||||
dw 0xFFFF ; limit_0_15(16)
|
||||
dw 0x0000 ; base_0_15(16)
|
||||
db 0x00 ; base_16_23(8)
|
||||
db 10010010b ; P(1)=1=present DPL(2)=00=ring0 S(1)=1=non_system E(1)=0=data
|
||||
; DC(1)=0=grows_up RW(1)=1=rw A(1)=0=not_accessed
|
||||
db 11001111b ; G(1)=1=page DB(1)=1=32b_sp L(1)=0=_ Reserved(1)=0=_
|
||||
; limit_16_19(4)=1111
|
||||
db 0x00 ; base_24_31(8)
|
||||
|
||||
gdt_end:
|
||||
gdt_pointer:
|
||||
dw gdt_end - gdt_start
|
||||
dd gdt_start
|
||||
disk:
|
||||
db 0x0
|
||||
CODE_SEG equ gdt_code - gdt_start
|
||||
DATA_SEG equ gdt_data - gdt_start
|
||||
|
||||
copy_target equ kernel_stack_bottom + 16384
|
||||
hello: db "Hello more than 512 bytes world!!",0
|
||||
|
||||
times 510 - ($-$$) db 0
|
||||
dw 0xaa55
|
||||
boot2:
|
||||
mov esi,hello
|
||||
mov ebx,0xb8000
|
||||
.loop:
|
||||
lodsb
|
||||
or al,al
|
||||
jz halt
|
||||
or eax,0x0F00
|
||||
mov word [ebx], ax
|
||||
add ebx,2
|
||||
jmp .loop
|
||||
halt:
|
||||
mov esp,kernel_stack_top
|
||||
extern kmain
|
||||
call kmain
|
||||
cli
|
||||
hlt
|
||||
|
||||
section .bss
|
||||
align 4
|
||||
kernel_stack_bottom: equ $
|
||||
resb 16384 ; 16 KB
|
||||
kernel_stack_top: equ kernel_stack_bottom + 16384
|
0
bootloader/Makefile
Normal file
0
bootloader/Makefile
Normal file
67
bootloader/boot.asm
Normal file
67
bootloader/boot.asm
Normal file
@ -0,0 +1,67 @@
|
||||
[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
|
17
bootloader/linker.ld
Normal file
17
bootloader/linker.ld
Normal file
@ -0,0 +1,17 @@
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.text : {
|
||||
*(.multiboot)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
.rodata : { *(.rodata*) }
|
||||
.data : { *(.data*) }
|
||||
.bss : {
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
}
|
||||
}
|
44
build.sh
44
build.sh
@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
|
||||
# Function to check if a command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check for required commands
|
||||
for cmd in nasm gcc qemu-system-i386; do
|
||||
if ! command_exists "$cmd"; then
|
||||
echo "Error: $cmd is not installed or not in PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Compile the assembly file
|
||||
echo "Compiling boot.asm..."
|
||||
nasm -f elf32 boot.asm -o boot.o
|
||||
|
||||
# Compile and link the kernel
|
||||
echo "Compiling and linking kernel..."
|
||||
gcc -m32 -ffreestanding -nostdlib -fno-pic -fno-pie kernel.c boot.o -o kernel.bin -T linker.ld
|
||||
|
||||
# Check if compilation was successful
|
||||
if [ -f kernel.bin ]; then
|
||||
echo "Build successful. kernel.bin created."
|
||||
|
||||
# Ask user if they want to run QEMU
|
||||
read -p "Do you want to run QEMU now? (y/n) " -n 1 -r
|
||||
echo # Move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Running QEMU..."
|
||||
qemu-system-i386 -enable-kvm -net none -fda kernel.bin
|
||||
else
|
||||
echo "QEMU not started. You can run it later with:"
|
||||
echo "qemu-system-i386 -enable-kvm -net none -fda kernel.bin"
|
||||
fi
|
||||
else
|
||||
echo "Build failed. Check for errors above." >&2
|
||||
exit 1
|
||||
fi
|
8
disk/Makefile
Normal file
8
disk/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
IMG_SIZE = 1440k
|
||||
BOOT_BIN = ../build/boot.bin
|
||||
KERNEL_BIN = ../build/kernel.bin
|
||||
DISK_IMG = ../build/disk.img
|
||||
|
||||
$(DISK_IMG): $(BOOT_BIN) $(KERNEL_BIN)
|
||||
dd if=$(BOOT_BIN) of=$@ bs=512 seek=4
|
||||
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200
|
29
do.sh
29
do.sh
@ -1,29 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# TODO: Check the compiler does x86
|
||||
|
||||
if [ "$1" = "build" ]; then
|
||||
shift
|
||||
|
||||
printf "Assembling bootloader...\n"
|
||||
nasm -f elf32 boot.asm -o boot.o
|
||||
|
||||
printf "Compiling...\n"
|
||||
gcc \
|
||||
-save-temps \
|
||||
-std=c99 -m32 \
|
||||
-fno-pic \
|
||||
-mgeneral-regs-only \
|
||||
-ffreestanding -nostdlib \
|
||||
-Wall -Wextra -Wpedantic \
|
||||
kernel.c boot.o \
|
||||
-o kernel.bin \
|
||||
-T linker.ld
|
||||
fi
|
||||
|
||||
if [ "$1" = "boot" ]; then
|
||||
printf "Booting...\n\n"
|
||||
MACHINE="-machine pc -cpu 486"
|
||||
qemu-system-i386 $MACHINE -net none -serial stdio -drive file=kernel.bin,index=0,if=floppy,format=raw
|
||||
fi
|
488
kernel.c
488
kernel.c
@ -1,488 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool terminate = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val)
|
||||
{
|
||||
__asm__ volatile ("outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory");
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port)
|
||||
{
|
||||
uint8_t ret;
|
||||
__asm__ volatile ("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void io_wait(void)
|
||||
{
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define PORT 0x3f8 // COM1
|
||||
|
||||
void init_serial(void)
|
||||
{
|
||||
// Enable "data available" interrupt.
|
||||
outb(PORT + 1, 0x01);
|
||||
}
|
||||
|
||||
// Use only when there _is_ something to read.
|
||||
char read_serial()
|
||||
{
|
||||
return inb(PORT);
|
||||
}
|
||||
|
||||
int is_transmit_empty()
|
||||
{
|
||||
return inb(PORT + 5) & 0x20;
|
||||
}
|
||||
|
||||
void write_serial(char a)
|
||||
{
|
||||
while (is_transmit_empty() == 0);
|
||||
outb(PORT, a);
|
||||
}
|
||||
|
||||
#undef PORT
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Convert numbers to hexadecimal chars.
|
||||
*/
|
||||
|
||||
char nibble_to_hex(int n)
|
||||
{
|
||||
if (n>=0 && n<10) return (n+48);
|
||||
else if (n>=10 && n<16) return (n+55+32);
|
||||
else return '?';
|
||||
}
|
||||
|
||||
void int_to_hex(char *hex, uint64_t a, size_t size)
|
||||
{
|
||||
int i = 0;
|
||||
int divisor = size << 3;
|
||||
while (divisor != 0) {
|
||||
divisor -= 4;
|
||||
hex[i] = nibble_to_hex((a >> divisor) & 0xF);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void u8_to_hex (char *s, uint8_t a) { int_to_hex(s, a, 1); }
|
||||
void u16_to_hex(char *s, uint16_t a) { int_to_hex(s, a, 2); }
|
||||
void u32_to_hex(char *s, uint32_t a) { int_to_hex(s, a, 4); }
|
||||
void u64_to_hex(char *s, uint64_t a) { int_to_hex(s, a, 8); }
|
||||
|
||||
/*
|
||||
* Logging functions.
|
||||
*/
|
||||
|
||||
// Use serial for log output.
|
||||
#define PUTCHAR write_serial
|
||||
|
||||
// Sends buffer of a given size to the log.
|
||||
void klog(char *buff, int size)
|
||||
{
|
||||
for (int i=0; i<size; ++i) PUTCHAR(buff[i]);
|
||||
}
|
||||
|
||||
// Like klog but also sends a newline character.
|
||||
void klogl(char *buff, int size)
|
||||
{
|
||||
klog(buff, size); PUTCHAR('\n');
|
||||
}
|
||||
|
||||
// Sends a NUL-terminated string to the log (not including the NUL).
|
||||
void klogs(const char *str)
|
||||
{
|
||||
while(*str) PUTCHAR(*str++);
|
||||
}
|
||||
|
||||
#undef PUTCHAR
|
||||
|
||||
// Functions that send integers to the log, in hex (without 0x prefix).
|
||||
#define B hexbuff
|
||||
#define D char B[16] = {0}
|
||||
void klog_u8 (uint8_t a) { D; u8_to_hex (B, a); klog(B, 2) ; }
|
||||
void klog_u16(uint16_t a) { D; u16_to_hex(B, a); klog(B, 4) ; }
|
||||
void klog_u32(uint32_t a) { D; u32_to_hex(B, a); klog(B, 8) ; }
|
||||
void klog_u64(uint64_t a) { D; u64_to_hex(B, a); klog(B, 16); }
|
||||
|
||||
// These send a newline character too.
|
||||
void klogl_u8 (uint8_t a) { D; u8_to_hex (B, a); klogl(B, 2) ; }
|
||||
void klogl_u16(uint16_t a) { D; u16_to_hex(B, a); klogl(B, 4) ; }
|
||||
void klogl_u32(uint32_t a) { D; u32_to_hex(B, a); klogl(B, 8) ; }
|
||||
void klogl_u64(uint64_t a) { D; u64_to_hex(B, a); klogl(B, 16); }
|
||||
#undef D
|
||||
#undef B
|
||||
|
||||
void demo_klog_functions(void)
|
||||
{
|
||||
klogs("\nCheck; one, two, one, two.\n");
|
||||
klogl_u8(0x01);
|
||||
klogl_u16(0x2345);
|
||||
klogl_u32(0x6789abcd);
|
||||
klogl_u64(0xef0123456789abcd);
|
||||
klog_u8(0x01);
|
||||
klog_u16(0x2345);
|
||||
klog_u32(0x6789abcd);
|
||||
klog_u64(0xef0123456789abcd);
|
||||
klogs("\n0123456789abcdef0123456789abcd\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define PIC1 0x20 /* IO base address for master PIC */
|
||||
#define PIC2 0xA0 /* IO base address for slave PIC */
|
||||
#define PIC1_COMMAND PIC1
|
||||
#define PIC1_DATA (PIC1+1)
|
||||
#define PIC2_COMMAND PIC2
|
||||
#define PIC2_DATA (PIC2+1)
|
||||
|
||||
#define ICW1_ICW4 0x01 /* Indicates that ICW4 will be present */
|
||||
#define ICW1_INIT 0x10 /* Initialization - required! */
|
||||
|
||||
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
|
||||
|
||||
void pic_remap(int offset)
|
||||
{
|
||||
uint8_t a1, a2;
|
||||
// save masks
|
||||
a1 = inb(PIC1_DATA); a2 = inb(PIC2_DATA);
|
||||
// starts the initialization sequence (in cascade mode)
|
||||
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait();
|
||||
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait();
|
||||
// ICW2: Master PIC vector offset
|
||||
outb(PIC1_DATA, offset); io_wait();
|
||||
// ICW2: Slave PIC vector offset
|
||||
outb(PIC2_DATA, offset+8); io_wait();
|
||||
// ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
|
||||
outb(PIC1_DATA, 4); io_wait();
|
||||
// ICW3: tell Slave PIC its cascade identity (0000 0010)
|
||||
outb(PIC2_DATA, 2); io_wait();
|
||||
// ICW4: have the PICs use 8086 mode (and not 8080 mode)
|
||||
outb(PIC1_DATA, ICW4_8086); io_wait();
|
||||
outb(PIC2_DATA, ICW4_8086); io_wait();
|
||||
// restore saved masks.
|
||||
outb(PIC1_DATA, a1); outb(PIC2_DATA, a2);
|
||||
}
|
||||
|
||||
void pic_end_of_irq(int irq)
|
||||
{
|
||||
if (irq >= 8) outb(PIC2_COMMAND, 0x20);
|
||||
outb(PIC1_COMMAND, 0x20);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct gdt_descriptor
|
||||
{
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
void sgdt(struct gdt_descriptor *gdtd)
|
||||
{
|
||||
__asm__ volatile ("sgdt %0" : : "m"(*gdtd) : "memory");
|
||||
}
|
||||
|
||||
void klog_info_about_gdt(void)
|
||||
{
|
||||
struct gdt_descriptor gdtd;
|
||||
sgdt(&gdtd);
|
||||
|
||||
klogs("**\n GDT info:\n");
|
||||
klogl_u32(gdtd.base);
|
||||
klogl_u16(gdtd.limit);
|
||||
|
||||
// For each (8 bytes long) entry in the table...
|
||||
for (uint32_t b = gdtd.base; b < gdtd.base + gdtd.limit; b += 8) {
|
||||
// ... log each byte, in hex.
|
||||
for (uint32_t i = b; i < b + 8; ++i) {
|
||||
klog_u8(*(uint8_t*)i);
|
||||
}
|
||||
klogs("\n");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct idt_gate_descriptor
|
||||
{
|
||||
uint16_t offset_1; // offset bits 0..15
|
||||
uint16_t selector; // a code segment selector in GDT or LDT
|
||||
uint8_t zero; // unused, set to 0
|
||||
uint8_t type_attributes; // gate type, dpl, and p fields
|
||||
uint16_t offset_2; // offset bits 16..31
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PIC_REMAP_OFFSET 0x20
|
||||
|
||||
// The IDT begins with gates for the exceptions; we remap the PIC to start at
|
||||
// some point after them. For the PIC IRQs that's 16 gates more. If we define
|
||||
// our software interrupts... we'll have to make room.
|
||||
#define IDT_NUM_GATES (PIC_REMAP_OFFSET + 16)
|
||||
|
||||
__attribute__((aligned(0x10)))
|
||||
struct idt_gate_descriptor idt[IDT_NUM_GATES] = {0};
|
||||
|
||||
static inline void lidt(void)
|
||||
{
|
||||
struct {
|
||||
uint16_t limit;
|
||||
void *base;
|
||||
} __attribute__((packed)) src = { IDT_NUM_GATES * 8 - 1, idt };
|
||||
__asm__ ("lidt %0" : : "m"(src) );
|
||||
}
|
||||
|
||||
struct interrupt_frame; // Not defined yet, but we need a pointer to it.
|
||||
|
||||
#define GATE_F void (*f)(struct interrupt_frame *)
|
||||
#define GATE_WITH_ERR_F void (*f)(struct interrupt_frame *, uint32_t)
|
||||
#define FOR_INTR 0x8e /* Present, DPL 0, 32b interrupt gate */
|
||||
#define FOR_TRAP 0x8f /* Present, DPL 0, 32b trap gate */
|
||||
|
||||
struct idt_gate_descriptor gate(uint8_t type_attributes, uint32_t f)
|
||||
{
|
||||
return (struct idt_gate_descriptor){
|
||||
.type_attributes = type_attributes,
|
||||
.selector = 0x0008, // Our code segment in GDT, ring0 requested.
|
||||
.offset_1 = (uint16_t)(f & 0xffff),
|
||||
.offset_2 = (uint16_t)((f>>16) & 0xffff),
|
||||
.zero = 0x00
|
||||
};
|
||||
}
|
||||
|
||||
void klog_idt_gate_update(int n)
|
||||
{
|
||||
klogs(" "); klog_u8(n);
|
||||
}
|
||||
|
||||
void set_irq_handler(size_t irq, GATE_F)
|
||||
{
|
||||
idt[irq + PIC_REMAP_OFFSET] = gate(FOR_INTR, (uint32_t)f);
|
||||
klog_idt_gate_update(irq);
|
||||
}
|
||||
|
||||
void set_exception_with_err_handler(size_t number, GATE_WITH_ERR_F)
|
||||
{
|
||||
idt[number] = gate(FOR_TRAP, (uint32_t)f);
|
||||
klog_idt_gate_update(number);
|
||||
}
|
||||
|
||||
void set_exception_handler(size_t number, GATE_F)
|
||||
{
|
||||
idt[number] = gate(FOR_TRAP, (uint32_t)f);
|
||||
klog_idt_gate_update(number);
|
||||
}
|
||||
|
||||
#undef FOR_TRAP
|
||||
#undef FOR_INTR
|
||||
#undef GATE_WITH_ERR_F
|
||||
#undef GATE_F
|
||||
|
||||
void pic_clear_irq(uint8_t irq)
|
||||
{
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
if(irq < 8) {
|
||||
port = PIC1_DATA;
|
||||
} else {
|
||||
port = PIC2_DATA; irq -= 8;
|
||||
}
|
||||
value = inb(port) & ~(1 << irq);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void pic_mask_all_irqs(void)
|
||||
{
|
||||
outb(PIC1_DATA, 0xff); outb(PIC2_DATA, 0xff);
|
||||
}
|
||||
|
||||
static inline void sti(void)
|
||||
{
|
||||
__asm__ volatile ("sti");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void panic(void)
|
||||
{
|
||||
klogs("Panic: halt.");
|
||||
__asm__ volatile ("cli; hlt;");
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_with_err_handler(struct interrupt_frame *frame, uint32_t error_code)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\n(generic handler) Exception with err "); klogl_u32(error_code);
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_handler(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\n(generic handler) Exception\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_de(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\nException: Divide Error.\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_gp(struct interrupt_frame *frame, uint32_t error_code)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\nException: General Protection; error code "); klogl_u32(error_code);
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void irq1_handler(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
uint8_t k = inb(0x60);
|
||||
klogs("Key: "); klogl_u8(k);
|
||||
if (k == /* q */ 0x90) terminate = true;
|
||||
pic_end_of_irq(1);
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void irq4_handler(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
uint8_t c = read_serial();
|
||||
write_serial(c); // Echo.
|
||||
|
||||
// Testing stuff when receiving some chars.
|
||||
if (c == 'q') terminate = true;
|
||||
if (c == '0')
|
||||
__asm__ volatile ("mov $0, %bl; div %bl"); // Division by zero.
|
||||
if (c == 'f')
|
||||
__asm__ volatile ("int $13;"); // General Protection fault.
|
||||
|
||||
pic_end_of_irq(4);
|
||||
}
|
||||
|
||||
void init_interrupts(void)
|
||||
{
|
||||
klogs("**\n Init interrupts\n");
|
||||
|
||||
klogs("Preparing PIC.\n");
|
||||
pic_mask_all_irqs();
|
||||
pic_remap(PIC_REMAP_OFFSET);
|
||||
|
||||
klogs("Init exceptions.\n");
|
||||
#define A(n) set_exception_handler(n, &exception_handler)
|
||||
#define B(n) set_exception_with_err_handler(n, &exception_with_err_handler)
|
||||
set_exception_handler(0, &exception_de);
|
||||
A(1); A(2); A(3); A(4); A(5); A(6); A(7); B(8); A(9);
|
||||
B(10); B(11); B(12);
|
||||
set_exception_with_err_handler(13, &exception_gp);
|
||||
B(14); A(15); A(16); B(17); A(18); A(19);
|
||||
A(20); B(21); A(28); B(29); B(30);
|
||||
#undef B
|
||||
#undef A
|
||||
|
||||
klogs("\nInit IRQs.\n");
|
||||
|
||||
// Assign handlers to hardware interrupts.
|
||||
// Use the IRQ number here (the offset is applied elsewhere).
|
||||
set_irq_handler(1, &irq1_handler); // Keyboard.
|
||||
set_irq_handler(4, &irq4_handler); // Serial.
|
||||
klogs("\n");
|
||||
|
||||
// Make our IDT the active one.
|
||||
lidt();
|
||||
|
||||
// Unmask hardware interrupts that we're ready to handle.
|
||||
pic_clear_irq(1);
|
||||
pic_clear_irq(4);
|
||||
|
||||
// Start accepting IRQs (that is... from the PIC).
|
||||
sti();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void kmain(void)
|
||||
{
|
||||
init_serial();
|
||||
klogs("Hello from kmain!\n");
|
||||
demo_klog_functions();
|
||||
|
||||
init_interrupts();
|
||||
|
||||
const uint16_t color = 0x7e00;
|
||||
volatile uint16_t *vga = (volatile uint16_t *)0xb8000;
|
||||
const char *hello = "Hello! Please, see serial output.";
|
||||
|
||||
for (int i = 0; hello[i] != 0; ++i) {
|
||||
vga[i + 80] = color | (uint16_t)hello[i];
|
||||
}
|
||||
|
||||
klog_info_about_gdt();
|
||||
|
||||
klogs("\n\n**\n Ready!\n");
|
||||
klogs(
|
||||
"(accepting input from keyboard)\n"
|
||||
" type q to return from kmain\n"
|
||||
" key info sent to serial\n"
|
||||
"(accepting input from serial, with echo)\n"
|
||||
" send q to return from kmain\n"
|
||||
" send f to invoke GPF\n"
|
||||
" send 0 to trigger divide by zero\n"
|
||||
);
|
||||
while (!terminate) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
|
||||
klogs("\nkmain returning now... o/\n");
|
||||
}
|
43
kernel/idt.c
Normal file
43
kernel/idt.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include "idt.h"
|
||||
#include "io.h"
|
||||
|
||||
#define KERNEL_CS 0x08 // Kernel code segment selector
|
||||
|
||||
idt_entry_t idt[IDT_ENTRIES];
|
||||
idt_ptr_t idt_ptr;
|
||||
|
||||
// External assembly stubs for ISRs (provided below)
|
||||
extern void isr0();
|
||||
extern void isr13();
|
||||
extern void isr_default();
|
||||
|
||||
// Helper to set an IDT gate
|
||||
void idt_set_gate(int n, uint32_t handler) {
|
||||
idt[n].offset_low = handler & 0xFFFF;
|
||||
idt[n].selector = KERNEL_CS;
|
||||
idt[n].zero = 0;
|
||||
idt[n].type_attr = 0x8E; // Present, ring 0, 32-bit interrupt gate
|
||||
idt[n].offset_high = (handler >> 16) & 0xFFFF;
|
||||
}
|
||||
|
||||
// Load IDT via lidt
|
||||
static void idt_load() {
|
||||
asm volatile("lidt (%0)" : : "r" (&idt_ptr));
|
||||
}
|
||||
|
||||
// IDT initialization
|
||||
void idt_init() {
|
||||
idt_ptr.limit = sizeof(idt_entry_t) * IDT_ENTRIES - 1;
|
||||
idt_ptr.base = (uint32_t)&idt;
|
||||
|
||||
// Clear all entries
|
||||
for (int i = 0; i < IDT_ENTRIES; i++) {
|
||||
idt_set_gate(i, (uint32_t)isr_default);
|
||||
}
|
||||
|
||||
// Set specific handlers
|
||||
idt_set_gate(0, (uint32_t)isr0); // Divide by zero
|
||||
idt_set_gate(13, (uint32_t)isr13); // General protection fault
|
||||
|
||||
idt_load();
|
||||
}
|
24
kernel/idt.h
Normal file
24
kernel/idt.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef IDT_H
|
||||
#define IDT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
|
||||
typedef struct {
|
||||
uint16_t offset_low;
|
||||
uint16_t selector;
|
||||
uint8_t zero;
|
||||
uint8_t type_attr;
|
||||
uint16_t offset_high;
|
||||
} __attribute__((packed)) idt_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed)) idt_ptr_t;
|
||||
|
||||
void idt_set_gate(int n, uint32_t handler);
|
||||
void idt_init(void);
|
||||
|
||||
#endif
|
14
kernel/io.h
Normal file
14
kernel/io.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val) {
|
||||
asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t ret;
|
||||
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
30
kernel/isr.asm
Normal file
30
kernel/isr.asm
Normal file
@ -0,0 +1,30 @@
|
||||
; isr.asm
|
||||
[BITS 32]
|
||||
[GLOBAL isr0, isr13, isr_default]
|
||||
|
||||
isr0:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 0
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
|
||||
isr13:
|
||||
cli
|
||||
push byte 13
|
||||
push byte 0
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
|
||||
isr_default:
|
||||
cli
|
||||
push byte 255
|
||||
push byte 0
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
21
kernel/isr.c
Normal file
21
kernel/isr.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include "terminal.h"
|
||||
#include "serial.h"
|
||||
|
||||
void isr_handler(uint32_t int_num, uint32_t err_code) {
|
||||
terminal_write("Interrupt occurred: ");
|
||||
// Add simple int-to-string printing here
|
||||
serial_write("INT triggered\n");
|
||||
|
||||
if (int_num == 0) {
|
||||
terminal_write(" -> Divide by zero error!\n");
|
||||
} else if (int_num == 13) {
|
||||
terminal_write(" -> General Protection Fault!\n");
|
||||
} else {
|
||||
terminal_write(" -> Unknown interrupt\n");
|
||||
}
|
||||
|
||||
// Halt CPU
|
||||
while (1) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
}
|
53
kernel/kmain.c
Normal file
53
kernel/kmain.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
#include "terminal.h"
|
||||
#include "idt.h"
|
||||
#include "paging.h"
|
||||
#include "memmap.h"
|
||||
|
||||
#define LPT1 0x378
|
||||
|
||||
void lpt_write(char c) {
|
||||
while ((inb(LPT1 + 1) & 0x80) == 0); // Wait for ready
|
||||
outb(LPT1, c);
|
||||
}
|
||||
|
||||
void kmain(void) {
|
||||
terminal_initialize();
|
||||
terminal_write("Welcome to ClassicOS\n");
|
||||
|
||||
serial_init();
|
||||
serial_write("Serial port initialized.\n");
|
||||
|
||||
lpt_write('L'); // Send 'L' to LPT1 to test
|
||||
|
||||
terminal_write("Initializing IDT...\n");
|
||||
idt_init();
|
||||
serial_write("IDT initialized.\n");
|
||||
|
||||
terminal_write("Enabling paging...\n");
|
||||
paging_init();
|
||||
serial_write("Paging initialized.\n");
|
||||
|
||||
terminal_write("Getting memory map...\n");
|
||||
memory_map_entry_t mmap[32];
|
||||
uint32_t mmap_size = get_memory_map(mmap, 32);
|
||||
serial_write("Memory map retrieved.\n");
|
||||
|
||||
terminal_write("Memory Regions:\n");
|
||||
for (uint32_t i = 0; i < mmap_size; i++) {
|
||||
terminal_write(" - Region: ");
|
||||
// You would format and print base/length/type here
|
||||
// (e.g., with a basic itoa and print_hex helper)
|
||||
serial_write("Memory region entry\n");
|
||||
}
|
||||
|
||||
terminal_write("System initialized. Halting.\n");
|
||||
|
||||
// Halt CPU in loop
|
||||
while (1) {
|
||||
asm volatile("hlt");
|
||||
}
|
||||
}
|
14
kernel/memmap.c
Normal file
14
kernel/memmap.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include "memmap.h"
|
||||
|
||||
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
|
||||
// Fill with dummy values for now
|
||||
map[0].base_addr = 0x00000000;
|
||||
map[0].length = 0x0009FC00;
|
||||
map[0].type = 1;
|
||||
|
||||
map[1].base_addr = 0x00100000;
|
||||
map[1].length = 0x1FF00000;
|
||||
map[1].type = 1;
|
||||
|
||||
return 2; // 2 regions
|
||||
}
|
14
kernel/memmap.h
Normal file
14
kernel/memmap.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef MEMMAP_H
|
||||
#define MEMMAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint64_t base_addr;
|
||||
uint64_t length;
|
||||
uint32_t type;
|
||||
} __attribute__((packed)) memory_map_entry_t;
|
||||
|
||||
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries);
|
||||
|
||||
#endif
|
56
kernel/paging.c
Normal file
56
kernel/paging.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include "paging.h"
|
||||
#include "io.h"
|
||||
|
||||
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x100000;
|
||||
page_table_entry_t *page_table = (page_table_entry_t *)0x101000; // Located right after the page directory
|
||||
|
||||
// Helper function to set up the page directory entry
|
||||
void set_page_directory(page_directory_entry_t *dir) {
|
||||
for (int i = 0; i < PAGE_DIRECTORY_SIZE; i++) {
|
||||
// Set up a page directory entry with identity mapping
|
||||
dir[i].present = 1;
|
||||
dir[i].rw = 1; // Read/Write
|
||||
dir[i].user = 0; // Kernel mode
|
||||
dir[i].write_through = 0;
|
||||
dir[i].cache_disabled = 0;
|
||||
dir[i].accessed = 0;
|
||||
dir[i].frame = (uint32_t)&page_table[i] >> 12; // Page table frame address
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to set up the page table entry
|
||||
void set_page_table(page_table_entry_t *table) {
|
||||
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
|
||||
// Set up page table entries with identity mapping
|
||||
table[i].present = 1;
|
||||
table[i].rw = 1; // Read/Write
|
||||
table[i].user = 0; // Kernel mode
|
||||
table[i].write_through = 0;
|
||||
table[i].cache_disabled = 0;
|
||||
table[i].accessed = 0;
|
||||
table[i].frame = i; // Identity mapping
|
||||
}
|
||||
}
|
||||
|
||||
// Enable paging by loading the page directory into CR3 and setting the PG bit in CR0
|
||||
void enable_paging() {
|
||||
uint32_t cr0;
|
||||
|
||||
// Load page directory into CR3
|
||||
asm volatile("mov %0, %%cr3" : : "r"(page_directory));
|
||||
|
||||
// Enable paging (set the PG bit in CR0)
|
||||
asm volatile("mov %%cr0, %0" : "=r"(cr0));
|
||||
cr0 |= 0x80000000; // Set the PG (paging) bit
|
||||
asm volatile("mov %0, %%cr0" : : "r"(cr0));
|
||||
}
|
||||
|
||||
// Initialize paging: set up the page directory and enable paging
|
||||
void paging_init() {
|
||||
// Set up the page directory and page tables
|
||||
set_page_directory(page_directory);
|
||||
set_page_table(page_table);
|
||||
|
||||
// Enable paging
|
||||
enable_paging();
|
||||
}
|
47
kernel/paging.h
Normal file
47
kernel/paging.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef PAGING_H
|
||||
#define PAGING_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PAGE_SIZE 4096 // Page size in bytes
|
||||
#define PAGE_DIRECTORY_SIZE 1024 // 1024 entries in page directory
|
||||
#define PAGE_TABLE_SIZE 1024 // 1024 entries in a page table
|
||||
|
||||
// Page Directory and Page Table structure
|
||||
typedef struct {
|
||||
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 user : 1; // User-supervisor bit (1: user mode access)
|
||||
uint32_t write_through : 1; // Write-through cache
|
||||
uint32_t cache_disabled : 1; // Cache disabled
|
||||
uint32_t accessed : 1; // Accessed bit
|
||||
uint32_t reserved : 1; // Reserved bit
|
||||
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
|
||||
uint32_t global : 1; // Global page (can be used across different processes)
|
||||
uint32_t available : 3; // Available bits for the system
|
||||
uint32_t frame : 20; // Frame address (physical address)
|
||||
} __attribute__((packed)) page_table_entry_t;
|
||||
|
||||
// Define page directory entry
|
||||
typedef struct {
|
||||
uint32_t present : 1;
|
||||
uint32_t rw : 1;
|
||||
uint32_t user : 1;
|
||||
uint32_t write_through : 1;
|
||||
uint32_t cache_disabled : 1;
|
||||
uint32_t accessed : 1;
|
||||
uint32_t reserved : 1;
|
||||
uint32_t zero : 5; // Must be zero for page directory
|
||||
uint32_t reserved_2 : 7; // Reserved bits
|
||||
uint32_t frame : 20; // Frame address of the page table
|
||||
} __attribute__((packed)) page_directory_entry_t;
|
||||
|
||||
extern page_directory_entry_t *page_directory;
|
||||
extern page_table_entry_t *page_table;
|
||||
|
||||
void paging_init(void);
|
||||
void set_page_directory(page_directory_entry_t *dir);
|
||||
void set_page_table(page_table_entry_t *table);
|
||||
void enable_paging(void);
|
||||
|
||||
#endif
|
20
kernel/serial.c
Normal file
20
kernel/serial.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include "io.h"
|
||||
|
||||
#define COM1 0x3F8
|
||||
|
||||
void serial_init(void) {
|
||||
outb(COM1 + 1, 0x00); // Disable interrupts
|
||||
outb(COM1 + 3, 0x80); // Enable DLAB
|
||||
outb(COM1 + 0, 0x03); // Set baud rate to 38400
|
||||
outb(COM1 + 1, 0x00);
|
||||
outb(COM1 + 3, 0x03); // 8 bits, no parity, one stop bit
|
||||
outb(COM1 + 2, 0xC7); // Enable FIFO, clear, 14-byte threshold
|
||||
outb(COM1 + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||
}
|
||||
|
||||
void serial_write(const char *str) {
|
||||
while (*str) {
|
||||
while (!(inb(COM1 + 5) & 0x20)); // Wait for the transmitter holding register to be empty
|
||||
outb(COM1, *str++);
|
||||
}
|
||||
}
|
10
kernel/serial.h
Normal file
10
kernel/serial.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void serial_init(void);
|
||||
void serial_write(char c);
|
||||
void serial_write_string(const char *str);
|
||||
|
||||
#endif
|
63
kernel/terminal.c
Normal file
63
kernel/terminal.c
Normal file
@ -0,0 +1,63 @@
|
||||
#include <stdint.h>
|
||||
#include "io.h"
|
||||
|
||||
#define VGA_ADDRESS 0xB8000
|
||||
#define VGA_WIDTH 80
|
||||
#define VGA_HEIGHT 25
|
||||
#define WHITE_ON_BLACK 0x0F
|
||||
|
||||
static uint16_t* const vga_buffer = (uint16_t*) VGA_ADDRESS;
|
||||
static uint8_t cursor_x = 0;
|
||||
static uint8_t cursor_y = 0;
|
||||
|
||||
static uint16_t vga_entry(char c, uint8_t color) {
|
||||
return (uint16_t) color << 8 | (uint8_t) c;
|
||||
}
|
||||
|
||||
void terminal_initialize(void) {
|
||||
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
vga_buffer[index] = vga_entry(' ', WHITE_ON_BLACK);
|
||||
}
|
||||
}
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
}
|
||||
|
||||
void terminal_putchar(char c) {
|
||||
if (c == '\n') {
|
||||
cursor_x = 0;
|
||||
cursor_y++;
|
||||
} else {
|
||||
const size_t index = cursor_y * VGA_WIDTH + cursor_x;
|
||||
vga_buffer[index] = vga_entry(c, WHITE_ON_BLACK);
|
||||
cursor_x++;
|
||||
if (cursor_x >= VGA_WIDTH) {
|
||||
cursor_x = 0;
|
||||
cursor_y++;
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll if needed
|
||||
if (cursor_y >= VGA_HEIGHT) {
|
||||
for (uint16_t y = 1; y < VGA_HEIGHT; y++) {
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
vga_buffer[(y - 1) * VGA_WIDTH + x] = vga_buffer[y * VGA_WIDTH + x];
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the last line
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
vga_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', WHITE_ON_BLACK);
|
||||
}
|
||||
|
||||
cursor_y = VGA_HEIGHT - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_write(const char* str) {
|
||||
for (size_t i = 0; str[i] != '\0'; i++) {
|
||||
terminal_putchar(str[i]);
|
||||
}
|
||||
}
|
11
kernel/terminal.h
Normal file
11
kernel/terminal.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef TERMINAL_H
|
||||
#define TERMINAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void terminal_initialize(void);
|
||||
void terminal_putchar(char c);
|
||||
void terminal_write(const char *str);
|
||||
void terminal_setcolor(uint8_t color);
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
#ifndef __CLASSIC_OS_KSTDC_STDBOOL_H__
|
||||
#define __CLASSIC_OS_KSTDC_STDBOOL_H__ 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#undef bool
|
||||
|
||||
#ifdef _Bool
|
||||
|
||||
#define bool _Bool
|
||||
|
||||
#undef true
|
||||
#define true 1
|
||||
#undef false
|
||||
#define false 0
|
||||
|
||||
#else
|
||||
|
||||
typedef enum bool_t
|
||||
{
|
||||
false,
|
||||
true,
|
||||
} bool;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __bool_true_false_are_defined
|
||||
#undef __bool_true_false_are_defined
|
||||
#endif
|
||||
#define __bool_true_false_are_defined 1
|
||||
|
||||
#endif // __CLASSIC_OS_KSTDC_STDBOOL_H__
|
@ -1,32 +0,0 @@
|
||||
#ifndef __CLASSIC_OS_KSTDC_STDDEF_H__
|
||||
#define __CLASSIC_OS_KSTDC_STDDEF_H__ 1
|
||||
|
||||
typedef long unsigned int size_t;
|
||||
typedef long int ptrdiff_t;
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
typedef int wchar_t;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
typedef decltype(nullptr) nullptr_t;
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
#else
|
||||
|
||||
#define NULL 0
|
||||
|
||||
#endif
|
||||
|
||||
#undef offsetof
|
||||
#define offsetof(s, m) __builtin_offsetof(s, m)
|
||||
|
||||
#endif // __CLASSIC_OS_KSTDC_STDDEF_H__
|
@ -1,120 +0,0 @@
|
||||
#ifndef __CLASSIC_OS_KSTDC_STDINT_H__
|
||||
#define __CLASSIC_OS_KSTDC_STDINT_H__ 1
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short unsigned int uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef long unsigned int uint64_t;
|
||||
|
||||
typedef unsigned char uint_least8_t;
|
||||
typedef short unsigned int uint_least16_t;
|
||||
typedef unsigned int uint_least32_t;
|
||||
typedef long unsigned int uint_least64_t;
|
||||
|
||||
typedef unsigned char uint_fast8_t;
|
||||
typedef long unsigned int uint_fast16_t;
|
||||
typedef long unsigned int uint_fast32_t;
|
||||
typedef long unsigned int uint_fast64_t;
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef long int int64_t;
|
||||
|
||||
typedef signed char int_least8_t;
|
||||
typedef short int int_least16_t;
|
||||
typedef int int_least32_t;
|
||||
typedef long int int_least64_t;
|
||||
|
||||
typedef signed char int_fast8_t;
|
||||
typedef long int int_fast16_t;
|
||||
typedef long int int_fast32_t;
|
||||
typedef long int int_fast64_t;
|
||||
|
||||
typedef long unsigned int uintptr_t;
|
||||
typedef long int intptr_t;
|
||||
|
||||
typedef long unsigned int uintmax_t;
|
||||
typedef long int intmax_t;
|
||||
|
||||
#define INT8_C(x) ((int8_t)(x))
|
||||
#define INT16_C(x) ((int16_t)(x))
|
||||
#define INT32_C(x) ((int32_t)(x))
|
||||
#define INT64_C(x) ((int64_t)(x))
|
||||
|
||||
#define UINT8_C(x) ((uint8_t)(x))
|
||||
#define UINT16_C(x) ((uint16_t)(x))
|
||||
#define UINT32_C(x) ((uint32_t)(x))
|
||||
#define UINT64_C(x) ((uint64_t)(x))
|
||||
|
||||
#define INTMAX_C(x) ((intmax_t)(x))
|
||||
#define UINTMAX_C(x) ((uintmax_t)(x))
|
||||
|
||||
#define UINT8_MAX 0xff
|
||||
#define UINT16_MAX 0xffff
|
||||
#define UINT32_MAX 0xffffffffU
|
||||
#define UINT64_MAX 0xffffffffffffffffUL
|
||||
|
||||
#define INT8_MAX 0x7f
|
||||
#define INT16_MAX 0x7fff
|
||||
#define INT32_MAX 0x7fffffff
|
||||
#define INT64_MAX 0x7fffffffffffffffL
|
||||
|
||||
#define INT8_MIN (-INT8_MAX - 1)
|
||||
#define INT16_MIN (-INT16_MAX - 1)
|
||||
#define INT32_MIN (-INT32_MAX - 1)
|
||||
#define INT64_MIN (-INT64_MAX - 1)
|
||||
|
||||
#define UINT_LEAST8_MAX 0xff
|
||||
#define UINT_LEAST16_MAX 0xffff
|
||||
#define UINT_LEAST32_MAX 0xffffffffU
|
||||
#define UINT_LEAST64_MAX 0xffffffffffffffffUL
|
||||
|
||||
#define INT_LEAST8_MAX 0x7f
|
||||
#define INT_LEAST16_MAX 0x7fff
|
||||
#define INT_LEAST32_MAX 0x7fffffff
|
||||
#define INT_LEAST64_MAX 0x7fffffffffffffffL
|
||||
|
||||
#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1)
|
||||
#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1)
|
||||
#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1)
|
||||
#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1)
|
||||
|
||||
#define UINT_FAST8_MAX 0xff
|
||||
#define UINT_FAST16_MAX 0xffffffffffffffffUL
|
||||
#define UINT_FAST32_MAX 0xffffffffffffffffUL
|
||||
#define UINT_FAST64_MAX 0xffffffffffffffffUL
|
||||
|
||||
#define INT_FAST8_MAX 0x7f
|
||||
#define INT_FAST16_MAX 0x7fffffffffffffffL
|
||||
#define INT_FAST32_MAX 0x7fffffffffffffffL
|
||||
#define INT_FAST64_MAX 0x7fffffffffffffffL
|
||||
|
||||
#define INT_FAST8_MIN (-INT_FAST8_MAX - 1)
|
||||
#define INT_FAST16_MIN (-INT_FAST16_MAX - 1)
|
||||
#define INT_FAST32_MIN (-INT_FAST32_MAX - 1)
|
||||
#define INT_FAST64_MIN (-INT_FAST64_MAX - 1)
|
||||
|
||||
#define UINTPTR_MAX 0xffffffffffffffffUL
|
||||
#define INTPTR_MAX 0x7fffffffffffffffL
|
||||
#define INTPTR_MIN (-INTPTR_MAX - 1)
|
||||
|
||||
#define UINTMAX_MAX 0xffffffffffffffffUL
|
||||
#define INTMAX_MAX 0x7fffffffffffffffL
|
||||
#define INTMAX_MIN (-INTMAX_MAX - 1)
|
||||
|
||||
#define PTRDIFF_MAX 0x7fffffffffffffffL
|
||||
#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)
|
||||
|
||||
#define SIG_ATOMIC_MAX 0x7fffffff
|
||||
#define SIG_ATOMIC_MIN (-SIG_ATOMIC_MAX - 1)
|
||||
|
||||
#define SIZE_MAX 0xffffffffffffffffUL
|
||||
|
||||
#define WCHAR_MAX 0x7fffffff
|
||||
#define WCHAR_MIN (-WCHAR_MAX - 1)
|
||||
|
||||
#define WINT_MAX 0xffffffffU
|
||||
#define WINT_MIN (-WINT_MAX - 1)
|
||||
|
||||
#endif // __CLASSIC_OS_KSTDC_STDINT_H__
|
Loading…
x
Reference in New Issue
Block a user