mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2026-03-09 08:25:19 -07:00
Compare commits
217 Commits
df429f0042
...
gbowne1-ad
| Author | SHA1 | Date | |
|---|---|---|---|
| 98cb663939 | |||
| c2859d5618 | |||
| 3e6f8d0072 | |||
| 56faa3143d | |||
| 9be6a4e64b | |||
| 255ac93cae | |||
|
|
bfc9911a85 | ||
|
|
9d394d984b | ||
|
|
45abf9418b | ||
|
|
0584f7d49d | ||
| 4700e3f11a | |||
| 769f96acbc | |||
| 1575c0dd12 | |||
| 37e672a1c2 | |||
| 375dd9afdf | |||
| a42822dd54 | |||
| 6f0fee4182 | |||
| 01f130227b | |||
| f473fd5c32 | |||
| 576f5be8a6 | |||
| 538b97b877 | |||
| 4d2eec9e93 | |||
| d30bc6a7f5 | |||
| 96050d6a99 | |||
| e9e3237ba9 | |||
| 3c6b2a4e8c | |||
|
|
8db5173495 | ||
| c0220b3afc | |||
| f9ef99d11b | |||
| 17ce531b62 | |||
| 4aa6fa3b59 | |||
|
|
6d78074060 | ||
|
|
3fde819c49 | ||
|
|
aac87e30e0 | ||
|
|
975e21f9be | ||
|
|
db2c17d4b4 | ||
|
|
c805d24dd0 | ||
|
|
b3831b6c69 | ||
| 4e1197356b | |||
| a765fe5238 | |||
| 175c2949be | |||
| 2dd82e69a7 | |||
|
|
aeecf2b0b7 | ||
|
|
091391907f | ||
|
|
5dcf5d24b2 | ||
|
|
a7e536a6d3 | ||
|
|
b99cb27092 | ||
|
|
a3267d2ac3 | ||
|
|
bc08701e63 | ||
|
|
e8f6beef8a | ||
|
|
bd427dc7e9 | ||
|
|
619430d4bf | ||
|
|
84e28d8871 | ||
| dee91ca211 | |||
| 59dfca9ebd | |||
| 42f0b62e50 | |||
| 0a4f0e59e3 | |||
| 9d37b7a944 | |||
| eecc2561da | |||
| cae3731f91 | |||
| df33351d60 | |||
| d2b19ce376 | |||
| 8aea3c3c0e | |||
| f64e40d460 | |||
| 45da511bfa | |||
| 0f2528e07b | |||
| b36356f10c | |||
| 4b981a5d18 | |||
| fa66433179 | |||
|
|
f08204a2b3 | ||
| bb6f9d1a16 | |||
| 57ac458a4f | |||
| 6ad8644752 | |||
| 40462e55e6 | |||
|
|
4d2197aa2d | ||
|
|
f7b6a78b59 | ||
|
|
2b32a29890 | ||
|
|
4ee0ddb9ef | ||
|
|
13b915a99d | ||
|
|
d480fbcc80 | ||
|
|
d0c9c9c4e0 | ||
| 8be984d565 | |||
| 4f992c8fc5 | |||
| 16057d41d6 | |||
| 41281de743 | |||
|
|
dfb161a15a | ||
|
|
a450ac06f2 | ||
|
|
235fd2636d | ||
|
|
8c529c6fe4 | ||
|
|
155563e434 | ||
|
|
8b1ea16c56 | ||
|
|
7bda5c25b8 | ||
|
|
ac0fde28a0 | ||
|
|
a182bbca19 | ||
|
|
c503709ff4 | ||
|
|
4fa82854dd | ||
| 903061551c | |||
| 4393fbf6cb | |||
| f572101d6b | |||
| a37f94de44 | |||
| 401a19143c | |||
| 3cd2ff6e1e | |||
| aafd4efcb2 | |||
| d6eb5115d5 | |||
| cc92ade8fd | |||
| a9f6d5fa05 | |||
| 84705fd225 | |||
| 6c69b5fd6a | |||
| 1037ba4f54 | |||
| 745deeddde | |||
| f9e281a7ae | |||
| 18801a742f | |||
| a08648eff5 | |||
| 5a664c6e31 | |||
| 4c7de228f9 | |||
| af3f20485f | |||
| 09b2b8cd11 | |||
| 4079d18a45 | |||
| b6c158957e | |||
| cca6aafd65 | |||
| 49c1bad935 | |||
| 7adb798c17 | |||
| 43b5cacd09 | |||
|
|
a7b0d1152f | ||
|
|
e38f1aa2ee | ||
|
|
78d5e9a7ab | ||
|
|
daead5ee57 | ||
|
|
4fb81d2e57 | ||
|
|
19ef0a8627 | ||
| c5f7e4e563 | |||
| f516334e0d | |||
| 19f7c7b213 | |||
| bc9d84a93e | |||
| 9066ceaddb | |||
|
|
3b67e81ed0 | ||
|
|
841892398a | ||
| 86608ef48c | |||
| 785c8920d8 | |||
| c0e7ab6be0 | |||
| f78bc27f35 | |||
| 507b4f5511 | |||
|
|
12046ce96b | ||
|
|
a9b8ac7066 | ||
| d6ab8c91f8 | |||
|
|
35ebd5fd72 | ||
| 10d3761be1 | |||
| cc2e967a4d | |||
| 9eae2e1005 | |||
|
|
bd4236ad9b | ||
| 5292808934 | |||
|
|
09c48c2f50 | ||
| caea475daf | |||
|
|
f30be3ddd5 | ||
| d83e247bbd | |||
|
|
a1a6fd2aa9 | ||
|
|
66f9056406 | ||
|
|
45acbb5c04 | ||
|
|
649a227e41 | ||
| 4e8b13ad77 | |||
| 9f8ca3a60c | |||
| 574980035e | |||
| a0bd0941d6 | |||
| 4047bc3936 | |||
| 7e54f0de66 | |||
| 940b2810cb | |||
| 01f85f97ec | |||
| fd2c567d29 | |||
| 9de9cc6523 | |||
| e9a78c835a | |||
| 77400d8f5a | |||
| cdf5676085 | |||
|
|
8743fa9e24 | ||
| 3036ee3dfd | |||
| d5906d72de | |||
| 2ab0efdee1 | |||
| 0e011c1682 | |||
| eccf9d7d7c | |||
|
|
62fe09d80d | ||
| f1b0670a15 | |||
| 48fdb348ca | |||
| 6dbd08c808 | |||
| 9ac3a2b862 | |||
| 95f0507c24 | |||
| 70539f72b8 | |||
| 1b046776e0 | |||
| 2609f52dd6 | |||
| f2e75c5142 | |||
| 056d3eb374 | |||
| 98f0f58ce4 | |||
| 7d9d0aeee3 | |||
| 8e5dff4271 | |||
| 9aa1b85ca0 | |||
|
|
9216673b18 | ||
| ed07e2cd9c | |||
| a4318d3c79 | |||
| 9cde2e708d | |||
|
|
c22f6b6f14 | ||
| 6267863939 | |||
|
|
49114214cb | ||
| e58abdae1c | |||
|
|
dd37ba8ed6 | ||
| dd68fd805f | |||
| 16309bc306 | |||
| 4a189f482f | |||
| 267130281a | |||
| e1e30b511a | |||
| 109e554524 | |||
| 69762b6650 | |||
| 49361a98be | |||
| 50efcc13fe | |||
| a9f2826014 | |||
| 512bd49ff7 | |||
| 799f744f47 | |||
| 10b8fdc33f | |||
| ecfa54e225 | |||
| a464e109cb | |||
| 6dcbfd5683 |
24
.clang-format
Normal file
24
.clang-format
Normal file
@@ -0,0 +1,24 @@
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
ColumnLimit: 80
|
||||
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Right
|
||||
ReferenceAlignment: Right
|
||||
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
# Std headers
|
||||
- Regex: '<[[:alnum:]_.]+>'
|
||||
Priority: 2
|
||||
|
||||
# Other headers
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
6
.clangd
Normal file
6
.clangd
Normal file
@@ -0,0 +1,6 @@
|
||||
CompileFlags:
|
||||
CompilationDatabase: build
|
||||
|
||||
Diagnostics:
|
||||
UnusedIncludes: Strict
|
||||
MissingIncludes: Strict
|
||||
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
tab_width = 8
|
||||
72
.gitignore
vendored
72
.gitignore
vendored
@@ -1,67 +1,5 @@
|
||||
# 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.env
|
||||
build
|
||||
cross
|
||||
.cache/
|
||||
compile_commands.json
|
||||
|
||||
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files.associations": {
|
||||
".fantomasignore": "ignore",
|
||||
"stddef.h": "c",
|
||||
"io.h": "c"
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13.4)
|
||||
|
||||
# Enable C, Assembly, and Shell scripting languages
|
||||
enable_language(C ASM SH)
|
||||
|
||||
# Set up the project name and version
|
||||
set(PROJECT_NAME "ClassicOS")
|
||||
set(PROJECT_VERSION "0.0.1")
|
||||
|
||||
# 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}")
|
||||
82
Makefile
82
Makefile
@@ -0,0 +1,82 @@
|
||||
AS = nasm
|
||||
ASFLAGS = -f elf32 -g -F dwarf
|
||||
CC = i386-elf-gcc
|
||||
LD = i386-elf-ld
|
||||
QEMU= qemu-system-i386
|
||||
OBJCOPY = i386-elf-objcopy
|
||||
|
||||
BUILD_DIR = build
|
||||
CROSS_DIR = cross
|
||||
DISK_IMG = $(BUILD_DIR)/disk.img
|
||||
|
||||
STAGE2_ADDR = 0x7e00
|
||||
STAGE2_SIZE = 2048
|
||||
|
||||
# Place the memory map (e820) past stage2 bl in memory
|
||||
MEMMAP_BASE = $(shell echo $$(($(STAGE2_ADDR) + $(STAGE2_SIZE))))
|
||||
|
||||
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))
|
||||
|
||||
KLIBC_SRC = $(wildcard klibc/src/*.c)
|
||||
KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC))
|
||||
|
||||
.PHONY: all stage1 stage2 kernel compile-commands $(BUILD_DIR)/compile_commands.json run gdb clean clean-cross clean-all
|
||||
all: $(DISK_IMG)
|
||||
|
||||
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) -DMEMMAP_BASE=$(MEMMAP_BASE) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
|
||||
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c
|
||||
$(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o
|
||||
$(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
|
||||
truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin
|
||||
|
||||
$(BUILD_DIR)/asm_%.o: kernel/%.asm
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: kernel/%.c
|
||||
$(CC) -DMEMMAP_BASE=$(MEMMAP_BASE) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/klibc/%.o: klibc/src/%.c
|
||||
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
|
||||
|
||||
kernel: $(KERNEL_OBJ) | $(BUILD_DIR) $(KLIBC_OBJ)
|
||||
$(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) $(KLIBC_OBJ)
|
||||
|
||||
$(DISK_IMG): stage1 stage2 kernel
|
||||
dd if=$(BUILD_DIR)/stage1.bin of=$@
|
||||
dd if=$(BUILD_DIR)/stage2.bin of=$@ oflag=append conv=notrunc
|
||||
dd if=$(BUILD_DIR)/kernel.elf of=$@ oflag=append conv=notrunc
|
||||
truncate -s 1M $@
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
mkdir -p $(BUILD_DIR)/klibc
|
||||
|
||||
compile-commands: $(BUILD_DIR)/compile_commands.json
|
||||
$(BUILD_DIR)/compile_commands.json: $(BUILD_DIR)
|
||||
bear --output $@ -- make -B
|
||||
|
||||
run:
|
||||
qemu-system-i386 -s -S $(DISK_IMG)
|
||||
|
||||
gdb:
|
||||
gdb -x gdb.txt
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
clean-cross:
|
||||
rm -rf $(CROSS_DIR)
|
||||
rm -rf .build.env
|
||||
|
||||
clean-all: clean clean-cross
|
||||
|
||||
236
README.md
236
README.md
@@ -1,211 +1,69 @@
|
||||
# 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:
|
||||
|
||||
at least one person doing hardware and software testing and writing tests in a test framework
|
||||
- `gdb`
|
||||
- `vncviewer` (TigerVNC or similar)
|
||||
|
||||
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)
|
||||
---
|
||||
|
||||
## CPU Processor Support
|
||||
## 🛠️ Building ClassicOS
|
||||
|
||||
This project initially aims to support all 32 bit Intel and AMD processors Including;
|
||||
Clone repository:
|
||||
|
||||
-- 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)
|
||||
```sh
|
||||
git clone https://github.com/gbowne1/ClassicOS.git
|
||||
cd ClassicOS
|
||||
```
|
||||
|
||||
-- AMD --
|
||||
Run `configure` script to build a cross-compiler toolchain for `i386-elf`:
|
||||
|
||||
AMD k5
|
||||
AMD K6
|
||||
AMD am386
|
||||
AMD am486
|
||||
Athlon
|
||||
Duron
|
||||
Sempron
|
||||
```sh
|
||||
./configure
|
||||
```
|
||||
|
||||
## Device Support
|
||||
Source the `.build.env` file to add the cross-compiler toolchain to your PATH:
|
||||
|
||||
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
|
||||
```sh
|
||||
source .build.env
|
||||
```
|
||||
|
||||
## Booting
|
||||
Build the kernel:
|
||||
|
||||
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
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
100
boot.asm
100
boot.asm
@@ -1,100 +0,0 @@
|
||||
section .boot
|
||||
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
|
||||
|
||||
times 510 - ($-$$) db 0
|
||||
dw 0xaa55
|
||||
copy_target:
|
||||
bits 32
|
||||
hello: db "Hello more than 512 bytes world!!",0
|
||||
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:
|
||||
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`)
|
||||
|
||||
- Queries CHS parameters from BIOS
|
||||
- Loads the second stage bootloader (2048 B) to `0x7c00`
|
||||
- Enables A20
|
||||
- Jumps to stage2
|
||||
|
||||
## Stage 2 (`stage2.asm, stage2_load.c`)
|
||||
|
||||
- Read and store E820 memory map from BIOS
|
||||
- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space
|
||||
- Set CR0.PE (enable protected mode)
|
||||
- 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
|
||||
265
bootloader/stage1.asm
Normal file
265
bootloader/stage1.asm
Normal file
@@ -0,0 +1,265 @@
|
||||
; ==============================================================================
|
||||
; boot.asm - First Stage Bootloader (CHS Based)
|
||||
; ==============================================================================
|
||||
|
||||
; Params for stage2
|
||||
%define s2_addr 1 ; stage2 disk offset, in sectors
|
||||
%define s2_laddr 0x7e00 ; stage2 load address
|
||||
%define s2_size 2048 ; stage2 size
|
||||
%define s2_nsect s2_size / 512 ; stage2 size in sectors
|
||||
|
||||
[BITS 16]
|
||||
global _start
|
||||
|
||||
_start:
|
||||
cli ; Disable interrupts
|
||||
|
||||
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
|
||||
|
||||
; Setup stack safely below EBDA area (choose 0x0000:0x7A00)
|
||||
xor ax, ax ; AX = 0
|
||||
mov ss, ax ; Stack segment = 0x0000
|
||||
mov sp, 0x7A00 ; Stack offset = 0x7A00
|
||||
|
||||
; Initialize DS, ES for zero-based segments
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
|
||||
; Query bios for disk parameters
|
||||
call get_disk_params
|
||||
|
||||
; Load second-stage bootloader (boot1.asm) to 0x7E00
|
||||
mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
|
||||
call lba_to_chs
|
||||
mov al, s2_nsect ; Number of sectors to read
|
||||
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
|
||||
call read_chs
|
||||
|
||||
; Enable A20 line
|
||||
call enable_a20
|
||||
jc a20_error ; Jump if A20 enable fails
|
||||
|
||||
; Jump to s2
|
||||
jmp 0x7e00
|
||||
|
||||
disk_error:
|
||||
mov si, disk_error_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
a20_error:
|
||||
mov si, a20_error_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Verify Checksum Routine
|
||||
; Uses SI = start address, CX = byte count
|
||||
; Simple XOR checksum over bytes, expects result 0
|
||||
|
||||
verify_checksum:
|
||||
push ax
|
||||
push bx
|
||||
push di
|
||||
mov di, si
|
||||
xor al, al
|
||||
xor bx, bx
|
||||
|
||||
.verify_loop:
|
||||
lodsb
|
||||
xor bl, al
|
||||
loop .verify_loop
|
||||
|
||||
test bl, bl
|
||||
jz .checksum_ok
|
||||
stc ; Set carry on checksum error
|
||||
jmp .done
|
||||
|
||||
.checksum_ok:
|
||||
clc ; Clear carry on success
|
||||
|
||||
.done:
|
||||
pop di
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
get_disk_params:
|
||||
mov ah, 08h ; BIOS: Get Drive Parameters
|
||||
int 13h
|
||||
; TODO: error checking
|
||||
|
||||
; CL bits 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)
|
||||
; Inputs:
|
||||
; AL = sector count
|
||||
; CH = cylinder
|
||||
; DH = head
|
||||
; CL = sector (1–63, with top 2 bits as high cylinder bits)
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Convert LBA to CHS
|
||||
; Inputs:
|
||||
; AX = LBA sector number (0-based)
|
||||
; Outputs:
|
||||
; CH = cylinder
|
||||
; DH = head
|
||||
; CL = sector (1-63, top 2 bits are upper cylinder bits)
|
||||
|
||||
lba_to_chs:
|
||||
; Sector
|
||||
xor dx, dx
|
||||
mov bx, ax
|
||||
div word [sectors_per_track] ; divide lba with max sectors
|
||||
add dl, 1 ; take the remainder, sectors start at 1
|
||||
mov cl, dl ; sector is in cl
|
||||
|
||||
; Head
|
||||
mov ax, bx
|
||||
mov dx, 0
|
||||
div word [sectors_per_track] ; divide lba with max sectors
|
||||
mov dx, 0
|
||||
div word [heads_per_cylinder] ; divide quotient with heads
|
||||
mov dh, dl ; take the remainder, head is in dh
|
||||
|
||||
; Cylinder
|
||||
mov ch, al ; take the quotient, cylinder is in ch
|
||||
ret
|
||||
|
||||
read_chs:
|
||||
pusha
|
||||
push dx
|
||||
|
||||
.retry:
|
||||
mov ah, 0x02 ; BIOS: Read sectors
|
||||
mov dl, [bootdev] ; Boot device
|
||||
; Assume CH, DH, CL already set before this call
|
||||
int 0x13
|
||||
jc .error
|
||||
pop dx
|
||||
popa
|
||||
ret
|
||||
|
||||
.error:
|
||||
dec cx
|
||||
jz disk_error
|
||||
xor ah, ah
|
||||
int 0x13
|
||||
jmp .retry
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
enable_a20:
|
||||
; Try fast A20 gate method
|
||||
in al, 0x92
|
||||
or al, 0x02
|
||||
and al, 0xFE ; Clear bit 0 to avoid fast A20 bugs
|
||||
out 0x92, al
|
||||
|
||||
; Verify A20
|
||||
call check_a20
|
||||
jnc .done ; Success
|
||||
|
||||
; Fallback: use keyboard controller method
|
||||
call .fallback
|
||||
|
||||
.done:
|
||||
ret
|
||||
|
||||
.fallback:
|
||||
mov al, 0xAD ; Disable keyboard
|
||||
out 0x64, al
|
||||
call .wait_input_clear
|
||||
|
||||
mov al, 0xD0 ; Command: read output port
|
||||
out 0x64, al
|
||||
call .wait_output_full
|
||||
in al, 0x60
|
||||
or al, 0x02 ; Set A20 enable bit
|
||||
mov bl, al
|
||||
|
||||
call .wait_input_clear
|
||||
mov al, 0xD1 ; Command: write output port
|
||||
out 0x64, al
|
||||
call .wait_input_clear
|
||||
mov al, bl
|
||||
out 0x60, al
|
||||
|
||||
call .wait_input_clear
|
||||
mov al, 0xAE ; Enable keyboard
|
||||
out 0x64, al
|
||||
ret
|
||||
|
||||
.wait_input_clear:
|
||||
in al, 0x64
|
||||
test al, 0x02
|
||||
jnz .wait_input_clear
|
||||
ret
|
||||
|
||||
.wait_output_full:
|
||||
in al, 0x64
|
||||
test al, 0x01
|
||||
jz .wait_output_full
|
||||
ret
|
||||
|
||||
check_a20:
|
||||
in al, 0x64 ; Read keyboard controller status
|
||||
test al, 0x02 ; Check if input buffer is full
|
||||
jnz check_a20 ; Wait until empty
|
||||
|
||||
mov al, 0xD0 ; Command: read output port
|
||||
out 0x64, al
|
||||
|
||||
.wait_output:
|
||||
in al, 0x60 ; Read output port value
|
||||
test al, 0x02 ; Check A20 gate bit (bit 1)
|
||||
jnz .a20_enabled ; If set, A20 enabled
|
||||
|
||||
xor al, al ; Clear carry to indicate failure
|
||||
stc ; Set carry for failure, jc will jump
|
||||
|
||||
ret
|
||||
|
||||
.a20_enabled:
|
||||
clc ; Clear carry flag to indicate success
|
||||
ret
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
print_string_16:
|
||||
.loop:
|
||||
lodsb
|
||||
or al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
int 0x10
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
disk_error_msg db "Disk error!", 0
|
||||
a20_error_msg db "A20 error!", 0
|
||||
|
||||
halt:
|
||||
cli
|
||||
hlt
|
||||
|
||||
bootdev db 0
|
||||
sectors_per_track dw 0
|
||||
heads_per_cylinder dw 0
|
||||
|
||||
times 510 - ($ - $$) db 0
|
||||
dw 0xAA55
|
||||
95
bootloader/stage2.asm
Normal file
95
bootloader/stage2.asm
Normal file
@@ -0,0 +1,95 @@
|
||||
global _start
|
||||
extern load_kernel
|
||||
|
||||
%define e820_magic 0x534d4150 ; "SMAP"
|
||||
%define e820_entry_size 24
|
||||
%define e820_max_entries 128
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Real mode
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 16]
|
||||
_start:
|
||||
call read_e820
|
||||
call setup_gdt
|
||||
call switch_to_pm
|
||||
|
||||
read_e820:
|
||||
xor ebx, ebx
|
||||
mov es, bx
|
||||
mov di, MEMMAP_BASE+4 ; ES=0 DI=MEMMAP_BASE+4
|
||||
xor bp, bp ; Keeping count in bp
|
||||
|
||||
.e820_loop:
|
||||
mov eax, 0xe820
|
||||
mov ecx, e820_entry_size
|
||||
mov edx, e820_magic
|
||||
int 0x15
|
||||
jc .done ; Error?
|
||||
|
||||
cmp eax, e820_magic ; Verify "SMAP"
|
||||
jne .done
|
||||
|
||||
test ecx, ecx ; Skip 0-sized entries
|
||||
jz .skip
|
||||
|
||||
add di, e820_entry_size ; Advance write addr
|
||||
inc bp ; Increment count
|
||||
|
||||
cmp bp, e820_max_entries ; Stop if we're at capacity
|
||||
jae .done
|
||||
.skip:
|
||||
test ebx, ebx
|
||||
jne .e820_loop
|
||||
.done:
|
||||
mov [MEMMAP_BASE], bp ; Store count
|
||||
ret
|
||||
|
||||
setup_gdt:
|
||||
lgdt [gdt_descriptor]
|
||||
ret
|
||||
|
||||
switch_to_pm:
|
||||
cli
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
jmp 0x08:pm_entry
|
||||
|
||||
e820_count:
|
||||
dw 0
|
||||
|
||||
gdt_start:
|
||||
dq 0x0000000000000000 ; Null descriptor
|
||||
dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08)
|
||||
dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10)
|
||||
dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18)
|
||||
gdt_descriptor:
|
||||
dw gdt_end - gdt_start - 1
|
||||
dd gdt_start
|
||||
gdt_end:
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Protected mode
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 32]
|
||||
|
||||
pm_entry:
|
||||
; 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
|
||||
mov esp, 0x90000
|
||||
|
||||
call load_kernel
|
||||
jmp eax
|
||||
12
bootloader/stage2.ld
Normal file
12
bootloader/stage2.ld
Normal file
@@ -0,0 +1,12 @@
|
||||
SECTIONS {
|
||||
. = 0x7e00;
|
||||
|
||||
.text : { *(.text*) }
|
||||
.rodata : { *(.rodata*) }
|
||||
.data : { *(.data*) }
|
||||
|
||||
.bss : {
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
}
|
||||
}
|
||||
235
bootloader/stage2_load.c
Normal file
235
bootloader/stage2_load.c
Normal file
@@ -0,0 +1,235 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ATA IO Ports
|
||||
#define ATA_PRIMARY_DATA 0x1F0
|
||||
#define ATA_PRIMARY_ERR_FEATURES 0x1F1
|
||||
#define ATA_PRIMARY_SEC_COUNT 0x1F2
|
||||
#define ATA_PRIMARY_LBA_LOW 0x1F3
|
||||
#define ATA_PRIMARY_LBA_MID 0x1F4
|
||||
#define ATA_PRIMARY_LBA_HIGH 0x1F5
|
||||
#define ATA_PRIMARY_DRIVE_SEL 0x1F6
|
||||
#define ATA_PRIMARY_COMM_STAT 0x1F7
|
||||
|
||||
// ATA Commands
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
|
||||
// ELF Ident indexes
|
||||
#define EI_NIDENT 16
|
||||
|
||||
// Program header types
|
||||
#define PT_NULL 0
|
||||
#define PT_LOAD 1
|
||||
|
||||
// Disk sector size
|
||||
#define SECTOR_SIZE 512
|
||||
#define PH_PER_SECTOR (SECTOR_SIZE / sizeof(Elf32_Phdr))
|
||||
|
||||
// Kernel start LBA
|
||||
#define KERN_START_SECT 5
|
||||
|
||||
// VGA
|
||||
// Expects bios initialization for text mode (3), buffer at 0xb8000
|
||||
#define VGA_ADDRESS 0xB8000
|
||||
#define VGA_COLS 80
|
||||
#define VGA_ROWS 25
|
||||
|
||||
// ELF Header (32-bit)
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry; // Entry point
|
||||
uint32_t e_phoff; // Program header table offset
|
||||
uint32_t e_shoff; // Section header table offset
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} __attribute__((packed)) Elf32_Ehdr;
|
||||
|
||||
// Program Header (32-bit)
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
} __attribute__((packed)) Elf32_Phdr;
|
||||
|
||||
static inline uint8_t inb(uint16_t port)
|
||||
{
|
||||
uint8_t ret;
|
||||
__asm__ volatile ("inb %1, %0"
|
||||
: "=a"(ret)
|
||||
: "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val)
|
||||
{
|
||||
__asm__ volatile ("outb %0, %1"
|
||||
:
|
||||
: "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint16_t inw(uint16_t port)
|
||||
{
|
||||
uint16_t ret;
|
||||
__asm__ volatile ("inw %1, %0"
|
||||
: "=a"(ret)
|
||||
: "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void ata_wait_bsy() {
|
||||
while (inb(ATA_PRIMARY_COMM_STAT) & 0x80);
|
||||
}
|
||||
|
||||
static inline void ata_wait_drq() {
|
||||
while (!(inb(ATA_PRIMARY_COMM_STAT) & 0x08));
|
||||
}
|
||||
|
||||
static void ata_read_sector(void *addr, uint32_t lba) {
|
||||
ata_wait_bsy();
|
||||
|
||||
outb(ATA_PRIMARY_DRIVE_SEL, 0xE0 | ((lba >> 24) & 0x0F));
|
||||
outb(ATA_PRIMARY_SEC_COUNT, 1);
|
||||
outb(ATA_PRIMARY_LBA_LOW, (uint8_t)lba);
|
||||
outb(ATA_PRIMARY_LBA_MID, (uint8_t)(lba >> 8));
|
||||
outb(ATA_PRIMARY_LBA_HIGH, (uint8_t)(lba >> 16));
|
||||
outb(ATA_PRIMARY_COMM_STAT, ATA_CMD_READ_PIO);
|
||||
|
||||
uint16_t* ptr = (uint16_t*)addr;
|
||||
ata_wait_bsy();
|
||||
ata_wait_drq();
|
||||
for (int i = 0; i < 256; i++) {
|
||||
*ptr++ = inw(ATA_PRIMARY_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
static void ata_read_sectors(uint8_t *addr, uint32_t offset, uint32_t size)
|
||||
{
|
||||
// Reads are offset from the starting sector of the kernel
|
||||
uint32_t lba = KERN_START_SECT + offset / SECTOR_SIZE;
|
||||
uint32_t off = offset % 512;
|
||||
uint8_t data[512];
|
||||
|
||||
while (size > 0) {
|
||||
ata_read_sector(data, lba);
|
||||
|
||||
uint32_t copy = 512 - off;
|
||||
if (copy > size) {
|
||||
copy = size;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < copy; i++) {
|
||||
addr[i] = data[off + i];
|
||||
}
|
||||
|
||||
addr += copy;
|
||||
size -= copy;
|
||||
lba++;
|
||||
off = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_error(const char *msg)
|
||||
{
|
||||
uint16_t *ptr = (uint16_t *)VGA_ADDRESS;
|
||||
|
||||
// Clear
|
||||
uint16_t val = 0x0f00 | (uint8_t)' ';
|
||||
for (size_t i = 0; i < VGA_COLS * VGA_ROWS; i++) {
|
||||
ptr[i] = val;
|
||||
}
|
||||
|
||||
// Print error
|
||||
for (size_t i = 0; msg[i]; i++) {
|
||||
ptr[i] = 0xf00 | (uint8_t)msg[i];
|
||||
}
|
||||
|
||||
// Halt
|
||||
while (1) {
|
||||
__asm__("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
// Load an ELF executable into memory.
|
||||
// NOTE: Only 32-byte program headers are supported.
|
||||
// Returns the entry point to the program.
|
||||
static void *elf_load(const void *data) {
|
||||
const Elf32_Ehdr *header = (const Elf32_Ehdr*)data;
|
||||
|
||||
if (header->e_phentsize != sizeof(Elf32_Phdr)) {
|
||||
// The bootloader only handles 32-byte program header entries
|
||||
on_error("ERROR: Unsupported program header entry size, halting...");
|
||||
}
|
||||
|
||||
// Buffer for the program headers
|
||||
uint8_t file_buf[SECTOR_SIZE];
|
||||
|
||||
// Current file offset to the next program header
|
||||
uint32_t file_offset = header->e_phoff;
|
||||
|
||||
for (int i = 0; i < header->e_phnum; i++) {
|
||||
// Check for sector boundary.
|
||||
// Program headers are read in a sector at a time
|
||||
// 512 / 32 = 16 PH per sector
|
||||
if (i % PH_PER_SECTOR == 0) {
|
||||
uint32_t count = (header->e_phnum - i) * sizeof(Elf32_Phdr);
|
||||
if (count > SECTOR_SIZE) {
|
||||
count = SECTOR_SIZE;
|
||||
}
|
||||
|
||||
// Reads
|
||||
ata_read_sectors(file_buf, file_offset, count);
|
||||
file_offset += count;
|
||||
}
|
||||
|
||||
// PH being processed currently, index mod 16 as headers
|
||||
// are being loaded in sector by sector.
|
||||
const Elf32_Phdr *ph = (const Elf32_Phdr *)file_buf + (i % PH_PER_SECTOR);
|
||||
|
||||
// Discard non-load segments
|
||||
if (ph->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
// Load in the segment
|
||||
uint32_t offset = ph->p_offset;
|
||||
uint32_t filesz = ph->p_filesz;
|
||||
uint32_t memsz = ph->p_memsz;
|
||||
uint8_t *vaddr = (uint8_t *)ph->p_vaddr;
|
||||
ata_read_sectors(vaddr, offset, filesz);
|
||||
|
||||
// Zero remaining BSS (if any)
|
||||
if (memsz > filesz) {
|
||||
uint8_t* bss_start = vaddr + filesz;
|
||||
for (uint32_t j = 0; j < memsz - filesz; j++) {
|
||||
bss_start[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the entry point
|
||||
return (void *)header->e_entry;
|
||||
}
|
||||
|
||||
void *load_kernel(void) {
|
||||
// ELF header buffer
|
||||
uint8_t header_buf[SECTOR_SIZE];
|
||||
|
||||
// Read the first sector (contains the ELF header)
|
||||
ata_read_sector(header_buf, KERN_START_SECT);
|
||||
|
||||
// `elf_load()` returns the entry point
|
||||
return elf_load(header_buf);
|
||||
}
|
||||
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
|
||||
169
configure
vendored
Executable file
169
configure
vendored
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
TARGET="i386-elf"
|
||||
BINUTILS_VERSION="2.45"
|
||||
GCC_VERSION="15.2.0"
|
||||
|
||||
# Paths
|
||||
SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")"
|
||||
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
|
||||
PREFIX="$SCRIPT_DIR/cross"
|
||||
SRC_DIR="$PREFIX/src"
|
||||
|
||||
BINUTILS_SRC="$SRC_DIR/binutils-$BINUTILS_VERSION"
|
||||
BINUTILS_BUILD="$PREFIX/build-binutils"
|
||||
GCC_SRC="$SRC_DIR/gcc-$GCC_VERSION"
|
||||
GCC_BUILD="$PREFIX/build-gcc"
|
||||
|
||||
# Flags
|
||||
DEBUG=0
|
||||
HELP=0
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-h|--help)
|
||||
HELP=1
|
||||
;;
|
||||
-d|--debug)
|
||||
DEBUG=1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $arg"
|
||||
echo "Use -h or --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Show help
|
||||
if [[ "$HELP" -eq 1 ]]; then
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Build a cross-compiler toolchain for $TARGET.
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-d, --debug Enable debug mode (set -x)
|
||||
|
||||
This script will:
|
||||
1. Download binutils $BINUTILS_VERSION and GCC $GCC_VERSION
|
||||
2. Build and install them to: $PREFIX
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Enable debug mode
|
||||
if [[ "$DEBUG" -eq 1 ]]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Print configuration
|
||||
cat << EOF
|
||||
|
||||
=== Build Configuration ===
|
||||
Target : $TARGET
|
||||
Prefix : $PREFIX
|
||||
Binutils : $BINUTILS_VERSION
|
||||
GCC : $GCC_VERSION
|
||||
===========================
|
||||
|
||||
EOF
|
||||
|
||||
# Create directory structure
|
||||
echo "Setting up directories..."
|
||||
mkdir -p "$SRC_DIR"
|
||||
|
||||
# Download sources
|
||||
cd "$SRC_DIR"
|
||||
|
||||
if [[ ! -d "$BINUTILS_SRC" ]]; then
|
||||
echo "Downloading binutils $BINUTILS_VERSION..."
|
||||
wget "https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.gz"
|
||||
echo "Extracting binutils..."
|
||||
tar xf "binutils-$BINUTILS_VERSION.tar.gz"
|
||||
rm "binutils-$BINUTILS_VERSION.tar.gz"
|
||||
else
|
||||
echo "Binutils source already exists, skipping download"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$GCC_SRC" ]]; then
|
||||
echo "Downloading GCC $GCC_VERSION..."
|
||||
wget "https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz"
|
||||
echo "Extracting GCC..."
|
||||
tar xf "gcc-$GCC_VERSION.tar.gz"
|
||||
rm "gcc-$GCC_VERSION.tar.gz"
|
||||
else
|
||||
echo "GCC source already exists, skipping download"
|
||||
fi
|
||||
|
||||
# Download GCC prerequisites
|
||||
if [[ ! -d "$GCC_SRC/gmp" ]]; then
|
||||
echo "Downloading GCC prerequisites..."
|
||||
cd "$GCC_SRC"
|
||||
./contrib/download_prerequisites
|
||||
cd "$SRC_DIR"
|
||||
else
|
||||
echo "GCC prerequisites already downloaded, skipping"
|
||||
fi
|
||||
|
||||
# Build binutils
|
||||
if [[ ! -f "$PREFIX/bin/$TARGET-ld" ]]; then
|
||||
echo "Building binutils..."
|
||||
mkdir -p "$BINUTILS_BUILD"
|
||||
cd "$BINUTILS_BUILD"
|
||||
|
||||
"$BINUTILS_SRC/configure" \
|
||||
--target="$TARGET" \
|
||||
--prefix="$PREFIX" \
|
||||
--with-sysroot \
|
||||
--disable-nls \
|
||||
--disable-werror
|
||||
|
||||
make -j"$(nproc)"
|
||||
make install
|
||||
else
|
||||
echo "Binutils already installed, skipping build"
|
||||
fi
|
||||
|
||||
# Build GCC
|
||||
if [[ ! -f "$PREFIX/bin/$TARGET-gcc" ]]; then
|
||||
echo "Building GCC..."
|
||||
mkdir -p "$GCC_BUILD"
|
||||
cd "$GCC_BUILD"
|
||||
|
||||
"$GCC_SRC/configure" \
|
||||
--target="$TARGET" \
|
||||
--prefix="$PREFIX" \
|
||||
--disable-nls \
|
||||
--enable-languages=c \
|
||||
--without-headers
|
||||
|
||||
make all-gcc -j"$(nproc)"
|
||||
make all-target-libgcc -j"$(nproc)"
|
||||
make install-gcc
|
||||
make install-target-libgcc
|
||||
else
|
||||
echo "GCC already installed, skipping build"
|
||||
fi
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Generate .build.env file
|
||||
cat > .build.env << EOF
|
||||
# Generated by configure on $(date)
|
||||
# Source this file to add the cross-compiler toolchain to your PATH
|
||||
export PATH="$PREFIX/bin:\$PATH"
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "=== Build Complete ==="
|
||||
echo "Toolchain installed to: $PREFIX"
|
||||
echo ""
|
||||
echo "To use the toolchain, run:"
|
||||
echo " source .build.env"
|
||||
echo "======================"
|
||||
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
|
||||
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
|
||||
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");
|
||||
}
|
||||
54
kernel/acpi.c
Normal file
54
kernel/acpi.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "acpi.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// Function to find the RSDP (Root System Description Pointer)
|
||||
acpi_rsdp_t* acpi_find_rsdp() {
|
||||
// Search memory from 0x000E0000 to 0x00100000 (BIOS)
|
||||
for (uint32_t addr = 0x000E0000; addr < 0x00100000; addr += 16) {
|
||||
acpi_rsdp_t* rsdp = (acpi_rsdp_t*)addr;
|
||||
if (memcmp(rsdp->signature, "RSD PTR ", 8) == 0) {
|
||||
uint8_t checksum = 0;
|
||||
for (size_t i = 0; i < sizeof(acpi_rsdp_t); i++) { // Change int to size_t
|
||||
checksum += ((uint8_t*)rsdp)[i];
|
||||
}
|
||||
if (checksum == 0) {
|
||||
return rsdp; // Valid RSDP found
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; // RSDP not found
|
||||
}
|
||||
|
||||
// Function to get the RSDT or XSDT based on the RSDP revision
|
||||
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp) {
|
||||
if (rsdp->revision >= 2) {
|
||||
return (void*)rsdp->xsdt_addr; // ACPI 2.0+ uses XSDT
|
||||
} else {
|
||||
return (void*)rsdp->rsdt_addr; // ACPI 1.0 uses RSDT
|
||||
}
|
||||
}
|
||||
|
||||
// Function to find the FADT table within the RSDT or XSDT
|
||||
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt) {
|
||||
acpi_rsdt_t* rsdt = (acpi_rsdt_t*)rsdt_or_xsdt;
|
||||
uint32_t num_tables = (rsdt->length - sizeof(acpi_rsdt_t)) / sizeof(uint32_t);
|
||||
|
||||
for (size_t i = 0; i < num_tables; i++) {
|
||||
uint32_t table_addr = rsdt->tables[i];
|
||||
acpi_fadt_t* fadt = (acpi_fadt_t*)table_addr;
|
||||
if (fadt->signature == 0x50434146) {
|
||||
uint8_t checksum = 0;
|
||||
for (size_t j = 0; j < fadt->length; j++) {
|
||||
checksum += ((uint8_t*)fadt)[j];
|
||||
}
|
||||
if (checksum == 0) {
|
||||
return fadt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
47
kernel/acpi.h
Normal file
47
kernel/acpi.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef ACPI_H
|
||||
#define ACPI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ACPI base address (replace with actual value)
|
||||
#define ACPI_BASE 0xE0000000
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
// ACPI RSDP Structure (Root System Description Pointer)
|
||||
typedef struct {
|
||||
uint8_t signature[8]; // Should be "RSD PTR "
|
||||
uint8_t checksum; // Checksum for the RSDP structure
|
||||
uint8_t oem_id[6]; // OEM ID string
|
||||
uint8_t revision; // ACPI revision
|
||||
uint32_t rsdt_addr; // 32-bit RSDT address (ACPI 1.0)
|
||||
uint32_t xsdt_addr; // 64-bit XSDT address (ACPI 2.0+)
|
||||
} __attribute__((packed)) acpi_rsdp_t;
|
||||
|
||||
// ACPI RSDT Structure (Root System Description Table)
|
||||
typedef struct {
|
||||
uint32_t signature; // Should be "RSDT"
|
||||
uint32_t length; // Length of the table
|
||||
uint8_t revision; // Revision of the RSDT table
|
||||
uint8_t checksum; // Checksum for the RSDT table
|
||||
uint32_t tables[]; // Array of pointers to other tables (RSDT/XSDT entries)
|
||||
} __attribute__((packed)) acpi_rsdt_t;
|
||||
|
||||
// ACPI FADT Structure (Fixed ACPI Description Table)
|
||||
typedef struct {
|
||||
uint32_t signature; // Should be "FACP"
|
||||
uint32_t length; // Length of the table
|
||||
uint8_t revision; // Revision of the FADT table
|
||||
uint8_t checksum; // Checksum for the FADT table
|
||||
uint32_t pm_tmr_address; // Power Management Timer Address
|
||||
// ... other FADT fields
|
||||
} __attribute__((packed)) acpi_fadt_t;
|
||||
|
||||
// Function prototypes
|
||||
acpi_rsdp_t* acpi_find_rsdp();
|
||||
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp);
|
||||
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt);
|
||||
|
||||
#endif /* ACPI_H */
|
||||
119
kernel/ata.c
Normal file
119
kernel/ata.c
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "ata.h"
|
||||
#include "io.h"
|
||||
#include "print.h"
|
||||
|
||||
#define ATA_TIMEOUT 100000
|
||||
|
||||
static inline void ata_delay(void) {
|
||||
/* 400ns delay by reading alternate status */
|
||||
inb(ATA_PRIMARY_CTRL);
|
||||
inb(ATA_PRIMARY_CTRL);
|
||||
inb(ATA_PRIMARY_CTRL);
|
||||
inb(ATA_PRIMARY_CTRL);
|
||||
}
|
||||
|
||||
bool ata_wait_ready(void) {
|
||||
for (int i = 0; i < ATA_TIMEOUT; i++) {
|
||||
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
|
||||
/* Must NOT be busy AND must be ready */
|
||||
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ata_wait(uint8_t mask) {
|
||||
for (int i = 0; i < ATA_TIMEOUT; i++) {
|
||||
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
|
||||
/* If ERR is set, stop waiting and return failure */
|
||||
if (status & ATA_SR_ERR) return false;
|
||||
|
||||
if (!(status & ATA_SR_BSY) && (status & mask))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ata_init(void) {
|
||||
/* Select drive */
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, ATA_MASTER);
|
||||
ata_delay();
|
||||
|
||||
/* Check if drive exists */
|
||||
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
|
||||
if (status == 0xFF || status == 0) return false;
|
||||
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||
ata_delay();
|
||||
|
||||
if (!ata_wait(ATA_SR_DRQ))
|
||||
return false;
|
||||
|
||||
uint16_t identify[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
identify[i] = inw(ATA_PRIMARY_IO);
|
||||
|
||||
print_string("[ATA] Primary master detected\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ata_read_sector(uint32_t lba, uint8_t* buffer) {
|
||||
if (!buffer) return false;
|
||||
|
||||
/* 1. Wait for drive to be ready for command */
|
||||
if (!ata_wait_ready()) return false;
|
||||
|
||||
/* 2. Setup Task File (LBA28) */
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F));
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1);
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba));
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
|
||||
/* 3. Issue Read Command */
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||
|
||||
/* 4. Wait for Data Request (DRQ) */
|
||||
if (!ata_wait(ATA_SR_DRQ))
|
||||
return false;
|
||||
|
||||
/* 5. Transfer data */
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint16_t data = inw(ATA_PRIMARY_IO);
|
||||
buffer[i * 2] = data & 0xFF;
|
||||
buffer[i * 2 + 1] = (data >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
ata_delay();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ata_write_sector(uint32_t lba, const uint8_t* buffer) {
|
||||
if (!buffer) return false;
|
||||
|
||||
/* 1. Wait for drive to be ready for command */
|
||||
if (!ata_wait_ready()) return false;
|
||||
|
||||
/* 2. Setup Task File */
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F));
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1);
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba));
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
|
||||
/* 3. Issue Write Command */
|
||||
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||
|
||||
/* 4. Wait for drive to request data */
|
||||
if (!ata_wait(ATA_SR_DRQ))
|
||||
return false;
|
||||
|
||||
/* 5. Transfer data */
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint16_t word = buffer[i * 2] | (buffer[i * 2 + 1] << 8);
|
||||
outw(ATA_PRIMARY_IO, word);
|
||||
}
|
||||
|
||||
ata_delay();
|
||||
return true;
|
||||
}
|
||||
44
kernel/ata.h
Normal file
44
kernel/ata.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef ATA_H
|
||||
#define ATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* ATA I/O ports */
|
||||
#define ATA_PRIMARY_IO 0x1F0
|
||||
#define ATA_PRIMARY_CTRL 0x3F6
|
||||
|
||||
/* ATA registers */
|
||||
#define ATA_REG_DATA 0x00
|
||||
#define ATA_REG_ERROR 0x01
|
||||
#define ATA_REG_FEATURES 0x01
|
||||
#define ATA_REG_SECCOUNT0 0x02
|
||||
#define ATA_REG_LBA0 0x03
|
||||
#define ATA_REG_LBA1 0x04
|
||||
#define ATA_REG_LBA2 0x05
|
||||
#define ATA_REG_HDDEVSEL 0x06
|
||||
#define ATA_REG_COMMAND 0x07
|
||||
#define ATA_REG_STATUS 0x07
|
||||
|
||||
/* ATA commands */
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
|
||||
/* Status flags */
|
||||
#define ATA_SR_BSY 0x80
|
||||
#define ATA_SR_DRDY 0x40
|
||||
#define ATA_SR_DRQ 0x08
|
||||
#define ATA_SR_ERR 0x01
|
||||
|
||||
/* Drive select */
|
||||
#define ATA_MASTER 0xA0
|
||||
#define ATA_SLAVE 0xB0
|
||||
|
||||
/* Public API */
|
||||
bool ata_init(void);
|
||||
bool ata_read_sector(uint32_t lba, uint8_t* buffer);
|
||||
bool ata_write_sector(uint32_t lba, const uint8_t* buffer);
|
||||
bool ata_wait_ready(void);
|
||||
|
||||
#endif
|
||||
25
kernel/context_switch.asm
Normal file
25
kernel/context_switch.asm
Normal file
@@ -0,0 +1,25 @@
|
||||
global ctx_switch
|
||||
|
||||
; void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp);
|
||||
; Arguments on stack (cdecl convention):
|
||||
; [ESP + 4] -> old_sp_ptr (pointer to the 'stack_ptr' field of current task)
|
||||
; [ESP + 8] -> new_sp (value of 'stack_ptr' of the next task)
|
||||
|
||||
ctx_switch:
|
||||
; 1. Save the context of the CURRENT task
|
||||
pushf ; Save EFLAGS (CPU status flags)
|
||||
pusha ; Save all General Purpose Regs (EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI)
|
||||
|
||||
; 2. Save the current stack pointer (ESP) into the pointer passed as 1st arg
|
||||
mov eax, [esp + 40] ; Get 1st argument (old_sp_ptr). Offset 40 = 36 (regs) + 4 (ret addr)
|
||||
mov [eax], esp ; *old_sp_ptr = ESP
|
||||
|
||||
; 3. Load the stack pointer of the NEW task
|
||||
mov esp, [esp + 44] ; Get 2nd argument (new_sp). Offset 44 = 40 + 4
|
||||
|
||||
; 4. Restore the context of the NEW task
|
||||
popa ; Restore all General Purpose Regs
|
||||
popf ; Restore EFLAGS
|
||||
|
||||
; 5. Jump to the new task (The 'ret' pops EIP from the new stack)
|
||||
ret
|
||||
107
kernel/cpu.c
Normal file
107
kernel/cpu.c
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "cpu.h"
|
||||
#include "serial.h"
|
||||
#include "terminal.h"
|
||||
#include "utils.h"
|
||||
|
||||
void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
|
||||
__asm__(
|
||||
"cpuid"
|
||||
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
||||
: "a"(leaf)
|
||||
);
|
||||
}
|
||||
|
||||
// Helper to print a labeled decimal value
|
||||
void print_val(const char* label, uint32_t val) {
|
||||
char buf[12];
|
||||
utoa(val, buf, 10);
|
||||
terminal_write(label);
|
||||
terminal_write(buf);
|
||||
terminal_write(" ");
|
||||
}
|
||||
|
||||
// Safely check if CPUID is supported by attempting to flip bit 21 of EFLAGS
|
||||
int check_cpuid_supported() {
|
||||
uint32_t f1, f2;
|
||||
__asm__ volatile (
|
||||
"pushfl\n\t"
|
||||
"pushfl\n\t"
|
||||
"popl %0\n\t"
|
||||
"movl %0, %1\n\t"
|
||||
"xorl $0x200000, %0\n\t"
|
||||
"pushl %0\n\t"
|
||||
"popfl\n\t"
|
||||
"pushfl\n\t"
|
||||
"popl %0\n\t"
|
||||
"popfl\n\t"
|
||||
: "=&r" (f1), "=&r" (f2));
|
||||
return ((f1 ^ f2) & 0x200000) != 0;
|
||||
}
|
||||
|
||||
void identify_cpu() {
|
||||
if (!check_cpuid_supported()) {
|
||||
terminal_write("CPUID not supported. Likely a 386 or early 486.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
char vendor[13];
|
||||
|
||||
// Leaf 0: Vendor String & Max Leaf
|
||||
cpuid(0, &eax, &ebx, &ecx, &edx);
|
||||
uint32_t max_leaf = eax;
|
||||
*(uint32_t *)&vendor[0] = ebx;
|
||||
*(uint32_t *)&vendor[4] = edx;
|
||||
*(uint32_t *)&vendor[8] = ecx;
|
||||
vendor[12] = '\0';
|
||||
|
||||
terminal_write("Vendor: ");
|
||||
terminal_write(vendor);
|
||||
terminal_write("\n");
|
||||
|
||||
// Leaf 1: Family, Model, Stepping
|
||||
if (max_leaf >= 1) {
|
||||
cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
uint32_t stepping = eax & 0xF;
|
||||
uint32_t model = (eax >> 4) & 0xF;
|
||||
uint32_t family = (eax >> 8) & 0xF;
|
||||
uint32_t type = (eax >> 12) & 0x3;
|
||||
|
||||
// Handle Extended Family/Model (Required for Pentium 4 and newer)
|
||||
if (family == 0xF) {
|
||||
family += (eax >> 20) & 0xFF;
|
||||
model += ((eax >> 16) & 0xF) << 4;
|
||||
}
|
||||
|
||||
print_val("Family:", family);
|
||||
print_val("Model:", model);
|
||||
print_val("Step:", stepping);
|
||||
terminal_write("\n");
|
||||
}
|
||||
|
||||
// Leaf 2: Cache Descriptors
|
||||
if (max_leaf >= 2) {
|
||||
cpuid(2, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
terminal_write("Cache Descriptors: ");
|
||||
// Note: Leaf 2 returns a list of 1-byte descriptors in the registers.
|
||||
// We look for common Intel ones:
|
||||
uint32_t regs[4] = {eax, ebx, ecx, edx};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (regs[i] & 0x80000000) continue; // Reserved bit
|
||||
for (int j = 0; j < 4; j++) {
|
||||
uint8_t desc = (regs[i] >> (j * 8)) & 0xFF;
|
||||
if (desc == 0) continue;
|
||||
|
||||
// Example decoding for specific chips you mentioned:
|
||||
if (desc == 0x06) terminal_write("8KB L1 I-Cache ");
|
||||
if (desc == 0x0A) terminal_write("8KB L1 D-Cache ");
|
||||
if (desc == 0x41) terminal_write("128KB L2 ");
|
||||
if (desc == 0x43) terminal_write("512KB L2 ");
|
||||
if (desc == 0x2C) terminal_write("32KB L1 D-Cache ");
|
||||
}
|
||||
}
|
||||
terminal_write("\n");
|
||||
}
|
||||
}
|
||||
43
kernel/cpu.h
Normal file
43
kernel/cpu.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Specific Intel Model Definitions for your targets
|
||||
#define INTEL_FAM4_486_DX 0x00 // Also 0x01
|
||||
#define INTEL_FAM4_486_SX 0x02
|
||||
#define INTEL_FAM4_486_DX2 0x03
|
||||
#define INTEL_FAM4_486_DX4 0x08
|
||||
#define INTEL_FAM5_PENTIUM 0x01 // P5
|
||||
#define INTEL_FAM5_PENTIUM_MMX 0x04 // P55C
|
||||
#define INTEL_FAM6_PENTIUM_PRO 0x01 // P6
|
||||
#define INTEL_FAM6_PENTIUM_II 0x05 // Deschutes
|
||||
#define INTEL_FAM6_PENTIUM_III 0x07 // Katmai/Coppermine
|
||||
#define INTEL_FAM15_P4_WILLY 0x00 // Willamette
|
||||
#define INTEL_FAM15_P4_NORTH 0x02 // Northwood
|
||||
#define INTEL_FAM15_P4_PRES 0x03 // Prescott
|
||||
|
||||
typedef struct {
|
||||
char vendor[13];
|
||||
uint32_t family;
|
||||
uint32_t model;
|
||||
uint32_t stepping;
|
||||
uint32_t type;
|
||||
uint32_t max_leaf;
|
||||
|
||||
// Feature flags (optional, but very helpful later)
|
||||
bool has_fpu;
|
||||
bool has_mmx;
|
||||
bool has_sse;
|
||||
} cpu_info_t;
|
||||
|
||||
// Function Prototypes
|
||||
void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
|
||||
bool cpu_check_cpuid_support(void);
|
||||
void identify_cpu(void);
|
||||
|
||||
// Helper to get the current CPU info after identification
|
||||
cpu_info_t* cpu_get_info(void);
|
||||
|
||||
#endif // CPU_H
|
||||
45
kernel/debug.c
Normal file
45
kernel/debug.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "debug.h"
|
||||
#include "vga.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define VGA_WIDTH 80
|
||||
#define VGA_HEIGHT 25
|
||||
#define VGA_MEMORY 0xB8000
|
||||
|
||||
// VGA text mode color attributes
|
||||
#define COLOR_WHITE 0x07
|
||||
|
||||
// Pointer to the VGA memory
|
||||
volatile uint16_t* vga_buffer = (uint16_t*)VGA_MEMORY;
|
||||
|
||||
// Function to print a string to the VGA text buffer
|
||||
void debug_print(const char *str) {
|
||||
while (*str) {
|
||||
if (*str == '\n') {
|
||||
// Handle new line
|
||||
// Move to the next line (not implemented here)
|
||||
// You can implement line wrapping if needed
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the position in the VGA buffer
|
||||
static int cursor_x = 0;
|
||||
static int cursor_y = 0;
|
||||
|
||||
// Write the character and its attribute to the VGA buffer
|
||||
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = (COLOR_WHITE << 8) | *str;
|
||||
|
||||
// Move the cursor to the right
|
||||
cursor_x++;
|
||||
if (cursor_x >= VGA_WIDTH) {
|
||||
cursor_x = 0;
|
||||
cursor_y++;
|
||||
if (cursor_y >= VGA_HEIGHT) {
|
||||
cursor_y = 0; // Scroll up (not implemented here)
|
||||
}
|
||||
}
|
||||
|
||||
str++;
|
||||
}
|
||||
}
|
||||
6
kernel/debug.h
Normal file
6
kernel/debug.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
void debug_print(const char *str);
|
||||
|
||||
#endif // DEBUG_H
|
||||
80
kernel/display.c
Normal file
80
kernel/display.c
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <string.h>
|
||||
#include "display.h"
|
||||
#include "io.h"
|
||||
#include "vga.h"
|
||||
|
||||
// Initialize the display
|
||||
void init_display(void) {
|
||||
// Initialize the VGA driver. This typically sets up the 80x25 text mode,
|
||||
// clears the screen, and sets the cursor.
|
||||
vga_init();
|
||||
}
|
||||
|
||||
// Enumerate connected displays
|
||||
void enumerate_displays(void) {
|
||||
// This function is often a complex operation in a real driver.
|
||||
// In this simplified kernel/VGA text mode environment, we use printf
|
||||
// to output a message and rely on the fact that VGA is present.
|
||||
|
||||
// Clear the display before printing a message
|
||||
vga_clear(vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK));
|
||||
|
||||
// Output a simplified enumeration message
|
||||
vga_printf("Display: Standard VGA Text Mode (80x25) Detected.\n");
|
||||
|
||||
// In a real driver, you would use inb() and outb() with specific VGA ports
|
||||
// to read information (e.g., from the CRTC registers 0x3D4/0x3D5)
|
||||
// to check for display presence or configuration.
|
||||
}
|
||||
|
||||
// Set the display mode
|
||||
// NOTE: Setting arbitrary VGA modes (like 0x13 for 320x200) is very complex
|
||||
// and requires writing hundreds of register values, often done via BIOS in
|
||||
// real mode. Since we are in protected mode and have a simple text driver,
|
||||
// this function is kept simple or treated as a placeholder for full mode changes.
|
||||
void set_display_mode(uint8_t mode) {
|
||||
// Check if the requested mode is a known mode (e.g., VGA Text Mode 3)
|
||||
// For this example, we simply acknowledge the call.
|
||||
// A true mode set would involve complex register sequencing.
|
||||
|
||||
// The provided vga.c is a Text Mode driver, so a graphical mode set
|
||||
// like 0x13 (320x200 256-color) would break the existing vga_printf functionality.
|
||||
|
||||
// A simplified text-mode-specific response:
|
||||
if (mode == 0x03) { // Mode 3 is standard 80x25 text mode
|
||||
vga_printf("Display mode set to 80x25 Text Mode (Mode 0x03).\n");
|
||||
vga_init(); // Re-initialize the text mode
|
||||
} else {
|
||||
// Simple I/O example based on the original structure (Caution: Incomplete for full mode set)
|
||||
outb(VGA_PORT, mode); // Example function to write to a port
|
||||
vga_printf("Attempting to set display mode to 0x%x. (Warning: May break current display)\n", mode);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the display
|
||||
void clear_display(void) {
|
||||
// Use the VGA driver's clear function, typically clearing to black on light grey
|
||||
// or black on black. We'll use the black on light grey from vga_init for consistency.
|
||||
vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY));
|
||||
// Reset cursor to 0, 0
|
||||
vga_set_cursor_position(0, 0);
|
||||
}
|
||||
|
||||
// Helper function to write a string
|
||||
void display_write_string(const char* str) {
|
||||
// Use the VGA driver's string writing function
|
||||
vga_write_string(str, strlen(str));
|
||||
}
|
||||
|
||||
// Helper function to print a formatted string
|
||||
void display_printf(const char* format, ...) {
|
||||
// Use the VGA driver's printf function
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
// The vga_printf function already handles the va_list internally,
|
||||
// so we can just call it directly.
|
||||
vga_printf(format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
22
kernel/display.h
Normal file
22
kernel/display.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "vga.h" // Include VGA functions
|
||||
|
||||
#define VGA_PORT 0x3C0 // Base port for VGA (Often used for general control, though 0x3D4/0x3D5 are used for cursor)
|
||||
|
||||
// Function prototypes
|
||||
void init_display(void);
|
||||
void enumerate_displays(void);
|
||||
void set_display_mode(uint8_t mode); // In this context, modes are typically BIOS or VESA modes, which are complex.
|
||||
// We'll treat this as a placeholder/simple mode call.
|
||||
void clear_display(void);
|
||||
|
||||
// New function to write a string using the VGA driver
|
||||
void display_write_string(const char* str);
|
||||
|
||||
// New function to print a formatted string using the VGA driver
|
||||
void display_printf(const char* format, ...);
|
||||
|
||||
#endif // DISPLAY_H
|
||||
50
kernel/elf.c
Normal file
50
kernel/elf.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "elf.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// This assumes that the elf is ELF32 and little-endian Intel X86 architecture.
|
||||
|
||||
// Check if a binary is a valid ELF executable.
|
||||
int elf_validate(const void* data) {
|
||||
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
|
||||
|
||||
if (*(uint32_t*)header->e_ident != ELF_MAGIC)
|
||||
return 0;
|
||||
|
||||
if (header->e_type != ET_EXEC && header->e_type != ET_DYN)
|
||||
return 0;
|
||||
|
||||
if (header->e_machine != EM_386)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load an ELF executable into memory.
|
||||
int elf_load(const void* data, void (*load_segment)(uint32_t vaddr, const void* src, uint32_t size)) {
|
||||
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
|
||||
const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff);
|
||||
|
||||
for (int i = 0; i < header->e_phnum; i++) {
|
||||
if (ph[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
const void* src = (uint8_t*)data + ph[i].p_offset;
|
||||
uint32_t vaddr = ph[i].p_vaddr;
|
||||
uint32_t filesz = ph[i].p_filesz;
|
||||
uint32_t memsz = ph[i].p_memsz;
|
||||
|
||||
// Copy data segment
|
||||
load_segment(vaddr, src, filesz);
|
||||
|
||||
// Zero remaining BSS (if any)
|
||||
if (memsz > filesz) {
|
||||
uint8_t* bss_start = (uint8_t*)(vaddr + filesz);
|
||||
for (uint32_t j = 0; j < memsz - filesz; j++) {
|
||||
bss_start[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return header->e_entry;
|
||||
}
|
||||
52
kernel/elf.h
Normal file
52
kernel/elf.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef ELF_H
|
||||
#define ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ELF_MAGIC 0x464C457F // "\x7FELF" in little-endian
|
||||
|
||||
// ELF Types
|
||||
#define ET_EXEC 2
|
||||
#define ET_DYN 3
|
||||
|
||||
// ELF Machine
|
||||
#define EM_386 3
|
||||
|
||||
// ELF Ident indexes
|
||||
#define EI_NIDENT 16
|
||||
|
||||
// Program header types
|
||||
#define PT_NULL 0
|
||||
#define PT_LOAD 1
|
||||
|
||||
// ELF Header (32-bit)
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry; // Entry point
|
||||
uint32_t e_phoff; // Program header table offset
|
||||
uint32_t e_shoff; // Section header table offset
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} __attribute__((packed)) Elf32_Ehdr;
|
||||
|
||||
// Program Header (32-bit)
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
} __attribute__((packed)) Elf32_Phdr;
|
||||
|
||||
#endif // ELF_H
|
||||
196
kernel/fat12.c
Normal file
196
kernel/fat12.c
Normal file
@@ -0,0 +1,196 @@
|
||||
#include "fat12.h"
|
||||
#include <stddef.h> // for NULL
|
||||
|
||||
// --- Globals for Filesystem State ---
|
||||
static fat12_bpb_t bpb;
|
||||
static uint32_t fat_start_lba;
|
||||
static uint32_t root_dir_lba;
|
||||
static uint32_t data_start_lba;
|
||||
static uint32_t root_dir_sectors;
|
||||
|
||||
// Scratch buffer to read sectors (avoids large stack usage)
|
||||
static uint8_t g_sector_buffer[FAT12_SECTOR_SIZE];
|
||||
|
||||
// --- Utils (Since we don't have string.h) ---
|
||||
static int k_memcmp(const void *s1, const void *s2, uint32_t n) {
|
||||
const uint8_t *p1 = (const uint8_t *)s1;
|
||||
const uint8_t *p2 = (const uint8_t *)s2;
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
// Correct way to return the difference:
|
||||
// If p1[i] > p2[i], returns positive.
|
||||
// If p1[i] < p2[i], returns negative.
|
||||
return (int)p1[i] - (int)p2[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Converts "file.txt" to "FILE TXT" for comparison
|
||||
static void to_fat_name(const char *src, char *dest) {
|
||||
// Initialize with spaces
|
||||
for(int i=0; i<11; i++) dest[i] = ' ';
|
||||
|
||||
int i = 0, j = 0;
|
||||
// Copy Name
|
||||
while (src[i] != '\0' && src[i] != '.' && j < 8) {
|
||||
// Convert to uppercase (simple version)
|
||||
char c = src[i];
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
dest[j++] = c;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Skip extension dot
|
||||
if (src[i] == '.') i++;
|
||||
|
||||
// Copy Extension
|
||||
j = 8;
|
||||
while (src[i] != '\0' && j < 11) {
|
||||
char c = src[i];
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
dest[j++] = c;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Core Logic ---
|
||||
|
||||
void fat12_init() {
|
||||
// 1. Read Boot Sector (LBA 0)
|
||||
disk_read_sector(0, g_sector_buffer);
|
||||
|
||||
// 2. Copy BPB data safely
|
||||
// We cast the buffer to our struct
|
||||
fat12_bpb_t *boot_sector = (fat12_bpb_t*)g_sector_buffer;
|
||||
bpb = *boot_sector;
|
||||
|
||||
// 3. Calculate System Offsets
|
||||
fat_start_lba = bpb.reserved_sectors;
|
||||
|
||||
// Root Dir starts after FATs
|
||||
// LBA = Reserved + (FatCount * SectorsPerFat)
|
||||
root_dir_lba = fat_start_lba + (bpb.fat_count * bpb.sectors_per_fat);
|
||||
|
||||
// Calculate size of Root Directory in sectors
|
||||
// (Entries * 32 bytes) / 512
|
||||
root_dir_sectors = (bpb.dir_entries_count * 32 + FAT12_SECTOR_SIZE - 1) / FAT12_SECTOR_SIZE;
|
||||
|
||||
// Data starts after Root Directory
|
||||
data_start_lba = root_dir_lba + root_dir_sectors;
|
||||
}
|
||||
|
||||
// Helper: Read the FAT table to find the NEXT cluster
|
||||
static uint16_t fat12_get_next_cluster(uint16_t current_cluster) {
|
||||
// FAT12 Offset Calculation:
|
||||
// Offset = Cluster + (Cluster / 2)
|
||||
uint32_t fat_offset = current_cluster + (current_cluster / 2);
|
||||
|
||||
uint32_t fat_sector = fat_start_lba + (fat_offset / FAT12_SECTOR_SIZE);
|
||||
uint32_t ent_offset = fat_offset % FAT12_SECTOR_SIZE;
|
||||
|
||||
// Read the sector containing the FAT entry
|
||||
disk_read_sector(fat_sector, g_sector_buffer);
|
||||
|
||||
// Read 16 bits (2 bytes)
|
||||
// Note: If ent_offset == 511, the entry spans two sectors.
|
||||
// For simplicity in this snippet, we ignore that edge case (rare).
|
||||
// A robust kernel would check if(ent_offset == 511) and read next sector.
|
||||
|
||||
uint16_t val = *(uint16_t*)&g_sector_buffer[ent_offset];
|
||||
|
||||
if (current_cluster & 1) {
|
||||
return val >> 4; // Odd: High 12 bits
|
||||
} else {
|
||||
return val & 0x0FFF; // Even: Low 12 bits
|
||||
}
|
||||
}
|
||||
|
||||
file_t fat12_open(const char *filename) {
|
||||
file_t file = {0};
|
||||
char target_name[11];
|
||||
to_fat_name(filename, target_name);
|
||||
|
||||
// Search Root Directory
|
||||
for (uint32_t i = 0; i < root_dir_sectors; i++) {
|
||||
disk_read_sector(root_dir_lba + i, g_sector_buffer);
|
||||
|
||||
fat12_entry_t *entry = (fat12_entry_t*)g_sector_buffer;
|
||||
|
||||
// Check all 16 entries in this sector (512 / 32 = 16)
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (entry[j].filename[0] == 0x00) return file; // End of Dir
|
||||
|
||||
// Check if filename matches
|
||||
if (k_memcmp(entry[j].filename, target_name, 11) == 0) {
|
||||
// Found it!
|
||||
file.start_cluster = entry[j].low_cluster_num;
|
||||
file.size = entry[j].file_size;
|
||||
|
||||
// Initialize file cursor
|
||||
file.current_cluster = file.start_cluster;
|
||||
file.bytes_read = 0;
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found (file.start_cluster will be 0)
|
||||
return file;
|
||||
}
|
||||
|
||||
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read) {
|
||||
if (file->start_cluster == 0) return 0; // File not open
|
||||
|
||||
uint32_t total_read = 0;
|
||||
|
||||
while (bytes_to_read > 0) {
|
||||
// Check for EOF marker in FAT12 (>= 0xFF8)
|
||||
if (file->current_cluster >= 0xFF8) break;
|
||||
|
||||
// Calculate Physical LBA of current cluster
|
||||
// LBA = DataStart + ((Cluster - 2) * SectorsPerCluster)
|
||||
uint32_t lba = data_start_lba + ((file->current_cluster - 2) * bpb.sectors_per_cluster);
|
||||
|
||||
// Read the cluster
|
||||
// NOTE: Assumes SectorsPerCluster = 1 (Standard Floppy)
|
||||
disk_read_sector(lba, g_sector_buffer);
|
||||
|
||||
// Determine how much to copy from this sector
|
||||
uint32_t chunk_size = FAT12_SECTOR_SIZE;
|
||||
|
||||
// If the file is smaller than a sector, or we are at the end
|
||||
if (chunk_size > bytes_to_read) chunk_size = bytes_to_read;
|
||||
|
||||
// Check if we are reading past file size
|
||||
if (file->bytes_read + chunk_size > file->size) {
|
||||
chunk_size = file->size - file->bytes_read;
|
||||
}
|
||||
|
||||
// Copy to user buffer
|
||||
for (uint32_t i = 0; i < chunk_size; i++) {
|
||||
buffer[total_read + i] = g_sector_buffer[i];
|
||||
}
|
||||
|
||||
total_read += chunk_size;
|
||||
file->bytes_read += chunk_size;
|
||||
bytes_to_read -= chunk_size;
|
||||
|
||||
// If we finished this cluster, move to the next one
|
||||
if (chunk_size == FAT12_SECTOR_SIZE) { // Or strictly logic based on position
|
||||
file->current_cluster = fat12_get_next_cluster(file->current_cluster);
|
||||
} else {
|
||||
// We finished the file or the request
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
int disk_read_sector(uint32_t lba, uint8_t *buffer) {
|
||||
// For now, do nothing and return success
|
||||
return 0;
|
||||
}
|
||||
67
kernel/fat12.h
Normal file
67
kernel/fat12.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef FAT12_H
|
||||
#define FAT12_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// --- Configuration ---
|
||||
#define FAT12_SECTOR_SIZE 512
|
||||
|
||||
// --- On-Disk Structures (Must be Packed) ---
|
||||
|
||||
// BIOS Parameter Block (Start of Boot Sector)
|
||||
typedef struct {
|
||||
uint8_t jump[3];
|
||||
char oem[8];
|
||||
uint16_t bytes_per_sector; // 512
|
||||
uint8_t sectors_per_cluster; // 1
|
||||
uint16_t reserved_sectors; // 1 (Boot sector)
|
||||
uint8_t fat_count; // 2
|
||||
uint16_t dir_entries_count; // 224
|
||||
uint16_t total_sectors; // 2880
|
||||
uint8_t media_descriptor; // 0xF0
|
||||
uint16_t sectors_per_fat; // 9
|
||||
uint16_t sectors_per_track; // 18
|
||||
uint16_t heads; // 2
|
||||
uint32_t hidden_sectors;
|
||||
uint32_t total_sectors_large;
|
||||
} __attribute__((packed)) fat12_bpb_t;
|
||||
|
||||
// Directory Entry (32 bytes)
|
||||
typedef struct {
|
||||
char filename[8];
|
||||
char ext[3];
|
||||
uint8_t attributes;
|
||||
uint8_t reserved;
|
||||
uint8_t creation_ms;
|
||||
uint16_t creation_time;
|
||||
uint16_t creation_date;
|
||||
uint16_t last_access_date;
|
||||
uint16_t high_cluster_num; // Always 0 in FAT12
|
||||
uint16_t last_mod_time;
|
||||
uint16_t last_mod_date;
|
||||
uint16_t low_cluster_num; // The starting cluster
|
||||
uint32_t file_size; // Size in bytes
|
||||
} __attribute__((packed)) fat12_entry_t;
|
||||
|
||||
// --- Kernel File Handle ---
|
||||
// This is what your kernel uses to track an open file
|
||||
typedef struct {
|
||||
char name[11];
|
||||
uint32_t size;
|
||||
uint16_t start_cluster;
|
||||
uint16_t current_cluster;
|
||||
uint32_t current_sector_in_cluster;
|
||||
uint32_t bytes_read;
|
||||
} file_t;
|
||||
|
||||
// --- Public API ---
|
||||
|
||||
// You must implement this in your disk driver (e.g., floppy.c)
|
||||
// Returns 0 on success, non-zero on error.
|
||||
int disk_read_sector(uint32_t lba, uint8_t *buffer);
|
||||
|
||||
void fat12_init();
|
||||
file_t fat12_open(const char *filename);
|
||||
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read);
|
||||
|
||||
#endif // FAT12_H
|
||||
107
kernel/fat16.c
Normal file
107
kernel/fat16.c
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "fat16.h"
|
||||
#include "ata.h" // Use ata_read_sector and ata_write_sector
|
||||
#include "print.h" // For debugging
|
||||
#include <string.h> // For string manipulation
|
||||
|
||||
// Global variables
|
||||
static fat16_boot_sector_t boot_sector;
|
||||
static uint32_t root_dir_sector = FAT16_ROOT_DIR_SECTOR;
|
||||
|
||||
// Read a sector from the disk using ATA
|
||||
bool read_sector(uint32_t lba, uint8_t* buffer) {
|
||||
return ata_read_sector(lba, buffer);
|
||||
}
|
||||
|
||||
// Write a sector to the disk using ATA
|
||||
bool write_sector(uint32_t lba, const uint8_t* buffer) {
|
||||
return ata_write_sector(lba, buffer);
|
||||
}
|
||||
|
||||
// Parse the boot sector to retrieve basic file system info
|
||||
bool parse_fat16_boot_sector(void) {
|
||||
uint8_t sector_buffer[FAT16_SECTOR_SIZE];
|
||||
|
||||
// Read the boot sector
|
||||
if (!read_sector(FAT16_BOOT_SECTOR, sector_buffer)) {
|
||||
print_string("[FAT16] Failed to read boot sector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cast to boot sector structure
|
||||
memcpy(&boot_sector, sector_buffer, sizeof(fat16_boot_sector_t));
|
||||
|
||||
// Check for FAT16 signature
|
||||
if (boot_sector.oem_name[0] != 'F' || boot_sector.oem_name[1] != 'A' || boot_sector.oem_name[2] != 'T') {
|
||||
print_string("[FAT16] Invalid FAT16 boot sector signature\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
print_string("[FAT16] FAT16 boot sector parsed successfully\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse the root directory
|
||||
bool parse_fat16_root_dir(void) {
|
||||
uint8_t sector_buffer[FAT16_SECTOR_SIZE];
|
||||
|
||||
for (int i = 0; i < (boot_sector.max_root_entries / (FAT16_SECTOR_SIZE / sizeof(fat16_dir_entry_t))); i++) {
|
||||
// Read root directory sector
|
||||
if (!read_sector(root_dir_sector + i, sector_buffer)) {
|
||||
print_string("[FAT16] Failed to read root directory sector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the root directory entries
|
||||
for (int j = 0; j < (FAT16_SECTOR_SIZE / sizeof(fat16_dir_entry_t)); j++) {
|
||||
fat16_dir_entry_t* entry = (fat16_dir_entry_t*)§or_buffer[j * sizeof(fat16_dir_entry_t)];
|
||||
if (entry->name[0] == 0x00) {
|
||||
// End of directory entries
|
||||
return true;
|
||||
}
|
||||
if (entry->name[0] != 0xE5) {
|
||||
// Print file name (8.3 format)
|
||||
char filename[12];
|
||||
strncpy(filename, (char*)entry->name, 8);
|
||||
filename[8] = '.';
|
||||
strncpy(&filename[9], (char*)entry->ext, 3);
|
||||
filename[11] = '\0';
|
||||
print_string(filename);
|
||||
print_string("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read a specific directory entry from the FAT16 root directory
|
||||
bool read_fat16_entry(uint16_t entry_index, fat16_dir_entry_t* entry) {
|
||||
uint8_t sector_buffer[FAT16_SECTOR_SIZE];
|
||||
uint32_t sector_num = FAT16_ROOT_DIR_SECTOR + (entry_index / (FAT16_SECTOR_SIZE / sizeof(fat16_dir_entry_t)));
|
||||
uint16_t entry_offset = entry_index % (FAT16_SECTOR_SIZE / sizeof(fat16_dir_entry_t));
|
||||
|
||||
// Read the sector
|
||||
if (!read_sector(sector_num, sector_buffer)) {
|
||||
print_string("[FAT16] Failed to read root directory sector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the entry
|
||||
memcpy(entry, §or_buffer[entry_offset * sizeof(fat16_dir_entry_t)], sizeof(fat16_dir_entry_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mount the FAT16 filesystem
|
||||
bool mount_fat16(void) {
|
||||
// Parse the boot sector
|
||||
if (!parse_fat16_boot_sector()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the root directory
|
||||
if (!parse_fat16_root_dir()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
print_string("[FAT16] Filesystem mounted successfully\n");
|
||||
return true;
|
||||
}
|
||||
60
kernel/fat16.h
Normal file
60
kernel/fat16.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef FAT16_H
|
||||
#define FAT16_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* FAT16 Constants */
|
||||
#define FAT16_SECTOR_SIZE 512
|
||||
#define FAT16_CLUSTER_SIZE 1
|
||||
#define FAT16_MAX_FILENAME_LEN 11 // 8.3 format
|
||||
#define FAT16_ROOT_DIR_ENTRIES 224 // Fat16 root directory entries (typically 512 bytes per entry)
|
||||
|
||||
#define FAT16_BOOT_SECTOR 0
|
||||
#define FAT16_FAT1_SECTOR 1
|
||||
#define FAT16_FAT2_SECTOR 2
|
||||
#define FAT16_ROOT_DIR_SECTOR 19 // First sector of root directory
|
||||
|
||||
/* Boot Sector */
|
||||
typedef struct {
|
||||
uint8_t jmp[3]; // Jump instruction to code
|
||||
uint8_t oem_name[8]; // OEM Name
|
||||
uint16_t bytes_per_sector; // Bytes per sector (512)
|
||||
uint8_t sectors_per_cluster; // Sectors per cluster
|
||||
uint16_t reserved_sectors; // Reserved sectors
|
||||
uint8_t num_fats; // Number of FAT tables
|
||||
uint16_t max_root_entries; // Max number of root directory entries
|
||||
uint16_t total_sectors_16; // Total sectors in FAT16
|
||||
uint8_t media_type; // Media type (0xF8 = fixed drive)
|
||||
uint16_t sectors_per_fat; // Sectors per FAT table
|
||||
uint16_t sectors_per_track; // Sectors per track (for CHS addressing)
|
||||
uint16_t num_heads; // Number of heads (for CHS addressing)
|
||||
uint32_t hidden_sectors; // Hidden sectors (before the partition)
|
||||
uint32_t total_sectors_32; // Total sectors in FAT16 (extended)
|
||||
} __attribute__((packed)) fat16_boot_sector_t;
|
||||
|
||||
/* FAT16 Directory Entry */
|
||||
typedef struct {
|
||||
uint8_t name[8]; // File name (8 chars)
|
||||
uint8_t ext[3]; // File extension (3 chars)
|
||||
uint8_t attributes; // File attributes (e.g., directory, read-only)
|
||||
uint8_t reserved; // Reserved
|
||||
uint8_t creation_time[2]; // Creation time
|
||||
uint8_t creation_date[2]; // Creation date
|
||||
uint8_t last_access_date[2]; // Last access date
|
||||
uint8_t first_cluster_high[2]; // High part of first cluster number
|
||||
uint8_t last_mod_time[2]; // Last modification time
|
||||
uint8_t last_mod_date[2]; // Last modification date
|
||||
uint8_t first_cluster_low[2]; // Low part of first cluster number
|
||||
uint32_t file_size; // File size in bytes
|
||||
} __attribute__((packed)) fat16_dir_entry_t;
|
||||
|
||||
/* Function Prototypes */
|
||||
bool mount_fat16(void);
|
||||
bool read_sector(uint32_t lba, uint8_t* buffer);
|
||||
bool write_sector(uint32_t lba, const uint8_t* buffer);
|
||||
bool parse_fat16_boot_sector(void);
|
||||
bool parse_fat16_root_dir(void);
|
||||
bool read_fat16_entry(uint16_t entry_index, fat16_dir_entry_t* entry);
|
||||
|
||||
#endif // FAT16_H
|
||||
92
kernel/framebuffer.c
Normal file
92
kernel/framebuffer.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "framebuffer.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Simple init
|
||||
void framebuffer_init(framebuffer_t *fb, void *base, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp) {
|
||||
fb->base = base;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
fb->pitch = pitch;
|
||||
fb->bpp = bpp;
|
||||
fb->initialized = true;
|
||||
}
|
||||
|
||||
// Pack color into 32-bit value. Format: 0xAARRGGBB
|
||||
uint32_t framebuffer_pack_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
|
||||
void framebuffer_put_pixel(framebuffer_t *fb, uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
if (!fb->initialized) return;
|
||||
if (x >= fb->width || y >= fb->height) return;
|
||||
if (fb->bpp != 32) return; // only 32bpp implemented here
|
||||
|
||||
uint8_t *line = (uint8_t*)fb->base + (size_t)y * fb->pitch;
|
||||
uint32_t *pixel = (uint32_t*)(line + x * 4);
|
||||
*pixel = framebuffer_pack_color(r, g, b, a);
|
||||
}
|
||||
|
||||
void framebuffer_clear(framebuffer_t *fb, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if (!fb->initialized) return;
|
||||
if (fb->bpp != 32) return;
|
||||
|
||||
uint32_t color = framebuffer_pack_color(r,g,b,0xFF);
|
||||
for (uint32_t y = 0; y < fb->height; ++y) {
|
||||
uint32_t *row = (uint32_t*)((uint8_t*)fb->base + (size_t)y * fb->pitch);
|
||||
for (uint32_t x = 0; x < fb->width; ++x) {
|
||||
row[x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void framebuffer_fill_rect(framebuffer_t *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if (!fb->initialized) return;
|
||||
if (fb->bpp != 32) return;
|
||||
|
||||
if (x >= fb->width || y >= fb->height) return;
|
||||
if (x + w > fb->width) w = fb->width - x;
|
||||
if (y + h > fb->height) h = fb->height - y;
|
||||
|
||||
uint32_t color = framebuffer_pack_color(r,g,b,0xFF);
|
||||
for (uint32_t yy = 0; yy < h; ++yy) {
|
||||
uint32_t *row = (uint32_t*)((uint8_t*)fb->base + (size_t)(y + yy) * fb->pitch) + x;
|
||||
for (uint32_t xx = 0; xx < w; ++xx) {
|
||||
row[xx] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple blit from a source buffer with 32bpp pixels and given pitch (bytes per line)
|
||||
void framebuffer_blit(framebuffer_t *fb, uint32_t dst_x, uint32_t dst_y, const void *src, uint32_t src_w, uint32_t src_h, uint32_t src_pitch) {
|
||||
if (!fb->initialized) return;
|
||||
if (fb->bpp != 32) return;
|
||||
|
||||
if (dst_x >= fb->width || dst_y >= fb->height) return;
|
||||
|
||||
uint32_t copy_w = src_w;
|
||||
uint32_t copy_h = src_h;
|
||||
if (dst_x + copy_w > fb->width) copy_w = fb->width - dst_x;
|
||||
if (dst_y + copy_h > fb->height) copy_h = fb->height - dst_y;
|
||||
|
||||
const uint8_t *s = (const uint8_t*)src;
|
||||
for (uint32_t yy = 0; yy < copy_h; ++yy) {
|
||||
uint32_t *dst_row = (uint32_t*)((uint8_t*)fb->base + (size_t)(dst_y + yy) * fb->pitch) + dst_x;
|
||||
const uint32_t *src_row = (const uint32_t*)(s + (size_t)yy * src_pitch);
|
||||
for (uint32_t xx = 0; xx < copy_w; ++xx) {
|
||||
dst_row[xx] = src_row[xx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void framebuffer_test_pattern(framebuffer_t *fb) {
|
||||
if (!fb->initialized) return;
|
||||
// simple color bars
|
||||
uint32_t band_h = fb->height / 6;
|
||||
framebuffer_fill_rect(fb, 0, 0, fb->width, band_h, 0xFF, 0x00, 0x00); // red
|
||||
framebuffer_fill_rect(fb, 0, band_h, fb->width, band_h, 0x00, 0xFF, 0x00); // green
|
||||
framebuffer_fill_rect(fb, 0, band_h*2, fb->width, band_h, 0x00, 0x00, 0xFF); // blue
|
||||
framebuffer_fill_rect(fb, 0, band_h*3, fb->width, band_h, 0xFF, 0xFF, 0x00); // yellow
|
||||
framebuffer_fill_rect(fb, 0, band_h*4, fb->width, band_h, 0xFF, 0x00, 0xFF); // magenta
|
||||
framebuffer_fill_rect(fb, 0, band_h*5, fb->width, fb->height - band_h*5, 0x00, 0xFF, 0xFF); // cyan
|
||||
}
|
||||
25
kernel/framebuffer.h
Normal file
25
kernel/framebuffer.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef FRAMEBUFFER_H
|
||||
#define FRAMEBUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
void *base;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t pitch;
|
||||
uint8_t bpp;
|
||||
bool initialized;
|
||||
} framebuffer_t;
|
||||
|
||||
void framebuffer_init(framebuffer_t *fb, void *base, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp);
|
||||
uint32_t framebuffer_pack_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
void framebuffer_put_pixel(framebuffer_t *fb, uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
void framebuffer_clear(framebuffer_t *fb, uint8_t r, uint8_t g, uint8_t b);
|
||||
void framebuffer_fill_rect(framebuffer_t *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t r, uint8_t g, uint8_t b);
|
||||
void framebuffer_blit(framebuffer_t *fb, uint32_t dst_x, uint32_t dst_y, const void *src, uint32_t src_w, uint32_t src_h, uint32_t src_pitch);
|
||||
void framebuffer_test_pattern(framebuffer_t *fb);
|
||||
|
||||
#endif /* FRAMEBUFFER_H */
|
||||
21
kernel/gdt.asm
Normal file
21
kernel/gdt.asm
Normal file
@@ -0,0 +1,21 @@
|
||||
; gdt.asm
|
||||
; Assembler function to load the GDT and update segment registers
|
||||
|
||||
global gdt_flush
|
||||
|
||||
gdt_flush:
|
||||
mov eax, [esp + 4] ; Argument: pointer to GDT descriptor
|
||||
lgdt [eax] ; Load GDT
|
||||
|
||||
; Reload segment registers
|
||||
mov ax, 0x10 ; 0x10 = offset to kernel data segment (3rd entry)
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Jump to flush the instruction pipeline and load CS
|
||||
jmp 0x08:.flush ; 0x08 = offset to code segment (2nd entry)
|
||||
.flush:
|
||||
ret
|
||||
48
kernel/gdt.c
Normal file
48
kernel/gdt.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "gdt.h"
|
||||
|
||||
// Structure of a GDT entry (8 bytes)
|
||||
struct gdt_entry {
|
||||
uint16_t limit_low; // Limit bits 0–15
|
||||
uint16_t base_low; // Base bits 0–15
|
||||
uint8_t base_middle; // Base bits 16–23
|
||||
uint8_t access; // Access flags
|
||||
uint8_t granularity; // Granularity + limit bits 16–19
|
||||
uint8_t base_high; // Base bits 24–31
|
||||
} __attribute__((packed));
|
||||
|
||||
// Structure of the GDT pointer
|
||||
struct gdt_ptr {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
// Declare GDT with 3 entries
|
||||
static struct gdt_entry gdt[3];
|
||||
static struct gdt_ptr gp;
|
||||
|
||||
// External ASM function to load GDT
|
||||
extern void gdt_flush(uint32_t);
|
||||
|
||||
// Set an individual GDT entry
|
||||
static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) {
|
||||
gdt[num].base_low = (base & 0xFFFF);
|
||||
gdt[num].base_middle = (base >> 16) & 0xFF;
|
||||
gdt[num].base_high = (base >> 24) & 0xFF;
|
||||
|
||||
gdt[num].limit_low = (limit & 0xFFFF);
|
||||
gdt[num].granularity = ((limit >> 16) & 0x0F) | (granularity & 0xF0);
|
||||
|
||||
gdt[num].access = access;
|
||||
}
|
||||
|
||||
// Initialize the GDT
|
||||
void gdt_init(void) {
|
||||
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
|
||||
gp.base = (uint32_t)&gdt;
|
||||
|
||||
gdt_set_gate(0, 0, 0, 0, 0); // Null segment
|
||||
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
|
||||
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
|
||||
|
||||
gdt_flush((uint32_t)&gp);
|
||||
}
|
||||
8
kernel/gdt.h
Normal file
8
kernel/gdt.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef GDT_H
|
||||
#define GDT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void gdt_init(void);
|
||||
|
||||
#endif
|
||||
66
kernel/gui.c
Normal file
66
kernel/gui.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "gui.h"
|
||||
#include "vga.h" // VGA functions for drawing and clearing screen
|
||||
#include "framebuffer.h" // For pixel manipulation if needed
|
||||
|
||||
// Initialize the GUI (could set up any global state or variables here)
|
||||
void gui_init(void) {
|
||||
// Clear the screen with black or any color
|
||||
gui_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE));
|
||||
}
|
||||
|
||||
// Draw a window (simple rectangle with a title)
|
||||
void gui_draw_window(gui_window_t* window) {
|
||||
// Draw the window's border
|
||||
for (uint32_t y = 0; y < window->height; ++y) {
|
||||
for (uint32_t x = 0; x < window->width; ++x) {
|
||||
// Check if we are at the border
|
||||
if (x == 0 || y == 0 || x == window->width - 1 || y == window->height - 1) {
|
||||
vga_put_entry_at('#', vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), window->x + x, window->y + y);
|
||||
} else {
|
||||
// Fill the inside of the window
|
||||
vga_put_entry_at(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_BLACK), window->x + x, window->y + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the title at the top
|
||||
if (window->title) {
|
||||
size_t i = 0;
|
||||
while (window->title[i] != '\0' && i < window->width - 2) {
|
||||
vga_put_entry_at(window->title[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK), window->x + i + 1, window->y);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a button (a simple rectangle with text in the middle)
|
||||
void gui_draw_button(gui_button_t* button) {
|
||||
for (uint32_t y = 0; y < button->height; ++y) {
|
||||
for (uint32_t x = 0; x < button->width; ++x) {
|
||||
// Check if we are at the border
|
||||
if (x == 0 || y == 0 || x == button->width - 1 || y == button->height - 1) {
|
||||
vga_put_entry_at('#', vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), button->x + x, button->y + y);
|
||||
} else {
|
||||
// Fill the inside of the button
|
||||
vga_put_entry_at(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_BLACK), button->x + x, button->y + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the label in the center of the button
|
||||
size_t label_len = 0;
|
||||
while (button->label[label_len] != '\0') {
|
||||
label_len++;
|
||||
}
|
||||
|
||||
size_t start_x = button->x + (button->width - label_len) / 2;
|
||||
size_t start_y = button->y + (button->height - 1) / 2;
|
||||
for (size_t i = 0; i < label_len; ++i) {
|
||||
vga_put_entry_at(button->label[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK), start_x + i, start_y);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the screen with a color
|
||||
void gui_clear(uint32_t color) {
|
||||
vga_clear(color); // Just clear the VGA screen with a solid color
|
||||
}
|
||||
34
kernel/gui.h
Normal file
34
kernel/gui.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define GUI_WINDOW_WIDTH 80
|
||||
#define GUI_WINDOW_HEIGHT 25
|
||||
#define GUI_BUTTON_WIDTH 10
|
||||
#define GUI_BUTTON_HEIGHT 3
|
||||
|
||||
// Window structure
|
||||
typedef struct {
|
||||
uint32_t x, y;
|
||||
uint32_t width, height;
|
||||
uint32_t color; // Background color
|
||||
const char* title;
|
||||
} gui_window_t;
|
||||
|
||||
// Button structure
|
||||
typedef struct {
|
||||
uint32_t x, y;
|
||||
uint32_t width, height;
|
||||
uint32_t color; // Background color
|
||||
const char* label;
|
||||
} gui_button_t;
|
||||
|
||||
// Function prototypes for GUI elements
|
||||
void gui_init(void);
|
||||
void gui_draw_window(gui_window_t* window);
|
||||
void gui_draw_button(gui_button_t* button);
|
||||
void gui_clear(uint32_t color);
|
||||
|
||||
#endif // GUI_H
|
||||
63
kernel/heap.c
Normal file
63
kernel/heap.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "heap.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct heap_block {
|
||||
size_t size;
|
||||
struct heap_block *next;
|
||||
int is_free;
|
||||
} heap_block_t;
|
||||
|
||||
static heap_block_t *free_list = NULL;
|
||||
static void *heap_start_ptr = NULL;
|
||||
static void *heap_end_ptr = NULL;
|
||||
|
||||
void heap_init(void *heap_start, void *heap_end) {
|
||||
heap_start_ptr = heap_start;
|
||||
heap_end_ptr = heap_end;
|
||||
|
||||
free_list = (heap_block_t *)heap_start;
|
||||
free_list->size = (uintptr_t)heap_end - (uintptr_t)heap_start - sizeof(heap_block_t);
|
||||
free_list->next = NULL;
|
||||
free_list->is_free = 1;
|
||||
}
|
||||
|
||||
void *heap_alloc(size_t size) {
|
||||
heap_block_t *current = free_list;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->is_free && current->size >= size) {
|
||||
// If it's a large block, split it
|
||||
if (current->size > size + sizeof(heap_block_t)) {
|
||||
heap_block_t *new_block = (heap_block_t *)((uintptr_t)current + sizeof(heap_block_t) + size);
|
||||
new_block->size = current->size - size - sizeof(heap_block_t);
|
||||
new_block->next = current->next;
|
||||
new_block->is_free = 1;
|
||||
|
||||
current->next = new_block;
|
||||
current->size = size;
|
||||
}
|
||||
|
||||
current->is_free = 0;
|
||||
return (void *)((uintptr_t)current + sizeof(heap_block_t));
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return NULL; // Out of memory
|
||||
}
|
||||
|
||||
void heap_free(void *ptr) {
|
||||
if (ptr == NULL) return;
|
||||
|
||||
heap_block_t *block = (heap_block_t *)((uintptr_t)ptr - sizeof(heap_block_t));
|
||||
block->is_free = 1;
|
||||
|
||||
// Coalesce with next block
|
||||
if (block->next && block->next->is_free) {
|
||||
block->size += block->next->size + sizeof(heap_block_t);
|
||||
block->next = block->next->next;
|
||||
}
|
||||
|
||||
// TODO: Coalesce with previous block for better compaction
|
||||
}
|
||||
11
kernel/heap.h
Normal file
11
kernel/heap.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef HEAP_H
|
||||
#define HEAP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void heap_init(void *heap_start, void *heap_end);
|
||||
|
||||
void *heap_alloc(size_t size);
|
||||
void heap_free(void *ptr);
|
||||
|
||||
#endif // HEAP_H
|
||||
65
kernel/hid.c
Normal file
65
kernel/hid.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "hid.h"
|
||||
#include "usb.h"
|
||||
#include "mouse.h"
|
||||
#include "keyboard.h"
|
||||
#include "print.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Global variables
|
||||
static bool hid_initialized = false;
|
||||
|
||||
void hid_init(void) {
|
||||
if (hid_initialized) return;
|
||||
hid_initialized = true;
|
||||
|
||||
// Initialize keyboard and mouse HID handling
|
||||
keyboard_init();
|
||||
// Assume USB mouse has been initialized and is connected.
|
||||
usb_hid_init(); // Initializes USB HID for both keyboard and mouse
|
||||
}
|
||||
|
||||
void hid_process_report(uint8_t* report, uint8_t length) {
|
||||
// Process the HID report based on its type
|
||||
if (length == 8) { // Assuming a standard 8-byte report for HID keyboard
|
||||
keyboard_hid_report_t* k_report = (keyboard_hid_report_t*) report;
|
||||
hid_process_keyboard_report(k_report);
|
||||
} else if (length == 3) { // Assuming a standard 3-byte report for HID mouse
|
||||
mouse_hid_report_t* m_report = (mouse_hid_report_t*) report;
|
||||
hid_process_mouse_report(m_report);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle HID keyboard report
|
||||
void hid_process_keyboard_report(const keyboard_hid_report_t* report) {
|
||||
// Iterate over the keycodes and process key presses
|
||||
for (int i = 0; i < 6; i++) {
|
||||
uint8_t keycode = report->keycodes[i];
|
||||
if (keycode != 0) {
|
||||
char key = scancode_map[keycode];
|
||||
if (key) {
|
||||
keyboard_buffer_add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle HID mouse report
|
||||
void hid_process_mouse_report(const mouse_hid_report_t* report) {
|
||||
// Process mouse movement and button clicks
|
||||
mouse_data.x += report->x;
|
||||
mouse_data.y += report->y;
|
||||
mouse_data.left_button = (report->buttons & 0x01) != 0;
|
||||
mouse_data.right_button = (report->buttons & 0x02) != 0;
|
||||
|
||||
print_hex((uint32_t)mouse_data.x, 1, 1);
|
||||
print_hex((uint32_t)mouse_data.y, 1, 1);
|
||||
print_hex((uint32_t)report->buttons, 1, 1);
|
||||
}
|
||||
|
||||
// Parse the HID descriptor (for parsing USB HID device descriptors)
|
||||
bool hid_parse_descriptor(uint8_t* descriptor, uint32_t length) {
|
||||
// HID descriptors are defined in the USB HID specification, we'll need to parse them here.
|
||||
// For now, just return true assuming we have a valid descriptor.
|
||||
return true;
|
||||
}
|
||||
46
kernel/hid.h
Normal file
46
kernel/hid.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef HID_H
|
||||
#define HID_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// HID Report types
|
||||
#define HID_REPORT_INPUT 0x01
|
||||
#define HID_REPORT_OUTPUT 0x02
|
||||
#define HID_REPORT_FEATURE 0x03
|
||||
|
||||
// HID usage page constants (USB HID)
|
||||
#define HID_USAGE_PAGE_GENERIC 0x01
|
||||
#define HID_USAGE_KEYBOARD 0x06
|
||||
#define HID_USAGE_MOUSE 0x02
|
||||
|
||||
// HID keyboard and mouse data
|
||||
typedef struct {
|
||||
uint8_t modifier; // Modifier keys (shift, ctrl, alt, etc.)
|
||||
uint8_t reserved; // Reserved byte
|
||||
uint8_t keycodes[6]; // Keycodes for keys pressed
|
||||
} keyboard_hid_report_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t buttons; // Mouse buttons (bitwise: 0x01 = left, 0x02 = right, 0x04 = middle)
|
||||
int8_t x; // X axis movement
|
||||
int8_t y; // Y axis movement
|
||||
int8_t wheel; // Mouse wheel
|
||||
} mouse_hid_report_t;
|
||||
|
||||
// Initialize the HID subsystem
|
||||
void hid_init(void);
|
||||
|
||||
// Process an incoming HID report
|
||||
void hid_process_report(uint8_t* report, uint8_t length);
|
||||
|
||||
// Process HID keyboard report
|
||||
void hid_process_keyboard_report(const keyboard_hid_report_t* report);
|
||||
|
||||
// Process HID mouse report
|
||||
void hid_process_mouse_report(const mouse_hid_report_t* report);
|
||||
|
||||
// USB HID report descriptor parsing
|
||||
bool hid_parse_descriptor(uint8_t* descriptor, uint32_t length);
|
||||
|
||||
#endif // HID_H
|
||||
105
kernel/idt.c
Normal file
105
kernel/idt.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#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 isr1();
|
||||
extern void isr2();
|
||||
extern void isr3();
|
||||
extern void isr4();
|
||||
extern void isr5();
|
||||
extern void isr6();
|
||||
extern void isr7();
|
||||
extern void isr8();
|
||||
extern void isr9();
|
||||
extern void isr10();
|
||||
extern void isr11();
|
||||
extern void isr12();
|
||||
extern void isr13();
|
||||
extern void isr14();
|
||||
extern void isr15();
|
||||
extern void isr16();
|
||||
extern void isr17();
|
||||
extern void isr18();
|
||||
extern void isr19();
|
||||
extern void isr20();
|
||||
extern void isr21();
|
||||
extern void isr22();
|
||||
extern void isr23();
|
||||
extern void isr24();
|
||||
extern void isr25();
|
||||
extern void isr26();
|
||||
extern void isr27();
|
||||
extern void isr28();
|
||||
extern void isr29();
|
||||
extern void isr30();
|
||||
extern void isr31();
|
||||
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__("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
|
||||
// Assign CPU exception handlers
|
||||
idt_set_gate(0, (uint32_t)isr0);
|
||||
idt_set_gate(1, (uint32_t)isr1);
|
||||
idt_set_gate(2, (uint32_t)isr2);
|
||||
idt_set_gate(3, (uint32_t)isr3);
|
||||
idt_set_gate(4, (uint32_t)isr4);
|
||||
idt_set_gate(5, (uint32_t)isr5);
|
||||
idt_set_gate(6, (uint32_t)isr6);
|
||||
idt_set_gate(7, (uint32_t)isr7);
|
||||
idt_set_gate(8, (uint32_t)isr8);
|
||||
idt_set_gate(9, (uint32_t)isr9);
|
||||
idt_set_gate(10, (uint32_t)isr10);
|
||||
idt_set_gate(11, (uint32_t)isr11);
|
||||
idt_set_gate(12, (uint32_t)isr12);
|
||||
idt_set_gate(13, (uint32_t)isr13);
|
||||
idt_set_gate(14, (uint32_t)isr14);
|
||||
idt_set_gate(15, (uint32_t)isr15);
|
||||
idt_set_gate(16, (uint32_t)isr16);
|
||||
idt_set_gate(17, (uint32_t)isr17);
|
||||
idt_set_gate(18, (uint32_t)isr18);
|
||||
idt_set_gate(19, (uint32_t)isr19);
|
||||
idt_set_gate(20, (uint32_t)isr20);
|
||||
idt_set_gate(21, (uint32_t)isr21);
|
||||
idt_set_gate(22, (uint32_t)isr22);
|
||||
idt_set_gate(23, (uint32_t)isr23);
|
||||
idt_set_gate(24, (uint32_t)isr24);
|
||||
idt_set_gate(25, (uint32_t)isr25);
|
||||
idt_set_gate(26, (uint32_t)isr26);
|
||||
idt_set_gate(27, (uint32_t)isr27);
|
||||
idt_set_gate(28, (uint32_t)isr28);
|
||||
idt_set_gate(29, (uint32_t)isr29);
|
||||
idt_set_gate(30, (uint32_t)isr30);
|
||||
idt_set_gate(31, (uint32_t)isr31);
|
||||
|
||||
|
||||
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
|
||||
36
kernel/io.h
Normal file
36
kernel/io.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val) {
|
||||
__asm__("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t ret;
|
||||
__asm__("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void outw(uint16_t port, uint16_t val) {
|
||||
__asm__("outw %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint16_t inw(uint16_t port) {
|
||||
uint16_t ret;
|
||||
__asm__("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void outl(uint16_t port, uint32_t val) {
|
||||
__asm__("outl %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint32_t inl(uint16_t port) {
|
||||
uint32_t ret;
|
||||
__asm__("inl %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
76
kernel/irq.c
Normal file
76
kernel/irq.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "idt.h"
|
||||
#include "irq.h"
|
||||
#include "io.h"
|
||||
#include "isr.h"
|
||||
|
||||
#define PIC1_CMD 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_CMD 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
// FIXME: stubs
|
||||
void irq0() {}
|
||||
void irq1() {}
|
||||
void irq2() {}
|
||||
void irq3() {}
|
||||
void irq4() {}
|
||||
void irq5() {}
|
||||
void irq6() {}
|
||||
void irq7() {}
|
||||
void irq8() {}
|
||||
void irq9() {}
|
||||
void irq10() {}
|
||||
void irq11() {}
|
||||
void irq12() {}
|
||||
void irq13() {}
|
||||
void irq14() {}
|
||||
void irq15() {}
|
||||
// --- stubs end
|
||||
|
||||
void irq_remap(void)
|
||||
{
|
||||
outb(PIC1_CMD, 0x11); // ICW1 – edge triggered, cascade, need ICW4
|
||||
outb(PIC2_CMD, 0x11);
|
||||
|
||||
outb(PIC1_DATA, 0x20); // ICW2 – master base vector
|
||||
outb(PIC2_DATA, 0x28); // ICW2 – slave base vector
|
||||
|
||||
outb(PIC1_DATA, 0x04); // ICW3 – slave on IRQ2
|
||||
outb(PIC2_DATA, 0x02); // ICW3 – cascade identity
|
||||
|
||||
outb(PIC1_DATA, 0x01); // ICW4 – 8086 mode
|
||||
outb(PIC2_DATA, 0x01);
|
||||
|
||||
// Mask everything except IRQ0 (timer) and IRQ1 (keyboard) for now
|
||||
outb(PIC1_DATA, 0b11111001);
|
||||
outb(PIC2_DATA, 0xFF);
|
||||
}
|
||||
|
||||
void irq_install(void)
|
||||
{
|
||||
irq_remap();
|
||||
|
||||
/* Fill IRQ entries in the IDT (0x20 … 0x2F) */
|
||||
//extern void irq0(), irq1(), irq2(), irq3(), irq4(), irq5(), irq6(), irq7();
|
||||
//extern void irq8(), irq9(), irq10(), irq11(), irq12(), irq13(), irq14(), irq15();
|
||||
|
||||
idt_set_gate(0x20, (uint32_t)irq0);
|
||||
idt_set_gate(0x21, (uint32_t)irq1);
|
||||
/* … repeat for the rest or loop … */
|
||||
for (int i = 2; i < 16; ++i)
|
||||
idt_set_gate(0x20 + i, (uint32_t)irq0 + i * 8); // crude but works
|
||||
}
|
||||
|
||||
/* Called from the assembly stubs (see irq.asm below) */
|
||||
void irq_handler(uint32_t int_num)
|
||||
{
|
||||
/* int_num is the *remapped* vector, e.g. 0x21 for keyboard */
|
||||
if (interrupt_handlers[int_num]) {
|
||||
interrupt_handlers[int_num]();
|
||||
}
|
||||
|
||||
/* ---- EOI ---- */
|
||||
if (int_num >= 0x28) // slave PIC
|
||||
outb(PIC2_CMD, 0x20);
|
||||
outb(PIC1_CMD, 0x20); // always master
|
||||
}
|
||||
10
kernel/irq.h
Normal file
10
kernel/irq.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef IRQ_H
|
||||
#define IRQ_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void irq_remap(void);
|
||||
void irq_install(void);
|
||||
void irq_handler(uint32_t int_num);
|
||||
|
||||
#endif
|
||||
80
kernel/isr.asm
Normal file
80
kernel/isr.asm
Normal file
@@ -0,0 +1,80 @@
|
||||
[BITS 32]
|
||||
GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9
|
||||
GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19
|
||||
GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29
|
||||
GLOBAL isr30, isr31, isr_default
|
||||
|
||||
[EXTERN isr_handler]
|
||||
|
||||
%macro ISR_NOERR 1
|
||||
isr%1:
|
||||
cli
|
||||
push dword 0 ; Dummy error code
|
||||
push dword %1 ; Interrupt number
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
%endmacro
|
||||
|
||||
%macro ISR_ERR 1
|
||||
isr%1:
|
||||
cli
|
||||
push dword %1 ; Interrupt number
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
%endmacro
|
||||
|
||||
; ISR 0–7: No error code
|
||||
ISR_NOERR 0
|
||||
ISR_NOERR 1
|
||||
ISR_NOERR 2
|
||||
ISR_NOERR 3
|
||||
ISR_NOERR 4
|
||||
ISR_NOERR 5
|
||||
ISR_NOERR 6
|
||||
ISR_NOERR 7
|
||||
|
||||
; ISR 8–14: Error code pushed automatically
|
||||
ISR_ERR 8
|
||||
ISR_NOERR 9 ; Coprocessor Segment Overrun (obsolete, no error code)
|
||||
ISR_ERR 10
|
||||
ISR_ERR 11
|
||||
ISR_ERR 12
|
||||
ISR_ERR 13
|
||||
ISR_ERR 14
|
||||
|
||||
; ISR 15 is reserved
|
||||
ISR_NOERR 15
|
||||
|
||||
; ISR 16–19 (FPU, Alignment Check, etc.)
|
||||
ISR_NOERR 16
|
||||
ISR_ERR 17
|
||||
ISR_NOERR 18
|
||||
ISR_NOERR 19
|
||||
|
||||
; ISR 20–31 (reserved or future use)
|
||||
ISR_NOERR 20
|
||||
ISR_NOERR 21
|
||||
ISR_NOERR 22
|
||||
ISR_NOERR 23
|
||||
ISR_NOERR 24
|
||||
ISR_NOERR 25
|
||||
ISR_NOERR 26
|
||||
ISR_NOERR 27
|
||||
ISR_NOERR 28
|
||||
ISR_NOERR 29
|
||||
ISR_NOERR 30
|
||||
ISR_NOERR 31
|
||||
|
||||
; Fallback handler
|
||||
isr_default:
|
||||
cli
|
||||
push dword 255
|
||||
push dword 0
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
54
kernel/isr.c
Normal file
54
kernel/isr.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <stdbool.h>
|
||||
#include "terminal.h"
|
||||
#include "serial.h"
|
||||
#include "isr.h"
|
||||
#include "io.h"
|
||||
#include "print.h"
|
||||
|
||||
isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
|
||||
|
||||
void isr_handler(uint32_t int_num, uint32_t err_code) {
|
||||
terminal_write("Interrupt occurred: ");
|
||||
|
||||
print_hex(int_num, true, false);
|
||||
terminal_write("\n");
|
||||
|
||||
serial_write("INT triggered\n");
|
||||
|
||||
terminal_write("Error code: ");
|
||||
print_hex(err_code, true, false);
|
||||
terminal_write("\n");
|
||||
|
||||
if (interrupt_handlers[int_num]) {
|
||||
interrupt_handlers[int_num](); // Call registered handler
|
||||
} else {
|
||||
terminal_write(" -> No handler registered\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__("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
// === Send End Of Interrupt to PIC(s) ===
|
||||
if (int_num >= 40) {
|
||||
// Send reset signal to slave PIC
|
||||
outb(0xA0, 0x20);
|
||||
}
|
||||
if (int_num >= 32) {
|
||||
// Send reset signal to master PIC
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
void register_interrupt_handler(uint8_t n, isr_callback_t handler) {
|
||||
interrupt_handlers[n] = handler;
|
||||
}
|
||||
14
kernel/isr.h
Normal file
14
kernel/isr.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef ISR_H
|
||||
#define ISR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_INTERRUPTS 256
|
||||
|
||||
typedef void (*isr_callback_t)(void);
|
||||
extern isr_callback_t interrupt_handlers[MAX_INTERRUPTS];
|
||||
|
||||
void isr_handler(uint32_t int_num, uint32_t err_code);
|
||||
void register_interrupt_handler(uint8_t n, isr_callback_t handler);
|
||||
|
||||
#endif
|
||||
92
kernel/keyboard.c
Normal file
92
kernel/keyboard.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "keyboard.h"
|
||||
#include "io.h"
|
||||
#include "isr.h"
|
||||
#include "terminal.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define KEYBOARD_DATA_PORT 0x60
|
||||
#define KEY_BUFFER_SIZE 256
|
||||
|
||||
// Use volatile so the compiler knows these change inside interrupts
|
||||
static volatile char key_buffer[KEY_BUFFER_SIZE];
|
||||
static volatile uint8_t buffer_head = 0;
|
||||
static volatile uint8_t buffer_tail = 0;
|
||||
static volatile uint8_t buffer_count = 0;
|
||||
|
||||
// Exported map: Removed 'static' so hid.c can reference it if needed
|
||||
const char scancode_map[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8',
|
||||
'9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r',
|
||||
't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0,
|
||||
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
|
||||
'\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n',
|
||||
'm', ',', '.', '/', 0, '*', 0, ' ', 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Shared function used by both PS/2 (callback) and USB (hid.c)
|
||||
* This fixes the "undefined reference to keyboard_buffer_add" error.
|
||||
*/
|
||||
void keyboard_buffer_add(char c) {
|
||||
if (!c) return;
|
||||
|
||||
uint8_t next_head = (buffer_head + 1) % KEY_BUFFER_SIZE;
|
||||
|
||||
// If buffer is full, we must drop the key
|
||||
if (next_head == buffer_tail) {
|
||||
return;
|
||||
}
|
||||
|
||||
key_buffer[buffer_head] = c;
|
||||
buffer_head = next_head;
|
||||
buffer_count++;
|
||||
|
||||
// Echo to terminal
|
||||
terminal_putchar(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardware Interrupt Handler for PS/2
|
||||
*/
|
||||
void keyboard_callback(void) {
|
||||
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
|
||||
|
||||
// Ignore break codes (key release)
|
||||
if (scancode & 0x80) return;
|
||||
|
||||
char c = scancode_map[scancode];
|
||||
keyboard_buffer_add(c);
|
||||
}
|
||||
|
||||
void keyboard_init(void) {
|
||||
buffer_head = 0;
|
||||
buffer_tail = 0;
|
||||
buffer_count = 0;
|
||||
// IRQ1 is usually mapped to IDT entry 33
|
||||
register_interrupt_handler(33, keyboard_callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking read with a safe HLT to prevent CPU 100% usage
|
||||
*/
|
||||
char keyboard_get_char(void) {
|
||||
char c;
|
||||
|
||||
while (1) {
|
||||
__asm__ __volatile__("cli"); // Disable interrupts to check buffer_count safely
|
||||
|
||||
if (buffer_count > 0) {
|
||||
c = key_buffer[buffer_tail];
|
||||
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
|
||||
buffer_count--;
|
||||
__asm__ __volatile__("sti"); // Re-enable interrupts after reading
|
||||
return c;
|
||||
}
|
||||
|
||||
/* * IMPORTANT: 'sti' followed by 'hlt' is guaranteed by x86
|
||||
* to execute 'hlt' BEFORE the next interrupt can trigger.
|
||||
* This prevents the race condition hang.
|
||||
*/
|
||||
__asm__ __volatile__("sti; hlt");
|
||||
}
|
||||
}
|
||||
12
kernel/keyboard.h
Normal file
12
kernel/keyboard.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef KEYBOARD_H
|
||||
#define KEYBOARD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void keyboard_init(void);
|
||||
void keyboard_buffer_add(char c);
|
||||
char keyboard_get_char(void);
|
||||
|
||||
extern const char scancode_map[128];
|
||||
|
||||
#endif
|
||||
91
kernel/kmain.c
Normal file
91
kernel/kmain.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <stdint.h>
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
#include "terminal.h"
|
||||
#include "idt.h"
|
||||
#include "paging.h"
|
||||
#include "memmap.h"
|
||||
#include "gdt.h"
|
||||
#include "cpu.h"
|
||||
#include "kmalloc.h"
|
||||
#include "print.h"
|
||||
#include "timer.h"
|
||||
#include "utils.h"
|
||||
#include "keyboard.h"
|
||||
#include "irq.h"
|
||||
|
||||
#define LPT1 0x378
|
||||
|
||||
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");
|
||||
|
||||
terminal_write("Identifying CPU...\n");
|
||||
identify_cpu();
|
||||
serial_write("CPU identification complete.\n");
|
||||
|
||||
lpt_write('L'); // Send 'L' to LPT1 to test
|
||||
|
||||
terminal_write("Initializing GDT...\n");
|
||||
gdt_init();
|
||||
serial_write("GDT initialized.\n");
|
||||
|
||||
terminal_write("Initializing IDT...\n");
|
||||
idt_init();
|
||||
serial_write("IDT initialized.\n");
|
||||
|
||||
irq_install();
|
||||
__asm__ __volatile__ ("sti");
|
||||
|
||||
terminal_write("Enabling paging...\n");
|
||||
paging_init();
|
||||
serial_write("Paging initialized.\n");
|
||||
|
||||
terminal_write("Initializing memory allocator...\n");
|
||||
kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!)
|
||||
serial_write("kmalloc initialized.\n");
|
||||
|
||||
serial_write("Allocated 128 bytes.\n");
|
||||
|
||||
terminal_write("Initializing timer...\n");
|
||||
timer_init(100); // 100 Hz (10 ms interval)
|
||||
serial_write("Timer initialized.\n");
|
||||
|
||||
terminal_write("Initializing keyboard...\n");
|
||||
keyboard_init();
|
||||
serial_write("Keyboard 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");
|
||||
|
||||
char buf[32];
|
||||
for (uint32_t i = 0; i < mmap_size; i++) {
|
||||
terminal_write(" - Base: ");
|
||||
print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF), true, false); // Lower 32 bits
|
||||
terminal_write(", Length: ");
|
||||
print_hex((uint32_t)(mmap[i].length & 0xFFFFFFFF), true, false); // Lower 32 bits
|
||||
terminal_write(", Type: ");
|
||||
itoa(mmap[i].type, buf, 10);
|
||||
terminal_write(buf);
|
||||
terminal_write("\n");
|
||||
}
|
||||
|
||||
terminal_write("System initialized. Halting.\n");
|
||||
|
||||
// Halt CPU in loop
|
||||
while (1) {
|
||||
__asm__("hlt");
|
||||
}
|
||||
}
|
||||
51
kernel/kmalloc.c
Normal file
51
kernel/kmalloc.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "kmalloc.h"
|
||||
#include "terminal.h" // Optional: for debug output
|
||||
|
||||
#define HEAP_END 0xC0500000
|
||||
|
||||
static uint32_t current_heap = 0;
|
||||
|
||||
// Initialize the allocator with a starting heap address
|
||||
void kmalloc_init(uint32_t heap_start) {
|
||||
current_heap = heap_start;
|
||||
}
|
||||
|
||||
// Simple bump allocator
|
||||
void* kmalloc(size_t size) {
|
||||
if (current_heap == 0) {
|
||||
terminal_write("kmalloc used before initialization!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* addr = (void*)current_heap;
|
||||
current_heap += size;
|
||||
return addr;
|
||||
}
|
||||
|
||||
// Allocate memory aligned to a power-of-two boundary (e.g., 0x1000)
|
||||
void* kmalloc_aligned(size_t size, uint32_t alignment) {
|
||||
if (current_heap == 0) {
|
||||
terminal_write("kmalloc_aligned used before initialization!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Align the current_heap pointer
|
||||
if ((current_heap & (alignment - 1)) != 0) {
|
||||
current_heap = (current_heap + alignment) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
if (current_heap + size > HEAP_END) {
|
||||
terminal_write("kmalloc_aligned: Out of memory!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* addr = (void*)current_heap;
|
||||
current_heap += size;
|
||||
return addr;
|
||||
}
|
||||
|
||||
void kfree(void* ptr) {
|
||||
// In a bump allocator, we cannot free individual blocks.
|
||||
// We can reset the allocator to the initial state.
|
||||
current_heap = 0; // Reset the heap pointer
|
||||
}
|
||||
11
kernel/kmalloc.h
Normal file
11
kernel/kmalloc.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef KMALLOC_H
|
||||
#define KMALLOC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
void kmalloc_init(uint32_t heap_start);
|
||||
void* kmalloc(size_t size);
|
||||
void* kmalloc_aligned(size_t size, uint32_t alignment);
|
||||
|
||||
#endif
|
||||
38
kernel/linker.ld
Normal file
38
kernel/linker.ld
Normal file
@@ -0,0 +1,38 @@
|
||||
ENTRY(kmain)
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD FLAGS(5); /* Read + Execute */
|
||||
rodata PT_LOAD FLAGS(4); /* Read only */
|
||||
data PT_LOAD FLAGS(6); /* Read + Write */
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.text : {
|
||||
*(.text*)
|
||||
} :text
|
||||
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
} :rodata
|
||||
|
||||
.data : {
|
||||
*(.data*)
|
||||
} :data
|
||||
|
||||
.bss : {
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
. = . + 0x1000;
|
||||
}
|
||||
|
||||
.heap (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
. = . + 0x10000;
|
||||
}
|
||||
}
|
||||
105
kernel/malloc.c
Normal file
105
kernel/malloc.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "malloc.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static void *heap_start; // Start of the heap
|
||||
static void *heap_end; // End of the heap
|
||||
static struct memory_block *free_blocks; // List of free blocks
|
||||
|
||||
void init_heap(void *start, void *end)
|
||||
{
|
||||
heap_start = start;
|
||||
heap_end = end;
|
||||
|
||||
// Initialize the heap with a single large free block
|
||||
free_blocks = (struct memory_block *)start;
|
||||
free_blocks->size = (uintptr_t)end - (uintptr_t)start - sizeof(struct memory_block);
|
||||
free_blocks->next = NULL;
|
||||
free_blocks->is_free = 1;
|
||||
}
|
||||
|
||||
void *find_free_block(size_t size) {
|
||||
struct memory_block *current = free_blocks;
|
||||
while (current != NULL) {
|
||||
if (current->is_free && current->size >= size) {
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
// No suitable block found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mark_as_used(void *ptr, size_t size) {
|
||||
struct memory_block *block = (struct memory_block *)ptr;
|
||||
block->is_free = 0;
|
||||
|
||||
// If the block is larger than needed, split it
|
||||
if (block->size > size + sizeof(struct memory_block)) {
|
||||
struct memory_block *new_block = (struct memory_block *)((uintptr_t)ptr + size + sizeof(struct memory_block));
|
||||
new_block->size = block->size - size - sizeof(struct memory_block);
|
||||
new_block->next = block->next;
|
||||
new_block->is_free = 1;
|
||||
|
||||
block->size = size;
|
||||
block->next = new_block;
|
||||
}
|
||||
}
|
||||
|
||||
void mark_as_free(void *ptr) {
|
||||
struct memory_block *block = (struct memory_block *)ptr;
|
||||
block->is_free = 1;
|
||||
|
||||
// Coalesce with next block if it's free
|
||||
if (block->next && block->next->is_free) {
|
||||
block->size += block->next->size + sizeof(struct memory_block);
|
||||
block->next = block->next->next;
|
||||
}
|
||||
|
||||
// Coalesce with previous block if it's free
|
||||
struct memory_block *prev = free_blocks;
|
||||
while (prev && prev->next != block) {
|
||||
prev = prev->next;
|
||||
}
|
||||
if (prev && prev->is_free) {
|
||||
prev->size += block->size + sizeof(struct memory_block);
|
||||
prev->next = block->next;
|
||||
}
|
||||
}
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
if (heap_start == NULL || heap_end == NULL)
|
||||
{
|
||||
// Heap not initialized, cannot allocate
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Align the size to the word size for efficiency
|
||||
size = (size + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1);
|
||||
|
||||
// Search for a free block of sufficient size
|
||||
void *block = find_free_block(size);
|
||||
if (block != NULL)
|
||||
{
|
||||
// Mark the block as used
|
||||
mark_as_used(block, size);
|
||||
return (void *)((uintptr_t)block + sizeof(struct memory_block));
|
||||
}
|
||||
|
||||
// No suitable block found, out of memory
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust pointer to the start of the memory block
|
||||
struct memory_block *block = (struct memory_block *)((uintptr_t)ptr - sizeof(struct memory_block));
|
||||
|
||||
// Mark the block as free
|
||||
mark_as_free(block);
|
||||
}
|
||||
27
kernel/malloc.h
Normal file
27
kernel/malloc.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef MALLOC_H
|
||||
#define MALLOC_H
|
||||
|
||||
#include <stddef.h> // For size_t
|
||||
|
||||
// Define the memory block structure
|
||||
struct memory_block {
|
||||
size_t size;
|
||||
struct memory_block *next;
|
||||
int is_free;
|
||||
};
|
||||
|
||||
// Function prototypes
|
||||
void init_heap(void *start, void *end);
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
// Helper function prototypes
|
||||
void *find_free_block(size_t size);
|
||||
void mark_as_used(void *ptr, size_t size);
|
||||
void mark_as_free(void *ptr);
|
||||
|
||||
// External heap boundaries
|
||||
extern void *user_heap_start;
|
||||
extern void *user_heap_end;
|
||||
|
||||
#endif // MALLOC_H
|
||||
18
kernel/memmap.c
Normal file
18
kernel/memmap.c
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "memmap.h"
|
||||
|
||||
#define BOOTLOADER_MEMMAP_COUNT_ADDR MEMMAP_BASE
|
||||
#define BOOTLOADER_MEMMAP_ADDR (MEMMAP_BASE + 4)
|
||||
|
||||
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
|
||||
// Read the number of entries found by the bootloader
|
||||
uint32_t entries_found = *(uint32_t*)BOOTLOADER_MEMMAP_COUNT_ADDR;
|
||||
memory_map_entry_t *bios_data = (memory_map_entry_t*)BOOTLOADER_MEMMAP_ADDR;
|
||||
|
||||
uint32_t count = 0;
|
||||
while (count < entries_found && count < max_entries) {
|
||||
map[count] = bios_data[count];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
15
kernel/memmap.h
Normal file
15
kernel/memmap.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef MEMMAP_H
|
||||
#define MEMMAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint64_t base_addr;
|
||||
uint64_t length;
|
||||
uint32_t type;
|
||||
uint32_t ext;
|
||||
} __attribute__((packed)) memory_map_entry_t;
|
||||
|
||||
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries);
|
||||
|
||||
#endif
|
||||
17
kernel/memory.c
Normal file
17
kernel/memory.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "memory.h"
|
||||
|
||||
/* note: this is a stub, please use care as theres duplicate functions in utils implementation
|
||||
/* --------------------------------------------------------------------- *
|
||||
* Helper: copy a single byte (used by both memcpy and memmove)
|
||||
* --------------------------------------------------------------------- */
|
||||
static inline void byte_copy_forward(uint8_t *dst, const uint8_t *src, size_t n)
|
||||
{
|
||||
while (n--) *dst++ = *src++;
|
||||
}
|
||||
|
||||
static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n)
|
||||
{
|
||||
dst += n; src += n;
|
||||
while (n--) *--dst = *--src;
|
||||
}
|
||||
|
||||
25
kernel/memory.h
Normal file
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 */
|
||||
27
kernel/mouse.c
Normal file
27
kernel/mouse.c
Normal file
@@ -0,0 +1,27 @@
|
||||
// mouse.c
|
||||
#include "mouse.h"
|
||||
#include "usb.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Mouse buffer
|
||||
mouse_data_t mouse_data;
|
||||
|
||||
// Read USB mouse data
|
||||
mouse_data_t usb_read_mouse(void) {
|
||||
uint8_t buffer[3]; // USB HID Mouse reports typically use 3 bytes
|
||||
if (usb_interrupt_transfer()) { // Ensure buffer is filled
|
||||
// Process the received data
|
||||
mouse_data.x += buffer[1]; // X movement
|
||||
mouse_data.y += buffer[2]; // Y movement
|
||||
mouse_data.left_button = buffer[0] & 0x01;
|
||||
mouse_data.right_button = buffer[0] & 0x02;
|
||||
} else {
|
||||
// Handle the case where no data was received
|
||||
// You can choose to reset mouse_data or leave it unchanged
|
||||
// For example, you might want to set movement to zero if no data is received
|
||||
// mouse_data.x = 0;
|
||||
// mouse_data.y = 0;
|
||||
}
|
||||
return mouse_data;
|
||||
}
|
||||
28
kernel/mouse.h
Normal file
28
kernel/mouse.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef MOUSE_H
|
||||
#define MOUSE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Mouse data structure
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
bool left_button;
|
||||
bool right_button;
|
||||
} mouse_data_t;
|
||||
|
||||
extern mouse_data_t mouse_data;
|
||||
|
||||
// Function declarations for USB 1.x HID mouse support
|
||||
bool usb_mouse_init(void);
|
||||
bool usb_mouse_detected(void);
|
||||
bool usb_mouse_received(void);
|
||||
mouse_data_t usb_read_mouse(void);
|
||||
|
||||
// USB Host Controller Interface functions (USB 1.x support)
|
||||
bool uhci_init(void);
|
||||
bool ohci_init(void);
|
||||
|
||||
#endif // MOUSE_H
|
||||
|
||||
51
kernel/paging.c
Normal file
51
kernel/paging.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "io.h"
|
||||
#include "paging.h"
|
||||
|
||||
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x200000;
|
||||
page_table_entry_t *page_table = (page_table_entry_t *)0x201000;
|
||||
|
||||
// Helper function to set up the page directory entry
|
||||
void set_page_directory(page_directory_entry_t *dir) {
|
||||
// Set first PDE
|
||||
dir[0].present = 1;
|
||||
dir[0].rw = 1;
|
||||
dir[0].addr = (uint32_t)page_table >> 12;
|
||||
}
|
||||
|
||||
// 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].addr = 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__("mov %0, %%cr3" : : "r"(page_directory));
|
||||
|
||||
// Enable paging (set the PG bit in CR0)
|
||||
__asm__("mov %%cr0, %0" : "=r"(cr0));
|
||||
cr0 |= 0x80000000; // Set the PG (paging) bit
|
||||
__asm__("mov %0, %%cr0" : : "r"(cr0));
|
||||
}
|
||||
|
||||
// Initialize paging: set up the page directory and enable paging
|
||||
void paging_init() {
|
||||
// Zero out the tables
|
||||
memset(page_directory, 0x00, PAGE_DIRECTORY_SIZE * sizeof *page_directory);
|
||||
memset(page_table, 0x00, PAGE_TABLE_SIZE * sizeof *page_table);
|
||||
|
||||
// Set up identity-mapped page directory + table
|
||||
set_page_directory(page_directory);
|
||||
set_page_table(page_table);
|
||||
|
||||
enable_paging();
|
||||
}
|
||||
49
kernel/paging.h
Normal file
49
kernel/paging.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#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
|
||||
#define KERNEL_HEAP_START 0xC0100000
|
||||
|
||||
// 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 dirty : 1; // Dirty bit
|
||||
uint32_t attribute : 1; // Page size (0: 4KB, 1: 4MB)
|
||||
uint32_t global : 1; // Global page (can be used across different processes)
|
||||
uint32_t reserved : 3; // Unused
|
||||
uint32_t addr : 20; // Page frame address (physical address)
|
||||
} __attribute__((packed)) page_table_entry_t;
|
||||
|
||||
// Define page directory entry
|
||||
typedef struct {
|
||||
uint32_t present : 1; // Present bit (1: PTE is present in memory)
|
||||
uint32_t rw : 1; // Read-Write bit (1: pages are 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 available : 1; // Unused
|
||||
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
|
||||
uint32_t available_2 : 4; // Unused
|
||||
uint32_t addr : 20; // Page table address
|
||||
} __attribute__((packed)) page_directory_entry_t;
|
||||
|
||||
extern page_directory_entry_t *page_directory;
|
||||
extern page_table_entry_t *page_table;
|
||||
extern page_table_entry_t *heap_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
|
||||
19
kernel/panic.c
Normal file
19
kernel/panic.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "panic.h"
|
||||
#include "terminal.h"
|
||||
#include "serial.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
void panic(const char *message) {
|
||||
terminal_write("KERNEL PANIC: ");
|
||||
terminal_write(message);
|
||||
terminal_write("\nSystem halted.\n");
|
||||
|
||||
serial_write("KERNEL PANIC: ");
|
||||
serial_write(message);
|
||||
serial_write("\nSystem halted.\n");
|
||||
|
||||
// Halt the system
|
||||
while (true) {
|
||||
__asm__("cli; hlt");
|
||||
}
|
||||
}
|
||||
6
kernel/panic.h
Normal file
6
kernel/panic.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef PANIC_H
|
||||
#define PANIC_H
|
||||
|
||||
void panic(const char *message);
|
||||
|
||||
#endif // PANIC_H
|
||||
109
kernel/pci.c
Normal file
109
kernel/pci.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "pci.h"
|
||||
#include "io.h"
|
||||
|
||||
/* --- Configuration Access Functions --- */
|
||||
|
||||
uint32_t pci_config_read_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
|
||||
uint32_t address = (uint32_t)((uint32_t)1 << 31) |
|
||||
((uint32_t)bus << 16) |
|
||||
((uint32_t)slot << 11) |
|
||||
((uint32_t)func << 8) |
|
||||
(offset & 0xFC);
|
||||
outl(PCI_CONFIG_ADDRESS, address);
|
||||
return inl(PCI_CONFIG_DATA);
|
||||
}
|
||||
|
||||
void pci_config_write_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t data) {
|
||||
uint32_t address = (uint32_t)((uint32_t)1 << 31) |
|
||||
((uint32_t)bus << 16) |
|
||||
((uint32_t)slot << 11) |
|
||||
((uint32_t)func << 8) |
|
||||
(offset & 0xFC);
|
||||
outl(PCI_CONFIG_ADDRESS, address);
|
||||
outl(PCI_CONFIG_DATA, data);
|
||||
}
|
||||
|
||||
/* To read a word or byte, we read the Dword and shift/mask */
|
||||
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
|
||||
uint32_t dword = pci_config_read_dword(bus, slot, func, offset);
|
||||
return (uint16_t)((dword >> ((offset & 2) * 8)) & 0xFFFF);
|
||||
}
|
||||
|
||||
uint8_t pci_config_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
|
||||
uint32_t dword = pci_config_read_dword(bus, slot, func, offset);
|
||||
return (uint8_t)((dword >> ((offset & 3) * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
/* --- BAR Decoding Logic --- */
|
||||
|
||||
pci_bar_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index) {
|
||||
pci_bar_t bar = {0};
|
||||
uint8_t offset = PCI_REG_BAR0 + (bar_index * 4);
|
||||
|
||||
uint32_t initial_val = pci_config_read_dword(bus, slot, func, offset);
|
||||
|
||||
// The Size Masking Trick
|
||||
pci_config_write_dword(bus, slot, func, offset, 0xFFFFFFFF);
|
||||
uint32_t mask = pci_config_read_dword(bus, slot, func, offset);
|
||||
pci_config_write_dword(bus, slot, func, offset, initial_val); // Restore
|
||||
|
||||
if (initial_val & 0x1) {
|
||||
// I/O Space BAR
|
||||
bar.is_io = true;
|
||||
bar.base_address = initial_val & 0xFFFFFFFC;
|
||||
bar.size = ~(mask & 0xFFFFFFFC) + 1;
|
||||
} else {
|
||||
// Memory Space BAR
|
||||
bar.is_io = false;
|
||||
bar.base_address = initial_val & 0xFFFFFFF0;
|
||||
bar.is_prefetchable = (initial_val & 0x8) != 0;
|
||||
bar.size = ~(mask & 0xFFFFFFF0) + 1;
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
/* --- Enumeration and Discovery --- */
|
||||
|
||||
void pci_check_function(uint8_t bus, uint8_t slot, uint8_t func) {
|
||||
uint16_t vendor_id = pci_config_read_word(bus, slot, func, PCI_REG_VENDOR_ID);
|
||||
if (vendor_id == 0xFFFF) return;
|
||||
|
||||
uint16_t device_id = pci_config_read_word(bus, slot, func, PCI_REG_DEVICE_ID);
|
||||
uint8_t class_code = pci_config_read_byte(bus, slot, func, PCI_REG_CLASS);
|
||||
|
||||
/* Optional: Set Master Latency Timer if it is 0.
|
||||
A value of 32 (0x20) or 64 (0x40) is typical.
|
||||
*/
|
||||
uint8_t latency = pci_config_read_byte(bus, slot, func, PCI_REG_LATENCY_TIMER);
|
||||
if (latency == 0) {
|
||||
// pci_config_write_byte would be needed here, or write a dword with the byte modified
|
||||
uint32_t reg_0c = pci_config_read_dword(bus, slot, func, 0x0C);
|
||||
reg_0c |= (0x20 << 8); // Set latency to 32
|
||||
pci_config_write_dword(bus, slot, func, 0x0C, reg_0c);
|
||||
}
|
||||
|
||||
// Replace with your kernel's print/logging function
|
||||
// printf("Found PCI Device: %x:%x Class: %x at %d:%d:%d\n", vendor_id, device_id, class_code, bus, slot, func);
|
||||
}
|
||||
|
||||
void pci_init(void) {
|
||||
for (uint16_t bus = 0; bus < 256; bus++) {
|
||||
for (uint8_t slot = 0; slot < 32; slot++) {
|
||||
// Check Function 0 first
|
||||
uint16_t vendor = pci_config_read_word(bus, slot, 0, PCI_REG_VENDOR_ID);
|
||||
if (vendor == 0xFFFF) continue;
|
||||
|
||||
pci_check_function(bus, slot, 0);
|
||||
|
||||
// Check if this is a multi-function device
|
||||
uint8_t header_type = pci_config_read_byte(bus, slot, 0, PCI_REG_HEADER_TYPE);
|
||||
if (header_type & 0x80) {
|
||||
// Check functions 1-7
|
||||
for (uint8_t func = 1; func < 8; func++) {
|
||||
pci_check_function(bus, slot, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
kernel/pci.h
Normal file
60
kernel/pci.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef PCI_H
|
||||
#define PCI_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* I/O Ports for PCI Configuration Mechanism #1 */
|
||||
#define PCI_CONFIG_ADDRESS 0xCF8
|
||||
#define PCI_CONFIG_DATA 0xCFC
|
||||
|
||||
/* Common PCI Configuration Register Offsets */
|
||||
#define PCI_REG_VENDOR_ID 0x00
|
||||
#define PCI_REG_DEVICE_ID 0x02
|
||||
#define PCI_REG_COMMAND 0x04
|
||||
#define PCI_REG_STATUS 0x06
|
||||
#define PCI_REG_REVISION_ID 0x08
|
||||
#define PCI_REG_PROG_IF 0x09
|
||||
#define PCI_REG_SUBCLASS 0x0A
|
||||
#define PCI_REG_CLASS 0x0B
|
||||
#define PCI_REG_CACHE_LINE_SIZE 0x0C
|
||||
#define PCI_REG_LATENCY_TIMER 0x0D
|
||||
#define PCI_REG_HEADER_TYPE 0x0E
|
||||
#define PCI_REG_BIST 0x0F
|
||||
#define PCI_REG_BAR0 0x10
|
||||
#define PCI_REG_BAR1 0x14
|
||||
#define PCI_REG_BAR2 0x18
|
||||
#define PCI_REG_BAR3 0x1C
|
||||
#define PCI_REG_BAR4 0x20
|
||||
#define PCI_REG_BAR5 0x24
|
||||
#define PCI_REG_INTERRUPT_LINE 0x3C
|
||||
|
||||
typedef struct {
|
||||
uint32_t base_address;
|
||||
uint32_t size;
|
||||
bool is_io;
|
||||
bool is_prefetchable; // Only for Memory BARs
|
||||
} pci_bar_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bus;
|
||||
uint8_t device;
|
||||
uint8_t function;
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint8_t class_code;
|
||||
uint8_t subclass;
|
||||
uint8_t interrupt_line;
|
||||
} pci_dev_t;
|
||||
|
||||
/* Function Prototypes */
|
||||
uint32_t pci_config_read_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
|
||||
void pci_config_write_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t data);
|
||||
|
||||
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
|
||||
uint8_t pci_config_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
|
||||
|
||||
pci_bar_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index);
|
||||
void pci_init(void);
|
||||
|
||||
#endif
|
||||
102
kernel/print.c
Normal file
102
kernel/print.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <stdarg.h>
|
||||
#include "print.h"
|
||||
#include "serial.h"
|
||||
#include "terminal.h"
|
||||
|
||||
void my_putchar(char ch) {
|
||||
// Write a single character to standard output
|
||||
// In a freestanding environment, you might need to implement this differently
|
||||
// For now, we will use the standard putchar for demonstration
|
||||
// Replace this with your own implementation if needed
|
||||
terminal_putchar(ch);
|
||||
}
|
||||
|
||||
void print_string(const char *str) {
|
||||
// Simple implementation to print a string
|
||||
while (*str) {
|
||||
my_putchar(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void my_printf(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
while (*format) {
|
||||
if (*format == '%') {
|
||||
format++; // Move to the next character after '%'
|
||||
switch (*format) {
|
||||
case 's': { // String
|
||||
char *str = va_arg(args, char *);
|
||||
print_string(str);
|
||||
break;
|
||||
}
|
||||
case 'd': { // Integer
|
||||
int num = va_arg(args, int);
|
||||
char buffer[20]; // Buffer to hold the string representation
|
||||
|
||||
//TODO: implement `snprintf()`
|
||||
//snprintf(buffer, sizeof(buffer), "%d", num);
|
||||
print_string(buffer);
|
||||
break;
|
||||
}
|
||||
case 'c': { // Character
|
||||
char ch = (char)va_arg(args, int); // Promote char to int
|
||||
my_putchar(ch);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
my_putchar('%'); // Print the '%' if no valid format specifier
|
||||
my_putchar(*format);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
my_putchar(*format);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void print_hex(uint32_t val, int include_prefix, int suppress_leading_zeros) {
|
||||
char hex_chars[] = "0123456789ABCDEF";
|
||||
char buffer[11]; // 8 hex digits + "0x" + null terminator
|
||||
int pos = 10; // Start from end of buffer (null terminator)
|
||||
|
||||
// Null-terminate the buffer
|
||||
buffer[pos--] = '\0';
|
||||
|
||||
// Convert value to hex digits
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
int digit = val & 0xF; // Get last 4 bits
|
||||
buffer[pos--] = hex_chars[digit];
|
||||
val >>= 4; // Shift right by 4 bits
|
||||
}
|
||||
|
||||
// Add "0x" prefix if requested
|
||||
if (include_prefix) {
|
||||
buffer[pos--] = 'x';
|
||||
buffer[pos--] = '0';
|
||||
}
|
||||
|
||||
// Determine start of output (skip leading zeros if requested)
|
||||
int start = include_prefix ? 0 : 2; // Start after "0x" if prefix included
|
||||
if (suppress_leading_zeros && !include_prefix) {
|
||||
int i = start;
|
||||
while (i < 9 && buffer[i] == '0') {
|
||||
i++;
|
||||
}
|
||||
if (i == 10) {
|
||||
// All zeros, output single '0'
|
||||
terminal_write("0");
|
||||
serial_write("0");
|
||||
return;
|
||||
}
|
||||
start = i;
|
||||
}
|
||||
|
||||
// Output the result
|
||||
terminal_write(buffer + start);
|
||||
serial_write(buffer + start);
|
||||
}
|
||||
11
kernel/print.h
Normal file
11
kernel/print.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void print_string(const char *str);
|
||||
void my_printf(const char *format, ...);
|
||||
void print_hex(uint32_t val, int include_prefix, int suppress_leading_zeros);
|
||||
void my_putchar(char ch);
|
||||
|
||||
#endif
|
||||
107
kernel/ps2.c
Normal file
107
kernel/ps2.c
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "ps2.h"
|
||||
#include "io.h"
|
||||
|
||||
/* --- Controller Synchronization --- */
|
||||
|
||||
// Wait until the controller is ready to receive a byte
|
||||
static void ps2_wait_write() {
|
||||
while (inb(PS2_STATUS_REG) & PS2_STATUS_INPUT);
|
||||
}
|
||||
|
||||
// Wait until the controller has a byte for us to read
|
||||
static void ps2_wait_read() {
|
||||
while (!(inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT));
|
||||
}
|
||||
|
||||
/* --- Initialization --- */
|
||||
|
||||
void ps2_write_device(uint8_t command) {
|
||||
ps2_wait_write();
|
||||
outb(PS2_DATA_PORT, command);
|
||||
}
|
||||
|
||||
void ps2_write_mouse(uint8_t data) {
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_WRITE_MOUSE); // "Next byte goes to mouse"
|
||||
ps2_wait_write();
|
||||
outb(PS2_DATA_PORT, data);
|
||||
}
|
||||
|
||||
void ps2_init(void) {
|
||||
// 1. Disable Devices
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_KB);
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_MS);
|
||||
|
||||
// 2. Flush Output Buffer
|
||||
while (inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT) {
|
||||
inb(PS2_DATA_PORT);
|
||||
}
|
||||
|
||||
// 3. Set Controller Configuration Byte
|
||||
// Bit 0: KB Interrupt, Bit 1: Mouse Interrupt, Bit 6: Translation
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_READ_CONFIG);
|
||||
ps2_wait_read();
|
||||
uint8_t status = inb(PS2_DATA_PORT);
|
||||
status |= (1 << 0) | (1 << 1); // Enable IRQ 1 and IRQ 12
|
||||
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_WRITE_CONFIG);
|
||||
ps2_wait_write();
|
||||
outb(PS2_DATA_PORT, status);
|
||||
|
||||
// 4. Enable Devices
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_KB);
|
||||
ps2_wait_write();
|
||||
outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_MS);
|
||||
|
||||
// 5. Initialize Mouse (The mouse won't send IRQs until you tell it to)
|
||||
ps2_write_mouse(MOUSE_CMD_SET_DEFAULTS);
|
||||
ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA)
|
||||
|
||||
ps2_write_mouse(MOUSE_CMD_ENABLE_SCAN);
|
||||
ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA)
|
||||
}
|
||||
|
||||
/* --- IRQ Handlers --- */
|
||||
|
||||
// Called from IRQ 1 (Keyboard)
|
||||
void ps2_keyboard_handler(void) {
|
||||
uint8_t scancode = inb(PS2_DATA_PORT);
|
||||
// Process scancode (e.g., put it into a circular buffer)
|
||||
}
|
||||
|
||||
// Called from IRQ 12 (Mouse)
|
||||
static uint8_t mouse_cycle = 0;
|
||||
static uint8_t mouse_bytes[3];
|
||||
|
||||
void ps2_mouse_handler(void) {
|
||||
uint8_t status = inb(PS2_STATUS_REG);
|
||||
|
||||
// Ensure this is actually mouse data
|
||||
if (!(status & PS2_STATUS_MOUSE)) return;
|
||||
|
||||
mouse_bytes[mouse_cycle++] = inb(PS2_DATA_PORT);
|
||||
|
||||
if (mouse_cycle == 3) {
|
||||
mouse_cycle = 0;
|
||||
|
||||
// Byte 0: Flags (Buttons, Signs)
|
||||
// Byte 1: X Delta
|
||||
// Byte 2: Y Delta
|
||||
|
||||
mouse_state_t state;
|
||||
state.left_button = (mouse_bytes[0] & 0x01);
|
||||
state.right_button = (mouse_bytes[0] & 0x02);
|
||||
state.middle_button = (mouse_bytes[0] & 0x04);
|
||||
|
||||
// Handle negative deltas (signed 9-bit logic)
|
||||
state.x_delta = (int8_t)mouse_bytes[1];
|
||||
state.y_delta = (int8_t)mouse_bytes[2];
|
||||
|
||||
// Update your kernel's internal mouse position here
|
||||
}
|
||||
}
|
||||
45
kernel/ps2.h
Normal file
45
kernel/ps2.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef PS2_H
|
||||
#define PS2_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* I/O Ports */
|
||||
#define PS2_DATA_PORT 0x60
|
||||
#define PS2_STATUS_REG 0x64
|
||||
#define PS2_COMMAND_REG 0x64
|
||||
|
||||
/* Status Register Bits */
|
||||
#define PS2_STATUS_OUTPUT 0x01 // 1 = Data ready to be read
|
||||
#define PS2_STATUS_INPUT 0x02 // 1 = Controller busy, don't write yet
|
||||
#define PS2_STATUS_SYS 0x04 // System flag
|
||||
#define PS2_STATUS_CMD_DATA 0x08 // 0 = Data written to 0x60, 1 = Cmd to 0x64
|
||||
#define PS2_STATUS_MOUSE 0x20 // 1 = Mouse data, 0 = Keyboard data
|
||||
|
||||
/* Controller Commands */
|
||||
#define PS2_CMD_READ_CONFIG 0x20
|
||||
#define PS2_CMD_WRITE_CONFIG 0x60
|
||||
#define PS2_CMD_DISABLE_MS 0xA7
|
||||
#define PS2_CMD_ENABLE_MS 0xA8
|
||||
#define PS2_CMD_DISABLE_KB 0xAD
|
||||
#define PS2_CMD_ENABLE_KB 0xAE
|
||||
#define PS2_CMD_WRITE_MOUSE 0xD4
|
||||
|
||||
/* Mouse Commands */
|
||||
#define MOUSE_CMD_SET_DEFAULTS 0xF6
|
||||
#define MOUSE_CMD_ENABLE_SCAN 0xF4
|
||||
|
||||
typedef struct {
|
||||
int8_t x_delta;
|
||||
int8_t y_delta;
|
||||
bool left_button;
|
||||
bool right_button;
|
||||
bool middle_button;
|
||||
} mouse_state_t;
|
||||
|
||||
/* Public API */
|
||||
void ps2_init(void);
|
||||
void ps2_keyboard_handler(void);
|
||||
void ps2_mouse_handler(void);
|
||||
|
||||
#endif
|
||||
96
kernel/scheduler.c
Normal file
96
kernel/scheduler.c
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "scheduler.h"
|
||||
#include <stddef.h>
|
||||
|
||||
// Defined in context_switch.s
|
||||
extern void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp);
|
||||
|
||||
static task_t tasks[MAX_TASKS];
|
||||
|
||||
// Stack memory area. Note: x86 Stacks grow DOWN from high to low addresses.
|
||||
static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)];
|
||||
|
||||
static int task_count = 0;
|
||||
static task_t *task_list = NULL;
|
||||
static task_t *current_task = NULL;
|
||||
|
||||
void scheduler_init() {
|
||||
task_list = NULL;
|
||||
current_task = NULL;
|
||||
task_count = 0;
|
||||
}
|
||||
|
||||
void scheduler_add_task(void (*entry)(void)) {
|
||||
if (task_count >= MAX_TASKS || entry == NULL) return;
|
||||
|
||||
task_t *new_task = &tasks[task_count];
|
||||
new_task->id = task_count;
|
||||
|
||||
// 1. Calculate the top of the stack (High Address)
|
||||
// We point to the very end of the array.
|
||||
uint32_t *sp = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t)];
|
||||
|
||||
// 2. "Forge" the stack frame to look like ctx_switch saved it.
|
||||
// We push values onto the stack by decrementing the pointer and writing.
|
||||
|
||||
// --- Return Address (EIP) ---
|
||||
sp--;
|
||||
*sp = (uint32_t)entry; // When ctx_switch does 'ret', it pops this and jumps to 'entry'
|
||||
|
||||
// --- EFLAGS ---
|
||||
sp--;
|
||||
*sp = 0x00000202; // Reserved bit set, Interrupts Enabled (IF=1). Important!
|
||||
|
||||
// --- General Purpose Registers (PUSHA/POPA layout) ---
|
||||
// Order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
|
||||
// We initialize them to 0 or meaningful values.
|
||||
sp--; *sp = 0; // EAX
|
||||
sp--; *sp = 0; // ECX
|
||||
sp--; *sp = 0; // EDX
|
||||
sp--; *sp = 0; // EBX
|
||||
sp--; *sp = 0; // ESP (Ignored by POPA)
|
||||
sp--; *sp = 0; // EBP
|
||||
sp--; *sp = 0; // ESI
|
||||
sp--; *sp = 0; // EDI
|
||||
|
||||
// Save this final stack location to the TCB
|
||||
new_task->stack_ptr = sp;
|
||||
new_task->next = NULL;
|
||||
|
||||
// 3. Add to linked list
|
||||
if (task_list == NULL) {
|
||||
task_list = new_task;
|
||||
current_task = new_task; // Make sure we have a current task to start
|
||||
} else {
|
||||
task_t *tail = task_list;
|
||||
while (tail->next) {
|
||||
tail = tail->next;
|
||||
}
|
||||
tail->next = new_task;
|
||||
}
|
||||
|
||||
task_count++;
|
||||
}
|
||||
|
||||
void scheduler_schedule() {
|
||||
if (!current_task) return;
|
||||
|
||||
task_t *prev = current_task;
|
||||
|
||||
// Round-robin logic
|
||||
if (current_task->next) {
|
||||
current_task = current_task->next;
|
||||
} else {
|
||||
current_task = task_list;
|
||||
}
|
||||
|
||||
// Perform the ACTUAL context switch
|
||||
// We pass the address of the previous task's stack pointer storage
|
||||
// and the value of the new task's stack pointer.
|
||||
if (prev != current_task) {
|
||||
ctx_switch(&prev->stack_ptr, current_task->stack_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler_yield() {
|
||||
scheduler_schedule();
|
||||
}
|
||||
24
kernel/scheduler.h
Normal file
24
kernel/scheduler.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef SCHEDULER_H
|
||||
#define SCHEDULER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_TASKS 8
|
||||
#define STACK_SIZE 1024 // in bytes
|
||||
|
||||
typedef struct task {
|
||||
uint32_t id;
|
||||
|
||||
// The most important field:
|
||||
// Where was the stack pointer when we last left this task?
|
||||
uint32_t *stack_ptr;
|
||||
|
||||
struct task *next;
|
||||
} task_t;
|
||||
|
||||
void scheduler_init();
|
||||
void scheduler_add_task(void (*entry)(void));
|
||||
void scheduler_schedule();
|
||||
void scheduler_yield();
|
||||
|
||||
#endif // SCHEDULER_H
|
||||
40
kernel/serial.c
Normal file
40
kernel/serial.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define COM1 0x3F8
|
||||
#define COM2 0x2F8
|
||||
#define COM3 0x3E8
|
||||
#define COM4 0x2E8
|
||||
|
||||
void serial_init_port(uint16_t port) {
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x80);
|
||||
outb(port + 0, 0x03);
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x03);
|
||||
outb(port + 2, 0xC7);
|
||||
outb(port + 4, 0x0B);
|
||||
}
|
||||
|
||||
void serial_init(void) {
|
||||
serial_init_port(COM1);
|
||||
serial_init_port(COM2);
|
||||
serial_init_port(COM3);
|
||||
serial_init_port(COM4);
|
||||
}
|
||||
|
||||
void serial_write_char(char c) {
|
||||
while (!(inb(COM1 + 5) & 0x20));
|
||||
outb(COM1, c);
|
||||
}
|
||||
|
||||
void serial_write(const char *str) {
|
||||
while (*str) {
|
||||
serial_write_char(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void serial_write_string(const char* str) {
|
||||
serial_write(str);
|
||||
}
|
||||
12
kernel/serial.h
Normal file
12
kernel/serial.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void serial_init(void);
|
||||
void serial_init_port(uint16_t port);
|
||||
void serial_write_char(char c);
|
||||
void serial_write(const char *str);
|
||||
void serial_write_string(const char *str);
|
||||
|
||||
#endif
|
||||
60
kernel/shell.c
Normal file
60
kernel/shell.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "shell.h"
|
||||
#include "keyboard.h"
|
||||
#include "terminal.h"
|
||||
#include "print.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
void execute(char *input) {
|
||||
if (my_strcmp(input, "help") == 0) {
|
||||
my_printf("Available commands: help, clear, exit\n");
|
||||
} else if (my_strcmp(input, "clear") == 0) {
|
||||
terminal_clear();
|
||||
} else {
|
||||
my_printf("Unknown command: %s\n", input);
|
||||
}
|
||||
}
|
||||
|
||||
void shell_loop()
|
||||
{
|
||||
char input[256];
|
||||
size_t index = 0;
|
||||
char c;
|
||||
|
||||
while (1)
|
||||
{
|
||||
my_printf("> ");
|
||||
index = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
c = keyboard_get_char(); // Waits for input
|
||||
|
||||
if (c == '\n' || c == '\r') // Enter key
|
||||
{
|
||||
input[index] = '\0';
|
||||
my_printf("\n");
|
||||
break;
|
||||
}
|
||||
else if (c == '\b' || c == 127) // Backspace
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
index--;
|
||||
my_printf("\b \b"); // Erase last char on screen
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index < sizeof(input) - 1) {
|
||||
input[index++] = c;
|
||||
terminal_putchar(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (my_strcmp(input, "exit") == 0)
|
||||
break;
|
||||
|
||||
execute(input);
|
||||
}
|
||||
}
|
||||
7
kernel/shell.h
Normal file
7
kernel/shell.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef SHELL_H
|
||||
#define SHELL_H
|
||||
|
||||
void shell_loop(void);
|
||||
void execute(char *input);
|
||||
|
||||
#endif
|
||||
80
kernel/string_utils.c
Normal file
80
kernel/string_utils.c
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "string_utils.h"
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
size_t my_strlen(const char *str) {
|
||||
const char *s = str;
|
||||
while (*s) s++;
|
||||
return s - str;
|
||||
}
|
||||
|
||||
// Forward declaration of my_itoa
|
||||
static size_t my_itoa(int value, char *str, size_t size);
|
||||
|
||||
int my_vsnprintf(char *str, size_t size, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t written = 0; // Change to size_t
|
||||
|
||||
for (const char *p = format; *p != '\0' && written < size - 1; p++) {
|
||||
if (*p == '%') {
|
||||
p++;
|
||||
if (*p == 's') {
|
||||
const char *s = va_arg(args, const char *);
|
||||
while (*s && written < size - 1) {
|
||||
str[written++] = *s++;
|
||||
}
|
||||
} else if (*p == 'd') {
|
||||
// Handle integer formatting
|
||||
written += my_itoa(va_arg(args, int), str + written, size - written);
|
||||
} else {
|
||||
// Handle other formats as needed
|
||||
str[written++] = *p; // Just copy the character
|
||||
}
|
||||
} else {
|
||||
str[written++] = *p;
|
||||
}
|
||||
}
|
||||
str[written] = '\0'; // Null-terminate the string
|
||||
va_end(args);
|
||||
return written;
|
||||
}
|
||||
|
||||
static size_t my_itoa(int value, char *str, size_t size) {
|
||||
size_t written = 0; // Change to size_t
|
||||
if (value < 0) {
|
||||
if (written < size - 1) {
|
||||
str[written++] = '-';
|
||||
}
|
||||
value = -value;
|
||||
}
|
||||
// Convert integer to string
|
||||
int temp = value;
|
||||
int digits = 0;
|
||||
do {
|
||||
digits++;
|
||||
temp /= 10;
|
||||
} while (temp);
|
||||
|
||||
if (written + digits >= size) {
|
||||
digits = size - written - 1; // Prevent overflow
|
||||
}
|
||||
|
||||
str += written + digits; // Move pointer to the end
|
||||
*str-- = '\0'; // Null-terminate the string
|
||||
|
||||
do {
|
||||
*str-- = (value % 10) + '0';
|
||||
value /= 10;
|
||||
} while (value && str >= str - digits);
|
||||
|
||||
return written + digits; // Return total written characters
|
||||
}
|
||||
|
||||
int my_strcmp(const char *str1, const char *str2) {
|
||||
while (*str1 && (*str1 == *str2)) {
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
return *(unsigned char *)str1 - *(unsigned char *)str2;
|
||||
}
|
||||
11
kernel/string_utils.h
Normal file
11
kernel/string_utils.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef STRING_UTILS_H
|
||||
#define STRING_UTILS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h> // Include for va_list and related macros
|
||||
|
||||
size_t my_strlen(const char *str); // Renamed to avoid conflict
|
||||
int my_vsnprintf(char *str, size_t size, const char *format, ...); // Renamed to avoid conflict
|
||||
int my_strcmp(const char *str1, const char *str2);
|
||||
|
||||
#endif // STRING_UTILS_H
|
||||
29
kernel/syscalls.c
Normal file
29
kernel/syscalls.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "syscalls.h"
|
||||
#include "scheduler.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
void syscall_handler(int code, va_list args) {
|
||||
switch (code) {
|
||||
case SYSCALL_INIT:
|
||||
scheduler_init();
|
||||
break;
|
||||
case SYSCALL_SPAWN: {
|
||||
void (*entry)(void) = va_arg(args, void (*)(void));
|
||||
scheduler_add_task(entry);
|
||||
break;
|
||||
}
|
||||
case SYSCALL_YIELD:
|
||||
scheduler_yield();
|
||||
break;
|
||||
default:
|
||||
// Unknown syscall
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void syscall(int code, ...) {
|
||||
va_list args;
|
||||
va_start(args, code);
|
||||
syscall_handler(code, args);
|
||||
va_end(args);
|
||||
}
|
||||
17
kernel/syscalls.h
Normal file
17
kernel/syscalls.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
#include <stdarg.h>
|
||||
// Syscall numbers
|
||||
typedef enum {
|
||||
SYSCALL_INIT = 0,
|
||||
SYSCALL_SPAWN,
|
||||
SYSCALL_YIELD
|
||||
} syscall_code_t;
|
||||
|
||||
// Syscall dispatcher
|
||||
void syscall_handler(int code, va_list args);
|
||||
// Syscall interface
|
||||
void syscall(int code, ...);
|
||||
|
||||
#endif // SYSCALLS_H
|
||||
109
kernel/terminal.c
Normal file
109
kernel/terminal.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <stdint.h>
|
||||
#include "io.h"
|
||||
#include "terminal.h"
|
||||
#include "vga.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 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++) {
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
vga_buffer[index] = vga_entry(' ', current_color);
|
||||
}
|
||||
}
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
update_cursor(); // Optional: good idea to reset position
|
||||
}
|
||||
|
||||
void terminal_putchar(char c) {
|
||||
// Handle backspace
|
||||
if (c == '\b') {
|
||||
if (cursor_x > 0) {
|
||||
cursor_x--;
|
||||
} else if (cursor_y > 0) {
|
||||
cursor_y--;
|
||||
cursor_x = VGA_WIDTH - 1;
|
||||
}
|
||||
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = vga_entry(' ', current_color);
|
||||
update_cursor(); // Optional, if you add cursor updating
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle newline
|
||||
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, current_color);
|
||||
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(' ', current_color);
|
||||
}
|
||||
|
||||
cursor_y = VGA_HEIGHT - 1;
|
||||
}
|
||||
|
||||
update_cursor(); // Optional, if you want the hardware cursor to follow
|
||||
}
|
||||
|
||||
|
||||
void terminal_write(const char* str) {
|
||||
for (size_t i = 0; str[i] != '\0'; i++) {
|
||||
terminal_putchar(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_setcolor(uint8_t color)
|
||||
{
|
||||
current_color = color;
|
||||
}
|
||||
|
||||
void terminal_clear(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(' ', current_color);
|
||||
}
|
||||
}
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
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));
|
||||
outb(0x3D4, 0x0E);
|
||||
outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
|
||||
}
|
||||
13
kernel/terminal.h
Normal file
13
kernel/terminal.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#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);
|
||||
void terminal_clear(void);
|
||||
void update_cursor(void);
|
||||
|
||||
#endif
|
||||
121
kernel/threading.c
Normal file
121
kernel/threading.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "malloc.h"
|
||||
#include "print.h"
|
||||
#include "threading.h"
|
||||
|
||||
#define MAX_THREADS 16 // Maximum number of threads
|
||||
#define THREAD_STACK_SIZE 8192 // Stack size for each thread
|
||||
|
||||
// The thread table stores information about all threads
|
||||
static Thread thread_table[MAX_THREADS];
|
||||
static uint32_t current_thread = 0; // Index of the currently running thread
|
||||
static uint32_t num_threads = 0; // Number of active threads
|
||||
|
||||
// A simple mutex spinlock
|
||||
static volatile int mutex_locked = 0;
|
||||
|
||||
// Function declaration for context_switch
|
||||
void context_switch(Thread* next);
|
||||
|
||||
// Initialize the threading system
|
||||
void thread_init(void) {
|
||||
memset(thread_table, 0, sizeof(thread_table));
|
||||
num_threads = 0;
|
||||
}
|
||||
|
||||
// Create a new thread
|
||||
void thread_create(Thread* thread __attribute__((unused)),
|
||||
void (*start_routine)(void*), void* arg) {
|
||||
if (num_threads >= MAX_THREADS) {
|
||||
my_printf("Error: Maximum thread count reached.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find an empty slot for the new thread
|
||||
int index = num_threads++;
|
||||
thread_table[index] = (Thread){0};
|
||||
|
||||
// Set up the new thread
|
||||
thread_table[index].start_routine = start_routine;
|
||||
thread_table[index].arg = arg;
|
||||
thread_table[index].stack_size = THREAD_STACK_SIZE;
|
||||
thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE);
|
||||
thread_table[index].stack_top =
|
||||
thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t);
|
||||
|
||||
// Initialize the stack (simulate pushing the function's return address)
|
||||
uint32_t* stack_top = thread_table[index].stack_top;
|
||||
*(--stack_top) =
|
||||
(uint32_t)start_routine; // Return address (the thread's entry point)
|
||||
*(--stack_top) = (uint32_t)arg; // Argument to pass to the thread
|
||||
|
||||
// Set the thread's state to ready
|
||||
thread_table[index].state = THREAD_READY;
|
||||
|
||||
// If this is the first thread, switch to it
|
||||
if (index == 0) {
|
||||
scheduler();
|
||||
}
|
||||
}
|
||||
|
||||
// Yield the CPU to another thread
|
||||
void thread_yield(void) {
|
||||
// Find the next thread in a round-robin manner
|
||||
uint32_t next_thread = (current_thread + 1) % num_threads;
|
||||
while (next_thread != current_thread &&
|
||||
thread_table[next_thread].state != THREAD_READY) {
|
||||
next_thread = (next_thread + 1) % num_threads;
|
||||
}
|
||||
|
||||
if (next_thread != current_thread) {
|
||||
current_thread = next_thread;
|
||||
scheduler();
|
||||
}
|
||||
}
|
||||
|
||||
// Exit the current thread
|
||||
void thread_exit(void) {
|
||||
thread_table[current_thread].state =
|
||||
THREAD_BLOCKED; // Mark the thread as blocked (finished)
|
||||
free(thread_table[current_thread].stack); // Free the thread's stack
|
||||
num_threads--; // Decrease thread count
|
||||
|
||||
// Yield to the next thread
|
||||
thread_yield();
|
||||
}
|
||||
|
||||
// Scheduler: This function selects the next thread to run
|
||||
void scheduler(void) {
|
||||
// Find the next ready thread
|
||||
uint32_t next_thread = (current_thread + 1) % num_threads;
|
||||
while (thread_table[next_thread].state != THREAD_READY) {
|
||||
next_thread = (next_thread + 1) % num_threads;
|
||||
}
|
||||
|
||||
if (next_thread != current_thread) {
|
||||
current_thread = next_thread;
|
||||
context_switch(&thread_table[current_thread]);
|
||||
}
|
||||
}
|
||||
|
||||
// Context switch to the next thread (assembly would go here to save/load
|
||||
// registers)
|
||||
void context_switch(Thread* next) {
|
||||
// For simplicity, context switching in this example would involve
|
||||
// saving/restoring registers. In a real system, you would need to save the
|
||||
// CPU state (registers) and restore the next thread's state.
|
||||
my_printf("Switching to thread...\n");
|
||||
next->start_routine(next->arg); // Start running the next thread
|
||||
}
|
||||
|
||||
// Simple mutex functions (spinlock)
|
||||
void mutex_init(void) { mutex_locked = 0; }
|
||||
|
||||
void mutex_lock(void) {
|
||||
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
|
||||
// Busy wait (spinlock)
|
||||
}
|
||||
}
|
||||
|
||||
void mutex_unlock(void) { __sync_lock_release(&mutex_locked); }
|
||||
36
kernel/threading.h
Normal file
36
kernel/threading.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef THREADING_H
|
||||
#define THREADING_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Define a basic thread structure (Thread Control Block - TCB)
|
||||
typedef struct Thread {
|
||||
void (*start_routine)(void *); // Function pointer to the thread function
|
||||
void *arg; // Argument to be passed to the thread function
|
||||
uint32_t *stack; // Pointer to the thread's stack
|
||||
uint32_t *stack_top; // Top of the stack (where the thread will start)
|
||||
uint32_t stack_size; // Size of the stack
|
||||
uint32_t state; // Thread state (running, ready, blocked)
|
||||
} Thread;
|
||||
|
||||
// Thread states
|
||||
#define THREAD_RUNNING 0
|
||||
#define THREAD_READY 1
|
||||
#define THREAD_BLOCKED 2
|
||||
|
||||
// Thread management functions
|
||||
void thread_init(void);
|
||||
void thread_create(Thread *thread, void (*start_routine)(void *), void *arg);
|
||||
void thread_yield(void);
|
||||
void thread_exit(void);
|
||||
|
||||
// Scheduler function
|
||||
void scheduler(void);
|
||||
|
||||
// Synchronization functions (mutex spinlocks)
|
||||
void mutex_init(void);
|
||||
void mutex_lock(void);
|
||||
void mutex_unlock(void);
|
||||
|
||||
#endif // THREADING_H
|
||||
36
kernel/timer.c
Normal file
36
kernel/timer.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "timer.h"
|
||||
#include "io.h"
|
||||
#include "isr.h"
|
||||
#include "terminal.h"
|
||||
#include "stdio.h"
|
||||
#include "utils.h"
|
||||
|
||||
static volatile uint32_t tick = 0;
|
||||
|
||||
void timer_callback(void) {
|
||||
tick++;
|
||||
|
||||
if (tick % 100 == 0) {
|
||||
char buf[16];
|
||||
itoa(tick, buf, 10);
|
||||
terminal_write("Tick count: ");
|
||||
terminal_write(buf);
|
||||
terminal_write("\n");
|
||||
}
|
||||
|
||||
outb(0x20, 0x20); // EOI to PIC
|
||||
}
|
||||
|
||||
void timer_init(uint32_t frequency) {
|
||||
register_interrupt_handler(32, timer_callback); // IRQ0 = Interrupt 32
|
||||
|
||||
uint32_t divisor = 1193180 / frequency;
|
||||
|
||||
outb(0x43, 0x36); // Command byte
|
||||
outb(0x40, divisor & 0xFF); // Low byte
|
||||
outb(0x40, (divisor >> 8)); // High byte
|
||||
}
|
||||
|
||||
uint32_t timer_get_ticks(void) {
|
||||
return tick;
|
||||
}
|
||||
9
kernel/timer.h
Normal file
9
kernel/timer.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void timer_init(uint32_t frequency);
|
||||
uint32_t timer_get_ticks(void);
|
||||
|
||||
#endif
|
||||
64
kernel/usb.c
Normal file
64
kernel/usb.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "usb.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// USB version detection
|
||||
bool usb_detect_version(uint16_t *version) {
|
||||
if (!version) return false;
|
||||
*version = 0x0110; // Example: USB 1.1
|
||||
return true;
|
||||
}
|
||||
|
||||
// USB initialization for 1.x
|
||||
bool usb_init(void) {
|
||||
// Detect controller type (UHCI or OHCI)
|
||||
bool uhci_supported = uhci_init();
|
||||
bool ohci_supported = ohci_init();
|
||||
|
||||
if (!uhci_supported && !ohci_supported) {
|
||||
return false; // No supported controllers found
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// USB device enumeration (1.x)
|
||||
bool usb_enumerate_devices(void) {
|
||||
// Implementation for detecting devices on USB 1.x ports
|
||||
return true;
|
||||
}
|
||||
|
||||
// HID initialization for USB 1.x
|
||||
bool usb_hid_init(void) {
|
||||
// Ensure USB is initialized
|
||||
if (!usb_init()) return false;
|
||||
|
||||
return usb_enumerate_devices();
|
||||
}
|
||||
|
||||
// USB transfers (stubs for 1.x)
|
||||
bool usb_control_transfer(/* parameters */) {
|
||||
// Implement control transfer for USB 1.x
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usb_interrupt_transfer(/* parameters */) {
|
||||
// Implement interrupt transfer for USB 1.x
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usb_bulk_transfer(/* parameters */) {
|
||||
// Implement bulk transfer for USB 1.x
|
||||
return true;
|
||||
}
|
||||
|
||||
// USB host controller initialization (UHCI & OHCI)
|
||||
bool uhci_init(void) {
|
||||
// Initialize UHCI controller (USB 1.x)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ohci_init(void) {
|
||||
// Initialize OHCI controller (USB 1.x)
|
||||
return true;
|
||||
}
|
||||
24
kernel/usb.h
Normal file
24
kernel/usb.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef USB_H
|
||||
#define USB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// USB initialization and management functions
|
||||
bool usb_init(void);
|
||||
bool usb_detect_version(uint16_t *version);
|
||||
bool usb_enumerate_devices(void);
|
||||
|
||||
// HID-specific functions
|
||||
bool usb_hid_init(void);
|
||||
|
||||
// USB transfer functions
|
||||
bool usb_control_transfer(/* parameters */);
|
||||
bool usb_interrupt_transfer(/* parameters */);
|
||||
bool usb_bulk_transfer(/* parameters */);
|
||||
|
||||
// USB host controller initialization (USB 1.x only)
|
||||
bool uhci_init(void); // UHCI support
|
||||
bool ohci_init(void); // OHCI support
|
||||
|
||||
#endif // USB_H
|
||||
78
kernel/utils.c
Normal file
78
kernel/utils.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "utils.h"
|
||||
|
||||
static void reverse(char* str, int len) {
|
||||
int start = 0;
|
||||
int end = len - 1;
|
||||
while (start < end) {
|
||||
char temp = str[start];
|
||||
str[start++] = str[end];
|
||||
str[end--] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
// Integer to ASCII for signed ints
|
||||
char* itoa(int value, char* str, int base) {
|
||||
int i = 0;
|
||||
int isNegative = 0;
|
||||
unsigned int uval;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Handle zero explicitly
|
||||
if (value == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Handle negative numbers (only for base 10)
|
||||
if (value < 0 && base == 10) {
|
||||
isNegative = 1;
|
||||
uval = (unsigned int)(-value);
|
||||
} else {
|
||||
uval = (unsigned int)value;
|
||||
}
|
||||
|
||||
while (uval != 0) {
|
||||
int rem = uval % base;
|
||||
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
|
||||
uval /= base;
|
||||
}
|
||||
|
||||
if (isNegative) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
reverse(str, i);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Integer to ASCII for unsigned ints
|
||||
char* utoa(unsigned int value, char* str, int base) {
|
||||
int i = 0;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
while (value != 0) {
|
||||
int rem = value % base;
|
||||
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
|
||||
value /= base;
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
reverse(str, i);
|
||||
return str;
|
||||
}
|
||||
12
kernel/utils.h
Normal file
12
kernel/utils.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// Convert integer to string (base is typically 10, 16, etc.)
|
||||
char* itoa(int value, char* str, int base);
|
||||
|
||||
// Convert unsigned integer to string (base is typically 10, 16, etc.)
|
||||
char* utoa(unsigned int value, char* str, int base);
|
||||
|
||||
#endif // UTILS_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user