195 Commits

Author SHA1 Message Date
05f5576c9c Create cmos.c 2026-01-28 16:58:08 -08:00
e750049556 Create cmos.h
Add a thing so that we can print CMOS backed clock time to the screen rtc etc
2026-01-28 16:55:04 -08:00
9be6a4e64b Merge pull request #74 from gbowne1/gbowne1-addpci
Adds PCI bus driver implementation
2026-01-28 13:40:37 -08:00
255ac93cae Merge pull request #75 from gbowne1/gbowne1-addps2
Add a PS/2 mouse keyboard driver
2026-01-28 13:38:06 -08:00
Borna Šoštarić
0584f7d49d Merge branch 'main' into gbowne1-addpci 2026-01-28 21:41:59 +01:00
4700e3f11a Update ata.h
Updated header to match ata.c
2026-01-28 21:41:10 +01:00
769f96acbc Update ata.c
Updated ata.c to include fixed BSY status register
2026-01-28 21:41:10 +01:00
1575c0dd12 Create ata.c 2026-01-28 21:41:10 +01:00
37e672a1c2 Create ata.h
Add base ATA PIO mode driver so that filesystems like fat16 fat32 work.
2026-01-28 21:41:10 +01:00
375dd9afdf Update gui.h
Adds gui base header
2026-01-28 21:41:10 +01:00
a42822dd54 Update gui.c
Add base gui implementation
2026-01-28 21:41:10 +01:00
6f0fee4182 Update hid.c
Fixing print_hex error
2026-01-28 21:41:10 +01:00
01f130227b Update keyboard.h
fixed typo in header include
2026-01-28 21:41:10 +01:00
f473fd5c32 Update mouse.h
Fixes static
2026-01-28 21:41:10 +01:00
576f5be8a6 Update mouse.c
Make mouse_data non static
2026-01-28 21:41:10 +01:00
538b97b877 Update keyboard.c
Fix missing definitions so theres nothing that would break the build
2026-01-28 21:41:10 +01:00
4d2eec9e93 Update keyboard.h
fixing missing includes and definition
2026-01-28 21:41:10 +01:00
d30bc6a7f5 Update keyboard.h
Add a extern const for the scancode map
2026-01-28 21:41:10 +01:00
96050d6a99 Update keyboard.c
remove static
2026-01-28 21:41:10 +01:00
e9e3237ba9 Create hid.c
Add bass HID implementation
2026-01-28 21:41:10 +01:00
3c6b2a4e8c Create hid.h
Adding base HID device support for early HID standards 1.0
2026-01-28 21:41:10 +01:00
vmttmv
8db5173495 Fix includes for string.h/string_utils.h 2026-01-28 21:41:10 +01:00
c0220b3afc Update vga.c
Change my_strlen to use our klibc strlen in klibc/include/string.h
2026-01-28 21:41:10 +01:00
f9ef99d11b Update vga.h
Add vga_init(); function prototype
2026-01-28 21:41:10 +01:00
17ce531b62 Update display.c
Added the 95% completely wired up display driver implementation file
2026-01-28 21:41:10 +01:00
4aa6fa3b59 Update display.h
updated header for display driver display.c and display.h this will need to be finished wired up. Old display driver would have done nothing.
2026-01-28 21:41:10 +01:00
Borna Šoštarić
6d78074060 add rules for formatting consecutives 2026-01-28 21:41:10 +01:00
Borna Šoštarić
3fde819c49 add .clang-format 2026-01-28 21:41:10 +01:00
Borna Šoštarić
aac87e30e0 added editorconfig file 2026-01-28 21:41:10 +01:00
Borna Šoštarić
975e21f9be add clangd config file 2026-01-28 21:41:10 +01:00
Borna Šoštarić
db2c17d4b4 add compile_commands.json genereation 2026-01-28 21:41:10 +01:00
vmttmv
c805d24dd0 Fix PDE/PTE definitions, header cleanup
- Fixes PDE/PTE definitions in kernel/paging.h
- removes memset declaration from kernel/utils.h, uses klibc string.h as
needed
2026-01-28 21:41:10 +01:00
vmttmv
b3831b6c69 Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid() 2026-01-28 21:40:39 +01:00
4e1197356b Fix k_memcmp return logic and add disk_read_sector
Refactor k_memcmp to return correct difference and add disk_read_sector function.
2026-01-28 21:40:39 +01:00
a765fe5238 Fix extern declaration for disk_read_sector function 2026-01-28 21:40:39 +01:00
175c2949be Enhance cpu.h with Intel model definitions and struct
Added Intel model definitions and CPU info structure.
2026-01-28 21:40:39 +01:00
2dd82e69a7 Implement CPUID support check and CPU info printing
Added functions to check CPUID support and print CPU details.
2026-01-28 21:40:39 +01:00
Borna Šoštarić
aeecf2b0b7 fix vga clear section in on_error 2026-01-28 21:40:39 +01:00
vmttmv
091391907f Establish well-defined read buffers for bl, implement error printing 2026-01-28 21:40:39 +01:00
vmttmv
5dcf5d24b2 Fix non-aligned disk reads in bootloader 2026-01-28 21:40:39 +01:00
Borna Šoštarić
a7e536a6d3 fix RWX perms warnings in link step 2026-01-28 21:40:39 +01:00
vmttmv
b99cb27092 fix stage2.asm: disk reads wait for BSY 2026-01-28 21:40:39 +01:00
Borna Šoštarić
a3267d2ac3 initial implementation of klibc
fix linker error about ctx_switch
2026-01-28 21:40:39 +01:00
Borna Šoštarić
bc08701e63 lessen indirection in the makefile 2026-01-28 21:40:39 +01:00
Borna Šoštarić
e8f6beef8a update readme 2026-01-28 21:40:39 +01:00
Borna Šoštarić
bd427dc7e9 generate .build.env as part of configure script 2026-01-28 21:40:39 +01:00
Borna Šoštarić
619430d4bf add configure script for setting up cross compilation tools 2026-01-28 21:40:39 +01:00
Borna Šoštarić
84e28d8871 Merge branch 'main' into gbowne1-addps2 2026-01-28 21:36:14 +01:00
dee91ca211 Update ata.h
Updated header to match ata.c
2026-01-28 21:34:50 +01:00
59dfca9ebd Update ata.c
Updated ata.c to include fixed BSY status register
2026-01-28 21:34:50 +01:00
42f0b62e50 Create ata.c 2026-01-28 21:34:50 +01:00
0a4f0e59e3 Create ata.h
Add base ATA PIO mode driver so that filesystems like fat16 fat32 work.
2026-01-28 21:34:50 +01:00
9d37b7a944 Update gui.h
Adds gui base header
2026-01-28 21:34:50 +01:00
eecc2561da Update gui.c
Add base gui implementation
2026-01-28 21:34:50 +01:00
cae3731f91 Update hid.c
Fixing print_hex error
2026-01-28 21:34:50 +01:00
df33351d60 Update keyboard.h
fixed typo in header include
2026-01-28 21:34:50 +01:00
d2b19ce376 Update mouse.h
Fixes static
2026-01-28 21:34:50 +01:00
8aea3c3c0e Update mouse.c
Make mouse_data non static
2026-01-28 21:34:50 +01:00
f64e40d460 Update keyboard.c
Fix missing definitions so theres nothing that would break the build
2026-01-28 21:34:50 +01:00
45da511bfa Update keyboard.h
fixing missing includes and definition
2026-01-28 21:34:50 +01:00
0f2528e07b Update keyboard.h
Add a extern const for the scancode map
2026-01-28 21:34:50 +01:00
b36356f10c Update keyboard.c
remove static
2026-01-28 21:34:50 +01:00
4b981a5d18 Create hid.c
Add bass HID implementation
2026-01-28 21:34:50 +01:00
fa66433179 Create hid.h
Adding base HID device support for early HID standards 1.0
2026-01-28 21:34:50 +01:00
vmttmv
f08204a2b3 Fix includes for string.h/string_utils.h 2026-01-28 21:34:50 +01:00
bb6f9d1a16 Update vga.c
Change my_strlen to use our klibc strlen in klibc/include/string.h
2026-01-28 21:34:50 +01:00
57ac458a4f Update vga.h
Add vga_init(); function prototype
2026-01-28 21:34:50 +01:00
6ad8644752 Update display.c
Added the 95% completely wired up display driver implementation file
2026-01-28 21:34:50 +01:00
40462e55e6 Update display.h
updated header for display driver display.c and display.h this will need to be finished wired up. Old display driver would have done nothing.
2026-01-28 21:34:50 +01:00
Borna Šoštarić
4d2197aa2d add rules for formatting consecutives 2026-01-28 21:34:50 +01:00
Borna Šoštarić
f7b6a78b59 add .clang-format 2026-01-28 21:34:50 +01:00
Borna Šoštarić
2b32a29890 added editorconfig file 2026-01-28 21:34:50 +01:00
Borna Šoštarić
4ee0ddb9ef add clangd config file 2026-01-28 21:34:50 +01:00
Borna Šoštarić
13b915a99d add compile_commands.json genereation 2026-01-28 21:34:50 +01:00
vmttmv
d480fbcc80 Fix PDE/PTE definitions, header cleanup
- Fixes PDE/PTE definitions in kernel/paging.h
- removes memset declaration from kernel/utils.h, uses klibc string.h as
needed
2026-01-28 21:34:50 +01:00
vmttmv
d0c9c9c4e0 Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid() 2026-01-28 21:33:36 +01:00
8be984d565 Fix k_memcmp return logic and add disk_read_sector
Refactor k_memcmp to return correct difference and add disk_read_sector function.
2026-01-28 21:33:36 +01:00
4f992c8fc5 Fix extern declaration for disk_read_sector function 2026-01-28 21:33:36 +01:00
16057d41d6 Enhance cpu.h with Intel model definitions and struct
Added Intel model definitions and CPU info structure.
2026-01-28 21:33:36 +01:00
41281de743 Implement CPUID support check and CPU info printing
Added functions to check CPUID support and print CPU details.
2026-01-28 21:33:36 +01:00
Borna Šoštarić
dfb161a15a fix vga clear section in on_error 2026-01-28 21:33:36 +01:00
vmttmv
a450ac06f2 Establish well-defined read buffers for bl, implement error printing 2026-01-28 21:33:36 +01:00
vmttmv
235fd2636d Fix non-aligned disk reads in bootloader 2026-01-28 21:33:36 +01:00
Borna Šoštarić
8c529c6fe4 fix RWX perms warnings in link step 2026-01-28 21:33:36 +01:00
vmttmv
155563e434 fix stage2.asm: disk reads wait for BSY 2026-01-28 21:33:36 +01:00
Borna Šoštarić
8b1ea16c56 initial implementation of klibc
fix linker error about ctx_switch
2026-01-28 21:33:36 +01:00
Borna Šoštarić
7bda5c25b8 lessen indirection in the makefile 2026-01-28 21:33:36 +01:00
Borna Šoštarić
ac0fde28a0 update readme 2026-01-28 21:33:36 +01:00
Borna Šoštarić
a182bbca19 generate .build.env as part of configure script 2026-01-28 21:33:36 +01:00
Borna Šoštarić
c503709ff4 add configure script for setting up cross compilation tools 2026-01-28 21:33:36 +01:00
903061551c Update ps2.c
Remove the definition of the in/out assembly and add io.h include instead of inline
2026-01-27 12:41:17 -08:00
4393fbf6cb Update pci.c
Change this to use io.h to define the in/out assembly instead of doing directly
2026-01-27 12:37:38 -08:00
f572101d6b Merge pull request #102 from gbowne1/gbowne1-patch-5
Create ata.h
2026-01-27 08:53:21 -08:00
a37f94de44 Update ps2.c
Fixed the `__volatile__` into a volatile keyword
2026-01-27 08:16:44 -08:00
401a19143c Update pci.c
Fixed volatile to a keyword
2026-01-27 08:10:56 -08:00
3cd2ff6e1e Merge pull request #97 from gbowne1/gbowne1-patch-2
Update gui.c
2026-01-27 07:45:58 -08:00
aafd4efcb2 Update ata.h
Updated header to match ata.c
2026-01-26 17:18:39 -08:00
d6eb5115d5 Update ata.c
Updated ata.c to include fixed BSY status register
2026-01-26 17:16:31 -08:00
cc92ade8fd Update ps2.c
Fixing asm to __asm__
2026-01-26 17:06:44 -08:00
a9f6d5fa05 Update pci.c
Fixed the inline assembly macro asm to __asm__
2026-01-26 16:55:35 -08:00
84705fd225 Merge pull request #99 from gbowne1/gbowne1-add-hid
Add HID support
2026-01-26 12:18:04 -08:00
6c69b5fd6a Update hid.c
Fixing print_hex error
2026-01-25 08:45:45 -08:00
1037ba4f54 Update keyboard.h
fixed typo in header include
2026-01-25 08:24:43 -08:00
745deeddde Update mouse.h
Fixes static
2026-01-24 22:49:07 -08:00
f9e281a7ae Update mouse.c
Make mouse_data non static
2026-01-24 22:46:03 -08:00
18801a742f Update keyboard.c
Fix missing definitions so theres nothing that would break the build
2026-01-24 22:31:50 -08:00
a08648eff5 Update keyboard.h
fixing missing includes and definition
2026-01-24 22:21:44 -08:00
5a664c6e31 Update keyboard.h
Add a extern const for the scancode map
2026-01-24 22:14:37 -08:00
4c7de228f9 Update keyboard.c
remove static
2026-01-24 22:13:32 -08:00
af3f20485f Merge pull request #71 from gbowne1/gbowne1-patch-1
Update display.h
2026-01-20 00:18:22 -08:00
09b2b8cd11 Create ata.c 2026-01-18 17:48:39 -08:00
4079d18a45 Create ata.h
Add base ATA PIO mode driver so that filesystems like fat16 fat32 work.
2026-01-18 17:46:26 -08:00
b6c158957e Merge pull request #95 from shoshta73/qol
[misc] add editorconfig, clangd config and clang-format config
2026-01-18 16:46:05 -08:00
cca6aafd65 Create hid.c
Add bass HID implementation
2026-01-18 16:21:45 -08:00
49c1bad935 Create hid.h
Adding base HID device support for early HID standards 1.0
2026-01-18 16:20:43 -08:00
7adb798c17 Update gui.h
Adds gui base header
2026-01-18 15:43:03 -08:00
43b5cacd09 Update gui.c
Add base gui implementation
2026-01-18 15:40:23 -08:00
Borna Šoštarić
a7b0d1152f add rules for formatting consecutives 2026-01-18 10:54:18 +01:00
Borna Šoštarić
e38f1aa2ee add .clang-format 2026-01-18 10:40:34 +01:00
Borna Šoštarić
78d5e9a7ab added editorconfig file 2026-01-18 10:18:23 +01:00
Borna Šoštarić
daead5ee57 add clangd config file 2026-01-18 10:15:38 +01:00
Borna Šoštarić
4fb81d2e57 add compile_commands.json genereation 2026-01-18 10:10:39 +01:00
vmttmv
19ef0a8627 Fix includes for string.h/string_utils.h 2026-01-16 20:49:06 +02:00
c5f7e4e563 Update vga.c
Change my_strlen to use our klibc strlen in klibc/include/string.h
2026-01-16 01:19:01 -08:00
f516334e0d Update vga.h
Add vga_init(); function prototype
2026-01-15 17:00:37 -08:00
19f7c7b213 Merge pull request #93 from vmttmv/fix/91-header-cleanup
Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid()
2026-01-13 07:40:03 -08:00
bc9d84a93e Merge branch 'main' into fix/91-header-cleanup 2026-01-13 07:39:46 -08:00
9066ceaddb Merge pull request #94 from vmttmv/fix/92-paging
Fix PDE/PTE definitions, header cleanup
2026-01-13 07:29:27 -08:00
vmttmv
3b67e81ed0 Fix PDE/PTE definitions, header cleanup
- Fixes PDE/PTE definitions in kernel/paging.h
- removes memset declaration from kernel/utils.h, uses klibc string.h as
needed
2026-01-12 04:02:15 +02:00
vmttmv
841892398a Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid() 2026-01-12 02:43:37 +02:00
86608ef48c Merge pull request #90 from gbowne1/gbowne1-patch-2
Fix extern declaration for disk_read_sector function
2026-01-10 21:31:34 -08:00
785c8920d8 Merge pull request #86 from gbowne1/gbowne1-cpuidfix-1
IImplement CPUID support check and CPU info printing
2026-01-09 11:59:52 -08:00
c0e7ab6be0 Fix k_memcmp return logic and add disk_read_sector
Refactor k_memcmp to return correct difference and add disk_read_sector function.
2026-01-08 21:10:35 -08:00
f78bc27f35 Fix extern declaration for disk_read_sector function 2026-01-08 21:03:10 -08:00
507b4f5511 Merge pull request #89 from vmttmv/fix/bl-bounds
Establish well-defined read buffers for bl, implement error printing
2026-01-07 21:00:00 -08:00
Borna Šoštarić
12046ce96b fix vga clear section in on_error 2026-01-08 05:20:42 +01:00
vmttmv
a9b8ac7066 Establish well-defined read buffers for bl, implement error printing 2026-01-07 02:33:46 +02:00
d6ab8c91f8 Merge pull request #87 from vmttmv/fix/bl-nonaligned-reads
Fix non-aligned disk reads in bootloader
2026-01-05 17:45:37 -08:00
vmttmv
35ebd5fd72 Fix non-aligned disk reads in bootloader 2026-01-06 00:44:55 +02:00
10d3761be1 Enhance cpu.h with Intel model definitions and struct
Added Intel model definitions and CPU info structure.
2026-01-05 00:46:14 -08:00
cc2e967a4d Implement CPUID support check and CPU info printing
Added functions to check CPUID support and print CPU details.
2026-01-05 00:42:05 -08:00
9eae2e1005 Merge pull request #84 from shoshta73/RWXPerms
[fix] LOAD segment with RWX permissions
2025-12-30 17:19:16 -08:00
Borna Šoštarić
bd4236ad9b fix RWX perms warnings in link step 2025-12-31 01:39:18 +01:00
5292808934 Merge pull request #83 from vmttmv/main
Fix BL disk read status polls
2025-12-30 16:13:42 -08:00
vmttmv
09c48c2f50 fix stage2.asm: disk reads wait for BSY 2025-12-30 05:08:54 +02:00
caea475daf Merge pull request #79 from shoshta73/klibc
Initial implementation of klibc
2025-12-27 11:14:45 -08:00
Borna Šoštarić
f30be3ddd5 initial implementation of klibc
fix linker error about ctx_switch
2025-12-27 19:44:54 +01:00
d83e247bbd Merge pull request #76 from shoshta73/configure-script
Configure script
2025-12-19 15:22:02 -08:00
Borna Šoštarić
a1a6fd2aa9 lessen indirection in the makefile 2025-12-19 23:46:03 +01:00
Borna Šoštarić
66f9056406 update readme 2025-12-19 23:44:00 +01:00
Borna Šoštarić
45acbb5c04 generate .build.env as part of configure script 2025-12-19 23:41:32 +01:00
Borna Šoštarić
649a227e41 add configure script for setting up cross compilation tools 2025-12-19 22:47:34 +01:00
4e8b13ad77 Create ps2.c
add initial ps/2 driver code. This will need wired up to be used
2025-12-17 05:50:02 -08:00
9f8ca3a60c Create ps2.h 2025-12-17 05:47:59 -08:00
574980035e Create pci.c 2025-12-17 05:32:44 -08:00
a0bd0941d6 Create pci.h 2025-12-17 05:31:15 -08:00
4047bc3936 Update display.c
Added the 95% completely wired up display driver implementation file
2025-11-26 16:02:07 -08:00
7e54f0de66 Update display.h
updated header for display driver display.c and display.h this will need to be finished wired up. Old display driver would have done nothing.
2025-11-26 15:53:58 -08:00
940b2810cb Update io.h
adding the missing io
2025-11-20 10:07:01 -08:00
01f85f97ec Update fat12.h
better header for FAT12 kernel driver
2025-11-19 09:31:22 -08:00
fd2c567d29 Update fat12.c
implementation of kernel space fat12 kernel driver for fat12
2025-11-19 09:29:04 -08:00
9de9cc6523 Update scheduler.h 2025-11-19 08:44:15 -08:00
e9a78c835a Create context_switch.s
new context_switch.s for x86 IA32.
must confirm nasm.
2025-11-19 08:43:11 -08:00
77400d8f5a Update scheduler.c
old scheduler might not work on x86 IA-32 32 bit
2025-11-19 08:41:03 -08:00
cdf5676085 Merge pull request #70 from vmttmv/main
Kernel build fixes
2025-11-18 18:11:18 -08:00
vmttmv
8743fa9e24 Multiple changes:
- Makefile: fix linker script path
- irq.c: `irqN()` stubs
- irq.h: fix missing header
- isr.h/isr.c extern `interrupt_handlers`
- utils.c: remove duplicate `memcmp`
2025-11-19 03:32:06 +02:00
3036ee3dfd Delete bootloader/linker.ld
delete linker.ld as moved to kernel space
2025-11-14 14:18:54 -08:00
d5906d72de Move linker.ld
Move to kernel
2025-11-14 14:17:27 -08:00
2ab0efdee1 Delete bootloader/Makefile
Remove Makefile in bootloader
2025-11-14 14:15:51 -08:00
0e011c1682 Update README.md 2025-11-13 14:45:56 -08:00
eccf9d7d7c Merge pull request #69 from vmttmv/bootloader
BL implementation
2025-11-13 14:36:09 -08:00
vmttmv
62fe09d80d multiple changes: BL1/BL2/kernel separation (build system, etc.) BL2 implementation. BL documentation 2025-11-13 23:35:54 +02:00
f1b0670a15 Update kmain.c
Adding isr stuff
2025-11-10 05:59:22 -08:00
48fdb348ca Update irq.h
add implementation for irq handles to header
2025-11-10 05:49:36 -08:00
6dbd08c808 Update irq.c
Implement the irq handles
2025-11-10 05:48:02 -08:00
9ac3a2b862 Update terminal.c
Fixed minor issue with terminal
2025-11-10 05:19:25 -08:00
95f0507c24 Update keyboard.c
some issues with keyboard buffer fixed and interrupt greater than 32 would cause EOI to get sent to PIC 2x
2025-11-10 05:09:30 -08:00
70539f72b8 Update Makefile
This Makefile is for i686-elf cross compilation  only
2025-11-10 03:42:17 -08:00
1b046776e0 Update boot1.asm
remove duplicate print
2025-11-10 03:29:17 -08:00
2609f52dd6 Update paging.c
Fixed page table entry so it doesnt clobber kernel
2025-11-10 03:12:34 -08:00
f2e75c5142 Update kmalloc.c
Safer 1MB heap. Original value would have caused a heap overflow
2025-11-10 02:51:56 -08:00
056d3eb374 Update framebuffer.h
Added the stub graphics framebuffer stub
2025-11-04 01:21:35 -08:00
98f0f58ce4 Add stub code for the graphics franebuffer 2025-11-04 01:13:36 -08:00
7d9d0aeee3 Create memory.c
This is the implementation for memory.c memory.h pair to house the 
memset. memcmp, memcpy, memmove etc careful as there are now duplicates in utils implementation
2025-11-02 17:39:31 -08:00
8e5dff4271 Add memory.h with memcpy and memmove declarations
Define memory management functions and include guards.

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

This is the header
2025-11-02 17:32:53 -08:00
9aa1b85ca0 Merge pull request #62 from vmttmv/main
begin fixing build errors for stage2
2025-10-25 17:42:46 -07:00
vmttmv
9216673b18 begin fixing build errors 2025-10-26 03:03:20 +03:00
ed07e2cd9c Delete kernel/linker.ld
Removing linker.ld for the grub legacy stuff I was gonna try
2025-10-25 15:10:55 -07:00
a4318d3c79 Delete kernel/multiboot.h
Remove empty multiboot.h for grub legacy
2025-10-25 15:09:32 -07:00
9cde2e708d Merge pull request #61 from vmttmv/main
stage1: fix load addresses for stage2/kernel
2025-10-25 14:53:23 -07:00
vmttmv
c22f6b6f14 stage1: fix load addresses for stage2/kernel 2025-10-26 00:18:45 +03:00
6267863939 Merge pull request #60 from vmttmv/main
build: debug symbols for stage1
2025-10-25 11:42:52 -07:00
vmttmv
49114214cb build: debug symbols for stage1 2025-10-25 21:26:52 +03:00
e58abdae1c Merge pull request #59 from vmttmv/main
Makefile target organization
2025-10-25 10:28:29 -07:00
vmttmv
dd37ba8ed6 make: explicit build dir, separate stage1 target so it can be called easier 2025-10-25 19:52:39 +03:00
79 changed files with 2707 additions and 723 deletions

24
.clang-format Normal file
View 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
View File

@@ -0,0 +1,6 @@
CompileFlags:
CompilationDatabase: build
Diagnostics:
UnusedIncludes: Strict
MissingIncludes: Strict

12
.editorconfig Normal file
View 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

6
.gitignore vendored
View File

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

View File

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

View File

@@ -5,7 +5,7 @@
[![Platform](https://img.shields.io/badge/platform-x86_IA32-lightgrey?style=flat-square)](https://en.wikipedia.org/wiki/IA-32)
[![Made with](https://img.shields.io/badge/made%20with-C%20%26%20NASM-9cf?style=flat-square)](#)
> **ClassicOS** is a 32-bit Intel x86 operating system built from scratch using C, NASM, and GCC.
> **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.
---
@@ -35,6 +35,7 @@ Youll need the following tools installed:
- `qemu-system-i386`
Optional:
- `gdb`
- `vncviewer` (TigerVNC or similar)
@@ -42,13 +43,27 @@ Optional:
## 🛠️ Building ClassicOS
Clone and build:
Clone repository:
```bash
```sh
git clone https://github.com/gbowne1/ClassicOS.git
cd ClassicOS
make
```
build kernel
for %f in (*.c) do gcc -m32 -O0 -Wall -Wextra -Werror -pedantic -ffreestanding -nostdlib -fno-pic -fno-stack-protector -fno-pie -march=i386 -mtune=i386 -c "%f" -o "%f.o"
Run `configure` script to build a cross-compiler toolchain for `i386-elf`:
```sh
./configure
```
Source the `.build.env` file to add the cross-compiler toolchain to your PATH:
```sh
source .build.env
```
Build the kernel:
```sh
make
```

View File

27
bootloader/README.md Normal file
View File

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

View File

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

View File

@@ -1,26 +0,0 @@
ENTRY(kmain)
SECTIONS {
. = 1M;
.text : {
*(.text*)
}
.rodata : { *(.rodata*) }
.data : { *(.data*) }
.bss : {
*(.bss*)
*(COMMON)
}
.stack (NOLOAD) : {
. = ALIGN(4);
. = . + 0x1000;
}
.heap (NOLOAD) : {
. = ALIGN(4);
. = . + 0x10000;
}
}

View File

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

26
bootloader/stage2.asm Normal file
View File

@@ -0,0 +1,26 @@
[BITS 32]
global _start
global ata_lba_read
extern load_kernel
_start:
; Set up segments
; Data segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Code segment
mov ax, 0x08
mov cs, ax
; Stack (must be identity-mapped)
mov esp, 0x90000
call load_kernel
jmp eax

12
bootloader/stage2.ld Normal file
View File

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

235
bootloader/stage2_load.c Normal file
View File

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

169
configure vendored Executable file
View File

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

6
gdb.txt Normal file
View File

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

119
kernel/ata.c Normal file
View 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
View 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

86
kernel/cmos.c Normal file
View File

@@ -0,0 +1,86 @@
#include "cmos.h"
#include "io.h"
#include "print.h"
#define CMOS_ADDR 0x70
#define CMOS_DATA 0x71
enum {
CMOS_SEC = 0x00,
CMOS_MIN = 0x02,
CMOS_HOUR = 0x04,
CMOS_DAY = 0x07,
CMOS_MONTH= 0x08,
CMOS_YEAR = 0x09,
CMOS_STAT_A = 0x0A,
CMOS_STAT_B = 0x0B
};
// Check if CMOS is currently updating its values
static int is_updating() {
outb(CMOS_ADDR, CMOS_STAT_A);
return (inb(CMOS_DATA) & 0x80);
}
static uint8_t get_register(int reg) {
outb(CMOS_ADDR, reg);
return inb(CMOS_DATA);
}
void cmos_read_time(cmos_time_t* time) {
// Wait for any current update to finish
while (is_updating());
uint8_t sec = get_register(CMOS_SEC);
uint8_t min = get_register(CMOS_MIN);
uint8_t hour = get_register(CMOS_HOUR);
uint8_t day = get_register(CMOS_DAY);
uint8_t month = get_register(CMOS_MONTH);
uint8_t year = get_register(CMOS_YEAR);
uint8_t statb = get_register(CMOS_STAT_B);
// If Bit 2 of Status Register B is 0, then values are BCD
if (!(statb & 0x04)) {
time->second = (sec & 0x0F) + ((sec / 16) * 10);
time->minute = (min & 0x0F) + ((min / 16) * 10);
time->hour = ((hour & 0x0F) + (((hour & 0x70) / 16) * 10)) | (hour & 0x80);
time->day = (day & 0x0F) + ((day / 16) * 10);
time->month = (month & 0x0F) + ((month / 16) * 10);
time->year = (year & 0x0F) + ((year / 16) * 10);
} else {
time->second = sec;
time->minute = min;
time->hour = hour;
time->day = day;
time->month = month;
time->year = year;
}
// Adjust for Century (assuming we are in the 2000s for ClassicOS)
time->year += 2000;
}
void cmos_print_time(cmos_time_t* time) {
// Using your print_string/itoa style logic
char buf[16];
print_string("System Time: ");
// Simple padding check for minutes
print_hex(time->hour, 0, 1);
print_string(":");
if (time->minute < 10) print_string("0");
print_hex(time->minute, 0, 1);
print_string(":");
if (time->second < 10) print_string("0");
print_hex(time->second, 0, 1);
print_string(" ");
print_hex(time->month, 0, 1);
print_string("/");
print_hex(time->day, 0, 1);
print_string("/");
print_hex(time->year, 0, 1);
print_string("\n");
}

18
kernel/cmos.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef CMOS_H
#define CMOS_H
#include <stdint.h>
typedef struct {
uint8_t second;
uint8_t minute;
uint8_t hour;
uint8_t day;
uint8_t month;
uint32_t year;
} cmos_time_t;
void cmos_read_time(cmos_time_t* time);
void cmos_print_time(cmos_time_t* time);
#endif

25
kernel/context_switch.asm Normal file
View File

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

View File

@@ -2,36 +2,106 @@
#include "serial.h"
#include "terminal.h"
#include "utils.h"
#include "print.h"
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
asm volatile (
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"(function)
: "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("CPU Vendor: ");
terminal_write("Vendor: ");
terminal_write(vendor);
terminal_write("\n");
serial_write("CPU Vendor: ");
serial_write(vendor);
serial_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;
terminal_write("CPUID max leaf: ");
print_hex(eax); // You must implement this (see below)
terminal_write("\n");
// 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");
}
}

View File

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

View File

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

View File

@@ -2,13 +2,21 @@
#define DISPLAY_H
#include <stdint.h>
#include "vga.h" // Include VGA functions
#define VGA_PORT 0x3C0 // Base port for VGA
#define VGA_PORT 0x3C0 // Base port for VGA (Often used for general control, though 0x3D4/0x3D5 are used for cursor)
// Function prototypes
void init_display(void);
void enumerate_displays(void);
void set_display_mode(uint8_t mode);
void set_display_mode(uint8_t mode); // In this context, modes are typically BIOS or VESA modes, which are complex.
// We'll treat this as a placeholder/simple mode call.
void clear_display(void);
// 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

View File

@@ -1,5 +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() {
// Filesystem initialization code
// 1. Read Boot Sector (LBA 0)
disk_read_sector(0, g_sector_buffer);
// 2. Copy BPB data safely
// We cast the buffer to our struct
fat12_bpb_t *boot_sector = (fat12_bpb_t*)g_sector_buffer;
bpb = *boot_sector;
// 3. Calculate System Offsets
fat_start_lba = bpb.reserved_sectors;
// Root Dir starts after FATs
// LBA = Reserved + (FatCount * SectorsPerFat)
root_dir_lba = fat_start_lba + (bpb.fat_count * bpb.sectors_per_fat);
// Calculate size of Root Directory in sectors
// (Entries * 32 bytes) / 512
root_dir_sectors = (bpb.dir_entries_count * 32 + FAT12_SECTOR_SIZE - 1) / FAT12_SECTOR_SIZE;
// Data starts after Root Directory
data_start_lba = root_dir_lba + root_dir_sectors;
}
// Helper: Read the FAT table to find the NEXT cluster
static uint16_t fat12_get_next_cluster(uint16_t current_cluster) {
// FAT12 Offset Calculation:
// Offset = Cluster + (Cluster / 2)
uint32_t fat_offset = current_cluster + (current_cluster / 2);
uint32_t fat_sector = fat_start_lba + (fat_offset / FAT12_SECTOR_SIZE);
uint32_t ent_offset = fat_offset % FAT12_SECTOR_SIZE;
// Read the sector containing the FAT entry
disk_read_sector(fat_sector, g_sector_buffer);
// Read 16 bits (2 bytes)
// Note: If ent_offset == 511, the entry spans two sectors.
// For simplicity in this snippet, we ignore that edge case (rare).
// A robust kernel would check if(ent_offset == 511) and read next sector.
uint16_t val = *(uint16_t*)&g_sector_buffer[ent_offset];
if (current_cluster & 1) {
return val >> 4; // Odd: High 12 bits
} else {
return val & 0x0FFF; // Even: Low 12 bits
}
}
file_t fat12_open(const char *filename) {
file_t file = {0};
char target_name[11];
to_fat_name(filename, target_name);
// Search Root Directory
for (uint32_t i = 0; i < root_dir_sectors; i++) {
disk_read_sector(root_dir_lba + i, g_sector_buffer);
fat12_entry_t *entry = (fat12_entry_t*)g_sector_buffer;
// Check all 16 entries in this sector (512 / 32 = 16)
for (int j = 0; j < 16; j++) {
if (entry[j].filename[0] == 0x00) return file; // End of Dir
// Check if filename matches
if (k_memcmp(entry[j].filename, target_name, 11) == 0) {
// Found it!
file.start_cluster = entry[j].low_cluster_num;
file.size = entry[j].file_size;
// Initialize file cursor
file.current_cluster = file.start_cluster;
file.bytes_read = 0;
return file;
}
}
}
// Not found (file.start_cluster will be 0)
return file;
}
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read) {
if (file->start_cluster == 0) return 0; // File not open
uint32_t total_read = 0;
while (bytes_to_read > 0) {
// Check for EOF marker in FAT12 (>= 0xFF8)
if (file->current_cluster >= 0xFF8) break;
// Calculate Physical LBA of current cluster
// LBA = DataStart + ((Cluster - 2) * SectorsPerCluster)
uint32_t lba = data_start_lba + ((file->current_cluster - 2) * bpb.sectors_per_cluster);
// Read the cluster
// NOTE: Assumes SectorsPerCluster = 1 (Standard Floppy)
disk_read_sector(lba, g_sector_buffer);
// Determine how much to copy from this sector
uint32_t chunk_size = FAT12_SECTOR_SIZE;
// If the file is smaller than a sector, or we are at the end
if (chunk_size > bytes_to_read) chunk_size = bytes_to_read;
// Check if we are reading past file size
if (file->bytes_read + chunk_size > file->size) {
chunk_size = file->size - file->bytes_read;
}
// Copy to user buffer
for (uint32_t i = 0; i < chunk_size; i++) {
buffer[total_read + i] = g_sector_buffer[i];
}
total_read += chunk_size;
file->bytes_read += chunk_size;
bytes_to_read -= chunk_size;
// If we finished this cluster, move to the next one
if (chunk_size == FAT12_SECTOR_SIZE) { // Or strictly logic based on position
file->current_cluster = fat12_get_next_cluster(file->current_cluster);
} else {
// We finished the file or the request
break;
}
}
return total_read;
}
int disk_read_sector(uint32_t lba, uint8_t *buffer) {
// For now, do nothing and return success
return 0;
}

View File

@@ -1,47 +1,67 @@
#ifndef FAT12_H
#define FAT12_H
#include <stdint.h> /* Include standard integer types */
#include <stdio.h> /* Include standard I/O library */
#include <stdlib.h> /* Include standard library */
#include <stdint.h>
#define FAT12_SECTOR_SIZE 512 /* Sector size for FAT12 */
#define FAT12_MAX_FILES 128 /* Maximum number of files in root directory */
#define FAT12_ROOT_DIR_SECTORS 1 /* Number of sectors for root directory */
// --- 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]; /* Jump instruction for boot */
char oem[8]; /* OEM name */
uint16_t bytes_per_sector; /* Bytes per sector */
uint8_t sectors_per_cluster; /* Sectors per cluster */
uint16_t reserved_sectors; /* Reserved sectors count */
uint8_t num_fats; /* Number of FATs */
uint16_t max_root_dir_entries; /* Max entries in root directory */
uint16_t total_sectors; /* Total sectors */
uint8_t media_descriptor; /* Media descriptor */
uint16_t fat_size; /* Size of each FAT */
uint16_t sectors_per_track; /* Sectors per track */
uint16_t num_heads; /* Number of heads */
uint32_t hidden_sectors; /* Hidden sectors count */
uint32_t total_sectors_large; /* Total sectors for large disks */
} __attribute__((packed)) FAT12_BootSector; /* Packed structure for boot sector */
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 name[11]; /* File name (8.3 format) */
uint8_t attr; /* File attributes */
uint16_t reserved; /* Reserved */
uint16_t time; /* Time of last write */
uint16_t date; /* Date of last write */
uint16_t start_cluster; /* Starting cluster number */
uint32_t file_size; /* File size in bytes */
} __attribute__((packed)) FAT12_DirEntry; /* Directory entry structure */
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;
void initialize_fat12(const char *disk_image); /* Function to initialize FAT12 */
void read_fat12(const char *disk_image); /* Function to read FAT12 */
void write_fat12(const char *disk_image); /* Function to write FAT12 */
void list_files(const char *disk_image); /* Function to list files in root directory */
void read_file(const char *disk_image, const char *filename); /* Function to read a file */
void write_file(const char *disk_image, const char *filename, const uint8_t *data, size_t size); /* Function to write a file */
// --- 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;
#endif
/* FAT12_H */
// --- 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

View File

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

View File

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

View File

@@ -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
}

View 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

65
kernel/hid.c Normal file
View 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
View 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

View File

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

View File

@@ -4,12 +4,32 @@
#include <stdint.h>
static inline void outb(uint16_t port, uint8_t val) {
asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
__asm__("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
__asm__("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outw(uint16_t port, uint16_t val) {
__asm__("outw %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t ret;
__asm__("inw %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outl(uint16_t port, uint32_t val) {
__asm__("outl %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint32_t inl(uint16_t port) {
uint32_t ret;
__asm__("inl %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
[BITS 32]
[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]
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]

View File

@@ -1,21 +1,22 @@
#include <stdbool.h>
#include "terminal.h"
#include "serial.h"
#include "isr.h"
#include "io.h"
#include "utils.h"
#include "print.h"
static isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
void isr_handler(uint32_t int_num, uint32_t err_code) {
terminal_write("Interrupt occurred: ");
print_hex(int_num);
print_hex(int_num, true, false);
terminal_write("\n");
serial_write("INT triggered\n");
terminal_write("Error code: ");
print_hex(err_code);
print_hex(err_code, true, false);
terminal_write("\n");
if (interrupt_handlers[int_num]) {
@@ -33,7 +34,7 @@ void isr_handler(uint32_t int_num, uint32_t err_code) {
// Halt CPU
while (1) {
asm volatile ("hlt");
__asm__("hlt");
}
}

View File

@@ -6,6 +6,7 @@
#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);

View File

@@ -2,58 +2,91 @@
#include "io.h"
#include "isr.h"
#include "terminal.h"
#include <stddef.h>
#define KEYBOARD_DATA_PORT 0x60
#define KEY_BUFFER_SIZE 256
static char key_buffer[256];
static uint8_t buffer_index = 0;
// 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;
// Basic US QWERTY keymap (scancode to ASCII)
static const char scancode_map[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', // 0x00 - 0x09
'9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', // 0x0A - 0x13
't', 'y', 'z', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x14 - 0x1D
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', // 0x1E - 0x27
';', '\'', '`', 0, '\\', 'x', 'c', 'v', 'b', // 0x28 - 0x31
'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, // 0x32 - 0x3B
// rest can be filled as needed
// 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
};
// Interrupt handler for IRQ1
/**
* 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(0x60);
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
// Only handle key press (ignore key release)
if (!(scancode & 0x80)) {
char c = scancode_map[scancode];
if (c && buffer_index < sizeof(key_buffer) - 1) {
key_buffer[buffer_index++] = c;
terminal_putchar(c);
}
}
// Ignore break codes (key release)
if (scancode & 0x80) return;
// Send End of Interrupt (EOI) to the PIC
outb(0x20, 0x20);
char c = scancode_map[scancode];
keyboard_buffer_add(c);
}
void keyboard_init() {
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
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 (returns one char)
char keyboard_get_char() {
while (buffer_index == 0); // Busy wait
/**
* Blocking read with a safe HLT to prevent CPU 100% usage
*/
char keyboard_get_char(void) {
char c;
__asm__ __volatile__("cli");
c = key_buffer[0];
for (uint8_t i = 1; i < buffer_index; i++) {
key_buffer[i - 1] = key_buffer[i];
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");
}
buffer_index--;
__asm__ __volatile__("sti");
return c;
}

View File

@@ -1,7 +1,12 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include <stdint.h>
void keyboard_init(void);
char keyboard_get_char(void); // Blocking read from buffer
void keyboard_buffer_add(char c);
char keyboard_get_char(void);
extern const char scancode_map[128];
#endif

View File

@@ -1,5 +1,4 @@
#include <stdint.h>
#include <stdbool.h>
#include "io.h"
#include "serial.h"
#include "terminal.h"
@@ -9,9 +8,11 @@
#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
@@ -41,6 +42,9 @@ void kmain(void) {
idt_init();
serial_write("IDT initialized.\n");
irq_install();
__asm__ __volatile__ ("sti");
terminal_write("Enabling paging...\n");
paging_init();
serial_write("Paging initialized.\n");
@@ -69,9 +73,9 @@ void kmain(void) {
char buf[32];
for (uint32_t i = 0; i < mmap_size; i++) {
terminal_write(" - Base: ");
print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF)); // Lower 32 bits
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)); // Lower 32 bits
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);
@@ -82,6 +86,6 @@ void kmain(void) {
// Halt CPU in loop
while (1) {
asm volatile("hlt");
__asm__("hlt");
}
}

View File

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

View File

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

17
kernel/memory.c Normal file
View File

@@ -0,0 +1,17 @@
#include "memory.h"
/* note: this is a stub, please use care as theres duplicate functions in utils implementation
/* --------------------------------------------------------------------- *
* Helper: copy a single byte (used by both memcpy and memmove)
* --------------------------------------------------------------------- */
static inline void byte_copy_forward(uint8_t *dst, const uint8_t *src, size_t n)
{
while (n--) *dst++ = *src++;
}
static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n)
{
dst += n; src += n;
while (n--) *--dst = *--src;
}

25
kernel/memory.h Normal file
View File

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

View File

@@ -5,12 +5,12 @@
#include <stdbool.h>
// Mouse buffer
static mouse_data_t mouse_data;
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(buffer, sizeof(buffer))) { // Ensure buffer is filled
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

View File

@@ -12,6 +12,8 @@ typedef struct {
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);

View File

View File

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

View File

@@ -10,31 +10,31 @@
// Page Directory and Page Table structure
typedef struct {
uint32_t present : 1; // Present bit (1: page is present in memory)
uint32_t rw : 1; // Read-Write bit (1: page is read-write)
uint32_t user : 1; // User-supervisor bit (1: user mode access)
uint32_t write_through : 1; // Write-through cache
uint32_t cache_disabled : 1; // Cache disabled
uint32_t accessed : 1; // Accessed bit
uint32_t reserved : 1; // Reserved bit
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
uint32_t global : 1; // Global page (can be used across different processes)
uint32_t available : 3; // Available bits for the system
uint32_t frame : 20; // Frame address (physical address)
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;
uint32_t rw : 1;
uint32_t user : 1;
uint32_t write_through : 1;
uint32_t cache_disabled : 1;
uint32_t accessed : 1;
uint32_t reserved : 1;
uint32_t zero : 5; // Must be zero for page directory
uint32_t reserved_2 : 7; // Reserved bits
uint32_t frame : 20; // Frame address of the page table
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;

View File

@@ -14,6 +14,6 @@ void panic(const char *message) {
// Halt the system
while (true) {
asm volatile ("cli; hlt");
__asm__("cli; hlt");
}
}

109
kernel/pci.c Normal file
View 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
View 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

View File

@@ -1,13 +1,14 @@
#include <stdio.h>
#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
putchar(ch);
terminal_putchar(ch);
}
void print_string(const char *str) {
@@ -33,7 +34,9 @@ void my_printf(const char *format, ...) {
case 'd': { // Integer
int num = va_arg(args, int);
char buffer[20]; // Buffer to hold the string representation
snprintf(buffer, sizeof(buffer), "%d", num);
//TODO: implement `snprintf()`
//snprintf(buffer, sizeof(buffer), "%d", num);
print_string(buffer);
break;
}
@@ -56,29 +59,44 @@ void my_printf(const char *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)
void print_hex(unsigned int num) {
// Buffer to hold the hexadecimal representation
char buffer[9]; // 8 hex digits + null terminator
buffer[8] = '\0'; // Null-terminate the string
// Null-terminate the buffer
buffer[pos--] = '\0';
// Convert value to hex digits
for (int i = 7; i >= 0; i--) {
int digit = num & 0xF; // Get the last 4 bits
buffer[i] = (digit < 10) ? (digit + '0') : (digit - 10 + 'A'); // Convert to hex character
num >>= 4; // Shift right by 4 bits
int digit = val & 0xF; // Get last 4 bits
buffer[pos--] = hex_chars[digit];
val >>= 4; // Shift right by 4 bits
}
// Print the buffer, skipping leading zeros
int leading_zero = 1;
for (int i = 0; i < 8; i++) {
if (buffer[i] != '0') {
leading_zero = 0; // Found a non-zero digit
}
if (!leading_zero) {
my_putchar(buffer[i]);
}
// Add "0x" prefix if requested
if (include_prefix) {
buffer[pos--] = 'x';
buffer[pos--] = '0';
}
if (leading_zero) {
my_putchar('0'); // If all were zeros, print a single '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);
}

View File

@@ -1,9 +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(unsigned int num);
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
View 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
View 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

View File

@@ -1,7 +1,12 @@
#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;
@@ -9,7 +14,6 @@ static task_t *task_list = NULL;
static task_t *current_task = NULL;
void scheduler_init() {
// Initialize task list, etc.
task_list = NULL;
current_task = NULL;
task_count = 0;
@@ -20,16 +24,42 @@ void scheduler_add_task(void (*entry)(void)) {
task_t *new_task = &tasks[task_count];
new_task->id = task_count;
new_task->entry = entry;
// Simulate a stack pointer pointing to the "top" of the stack
new_task->stack_ptr = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t) - 1];
// 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;
// Add to task list
// 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) {
@@ -42,21 +72,25 @@ void scheduler_add_task(void (*entry)(void)) {
}
void scheduler_schedule() {
// Very basic round-robin switch
if (current_task && current_task->next) {
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; // Loop back
current_task = task_list;
}
// Call context switch or simulate yielding to current_task
// In real system: context_switch_to(current_task)
if (current_task && current_task->entry) {
current_task->entry(); // Simulate switching by calling
// 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() {
// Stub: manually call schedule for cooperative multitasking
scheduler_schedule();
}

View File

@@ -4,18 +4,21 @@
#include <stdint.h>
#define MAX_TASKS 8
#define STACK_SIZE 1024
#define STACK_SIZE 1024 // in bytes
typedef struct task {
uint32_t id;
void (*entry)(void);
uint32_t *stack_ptr;
// 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(); // Optional for cooperative scheduling
void scheduler_yield();
#endif // SCHEDULER_H

View File

@@ -2,8 +2,6 @@
#include "keyboard.h"
#include "terminal.h"
#include "print.h"
#include <stdio.h>
#include <string.h>
#include "string_utils.h"
void execute(char *input) {
@@ -49,7 +47,7 @@ void shell_loop()
{
if (index < sizeof(input) - 1) {
input[index++] = c;
putchar(c);
terminal_putchar(c);
}
}
}

View File

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

View File

@@ -1,11 +1,11 @@
#include "threading.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#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
#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];
@@ -16,103 +16,106 @@ static uint32_t num_threads = 0; // Number of active threads
static volatile int mutex_locked = 0;
// Function declaration for context_switch
void context_switch(Thread *next);
void context_switch(Thread* next);
// Initialize the threading system
void thread_init(void) {
memset(thread_table, 0, sizeof(thread_table));
num_threads = 0;
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) {
printf("Error: Maximum thread count reached.\n");
return;
}
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);
// Find an empty slot for the new thread
int index = num_threads++;
thread_table[index] = (Thread){0};
// 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 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);
// Set the thread's state to ready
thread_table[index].state = THREAD_READY;
// 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
// If this is the first thread, switch to it
if (index == 0) {
scheduler();
}
// 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;
}
// 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();
}
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
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();
// 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;
}
// 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]);
}
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.
printf("Switching to thread...\n");
next->start_routine(next->arg); // Start running the next 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_init(void) { mutex_locked = 0; }
void mutex_lock(void) {
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
// Busy wait (spinlock)
}
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
// Busy wait (spinlock)
}
}
void mutex_unlock(void) {
__sync_lock_release(&mutex_locked);
}
void mutex_unlock(void) { __sync_lock_release(&mutex_locked); }

View File

@@ -3,6 +3,7 @@
#include "isr.h"
#include "terminal.h"
#include "stdio.h"
#include "utils.h"
static volatile uint32_t tick = 0;

View File

@@ -1,9 +0,0 @@
#include "types.h"
// Example: Basic memory helper (unnecessary if libc exists)
void *memset(void *dest, int value, size_t len) {
unsigned char *ptr = (unsigned char *)dest;
while (len-- > 0)
*ptr++ = (unsigned char)value;
return dest;
}

View File

@@ -1,62 +0,0 @@
#ifndef TYPES_H
#define TYPES_H
// ----------------------------
// Fixed-width integer types
// ----------------------------
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
typedef unsigned long long uint64_t;
typedef signed long long int64_t;
// ----------------------------
// Boolean & NULL definitions
// ----------------------------
#ifndef __cplusplus
typedef enum { false = 0, true = 1 } bool;
#endif
#ifndef NULL
#define NULL ((void*)0)
#endif
// ----------------------------
// OS subsystem types
// ----------------------------
typedef uint32_t size_t;
typedef int32_t ssize_t;
typedef uint32_t phys_addr_t; // Physical address
typedef uint32_t virt_addr_t; // Virtual address
typedef uint32_t pid_t; // Process ID
typedef uint32_t tid_t; // Thread ID
// ----------------------------
// Bitfield & utility macros
// ----------------------------
#define BIT(n) (1U << (n))
#define BITS(m, n) (((1U << ((n) - (m) + 1)) - 1) << (m))
// Align value to next multiple of alignment
#define ALIGN_UP(val, align) (((val) + ((align)-1)) & ~((align)-1))
#define ALIGN_DOWN(val, align) ((val) & ~((align)-1))
// ----------------------------
// Attributes for structures
// ----------------------------
#define PACKED __attribute__((packed))
#define ALIGN(x) __attribute__((aligned(x)))
// ----------------------------
// Likely/unlikely branch hints
// (for future optimization use)
// ----------------------------
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif // TYPES_H

View File

@@ -1,6 +1,4 @@
#include "utils.h"
#include "serial.h"
#include "terminal.h"
static void reverse(char* str, int len) {
int start = 0;
@@ -78,14 +76,3 @@ char* utoa(unsigned int value, char* str, int base) {
reverse(str, i);
return str;
}
void print_hex(uint32_t val) {
char hex_chars[] = "0123456789ABCDEF";
char buf[11] = "0x00000000";
for (int i = 9; i >= 2; i--) {
buf[i] = hex_chars[val & 0xF];
val >>= 4;
}
terminal_write(buf);
serial_write(buf);
}

View File

@@ -1,7 +1,7 @@
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
#include <stddef.h>
// Convert integer to string (base is typically 10, 16, etc.)
char* itoa(int value, char* str, int base);
@@ -9,6 +9,4 @@ 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);
void print_hex(uint32_t val);
#endif // UTILS_H

View File

@@ -1,9 +1,9 @@
#include "vga.h"
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "string_utils.h"
#include "vga.h"
void outb(uint16_t port, uint8_t value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
@@ -134,7 +134,7 @@ void vga_printf(const char* format, ...) {
va_end(args);
// Now you can use the buffer with vga_write_string
vga_write_string(buffer, my_strlen(buffer)); // Use my_strlen instead of strlen
vga_write_string(buffer, strlen(buffer)); // Use my_strlen instead of strlen
}
void vga_init(void) {

View File

@@ -35,6 +35,7 @@ typedef enum {
// Function prototypes
uint8_t vga_entry_color(vga_color fg, vga_color bg);
uint16_t vga_entry(unsigned char uc, uint8_t color);
void vga_init(void);
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y);
void vga_clear(uint8_t color);
@@ -50,4 +51,4 @@ void vga_set_cursor_blink_rate(uint8_t rate);
void vga_printf(const char* format, ...);
#endif
#endif

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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