Compare commits
26 Commits
87e5e06142
...
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 |
4
Makefile
4
Makefile
@ -3,7 +3,7 @@
|
||||
|
||||
CC = i686-elf-gcc
|
||||
|
||||
DEBUG_FLAGS = -D__TESTING__ -g
|
||||
DEBUG_FLAGS = -D__TESTING__ -g
|
||||
|
||||
CFLAGS = -m32 -ffreestanding -Wall -Wextra -Werror -Wpedantic --sysroot=$(PWD)/sysroot -isystem=/usr/include -Iinclude -MD $(DEBUG_FLAGS)
|
||||
|
||||
@ -39,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
|
||||
|
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/
|
44
TODO.md
44
TODO.md
@ -1,7 +1,43 @@
|
||||
## What do I need to do?
|
||||
|
||||
Exception handling is a must for next part, we need to be able to dianogse issues better
|
||||
- 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?
|
||||
|
||||
- Want the address of the fault
|
||||
- Proper handling of the issue
|
||||
- Maybe dump registers?
|
||||
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
|
||||
|
@ -3,7 +3,7 @@
|
||||
; 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
|
||||
@ -30,7 +30,10 @@ section .text
|
||||
loader:
|
||||
mov esp, kernel_stack + KERNEL_STACK_SIZE ; move the top of the stack into esp
|
||||
|
||||
call kmain ; pass execution over to our kmain function, where all of the real stuff is done
|
||||
push eax
|
||||
push ebx
|
||||
|
||||
call _main ; pass execution over to our _main function, where all of the real stuff is done
|
||||
|
||||
; Should the system exit, we clear the interrupt flag
|
||||
; and do an infinite loop of nothing
|
||||
|
@ -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
|
||||
|
@ -26,8 +26,58 @@
|
||||
* 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)
|
||||
{
|
||||
uint64_t descriptor;
|
||||
@ -63,11 +113,11 @@ 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
|
||||
gdt[3] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL3)); // user code
|
||||
gdt[4] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL3)); // user data
|
||||
|
||||
setGdt((sizeof(uint64_t) * GDT_SIZE) - 1, &(gdt[0])); // limit, base
|
||||
reloadSegments();
|
||||
set_gdt((sizeof(uint64_t) * GDT_SIZE) - 1, &(gdt[0])); // limit, base
|
||||
reload_segments();
|
||||
#ifdef __TESTING__
|
||||
kinfo("Initialized the GDT");
|
||||
#endif
|
||||
@ -76,4 +126,31 @@ void gdt_init(void)
|
||||
#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,39 +1,313 @@
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __TESTING__
|
||||
#include <kernel/_kernel.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#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];
|
||||
extern struct pit_state pit;
|
||||
|
||||
extern struct keyboard_state keyb_state;
|
||||
|
||||
void exception_handler(unsigned int i)
|
||||
#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)
|
||||
{
|
||||
if (i <= 31) { // TODO: implement proper handling for each exception, also implement the proper gates & error code checking
|
||||
#ifdef __TESTING__
|
||||
kerror("EXCEPTION");
|
||||
printf("Exeption: %u\n", i);
|
||||
#endif
|
||||
__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 (i == PIC_KEYB) {
|
||||
unsigned char in = inb(0x60);
|
||||
do_keypress(decode_scancode(in));
|
||||
PIC_sendEOI(1);
|
||||
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)
|
||||
{
|
||||
@ -65,10 +339,13 @@ void idt_init(void)
|
||||
|
||||
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,27 +1,47 @@
|
||||
extern exception_handler
|
||||
extern exception_handler_err
|
||||
|
||||
%macro isr_err_stub 1
|
||||
isr_stub_%+%1:
|
||||
pushad
|
||||
cld
|
||||
push dword %1
|
||||
call exception_handler
|
||||
pop eax ; make sure to pop off the dword!!
|
||||
popad
|
||||
iret
|
||||
push dword %1 ; push the interrupt number
|
||||
jmp common_interrupt_handler
|
||||
|
||||
%endmacro
|
||||
|
||||
%macro isr_no_err_stub 1
|
||||
isr_stub_%+%1:
|
||||
pushad
|
||||
cld
|
||||
push dword 0
|
||||
push dword %1
|
||||
call exception_handler
|
||||
pop eax
|
||||
popad
|
||||
iret
|
||||
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
|
||||
|
@ -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,9 +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
|
||||
|
||||
|
||||
|
||||
|
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);
|
||||
}
|
@ -19,6 +19,7 @@ SECTIONS
|
||||
*/
|
||||
.text BLOCK(4K) : ALIGN(4K) /* The first section we want is the text section, this is where most code is */
|
||||
{
|
||||
text_start = .;
|
||||
*(.multiboot) /* Multiboot needs to be early in the file, required by grub */
|
||||
*(.text) /* The text section */
|
||||
}
|
||||
@ -33,12 +34,16 @@ SECTIONS
|
||||
.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;
|
||||
}
|
@ -5,6 +5,33 @@
|
||||
#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
|
||||
@ -123,3 +150,22 @@ 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,59 +3,6 @@
|
||||
#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
|
||||
|
||||
|
||||
#define GDT_SIZE 3
|
||||
|
||||
|
||||
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,47 +3,25 @@
|
||||
#ifndef ARCH_IDT_H
|
||||
#define ARCH_IDT_H
|
||||
|
||||
#define IDT_MAX_DESCRIPTORS 48 // number of entries in the idt table
|
||||
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));
|
||||
|
||||
struct stack_state {
|
||||
uint32_t error_code;
|
||||
uint32_t eip;
|
||||
uint32_t cs;
|
||||
uint32_t eflags;
|
||||
} __attribute__((packed));
|
||||
|
||||
#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_SEG_OVERRUN 9
|
||||
#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
|
||||
void exception_handler(struct cpu_state cpu, uint32_t interrupt, struct stack_state stack);
|
||||
|
||||
void exception_handler(unsigned int i);
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef ARCH_IO_H
|
||||
#define ARCH_IO_H
|
||||
|
||||
@ -8,7 +10,9 @@
|
||||
* @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, unsigned char data);
|
||||
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:
|
||||
@ -17,7 +21,9 @@ void outb(unsigned short port, unsigned char data);
|
||||
* @param port The address of the I/O port
|
||||
* @return The read byte
|
||||
*/
|
||||
unsigned char inb(unsigned short port);
|
||||
uint8_t inb(unsigned short port);
|
||||
uint16_t inb_16(unsigned short port);
|
||||
uint32_t inb_32(unsigned short port);
|
||||
|
||||
/**
|
||||
* io_wait:
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef ARCH_KEYB_H
|
||||
#define ARCH_KEYB_H
|
||||
|
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
|
@ -3,34 +3,6 @@
|
||||
#ifndef ARCH_PIC_H
|
||||
#define ARCH_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 **/
|
||||
|
||||
|
||||
#define PIC_PIT 32
|
||||
#define PIC_KEYB 33
|
||||
#define PIC_CASCADE 34 // never raised
|
||||
@ -63,11 +35,6 @@ void pic_disable(void);
|
||||
void IRQ_set_mask(uint8_t IRQline);
|
||||
void IRQ_clear_mask(uint8_t IRQline);
|
||||
|
||||
/** Returns the combined value of the cascaded PICs irq request register **/
|
||||
uint16_t pic_get_irr(void);
|
||||
/** Returns the combined value of the cascaded PICs in-service register **/
|
||||
uint16_t pic_get_isr(void);
|
||||
|
||||
/**
|
||||
* TODO: implement handling for Spurious IRQs
|
||||
* https://wiki.osdev.org/8259_PIC#Spurious_IRQs
|
||||
|
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
|
@ -29,7 +29,7 @@ void klog(const char* buf, enum log_mode mode)
|
||||
serial_writestring(buf);
|
||||
terminal_writestring(buf);
|
||||
|
||||
serial_writestring("\n");
|
||||
serial_writestring("\n\r");
|
||||
terminal_writestring("\n");
|
||||
}
|
||||
|
||||
|
@ -1,22 +1,61 @@
|
||||
#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>
|
||||
|
||||
#include "multiboot.h"
|
||||
|
||||
extern struct pmm_mem_info main_mem;
|
||||
|
||||
void kmain(void)
|
||||
void verify_memmap(multiboot_info_t* mbd, uint32_t magic)
|
||||
{
|
||||
if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
|
||||
panic("Invalid magic number!");
|
||||
|
||||
if (!(mbd->flags >> 6 & 0x1))
|
||||
panic("Invalid memory map given by GRUB bootloader!");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@ -24,9 +63,15 @@ void kmain(void)
|
||||
terminal_initialize();
|
||||
serial_initialize();
|
||||
#endif
|
||||
|
||||
|
||||
pmm_init();
|
||||
|
||||
//setup_paging();
|
||||
|
||||
init_kb();
|
||||
|
||||
init_pit(0x36, PIT_CHANNEL_0, 0);
|
||||
|
||||
printf("Entering loop...\n");
|
||||
while (1) {
|
||||
|
||||
|
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
|
@ -8,6 +8,8 @@
|
||||
__attribute__((__noreturn__))
|
||||
void abort(void);
|
||||
|
||||
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);
|
||||
@ -20,5 +22,7 @@ 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;
|
||||
}
|
@ -16,7 +16,7 @@ void init_queue(queue *q)
|
||||
bool queue_is_empty(queue *q)
|
||||
{
|
||||
bool empty = q->front == q->rear - 1;
|
||||
if (empty)
|
||||
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;
|
||||
}
|
||||
|
@ -77,6 +77,18 @@ int printf(const char* restrict format, ...) {
|
||||
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);
|
||||
@ -103,8 +115,8 @@ int printf(const char* restrict format, ...) {
|
||||
written += len;
|
||||
} else if (*format == '1') {
|
||||
format++;
|
||||
int32_t i = (int32_t) va_arg(parameters, int32_t);
|
||||
s32toa(i, buffer, 10);
|
||||
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.
|
||||
@ -149,6 +161,17 @@ int printf(const char* restrict format, ...) {
|
||||
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);
|
||||
|
@ -13,6 +13,10 @@ int putchar(int 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.
|
||||
|
@ -13,3 +13,9 @@ void abort(void) {
|
||||
while (1) { }
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void panic(const char* str)
|
||||
{
|
||||
printf("%s\n", str);
|
||||
abort();
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
static void reverse(char* buf, int len)
|
||||
{
|
||||
@ -273,3 +275,35 @@ char* utoa(unsigned int num, char* buf, int base)
|
||||
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;
|
||||
}
|
||||
|
@ -8,4 +8,4 @@ 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"
|
||||
|
Reference in New Issue
Block a user