Compare commits

...

46 Commits

Author SHA1 Message Date
3fd818395d adds some beginning documentation for setting up paging 2025-08-13 13:15:08 -04:00
8a18cf5633 ... 2025-06-15 16:03:47 -04:00
88921f024e cleans up PIC 2025-06-13 16:09:47 -04:00
ed2c0a3568 cleans up gdt code 2025-06-13 14:37:22 -04:00
2ec3e259d8 cleans up idt.h and idt.c 2025-06-13 13:12:05 -04:00
99a842df7e cleans up a bit, adds a pmm panic 2025-06-13 12:58:50 -04:00
af596cd43a updates TODO 2025-06-13 12:56:43 -04:00
779680987f adds a little bit of memory protection 2025-06-13 12:53:35 -04:00
45c167568c allocin'g actually works now, but we can free random pointers... weird 2025-06-13 12:39:19 -04:00
a009462c72 fixes serial log printing not having \r 2025-06-12 11:06:12 -04:00
7669ea32a4 implements a physical memory manager... sort of, it gives an out of mem error, and variable are setting incorrectly 2025-06-11 20:36:30 -04:00
98f8a0dc88 implements first free mem block 2025-06-11 17:41:35 -04:00
f216c32f22 idk we're trying out pmm 2025-06-11 17:33:42 -04:00
03aecd514f adds some references 2025-06-10 09:28:58 -04:00
920d0b01e1 paging is now enabled with __shaky__ code 2025-06-09 21:01:17 -04:00
83b6a9eaf2 gets the memory map from GRUB 2025-06-09 17:44:50 -04:00
f1515ad7b5 some work with PCI, updated my TODO 2025-06-09 17:02:46 -04:00
a776376403 adds some more io commnads; starts work on a PCI driver 2025-06-08 18:22:22 -04:00
ac3dc4d48a implements the PIT 2025-06-08 13:22:09 -04:00
c6cc318c69 adds a readme 2025-06-06 22:02:56 -04:00
f9c2ea2f2b Does a lot of work towards the PIT
Added a math library, with clamp and POW.
added printf support for printing doubles
added a few helper functions in PIT for calcaulting the time in seconds of a clock cycle based on divisor
2025-06-06 22:01:18 -04:00
378f7ef23d adds some more exception stuff 2025-06-06 12:58:54 -04:00
a0749400f5 adds a todo' 2025-06-06 12:49:41 -04:00
1e1a28e23d starts work on the PIT 2025-06-06 12:47:02 -04:00
3e01dc2074 massive overhaul to my exception system 2025-06-05 20:28:17 -04:00
79f04df82e my exception handler sucked so i redid it 2025-06-05 17:32:38 -04:00
87e5e06142 adds some debug flags for GDB support 2025-06-04 17:34:54 -04:00
e471564f89 cleaned up some log messages, polished up the kb driver a bit, etc 2025-06-03 20:51:25 -04:00
01076e24b8 adds some extensibility through putting the term into a variable 2025-06-03 19:11:15 -04:00
af92026a74 Okay wow i forgot to commit a bunch of stuff
I added some work on a keyboard driver here
also changed the emulation system, since bochs was giving me headaches when it came to keyboard input
added some todo stuff.. probably some cleanup, idk
2025-06-03 19:08:45 -04:00
ca77157344 adds a queue library, work for keyb driver 2025-06-02 10:35:57 -04:00
0fc2e6a199 half assed keyboard driver yippegit add .git add .git add .git add .git add .git add .git add .git add .git add .git add .git add .git add .! 2025-06-01 23:58:23 -04:00
3ffb3aa181 some cleanup from debugging and todos 2025-06-01 21:00:26 -04:00
4c938a0855 holy crap im an idiot, i was getting constatnt gpf because i forgot to pop the stack 2025-06-01 20:57:12 -04:00
ecc91fdc7d fixes issue with exceptions (im dumb) 2025-06-01 20:24:05 -04:00
89c02d6a93 adds some comments, fixes a few tiny bugs 2025-05-31 20:50:47 -04:00
c50d3f14a6 progress? 2025-05-31 20:28:00 -04:00
013b5a557e i need a break 2025-05-30 18:58:45 -04:00
f8c38d22b0 some finishing touches to printf 2025-05-30 15:20:02 -04:00
47056f7d9a adds a utility to dump the gdt, adds serial printing for printf in testing 2025-05-30 13:49:37 -04:00
1a60e91745 adds a touch more to printf for debugging purposes 2025-05-30 13:38:52 -04:00
ee2c5a72a1 adds some more data types for printf, and moves gdt_init to a different file 2025-05-30 13:32:52 -04:00
c115dcd4f7 implements integer printing into printf, and adds some testing logs 2025-05-30 13:19:22 -04:00
0256466f4c sets up the PIC 2025-05-30 12:14:52 -04:00
b87738ca47 removes the stupid testing line i left in here 2025-05-30 10:31:54 -04:00
72bab4a548 just a small reminder 2025-05-30 09:50:03 -04:00
45 changed files with 2945 additions and 160 deletions

View File

@ -2,7 +2,10 @@
# or maybe add it to a bash script instead? im not sure
CC = i686-elf-gcc
CFLAGS = -m32 -ffreestanding -Wall -Wextra -Werror -Wpedantic --sysroot=$(PWD)/sysroot -isystem=/usr/include -Iinclude -MD
DEBUG_FLAGS = -D__TESTING__ -g
CFLAGS = -m32 -ffreestanding -Wall -Wextra -Werror -Wpedantic --sysroot=$(PWD)/sysroot -isystem=/usr/include -Iinclude -MD $(DEBUG_FLAGS)
AS = nasm
ASFLAGS = -f elf
@ -12,6 +15,8 @@ LDFLAGS = -T $(shell find . -name "link.ld") -melf_i386 --sysroot=$(PWD)/sysroot
AR = i686-elf-ar
TERM_EXEC = kitty --hold sh -c
c_kern_objects := $(patsubst %.c,%.o,$(shell find kernel/ -name "*.c"))
asm_kern_objects := $(patsubst %.s,%.o,$(shell find kernel/ -name "*.s"))
@ -34,7 +39,7 @@ all: kernel.elf
env: install-headers install-libs # TODO: change this to a file specific target, so it doesnt recompile everytime, and so we can move as a prereq on kernel.elf
util/./gen-clangd.sh
kernel.elf: install-headers install-libs $(c_kern_objects) $(asm_kern_objects)
kernel.elf: install-headers install-libs env $(c_kern_objects) $(asm_kern_objects)
$(CC) $(CFLAGS) -T $(shell find . -name "link.ld") -o $@ $(c_kern_objects) $(asm_kern_objects) -nostdlib -lk -lgcc
os.iso: kernel.elf
@ -44,9 +49,15 @@ os.iso: kernel.elf
grub-mkrescue -o $@ iso
run: os.iso
bochs -f util/bochsrc.txt -q
$(TERM_EXEC) "gdb kernel.elf -x util/gdbcmds" &
qemu-system-i386 -s -cdrom os.iso
libc.a: CFLAGS:=$(CFLAGS) -D__is_libc # Target rule to define __is_libc
bochs: os.iso
bochs -f util/bochsrc.txt -q
#TODO: when i inevitably do add libc, this is going to fail because they have the same name as the libk objects
#TODO: so we'll need to do a batch rename on one of the two object files,
libc.a: CFLAGS:=$(CFLAGS) -D__is_libc # Target rule to define __is_libc
libc.a: $(libc_objs)
$(AR) rcs $@ $(libc_objs)

6
README.md Normal file
View File

@ -0,0 +1,6 @@
## What is this?
A research Operating System.. i guess
idk. im trying my hand at OS dev because I want to be better at low level programming
Its fun, and terrible, but mostly fun

11
REFERENCES.md Normal file
View File

@ -0,0 +1,11 @@
Websites:
OSDev Wiki - https://wiki.osdev.org/
bona fide os developer - http://osdever.net/
LittleOSBook - https://littleosbook.github.io/
http://www.brokenthorn.com/Resources/OSDevIndex.html
References:
NASM quick reference - https://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html
ELF Reference - https://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf
Anatomy of a program in memory - https://manybutfinite.com/post/anatomy-of-a-program-in-memory/
Linux Buddy System Paging - https://lwn.net/Articles/253361/

43
TODO.md Normal file
View File

@ -0,0 +1,43 @@
## What do I need to do?
- Unit testing
(https://wiki.osdev.org/Unit_Testing)
(https://wiki.osdev.org/Troubleshooting#Showing_the_stack_content)
lets implmeent some better testing functions aswell
- Paging & virtual memory
- Get GRUB to show memory map - DONE
- Write a page allocator - DONE
- Setup paging
- Setup virtual memory
- Setup higher half kernel
- Heap allocator (malloc)
- after this, implment some data structures? cleanup queue?
After the above is done, i want to clean up the code and restructure some stuff
clean up the headers so that we're not exposing kernel functions to the entire c library
and also only make required functions be exposed to the header
Drivers:
PIC - Done
PIT - Done
- Need sleep and timers implemented, ACPI can help with this
- Lets also maybe set up a hardware abstraction for timing interfaces?
ACPI (use ACPICA?)
PCI, PCIE (i think qemue and bochs dont have legacy PCI, so lets try for PCIE)
PS/2 controller
Proper KEYB driver
From here, determine whats needed next for pushing into userspace.
Ideally I would like a shell, and then create some essential userspace posix tools (gnutils?)
POSIX compliant?
### Features I want
- A kernel bus
- some kind of driver where I can send messages that can eventually be accessed globally by other drives / applications
- modularity in terms of drivers:
- I want drivers to be optional and to not limit functionality
- perhaps this means providing every thing as a hardware abstraction layer, so that when its absent we can have some sort of virtual replacement

View File

@ -1,17 +1,26 @@
; Here is our entry point to the kernel
; A very simple assembly file
; We set up some parameters and then pass the execution to our kmain
global loader ; entry symbol for ELF
extern kmain
extern _main
MAGIC_NUMBER equ 0x1BADB002 ; magic number constant
FLAGS equ 0x3
CHECKSUM equ -(MAGIC_NUMBER + FLAGS) ; calculate the checksum
KERNEL_STACK_SIZE equ 4096
; The multiboot section is a semi requirement for grub
; All this is doing is placing the required magic number, flags, and the checksum into the file first
; This lets grub know if this is a valid multiboot kernel or not
section .multiboot
align 4 ; code must be 4 byte aligned
dd MAGIC_NUMBER
dd FLAGS
dd CHECKSUM
; Here we're setting up the stack
; Giving us 4096 bytes (resb)
section .bss
align 4
kernel_stack:
@ -19,12 +28,15 @@ kernel_stack:
section .text
loader:
mov esp, kernel_stack + KERNEL_STACK_SIZE
mov esp, kernel_stack + KERNEL_STACK_SIZE ; move the top of the stack into esp
;mov eax, 0xCAFEBABE
push eax
push ebx
call kmain
call _main ; pass execution over to our _main function, where all of the real stuff is done
cli
; Should the system exit, we clear the interrupt flag
; and do an infinite loop of nothing
cli
loop: hlt
jmp loop

View File

@ -1,8 +1,8 @@
gdtr DW 0 ; limit store
DD 0 ; base storage
global setGdt
setGdt:
global set_gdt
set_gdt:
mov ax, [esp + 4]
mov [gdtr], ax
mov eax, [esp + 8]
@ -10,8 +10,8 @@ setGdt:
lgdt [gdtr]
ret
global reloadSegments
reloadSegments:
global reload_segments
reload_segments:
jmp 0x08:.reload_CS ; 0x08 is a stand in for the code segment
.reload_CS:
mov ax, 0x10 ; stand in for the data segment

View File

@ -1,4 +1,82 @@
#include <kernel/x86/gdt.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __TESTING__
#include <kernel/_kernel.h>
#endif
/**
* What is this file?
*
* Well, to properly set up a lot of the system, we need something called a GDT
* or, a Global Descriptor Table.
*
* This table, establishes a few things.
*
* Mainly it sets 4 segments,
* A kernel code segment, with RING 0 permissions
* A kernel data segment, with RING 0 permissions
* A user code segment, with RING 3 permissions
* A user data segment, with RING 3 permissions
*
* This allows for future userspace to properly segment code and data,
* anything in userspace shouldn't have access to hardware like the kernel does
* So by passing through this GDT, we can dish out authority to access certain data, functions,
* etc, by going through the CPU permission system (RING 0 - 3)
*/
#define GDT_SIZE 5
// Each define here is for a specific flag in the descriptor.
// Refer to the intel documentation for a description of what each one does.
#define SEG_DESCTYPE(x) ((x) << 0x04) // Descriptor type (0 for system, 1 for code/data)
#define SEG_PRES(x) ((x) << 0x07) // Present
#define SEG_SAVL(x) ((x) << 0x0C) // Available for system use
#define SEG_LONG(x) ((x) << 0x0D) // Long mode
#define SEG_SIZE(x) ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32)
#define SEG_GRAN(x) ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB)
#define SEG_PRIV(x) (((x) & 0x03) << 0x05) // Set privilege level (0 - 3)
#define SEG_DATA_RD 0x00 // Read-Only
#define SEG_DATA_RDA 0x01 // Read-Only, accessed
#define SEG_DATA_RDWR 0x02 // Read/Write
#define SEG_DATA_RDWRA 0x03 // Read/Write, accessed
#define SEG_DATA_RDEXPD 0x04 // Read-Only, expand-down
#define SEG_DATA_RDEXPDA 0x05 // Read-Only, expand-down, accessed
#define SEG_DATA_RDWREXPD 0x06 // Read/Write, expand-down
#define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed
#define SEG_CODE_EX 0x08 // Execute-Only
#define SEG_CODE_EXA 0x09 // Execute-Only, accessed
#define SEG_CODE_EXRD 0x0A // Execute/Read
#define SEG_CODE_EXRDA 0x0B // Execute/Read, accessed
#define SEG_CODE_EXC 0x0C // Execute-Only, conforming
#define SEG_CODE_EXCA 0x0D // Execute-Only, conforming, accessed
#define SEG_CODE_EXRDC 0x0E // Execute/Read, conforming
#define SEG_CODE_EXRDCA 0x0F // Execute/Read, conforming, accessed
#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_CODE_EXRD
#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_DATA_RDWR
#define GDT_CODE_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(3) | SEG_CODE_EXRD
#define GDT_DATA_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(3) | SEG_DATA_RDWR
uint64_t gdt[GDT_SIZE];
void set_gdt(unsigned short limit, uint64_t* base);
void reload_segments(void);
uint64_t create_descriptor(uint32_t base, uint32_t limit, uint16_t flag)
{
@ -17,3 +95,62 @@ uint64_t create_descriptor(uint32_t base, uint32_t limit, uint16_t flag)
return descriptor;
}
#ifdef __TESTING__
void dump_gdt(void)
{
for (int i = 0; i < GDT_SIZE; i++) {
printf("GDT_ENTRY %d: %4 | %2\n", i, gdt[i], gdt[i]);
}
}
#endif
void gdt_init(void)
{
#ifdef __TESTING__
kinfo("Initializing the GDT");
#endif
gdt[0] = create_descriptor(0, 0, 0); // null
gdt[1] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL0)); // kernel code
gdt[2] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL0)); // kernel data
gdt[3] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL3)); // user code
gdt[4] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL3)); // user data
set_gdt((sizeof(uint64_t) * GDT_SIZE) - 1, &(gdt[0])); // limit, base
reload_segments();
#ifdef __TESTING__
kinfo("Initialized the GDT");
#endif
#ifdef __TESTING__
dump_gdt();
#endif
}
#undef SEG_DESCTYPE
#undef SEG_PRES
#undef SEG_SAVL
#undef SEG_LONG
#undef SEG_SIZE
#undef SEG_GRAN
#undef SEG_PRIV
#undef SEG_DATA_RD
#undef SEG_DATA_RDA
#undef SEG_DATA_RDWR
#undef SEG_DATA_RDWRA
#undef SEG_DATA_RDEXPD
#undef SEG_DATA_RDEXPDA
#undef SEG_DATA_RDWREXPD
#undef SEG_DATA_RDWREXPDA
#undef SEG_CODE_EX
#undef SEG_CODE_EXA
#undef SEG_CODE_EXRD
#undef SEG_CODE_EXRDA
#undef SEG_CODE_EXC
#undef SEG_CODE_EXCA
#undef SEG_CODE_EXRDC
#undef SEG_CODE_EXRDCA
#undef GDT_CODE_PL0
#undef GDT_DATA_PL0
#undef GDT_CODE_PL3
#undef GDT_DATA_PL3
#undef GDT_SIZE

View File

@ -1,18 +1,313 @@
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <kernel/_kernel.h>
#include <kernel/x86/idt.h>
#include <kernel/x86/pic.h>
#include <kernel/x86/pit.h>
#include <kernel/x86/io.h>
#include <kernel/x86/keyb.h>
#define IDT_MAX_DESCRIPTORS 48 // number of entries in the idt table
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR
uint8_t reserved; // set to zero
uint8_t attributes; // Type and attributes
uint16_t isr_high; // The higher 16 bits of the ISR's address
} __attribute__((packed)) idt_entry_t;
__attribute__((aligned(0x10)))
static idt_entry_t idt[256];
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
static idtr_t idtr;
static bool vectors[IDT_MAX_DESCRIPTORS];
void exception_handler()
static bool vectors[IDT_MAX_DESCRIPTORS];
extern struct pit_state pit;
#define EXTERNAL_BIT (0x1)
#define TBL_GDT (0x0)
#define TBL_LDT (0x2)
#define TBL_IDT (0x1)
#define TBL_IDT_TWO (0x3)
static void examine_selector(uint32_t selector)
{
__asm__ volatile ("cli; hlt"); // hangs the computer
printf("The interrupt arised %s.\n", (selector & EXTERNAL_BIT) == EXTERNAL_BIT ? "externally" : "internally");
printf("The descriptor 0x%x, is located in the ", selector, (selector & 0x0000FFFF) >> 3);
if ((((selector >> 1) & TBL_IDT) == TBL_IDT)
|| ((selector >> 1) & TBL_IDT_TWO) == TBL_IDT_TWO) {
puts("IDT.");
} else if (((selector >> 1) & TBL_LDT) == TBL_LDT) {
puts("LDT.");
} else {
puts("GDT.");
}
return;
}
#undef TBL_GDT
#undef TBL_LDT
#undef TBL_IDT
#undef TBL_IDT_TWO
#undef EXTERNAL_BIT
#define PF_P (1 << 0)
#define PF_W (1 << 1)
#define PF_U (1 << 2)
#define PF_R (1 << 3)
#define PF_I (1 << 4)
#define PF_PK (1 << 5)
#define PF_SS (1 << 6)
#define PF_SGX (1 << 15)
static void examine_page_fault(uint32_t error_code)
{
puts("Page fault:");
if (error_code & PF_P) {
puts("Page-protection violation.");
} else {
puts("Not present page.");
}
if (error_code & PF_W) {
puts("Write access.");
} else {
puts("Read access.");
}
if (error_code & PF_U) {
puts("In CPL 3.");
}
if (error_code & PF_R) { // Only applies if PSE or PAE are set in CR4
puts("One or more page directory entires contain reserved bits which are set to 1.");
}
if (error_code & PF_I) { // Only applicable when the No-Execute bit is supported and enabled
puts("Instruction fetch.");
}
if (error_code & PF_PK) {
puts("Protection-key violation"); //TODO PRKU (user mode access) or PRKS MSR (supervisor mode access) specifies the key rights
}
if (error_code & PF_SS) {
puts("Shadow stack access.");
}
if (error_code & PF_SGX) { // Unrelated to ordinary paging
puts("SGX vioilation");
}
// TODO, also sets CR2 reg to the virtual address which caused the page fault
}
#undef PF_P
#undef PF_W
#undef PF_U
#undef PF_R
#undef PF_I
#undef PF_PK
#undef PF_SS
#undef PF_SGX
static void dump_cpu_state(struct cpu_state cpu, struct stack_state stack)
{
kinfo("DUMPING CPU STATE");
printf("eax: %2\nebx: %2\necx: %2\nedx: %2\nesi: %2\nedi: %22\nebp: %2\n",
cpu.eax, cpu.ebx, cpu.ecx, cpu.edx, cpu.esi, cpu.edi, cpu.ebp);
kinfo("DUMPING STACK STATE FOR INTERRUPT");
printf("eip: %2\ncs: %2\neflags: %2\n",
stack.eip, stack.cs, stack.eflags);
}
#define EXCEPTION_LOCATION() printf("Exception occurred at 0x%2\n", stack.eip)
#define EXCEPT_DIV_ERR 0
#define EXCEPT_DEBUG 1
#define EXCEPT_NMI 2
#define EXCEPT_BREAKPOINT 3
#define EXCEPT_OVERFLOW 4
#define EXCEPT_BOUND_RANGE_EXCEEDED 5
#define EXCEPT_INVALID_OPCODE 6
#define EXCEPT_DEVICE_NOT_AVAILABLE 7
#define EXCEPT_DOUBLE_FAULT 8
#define EXCEPT_INVALID_TSS 10
#define EXCEPT_SEG_NOT_PRESENT 11
#define EXCEPT_STACK_SEG_FAULT 12
#define EXCEPT_GENERAL_PROTECTION 13
#define EXCEPT_PAGE_FAULT 14
#define EXCEPT_FLOATING_POINT_ERR_FPU 16
#define EXCEPT_ALIGNMENT_CHECK 17
#define EXCEPT_MACHINE_CHECK 18
#define EXCEPT_FLOATING_POINT_ERR_SIMD 19
#define EXCEPT_VIRT 20
#define EXCEPT_CTRL_PROT 21
#define EXCEPT_HYPERVISOR_INJECTION 28
#define EXCEPT_VMM_COMMUNICATION 29
#define EXCEPT_SECURITY_EXCEPTION 30
void exception_handler(struct cpu_state __attribute__((unused)) cpu, uint32_t interrupt, struct stack_state stack)
{
uint32_t inbyte;
switch (interrupt) {
/** EXCEPTIONS BEGIN **/
case EXCEPT_DIV_ERR:
kerror("EXCEPTION: DIV BY ZERO");
EXCEPTION_LOCATION();
break;
case EXCEPT_DEBUG:
kinfo("EXCEPTION: DEBUG"); // TODO: this one has more specific requirements
break;
case EXCEPT_NMI:
kinfo("EXCEPTION: NON-MASKABLE INTERRUPT"); // TODO
break;
case EXCEPT_BREAKPOINT:
kinfo("EXCEPTION: BREAKPOINT");
// TODO
break;
case EXCEPT_OVERFLOW:
kerror("EXCEPTION: OVERFLOW");
break;
case EXCEPT_BOUND_RANGE_EXCEEDED:
kerror("EXCEPTION: BOUND RANGE EXCEEDED");
EXCEPTION_LOCATION();
break;
case EXCEPT_INVALID_OPCODE:
kerror("EXCEPTION: INVALID OPCODE");
EXCEPTION_LOCATION();
break;
case EXCEPT_DEVICE_NOT_AVAILABLE:
kerror("EXCEPTION: DEVICE NOT AVAILABLE");
printf("Instructions expecting an FPU were used, but no FPU was found. Is it disabled in CR0? Do you have an FPU?\n");
EXCEPTION_LOCATION();
break;
case EXCEPT_DOUBLE_FAULT:
kerror("EXCEPTION: DOUBLE FAULT");
__asm__ volatile ("cli; hlt"); // boned break;
break; // don't need t his but -Werror lol
case EXCEPT_INVALID_TSS:
kerror("EXCEPTION: INVALID TSS");
examine_selector(stack.error_code);
// TODO: when i implement the TSS, the EIP will be one of two things
// if this occurs before loading segmenet selectors from the TSS, EIP will be the instruction that caused the instruction
// otherwise its the first instruction in the new task
break;
case EXCEPT_SEG_NOT_PRESENT:
kerror("EXCEPTION: SEGMENT NOT PRESENT");
EXCEPTION_LOCATION();
examine_selector(stack.error_code);
break;
case EXCEPT_STACK_SEG_FAULT:
kerror("EXCEPTION: STACK-SEGMENT FAULT");
if (stack.error_code == 0) {
EXCEPTION_LOCATION();
} else {
printf("Tried loading non-present stack segment during hardware task switch.\n");
examine_selector(stack.error_code);
}
break;
case EXCEPT_GENERAL_PROTECTION:
kerror("EXCEPTION: GENERAL PROTECTION FAULT");
EXCEPTION_LOCATION();
if (stack.error_code != 0)
examine_selector(stack.error_code);
break;
case EXCEPT_PAGE_FAULT:
kerror("EXCEPTION: PAGE FAULT");
EXCEPTION_LOCATION();
examine_page_fault(stack.error_code);
break;
case EXCEPT_FLOATING_POINT_ERR_FPU:
kerror("EXCEPTION: FLOATING POINT ERROR FPU");
// TODO: this requires a custom handler, it needs another register
break;
case EXCEPT_ALIGNMENT_CHECK:
kerror("EXCEPTION: ALIGNMENT CHECK");
// TODO: only in CPL3
break;
case EXCEPT_MACHINE_CHECK:
kerror("EXCEPTION: MACHINE CHECK");
// TODO disabled by default
break;
case EXCEPT_FLOATING_POINT_ERR_SIMD:
kerror("EXCEPTION: FLOATING POINT ERROR SIMD");
// TODO disabled by default
break;
case EXCEPT_VIRT:
kerror("EXCEPTION: VIRTUALIZATION");
break;
case EXCEPT_CTRL_PROT:
kerror("EXCEPTION: CONTROL PROTECTION");
printf("Error code: 0x%2\n", stack.error_code);
break;
case EXCEPT_HYPERVISOR_INJECTION:
kerror("EXCEPTION: HYPERVISOR INJECTION");
break;
case EXCEPT_VMM_COMMUNICATION:
kerror("EXCEPTION: VMM COMMUNICATION");
printf("Error code: 0x%2\n", stack.error_code);
break;
case EXCEPT_SECURITY_EXCEPTION:
kerror("EXCEPTION: SECURITY");
printf("Error code: 0x%2\n", stack.error_code);
break;
/** EXCEPTIONS END **/
/** PIC BEGIN **/
case PIC_PIT:
pit.interrupts++;
PIC_sendEOI(0);
break;
case PIC_KEYB:
inbyte = inb(0x60);
do_keypress(decode_scancode(inbyte));
PIC_sendEOI(1);
break;
/** PIC END **/
default:
kerror("EXCEPTION: UNHANDLED EXCEPTION OR INTERRUPT");
printf("Error code: 0x%2\n", stack.error_code);
dump_cpu_state(cpu, stack);
// TODO i would love to track spurious interrupts as well, would be useful to dump their state and specify that its spurious (if possible)
break;
}
}
#undef EXCEPT_DIV_ERR
#undef EXCEPT_DEBUG
#undef EXCEPT_NMI
#undef EXCEPT_BREAKPOINT
#undef EXCEPT_OVERFLOW
#undef EXCEPT_BOUND_RANGE_EXCEEDED
#undef EXCEPT_INVALID_OPCODE
#undef EXCEPT_DEVICE_NOT_AVAILABLE
#undef EXCEPT_DOUBLE_FAULT
#undef EXCEPT_INVALID_TSS
#undef EXCEPT_SEG_NOT_PRESENT
#undef EXCEPT_STACK_SEG_FAULT
#undef EXCEPT_GENERAL_PROTECTION
#undef EXCEPT_PAGE_FAULT
#undef EXCEPT_FLOATING_POINT_ERR_FPU
#undef EXCEPT_ALIGNMENT_CHECK
#undef EXCEPT_MACHINE_CHECK
#undef EXCEPT_FLOATING_POINT_ERR_SIMD
#undef EXCEPT_VIRT
#undef EXCEPT_CTRL_PROT
#undef EXCEPT_HYPERVISOR_INJECTION
#undef EXCEPT_VMM_COMMUNICATION
#undef EXCEPT_SECURITY_EXCEPTION
#undef EXCEPTION_LOCATION
void idt_set_descriptor(uint8_t vector, void *isr, uint8_t flags)
{
@ -25,19 +320,32 @@ void idt_set_descriptor(uint8_t vector, void *isr, uint8_t flags)
descriptor->reserved = 0;
}
extern void* isr_stub_table[];
void idt_init(void)
{
#ifdef __TESTING__
kinfo("Initializing the IDT");
#endif
idtr.base = (uintptr_t)&idt[0];
idtr.limit = (uint16_t) sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
for (uint8_t vector = 0; vector < 32; vector++) {
for (uint8_t vector = 0; vector < IDT_MAX_DESCRIPTORS; vector++) {
idt_set_descriptor(vector, isr_stub_table[vector], 0x8E);
vectors[vector] = true;
}
// The "m" indicates actual data, not a pointer
__asm__ volatile("lidt %0" : : "m"(idtr)); // load the new IDT
PIC_remap(0x20, 0x28);
pic_disable(); // mask everything
IRQ_clear_mask(0);
IRQ_clear_mask(1);
__asm__ volatile("sti"); // set the interrupt flag
#ifdef __TESTING__
kinfo("Initialized the IDT");
#endif
}
#undef IDT_MAX_DESCRIPTORS

View File

@ -1,17 +1,47 @@
extern exception_handler
extern exception_handler_err
%macro isr_err_stub 1
isr_stub_%+%1:
call exception_handler
iret
push dword %1 ; push the interrupt number
jmp common_interrupt_handler
%endmacro
%macro isr_no_err_stub 1
isr_stub_%+%1:
call exception_handler
iret
push dword 0
push dword %1
jmp common_interrupt_handler
%endmacro
common_interrupt_handler:
; lets save the registers
push eax
push ebx
push ecx
push edx
push esi
push edi
push ebp
call exception_handler
; restore the registers
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
; lets move the stack before the error code and interrupt number
; since we don't want these to be popped anywhere
add esp, 8
iret
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
@ -44,11 +74,28 @@ isr_no_err_stub 28
isr_no_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
isr_no_err_stub 32
isr_no_err_stub 33
isr_no_err_stub 34
isr_no_err_stub 35
isr_no_err_stub 36
isr_no_err_stub 37
isr_no_err_stub 38
isr_no_err_stub 39
isr_no_err_stub 40
isr_no_err_stub 41
isr_no_err_stub 42
isr_no_err_stub 43
isr_no_err_stub 44
isr_no_err_stub 45
isr_no_err_stub 46
isr_no_err_stub 47
global isr_stub_table
isr_stub_table:
%assign i 0
%rep 32
%rep 48
dd isr_stub_%+i
%assign i i+1
%endrep

View File

@ -1,24 +0,0 @@
#ifndef ARCH_IO_H
#define ARCH_IO_H
/**
* outb:
* Sends the given data to the I/O port. Defined in io.s
*
* @param port The I/O port to send the data to
* @param data TThe data to send to the I/O port
*/
void outb(unsigned short port, unsigned char data);
/**
* inb:
* Read a byte from an I/O port
*
* @param port The address of the I/O port
* @return The read byte
*/
unsigned char inb(unsigned short port);
#endif

View File

@ -9,8 +9,25 @@ outb:
out dx, al ; send the data to the I/O port
ret ; return to the calling function
global outb_16 ; TODO these are probably wrong, eh, the stack might need more bcs the variables are bigger? im not sure
outb_16:
mov ax, [esp + 8]
mov dx, [esp + 4]
out dx, ax
ret
global outb_32
outb_32:
mov eax, [esp + 8]
mov dx, [esp + 4]
out dx, eax
ret
global inb
; inb - returns a byte from the given I/O port
; inb - returns a byte from the given I/O porot
; stack: [esp + 4] The address of the I/O port
; [esp ] The return address
inb:
@ -18,3 +35,24 @@ inb:
in al, dx ; read a byte from the I/O port and store it in the al register
ret ; return the read byte
global inb_16
inb_16:
mov dx, [esp + 4]
in ax, dx
ret
global inb_32
inb_32:
mov dx, [esp + 4]
in eax, dx
ret
global io_wait
io_wait:
mov al, 0x0
out 0x80, al
ret

201
kernel/arch/io/keyb.c Normal file
View File

@ -0,0 +1,201 @@
#include <stdbool.h>
#include <stdint.h>
#include <queue.h>
#ifdef __TESTING__
#include <kernel/_kernel.h>
#include <stdio.h>
#endif
#include <kernel/x86/keyb.h>
#include <kernel/x86/io.h>
bool is_shift_down = false;
queue kb_cmd_queue;
// current plan is to create a a multi d array
//
//
// 16 entries each for each scan code... so
// 0x01..0x0F (15)/ this is the top row, so esc - 1..0, etc
// 0x10..0x1F (16) ...
// 0x20..0x2F ...
// these arrays will keep a 0/1 flag to see if these keys are pressed
// as a stort of state machine
// this will also let us tell multiple keys pressed
//
// another more simple version, which i could start with
//
// is do a Single character as currently pressed
// and a bitflag we can mask for leftshift, leftctrl, right shift, right ctrl, alt, etc being pressed down
//
//
//
struct keyboard_state keyb_state;
void init_kb(void)
{
#ifdef __TESTING__
puts("Initializing keyboard driver!");
#endif
keyb_state.ready = false;
keyb_state.waiting_for_commands = true;
init_queue(&kb_cmd_queue);
keyb_state.leds = 0;
keyb_state.flags = 0;
queue_kb_command(KEYB_CMD_ENABLE_SCANNING);
queue_kb_command(KEYB_CMD_SET_TYPEMATIC);
queue_kb_command(0x1F);
queue_kb_command(KEYB_CMD_DO_SCAN_CODE);
/**
* TODO: look into this further,
* this is setting scan code 2, but the scan code this results in (on my hardware) is scan code set 1 according to https://wiki.osdev.org/PS/2_Keyboard
*/
queue_kb_command(0x2); // lets use scan code set 1
send_kb_commands();
keyb_state.ready = true;
keyb_state.waiting_for_commands = false;
#ifdef __TESTING__
puts("Initialized keyboard driver!");
#endif
}
void queue_kb_command(uint8_t command)
{enqueue(&kb_cmd_queue, command);}
void send_kb_commands(void)
{
int i;
uint8_t resp, cmd;
while (!queue_is_empty(&kb_cmd_queue)) {
cmd = queue_peek(&kb_cmd_queue);
for (i = 0; i < KEYB_CMD_RETRIES; i++) {
outb(KEYB_PORT, cmd);
io_wait();
resp = inb(KEYB_PORT);
io_wait();
switch(resp) {
case KEYB_RESP_ACK:
break;
case KEYB_RESP_RESEND:
continue;
default:
break;
}
}
dequeue(&kb_cmd_queue);
}
}
kb_key_code_t decode_scancode(uint8_t scancode)
{
switch (scancode) {
case KEYB_RESP_ERR:
case KEYB_RESP_SELF_TEST_PASSED:
case KEYB_RESP_ECHO:
case KEYB_RESP_ACK:
case KEYB_RESP_SELF_TEST_FAILED:
case KEYB_RESP_SELF_TEST_FAILED_TWO:
case KEYB_RESP_RESEND:
case KEYB_RESP_ERR_TWO:
keyb_state.waiting_for_commands = false;
return KEY_NONE;
default:
keyb_state.waiting_for_commands = false;
return (kb_key_code_t) scancode;
}
}
key_press_t do_keypress(kb_key_code_t keycode)
{
key_press_t packet;
if (!keyb_state.ready || keycode == KEY_NONE) {
packet.key_code = KEY_NONE;
return packet;
}
if (keycode >= KEY_ESC_R) {
packet.key_code = keycode - KEY_PRESS_RELEASE_DIFF;
packet.release = true;
} else {
packet.key_code = keycode;
packet.release = false;
}
switch (keycode) {
case KEY_LEFT_SHIFT:
keyb_state.flags |= (STATUS_FLAG_LSHIFT);
break;
case KEY_LEFT_SHIFT_R:
keyb_state.flags &= ~(STATUS_FLAG_LSHIFT);
break;
case KEY_RIGHT_SHIFT:
keyb_state.flags |= (STATUS_FLAG_RSHIFT);
break;
case KEY_RIGHT_SHIFT_R:
keyb_state.flags &= ~(STATUS_FLAG_RSHIFT);
break;
case KEY_LEFT_CTRL:
keyb_state.flags |= (STATUS_FLAG_CTRL);
break;
case KEY_LEFT_CTRL_R:
keyb_state.flags &= ~(STATUS_FLAG_CTRL);
break;
case KEY_LEFT_ALT:
keyb_state.flags |= (STATUS_FLAG_ALT);
break;
case KEY_LEFT_ALT_R:
keyb_state.flags &= ~(STATUS_FLAG_ALT);
break;
case KEY_CAPSLOCK:
keyb_state.flags ^= (STATUS_FLAG_CAPS_LOCK);
keyb_state.leds ^= (KEYB_LED_CAP_LCK);
queue_kb_command(KEYB_CMD_SET_LEDS);
queue_kb_command(keyb_state.leds);
break;
case KEY_NUMLOCK:
keyb_state.flags ^= (STATUS_FLAG_NUM_LOCK);
keyb_state.leds ^= (KEYB_LED_NUM_LCK);
queue_kb_command(KEYB_CMD_SET_LEDS);
queue_kb_command(keyb_state.leds);
break;
case KEY_SCROLLLOCK:
keyb_state.flags ^= (STATUS_FLAG_NUM_LOCK);
keyb_state.leds ^= (KEYB_LED_SCRL_LCK);
queue_kb_command(KEYB_CMD_SET_LEDS);
queue_kb_command(keyb_state.leds);
break;
// case KEY_INSERT TODO: this shit baby
default:
break;
}
send_kb_commands();
packet.flags = keyb_state.flags;
return packet;
}
/**
* shift
* ctrl
* alt
* caps
* num
* scroll
*/

75
kernel/arch/io/pit.c Normal file
View File

@ -0,0 +1,75 @@
#include <math.h>
#include <kernel/x86/pit.h>
#include <kernel/x86/io.h>
struct pit_state pit;
uint32_t normalize_divisor(uint32_t divisor)
{
if (divisor == 0)
return 65536;
return clamp_u32(divisor, 0, 65535);
}
double get_time_from_divisor(uint32_t divisor)
{
uint32_t _divisor = normalize_divisor(divisor);
double clock_rate = 3579545.0 / 3.0; // This is for a bit more precision in the hz rate
return (_divisor / (clock_rate)) * 1000;
}
/**
* Im worried about an overflow
*
*
* What i want to do is
*
* Every time we get the Timer intterupt, i want to increment a variable, just the amount of times its fired
* and then we can use the time rate above to get total time elapsed, which we can use for sleep and stuff
*
* Im worried about overflows, but i can add something that will automatically flip it over? or do uints already roll over? they already rollover!!! WOOOO!!!
* just need to be aware that only a maximum of 52ms * 32int max (64 int max) can be tracked before it rolls over!
*/
uint16_t read_pit_count(uint8_t channel) // Only in lobyte/hibyte mode
{
uint16_t count = 0;
__asm__ volatile ("cli");
outb(PIT_CMD_REG, (channel << 6)); // xx000000 where x is the channel
count = inb(channel);
count |= inb(channel) << 8;
return count; // TODO make sure to enable interrupts after this
}
void set_pit_count(uint8_t channel, uint16_t count) // Only in lobyte/hibyte mode
{
__asm__ volatile ("cli");
outb(channel, count & 0xFF); // low byte
outb(channel, (count & 0xFF00) >> 8); // high byte
// TODO: make sure to set interrupts
}
void init_pit(uint8_t init_command, uint8_t channel, uint32_t divisor)
{
uint32_t normalized_div = normalize_divisor(divisor);
pit.divisor = normalized_div;
pit.interrupts = 0;
outb(PIT_CMD_REG, init_command); // access mode must be lobyte/hibyte
outb(channel, normalized_div & 0xFF);
outb(channel, (normalized_div & 0xFF00) >> 8);
}

View File

@ -3,9 +3,7 @@
#include <string.h>
#include <kernel/serial.h>
#include "io.h"
#include <kernel/x86/io.h>
/* I/O ports */

View File

@ -4,9 +4,9 @@
#include <string.h>
#include <kernel/tty.h>
#include <kernel/x86/io.h>
#include "vga.h"
#include "io.h"
/* I/O ports */
#define VGA_COMMAND_PORT 0x3D4

View File

@ -1,28 +1,49 @@
ENTRY(loader)
/**
* This is the linker file.
*
* This file tells the linker (ld) how we want to arrange the code into the output file.
*/
ENTRY(loader) /* This is the first entry point, defined in boot.s */
SECTIONS
{
. = 1M;
/*
* This is telling the linker that anything after here should be loaded at 2M onwards
* The reason for 2M, is that we want to give room for the BIOS, (and UEFI), 2M is generally regarded as a safe spot
* to place the memory offset
* */
. = 2M;
.text BLOCK(4K) : ALIGN(4K)
/*
* BLOCK is an alias for ALIGN, we're telling the linker that we want each section to be aligned at 4K
*/
.text BLOCK(4K) : ALIGN(4K) /* The first section we want is the text section, this is where most code is */
{
*(.multiboot)
*(.text)
text_start = .;
*(.multiboot) /* Multiboot needs to be early in the file, required by grub */
*(.text) /* The text section */
}
/* R/O data */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Data */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
end_data = .;
}
/* BSS */
.bss BLOCK(4K) : ALIGN(4K)
{
sbss = .;
*(COMMON)
*(.bss)
ebss = .;
endkernel = .;
}
}

View File

@ -0,0 +1,86 @@
/**
* The page direcotry and the page table have very specific
* entry formats.
*
* They will be defined here, and constants will be properly allocated.
*/
/**
* Page directory
* containes 1024 4 byte entries, making them 4KiB each.
*
* In the page directory, each entry points to a page table.
*
*
* Page Directory Entry
*
* (this is following the page size of 0, or 4KiB)
* bits:
* 31 - 12 : Bits 31-12 of address
* 11 - 8 : AVL
* 7 : PS
* 6 : AVL
* 5 : A
* 4 : PCD
* 3 : PWT
* 2 : U/S
* 1 : R/W
* 0 : P
*
*
* The address field represnets the physical address of the page table that manages the four megabytes at that point.
* It is very important that this address by 4KiB aligned. As the remainder of bytes are overwritten by access bits and such.
*
* P: Present
* If the bit is set, the page is actually in physical memory at the moment. For example, when a page is swapped out, it is not in physical memory and therefor not 'Present'.
* If a page is called, but not present, a page fault will occur, and the OS should handle it.
* R/W: Read/Write
* If the big is set, the page is read/write. Otherwise, it is read-only. The WP bit in CR0 determines if this is only applied to userland, always giving the kernel write access (the default)
* or both userland and the kernel. (see Intel Manuals 3A 2-20)
* U/S: User/Supervisor
* Controls access to the page based on privelege level. If the bit is set, then the page may be accessed by all; if the bit is not set, however,
* only the supervisor can access it. For ap age directory entry, the user bit controls access to all the pages referenced by the page directory entry.
* Therefore if you wish to make a page a user page, you must set the bit in the releveant page directory, as well as the page table entry.
* PWT: Write-through
* Controls Write-through abilities of the page. If the bit is set, write-through caching is enabled.
* If not, then write-back is enabled instead.
* PCD: Cache Disable
* Cache disable bit, if the bit is set, the page will not be cached. Otherwise, it will be.
* A: Accessed
* Accessed is used to discover whether a PDE or PTE was read during virtual address translation. If it has, then the bit is set, otherwise it is not.
* Note that, this bit will not be cleared by the CPU, so that burden falls on the OS (if its needed at all).
* D: Dirty
* Dirty is used to determine wether a page has been written to.
* PS: Page Size (always 0, since we're using 4KiB pages)
* Stores the page size for that specific entry. If the bit is set, then the PDE maps a page that is 4MiB in size. Otherwise, it maps to a 4KiB page table.
* AVL: Available
* These bits are unused and are free for the OS to use for accounting information.
*/
/**
* Page Table
*
* In each page table, as it is, there are also 1024 entries. Each entry points to a 4KiB physical page frame.
*
* These are called page table entries, and are very similar to the entries described above.
*
* The first item is a 4KiB aligned physical address. However, these point to a 4KiB block of physical memory, which si then mapped to that location in the page table and driectory.
*
* 31 - 12 : Bits 31-12 of address
* 11 - 9 : AVL
* 8 : G
* 7 : PAT
* 6 : D
* 5 : A
* 4 : PCD
* 3 : PWT
* 2 : U/S
* 1 : R/W
* 0 : P
*
* G: Global
* Tells the processor not to invalidate the TLB entry corresponding to the page upon a MOV to CR3 instruction.
* Bit 7 (PGE) in CR4 must be set to enable global pages.
* PAT: Page Attribute Table
* If PAT is supported, then PAT along with PCD and PWT shall indicate the memory caching type. Otherwise, it is reserved and must be set to 0.
*/

42
kernel/arch/pic/pci.c Normal file
View File

@ -0,0 +1,42 @@
#include <kernel/x86/io.h>
#include <kernel/x86/pci.h>
#include <stdio.h>
/**
* None of this is working. I'm assuming that QEMU (and bochs) are using pcie purely memory mapped, without legacy PCI support
*
* so we'll need to setup ACPI first :)
*/
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset)
{
uint32_t address;
uint32_t lbus = (uint32_t) bus;
uint32_t lslot = (uint32_t) slot;
uint32_t lfunc = (uint32_t) func;
uint16_t tmp = 0;
address = (uint32_t) ((lbus << 16) | (lslot << 11) |
(lfunc << 8) | (offset & 0xFC) | ((uint32_t) 0x80000000));
printf("%b\n", address);
outb_32(PCI_CONFIG_ADDRESS, address);
tmp = (uint16_t)((inb(PCI_CONFIG_DATA) >> ((offset & 2) * 8)) & 0xFFFF);
return tmp;
}
uint16_t pci_check_vendor(uint8_t bus, uint8_t slot, uint8_t func)
{
uint16_t vendor, device;
if ((vendor = pci_config_read_word(bus, slot, func, 0)) != 0xFFFF) {
device = pci_config_read_word(bus, slot, func, 2);
// we'll do something here, store all the pcis in memory or something
device++;
}
return vendor;
}

171
kernel/arch/pic/pic.c Normal file
View File

@ -0,0 +1,171 @@
#ifdef __TESTING__
#include <kernel/_kernel.h>
#include <stdio.h>
#endif
#include <kernel/x86/io.h>
#include <kernel/x86/pic.h>
/** PIC I/O ports **/
#define PIC1 0x20 /** Master PIC **/
#define PIC2 0xA0 /** Slave PIC **/
/** PIC helper defines **/
#define PIC1_COMMAND (PIC1)
#define PIC1_DATA (PIC1 + 1)
#define PIC2_COMMAND (PIC2)
#define PIC2_DATA (PIC2 + 1)
/** PIC Commands **/
#define ICW1_ICW4 0x01 /** Indicates ICW4 will be present **/
#define ICW1_SINGLE 0x02 /** Single (cascade mode) **/
#define ICW1_INTERVAL4 0x04 /** Call address interval 4 (8) **/
#define ICW1_LEVEL 0x08 /** Level triggered (edge) mode **/
#define ICW1_INIT 0x10 /** Initialization **/
#define ICW4_8086 0x01 /** 8086/88 (MCS-80/85) mode **/
#define ICW4_AUTO 0x02 /** Auto (normal) EOI **/
#define ICW4_BUF_SLAVE 0x08 /** Buffered mode/slave **/
#define ICW4_BUF_MASTER 0x0C /** Buffered mode/master **/
#define ICW4_SFNM 0x10 /** Special fully nested (not) **/
#define PIC_EOI 0x20 /** End-of-interrupt command code **/
#define PIC_READ_IRR 0x0a /** OCW3 irq ready next CMD read **/
#define PIC_READ_ISR 0x0b /** OCW3 irq service next CMD read **/
void PIC_sendEOI(uint8_t irq)
{
if (irq >= 8) // if we're over the PIC1 limit
outb(PIC2_COMMAND, PIC_EOI);
outb(PIC1_COMMAND, PIC_EOI); // if the IRQ came from the slave, it must go to both PICs
}
void PIC_remap(int offset1, int offset2)
{
#ifdef __TESTING__
kinfo("Remapping the PIC...");
#endif
// The io_wait calls are necessary for older machines, to give the PIC time to react
//
// After the init, the PIC requires 3 init words
// ICW2 // its vector offset
// ICW3 // how its wired to the master/slave
// ICW4 // additional info about the environment
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the init sequence, in cascade mode
io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC1_DATA, offset1); // ICW2 - the offset for master
io_wait();
outb(PIC2_DATA, offset2); // same as above for slave
io_wait();
outb(PIC1_DATA, 4); // ICW3 - Tells master theres a slave at IRQ2
io_wait();
outb(PIC2_DATA, 2); // ICW3 - Tells slave the cascade identity
io_wait();
outb(PIC1_DATA, ICW4_8086); // ICW4 - Use 8086 mode (not 8080 mode)
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
// Unmask the PICs
outb(PIC1_DATA, 0);
outb(PIC2_DATA, 0);
#ifdef __TESTING__
kinfo("Remapped the PIC!");
#endif
}
void pic_disable(void)
{ // Mask the PIC interrupts to disable them
outb(PIC1_DATA, 0xFF);
outb(PIC2_DATA, 0xFF);
#ifdef __TESTING__
kinfo("Masked off the PIC");
#endif
}
void IRQ_set_mask(uint8_t IRQline) // Masked IRQlines are ignored by the PIC, masked IRQ2 will fully ignore the slave
{
uint16_t port;
uint8_t value;
if (IRQline < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
IRQline -= 8;
}
value = inb(port) | (1 << IRQline);
outb(port, value);
#ifdef __TESTING__
kinfo("Masked IRQ line");
printf("IRQ line: %d\n", IRQline);
#endif
}
void IRQ_clear_mask(uint8_t IRQline)
{
uint16_t port;
uint8_t value;
if (IRQline < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
IRQline -= 8;
}
value = inb(port) & ~(1 << IRQline);
outb(port, value);
#ifdef __TESTING__
kinfo("Cleared mask from IRQ line");
printf("IRQ line: %d\n", IRQline);
#endif
}
static uint16_t __pic_get_irq_reg(int ocw3)
{
/** OCW3 to PIC CMD to get the register values
* PIC2 is chained, and represents IRQs 8-1.
* PIC1 is IRQs 0-7, with 2 being the chain **/
outb(PIC1_COMMAND, ocw3);
outb(PIC2_COMMAND, ocw3);
return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND);
}
uint16_t pic_get_irr(void)
{
return __pic_get_irq_reg(PIC_READ_IRR);
}
uint16_t pic_get_isr(void)
{
return __pic_get_irq_reg(PIC_READ_ISR);
}
#undef PIC1
#undef PIC2
#undef PIC1_COMMAND
#undef PIC1_DATA
#undef PIC2_COMMAND
#undef PIC2_DATA
#undef ICW1_ICW4
#undef ICW1_SINGLE
#undef ICW1_INTERVAL4
#undef ICW1_LEVEL
#undef ICW1_INIT
#undef ICW4_8086
#undef ICW4_AUTO
#undef ICW4_BUF_SLAVE
#undef ICW4_BUF_MASTER
#undef ICW4_SFNM
#undef PIC_EOI
#undef PIC_READ_IRR
#undef PIC_READ_ISR

211
kernel/arch/pmm/pmm.c Normal file
View File

@ -0,0 +1,211 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <kernel/_kernel.h>
#include <kernel/pmm.h>
/**
* What i want to do is create a linked list of all the memory structures
*
* Theres one at the very start of the memory
*
* one at 1MB
*
* and then one provided by ram.
*
*
* So the idea is to create a way to access memory through this such that,
* when you give a bit block number, it'll go through the first item in the linked list, if the block is out of that range, it
* traverses to the next node, tries to find it there, and then continues until it either runs out of memory, or finds a location
*/
#define PMM_PAGE_SIZE 4096
#define PMM_BLOCKS_PER_BYTE 8
struct pmm_mem_info {
uint32_t startaddr;
uint32_t len; // in kb
uint32_t* bitmap;
uint32_t max_blocks;
uint32_t free_blocks;
uint32_t used_blocks;
};
#define PMM_GET_MEM_BLOCKS(x) x.len / PMM_PAGE_SIZE
struct pmm_mem_info main_mem;
void pmm_set(uint32_t bit);
void pmm_unset(uint32_t bit);
bool pmm_test(uint32_t bit);
int pmm_first_free(void);
void pmm_init(void);
void* pmm_alloc_block(void);
void pmm_free_block(void* p);
void pmm_panic(const char* str)
{
printf("PMM: ");
panic(str);
}
void __pmm_set(uint32_t bit, struct pmm_mem_info* mem_block)
{
(mem_block->bitmap)[bit / 32] |= (1 << (bit % 32));
mem_block->used_blocks++;
mem_block->free_blocks--;
}
void __pmm_unset(uint32_t bit, struct pmm_mem_info* mem_block)
{
(mem_block->bitmap)[bit / 32] &= ~(1 << (bit % 32));
mem_block->used_blocks--;
mem_block->free_blocks++;
}
bool __pmm_test(uint32_t bit, struct pmm_mem_info* mem_block)
{
return (mem_block->bitmap)[bit / 32] & (1 << (bit % 32));
}
int __pmm_first_free(struct pmm_mem_info* mem_block)
{
for (uint32_t i = 0; i < PMM_GET_MEM_BLOCKS((*mem_block)) / 32; i++) {
if (mem_block->bitmap[i] == 0xFFFFFFFF) // this segment is full
continue;
for (int j = 0; j < 32; j++) {
if (mem_block->bitmap[i] & (1 << j))
continue; // this page is used
return (i * 32) + j; // i * 32 is the chunk of 32, plus j to get to the page in the chunk
}
}
return -1;
}
void __pmm_add_mem_block(uint32_t addr, int32_t len, struct pmm_mem_info* mem_block)
{
mem_block->startaddr = addr;
mem_block->len = len;
mem_block->bitmap = 0;
}
void __pmm_init(struct pmm_mem_info* mem_block)
{
// TODO same as above
mem_block->used_blocks = 0;
mem_block->max_blocks = PMM_GET_MEM_BLOCKS(main_mem);
mem_block->free_blocks = mem_block->max_blocks;
memset(mem_block->bitmap, 0x0, PMM_GET_MEM_BLOCKS((*mem_block)) / PMM_BLOCKS_PER_BYTE); // declare all memory available
#ifdef __TESTING__
printf("Initialized %1 blocks of memory (%1KiB available)\n", mem_block->max_blocks, mem_block->free_blocks * 4096);
#endif
__pmm_set(0, mem_block); // first block must always be set
}
void* __pmm_alloc_block(struct pmm_mem_info* mem_block)
{
if (mem_block->free_blocks == 0) {
kerror("OUT OF MEMORY");
return 0;
}
int block_in_map = pmm_first_free();
if (block_in_map == -1) {
kerror("OUT OF MEMORY");
return 0;
}
__pmm_set(block_in_map, mem_block);
return (void*) (mem_block->startaddr + (block_in_map * PMM_PAGE_SIZE));
}
void __pmm_free_block(void* p, struct pmm_mem_info* mem_block)
{
uint64_t* addr = (uint64_t*) &p;
uint32_t idx = ((*addr) - mem_block->startaddr) / PMM_PAGE_SIZE;
// TODO this might still be a little flaky
// should we be able to free any pointer? or just ones that we've given out?
if (idx == 0)
pmm_panic("Trying to free reserved memory!");
if (pmm_test(idx) == 0)
pmm_panic("Trying to free a block that was already free!");
__pmm_unset(idx, mem_block);
}
void pmm_set(uint32_t bit)
{
/**
* Here we want to calculate if the bit is over the length
* subtract the length and bit amount so that we compensate for the bit map
*
* i.e. (length / 4096) == amount of blocks in that specific mem region
* if (bit > amt of blocks),
* go to next node, subtract amt of blocks from bit, and pass that
*
* below is merely a temporary solution
*/
__pmm_set(bit, &main_mem);
}
void pmm_unset(uint32_t bit)
{
// TODO: same as above
__pmm_unset(bit, &main_mem);
}
bool pmm_test(uint32_t bit)
{
// TODO: same as above
return __pmm_test(bit, &main_mem);
}
int pmm_first_free(void) //TODO implement a free_s where it finds a series of free pages
{
// TODO: same as above
int ret = __pmm_first_free(&main_mem);
if (ret == -1)
kerror("OUT OF MEMORY");
return ret;
}
void pmm_init(void)
{
// TODO same as above
__pmm_init(&main_mem);
}
void* pmm_alloc_block(void)
{
return __pmm_alloc_block(&main_mem);
}
void pmm_free_block(void* p)
{
__pmm_free_block(p, &main_mem);
}
void pmm_add_mem_block(uint32_t addr, uint32_t len)
{
// TODO: make this add to a linked list
__pmm_add_mem_block(addr, len, &main_mem);
}
#ifdef __TESTING__
void print_main_mem()
{
printf("Available blocks: %1\nUsed blocks: %1\nMax blocks: %1\n",
main_mem.free_blocks, main_mem.used_blocks, main_mem.max_blocks);
}
#endif

View File

@ -7,6 +7,5 @@ void kwarn(const char*);
void kinfo(const char*);
#endif

View File

@ -0,0 +1,6 @@
#include <stdint.h>
#ifndef ARCH_PAGING_H
#define ARCH_PAGING_H
#endif

View File

@ -0,0 +1,15 @@
#include <stdint.h>
#ifndef ARCH_PMM_H
#define ARCH_PMM_H
void pmm_init(void);
void* pmm_alloc_block(void);
void pmm_free_block(void* p);
void pmm_add_mem_block(uint32_t addr, uint32_t len);
#ifdef __TESTING__
void print_main_mem();
#endif
#endif

View File

@ -3,58 +3,9 @@
#ifndef ARCH_I386_GDT_H
#define ARCH_I386_GDT_H
// Each define here is for a specific flag in the descriptor.
// Refer to the intel documentation for a description of what each one does.
#define SEG_DESCTYPE(x) ((x) << 0x04) // Descriptor type (0 for system, 1 for code/data)
#define SEG_PRES(x) ((x) << 0x07) // Present
#define SEG_SAVL(x) ((x) << 0x0C) // Available for system use
#define SEG_LONG(x) ((x) << 0x0D) // Long mode
#define SEG_SIZE(x) ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32)
#define SEG_GRAN(x) ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB)
#define SEG_PRIV(x) (((x) & 0x03) << 0x05) // Set privilege level (0 - 3)
#define SEG_DATA_RD 0x00 // Read-Only
#define SEG_DATA_RDA 0x01 // Read-Only, accessed
#define SEG_DATA_RDWR 0x02 // Read/Write
#define SEG_DATA_RDWRA 0x03 // Read/Write, accessed
#define SEG_DATA_RDEXPD 0x04 // Read-Only, expand-down
#define SEG_DATA_RDEXPDA 0x05 // Read-Only, expand-down, accessed
#define SEG_DATA_RDWREXPD 0x06 // Read/Write, expand-down
#define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed
#define SEG_CODE_EX 0x08 // Execute-Only
#define SEG_CODE_EXA 0x09 // Execute-Only, accessed
#define SEG_CODE_EXRD 0x0A // Execute/Read
#define SEG_CODE_EXRDA 0x0B // Execute/Read, accessed
#define SEG_CODE_EXC 0x0C // Execute-Only, conforming
#define SEG_CODE_EXCA 0x0D // Execute-Only, conforming, accessed
#define SEG_CODE_EXRDC 0x0E // Execute/Read, conforming
#define SEG_CODE_EXRDCA 0x0F // Execute/Read, conforming, accessed
#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_CODE_EXRD
#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_DATA_RDWR
#define GDT_CODE_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(3) | SEG_CODE_EXRD
#define GDT_DATA_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(3) | SEG_DATA_RDWR
void setGdt(unsigned short limit, uint64_t* base);
void reloadSegments();
uint64_t create_descriptor(uint32_t base, uint32_t limit, uint16_t flag);
void gdt_init(void);
#endif

View File

@ -3,24 +3,25 @@
#ifndef ARCH_IDT_H
#define ARCH_IDT_H
#define IDT_MAX_DESCRIPTORS 32 // number of entries in the idt table, 32 i believe
struct cpu_state {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
} __attribute__((packed));
void exception_handler(void);
struct stack_state {
uint32_t error_code;
uint32_t eip;
uint32_t cs;
uint32_t eflags;
} __attribute__((packed));
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR
uint8_t reserved; // set to zero
uint8_t attributes; // Type and attributes
uint16_t isr_high; // The higher 16 bits of the ISR's address
} __attribute__((packed)) idt_entry_t;
void exception_handler(struct cpu_state cpu, uint32_t interrupt, struct stack_state stack);
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags);
void idt_init(void);
#endif

View File

@ -0,0 +1,39 @@
#include <stdint.h>
#ifndef ARCH_IO_H
#define ARCH_IO_H
/**
* outb:
* Sends the given data to the I/O port. Defined in io.s
*
* @param port The I/O port to send the data to
* @param data The data to send to the I/O port
*/
void outb(unsigned short port, uint8_t data);
void outb_16(unsigned short port, uint16_t data);
void outb_32(unsigned short port, uint32_t data);
/**
* inb:
* Read a byte from an I/O port
*
* @param port The address of the I/O port
* @return The read byte
*/
uint8_t inb(unsigned short port);
uint16_t inb_16(unsigned short port);
uint32_t inb_32(unsigned short port);
/**
* io_wait:
* Wait for a very small amount of time (1 to 4 microseconds, generally)
* A simple imprecise wait.
*
* This performs an operation (sends 0) to port 0x80
*/
void io_wait(void);
#endif

View File

@ -0,0 +1,145 @@
#include <stdint.h>
#include <stdbool.h>
#ifndef ARCH_KEYB_H
#define ARCH_KEYB_H
#define KEYB_PORT (0x60)
#define KEYB_CMD_RETRIES 3
typedef enum {
KEYB_CMD_SET_LEDS = 0xED,
KEYB_CMD_ECHO = 0xEE,
KEYB_CMD_DO_SCAN_CODE = 0xF0,
KEYB_CMD_IDENTIFY_KEYB = 0xF2,
KEYB_CMD_SET_TYPEMATIC = 0xF3,
KEYB_CMD_ENABLE_SCANNING = 0xF4,
KEYB_CMD_DISABLE_SCANNING = 0xF5,
KEYB_CMD_SET_DEFAULT_PARAMS = 0xF6,
KEYB_CMD_TYPEMATIC_AUTOREPEAT_3 = 0xF7,
KEYB_CMD_ALL_MAKE_RELEASE_3 = 0xF8,
KEYB_CMD_MAKE_ONLY_3 = 0xF9,
KEYB_CMD_TYPE_AUTOREPEAT_MAKE_RELEASE_3 = 0xFA,
KEYB_CMD_KEY_TYPEMATIC_AUTOREPEAT_3 = 0xFB,
KEYB_CMD_KEY_MAKE_RELEASE_3 = 0xFC,
KEYB_CMD_KEY_MAKE_3 = 0xFD,
KEYB_RESEND_LAST_BYTE = 0xFE,
KEYB_RESET_SELF_TEST = 0xFF
} keyb_cmd_t;
#define KEYB_LED_SCRL_LCK (1 << 0)
#define KEYB_LED_NUM_LCK (1 << 1)
#define KEYB_LED_CAP_LCK (1 << 2)
// TODO investigate
#define KEYB_GET_SCAN_SET (0x00)
#define KEYB_SET_SCAN_1 (0x01)
#define KEYB_SET_SCAN_2 (0x02)
#define KEYB_SET_SCAN_3 (0x03)
typedef enum {
KEYB_RESP_ERR = 0x00,
KEYB_RESP_SELF_TEST_PASSED = 0xAA,
KEYB_RESP_ECHO = 0xEE,
KEYB_RESP_ACK = 0xFA,
KEYB_RESP_SELF_TEST_FAILED = 0xFC,
KEYB_RESP_SELF_TEST_FAILED_TWO = 0xFD,
KEYB_RESP_RESEND = 0xFE,
KEYB_RESP_ERR_TWO = 0xFF
} keyb_resp_t;
#define STATUS_FLAG_RSHIFT (1 << 0)
#define STATUS_FLAG_LSHIFT (1 << 1)
#define STATUS_FLAG_CTRL (1 << 2)
#define STATUS_FLAG_ALT (1 << 3)
#define STATUS_FLAG_SCRL_LOCK (1 << 4)
#define STATUS_FLAG_NUM_LOCK (1 << 5)
#define STATUS_FLAG_CAPS_LOCK (1 << 6)
#define STATUS_FLAG_INSERT (1 << 7)
typedef enum {
KEY_NONE,
KEY_ESC = 0x01,
KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
KEY_MINUS, KEY_EQUALS,
KEY_BACKSPACE, KEY_TAB,
KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P,
KEY_LEFT_BRACKET, KEY_RIGHT_BRACKET, KEY_ENTER, KEY_LEFT_CTRL,
KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L,
KEY_SEMICOLON, KEY_SINGLE_QUOTE, KEY_BACK_TICK, KEY_LEFT_SHIFT, KEY_BACKSLASH,
KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M,
KEY_COMMA, KEY_PERIOD,
KEY_FORWARDSLASH,
KEY_RIGHT_SHIFT,
KEY_KP_ASTERISK,
KEY_LEFT_ALT,
KEY_SPACE, KEY_CAPSLOCK,
KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
KEY_NUMLOCK, KEY_SCROLLLOCK,
KEY_KP_7, KEY_KP_8, KEY_KP_9,
KEY_KP_MINUS,
KEY_KP_4, KEY_KP_5, KEY_KP_6,
KEY_KP_PLUS,
KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_0,
KEY_KP_PERIOD,
KEY_F11 = 0x57,
KEY_F12,
KEY_ESC_R = 0x81,
KEY_1_R, KEY_2_R, KEY_3_R, KEY_4_R, KEY_5_R, KEY_6_R, KEY_7_R, KEY_8_R, KEY_9_R, KEY_0_R,
KEY_MINUS_R, KEY_EQUALS_R,
KEY_BACKSPACE_R,
KEY_TAB_R,
KEY_Q_R, KEY_W_R, KEY_E_R, KEY_R_R, KEY_T_R, KEY_Y_R, KEY_U_R, KEY_I_R, KEY_O_R, KEY_P_R,
KEY_LEFT_BRACKET_R, KEY_RIGHT_BRACKET_R,
KEY_ENTER_R, KEY_LEFT_CTRL_R,
KEY_A_R, KEY_S_R, KEY_D_R, KEY_F_R, KEY_G_R, KEY_H_R, KEY_J_R, KEY_K_R, KEY_L_R,
KEY_SEMICOLON_R, KEY_SINGLE_QUOTE_R, KEY_BACK_TICK_R, KEY_LEFT_SHIFT_R, KEY_BACKSLASH_R,
KEY_Z_R, KEY_X_R, KEY_C_R, KEY_V_R, KEY_B_R, KEY_N_R, KEY_M_R,
KEY_COMMA_R, KEY_PERIOD_R,
KEY_FORWARDSLASH_R,
KEY_RIGHT_SHIFT_R,
KEY_KP_ASTERISK_R,
KEY_LEFT_ALT_R,
KEY_SPACE_R, KEY_CAPSLOCK_R,
KEY_F1_R, KEY_F2_R, KEY_F3_R, KEY_F4_R, KEY_F5_R, KEY_F6_R, KEY_F7_R, KEY_F8_R, KEY_F9_R, KEY_F10_R,
KEY_NUMLOCK_R, KEY_SCROLLLOCK_R,
KEY_KP_7_R, KEY_KP_8_R, KEY_KP_9_R,
KEY_KP_MINUS_R,
KEY_KP_4_R, KEY_KP_5_R, KEY_KP_6_R,
KEY_KP_PLUS_R,
KEY_KP_1_R, KEY_KP_2_R, KEY_KP_3_R, KEY_KP_0_R,
KEY_KP_PERIOD_R,
KEY_F11_R = 0xD7,
KEY_F12_R,
} kb_key_code_t;
#define KEY_PRESS_RELEASE_DIFF (KEY_ESC_R - KEY_ESC)
typedef struct {
kb_key_code_t key_code;
uint8_t flags;
bool release;
} key_press_t ;
struct keyboard_state {
bool ready;
bool waiting_for_commands;
uint8_t leds;
uint8_t flags;
};
void init_kb(void);
void send_kb_commands(void);
void queue_kb_command(uint8_t command);
kb_key_code_t decode_scancode(uint8_t scancode);
key_press_t do_keypress(kb_key_code_t keycode);
#endif

View File

@ -0,0 +1,16 @@
#include <stdint.h>
#ifndef ARCH_PCI_H
#define ARCH_PCI_H
#define PCI_CONFIG_ADDRESS (0xCF8)
#define PCI_CONFIG_DATA (0xCFC)
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
uint16_t pci_check_vendor(uint8_t bus, uint8_t slot, uint8_t func);
void check_all_busses(void);
#endif

View File

@ -0,0 +1,43 @@
#include <stdint.h>
#ifndef ARCH_PIC_H
#define ARCH_PIC_H
#define PIC_PIT 32
#define PIC_KEYB 33
#define PIC_CASCADE 34 // never raised
#define PIC_COM2 35
#define PIC_COM1 36
#define PIC_LPT2 37
#define PIC_FLOPPY_DISK 38
#define PIC_LPT1 39 // usually spurious interrupt
#define PIC_CMOS 40
#define PIC_FREE_ONE 41
#define PIC_FREE_TWO 42
#define PIC_FREE_THREE 43
#define PIC_PS2_MOUSE 44
#define PIC_FPU 45
#define PIC_ATA_ONE 46
#define PIC_ATA_TWO 47
void PIC_sendEOI(uint8_t irq);
/**
* PIC_remap:
*
* @param offset1 - Vector offset for master PIC
* @param offset2 - Vector offset for slave PIC
*/
void PIC_remap(int offset1, int offset2);
void pic_disable(void);
void IRQ_set_mask(uint8_t IRQline);
void IRQ_clear_mask(uint8_t IRQline);
/**
* TODO: implement handling for Spurious IRQs
* https://wiki.osdev.org/8259_PIC#Spurious_IRQs
*/
#endif

View File

@ -0,0 +1,261 @@
#include <stdint.h>
#ifndef ARCH_PIT_H
#define ARCH_PIT_H
#define PIT_CHANNEL_0 (0x40) // R/W
#define PIT_CHANNEL_1 (0x41) // R/W
#define PIT_CHANNEL_2 (0x42) // R/W
#define PIT_CMD_REG (0x43) // W
/**
* Selects which channel is being configured.
* Must always be valid on every write to the CMD register
*
* READ_BACK isn't always supported
*/
enum PIT_CHANNEL {
CHANNEL_0 = 0x0,
CHANNEL_1, // this channel isn't always here...
CHANNEL_2,
/**
* Isn't supported on 8253 chips, but should be supported on AT and later (except for PS/2).
*
* Layout goes:
* bits:
* 76 - Set for READ_BACK
* 5 - Latch count flag (0 = latch count, 1 = don't latch count)
* 4 - Latch status flag (0 = latch status, 1 = don't latch status) - If this is clear, the next read of the corresponding data port will be a status byte
* 3 - Read back timer channel 2 (1 = yes, 0 = no)
* 2 - Read back timer channel 1 (..)
* 1 - Read back timer channel 0 (..)
* 0 - Reserved - set to 0
*
* Read back status byte:
* bits:
* 7 - Output pin state
* 6 - Null count flags
* 54 - Access mode
* 321 - Operating mode
* 0 BCD/Binary mode
*/
READ_BACK // 8254 only
};
/**
* Tells the PIT which access mode to use for the selected channel. (Or counter latch cmd)
* These must be valid on every write to the CMD register.
*
* LOBYTE indicates that only the low 8 bytes of a (16-bit) value will be transmitted to the lower bit of the register
* HIBYTE is the same, but for the higher value
* HI AND LOW means that the values will come sequentially through the IO port, lowest followed by highest
*/
enum PIT_ACCESS_MODE {
/*
* To prevent the count from being updated, we can use this command to latch the PIT.
* This must be xx000000 ( where x is the channel ).
* The value for each channel remains latched, until its fully read (or a new CMD has been issued).
* Some older/cheap motherboards have an issue with this command being sent a lot, leading to innaccuracies
* this command shouldn't be sent a lot anyways.
*/
LATCH_COUNT_VAL_CMD = 0x0,
LO_BYTE_ONLY,
HI_BYTE_ONLY,
HI_AND_LO_BYTE
};
/**
* While each operating mode functions differently, they all have these in common:
*
* Everytime the cmd register is written to, all interal logic on the selected PIT channel is reset, the output will immediately revert to the mode's initial state
*
* A new reload value can be written to a PIT channel's data port at any time.
*
* The current counter value is always either decremented or reset to the reload value on the *falling* edge of the input signal.
*
* In modes where the current count is decremented when it is reloaded, the current count is not decremented on the same input clock pulse as the reload
* it starts decrementing on the next input clock pulse
*
*/
enum PIT_OPERATING_MODE {
/**
* **MODE 0**
* When the cmd reigster is written, the output signal goes low and the PIT waits for the reload register to be set by software, to begin the countdown.
* After the reload register has been set, the current count will be set to the reload value on the next falling edge of the signal.
* Subsequent falling edges of the input signal will decrement the current count, (if the gate input is high on the preceding rising edge of the input signal).
*
* When the current count decrements from one to zero, the output goes high and remains high until another cmd register is written, or the reload register is set again.
* The current count will wrap around to 0xFFFF (0x9999 in BCD) and continue to decrement until the cmd register or the reload register are set, however this will not affect the output pin state.
*
* The reload value can be changed at any time. In "lobyte/hibyte" access mode counting will stop when the first byte of the reload value is set.
* Once the full reload value is set (in any access mode), the next falling edge of the input signal will cause the new reload value to be copied into the current count,
* and the countdown will continue from the new value.
*
* This mode only generates interrupts on channel zero.
*
*/
MODE_0 = 0x0, // Interrupt on terminal count
/**
* **MODE 1**
* This mode is similar to mode 0, however counting doesn't start until a rising edge of the gate input is detected.
* For this reason it is not usable for PIT channels 0 or 1 (where the gate input can't be changed).
*
* When the CMD register is written, the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set, the PIT will wait for the next rising edge of the gate input.
* Once this occurs, the output signal will go low and the current count will be set to the reload value on the next falling edge of the input signal.
* Subsequent falling edges of the input signal will decrement the current count.
*
* When the current count decrements from one to zero, the output goes high and remains high until another cmd register is written or the reload register is set again.
* The current count will wrap around to 0xFFFF (0x9999 in BCD) and continue to decrement until the cmd register or the reload register are set, however this will not affect the output pin state.
*
* If the gate input signal goes low during this process it will have no effect.
* However, if the gate input goes high again it will cause the current count to be reloaded from the reload register on the next falling edge of the input signal,
* and restart the count again (the same as when counting first started).
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded (on the next rising edge of gate input)
* So if you want to do this, clear and then reset bit 0 of IO port 0x61, after modifying the reload value.
*/
MODE_1, // hardware re-triggerable one-shot
/**
* **MODE 2**
* This mode operates as a frequency divider.
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software. After the reload register has been set, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal. Subsequent falling edges of the input signal will decrement the current count (if the gate input is high on the preceding rising edge of the input signal).
*
* When the current count decrements from two to one, the output goes low, and on the next falling edge of the (1.193182 MHz) input signal it will go high again and the current count will be set to the reload value and counting will continue.
*
* If the gate input goes low, counting stops and the output goes high immediately. Once the gate input has returned high, the next falling edge on input signal will cause the current count to be set to the reload value and operation will continue.
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded (when it is decreased from two to one, or the gate input going low then high). When this occurs counting will continue using the new reload value.
*
* A reload value (or divisor) of one must not be used with this mode.
*
* This mode creates a high output signal that drops low for one input signal cycle (0.8381 uS), which is too fast to make a difference to the PC speaker (see mode 3). For this reason mode 2 is useless for producing sounds with PIT channel 2.
*
* Typically, OSes and BIOSes use mode 3 (see below) for PIT channel 0 to generate IRQ 0 timer ticks, but some use mode 2 instead, to gain frequency accuracy (frequency = 1193182 / reload_value Hz).
*/
MODE_2, // rate generator
/**
* **MODE 3**
*
* For mode 3, the PIT channel operates as a frequency divider like mode 2,
however the output signal is fed into an internal "flip flop" to produce a square wave (rather than a short pulse).
* The flip flop changes its output state each time its input state (or the output of the PIT channel's frequency divider) changes.
* This causes the actual output to change state half as often, so to compensate for this the current count is decremented twice on each falling edge of the input signal (instead of once),
* and the current count is set to the reload value twice as often.
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal.
* Subsequent falling edges of the input signal will decrement the current count twice (if the gate input is high on the preceding rising edge of the input signal).
*
* Note: under normal circumstances the output state will be low 50% of the time when the mode/command register is written.
* The output will then go high, which will generate an immediate (perhaps spurious) IRQ0. The other 50% of the time the output will already be high, and there will be no IRQ0 generated.
*
* For even reload values, when the current count decrements from two to zero the output of the flip-flop changes state;
* the current count will be reset to the reload value and counting will continue.
*
* For odd reload values, the current count is always set to one less than the reload value.
* If the output of the flip flop is low when the current count decrements from two to zero it will behave the same as the equivalent even reload value.
* However, if the output of the flip flop is high the reload will be delayed for one input signal cycle (0.8381 uS),
* which causes the "high" pulse to be slightly longer and the duty cycle will not be exactly 50%.
* Because the reload value is rounded down to the nearest even number anyway,
* it is recommended that only even reload values be used (which means you should mask the value before sending it to the port).
*
* Note: This even value limitation on the reload value in mode 3 reduces the number of possible output frequencies in half.
* If you want to be able to control the frequency of IRQ0 to a somewhat higher degree, then think about using mode 2 instead for channel 0.
*
* On channel 2, if the gate input goes low, counting stops and the output goes high immediately.
* Once the gate input has returned high, the next falling edge on input signal will cause the current count to be set to the reload value and operation will continue (with the output left high).
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded
* (when it is decreased from two to zero, or the gate input going low then high). When this occurs counting will continue using the new reload value.
*
* A reload value (or divisor) of one must not be used with this mode.
*/
MODE_3, // square wave generator
/**
* **MODE 4**
*
* Mode four operates as a retriggerable delay, and generates a pulse when the current count reaches zero.
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal.
* Subsequent falling edges of the input signal will decrement the current count (if the gate input is high on the preceding rising edge of the input signal).
*
* When the current count decrements from one to zero, the output goes low for one cycle of the input signal (0.8381 uS).
* The current count will wrap around to 0xFFFF (or 0x9999 in BCD mode) and continue to decrement until the mode/command register or the reload register are set,
* however this will not affect the output state.
*
* If the gate input goes low, counting stops but the output will not be affected and the current count will not be reset to the reload value.
*
* The reload value can be changed at any time.
* When the new value has been set (both bytes for "lobyte/hibyte" access mode) it will be loaded into the current count on the next falling edge of the (1.193182 MHz) input signal,
* and counting will continue using the new reload value.
*/
MODE_4, // software triggered strobe
/**
* **MODE 5**
* Mode 5 is similar to mode 4, except that it waits for the rising edge of the gate input to trigger (or re-trigger) the delay period (like mode 1).
* For this reason it is not usable for PIT channels 0 or 1 (where the gate input can't be changed).
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set the PIT will wait for the next rising edge of the gate input.
* Once this occurs, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal.
* Subsequent falling edges of the input signal will decrement the current count.
*
* When the current count decrements from one to zero, the output goes low for one cycle of the input signal (0.8381 uS).
* The current count will wrap around to 0xFFFF (or 0x9999 in BCD mode) and continue to decrement until the mode/command register or the reload register are set,
* however this will not affect the output state.
*
* If the gate input signal goes low during this process it will have no effect.
* However, if the gate input goes high again it will cause the current count to be reloaded from the reload register on the next falling edge of the input signal,
* and restart the count again (the same as when counting first started).
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded (on the next rising edge of the gate input).
* When this occurs counting will continue using the new reload value.
*/
MODE_5, // hardware triggered strobe
_MODE_2, // mode 2
_MODE_3 // mode 3
};
/**
* Binary mode is the standard value,
* where as BCD will hold a single decimal value in each of the four bits
* The counter for BCD goes to 0x9999, instead of 0xFFFF
*
* BCD is not properly implemented on all chips, even if they say they support it.
*/
enum PIT_DIGIT_MODE {
BINARY_MODE = 0x0, // 16-bit binary
BCD_MODE // four-digit bcd
};
struct pit_state {
uint32_t divisor;
// this allows us to track a.. very long time before an overflow... 52ms * INT64MAX = 4.8e20ms = 5.6e12 days = 1.5e7 centuries
// TODO: ... handle overflow? :p
uint64_t interrupts;
} __attribute__((packed));
void init_pit(uint8_t init_command, uint8_t channel, uint32_t divisor);
double get_time_from_divisor(uint32_t divisor);
uint16_t read_pit_count(uint8_t channel);
void set_pit_count(uint8_t channel, uint16_t count);
#endif

View File

@ -2,6 +2,7 @@
#include <kernel/_kernel.h>
#include <kernel/serial.h>
#include <kernel/tty.h>
enum log_mode {
LOG_ERR,
@ -14,16 +15,22 @@ void klog(const char* buf, enum log_mode mode)
switch(mode) {
case LOG_ERR:
serial_writestring("ERROR: ");
terminal_writestring("ERROR: ");
break;
case LOG_WARN:
serial_writestring("WARNING: ");
terminal_writestring("WARNING: ");
break;
case LOG_INFO:
serial_writestring("INFO: ");
terminal_writestring("INFO: ");
break;
}
serial_writestring(buf);
serial_writestring("\n");
terminal_writestring(buf);
serial_writestring("\n\r");
terminal_writestring("\n");
}
void kerror(const char* buf)

View File

@ -1,42 +1,82 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <kernel/_kernel.h>
#include <kernel/tty.h>
#include <kernel/serial.h>
#include <kernel/paging.h>
#include <kernel/x86/gdt.h>
#include <kernel/x86/idt.h>
#include <kernel/x86/io.h>
#include <kernel/x86/keyb.h>
#include <kernel/x86/pit.h>
#include <kernel/x86/pci.h>
#include <kernel/pmm.h>
#define GDT_SIZE 5
#include "multiboot.h"
uint64_t gdt[GDT_SIZE];
extern struct pmm_mem_info main_mem;
void verify_memmap(multiboot_info_t* mbd, uint32_t magic)
{
if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
panic("Invalid magic number!");
void gdt_init(void)
{
gdt[0] = create_descriptor(0, 0, 0); // null
gdt[1] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL0)); // kernel code
gdt[2] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL0)); // kernel data
gdt[3] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL3)); // user code
gdt[4] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL3)); // user data
if (!(mbd->flags >> 6 & 0x1))
panic("Invalid memory map given by GRUB bootloader!");
setGdt((sizeof(uint64_t) * GDT_SIZE) - 1, &(gdt[0])); // limit, base
reloadSegments();
if (!(mbd->flags & (1 << 0)))
panic("Memory info not passed to kernel!");
puts("Printing available memory map...");
uint32_t i;
for (i = 0; i < mbd->mmap_length; i += sizeof(multiboot_memory_map_t)) {
multiboot_memory_map_t* mmmt = (multiboot_memory_map_t*) (mbd->mmap_addr + i);
printf("Start Addr: %4 | Length: %4 | Size: %2 | Type: %d\n",
mmmt->addr, mmmt->len, mmmt->size, mmmt->type);
// This is pretty flaky, we want to actually create a linked list,
// where each block of available memory gets its own mem_block
// not just this main one
// TODO
if (mmmt->addr == 0x100000) {
pmm_add_mem_block((uint32_t) mmmt->addr, (uint32_t) mmmt->len);
}
}
}
#undef GDT_SIZE
void kmain(void)
void _main(multiboot_info_t* mbd, uint32_t magic)
{
#ifdef __TESTING__ // important components should be declared first, but if we're testing we want to log all of that
terminal_initialize();
serial_initialize();
#endif
verify_memmap(mbd, magic);
gdt_init();
idt_init();
#ifndef __TESTING__
terminal_initialize();
serial_initialize();
#endif
pmm_init();
terminal_writestring("test");
//setup_paging();
serial_writestring("test!");
init_kb();
init_pit(0x36, PIT_CHANNEL_0, 0);
printf("Entering loop...\n");
while (1) {
continue;
}
printf("Exiting loop...\n");
printf("test..");
}

274
kernel/multiboot.h Normal file
View File

@ -0,0 +1,274 @@
/* multiboot.h - Multiboot header file. */
/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 8192
#define MULTIBOOT_HEADER_ALIGN 4
/* The magic field should contain this. */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/* This should be in %eax. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000004
/* Flags set in the flags member of the multiboot header. */
/* Align all boot modules on i386 page (4KB) boundaries. */
#define MULTIBOOT_PAGE_ALIGN 0x00000001
/* Must pass memory information to OS. */
#define MULTIBOOT_MEMORY_INFO 0x00000002
/* Must pass video information to OS. */
#define MULTIBOOT_VIDEO_MODE 0x00000004
/* This flag indicates the use of the address fields in the header. */
#define MULTIBOOT_AOUT_KLUDGE 0x00010000
/* Flags to be set in the flags member of the multiboot info structure. */
/* is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY 0x00000001
/* is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
/* is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE 0x00000004
/* are there modules to do something with? */
#define MULTIBOOT_INFO_MODS 0x00000008
/* These next two are mutually exclusive */
/* is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
/* is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
/* is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
/* Is there drive info? */
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
/* Is there a config table? */
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
/* Is there a boot loader name? */
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
/* Is there a APM table? */
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
/* Is there video information? */
#define MULTIBOOT_INFO_VBE_INFO 0x00000800
#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000
#ifndef ASM_FILE
typedef unsigned char multiboot_uint8_t;
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT_MAGIC - see above. */
multiboot_uint32_t magic;
/* Feature flags. */
multiboot_uint32_t flags;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
multiboot_uint32_t entry_addr;
/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
multiboot_uint32_t mode_type;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
/* The symbol table for a.out. */
struct multiboot_aout_symbol_table
{
multiboot_uint32_t tabsize;
multiboot_uint32_t strsize;
multiboot_uint32_t addr;
multiboot_uint32_t reserved;
};
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
/* The section header table for ELF. */
struct multiboot_elf_section_header_table
{
multiboot_uint32_t num;
multiboot_uint32_t size;
multiboot_uint32_t addr;
multiboot_uint32_t shndx;
};
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
struct multiboot_info
{
/* Multiboot info version number */
multiboot_uint32_t flags;
/* Available memory from BIOS */
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
/* "root" partition */
multiboot_uint32_t boot_device;
/* Kernel command line */
multiboot_uint32_t cmdline;
/* Boot-Module list */
multiboot_uint32_t mods_count;
multiboot_uint32_t mods_addr;
union
{
multiboot_aout_symbol_table_t aout_sym;
multiboot_elf_section_header_table_t elf_sec;
} u;
/* Memory Mapping buffer */
multiboot_uint32_t mmap_length;
multiboot_uint32_t mmap_addr;
/* Drive Info buffer */
multiboot_uint32_t drives_length;
multiboot_uint32_t drives_addr;
/* ROM configuration table */
multiboot_uint32_t config_table;
/* Boot Loader Name */
multiboot_uint32_t boot_loader_name;
/* APM table */
multiboot_uint32_t apm_table;
/* Video */
multiboot_uint32_t vbe_control_info;
multiboot_uint32_t vbe_mode_info;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
multiboot_uint64_t framebuffer_addr;
multiboot_uint32_t framebuffer_pitch;
multiboot_uint32_t framebuffer_width;
multiboot_uint32_t framebuffer_height;
multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
multiboot_uint8_t framebuffer_type;
union
{
struct
{
multiboot_uint32_t framebuffer_palette_addr;
multiboot_uint16_t framebuffer_palette_num_colors;
};
struct
{
multiboot_uint8_t framebuffer_red_field_position;
multiboot_uint8_t framebuffer_red_mask_size;
multiboot_uint8_t framebuffer_green_field_position;
multiboot_uint8_t framebuffer_green_mask_size;
multiboot_uint8_t framebuffer_blue_field_position;
multiboot_uint8_t framebuffer_blue_mask_size;
};
};
};
typedef struct multiboot_info multiboot_info_t;
struct multiboot_color
{
multiboot_uint8_t red;
multiboot_uint8_t green;
multiboot_uint8_t blue;
};
struct multiboot_mmap_entry
{
multiboot_uint32_t size;
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
multiboot_uint32_t type;
} __attribute__((packed));
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_mod_list
{
/* the memory used goes from bytes mod_start to mod_end-1 inclusive */
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
/* Module command line */
multiboot_uint32_t cmdline;
/* padding to take it to 16 bytes (must be zero) */
multiboot_uint32_t pad;
};
typedef struct multiboot_mod_list multiboot_module_t;
/* APM BIOS info. */
struct multiboot_apm_info
{
multiboot_uint16_t version;
multiboot_uint16_t cseg;
multiboot_uint32_t offset;
multiboot_uint16_t cseg_16;
multiboot_uint16_t dseg;
multiboot_uint16_t flags;
multiboot_uint16_t cseg_len;
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
#endif /* ! ASM_FILE */
#endif /* ! MULTIBOOT_HEADER */

13
libc/include/math.h Normal file
View File

@ -0,0 +1,13 @@
#include <stdint.h>
#ifndef MATH_H
#define MATH_H
int pow(int base, int exp);
uint8_t clamp_u8(uint8_t val, uint8_t min, uint8_t max);
uint16_t clamp_u16(uint16_t val, uint16_t min, uint16_t max);
uint32_t clamp_u32(uint32_t val, uint32_t min, uint32_t max);
uint64_t clamp_u64(uint64_t val, uint64_t min, uint64_t max);
#endif

25
libc/include/queue.h Normal file
View File

@ -0,0 +1,25 @@
#include <stdint.h>
#ifndef QUEUE_H
#define QUEUE_H
#define MAX_QUEUE_SIZE 100
typedef struct {
uint8_t items[MAX_QUEUE_SIZE];
short front;
short rear;
} queue;
void init_queue(queue* q);
bool queue_is_empty(queue* q);
bool queue_is_full(queue* q);
void enqueue(queue* q, uint8_t val);
void dequeue(queue* q);
uint8_t queue_peek(queue* q);
#endif

View File

@ -1,17 +1,28 @@
#include <stdint.h>
#ifndef _STDLIB_H
#define _STDLIB_H 1
#include <sys/cdefs.h>
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((__noreturn__))
void abort(void);
#ifdef __cplusplus
}
#endif
void panic(const char* str);
char* s64toa(int64_t num, char* buf, int base);
char* u64toa(uint64_t num, char* buf, int base);
char* s32toa(int32_t num, char* buf, int base);
char* u32toa(uint32_t num, char* buf, int base);
char* s16toa(int16_t num, char* buf, int base);
char* u16toa(uint16_t num, char* buf, int base);
char* s8toa(int8_t num, char* buf, int base);
char* u8toa(uint8_t num, char* buf, int base);
char* itoa(int num, char* buf, int base);
char* utoa(unsigned int num, char* buf, int base);
char* dtoa(double n, char* buf, int afterpoint);
#endif

39
libc/math/clamp.c Normal file
View File

@ -0,0 +1,39 @@
#include <stdint.h>
#include <math.h>
uint8_t clamp_u8(uint8_t val, uint8_t min, uint8_t max)
{
if (val > max)
return max;
if (val < min)
return min;
return val;
}
uint16_t clamp_u16(uint16_t val, uint16_t min, uint16_t max)
{
if (val > max)
return max;
if (val < min)
return min;
return val;
}
uint32_t clamp_u32(uint32_t val, uint32_t min, uint32_t max)
{
if (val > max)
return max;
if (val < min)
return min;
return val;
}
uint64_t clamp_u64(uint64_t val, uint64_t min, uint64_t max)
{
if (val > max)
return max;
if (val < min)
return min;
return val;
}

9
libc/math/pow.c Normal file
View File

@ -0,0 +1,9 @@
#include <math.h>
int pow(int base, int exp)
{
int res = base;
for (int i = 0; i < exp; i++)
res *= base;
return res;
}

58
libc/queue/queue.c Normal file
View File

@ -0,0 +1,58 @@
#ifdef __TESTING__
#include <kernel/_kernel.h>
#endif
#include <stdint.h>
#include <stdbool.h>
#include <queue.h>
void init_queue(queue *q)
{
q->front = -1;
q->rear = 0;
}
bool queue_is_empty(queue *q)
{
bool empty = q->front == q->rear - 1;
if (empty) // This allows us to continuously reuse the queue when it empties. Ideally i'd like to switch to a heap implementation... TODO
init_queue(q);
return empty;
}
bool queue_is_full(queue *q)
{return (q->rear == MAX_QUEUE_SIZE);} // TODO: this is an error for SURE
void enqueue(queue *q, uint8_t val)
{
if (queue_is_full(q)) {
#ifdef __TESTING__
kwarn("Queue being added to is full!!"); // TODO add a way to print like code lines and addresses to stack trace this
#endif
return;
}
q->items[q->rear++] = val;
}
void dequeue(queue* q)
{
if (queue_is_empty(q)) {
#ifdef __TESTING__
kwarn("Queue thats already empty is trying to be removed from!!!"); // TODO same as above
#endif
return;
}
q->front++;
}
uint8_t queue_peek(queue* q)
{
if (queue_is_empty(q)) {
#ifdef __TESTING__
kwarn("Peeking an empty queue!");
#endif
return -1;
}
return q->items[q->front + 1];
}

View File

@ -3,12 +3,14 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
static bool print(const char* data, size_t length) {
const unsigned char* bytes = (const unsigned char*) data;
for (size_t i = 0; i < length; i++)
if (putchar(bytes[i]) == EOF)
return false;
if (putchar(bytes[i]) == EOF) return false;
return true;
}
@ -18,6 +20,8 @@ int printf(const char* restrict format, ...) {
int written = 0;
char buffer[512];
while (*format != '\0') {
size_t maxrem = INT_MAX - written;
@ -61,6 +65,113 @@ int printf(const char* restrict format, ...) {
if (!print(str, len))
return -1;
written += len;
} else if (*format == 'd') {
format++;
int i = (int) va_arg(parameters, int);
itoa(i, buffer, 10);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == 'f') {
format++;
double d = (double) va_arg(parameters, double);
dtoa(d, buffer, 5);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == 'u') {
format++;
unsigned int i = (unsigned int) va_arg(parameters, unsigned int);
utoa(i, buffer, 10);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == 'x') {
format++;
int i = (int) va_arg(parameters, int);
itoa(i, buffer, 16);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == '1') {
format++;
int32_t i = (uint32_t) va_arg(parameters, int32_t);
u32toa(i, buffer, 10);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == '2') {
format++;
uint32_t i = (uint32_t) va_arg(parameters, uint32_t);
u32toa(i, buffer, 16);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == '3') {
format++;
int64_t i = (int64_t) va_arg(parameters, int64_t);
s64toa(i, buffer, 10);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == '4') {
format++;
uint64_t i = (uint64_t) va_arg(parameters, uint64_t);
u64toa(i, buffer, 16);
size_t len = strlen(buffer);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else if (*format == 'b') {
format++;
uint32_t i = (uint32_t) va_arg(parameters, uint32_t);
u32toa(i, buffer, 2);
size_t len = strlen(buffer);
if (maxrem < len) {
return -1;
}
if (!print(buffer, len))
return -1;
written += len;
} else {
format = format_begun_at;
size_t len = strlen(format);

View File

@ -2,12 +2,22 @@
#if defined(__is_libk)
#include <kernel/tty.h>
#ifdef __TESTING__
#include <kernel/serial.h>
#endif
#endif
int putchar(int ic) {
#if defined(__is_libk)
char c = (char) ic;
terminal_write(&c, sizeof(c));
#ifdef __TESTING__
serial_write(&c, sizeof(c));
if (c == '\n') {
char tmp = '\r';
serial_write(&tmp, sizeof(tmp));
}
#endif
#else
// TODO: Implement stdio and the write system call.
#endif

View File

@ -13,3 +13,9 @@ void abort(void) {
while (1) { }
__builtin_unreachable();
}
void panic(const char* str)
{
printf("%s\n", str);
abort();
}

309
libc/stdlib/itoa.c Normal file
View File

@ -0,0 +1,309 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
static void reverse(char* buf, int len)
{
int start = 0;
int end = len - 1;
while (start < end) {
char tmp = buf[start];
buf[start] = buf[end];
buf[end] = tmp;
end--;
start++;
}
}
char* s64toa(int64_t num, char* buf, int base)
{
int i = 0;
bool neg = false;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
if (num < 0 && base == 10) { // only handle negative on base10
neg = true;
num = -num;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
if (neg) // lets reapply the negative sign
buf[i++] = '-';
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* u64toa(uint64_t num, char* buf, int base)
{
int i = 0;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* s32toa(int32_t num, char* buf, int base)
{
int i = 0;
bool neg = false;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
if (num < 0 && base == 10) { // only handle negative on base10
neg = true;
num = -num;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
if (neg) // lets reapply the negative sign
buf[i++] = '-';
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* u32toa(uint32_t num, char* buf, int base)
{
int i = 0;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* s16toa(int16_t num, char* buf, int base)
{
int i = 0;
bool neg = false;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
if (num < 0 && base == 10) { // only handle negative on base10
neg = true;
num = -num;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
if (neg) // lets reapply the negative sign
buf[i++] = '-';
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* u16toa(uint16_t num, char* buf, int base)
{
int i = 0;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* s8toa(int8_t num, char* buf, int base)
{
int i = 0;
bool neg = false;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
if (num < 0 && base == 10) { // only handle negative on base10
neg = true;
num = -num;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
if (neg) // lets reapply the negative sign
buf[i++] = '-';
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* u8toa(uint8_t num, char* buf, int base)
{
int i = 0;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* itoa(int num, char* buf, int base)
{
int i = 0;
bool neg = false;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
if (num < 0 && base == 10) { // only handle negative on base10
neg = true;
num = -num;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
if (neg) // lets reapply the negative sign
buf[i++] = '-';
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
char* utoa(unsigned int num, char* buf, int base)
{
int i = 0;
if (num == 0) { // Handle zero explicitly
buf[i++] = '0';
buf[i] = '\0';
return buf;
}
while (num != 0) {
int rem = num % base;
buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
buf[i] = '\0';
reverse(buf, i); // reverse, since we did it backwards!
return buf;
}
static int dbl_to_str(int x, char* buf, int d)
{
int i = 0;
while (x) {
buf[i++] = (x % 10) + '0';
x = x / 10;
}
while (i < d)
buf[i++] = '0';
reverse(buf, i);
buf[i] = '\0';
return i;
}
char* dtoa(double n, char* buf, int afterpoint)
{
int whole = (int) n;
double decimals = n - (double) whole;
int i = dbl_to_str(whole, buf, 0);
if (afterpoint != 0) {
buf[i] = '.'; // add the decimal
decimals = decimals * pow(10, afterpoint);
dbl_to_str((int) decimals, buf + i + 1, afterpoint + 1);
}
return buf;
}

View File

@ -1,10 +1,11 @@
megs: 32
megs: 32
display_library: sdl
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=1000000
com1: enabled=1, mode=file, dev=com1.out
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=10000000
com1: enabled=1, mode=file, dev=com1.out
usb_uhci: port1=mouse, port2=disk, options2="path:usbstick.img"

2
util/gdbcmds Normal file
View File

@ -0,0 +1,2 @@
target remote :1234
c