diff --git a/include/Tier0/acpi.h b/include/Tier0/acpi.h new file mode 100644 index 0000000..46468fe --- /dev/null +++ b/include/Tier0/acpi.h @@ -0,0 +1,44 @@ +#include "types.h" + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#define ACPI_VERSION_UNK 0 +#define ACPI_VERSION_10 1 +#define ACPI_VERSION_20 2 + +#define ACPI_10_RSDP_SIZE 20 +#define ACPI_20_RSDP_SIZE 16 + +struct S_ACPI_RSDP { + s8 Magic[8]; + u8 Checksum; + s8 OEMID[6]; + u8 Revision; + u32 RSDTAddress; + + // Additional ACPI 2.0 fields + u32 Length; + u32 RSDTAddressExLow; + u32 RSDTAddressExHigh; + u8 ChecksumEx; + u8 Reserved[3]; +} __attribute__((packed)); +typedef struct S_ACPI_RSDP T_ACPI_RSDP; + +struct S_ACPI_SDT_HEADER { + s8 Signature[4]; + u32 Length; + u8 Revision; + u8 Checksum; + s8 OEMID[6]; + s8 OEMTableID[8]; + u32 OEMRevision; + u32 CreatorID; + u32 CreatorRevision; +} __attribute__ ((packed)); +typedef struct S_ACPI_SDT_HEADER T_ACPI_SDT_HEADER; + +u32 acpi_find_rsdp(void); + +#endif diff --git a/include/Tier0/interrupts.h b/include/Tier0/interrupts.h new file mode 100644 index 0000000..7f770fc --- /dev/null +++ b/include/Tier0/interrupts.h @@ -0,0 +1,52 @@ +#include "types.h" + +struct S_IDT_PTR { + u16 Limit; + u32 Base; +} __attribute__ ((packed)); +typedef struct S_IDT_PTR T_IDT_PTR; + +struct S_IDT_ENTRY { + u16 OffsetLow; + u16 Selector; + u8 Zero; + u8 Type; + u16 OffsetHigh; +} __attribute__ ((packed)); +typedef struct S_IDT_ENTRY T_IDT_ENTRY; + +enum E_INTERRUPTS_RING { + E_INTERRUPTS_RING0 = 0, + E_INTERRUPTS_RING1, + E_INTERRUPTS_RING2, + E_INTERRUPTS_RING3 +}; +typedef enum E_INTERRUPTS_RING T_INTERRUPTS_RING; + +enum E_INTERRUPTS_CHIP { + E_INTERRUPTS_CHIP_UNK, + E_INTERRUPTS_CHIP_PIC, + E_INTERRUPTS_CHIP_APIC +}; +typedef enum E_INTERRUPTS_CHIP T_INTERRUPTS_CHIP; + +// This is a structure that allows easy access to a 12-byte ASM stub which +// calls a stdcall handler. Not the best way and not the shortest stub, +// but hey, it works. +struct S_ISR_STUB { + u16 Code1; + u8 Code2; + u32 Handler; + u32 Code3; + u8 Code4; +} __attribute__ ((packed)); +typedef struct S_ISR_STUB T_ISR_STUB; + +u8 interrupts_init_idt(void); +void interrupts_setup_irq(u8 IRQ, void *Handler); +void interrupts_setup_isr(u8 Interrupt, void *Handler, T_INTERRUPTS_RING Ring); +void interrupts_init_simple(void); +void interrupts_interrupt_finish(u8 IRQ); +void interrupts_lidt(void); +void interrupts_dump_idt_entry(u8 Interrupt); +void interrupts_dump_idt_ptr(void); diff --git a/include/Tier0/kstdio.h b/include/Tier0/kstdio.h index 721c45c..f1641fd 100644 --- a/include/Tier0/kstdio.h +++ b/include/Tier0/kstdio.h @@ -4,6 +4,8 @@ #include "types.h" void koutb(u16 Port, u8 Data); +u8 kinb(u16 Port); +void kio_wait(void); u32 kstrlen(s8 *szString); void kmove_cursor(u8 X, u8 Y); void kputs(s8 *szString); @@ -14,5 +16,6 @@ void kputi(s32 Number); void kprintf(s8 *Format, ...); void kdump(u8 *bData, u32 Length); void kprint_hex(u32 Number); +s32 kmemcmp(u8 *MemA, u8 *MemB, u32 Length); #endif diff --git a/include/Tier0/paging.h b/include/Tier0/paging.h index e6057e5..6b739b6 100644 --- a/include/Tier0/paging.h +++ b/include/Tier0/paging.h @@ -1,6 +1,8 @@ #ifndef __PAGING_H__ #define __PAGING_H__ -void paging_init_simple(void); +#include "types.h" +void paging_init_simple(void); +u8 paging_get_physical(u32 Virtual, u32 *Physical); #endif diff --git a/include/Tier0/pic.h b/include/Tier0/pic.h new file mode 100644 index 0000000..9eb582a --- /dev/null +++ b/include/Tier0/pic.h @@ -0,0 +1,20 @@ +#ifndef __PIC_H__ +#define __PIC_H__ + +#include "types.h" + +#define PIC_1_ADDR 0x20 +#define PIC_1_DATA 0x21 +#define PIC_1_START 0xF0 + +#define PIC_2_ADDR 0xA0 +#define PIC_2_DATA 0xA1 +#define PIC_2_START 0xF8 + +#define PIC_IRQ_START 0xF0 + +void pic_init(u8 *OldMask1, u8 *OldMask2); +void pic_eoi(u8 IRQ); +void pic_unmask_irq(u8 IRQ); + +#endif diff --git a/src/Tier0/_start.asm b/src/Tier0/_start.asm index c5e8759..d824bbe 100644 --- a/src/Tier0/_start.asm +++ b/src/Tier0/_start.asm @@ -45,7 +45,6 @@ higherhalf: mov esp, kstack + STACKSIZE push eax - push ebx call kmain diff --git a/src/Tier0/acpi.c b/src/Tier0/acpi.c new file mode 100644 index 0000000..29297de --- /dev/null +++ b/src/Tier0/acpi.c @@ -0,0 +1,66 @@ +#include "Tier0/acpi.h" +#include "Tier0/kstdio.h" + +u8 g_acpi_version = 0; +u32 g_acpi_rsdt_address = 0; + +u32 acpi_find_rsdp(void) +{ + s8 *szMagic = "RSD PTR "; + + // Try to find the pointer... apparently it's 16byte-aligned... + + kprintf("[i] Looking for the RSDP...\n"); + u32 Address = 0; + for (u32 Search = 0x000E0000; Search <= 0x000FFFFF; Search += 4) + { + if (kmemcmp((u8 *)Search, (u8 *)szMagic, 8) == 0) + { + Address = Search; + break; + } + } + + if (Address == 0) + return 0; + + T_ACPI_RSDP *RSDP = (T_ACPI_RSDP *)Address; + + u8 ChecksumVerify = 0; + for (u8 i = 0; i < ACPI_10_RSDP_SIZE; i++) + ChecksumVerify += ((u8*)Address)[i]; + + if (ChecksumVerify != 0) + { + kprintf("[e] ACPI checksum failed. Memory failure? Weird PC? We may never " \ + "know ...\n"); + return 0; + } + + if (RSDP->Revision == 1) + { + // ACPI 2.0... + ChecksumVerify = 0; + for (u8 i = ACPI_10_RSDP_SIZE; i < ACPI_20_RSDP_SIZE; i++) + ChecksumVerify += ((u8*)Address)[i]; + + if (ChecksumVerify != 0) + { + kprintf("[e] ACPI 2.0 checksum failed. Yet 1.0 succeeded? WTF.\n"); + return 0; + } + } + + g_acpi_version = RSDP->Revision + 1; + + kprintf("[i] Detected ACPI version %i.0.\n", RSDP->Revision + 1); + if (*RSDP->OEMID != 0) + kprintf("[i] OEMID %s.\n", RSDP->OEMID); + + if (g_acpi_version == ACPI_VERSION_10) + g_acpi_rsdt_address = Address; + else + g_acpi_rsdt_address = RSDP->RSDTAddressExLow; + + return Address; +} diff --git a/src/Tier0/cpp.c b/src/Tier0/cpp.c new file mode 100644 index 0000000..742bdf6 --- /dev/null +++ b/src/Tier0/cpp.c @@ -0,0 +1 @@ +// Basically stuff that is needed to go into C++ Land diff --git a/src/Tier0/paging.asm b/src/Tier0/gdt.asm similarity index 100% rename from src/Tier0/paging.asm rename to src/Tier0/gdt.asm diff --git a/src/Tier0/interrupts.asm b/src/Tier0/interrupts.asm new file mode 100644 index 0000000..59d73f1 --- /dev/null +++ b/src/Tier0/interrupts.asm @@ -0,0 +1,9 @@ +BITS 32 +section .text + +global interrupts_lidt +extern g_idt_ptr + +interrupts_lidt: + lidt [g_idt_ptr] + ret diff --git a/src/Tier0/interrupts.c b/src/Tier0/interrupts.c new file mode 100644 index 0000000..cdd4188 --- /dev/null +++ b/src/Tier0/interrupts.c @@ -0,0 +1,139 @@ +#include "Tier0/interrupts.h" +#include "Tier0/paging.h" +#include "Tier0/kstdio.h" +#include "Tier0/pic.h" + +T_IDT_PTR g_idt_ptr; +T_IDT_ENTRY g_idt_entries[256]; +T_ISR_STUB g_isr_stubs[256]; + +T_INTERRUPTS_CHIP g_interrupts_chip = E_INTERRUPTS_CHIP_UNK; + +void interrupts_set_chip(T_INTERRUPTS_CHIP Chip) +{ + g_interrupts_chip = Chip; + + if (Chip == E_INTERRUPTS_CHIP_UNK) + kprintf("[i] Interrupts: Turning off.\n"); + else if (Chip == E_INTERRUPTS_CHIP_PIC) + kprintf("[i] Interrupts: Switching to 8259 based interrupts.\n"); + else if (Chip == E_INTERRUPTS_CHIP_APIC) + kprintf("[i] Interrupts: Switching to intel I/O APIC based " + "interrupts.\n"); +} + +u8 interrupts_init_idt(void) +{ + g_idt_ptr.Limit = 256 * 8; + + u32 Physical = 0; + u8 Result = paging_get_physical((u32)g_idt_entries, &Physical); + if (!Result) + return 0; + + kprintf("[i] Setting up IDT at 0x%x (0x%x Virtual).\n", Physical, g_idt_entries); + + g_idt_ptr.Base = Physical; + + // Null those entries! + for (u16 i = 0; i < 256; i++) + { + // Maybe I should access the struct's members... + // Or i can just cast that to to u32's and null them. + // This will set the Present flag to 0 either way + *((u32 *)(&g_idt_entries[i])) = 0; + *(((u32 *)(&g_idt_entries[i]) + 1)) = 0; + } + + return 1; +} + +// This creates a 12-byte ASM stub for a handler +void interrupts_create_stub(T_ISR_STUB *Destination, u32 Address) +{ + // The ASM code is as follows: + // cli + // pushad + // mov eax, Handler + // call eax + // popad + // sti + // iret + Destination->Code1 = 0x60FA; // pishad, cli + Destination->Code2 = 0xB8; // mov eax, + Destination->Handler = Address; // Address + Destination->Code3 = 0xFB61D0FF; // sti, popad, call eax + Destination->Code4 = 0xCF; // iret +} + +void interrupts_setup_irq(u8 IRQ, void *Handler) +{ + if (g_interrupts_chip != E_INTERRUPTS_CHIP_PIC) + { + kprintf("[e] Sorry, but I only do PIC-based interrupts for now :(.\n"); + return; + } + + u8 Interrupt = IRQ + PIC_IRQ_START; + interrupts_setup_isr(Interrupt, Handler, E_INTERRUPTS_RING0); + + // We also need to set the IRQ mask + pic_unmask_irq(IRQ); +} + +void interrupts_dump_idt_ptr(void) +{ + kprintf("[i] IDT Pointer structure:\n"); + kprintf(" Base: 0x%x.\n", g_idt_ptr.Base); + kprintf(" Limit: 0x%x.\n", g_idt_ptr.Limit); +} + +void interrupts_dump_idt_entry(u8 Interrupt) +{ + u32 *dwEntry = (u32 *)&g_idt_entries[Interrupt]; + kprintf("[i] IDT Entry for interrupt %x:\n", Interrupt); + kprintf(" DW Low : 0x%x.\n", *dwEntry); + kprintf(" DW High: 0x%x.\n", *(dwEntry + 1)); + + T_IDT_ENTRY Entry = g_idt_entries[Interrupt]; + kprintf(" Offset: 0x%x.\n", (Entry.OffsetHigh << 16) + Entry.OffsetLow); + kprintf(" Selector: %u.\n", Entry.Selector); + kprintf(" Zero: %u.\n", Entry.Zero); + kprintf(" P: %u.\n", (Entry.Type >> 7) & 0b1); + kprintf(" DPL: %u.\n", (Entry.Type >> 5) & 0b11); + kprintf(" S: %u.\n", (Entry.Type >> 4) & 0b1); + kprintf(" Gate type: %u.\n", Entry.Type & 0b1111); +} + +void interrupts_setup_isr(u8 Interrupt, void *Handler, T_INTERRUPTS_RING Ring) +{ + interrupts_create_stub(&g_isr_stubs[Interrupt], (u32)Handler); + + u32 ASMHandler = (u32)&g_isr_stubs[Interrupt]; + g_idt_entries[Interrupt].OffsetLow = ASMHandler & 0xFFFF; + g_idt_entries[Interrupt].OffsetHigh = (ASMHandler >> 16) & 0xFFFF; + g_idt_entries[Interrupt].Selector = 0x08; // Second segment, code. + g_idt_entries[Interrupt].Zero = 0; + + u8 Type = 0; + Type |= (1 << 7); // Present = 1 + Type |= (Ring << 5); // DPL = Ring + Type |= (0 << 4); // Storage = 0 + Type |= 0xE; // Type = 0xE (32-bit interrupt gate) + g_idt_entries[Interrupt].Type = Type; +} + +void interrupts_init_simple(void) +{ + interrupts_set_chip(E_INTERRUPTS_CHIP_PIC); + interrupts_init_idt(); + interrupts_lidt(); + + pic_init(0, 0); +} + +void interrupts_interrupt_finish(u8 IRQ) +{ + if (g_interrupts_chip == E_INTERRUPTS_CHIP_PIC) + pic_eoi(IRQ); +} diff --git a/src/Tier0/kmain.c b/src/Tier0/kmain.c index a6ad8bf..a529034 100644 --- a/src/Tier0/kmain.c +++ b/src/Tier0/kmain.c @@ -2,15 +2,28 @@ #include "Tier0/kstdio.h" #include "Tier0/gdt.h" #include "Tier0/paging.h" +#include "Tier0/acpi.h" +#include "Tier0/interrupts.h" + + +// Just to see whether this stuff actually works +void sample_interrupt_0x2A(void) +{ + kprintf("[i] Hello from ISR for interrupt 0x2A!\n"); + return; +} // Real kernel entry point, called from _start.asm -void kmain(void *mbd, u32 magic) +void kmain(u32 Magic) { kclear(); + kprintf(" _ \n" + " ___ _ _ ___ _ _ _____| |_ ___ ___ \n" + " | _| | | _| | | | . | -_| _|\n" + " |___|___|___|___|_|_|_|___|___|_| \n\n"); kprintf("[i] Welcome to Cucumber!\n\n"); - kprintf("[i] Magic from bootloader: 0x%x.\n", magic); - if (magic != 0x2BADB002) + if (Magic != 0x2BADB002) { kprintf("[e] Fatal! Boot via incompatible bootloader.\n"); return; @@ -18,5 +31,28 @@ void kmain(void *mbd, u32 magic) paging_init_simple(); gdt_create_flat(); -} + + kprintf("[i] Paging and GDT set up correctly.\n"); + u32 RSDPAddress = acpi_find_rsdp(); + if (RSDPAddress == 0) + { + kprintf("[e] Fatal! ACPI not found.\n"); + return; + } + + kprintf("[i] RSDP found at 0x%X.\n", RSDPAddress); + + u32 PhysicalTest; + u8 Result = paging_get_physical(0xC00B8000, &PhysicalTest); + if (!Result || PhysicalTest != 0xB8000) + { + kprintf("[e] Paging self-test failed!\n"); + return; + } + + interrupts_init_simple(); + interrupts_setup_isr(0x2A, sample_interrupt_0x2A, E_INTERRUPTS_RING0); + + __asm__ volatile("int $0x2a"); +} diff --git a/src/Tier0/kstdio.c b/src/Tier0/kstdio.c index 6f099b3..4b94923 100644 --- a/src/Tier0/kstdio.c +++ b/src/Tier0/kstdio.c @@ -14,7 +14,19 @@ u8 g_kstdio_cur_x = 0, g_kstdio_cur_y = 0; void koutb(u16 Port, u8 Data) { - __asm__ __volatile__ ("outb %1, %0" : : "dN" (Port), "a" (Data)); + __asm__ volatile("outb %1, %0" :: "dN" (Port), "a" (Data)); +} + +u8 kinb(u16 Port) +{ + u8 Return; + __asm__ volatile("inb %1, %0" :"=a"(Return):"Nd"(Port)); + return Return; +} + +void kio_wait(void) +{ + __asm__ volatile("jmp 1f;1:jmp 1f;1:"); } void kputi(s32 Number) @@ -216,3 +228,17 @@ void kclear(void) } kmove_cursor(0, 0); } + +s32 kmemcmp(u8 *MemA, u8 *MemB, u32 Length) +{ + u32 Result = -1; + for (u32 Search = 0; Search < Length; Search++) + { + if (MemA[Search] != MemB[Search]) + { + Result = Search; + break; + } + } + return Result; +} diff --git a/src/Tier0/paging.c b/src/Tier0/paging.c index ff7b202..ead4585 100644 --- a/src/Tier0/paging.c +++ b/src/Tier0/paging.c @@ -5,30 +5,40 @@ u32 g_kernel_page_directory[1024] __attribute__ ((aligned (4096))); u32 g_low_page_table[1024] __attribute__ ((aligned (4096))); -/*u8 paging_get_physical(u32 Virtual, u32 *Physical) +u8 paging_get_physical(u32 Virtual, u32 *Physical) { - u16 Index = - - u32 DirectoryEntry = g_kernel_page_directory[Index]; + u16 DirectoryIndex = (Virtual >> 22) & 0x3FF; + u32 DirectoryEntry = g_kernel_page_directory[DirectoryIndex]; u8 TablePresent = DirectoryEntry & 0b1; - if (!TablePresent) return 0; u32 TableAddress = 0; TableAddress |= (DirectoryEntry & 0xFFFFF000); + u32 *Table = (u32 *)TableAddress; - u32 Table = *((u32 *) &TableAddress); - u8 PagePresent = DirectoryEntry & -}*/ + u16 TableIndex = (Virtual >> 12) & 0x3FF; + u32 TableEntry = Table[TableIndex]; + + u8 PagePresent = TableEntry &0b1; + if (!PagePresent) + return 0; + + *Physical = 0; + *Physical |= (TableEntry & 0xFFFFF000); + *Physical |= (Virtual & 0xFFF); + + return 1; +} void paging_dump_directory(void) { for (u32 i = 0; i < 10; i++) { - kprintf("[i] Virtual 0x%X - 0x%X, Entry 0x%X.\n", i * 4096 * 1024, (i + 1) * 4096 * 1024, g_kernel_page_directory[i] & 0xFFFFF000); + kprintf("[i] Virtual 0x%X - 0x%X, Entry 0x%X.\n", i * 4096 * 1024, \ + (i + 1) * 4096 * 1024, g_kernel_page_directory[i] & 0xFFFFF000); } } @@ -43,15 +53,14 @@ void paging_init_simple(void) g_kernel_page_directory[i] = 0; } - kprintf("[i] Page Directory Physical: 0x%X, Virtual: 0x%X.\n", RealKernelPageDir, g_kernel_page_directory); - kprintf("[i] Low Page Table Physical: 0x%X, Virtual: 0x%X.\n", RealLowPageTable, g_low_page_table); + kprintf("[i] Page Directory Physical: 0x%X, Virtual: 0x%X.\n", \ + RealKernelPageDir, g_kernel_page_directory); + kprintf("[i] Low Page Table Physical: 0x%X, Virtual: 0x%X.\n", \ + RealLowPageTable, g_low_page_table); g_kernel_page_directory[0] = (u32)RealLowPageTable | 0x03; g_kernel_page_directory[768] = (u32)RealLowPageTable | 0x03; - paging_dump_directory(); - - __asm volatile ( "mov %0, %%eax\n" "mov %%eax, %%cr3\n" "mov %%cr0, %%eax\n" diff --git a/src/Tier0/pic.c b/src/Tier0/pic.c new file mode 100644 index 0000000..fc55bef --- /dev/null +++ b/src/Tier0/pic.c @@ -0,0 +1,93 @@ +// 8259 Programmable interrupt controller routines... +// The usual computer will switch to IOAPIC later on, once ACPI is fully +// initialized and the driver/module system is active. + +// I could probably try to do without interrupts until I get in C++-land but +// you never know - also, paging without interrupts is pointless, especiallly +// if I want a good heap implementation, which is more-or-less indispensable +// if I want to ever reach Tier1 in a sane way. + +// tl;dr - This code isn't going to be running all the time + +#include "types.h" +#include "Tier0/kstdio.h" +#include "Tier0/pic.h" + +#define PIC_COMMAND_EOI 0x20 + +// Initialisation "words". +#define PIC_ICW1_INIT 0x10 +#define PIC_ICW1_ICW4 0x01 +#define PIC_ICW4_8086 0x01 + +void pic_init(u8 *OldMask1, u8 *OldMask2) +{ + if (OldMask1 != 0) + *OldMask1 = kinb(PIC_1_DATA); + + if (OldMask2 != 0) + *OldMask2 = kinb(PIC_2_DATA); + + koutb(PIC_1_ADDR, PIC_ICW1_INIT | PIC_ICW1_ICW4); + kio_wait(); + + koutb(PIC_2_ADDR, PIC_ICW1_INIT | PIC_ICW1_ICW4); + kio_wait(); + + // ICW2 + koutb(PIC_1_DATA, PIC_1_START); + kio_wait(); + + koutb(PIC_2_DATA, PIC_2_START); + kio_wait(); + + // ICW3 + koutb(PIC_1_DATA, 4); + kio_wait(); + + koutb(PIC_2_DATA, 2); + kio_wait(); + + // ICW4 + koutb(PIC_1_DATA, PIC_ICW4_8086); + kio_wait(); + + koutb(PIC_2_DATA, PIC_ICW4_8086); + kio_wait(); + + //We don't support ANYTHING + koutb(PIC_1_DATA, 0xFF); + koutb(PIC_2_DATA, 0xFF); +} + +void pic_eoi(u8 IRQ) +{ + if (IRQ > 7) + koutb(PIC_2_ADDR, PIC_COMMAND_EOI); + + koutb(PIC_1_ADDR, PIC_COMMAND_EOI); +} + +void pic_unmask_irq(u8 IRQ) +{ + if (IRQ > 7) + { + u8 Bit = 0; + Bit |= 1 << (IRQ - 8); + + u8 CurrentMask = kinb(PIC_2_DATA); + u8 NewMask = ~((~CurrentMask) | Bit); + + koutb(PIC_2_DATA, NewMask); + } + else + { + u8 Bit = 0; + Bit |= 1 << (IRQ); + + u8 CurrentMask = kinb(PIC_1_DATA); + u8 NewMask = ~((~CurrentMask) | Bit); + + koutb(PIC_1_DATA, NewMask); + } +}