Compare commits
46 Commits
7dedc0019b
...
main
Author | SHA1 | Date | |
---|---|---|---|
3fd818395d | |||
8a18cf5633 | |||
88921f024e | |||
ed2c0a3568 | |||
2ec3e259d8 | |||
99a842df7e | |||
af596cd43a | |||
779680987f | |||
45c167568c | |||
a009462c72 | |||
7669ea32a4 | |||
98f8a0dc88 | |||
f216c32f22 | |||
03aecd514f | |||
920d0b01e1 | |||
83b6a9eaf2 | |||
f1515ad7b5 | |||
a776376403 | |||
ac3dc4d48a | |||
c6cc318c69 | |||
f9c2ea2f2b | |||
378f7ef23d | |||
a0749400f5 | |||
1e1a28e23d | |||
3e01dc2074 | |||
79f04df82e | |||
87e5e06142 | |||
e471564f89 | |||
01076e24b8 | |||
af92026a74 | |||
ca77157344 | |||
0fc2e6a199 | |||
3ffb3aa181 | |||
4c938a0855 | |||
ecc91fdc7d | |||
89c02d6a93 | |||
c50d3f14a6 | |||
013b5a557e | |||
f8c38d22b0 | |||
47056f7d9a | |||
1a60e91745 | |||
ee2c5a72a1 | |||
c115dcd4f7 | |||
0256466f4c | |||
b87738ca47 | |||
72bab4a548 |
19
Makefile
19
Makefile
@ -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
6
README.md
Normal 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
11
REFERENCES.md
Normal 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
43
TODO.md
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
201
kernel/arch/io/keyb.c
Normal 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
75
kernel/arch/io/pit.c
Normal 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);
|
||||
}
|
@ -3,9 +3,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <kernel/serial.h>
|
||||
|
||||
|
||||
#include "io.h"
|
||||
#include <kernel/x86/io.h>
|
||||
|
||||
/* I/O ports */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 = .;
|
||||
}
|
||||
}
|
||||
|
86
kernel/arch/paging/paging.h
Normal file
86
kernel/arch/paging/paging.h
Normal 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
42
kernel/arch/pic/pci.c
Normal 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
171
kernel/arch/pic/pic.c
Normal 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
211
kernel/arch/pmm/pmm.c
Normal 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
|
||||
|
@ -7,6 +7,5 @@ void kwarn(const char*);
|
||||
|
||||
void kinfo(const char*);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
6
kernel/include/kernel/paging.h
Normal file
6
kernel/include/kernel/paging.h
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef ARCH_PAGING_H
|
||||
#define ARCH_PAGING_H
|
||||
|
||||
#endif
|
15
kernel/include/kernel/pmm.h
Normal file
15
kernel/include/kernel/pmm.h
Normal 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
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
39
kernel/include/kernel/x86/io.h
Normal file
39
kernel/include/kernel/x86/io.h
Normal 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
|
||||
|
||||
|
145
kernel/include/kernel/x86/keyb.h
Normal file
145
kernel/include/kernel/x86/keyb.h
Normal 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
|
16
kernel/include/kernel/x86/pci.h
Normal file
16
kernel/include/kernel/x86/pci.h
Normal 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
|
43
kernel/include/kernel/x86/pic.h
Normal file
43
kernel/include/kernel/x86/pic.h
Normal 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
|
261
kernel/include/kernel/x86/pit.h
Normal file
261
kernel/include/kernel/x86/pit.h
Normal 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
|
@ -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)
|
||||
|
@ -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
274
kernel/multiboot.h
Normal 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
13
libc/include/math.h
Normal 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
25
libc/include/queue.h
Normal 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
|
@ -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
39
libc/math/clamp.c
Normal 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
9
libc/math/pow.c
Normal 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
58
libc/queue/queue.c
Normal 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];
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
309
libc/stdlib/itoa.c
Normal 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;
|
||||
}
|
@ -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
2
util/gdbcmds
Normal file
@ -0,0 +1,2 @@
|
||||
target remote :1234
|
||||
c
|
Reference in New Issue
Block a user