Major refactor. Major improvements.

All ISR/IRQ stuff moved into its own header and source.

Comments added on all major parts.

Some optimisations in important functions.

All ASM removed for ISR and IRQ, instead using new GCC directives.
This commit is contained in:
Curle 2019-06-27 19:57:54 +01:00
parent 022884e20d
commit 4edd4b7cc8
16 changed files with 942 additions and 415 deletions

View File

@ -81,141 +81,11 @@ load_gdt:
jmp 0x08:flush
flush:
ret
%macro ISR 1
global isr%1
isr%1:
cli
push byte 0
push byte %1
jmp isr_common
%endmacro
%macro ISR_ERR 1
global isr%1
isr%1:
cli
push byte %1
jmp isr_common
%endmacro
%macro IRQ 2
global irq%1
irq%1:
cli
push byte 0
push byte %2
jmp irq_common
%endmacro
ISR 0
ISR 1
ISR 2
ISR 3
ISR 4
ISR 5
ISR 6
ISR 7
ISR_ERR 8
ISR 9
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
ISR 15
ISR 16
ISR_ERR 17
ISR 18
ISR 19
ISR 20
ISR 21
ISR 22
ISR 23
ISR 24
ISR 25
ISR 26
ISR 27
ISR 28
ISR 29
ISR_ERR 30
ISR 31
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
[EXTERN fault_handler]
isr_common:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, fault_handler
call eax
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
[EXTERN irq_handler]
irq_common:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, irq_handler
call eax
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
[GLOBAL idt_load]
[EXTERN idtp]
idt_load:
lidt [eax]
lidt [idtp]
ret

View File

@ -1,10 +1,12 @@
#include <kernel/utils.h>
#include <kernel/descriptor_tables.h>
#include <kernel.h>
#include <kernel/serial.h>
#include <kernel/descriptor_tables.h>
size_t timer_ticks = 0;
size_t flag = 0;
void timer_handler(registers_t* r) {
void timer_handler(struct int_frame* r) {
gdb_end();
timer_ticks++;
if(timer_ticks % 18 == 0) {
@ -14,8 +16,11 @@ void timer_handler(registers_t* r) {
serial_print(0x3F8, "Tock.");
}
}
if(timer_ticks > 18)
timer_ticks = 0;
}
void timer_install() {
irq_install_handler(0, timer_handler);
irq_install_handler(0, &timer_handler);
}

12
include/kernel.h Normal file
View File

@ -0,0 +1,12 @@
/************************
*** Team Kitty, 2019 ***
*** ProjectRED ***
***********************/
#include <stdint.h>
#include <stddef.h>
#include <kernel/tty.h>
void panic(char*);
void gdb_end();

View File

@ -2,20 +2,62 @@
#include <stddef.h>
#include <stdint.h>
void gdt_set_gate(int, unsigned long, unsigned long, unsigned char, unsigned char);
void gdt_set_gate(int, uint32_t, uint32_t, uint8_t, uint8_t);
void gdt_install();
struct gdt_item {
uint16_t low_limit;
uint16_t low_base;
uint8_t middle_base;
uint8_t access;
uint8_t granular;
uint8_t high_base;
} __attribute__((packed)); //Prevent compiler optimisation by packing
struct gdt_ptr {
uint16_t limit;
unsigned int base;
} __attribute__((packed));
struct gdt_item gdt[3]; //3-entry gdt
struct gdt_ptr gp;
extern void load_gdt();
void idt_set_gate(unsigned char, unsigned long, unsigned short, unsigned char);
void idt_install();
typedef struct registers {
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
} registers_t;
struct idt_entry {
unsigned short base_low;
unsigned short selector;
unsigned char always0;
unsigned char flags;
unsigned short base_high;
} __attribute__((packed));
struct idt_ptr {
unsigned short limit;
unsigned int base;
}__attribute__((packed));
struct idt_entry idt[256]; //interrupt table of 256 entries
struct idt_ptr idtp; //pointer to idt table
struct int_frame {
void (*eip) (void);
uint16_t cs, :16;
uint32_t eflags;
void* esp;
uint16_t ss, :16;
} __attribute__((packed));
void isr_common(struct int_frame*, size_t);
void isr_error_common(struct int_frame*, size_t, size_t);
void irq_common(struct int_frame*, size_t);
extern void idt_load();
//These are all reserved by Intel, and need to be here.
extern void isr0();
@ -73,13 +115,13 @@ static void* irq_routines[16] = {
0, 0, 0, 0, 0, 0, 0, 0
};
void irq_install_handler(int irq, void (*handler)(registers_t* r));
void irq_install_handler(int irq, void (*handler)(struct int_frame* r));
void irq_uninstall_handler(int);
void irq_install();
void irq_handler(registers_t*);
void irq_remap();
void timer_install();
@ -116,4 +158,4 @@ static const char* exception_messages[] = {
"Reserved",
"Reserved",
"Reserved"
};
};

View File

@ -1,4 +0,0 @@
#pragma once
void gdt_set_gate(int, unsigned long, unsigned long, unsigned char, unsigned char);
void gdt_install();

View File

@ -1,19 +0,0 @@
#include <kernel/utils.h>
struct idt_entry {
unsigned short base_low;
unsigned short selector;
unsigned char always0;
unsigned char flags;
unsigned short base_high;
} __attribute__((packed));
struct idt_ptr {
unsigned short limit;
unsigned int base;
}__attribute__((packed));
struct idt_entry idt[256]; //interrupt table of 256 entries
struct idt_ptr idtp; //pointer to idt table
extern void idt_load();

278
include/kernel/multiboot.h Normal file
View File

@ -0,0 +1,278 @@
/* 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 */

View File

@ -2,20 +2,20 @@
#include <stdint.h>
#include <stddef.h>
extern void init_serial();
void init_serial();
extern void serial_set_baud_rate(uint16_t, uint16_t);
void serial_set_baud_rate(uint16_t, uint16_t);
extern void serial_configure_line(uint16_t);
void serial_configure_line(uint16_t);
extern void serial_configure_buffers(uint16_t);
void serial_configure_buffers(uint16_t);
extern void serial_write(uint16_t, const char);
void serial_write(uint16_t, const char);
extern void serial_configure_modem(uint16_t);
void serial_configure_modem(uint16_t);
extern int serial_check_tqueue(uint16_t);
int serial_check_tqueue(uint16_t);
extern void serial_print(uint16_t, const char*);
void serial_print(uint16_t, const char*);
extern void serial_printf(const char*, ...);
void serial_printf(uint16_t, const char*, ...);

257
kernel/descriptor_tables.c Executable file
View File

@ -0,0 +1,257 @@
/************************
*** Team Kitty, 2019 ***
*** ProjectRED ***
***********************/
/* This file combines the implementation
of IDT and GDT, Interrupt Descriptor
Table and Global Descriptor Table
respectively.
These are in the same file, because
otherwise the GDT file would be
roughly 10 lines long.
This file contains the code to set up
and configure the PIC, to configure and
load the IDT and GDT through the processor,
and other functions that are uniquely
related to the above tasks. */
#include <kernel/utils.h>
#include <kernel/descriptor_tables.h>
#include <kernel/serial.h>
/*
* The GDT gate setting function.
* Defines where the important parts of memory are.
*
* @param num: GDT position ID.
* @param base: Start of the memory block this entry is for.
* @param limit: End of the above memory block.
* @param access: The ring number associated with this entry.
* @param gran: Granularity - how big the assigned chunks of memory are.
*/
void gdt_set_gate(int num, uint32_t base,
uint32_t limit, uint8_t access,
uint8_t gran) {
/* The function is mostly shifts and masks, nothing too special. */
/* The gdt[] array is defined in the descriptor_tables header. */
gdt[num].low_base = (base & 0xFFFF);
gdt[num].middle_base = (base >> 16) & 0xFF;
gdt[num].high_base = (base >> 24) & 0xFF;
gdt[num].low_limit = (limit & 0xFFFF);
gdt[num].granular = ((limit >> 16) & 0x0F);
gdt[num].granular = (gran & 0xF0);
gdt[num].access = access;
}
/*
The function that puts it all together. It sets up the pointer,
fills it in, and tells the CPU to load the new GDT immediately.
*/
void gdt_install() {
/* gp = GDT Pointer, defined in descriptor_tables header. */
gp.limit = (sizeof(struct gdt_item) * 3) - 1;
gp.base = (uint32_t)&gdt;
/* The CPU requires that the first gate always be null. */
gdt_set_gate(0, 0, 0, 0, 0);
/* This is the Code Segment, it starts at 0 and ends at 4GB, meaning
that it encompasses the entirety of available memory.
This defines the code segment with exclusive execute permission,
allowing the kernel to execute itself. */
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
/* The Data Segment, the same range as the Code Segment (0 - 4GB)
but with Read/Write permission, allowing the kernel to modify RAM. */
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
/* Lastly, call the external ASM function that loads the GDT into the
processor. */
load_gdt();
}
/*
Identically to the GDT set gate function, but for the IDT array.
* @param num: IDT access ID
* @param base: Start of memory block
* @param sel: Selector. I have no idea what that means.
* @param flags: Permission levels and all that stuff.
*/
void idt_set_gate(unsigned char num,
unsigned long base,
unsigned short sel,
unsigned char flags) {
/* Again, it's all shifts and masks. Nothing special. */
idt[num].base_low = base & 0xFFFF;
idt[num].base_high = (base >> 16) & 0xFFFF;
idt[num].selector = sel;
idt[num].always0 = 0;
/* Set the permission level to 3. All interrupts are from ring 3. */
idt[num].flags = flags | 0x60;
}
/*
Same as GDT_install: sets up the pointers and fills in the contents, then passes
it on to the CPU to be loaded.
*/
void idt_install() {
/* idtp is the IDT Pointer, defined in the descriptor_tables header. */
idtp.limit = (sizeof (struct idt_entry) * 256) - 1;
idtp.base = (uint32_t) &idt;
memset(&idt, 0, sizeof(struct idt_entry) * 256);
/* Manually fill in the array - these functions are defined in
interrupts.c */
idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);
idt_set_gate(4, (unsigned)isr4, 0x08, 0x8E);
idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);
idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
idt_set_gate(12, (unsigned)isr12, 0x08, 0x8E);
idt_set_gate(13, (unsigned)isr13, 0x08, 0x8E);
idt_set_gate(14, (unsigned)isr14, 0x08, 0x8E);
idt_set_gate(15, (unsigned)isr15, 0x08, 0x8E);
idt_set_gate(16, (unsigned)isr16, 0x08, 0x8E);
idt_set_gate(17, (unsigned)isr17, 0x08, 0x8E);
idt_set_gate(18, (unsigned)isr18, 0x08, 0x8E);
idt_set_gate(19, (unsigned)isr19, 0x08, 0x8E);
idt_set_gate(20, (unsigned)isr20, 0x08, 0x8E);
idt_set_gate(21, (unsigned)isr21, 0x08, 0x8E);
idt_set_gate(22, (unsigned)isr22, 0x08, 0x8E);
idt_set_gate(23, (unsigned)isr23, 0x08, 0x8E);
idt_set_gate(24, (unsigned)isr24, 0x08, 0x8E);
idt_set_gate(25, (unsigned)isr25, 0x08, 0x8E);
idt_set_gate(26, (unsigned)isr26, 0x08, 0x8E);
idt_set_gate(27, (unsigned)isr27, 0x08, 0x8E);
idt_set_gate(28, (unsigned)isr28, 0x08, 0x8E);
idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
idt_load();
irq_install();
}
/* A simple wrapper that adds a function pointer to the IRQ array. */
void irq_install_handler(int irq, void (*handler)(struct int_frame* r)) {
irq_routines[irq] = handler;
}
/* A simple wrapper that unlinks a function pointer, rendering the IRQ unused. */
void irq_uninstall_handler(int irq) {
irq_routines[irq] = 0;
}
/*
Since the PIC starts with irq values that overlap with the CPU default ISR
lines, it's necessary to remap the PIC.
Doing this also means that we have to completely reinitialize the PIC, which
is why this is so long.
*/
void irq_remap() {
/* 0x20 is the Master PIC,
0xA0 is the Slave PIC. */
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x04);
outb(0xA1, 0x02);
outb(0x21, 0x01);
outb(0xA1, 0x01);
outb(0x21, 0x0);
outb(0xA1, 0x0);
}
/* A handler function to perform all of the required steps in one function call.
TODO: move all Descriptor Table things to a single callable function.
*/
void irq_install() {
irq_remap();
idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E);
idt_set_gate(33, (unsigned)irq1, 0x08, 0x8E);
idt_set_gate(34, (unsigned)irq2, 0x08, 0x8E);
idt_set_gate(35, (unsigned)irq3, 0x08, 0x8E);
idt_set_gate(36, (unsigned)irq4, 0x08, 0x8E);
idt_set_gate(37, (unsigned)irq5, 0x08, 0x8E);
idt_set_gate(38, (unsigned)irq6, 0x08, 0x8E);
idt_set_gate(39, (unsigned)irq7, 0x08, 0x8E);
idt_set_gate(40, (unsigned)irq8, 0x08, 0x8E);
idt_set_gate(41, (unsigned)irq9, 0x08, 0x8E);
idt_set_gate(42, (unsigned)irq10, 0x08, 0x8E);
idt_set_gate(43, (unsigned)irq11, 0x08, 0x8E);
idt_set_gate(44, (unsigned)irq12, 0x08, 0x8E);
idt_set_gate(45, (unsigned)irq13, 0x08, 0x8E);
idt_set_gate(46, (unsigned)irq14, 0x08, 0x8E);
idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E);
}
/* All of the ISR routines call this function for now.
! This function is NOT leaf, and it might clobber the stack.
! Be careful!
*/
void isr_common(struct int_frame* r, size_t exception) {
/* We only have the capacity to handle 32 exceptions. This is a limitation of the CPU. */
if(exception < 32) {
/* exception_messages is an array defined in descriptor_tables.h */
serial_print(0x3F8, exception_messages[exception]);
serial_print(0x3F8, " Exception.\r\n");
panic(exception_messages[exception]);
}
}
/* The common handler for exceptions that throw error codes, which give us useful insight
into what went wrong. In pure Curle style, though, we just ignore the error code. */
void isr_error_common(struct int_frame* r, size_t exception, size_t error) {
if(exception < 32) {
serial_print(0x3F8, exception_messages[exception]);
serial_printf(0x3F8, " Exception. Context given: %d\r\n", error);
panic(exception_messages[exception]);
}
}
/* Likewise, this function is common to all IRQ handlers. It calls the assigned routine,
which was set up earlier by irq_install.*/
void irq_common(struct int_frame* r, size_t interrupt) {
void (*handler)(struct int_frame* r);
serial_print(0x3F8, "[INFO] Received IRQ: " + interrupt);
/* We set all uninitialized routines to 0, so the if(handler) check here allows us to
safely tell whether we've actually got something for this IRQ. */
handler = irq_routines[interrupt];
if(handler)
handler(r);
/* The Slave PIC must be told it's been read in order to receive another 8+ IRQ. */
if(interrupt > 7)
outb(0xA0, 0x20);
/* In either case, we tell the Master PIC it's been read to receive any IRQ. */
outb(0x20, 0x20);
}

View File

@ -1,56 +0,0 @@
#include <kernel/utils.h>
#include <kernel/serial.h>
#include <stddef.h>
#include <stdint.h>
struct gdt_item {
uint16_t low_limit;
uint16_t low_base;
uint8_t middle_base;
uint8_t access;
uint8_t granular;
uint8_t high_base;
} __attribute__((packed)); //Prevent compiler optimisation by packing
struct gdt_ptr {
uint16_t limit;
unsigned int base;
} __attribute__((packed));
struct gdt_item gdt[3]; //3-entry gdt
struct gdt_ptr gp;
extern void load_gdt();
void gdt_set_gate(int num, uint32_t base,
uint32_t limit, uint8_t access,
uint8_t gran) {
//Implementation taken from osdever.net
gdt[num].low_base = (base & 0xFFFF);
gdt[num].middle_base = (base >> 16) & 0xFF;
gdt[num].high_base = (base >> 24) & 0xFF;
gdt[num].low_limit = (limit & 0xFFFF);
gdt[num].granular = ((limit >> 16) & 0x0F);
gdt[num].granular = (gran & 0xF0);
gdt[num].access = access;
}
void gdt_install() {
gp.limit = (sizeof(struct gdt_item) * 3) - 1;
gp.base = (unsigned int)&gdt;
gdt_set_gate(0, 0, 0, 0, 0); //NULL item
//Code Segment - base 0, 4KiB granularity, 4GiB limit
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
//Data Segment - ditto
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
//Apply changes
load_gdt();
}

View File

@ -1,142 +0,0 @@
#include <kernel/utils.h>
#include <kernel/descriptor_tables.h>
struct idt_entry {
unsigned short base_low;
unsigned short selector;
unsigned char always0;
unsigned char flags;
unsigned short base_high;
} __attribute__((packed));
struct idt_ptr {
unsigned short limit;
unsigned int base;
} __attribute__((packed));
struct idt_entry idt[256]; // Interrupt table of 256 entries
struct idt_ptr idtp; // Pointer to idt table
extern void idt_load();
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags) {
idt[num].base_low = base & 0xFFFF;
idt[num].base_high = (base >> 16) & 0xFFFF;
idt[num].selector = sel;
idt[num].always0 = 0;
// Must be uncommented when we get to usermode - it sets the permission
// level to 3
idt[num].flags = flags; // | 0x60;
}
void idt_install() {
idtp.limit = (sizeof (struct idt_entry) * 256) - 1;
idtp.base = (uint16_t) &idt;
memset(&idt, 0, sizeof(struct idt_entry) * 256);
idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);
idt_set_gate(4, (unsigned)isr4, 0x08, 0x8E);
idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);
idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
idt_set_gate(12, (unsigned)isr12, 0x08, 0x8E);
idt_set_gate(13, (unsigned)isr13, 0x08, 0x8E);
idt_set_gate(14, (unsigned)isr14, 0x08, 0x8E);
idt_set_gate(15, (unsigned)isr15, 0x08, 0x8E);
idt_set_gate(16, (unsigned)isr16, 0x08, 0x8E);
idt_set_gate(17, (unsigned)isr17, 0x08, 0x8E);
idt_set_gate(18, (unsigned)isr18, 0x08, 0x8E);
idt_set_gate(19, (unsigned)isr19, 0x08, 0x8E);
idt_set_gate(20, (unsigned)isr20, 0x08, 0x8E);
idt_set_gate(21, (unsigned)isr21, 0x08, 0x8E);
idt_set_gate(22, (unsigned)isr22, 0x08, 0x8E);
idt_set_gate(23, (unsigned)isr23, 0x08, 0x8E);
idt_set_gate(24, (unsigned)isr24, 0x08, 0x8E);
idt_set_gate(25, (unsigned)isr25, 0x08, 0x8E);
idt_set_gate(26, (unsigned)isr26, 0x08, 0x8E);
idt_set_gate(27, (unsigned)isr27, 0x08, 0x8E);
idt_set_gate(28, (unsigned)isr28, 0x08, 0x8E);
idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
idt_load();
irq_install();
}
void irq_install_handler(int irq, void (*handler)(registers_t* r)) {
irq_routines[irq] = handler;
}
void irq_uninstall_handler(int irq) {
irq_routines[irq] = 0;
}
void irq_remap() {
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x04);
outb(0xA1, 0x02);
outb(0x21, 0x01);
outb(0xA1, 0x01);
outb(0x21, 0x0);
outb(0xA1, 0x0);
}
void irq_install() {
irq_remap();
idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E);
idt_set_gate(33, (unsigned)irq1, 0x08, 0x8E);
idt_set_gate(34, (unsigned)irq2, 0x08, 0x8E);
idt_set_gate(35, (unsigned)irq3, 0x08, 0x8E);
idt_set_gate(36, (unsigned)irq4, 0x08, 0x8E);
idt_set_gate(37, (unsigned)irq5, 0x08, 0x8E);
idt_set_gate(38, (unsigned)irq6, 0x08, 0x8E);
idt_set_gate(39, (unsigned)irq7, 0x08, 0x8E);
idt_set_gate(40, (unsigned)irq8, 0x08, 0x8E);
idt_set_gate(41, (unsigned)irq9, 0x08, 0x8E);
idt_set_gate(42, (unsigned)irq10, 0x08, 0x8E);
idt_set_gate(43, (unsigned)irq11, 0x08, 0x8E);
idt_set_gate(44, (unsigned)irq12, 0x08, 0x8E);
idt_set_gate(45, (unsigned)irq13, 0x08, 0x8E);
idt_set_gate(46, (unsigned)irq14, 0x08, 0x8E);
idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E);
}
void fault_handler(registers_t *r) {
if(r->int_no < 32) {
serial_print(exception_messages[r->int_no], strlen(exception_messages[r->int_no]));
serial_print(" Exception. Halting.\r\n");
for(;;);
}
}
void irq_handler(registers_t *r) {
void (*handler)(registers_t* r);
serial_print(0x3F8, "[INFO] Received IRQ: " + r->int_no);
handler = irq_routines[r->int_no - 32];
if(handler)
handler(r);
if(r->int_no > 39)
outb(0xA0, 0x20);
outb(0x20, 0x20);
}

212
kernel/interrupts.c Normal file
View File

@ -0,0 +1,212 @@
/************************
*** Team Kitty, 2019 ***
*******ProjectRED*******
***********************/
/* This file contains all of the ISR and IRQ
* (Interrupt Service Request) functions.
*
* As they use the GCC interrupt attribute,
* this file must be compiled without red-
* zone protection, thus all of these
* functions are in their own file to
* accomodate this.
*
* Calling a function like so:
*
* __attribute__((interrupt)) isr1(registers_t* frame) {}
*
* allows the function to be used to serve
* interrupts - GCC compiles it under unique
* conditions, as it preserves the state of
* the processor and stack between execution,
* as well as using the IRET instruction to
* return to the middle of the previous function.
*
* There is also a version of the interrupt
* attribute which allows for error handlers,
* these having a size_t input as an error code.
*/
#include <stddef.h>
#include <stdint.h>
#include "kernel/descriptor_tables.h"
#ifdef __x86_64__
typedef unsigned long long int uword_t;
#else
typedef unsigned int uword_t;
#endif
/* The interrupt numbers, their meanings, and
* special information is laid out below:
*
* 0 - Divide by Zero
* 1 - Debug
* 2 - Non-Maskable
* 3 - Breakpoint
* 4 - Into Detected Overflow
* 5 - Out of Bounds
* 6 - Invalid Opcode
* 7 - No Coprocessor
* 8 - Double Fault * (With Error)
* 9 - Coprocessor Segment Overrun
* 10 - Bad TSS * (With Error)
* 11 - Segment Not Present * (With Error)
* 12 - Stack Fault * (With Error)
* 13 - General Protection Fault * (With Error)
* 14 - Page Fault * (With Error)
* 15 - Unknown Interrupt
* 16 - Coprocessor Fault
* 17 - Alignment Check
* 18 - Machine Check
* 19 to 31 - Reserved
*/
__attribute__((interrupt)) void isr0(struct int_frame* r) {
isr_common(r, 0);
}
__attribute__((interrupt)) void isr1(struct int_frame* r) {
isr_common(r, 1);
}
__attribute__((interrupt)) void isr2(struct int_frame* r) {
isr_common(r, 2);
}
__attribute__((interrupt)) void isr3(struct int_frame* r) {
isr_common(r, 3);
}
__attribute__((interrupt)) void isr4(struct int_frame* r) {
isr_common(r, 4);
}
__attribute__((interrupt)) void isr5(struct int_frame* r) {
isr_common(r, 5);
}
__attribute__((interrupt)) void isr6(struct int_frame* r) {
isr_common(r, 6);
}
__attribute__((interrupt)) void isr7(struct int_frame* r) {
isr_common(r, 7);
}
__attribute__((interrupt)) void isr8(struct int_frame* r, size_t error) {
isr_error_common(r, 8, error);
}
__attribute__((interrupt)) void isr9(struct int_frame* r) {
isr_common(r, 9);
}
__attribute__((interrupt)) void isr10(struct int_frame* r, size_t error) {
isr_error_common(r, 10, error);
}
__attribute__((interrupt)) void isr11(struct int_frame* r, size_t error) {
isr_error_common(r, 11, error);
}
__attribute__((interrupt)) void isr12(struct int_frame* r, size_t error) {
isr_error_common(r, 12, error);
}
__attribute__((interrupt)) void isr13(struct int_frame* r, size_t error) {
isr_error_common(r, 13, error);
}
__attribute__((interrupt)) void isr14(struct int_frame* r, size_t error) {
isr_error_common(r, 14, error);
}
__attribute__((interrupt)) void isr15(struct int_frame* r) {
isr_common(r, 15);
}
__attribute__((interrupt)) void isr16(struct int_frame* r) {
isr_common(r, 16);
}
__attribute__((interrupt)) void isr17(struct int_frame* r) {
isr_common(r, 17);
}
__attribute__((interrupt)) void isr18(struct int_frame* r) {
isr_common(r, 18);
}
__attribute__((interrupt)) void isr19(struct int_frame* r) {
isr_common(r, 19);
}
__attribute__((interrupt)) void isr20(struct int_frame* r) {
isr_common(r, 20);
}
__attribute__((interrupt)) void isr21(struct int_frame* r) {
isr_common(r, 21);
}
__attribute__((interrupt)) void isr22(struct int_frame* r) {
isr_common(r, 22);
}
__attribute__((interrupt)) void isr23(struct int_frame* r) {
isr_common(r, 23);
}
__attribute__((interrupt)) void isr24(struct int_frame* r) {
isr_common(r, 24);
}
__attribute__((interrupt)) void isr25(struct int_frame* r) {
isr_common(r, 25);
}
__attribute__((interrupt)) void isr26(struct int_frame* r) {
isr_common(r, 26);
}
__attribute__((interrupt)) void isr27(struct int_frame* r) {
isr_common(r, 27);
}
__attribute__((interrupt)) void isr28(struct int_frame* r) {
isr_common(r, 28);
}
__attribute__((interrupt)) void isr29(struct int_frame* r) {
isr_common(r, 29);
}
__attribute__((interrupt)) void isr30(struct int_frame* r) {
isr_common(r, 30);
}
__attribute__((interrupt)) void isr31(struct int_frame* r) {
isr_common(r, 31);
}
__attribute__((interrupt)) void irq0(struct int_frame* r) {
irq_common(r, 0);
}
__attribute__((interrupt)) void irq1(struct int_frame* r) {
irq_common(r, 1);
}
__attribute__((interrupt)) void irq2(struct int_frame* r) {
irq_common(r, 2);
}
__attribute__((interrupt)) void irq3(struct int_frame* r) {
irq_common(r, 3);
}
__attribute__((interrupt)) void irq4(struct int_frame* r) {
irq_common(r, 4);
}
__attribute__((interrupt)) void irq5(struct int_frame* r) {
irq_common(r, 5);
}
__attribute__((interrupt)) void irq6(struct int_frame* r) {
irq_common(r, 6);
}
__attribute__((interrupt)) void irq7(struct int_frame* r) {
irq_common(r, 7);
}
__attribute__((interrupt)) void irq8(struct int_frame* r) {
irq_common(r, 8);
}
__attribute__((interrupt)) void irq9(struct int_frame* r) {
irq_common(r, 9);
}
__attribute__((interrupt)) void irq10(struct int_frame* r) {
irq_common(r, 10);
}
__attribute__((interrupt)) void irq11(struct int_frame* r) {
irq_common(r, 11);
}
__attribute__((interrupt)) void irq12(struct int_frame* r) {
irq_common(r, 12);
}
__attribute__((interrupt)) void irq13(struct int_frame* r) {
irq_common(r, 13);
}
__attribute__((interrupt)) void irq14(struct int_frame* r) {
irq_common(r, 14);
}
__attribute__((interrupt)) void irq15(struct int_frame* r) {
irq_common(r, 15);
}

View File

@ -1,5 +0,0 @@
#include <kernel/descriptor_tables.h>
#include <kernel/tty.h>
#include <kernel/utils.h>
#include <stdint.h>

View File

@ -1,15 +1,13 @@
/////////////////////////////
/// ///
/// Team Kitty, 2019 ///
/// ProjectRED ///
/// ///
/////////////////////////////
/************************
*** Team Kitty, 2019 ***
*** ProjectRED ***
***********************/
//#include <stdio.h>
#include <kernel.h>
#include <kernel/tty.h>
#include <kernel/descriptor_tables.h>
#include <kernel/serial.h>
#include <kernel/descriptor_tables.h>
void gdb_end() {} /* GDB Debugging stump */
@ -77,9 +75,9 @@ int kernel_main(void) {
/* A stub causing a Divide by Zero error. */
//serial_print(0x3F8, "[DEBUG] Attempting a Divide by Zero error.\r\n");
//char div = (5 / 0);
//serial_print(0x3F8, "[DEBUG] Survived the error!\r\n");
serial_print(0x3F8, "[DEBUG] Attempting a Divide by Zero error.\r\n");
char div = (5 / 0);
serial_print(0x3F8, "[DEBUG] Survived the error!\r\n");
gdb_end(); /* Everything is done. The last debug routine. */
return 0;

View File

@ -1,9 +1,19 @@
/************************
*** Team Kitty, 2019 ***
*** ProjectRED ***
***********************/
#include <kernel/utils.h>
#include <kernel/tty.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/*
Returns the length of a given string.
* @param string: String to count
*/
size_t strlen(const char* string) {
size_t size = 0;
while (string[size])
@ -11,16 +21,33 @@ size_t strlen(const char* string) {
return size;
}
unsigned char inb(unsigned short port) {
unsigned char ptr;
/*
Read data from a port. Effectively communicates with hardware.
* @param port: The port number to read, as a hex short.
*/
uint8_t inb(uint16_t port) {
uint8_t ptr;
asm volatile("inb %1, %0" : "=a" (ptr) : "dN" (port));
return ptr;
}
void outb(unsigned short port, unsigned char data) {
/*
Write data to a port. Effectively communicates with hardware.
* @param port: The port to read
* @param data: A pointer to which the data will be stored.
*/
void outb(uint16_t port, uint8_t data) {
asm volatile("outb %1, %0" : : "dN" (port), "a" (data));
}
/*
Memory Copy. Required by GCC.
* @param dest: The destination in memory, to which the data will be copied.
* @param src: The source of the data which will be copied.
* @param n: The length of the copy, in byets.
*/
void memcpy(void* dest, void* src, size_t n) {
char* src_c = (char*)src;
char* dest_c = (char*)dest;
@ -30,16 +57,29 @@ void memcpy(void* dest, void* src, size_t n) {
}
}
/*
Memory Set. Required by GCC>
* @param src: The data to be overwritten.
* @param chr: The byte to overwrite the source with.
* @param n: How many bytes to overwrite.
*/
void memset(void* src, int chr, size_t n) {
unsigned char* ptr = src;
uint8_t* ptr = src;
while(n--) {
*ptr++ = (unsigned char) chr;
*ptr++ = (uint8_t) chr;
}
}
/*
Turns an integer into a C-str.
* @note Inefficient and unsafe.
* @param num: The number to convert
* @param string: The string to be written into.
*/
void int_to_ascii(int num, char* string) {
size_t i = 0; //Counter.
//TODO: Convert this to a for-loop?
// TODO: Convert this to a for-loop?
int32_t calc = 0;
bool negative = false;
@ -49,32 +89,40 @@ void int_to_ascii(int num, char* string) {
return;
}
// TODO: Implement this as an abs() function?
if(num < 0) {
negative = true;
num = -num;
}
while(num != 0) {
calc = num % 10;
num = num / 10;
calc += 48;
string[i] = calc;
i++;
}
if(negative) {
string[i] = '-';
i++;
}
while(num != 0) {
// Overall this looks pretty confusing.
// TODO: cleanup?
calc = num % 10; // Gets the lowest digit,
num = num / 10; // Shifts the other digits down to prepare for the next iteration.
calc += 48; // Convert the number to an ASCII value
string[i] = calc;// Set the current position in the string
i++; // Increase for the next number.
}
// ! This code does weird things.
// It serves an unknown purpose, and should not be used, but is left here just in case.
/*
for(size_t j = 0; j < i/2; j++) {
calc = string[j];
string[j] = string[i-j-1];
string[j-i-1] = calc;
}
*/
}
/*
A strange version of the above function. Different way of doing the same thing?
* @param num: the number to convert. Must be positive
* @retval The new string.
*/
char* itoc(size_t num) {
char* result;
size_t tmp_value;
@ -87,6 +135,12 @@ char* itoc(size_t num) {
return result;
}
/*
Same as itoa, but for hexadecimal.
* @param num: Number to convert
* @param string: String pointer to put the converted number.
*/
void int_to_hex(int num, char* string) {
empty_string(string);
@ -107,6 +161,11 @@ void int_to_hex(int num, char* string) {
}
}
/*
Converts a C-str to an empty C-str. :)
* @param string: The string to empty.
*/
void empty_string(char* string) {
size_t len = strlen(string);
@ -114,3 +173,20 @@ void empty_string(char* string) {
string[i] = '\0';
}
}
/*
The signal that everything has gone wrong.
Equivalent to a linux kernel panic and a Windows Blue Screen.
TODO: Needs to be re-engineered to give as much useful information as possible. Ideally also have a visual interface, XP installer style.
* @param cause: A string, telling the basic reason for the crash.
*/
void panic(char* cause) {
term_writes("\n\n>>>> PANIC <<<<\nCaused by: ");
term_writes(cause);
for(;;);
}

View File

@ -1,8 +1,9 @@
DEFAULT_HOST:=i686-elf
HOST?=DEFAULT_HOST
HOSTARCH:=i386
CC:=$(PREFIX)/bin/$(DEFAULT_HOST)-gcc
CFLAGS?= -O2 -g -m32 -fno-pie -fno-stack-protector -Wl,--build-id=none
CFLAGS?= -O0 -g -fno-pie -fno-stack-protector -Wl,--build-id=none
CPPFLAGS?=
LDFLAGS?=
LIBS?=
@ -31,8 +32,8 @@ KERNEL_OBJS= \
$(KERNEL_ARCH_OBJS) \
kernel/utils.o \
kernel/serial.o \
kernel/gdt.o \
kernel/idt.o \
kernel/interrupts.o \
kernel/descriptor_tables.o\
kernel/kernel.o
OBJS=\
@ -51,6 +52,9 @@ all: red.kernel
red.kernel: $(OBJS) $(ARCHDIR)/linker.ld
$(CC) -T $(ARCHDIR)/linker.ld -o $@ $(CFLAGS) $(LINK_LIST)
kernel/interrupts.o:
$(CC) -MD -c kernel/interrupts.c -o $@ -std=gnu11 $(CFLAGS) -mno-red-zone -mgeneral-regs-only $(CPPFLAGS)
.c.o:
$(CC) -MD -c $< -o $@ -std=gnu11 $(CFLAGS) $(CPPFLAGS)
.s.o:
@ -85,4 +89,3 @@ gen-iso:
iso
-include $(OBJS:.o=.d)