Add kernel documentation

This commit is contained in:
pjht 2020-07-25 16:54:37 -05:00
parent 794dd702b0
commit ee7ce4dabe
32 changed files with 5837 additions and 194 deletions

3
.gitignore vendored
View File

@ -15,4 +15,5 @@ sysroot/boot/initrd.tar
serout
vga_drv/vga_drv
.vagrant
.vscode
.vscode
kerneldoc

2553
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

2582
Doxyfile.bak Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
PLAT=i386
C_SOURCES = $(wildcard kernel/*.c kernel/cpu/$(PLAT)/*.c kernel/cpu/*.c)
C_HEADERS = $(wildcard kernel/*.h kernel/cpu/$(PLAT)/*.h kernel/cpu/*.h)
ASM = $(wildcard kernel/cpu/$(PLAT)/*.asm)
S_ASM = $(wildcard kernel/cpu/$(PLAT)/*.s)
LIBC_SOURCES = $(wildcard libc/*.c libc/*/*.c)
@ -86,3 +87,6 @@ kernel/cpu/isr.h: kernel/cpu/$(PLAT)/isr.h
clean:
@rm -rf initrd/* kernel/*.o drivers/*/*.o drivers/*/*/*.o cpu/*/*.o fs/*.o libc/libc.a kernel/cstart.o cpu/memory.h os.iso */*.elf sysroot/boot/initrd.tar
doc: $(C_SOURCES) $(C_HEADERS)
@doxygen > /dev/null

View File

@ -1,6 +1,25 @@
/**
* \file
*/
#ifndef ADDRESS_SPACES_H
#define ADDRESS_SPACES_H
/**
* Copy data into an address space at a specified virtual address
* \param cr3 The adress space to copy data to.
* \param data The data to copy
* \param size The size of the data
* \param virt_addr The address to copy the data to in the address space
*/
void address_spaces_copy_data(void* cr3, void* data,uint32_t size,void* virt_addr);
/**
* Put data into an address space at an unknown virtual address
* \param cr3 The adress space to copy data to.
* \param data The data to copy
* \param size The size of the data
* \return The address that the data was copied to.
*/
void* address_spaces_put_data(void* cr3, void* data,uint32_t size);
#endif

View File

@ -1,6 +1,13 @@
/**
* \file
*/
#ifndef CPU_INIT_H
#define CPU_INIT_H
/**
* Initialize any architecture-specific CPU things.
*/
void cpu_init();
#endif

View File

@ -1,6 +1,13 @@
/**
* \file
*/
#ifndef HALT_H
#define HALT_H
/**
* Clear interrupts and halt the CPU,
*/
void halt() __attribute__((noreturn));
#endif

View File

@ -1,3 +1,7 @@
/**
* \file
*/
#include "gdt.h"
void cpu_init() {

View File

@ -1,63 +1,95 @@
/**
* \file
*/
#include <stdint.h>
#include <string.h>
#define NUM_ENTRIES 6
extern uint32_t int_stack_top;
#define NUM_ENTRIES 6 //!< Number of entries in the GDT
extern uint32_t int_stack_top; //!< Initial kernel stack before the kernel's first yield
/**
* Represents an entry in the GDT.
*/
typedef struct {
uint16_t limit_low16;
uint16_t base_low16;
uint8_t base_mid8;
uint8_t access;
uint8_t limit_flags;
uint8_t base_high8;
uint16_t limit_low16;//!< Low 16 bits of the limit
uint16_t base_low16; //!< Low 16 bits of the base
uint8_t base_mid8; //!< Middle 8 bits of the base
uint8_t access; /**<
Access byte. Gives info about the descriptor. <br>
Format: <br>
Bit 7: Present. Must be 1 for all valid selectors. <br>
Bits 6-5. Privilege. Contains the ring level for the selector. 0 for kernel mode, 3 for user mode. <br>
Bit 4. Descriptor type. Must be set for code/data segments and cleared for system segments like the TSS. <br>
Bit 3. Executable. If this bit is set, it is a code selector, otherwise a data selector. <br>
Bit 2. Direction/Conforming. Too complex to explain, should be set to 0. <br>
Bit 1. Readable/Writable. For code sels, this bit sets whther you can use it like a read-only data segment. For data sels, it sets whether the selector is writable. <br>
Bit 0. Acessed bit. Set to 0. <br>
*/
uint8_t limit_flags; /**<
High nibble of this contains two flags, and the lower niblle contains the high 4 bits of the limit. <br>
The flags are: <br>
Bit 3. Granularity. 0 for byte granularity, 1 for 4 KB granularity. <br>
Bit 2. Size. 0 for 16 bit protected mode, 1 for 32 bit protected mode. <br>
Bits 1-0. Unused. Set to 0. <br>
*/
uint8_t base_high8; //!< High 8 bits of the base
} __attribute__((packed)) gdt_entry;
/**
* Pointed to by the GDTR to tell the processor the GDT's size and address.
*/
typedef struct {
uint16_t size;
gdt_entry* address;
uint16_t size; //!< Size of the GDT.
gdt_entry* address; //!< Address of the GDT.
} __attribute__((packed)) gdt_description;
/**
* Represents a TSS.
*/
typedef struct {
uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list.
uint32_t esp0; // The stack pointer to load when we change to kernel mode.
uint32_t ss0; // The stack segment to load when we change to kernel mode.
uint32_t esp1; // Unused...
uint32_t ss1;
uint32_t esp2;
uint32_t ss2;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint32_t es; // The value to load into ES when we change to kernel mode.
uint32_t cs; // The value to load into CS when we change to kernel mode.
uint32_t ss; // The value to load into SS when we change to kernel mode.
uint32_t ds; // The value to load into DS when we change to kernel mode.
uint32_t fs; // The value to load into FS when we change to kernel mode.
uint32_t gs; // The value to load into GS when we change to kernel mode.
uint32_t ldt; // Unused...
uint16_t trap;
uint16_t iomap_base;
char iopb[8192]; // IO port bitmap
uint8_t set_ff;
uint32_t prev_tss; //!< The previous TSS - if we used hardware task switching this would form a linked list.
uint32_t esp0; //!< The stack pointer to load when we change to kernel mode.
uint32_t ss0; //!< The stack segment to load when we change to kernel mode.
uint32_t esp1; //!< Unused
uint32_t ss1; //!< Unused
uint32_t ss2; //!< Unused
uint32_t esp2; //!< Unused
uint32_t cr3; //!< Unused
uint32_t eip; //!< Unused
uint32_t eflags; //!< Unused
uint32_t eax; //!< Unused
uint32_t ecx; //!< Unused
uint32_t edx; //!< Unused
uint32_t ebx; //!< Unused
uint32_t esp; //!< Unused
uint32_t ebp; //!< Unused
uint32_t esi; //!< Unused
uint32_t edi; //!< Unused
uint32_t es; //!< The value to load into ES when we change to kernel mode.
uint32_t cs; //!< The value to load into CS when we change to kernel mode.
uint32_t ss; //!< The value to load into SS when we change to kernel mode.
uint32_t ds; //!< The value to load into DS when we change to kernel mode.
uint32_t fs; //!< The value to load into FS when we change to kernel mode.
uint32_t gs; //!< The value to load into GS when we change to kernel mode.
uint32_t ldt; //!< Unused
uint16_t trap; //!< Unused
uint16_t iomap_base; //!< Offset of the IOPB in the TSS.
char iopb[8192]; //!< IO port bitmap
uint8_t set_ff; //!< Must be set to 0xFF to mark the end of the IOPB
} __attribute__((packed)) tss_entry;
static gdt_entry gdt[NUM_ENTRIES];
static gdt_description gdt_desc;
tss_entry tss;
static gdt_entry gdt[NUM_ENTRIES]; //!< The GDT
static gdt_description gdt_desc; //!< The value to load into the GDTR
tss_entry tss; //!< The TSS
void tss_stack_reset() {
tss.esp0=int_stack_top+0xC0000000;
}
/**
* Set a GDT entry.
* \param i The GDT entry to set.
* \param base The base of the GDT entry.
* \param limit The limit of the GDT entry.
* \param access The access byte of the GDT entry.
*/
static void set_entry(int i,uint32_t base,uint32_t limit,uint8_t access) {
gdt[i].limit_low16=limit&0xFFFF;
@ -69,6 +101,13 @@ static void set_entry(int i,uint32_t base,uint32_t limit,uint8_t access) {
gdt[i].base_high8=(base&0xFF000000)>>24;
}
/**
* Set a GDT entry.
* \param num The GDT entry to set.
* \param ss0 The kernel stack selector.
* \param esp0 The kernel stack pointer.
*/
static void write_tss(int32_t num, uint16_t ss0, uint32_t esp0) {
// Firstly, let's compute the base and limit of our entry into the GDT.
uint32_t base = (uint32_t) &tss;
@ -111,20 +150,18 @@ void allow_all_ports() {
}
}
void block_all_ports() {
for (int i=0;i<8192;i++) {
tss.iopb[i]=0xFF;
}
}
void gdt_init() {
set_entry(0,0,0,0);
set_entry(1,0,0xFFFFF,0x9A);
set_entry(2,0,0xFFFFF,0x92);
set_entry(3,0,0xFFFFF,0xFA);
set_entry(4,0,0xFFFFF,0xF2);
set_entry(1,0,0xFFFFF,0b10011010);
set_entry(2,0,0xFFFFF,0b10010010);
set_entry(3,0,0xFFFFF,0b11111010);
set_entry(4,0,0xFFFFF,0b11110010);
write_tss(5,0x10,int_stack_top+0xC0000000);
gdt_desc.size=(sizeof(gdt_entry)*NUM_ENTRIES)-1;
gdt_desc.address=gdt;

View File

@ -1,8 +1,26 @@
/**
* \file
*/
#ifndef GDT_H
#define GDT_H
/**
* Initializes the GDT & TSS.
*/
void gdt_init();
void tss_stack_reset();
/**
* Allows all ports in the IOPB.
*/
void allow_all_ports();
/**
* Blocks all ports in the IOPB.
*/
void block_all_ports();
#endif

View File

@ -1,51 +1,57 @@
/**
* \file
*/
#include "idt.h"
#include <stdint.h>
/* Segment selectors */
#define KERNEL_CS 0x08
#define KERNEL_CS 0x08 //!< Kernel code segemnt selector
/* How every interrupt gate (handler) is defined */
/**
* Defines an interrupt gate
*/
typedef struct {
uint16_t low_offset; /* Lower 16 bits of handler function address */
uint16_t sel; /* Kernel segment selector */
uint8_t always0;
/* First byte
* Bit 7: "Interrupt is present"
* Bits 6-5: Privilege level of caller (0=kernel..3=user)
* Bit 4: Set to 0 for interrupt gates
* Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
uint8_t flags;
uint16_t high_offset; /* Higher 16 bits of handler function address */
} __attribute__((packed)) idt_gate_t ;
uint16_t low_offset; //!< Lower 16 bits of handler function address
uint16_t sel; //!< Kernel segment selector
uint8_t always0; //!< Must be 0.
uint8_t flags; /**<
Flags byte. Gives info about the descriptor
* Bit 7: Present. Must be 1 for all valid selectors.
* Bits 6-5: Privilege. Contains the minimum ring level for the caller. 0 for kernel mode, 3 for user mode.
* Bit 4: Set to 0 for interrupt gates.
* Bits 3-0: 1110 = "32 bit interrupt gate".
*/
uint16_t high_offset; //!< Higher 16 bits of handler function address
} __attribute__((packed)) idt_gate_t;
/* A pointer to the array of interrupt handlers.
* Assembly instruction 'lidt' will read it */
/**
* Pointed to by the IDTR to tell the processor the IDT's size and address.
*/
typedef struct {
uint16_t limit;
uint32_t base;
uint16_t limit; //!< Size of the IDT.
idt_gate_t* base; //!< Address of the IDT.
} __attribute__((packed)) idt_register_t;
#define IDT_ENTRIES 256
#define IDT_ENTRIES 256 //!< Number of entries in the IDT
static idt_gate_t idt[IDT_ENTRIES];
static idt_register_t idt_reg;
#define low_16(address) (uint16_t)((address) & 0xFFFF)
#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF)
static idt_gate_t idt[IDT_ENTRIES]; //!< The IDT
static idt_register_t idt_reg; //!< The value to load into the IDTR
#define low_16(address) (uint16_t)((address) & 0xFFFF) //!< Macro to get the low 16 bits of an address
#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) //!< Macro to get the high 16 bits of an address
void idt_set_gate(int n,uint32_t handler) {
idt[n].low_offset=low_16(handler);
idt[n].sel=0x08;
idt[n].always0=0;
idt[n].flags=0xEE;
idt[n].high_offset=high_16(handler);
idt[n].low_offset=low_16(handler);
idt[n].sel=0x08;
idt[n].always0=0;
idt[n].flags=0xEE;
idt[n].high_offset=high_16(handler);
}
void load_idt() {
idt_reg.base=(uint32_t) &idt;
idt_reg.limit=IDT_ENTRIES * sizeof(idt_gate_t) - 1;
/* Don't make the mistake of loading &idt -- always load &idt_reg */
asm volatile("lidtl (%0)":: "r" (&idt_reg));
idt_reg.base=&idt[0];
idt_reg.limit=IDT_ENTRIES * sizeof(idt_gate_t) - 1;
/* Don't make the mistake of loading &idt -- always load &idt_reg */
asm volatile("lidtl (%0)":: "r" (&idt_reg));
}

View File

@ -1,10 +1,22 @@
/**
* \file
*/
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
/* Functions implemented in idt.c */
/**
* Sets an IDT gate.
* \param n the IDT gate to set
* \param handler the handler for the gate.
*/
void idt_set_gate(int n,uint32_t handler);
/**
* Loads the IDT
*/
void load_idt();
#endif

View File

@ -1,3 +1,6 @@
/**
* \file
*/
#include "../../kernel.h"
#include "../../tasking.h"
#include "../../vga_err.h"
@ -8,6 +11,7 @@
#include "../serial.h"
#include "gdt.h"
#include "idt.h"
#include "isr.h"
#include "interrupt.h"
#include <cpu/ports.h>
#include <stdint.h>
@ -16,10 +20,8 @@
#include <sys/types.h>
void irq_handler(registers_t* r);
static isr_t irq_handlers[16];
static isr_t irq_handlers[16]; //!< Handlers for the PIC interrupts
/* Can't do this with a loop because we need the address
* of the function names */
void isr_install() {
idt_set_gate(0,(uint32_t)isr0);
idt_set_gate(1,(uint32_t)isr1);
@ -90,8 +92,8 @@ void isr_install() {
}
/* To print the message which defines every exception */
//! List of messages for each exception
__attribute__((unused)) static char *exception_messages[] = {
"Division By Zero",
"Debug",
@ -130,6 +132,11 @@ __attribute__((unused)) static char *exception_messages[] = {
"Reserved"
};
/**
* Handler for non-PIC interrupts
* \param r The saved state of the CPU
*/
void isr_handler(registers_t* r) {
if (r->int_no!=80 && r->int_no!=14) {
serial_write_string(exception_messages[r->int_no]);
@ -276,6 +283,10 @@ void isr_register_handler(int n,isr_t handler) {
irq_handlers[n] = handler;
}
/**
* Handler for PIC interrupts
* \param r The saved state of the CPU
*/
void irq_handler(registers_t* r) {
/* After every interrupt we need to send an EOI to the PICs
* or they will not send another interrupt again */

View File

@ -1,19 +1,46 @@
/**
* \file
*/
#ifndef ISR_H
#define ISR_H
#include <stdint.h>
/* Struct which aggregates many registers */
/**
* Saved state of the CPU when an interrupt occurs
*/
typedef struct {
uint32_t ds; /* Data segment selector */
uint32_t edi,esi,ebp,esp,ebx,edx,ecx,eax; /* Pushed by pusha. */
uint32_t int_no,err_code; /* Interrupt number and error code (if applicable) */
uint32_t eip,cs,eflags,useresp,ss; /* Pushed by the processor automatically */
uint32_t ds; //!< Data segment selector
uint32_t edi; //!< Pushed by pusha.
uint32_t esi; //!< Pushed by pusha.
uint32_t ebp; //!< Pushed by pusha.
uint32_t esp; //!< Pushed by pusha.
uint32_t ebx; //!< Pushed by pusha.
uint32_t edx; //!< Pushed by pusha.
uint32_t ecx; //!< Pushed by pusha.
uint32_t eax; //!< Pushed by pusha.
uint32_t int_no; //!< Interrupt number
uint32_t err_code; //!< Error code (if applicable)
uint32_t eip; //!< Pushed by the processor automatically
uint32_t cs; //!< Pushed by the processor automatically
uint32_t eflags; //!< Pushed by the processor automatically
uint32_t useresp; //!< Pushed by the processor automatically
uint32_t ss; //!< Pushed by the processor automatically
} registers_t;
typedef void (*isr_t)(registers_t*);
typedef void (*isr_t)(registers_t*); //!< Type of an ISR handler function pointer
/**
* Install the interrupt handlers into the IDT.
*/
void isr_install();
/**
* Register an IRQ handler
* \param n the IRQ to register a handler for
* \param handler the handler to register
*/
void isr_register_handler(int n,isr_t handler);
#endif

View File

@ -1,3 +1,7 @@
/**
* \file
*/
#include "../../pmem.h"
#include "../../vga_err.h"
#include "../halt.h"
@ -7,12 +11,34 @@
#include <stdint.h>
#include <stdlib.h>
static uint32_t page_directory[1024] __attribute__((aligned(4096)));
static uint32_t kern_page_tables[NUM_KERN_FRAMES] __attribute__((aligned(4096)));
static uint32_t kstack_page_tables[218*1024] __attribute__((aligned(4096)));
static uint32_t kmalloc_page_tables[4*1024] __attribute__((aligned(4096)));
static uint32_t* pagdirmap=(uint32_t*)0xFFFFF000;
static uint32_t* pagtblmap=(uint32_t*)0xFFC00000;
/**
* \page pg_struct_entry Format of a paging structure entry
* The format of a page table/directiry entry is as following: <br>
* Bits 31-11 is the physical frame number the entry points to. <br>
* Bits 11-9 are availible for use by the OS. <br>
* Bit 8 is ignored. <br>
* Bit 7 is the page size in page directories, and must be 0 in page tables. If set to 1 in a page directory, it indicates 4MB pages. <br>
* Bit 6 is the dirty bit in page tables, and must be 0 in page directories. In page tabes, it is set to 1 by the CPU when the page is written to. <br>
* Bit 5 will be set to 1 by the CPU when the page is accessed. <br>
* Bit 4 indicates whether the page has it's cache disabled. <br>
* Bit 3 indictates whether write-through caching (when it is 1), or write-back caching, (when it is 0) is enabled. <br>
* Bit 2 indictaes whether user mode code can access the page. <br>
* Bit 1 indicates whether the page is writable. <br>
* Bit 0 indicates whether the entry is present. If it is 0, the CU ignores the other 31 bits of the entry. <br>
* Privlege bits in the entries are ANDed together, so the most restrictive privlege between the page directory and the page table wins.
*/
static uint32_t page_directory[1024] __attribute__((aligned(4096))); //!< The kernel process's page directory
static uint32_t kern_page_tables[NUM_KERN_FRAMES] __attribute__((aligned(4096))); //!< The page tables where the kernel binary is mapped in
static uint32_t kstack_page_tables[218*1024] __attribute__((aligned(4096))); //!< Page tables for thread kernel stacks
static uint32_t kmalloc_page_tables[4*1024] __attribute__((aligned(4096))); //!< Page tables for the kmalloc heap
static uint32_t* pagdirmap=(uint32_t*)0xFFFFF000; //!< Pointer to the page directory entries in the recursive mapping
static uint32_t* pagtblmap=(uint32_t*)0xFFC00000; //!< Pointer to the page table entries in the recursive mapping
/**
* Checks whether a page is present
* \param page The page number to check
* \return Whether the page is present
*/
static char is_page_present(size_t page) {
int table=page>>10;
page=page&0x3FF;
@ -118,7 +144,11 @@ void alloc_pages_virt(int num_pages,void* addr) {
map_pages(addr,phys_addr,num_pages,1,1);
}
void invl_page(void* addr) {
/**
* Invalidates a page in the TLB,
* \param addr The address of the page to invalidate.
*/
static void invl_page(void* addr) {
asm volatile("invlpg (%0)"::"r"(addr):"memory");
}

View File

@ -1,17 +1,26 @@
/**
* \file
*/
#include <cpu/ports.h>
static char configured[]={0,0,0,0};
static int data_ports[4]={0x3f8,0x2f8,0x3e8,0x2e8};
static char configured[]={0,0,0,0}; //!< What serial ports have been detected and configured
static int data_ports[4]={0x3f8,0x2f8,0x3e8,0x2e8}; //!< List of the data ports for all the potential serial ports
#define data_port(com) (data_ports[com])
#define int_port(com) (data_port(com)+1)
#define fifo_port(com) (data_port(com)+2)
#define line_cmd_port(com) (data_port(com)+3)
#define modem_cmd_port(com) (data_port(com)+4)
#define line_stat_port(com) (data_port(com)+5)
#define scratch_port(com) (data_port(com)+7)
#define is_transmit_fifo_empty(com) (port_byte_in(line_stat_port(com))&0x20)
#define data_port(com) (data_ports[com]) //!< Returns the data port of a serial port
#define int_port(com) (data_port(com)+1) //!< Returns the interrupt config port of a serial port
#define fifo_port(com) (data_port(com)+2) //!< Returns the fifo config port of a serial port
#define line_cmd_port(com) (data_port(com)+3) //!< Returns the line cmd port of a serial port
#define modem_cmd_port(com) (data_port(com)+4) //!< Returns the modem cmd port of a serial port
#define line_stat_port(com) (data_port(com)+5) //!< Returns the line status port of a serial port
#define scratch_port(com) (data_port(com)+7) //!< Returns the scratch port of a serial port
#define is_transmit_fifo_empty(com) (port_byte_in(line_stat_port(com))&0x20) //!< Returns whether the trasmit FIFO is empty.
/**
* Configure a serial port with a specified baud rate.
* \param com The number of the serial port to configure
* \param rate The baud rate to set the serial port to.
*/
static void configure(int com, int rate) {
configured[com]=1;
port_byte_out(line_cmd_port(com),0x80); // Enable DLAB

View File

@ -1,29 +1,46 @@
/**
* \file
*/
#include "../../tasking.h"
#include "../paging.h"
#include "../tasking_helpers.h"
#include "../../pmem.h"
#include <stddef.h>
static void** kstacks=(void*)0xC8000000;
static char kstack_bmap[(218*1024)/8]={0};
static void** kstacks=(void*)0xC8000000; //!< Pointer to all the thread kernel stacks
static char kstack_bmap[(218*1024)/8]={0}; //!< Bitmap of what kernel stacks have been allocated
static char get_bmap_bit(size_t index) {
/**
* Check whether a kernel stack is allocated
* \param index The kernel stack to check
* \return whether the kernel stack is allocated
*/
static char is_kstack_allocated(size_t index) {
size_t byte=index/8;
size_t bit=index%8;
char entry=kstack_bmap[byte];
return (entry&(1<<bit))>0;
}
static void set_bmap_bit(size_t index) {
/**
* Mark that a kernel stack is allocated
* \param index The kernel stack to mark
*/
static void mark_kstack_allocated(size_t index) {
size_t byte=index/8;
size_t bit=index%8;
kstack_bmap[byte]=kstack_bmap[byte]|(1<<bit);
}
int new_kstack() {
/**
* Allocate a kernel stack for a thread
* \return The number of the new kernel stack, or -1 if none are unallocated.
*/
static int new_kstack() {
int num=-1;
for (int i=0;i<(218*1024);i++) {
if (get_bmap_bit(i)==0) {
if (is_kstack_allocated(i)==0) {
num=i;
break;
}
@ -31,7 +48,7 @@ int new_kstack() {
if (num==-1) {
return -1;
}
set_bmap_bit(num);
mark_kstack_allocated(num);
map_pages(((char*)kstacks+num*0x1000),pmem_alloc(1),1,1,1);
return num;
}

View File

@ -1,19 +1,46 @@
/**
* \file
*/
#ifndef ISR_H
#define ISR_H
#include <stdint.h>
/* Struct which aggregates many registers */
/**
* Saved state of the CPU when an interrupt occurs
*/
typedef struct {
uint32_t ds; /* Data segment selector */
uint32_t edi,esi,ebp,esp,ebx,edx,ecx,eax; /* Pushed by pusha. */
uint32_t int_no,err_code; /* Interrupt number and error code (if applicable) */
uint32_t eip,cs,eflags,useresp,ss; /* Pushed by the processor automatically */
uint32_t ds; //!< Data segment selector
uint32_t edi; //!< Pushed by pusha.
uint32_t esi; //!< Pushed by pusha.
uint32_t ebp; //!< Pushed by pusha.
uint32_t esp; //!< Pushed by pusha.
uint32_t ebx; //!< Pushed by pusha.
uint32_t edx; //!< Pushed by pusha.
uint32_t ecx; //!< Pushed by pusha.
uint32_t eax; //!< Pushed by pusha.
uint32_t int_no; //!< Interrupt number
uint32_t err_code; //!< Error code (if applicable)
uint32_t eip; //!< Pushed by the processor automatically
uint32_t cs; //!< Pushed by the processor automatically
uint32_t eflags; //!< Pushed by the processor automatically
uint32_t useresp; //!< Pushed by the processor automatically
uint32_t ss; //!< Pushed by the processor automatically
} registers_t;
typedef void (*isr_t)(registers_t*);
typedef void (*isr_t)(registers_t*); //!< Type of an ISR handler function pointer
/**
* Install the interrupt handlers into the IDT.
*/
void isr_install();
/**
* Register an IRQ handler
* \param n the IRQ to register a handler for
* \param handler the handler to register
*/
void isr_register_handler(int n,isr_t handler);
#endif

View File

@ -1,15 +1,68 @@
/**
* \file
*/
#ifndef PAGING_H
#define PAGING_H
/**
* Map virtual pages to physical frames.
* \param virt_addr_ptr The start of the virtual range to map.
* \param phys_addr_ptr The start of the physical range to map.
* \param num_pages The number of pages to map.
* \param usr Are the pages acessible by user mode code
* \param wr Are the pages writable by user mode code (kernel always has write permissions)
*/
void map_pages(void* virt_addr_ptr,void* phys_addr_ptr,int num_pages,char usr,char wr);
/**
* Unmap virtual pages,
* \param start_virt The start of the virtual range to unmap.
* \param num_pages The number of pages to map.
*/
void unmap_pages(void* start_virt,int num_pages);
/**
* Allocate virtual pages & map them to physical memory.
* \param num_pages The number of pages to allocate.
* \return a pointer to the allocated pages.
*/
void* alloc_pages(int num_pages);
/**
* Allocate virtual pages at a specific address & map them to physical memory.
* \param num_pages The number of pages to allocate.
* \param addr The adress to start allocation at.
*/
void alloc_pages_virt(int num_pages,void* addr);
/**
* Initialize paging
*/
void paging_init();
/**
* Create a new address space
* \return a pointer to the new address space in physical memory.
*/
void* paging_new_address_space();
/**
* Load an address space
* \param cr3 The address space to load
*/
void load_address_space(void* cr3);
/**
* Convert a virtual address to a physical one.
* \param virt_addr The virtual address to convert
* \return the physical adress it maps to, or NULL if it is not mapped.
*/
void* virt_to_phys(void* virt_addr);
/**
* Finds free virtual pages and returns the start address
* \param num_pages The minimum size of the free area
* \return the start of the free area
*/
void* find_free_pages(int num_pages);
/**
* Get the current address space
* \return a pointer to the current address space in physical memory.
*/
void* get_cr3();
#endif

View File

@ -1,9 +1,34 @@
/**
* \file
*/
#ifndef SERIAL_H
#define SERIAL_H
/**
* Initialize the serial driver
*/
void serial_init();
/**
* Write a character to the serial port
* \param c The character to write
*/
void serial_putc(char c);
void serial_write_string(const char* s); //Provided by platform-independent code
void serial_printf(const char* format,...); //Provided by platform-independent code
/**
* Write a string to the serial port
* \param s The string to write
* \note This function is provided by platform-independent code, a serial driver does not need to implement this.
*/
void serial_write_string(const char* s);
/**
* Printf, but to the serial port
* \param format The format string
* \param ... Arguments for the format string
* \note This function is provided by platform-independent code, a serial driver does not need to implement this.
*/
void serial_printf(const char* format,...);
#endif

View File

@ -1,11 +1,37 @@
/**
* \file
*/
#ifndef TASKING_HELPERS_H
#define TASKING_HELPERS_H
#include "../tasking.h"
/**
* The assembly part of switching to a thread. Performs the actual context switch.
* \param thread The thread to switch to.
*/
void switch_to_thread_asm(Thread* thread);
/**
* Initializes a usermode task
*/
void task_init();
/**
* An assembly helper for waiting for an unblocked thread
* Starts interrupts, halts, then clears interrupts.
*/
void wait_for_unblocked_thread_asm();
/**
* Setup a kernel stack for a thread
* \param thread The thread to setup a stack for
* \param param1 The thread's start function first parameter
* \param param2 The thread's start function second parameter
* \param kmode Whether the thread is a kernel mode thread
* \param eip The start address of the thread
*/
void setup_kstack(Thread* thread,void* param1,void* param2,char kmode,void* eip);
#endif

View File

@ -1,6 +1,5 @@
// #include "../tasking.h"
// #include "paging.h"
#include "../../drivers/vga.h"
#include "../halt.h"
#include "../isr.h"
#include "idt.h"
@ -170,7 +169,6 @@ void isr_handler(registers_t* r) {
break;
case 80:
// if (r->eax==1) {
// tss_stack_reset();
// tasking_yield();
// } else if (r->eax==2) {
// tasking_createTask((void*)r->ebx);

View File

@ -12,15 +12,18 @@
#include <string.h>
#include <tasking.h>
/**
* REspresents a TAR file header
*/
typedef struct {
char filename[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag[1];
char filename[100]; //!< Filename of file descried by the tar header
char mode[8]; //!< Mode as an octal string
char uid[8]; //!< UID of owner as an octal string
char gid[8]; //!< GID of owner as an octal string
char size[12]; //!< Size of file as an octal string
char mtime[12]; //!< Modification time as an octal string
char chksum[8]; //!< Checksum as octal string
char typeflag[1]; //!< File type. (0 for normal file)
} tar_header;
long initrd_sz;

View File

@ -1,14 +1,22 @@
/**
* \file
*/
#include "cpu/arch_consts.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define KMALLOC_BMAP_SZ (((KMALLOC_SZ*1024)/4)/8)
static char bitmap[KMALLOC_BMAP_SZ];
static void* data=(void*)KMALLOC_START;
#define KMALLOC_BMAP_SZ (((KMALLOC_SZ*1024)/4)/8) //!< The size of the kmalloc bitmap
static char bitmap[KMALLOC_BMAP_SZ]; //!< Bitmap of used areas of the heap
static void* data=(void*)KMALLOC_START; //!< Start of the kmalloc heap
/**
* Get a bit in the heap bitmap
* \param index The bit to get
* \return the bit
*/
static char get_bmap_bit(size_t index) {
size_t byte=index/8;
size_t bit=index%8;
@ -16,12 +24,20 @@ static char get_bmap_bit(size_t index) {
return (entry&(1<<bit))>0;
}
/**
* Set a bit in the heap bitmap
* \param index The bit to set
*/
static void set_bmap_bit(size_t index) {
size_t byte=index/8;
size_t bit=index%8;
bitmap[byte]=bitmap[byte]|(1<<bit);
}
/**
* Clear a bit in the heap bitmap
* \param index The bit to clear
*/
static void clear_bmap_bit(size_t index) {
size_t byte=index/8;
size_t bit=index%8;

View File

@ -1,9 +1,24 @@
/**
* \file
*/
#ifndef KMALLOC_H
#define KMALLOC_H
#include <stddef.h>
/**
* Allocate a block in the kernel heap
* \param size The size of the block
* \return the address of the block in the heap.
*/
void* kmalloc(size_t size);
/**
* Free a block in the kernel heap
* \param mem The address of the block
*/
void kfree(void* mem);
#endif

View File

@ -1,3 +1,7 @@
/**
* \file
*/
#include "cpu/arch_consts.h"
#include "cpu/halt.h"
#include "vga_err.h"
@ -6,10 +10,15 @@
#include <stdint.h>
#include <stdlib.h>
#define BMAP_LEN (NUM_FRAMES/8)
#define BMAP_LEN (NUM_FRAMES/8) //!< The size of the physical memory manager's bitmap
static char bmap[BMAP_LEN];
static char bmap[BMAP_LEN]; //!< Bitmap of allocated/non-present page frames
/**
* Get a bit in the bitmap
* \param index The bit to get
* \return the bit
*/
static char get_bmap_bit(int index) {
int byte=index/8;
int bit=index%8;
@ -17,12 +26,20 @@ static char get_bmap_bit(int index) {
return (entry&(1<<bit))>0;
}
/**
* Set a bit in the heap bitmap
* \param index The bit to set
*/
static void set_bmap_bit(int index) {
int byte=index/8;
int bit=index%8;
bmap[byte]=bmap[byte]|(1<<bit);
}
/**
* Clear a bit in the heap bitmap
* \param index The bit to clear
*/
static void clear_bmap_bit(int index) {
int byte=index/8;
int bit=index%8;

View File

@ -1,10 +1,29 @@
/**
* \file
*/
#ifndef PMEM_H
#define PMEM_H
#include <grub/multiboot2.h>
/**
* Initialize the physical memory manager
* \param tags The multiboot header
*/
void pmem_init(struct multiboot_boot_header_tag* tags);
/**
* Allocate physical frames
* \param num_pages The number of frames to allocate
* \return the physical address of the allocated frames
*/
void* pmem_alloc(int num_pages);
/**
* Free allocated physical frames
* \param start_page The frame to start freeing at.
* \param num_pages The number of frames to free
*/
void pmem_free(int start_page,int num_pages);
#endif

View File

@ -1,18 +1,16 @@
/**
* \file
*/
#ifndef RPC_H
#define RPC_H
/**
* Represents an RPC fumctiom
*/
typedef struct RPCFuncInfo {
char name[32];
void* (*code)(void*);
char name[32]; //!< THe name of the function
void* (*code)(void*); //!< A pointer to the code that implements the funtcion
} RPCFuncInfo;
typedef struct ThreadRPCStruct {
RPCFuncInfo funcs[32];
int num_funcs;
void* rpc_response;
} ThreadRPCStruct;
void rpc_init_struct(ThreadRPCStruct* info);
#endif

View File

@ -1,3 +1,7 @@
/**
* \file
*/
#include "cpu/halt.h"
#include "cpu/paging.h"
#include "cpu/serial.h"
@ -6,19 +10,24 @@
#include "tasking.h"
#include <sys/types.h>
#define MAX_PROCS 32768
#define HAS_UNBLOCKED_THREADS(proc) (proc->numThreads!=proc->numThreadsBlocked)
#define NUM_UNBLOCKED_THREADS(proc) (proc->numThreads-proc->numThreadsBlocked)
#define SAME_PROC(thread1,thread2) (thread1->process->pid==thread2->process->pid)
#define SAME_THREAD(thread1,thread2) (thread1->process->pid==thread2->process->pid&&thread1->tid==thread2->tid)
pid_t next_pid=0;
size_t num_procs=0;
Process* processes[MAX_PROCS];
char proc_schedule_bmap[MAX_PROCS/8];
Thread* currentThread;
static Thread* readyToRunHead=NULL;
static Thread* readyToRunTail=NULL;
#define MAX_PROCS 32768 //!< Maximum number of processes that can be running at a time
#define HAS_UNBLOCKED_THREADS(proc) (proc->numThreads!=proc->numThreadsBlocked) //!< Macro to check whethe a process has unblocked threads
#define NUM_UNBLOCKED_THREADS(proc) (proc->numThreads-proc->numThreadsBlocked) //!< Macro to get the number of unblocked threads for a process
#define SAME_PROC(thread1,thread2) (thread1->process->pid==thread2->process->pid) //!< Macro to check whether two threads have the same PID
#define SAME_THREAD(thread1,thread2) (thread1->process->pid==thread2->process->pid&&thread1->tid==thread2->tid) //!< Macro to check whether two threads have the same PID and TID
pid_t next_pid=0; //!< PID to use for the next created process
size_t num_procs=0; //!< Number of non-exited processes
Process* processes[MAX_PROCS]; //!< Array pf processes by PID
char proc_schedule_bmap[MAX_PROCS/8]; //!< Bitmap of what processes are scheduled
Thread* currentThread; //!< Currently running thread
static Thread* readyToRunHead=NULL; //!< Head of the linked list of ready to run threads
static Thread* readyToRunTail=NULL; //!< Tail of the linked list of ready to run threads
/**
* Check whether a process is scheduled
* \param index The PID to check
* \return whether the process is scheduled
*/
static char is_proc_scheduled(pid_t index) {
size_t byte=index/8;
size_t bit=index%8;
@ -26,6 +35,10 @@ static char is_proc_scheduled(pid_t index) {
return (entry&(1<<bit))>0;
}
/**
* Mark a process as scheduled
* \param index The PID to mark
*/
static void mark_proc_scheduled(pid_t index) {
if (is_proc_scheduled(index)) {
serial_printf("Attempt to schedule a thread in a process with a scheduled thread! (PID %d)\n",index);
@ -36,6 +49,10 @@ static void mark_proc_scheduled(pid_t index) {
proc_schedule_bmap[byte]=proc_schedule_bmap[byte]|(1<<bit);
}
/**
* Unmark a process as scheduled
* \param index The PID to unmark
*/
static void unmark_proc_scheduled(pid_t index) {
size_t byte=index/8;
size_t bit=index%8;
@ -146,6 +163,10 @@ pid_t tasking_new_thread(void* start,pid_t pid,char param_exists,void* param_arg
return processes[pid]->firstThread->tid;
}
/**
* Switch to a thread and schedule the next ready thread in the current process, if there is one.
* \param thread The thread to switch to
*/
void switch_to_thread(Thread* thread) {
// Unlink the thread from the list of ready-to-run threads
if (thread!=readyToRunHead) {

View File

@ -1,3 +1,7 @@
/**
* \file
*/
#ifndef KERN_TASKING_H
#define KERN_TASKING_H
@ -6,52 +10,109 @@
#ifndef TASKING_H
/**
* Represents the state of a thread
*/
typedef enum ThreadState {
THREAD_RUNNING,
THREAD_READY,
THREAD_EXITED,
THREAD_BLOCKED
THREAD_RUNNING, //!< The state of a running thread
THREAD_READY, //!< The state of a ready to run thread
THREAD_EXITED, //!< The state of an exited thread
THREAD_BLOCKED //!< The state of a generically blocked thread
} ThreadState;
#endif
struct Thread;
/**
* Represents a process
*/
typedef struct Process {
char priv;
pid_t pid;
pid_t next_tid;
int numThreads;
int numThreadsBlocked;
struct Thread* firstThread;
char priv; //!< Whether the process is privileged (can execute syscalls to acesss all of memory/has acess to IO ports).
pid_t pid; //!< The PID of this process
pid_t next_tid; //!< The TID that the next created thread will use.
int numThreads; //!< The number of threads in this process
int numThreadsBlocked; //!< The number of blocked threads in this process
struct Thread* firstThread; //!< A pointer to the head of the linked list of threads for this process.
} Process;
/**
* Represents a thread of a process
*/
typedef struct Thread {
void* kernel_esp;
void* kernel_esp_top;
void* cr3; //In thread to make the task switch asm easier
pid_t tid;
ThreadState state;
int errno;
struct Thread* nextThreadInProcess;
struct Thread* prevThreadInProcess;
struct Thread* nextReadyToRun;
struct Thread* prevReadyToRun;
Process* process;
void* kernel_esp; //!< The thread's kernel stack.
void* kernel_esp_top; //!< The top of the thread's kernel stack.
void* cr3; //!< The address space of this thread. (it is in here and not in the process to simplify the task switch asembly)
pid_t tid; //!< The TID of this thread.
ThreadState state; //!< The state of this thread. (running,ready to run,blocked,etc.)
int errno; //!< The errno value for this thread.
struct Thread* nextThreadInProcess; //!< The next thread in the process.
struct Thread* prevThreadInProcess; //!< The previous thread in the process.
struct Thread* nextReadyToRun; //!< If the thread is in the ready to run list, this is the next ready to run thread. (potentially in a different process)
struct Thread* prevReadyToRun; //!< If the thread is in the ready to run list, this is the previous ready to run thread. (potentially in a different process)
Process* process; //!< The thread's process.
} Thread;
extern Thread* currentThread;
/**
* Create a task
* \param eip The start address of the task
* \param cr3 The address space of the task
* \param kmode Whether the task is a kernel mode task
* \param param1_exists Whether param1_arg is a valid value
* \param param1_arg The thread's start function first parameter
* \param param2_exists Whether param2_arg is a valid value
* \param param2_arg The thread's start function second parameter/
* \param isThread Whether we are creating a new process or a thread in a process. If we are creating a theead, param2_arg becomes the PID for the newly created thread, and param2_exists must be 0.
*/
void tasking_createTask(void* eip,void* cr3,char kmode,char param1_exists,void* param1_arg,char param2_exists,void* param2_arg,char isThread);
/**
* Initialize tasking
*/
void tasking_init();
/**
* Check whether the current process is privleged
*/
char tasking_isPrivleged();
/**
* Get the PID of the current thread.
*/
pid_t tasking_getPID();
/**
* Get the adddress of errno for the current thread
*/
int* tasking_get_errno_address();
/**
* Create a new thread
* \param start The start address of the task
* \param pid The PID that gets the new thread
* \param param_exists Whether param_arg is a valid value
* \param param_arg The thread's start function parameter
* \return the TID of the thread
*/
pid_t tasking_new_thread(void* start,pid_t pid,char param_exists,void* param_arg);
/**
* Terminate the current thread
* If the main thread terminates, the whole process terminates.
* \note Currently, calling tasking_exit from any thread terminates the whole process.
*/
void tasking_exit(int code);
/**
* Block the current thread & yield
* \param newstate The state to block it in
*/
void tasking_block(ThreadState newstate);
/**
* Unblock a thread
* \param pid The PID that contains the thread to unblock
* \param tid The TID in the process to unblock.
*/
void tasking_unblock(pid_t pid,pid_t tid);
/**
* Yield to the next ready thread in any process
*/
void tasking_yield();
#endif

View File

@ -1,11 +1,20 @@
/**
* \file
*/
#include <cpu/ports.h>
#include <string.h>
#define VGA_BLACK 0
#define VGA_WHITE 15
static char* screen;
static int x=0;
#define VGA_BLACK 0 //!< The color black
#define VGA_WHITE 15 //!< The color white
static char* screen; //!< Pointer to VGA screen memory
static int x=0; //!< Next character offset in VGA sreen memory
/**
* Set a character on the screen
* \param x The character index to set
* \param c The character to write
*/
static void set_char(int x,char c) {
screen[x*2]=c;
screen[x*2+1]=(VGA_BLACK<<4)|VGA_WHITE;

View File

@ -1,7 +1,18 @@
/**
* \file
*/
#ifndef VGA_ERR_H
#define VGA_ERR_H
/**
* Initilaze the VGA error writing driver
*/
void vga_init(char* screen);
/**
* Write a string starting at the top line of the VGA display
*/
void vga_write_string(const char *string);
#endif