├── kernel ├── devices │ ├── apic_timer.h │ ├── ps2_keyboard.h │ ├── pit.h │ ├── pci.h │ ├── ioapic.h │ ├── ps2.h │ ├── serial.h │ ├── apic.h │ ├── pit.c │ ├── pic.h │ ├── serial.c │ ├── ps2_keyboard.c │ ├── ioapic.c │ ├── pic.c │ ├── pci.c │ ├── apic.c │ ├── ahci.c │ ├── ps2.c │ └── ahci.h ├── lib │ ├── print.h │ ├── stdlib.h │ ├── print.c │ ├── string.h │ ├── stdlib.c │ └── string.c ├── kernel.h ├── memory │ ├── heap.c │ ├── heap.h │ ├── pmm.h │ ├── paging.h │ ├── pmm.c │ └── paging.c ├── sys │ ├── io.h │ └── io.c ├── filesystem │ ├── vfs.h │ ├── initrd.h │ ├── simplefs.h │ ├── file.c │ ├── simplefs.c │ ├── file.h │ ├── sfs.h │ └── initrd.c ├── interfaces │ ├── description_tables │ │ ├── fadt.c │ │ ├── madt.c │ │ ├── madt.h │ │ └── fadt.h │ ├── acpi.h │ └── acpi.c ├── interrupts │ ├── isr.h │ ├── idt.h │ ├── idt.c │ ├── isr.c │ └── interrupt.asm ├── gdt.h ├── process │ ├── task.h │ ├── switch.asm │ └── task.c ├── gdt.c ├── linker.ld ├── GNUmakefile ├── kernel.c └── limine.h ├── initrd.img ├── .gitignore ├── .github └── workflows │ └── c-cpp.yml ├── limine.cfg ├── README.md ├── LICENSE.md ├── GNUmakefile └── STIVALE_README.md /kernel/devices/apic_timer.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /initrd.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickleAndMortimer/MakenOS/HEAD/initrd.img -------------------------------------------------------------------------------- /kernel/lib/print.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void printNumber(uint64_t num); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | limine 2 | ovmf-x64 3 | kernel/stivale2.h 4 | *.elf 5 | *.iso 6 | *.hdd 7 | *.o 8 | *.d 9 | kernel.sym 10 | -------------------------------------------------------------------------------- /kernel/devices/ps2_keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef PS2_KEYBOARD_H 2 | #define PS2_KEYBOARD_H 3 | 4 | #include 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /kernel/lib/stdlib.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void itoa(int value, char* str, int base); 4 | void uint64toa(uint64_t value, char* str, int base); 5 | -------------------------------------------------------------------------------- /kernel/kernel.h: -------------------------------------------------------------------------------- 1 | #ifndef KERNEL_H 2 | #define KERNEL_H 3 | 4 | #include 5 | extern void term_write(const char *string, size_t length); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /kernel/devices/pit.h: -------------------------------------------------------------------------------- 1 | #ifndef PIT_H 2 | #define PIT_H 3 | 4 | #include 5 | 6 | void initPIT(uint32_t frequency); 7 | uint32_t PIT_sleep(uint64_t milliseconds); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /kernel/memory/heap.c: -------------------------------------------------------------------------------- 1 | #include "heap.h" 2 | 3 | #define HEAP_START 4096 4 | #define HEAP_END 4096 5 | 6 | 7 | void* malloc(size_t size) 8 | { 9 | return NULL; 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /kernel/memory/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAP_H 2 | #define HEAP_H 3 | 4 | #include 5 | #include 6 | 7 | void* malloc(size_t size); 8 | void free(void* ptr); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /kernel/sys/io.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_H 2 | #define IO_H 3 | 4 | #include 5 | 6 | void outb(uint16_t port, uint8_t val); 7 | void out(uint16_t port, uint32_t val); 8 | uint8_t inb(uint16_t port); 9 | uint16_t inw(uint16_t port); 10 | uint32_t in(uint16_t port); 11 | void io_wait(void); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /kernel/lib/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "print.h" 4 | #include "string.h" 5 | #include "stdlib.h" 6 | #include "../kernel.h" 7 | 8 | void printNumber(uint64_t num) 9 | { 10 | char x[20]; 11 | uint64toa(num, x, 10); 12 | term_write(x, strlen(x)); 13 | term_write("\n", 1); 14 | } 15 | -------------------------------------------------------------------------------- /kernel/filesystem/vfs.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct VFS { 5 | int magic_no; // identifies the fs 6 | int (open*)(char* path, int flags); 7 | ssize_t (read*)(int fd, char* buf, size_t bytes); 8 | ssize_t (write*)(int fd, char* buf); 9 | int (memmap*)(int fd, char* buf); 10 | } VFS; 11 | -------------------------------------------------------------------------------- /kernel/devices/pci.h: -------------------------------------------------------------------------------- 1 | #ifndef PCI_H 2 | #define PCI_H 3 | 4 | #include 5 | #include 6 | 7 | uint16_t pciConfigReadWord(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset); 8 | void enablePCIInterrupts(uint8_t bus, uint8_t device, uint8_t function, size_t ioapicaddr); 9 | void checkMSI(uint8_t bus, uint8_t device, uint8_t func); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /kernel/lib/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include 5 | 6 | size_t strlen(const char* s); 7 | char* strcpy(char* destination, char* source); 8 | int strcmp(char* destination, char* source); 9 | void* reverse(char* s); 10 | void* memcpy(void* destination, void* source, size_t num); 11 | void* memset(void* ptr, int value, size_t num); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /kernel/devices/ioapic.h: -------------------------------------------------------------------------------- 1 | #ifndef IOAPIC_H 2 | #define IOAPIC_H 3 | 4 | #include 5 | #include 6 | 7 | uint32_t readIOAPIC(size_t ioapicaddr, uint32_t reg); 8 | void writeIOAPIC(size_t ioapicaddr, uint32_t reg, uint32_t value); 9 | void enableKeyboard(size_t ioapicaddr); 10 | void enableSerialCOM1(size_t ioapicaddr); 11 | void readIOREDTBLs(size_t ioapicaddr); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: install nasm 18 | run: sudo apt-get install -y nasm 19 | - name: make 20 | run: make 21 | 22 | -------------------------------------------------------------------------------- /kernel/devices/ps2.h: -------------------------------------------------------------------------------- 1 | #ifndef PS2_H 2 | 3 | #include 4 | 5 | #define PS2_H 6 | 7 | #define PS2_DATA_PORT 0x60 // Read/Write 8 | #define PS2_STATUS_REG 0x64 // Read 9 | #define PS2_COMMAND_REG 0x64 // Write 10 | 11 | void initKeyboard(); 12 | uint8_t readDataPort(); 13 | 14 | // TODO: map scan codes to ascii 15 | 16 | // Scancodes: Set 1 17 | 18 | // Scancodes: Set 2 19 | 20 | // Scancodes: Set 3 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /limine.cfg: -------------------------------------------------------------------------------- 1 | # Timeout in seconds that Limine will use before automatically booting. 2 | TIMEOUT=5 3 | 4 | # The entry name that will be displayed in the boot menu 5 | :MakenOS 6 | # Change the protocol line depending on the used protocol. 7 | PROTOCOL=limine 8 | 9 | # Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located. 10 | KERNEL_PATH=boot:///kernel.elf 11 | MODULE_PATH=boot:///initrd.img 12 | 13 | -------------------------------------------------------------------------------- /kernel/interfaces/description_tables/fadt.c: -------------------------------------------------------------------------------- 1 | #include "fadt.h" 2 | #include "../../sys/io.h" 3 | 4 | // see https://wiki.osdev.org/FADT for more info 5 | 6 | FADT* fadt; 7 | 8 | void initFADT() 9 | { 10 | fadt = (FADT*) findHeader("FACP"); 11 | } 12 | 13 | void enableACPI() 14 | { 15 | outb(fadt->SMI_command_port, fadt->ACPI_enable); 16 | // Poll PM1a control block until it is clear 17 | while ((inw(fadt->PM1a_control_block) & 1) == 0); 18 | } 19 | -------------------------------------------------------------------------------- /kernel/devices/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_H 2 | #define SERIAL_H 3 | 4 | #include 5 | #include 6 | 7 | #define COM1 0x3F8 8 | 9 | // Reference: https://wiki.osdev.org/Serial_Ports 10 | 11 | bool initSerial(); 12 | 13 | bool isSerialReceived(); 14 | char readSerial(); 15 | char* readSerialString(char* buffer, size_t len); 16 | 17 | bool isTransmitEmpty(); 18 | void writeSerial(char a); 19 | void writeSerialString(const char* str); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MakenOS 2 | A WIP hobby x86-64 OS using the Limine bootloader for me to learn about OS development. Written with C and a bit of NASM. 3 | 4 | Currently, it features 4-level paging, interrupt handling with the PIC and APIC, PS2 keyboard support, task switching, ACPI table parsing, and data fetching from the SATA drive. 5 | 6 | Current Roadmap: 7 | - Read and writes to AHCI drives 8 | - Implement a working filesystem (ext2, FAT32, etc.) 9 | - Add a shell that can load and run programs 10 | - USB Drivers? 11 | -------------------------------------------------------------------------------- /kernel/interrupts/isr.h: -------------------------------------------------------------------------------- 1 | #ifndef ISR_H 2 | #define ISR_H 3 | 4 | #include 5 | 6 | typedef struct InterruptFrame 7 | { 8 | uint64_t r11, r10, r9, r8; 9 | uint64_t rsi, rdi, rdx, rcx, rax; 10 | uint64_t int_no, err_code; 11 | uint64_t rsp, rflags, cs, rip; 12 | }__attribute__((packed)) InterruptFrame; 13 | 14 | void exceptionHandler(InterruptFrame* frame); 15 | void irqHandler(InterruptFrame* frame); 16 | void registerInterruptHandler(uint8_t irq, void (*handler) (InterruptFrame*)); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /kernel/devices/apic.h: -------------------------------------------------------------------------------- 1 | #ifndef APIC_H 2 | 3 | #define APIC_H 4 | #include 5 | 6 | // https://wiki.osdev.org/APIC#IO_APIC_Registers 7 | 8 | void cpuGetMSR(uint32_t msr, uint32_t *lo, uint32_t *hi); 9 | void cpuSetMSR(uint32_t msr, uint32_t lo, uint32_t hi); 10 | uintptr_t cpuGetAPICBase(); 11 | void cpuSetAPICBase(uintptr_t apic); 12 | uint32_t readAPICRegister(uint32_t reg); 13 | void writeAPICRegister(uint32_t reg, uint32_t value); 14 | void enableAPIC(); 15 | void enableAPICTimer(uint32_t frequency); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /kernel/memory/pmm.h: -------------------------------------------------------------------------------- 1 | #ifndef PMM_H 2 | #define PMM_H 3 | 4 | #include 5 | #include 6 | 7 | #define BLOCK_SIZE 4096 8 | 9 | typedef struct Node { 10 | void* base; 11 | size_t length; 12 | struct Node* next; 13 | } Node; 14 | 15 | typedef struct alloc_entry { 16 | size_t size; 17 | void* base; 18 | } alloc_entry; 19 | 20 | void printMemoryMaps(); 21 | void setMemoryMap(uint8_t selection); 22 | void* printHeader(void* start); 23 | void* getMemoryMapBase(); 24 | uint64_t getMemoryMapLength(); 25 | 26 | void* k_malloc(); 27 | void k_free(void* ptr); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2020, 2021 mintsuki and contributors. 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /kernel/filesystem/initrd.h: -------------------------------------------------------------------------------- 1 | #ifndef INITRD_H 2 | #define INITRD_H 3 | #include "./file.h" 4 | 5 | typedef struct { 6 | size_t nfiles; // The number of files in the ramdisk. 7 | } initrd_header_t; 8 | 9 | typedef struct { 10 | uint8_t magic; // Magic number, for error checking. 11 | char name[64]; // Filename. 12 | size_t offset; // Offset in the initrd that the file starts. 13 | size_t length; // Length of the file. 14 | } initrd_file_header_t; 15 | 16 | // Initialises the initial ramdisk. It gets passed the address of the multiboot module, 17 | // and returns a completed filesystem node. 18 | fs_node_t* initialise_initrd(size_t location); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /kernel/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | #include 4 | 5 | typedef struct SegmentDescriptor 6 | { 7 | uint8_t base; 8 | uint8_t flags; 9 | uint8_t access_byte; 10 | uint8_t base_high; 11 | uint16_t base_low; 12 | uint16_t limit_low; 13 | }__attribute__((packed)) SegmentDescriptor; 14 | 15 | typedef struct GDTPointer 16 | { 17 | uint64_t gdt_address; 18 | uint16_t limit; 19 | }__attribute__((packed)) GDTPointer; 20 | 21 | void setEntry(SegmentDescriptor* entry, uint32_t base, uint8_t flags, uint8_t access_byte, uint16_t limit); 22 | void setSystemEntry(SegmentDescriptor* entry_1, SegmentDescriptor* entry_2, uint64_t base, uint8_t flags, uint8_t access_byte, uint16_t limit); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /kernel/lib/stdlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stdlib.h" 4 | #include "string.h" 5 | 6 | void itoa(int value, char* str, int base) 7 | { 8 | int i = 0; 9 | uint8_t negative = value < 0; 10 | 11 | value = negative ? -value : value; 12 | 13 | do 14 | { 15 | str[i++] = value % base + '0'; 16 | } 17 | while ((value /= base) > 0); 18 | 19 | if (negative) 20 | str[i++] = '-'; 21 | 22 | str[i] = '\0'; 23 | reverse(str); 24 | } 25 | 26 | void uint64toa(uint64_t value, char* str, int base) 27 | { 28 | int i = 0; 29 | do 30 | { 31 | str[i++] = value % base + '0'; 32 | } 33 | while ((value /= base) > 0); 34 | str[i] = '\0'; 35 | reverse(str); 36 | } 37 | -------------------------------------------------------------------------------- /kernel/interrupts/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | #include 5 | 6 | typedef struct IDTEntry 7 | { 8 | uint16_t offset_1; // offset bits 0..15 9 | uint16_t selector; // a code segment selector in GDT or LDT 10 | uint8_t ist; // bits 0..2 holds Interrupt Stack Table offset, rest of bits zero. 11 | uint8_t type_attributes; // gate type, dpl, and p fields 12 | uint16_t offset_2; // offset bits 16..31 13 | uint32_t offset_3; // offset bits 32..63 14 | uint32_t zero; // reserved 15 | }__attribute__((packed)) IDTEntry; 16 | 17 | typedef struct IDTPointer 18 | { 19 | uint16_t size; 20 | uint64_t offset; 21 | }__attribute__((packed)) IDTPointer; 22 | 23 | void initIdt(); 24 | void setIdtEntry(IDTEntry *target, uint64_t offset, uint16_t selector, uint8_t ist, uint8_t type_attributes); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /kernel/memory/paging.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGING_H 2 | #define PAGING_H 3 | 4 | #include 5 | 6 | // options1: R P/W U/S PWT PCD A D PS(1) G AVL 7 | // Bits (0-11): 1 1 1 1 1 1 1 1 1 3 8 | 9 | // bits (12-51): address (significant bits are 0 by default,) 10 | 11 | // options2: AVL PK XD 12 | // Bits (52-63): 7 4 1 13 | 14 | typedef struct PageEntry { 15 | uint8_t present : 1; 16 | uint8_t writable : 1; 17 | uint8_t user_accessible : 1; 18 | uint8_t write_through_caching : 1; 19 | uint8_t disable_cache : 1; 20 | uint8_t accessed : 1; 21 | uint8_t dirty : 1; 22 | uint8_t null : 1; 23 | uint8_t global : 1; 24 | uint8_t avl1 : 3; 25 | uintptr_t physical_address : 40; 26 | uint16_t avl2 : 11; 27 | uint8_t no_execute : 1; 28 | } PageEntry; 29 | 30 | typedef struct PageTable { 31 | PageEntry entries[512]; 32 | } PageTable; 33 | 34 | void* getPhysicalAddress(void* virtual_address); 35 | PageTable* initPML4(void); 36 | void mapPage(void* virtual_address, void* physical_address, uint8_t flags); 37 | uint64_t readCR3(void); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /kernel/interrupts/idt.c: -------------------------------------------------------------------------------- 1 | #include "idt.h" 2 | 3 | extern void* isr_stub_table[]; 4 | extern void* irq_stub_table[]; 5 | 6 | IDTEntry idt_entries[256]; 7 | 8 | IDTPointer idt_ptr = (IDTPointer) 9 | { 10 | (uint16_t)sizeof(idt_entries) - 1, 11 | (uintptr_t)&idt_entries[0] 12 | }; 13 | 14 | void setIdtEntry(IDTEntry *target, uint64_t offset, uint16_t selector, uint8_t ist, uint8_t type_attributes) 15 | { 16 | target->offset_1 = offset & 0xFFFF; 17 | target->selector = selector; 18 | target->ist = ist; 19 | target->type_attributes = type_attributes; 20 | target->offset_2 = (offset >> 16) & 0xFFFF; 21 | target->offset_3 = (offset >> 32) & 0xFFFFFFFF; 22 | target->zero = 0; 23 | } 24 | 25 | void initIdt() 26 | { 27 | for (uint8_t i = 0; i < 32; i++) 28 | { 29 | setIdtEntry(&idt_entries[i], (uint64_t)isr_stub_table[i], 0x28, 0, 0x8E); 30 | } 31 | for (uint8_t i = 32; i < 255; i++) 32 | { 33 | setIdtEntry(&idt_entries[i], (uint64_t)irq_stub_table[i-32], 0x28, 0, 0x8E); 34 | } 35 | __asm__ volatile("lidt %0" : : "m"(idt_ptr)); 36 | __asm__ volatile("sti"); 37 | } 38 | -------------------------------------------------------------------------------- /kernel/interrupts/isr.c: -------------------------------------------------------------------------------- 1 | #include "../kernel.h" 2 | #include "isr.h" 3 | #include "../memory/paging.h" 4 | #include "../lib/print.h" 5 | #include "../devices/pic.h" 6 | 7 | void (*interrupt_handlers[256]) (InterruptFrame* frame); 8 | 9 | static inline uint64_t getCR2(void) 10 | { 11 | uint64_t val; 12 | __asm__ volatile ( "mov %%cr2, %0" : "=r"(val) ); 13 | return val; 14 | } 15 | 16 | void exceptionHandler(InterruptFrame* frame) { 17 | printNumber(frame->int_no); 18 | printNumber(frame->err_code); 19 | switch (frame->int_no) { 20 | case 14: 21 | if (frame->err_code & 1) { 22 | term_write("Page protection violation\n", 26); 23 | for (;;) 24 | { 25 | __asm__ volatile ("hlt"); 26 | } 27 | } 28 | uint64_t cr2 = getCR2(); 29 | printNumber(cr2); 30 | term_write("dead :(", 7); 31 | break; 32 | } 33 | } 34 | 35 | void registerInterruptHandler(uint8_t interrupt, void (*handler) (InterruptFrame* frame)) 36 | { 37 | interrupt_handlers[interrupt] = handler; 38 | } 39 | 40 | void irqHandler(InterruptFrame* frame) 41 | { 42 | if (&interrupt_handlers[frame->int_no] != NULL) 43 | { 44 | interrupt_handlers[frame->int_no](frame); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /kernel/process/task.h: -------------------------------------------------------------------------------- 1 | #ifndef TASK_H 2 | #define TASK_H 3 | 4 | #include 5 | 6 | typedef struct Registers 7 | { 8 | uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp; 9 | uint64_t r8, r9, r10, r11, r12, r13, r14, r15; 10 | uint64_t rflags; 11 | uint64_t rip; 12 | uint64_t cr3; 13 | }__attribute__((packed)) Registers; 14 | 15 | typedef struct Task 16 | { 17 | Registers regs; 18 | struct Task* next; 19 | }__attribute__((packed)) Task; 20 | 21 | typedef struct TSSDescriptor { 22 | uint16_t limit1; 23 | uint16_t base1; 24 | uint8_t base2; 25 | // type[4] 0 DPL[0:1] P[1] 26 | uint8_t flags1; 27 | // limit[16:19] AVL[1] 0 0 G[1] 28 | uint8_t flags2; 29 | uint8_t base3; 30 | uint32_t base4; 31 | uint32_t reserved; 32 | } TSSDescriptor; 33 | 34 | typedef struct TSS 35 | { 36 | uint32_t reserved; 37 | uint64_t rsp0; 38 | uint64_t rsp1; 39 | uint64_t rsp2; 40 | uint64_t reserved2; 41 | uint64_t ist1; 42 | uint64_t ist2; 43 | uint64_t ist3; 44 | uint64_t ist4; 45 | uint64_t ist5; 46 | uint64_t ist6; 47 | uint64_t ist7; 48 | uint64_t reserved3; 49 | uint16_t reserved4; 50 | uint16_t io_map_address; 51 | }__attribute__((packed)) TSS; 52 | 53 | void doIt(); 54 | void initTasking(); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /kernel/filesystem/simplefs.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_H 2 | #define FILE_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct vfs 8 | { 9 | uint16_t id; 10 | uint64_t length; 11 | uint64_t inode_len; 12 | uint64_t data_blocks; 13 | } vfs; 14 | 15 | typedef struct inode 16 | { 17 | uint16_t mode; 18 | uint16_t uid; 19 | uint32_t size; 20 | uint32_t time; 21 | uint32_t ctime; 22 | uint32_t mtime; 23 | uint32_t dtime; 24 | uint16_t gid; 25 | uint16_t links_count; 26 | uint32_t blocks; 27 | uint32_t flags; 28 | uint32_t osd1; 29 | void* block[15]; 30 | uint32_t generation; 31 | uint32_t file_acl; 32 | uint32_t dir_acl; 33 | } inode; 34 | 35 | typedef struct directory_entry 36 | { 37 | char name[256]; 38 | uint16_t inum; 39 | uint32_t rec_size; 40 | uint32_t strlen; 41 | } directory_entry; 42 | 43 | typedef struct inode_table { 44 | vfs v; 45 | uint16_t inode_bitmap; 46 | uint64_t dnode_bitmap; 47 | inode inodes[16]; 48 | } inode_table; 49 | 50 | inode* fopen(char* filename); 51 | int fwrite(inode* node, char* data, size_t len); 52 | int fread(inode* i, char* buffer, size_t blocks, size_t length); 53 | void* f_malloc(inode* node, size_t block_index); 54 | inode_table* initRamFS(); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /kernel/filesystem/file.c: -------------------------------------------------------------------------------- 1 | #include "file.h" 2 | 3 | fs_node_t* fs_root = NULL; // The root of the filesystem. 4 | 5 | size_t read_fs(fs_node_t *node, size_t offset, uint32_t size, uint8_t* buffer) 6 | { 7 | // Has the node got a read callback? 8 | if (node->read != 0) 9 | return node->read(node, offset, size, buffer); 10 | else 11 | return 0; 12 | } 13 | 14 | size_t write_fs(fs_node_t *node, size_t offset, size_t size, uint8_t* buffer) 15 | { 16 | // Has the node got a read callback? 17 | if (node->write != 0) 18 | return node->write(node, offset, size, buffer); 19 | else 20 | return 0; 21 | } 22 | 23 | void open_fs(fs_node_t *node, uint8_t read, uint8_t write) 24 | { 25 | // Has the node got a read callback? 26 | if (node->open != 0) 27 | node->open(node); 28 | } 29 | 30 | void close_fs(fs_node_t *node) 31 | { 32 | // Has the node got a read callback? 33 | if (node->close != 0) 34 | node->close(node); 35 | } 36 | 37 | struct dirent* readdir_fs(fs_node_t *node, size_t index) 38 | { 39 | if (node->readdir != 0 && (node->flags & 0x7) == FS_DIRECTORY) 40 | return node->readdir(node, index); 41 | else 42 | return NULL; 43 | } 44 | 45 | fs_node_t* finddir_fs(fs_node_t *node, char *name) 46 | { 47 | if (node->finddir != 0 && (node->flags & 0x7) == FS_DIRECTORY) 48 | return node->finddir(node, name); 49 | else 50 | return NULL; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /kernel/process/switch.asm: -------------------------------------------------------------------------------- 1 | section .text 2 | global switchTask 3 | switchTask: 4 | mov [rdi], rax ; Save state of the task 5 | mov rax, rsp 6 | add rax, 8 ; Skip the other register pushed in 7 | 8 | mov [rdi + 8], rbx 9 | mov [rdi + 16], rcx 10 | mov [rdi + 24], rdx 11 | mov [rdi + 32], rsi 12 | mov [rdi + 40], rdi 13 | mov [rdi + 48], rax 14 | mov [rdi + 56], rbp 15 | mov [rdi + 64], r8 16 | mov [rdi + 72], r9 17 | mov [rdi + 80], r10 18 | mov [rdi + 88], r11 19 | mov [rdi + 96], r12 20 | mov [rdi + 104], r13 21 | mov [rdi + 112], r14 22 | mov [rdi + 120], r15 23 | 24 | pushfq ; Save RFLAGS 25 | pop rax 26 | mov [rdi + 128], rax 27 | 28 | mov rax, [rsp] ; Save RIP 29 | mov [rdi + 136], rax 30 | push rax 31 | 32 | mov rax, [rsi] ; Load the state of the next task 33 | mov rbx, [rsi + 8] 34 | mov rdx, [rsi + 24] 35 | mov rdi, [rsi + 40] 36 | mov r8, [rsi + 64] 37 | mov r9, [rsi + 72] 38 | mov r10, [rsi + 80] 39 | mov r11, [rsi + 88] 40 | mov r12, [rsi + 96] 41 | mov r13, [rsi + 104] 42 | mov r14, [rsi + 112] 43 | mov r15, [rsi + 120] 44 | 45 | mov rax, [rsi + 128] 46 | 47 | push rax ; Load RFLAGS 48 | popfq 49 | 50 | mov rbp, [rsi + 56] 51 | mov rsp, [rsi + 48] 52 | 53 | mov rax, [rsi + 136] ; Load new RIP 54 | push rax 55 | mov rax, [rsi + 16] 56 | 57 | mov rsi, [rsi + 32] 58 | 59 | ret 60 | -------------------------------------------------------------------------------- /kernel/sys/io.c: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | uint8_t inb(uint16_t port) 4 | { 5 | uint8_t ret; 6 | __asm__ volatile ( "inb %1, %0" 7 | : "=a"(ret) 8 | : "Nd"(port) ); 9 | return ret; 10 | } 11 | 12 | uint16_t inw(uint16_t port) 13 | { 14 | uint16_t ret; 15 | __asm__ volatile ( "inw %1, %0" 16 | : "=a"(ret) 17 | : "Nd"(port) ); 18 | return ret; 19 | } 20 | 21 | uint32_t in(uint16_t port) 22 | { 23 | uint32_t ret; 24 | __asm__ volatile ( "in %1, %0" 25 | : "=a"(ret) 26 | : "Nd"(port) ); 27 | return ret; 28 | } 29 | 30 | void outb(uint16_t port, uint8_t val) 31 | { 32 | __asm__ volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); 33 | /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). 34 | * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). 35 | * The outb %al, %dx encoding is the only option for all other cases. 36 | * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ 37 | } 38 | 39 | void out(uint16_t port, uint32_t val) 40 | { 41 | __asm__ volatile ( "out %0, %1" : : "a"(val), "Nd"(port) ); 42 | } 43 | 44 | void io_wait(void) 45 | { 46 | outb(0x80, 0); 47 | } 48 | -------------------------------------------------------------------------------- /kernel/gdt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "gdt.h" 3 | 4 | SegmentDescriptor entries[6]; 5 | 6 | void setGdtEntry(SegmentDescriptor *entry, uint32_t base, uint8_t flags, uint8_t access_byte, uint16_t limit) 7 | { 8 | entry->base = base >> 24; 9 | entry->base_high = base >> 16 & 0xFF; 10 | entry->base_low = base & 0xFFFF; 11 | entry->flags = flags; 12 | entry->access_byte = access_byte; 13 | entry->limit_low = limit; 14 | } 15 | 16 | void setSystemEntry(SegmentDescriptor *entry_1, SegmentDescriptor *entry_2, uint64_t base, uint8_t flags, uint8_t access_byte, uint16_t limit) 17 | { 18 | entry_1->base = 0; 19 | entry_1->flags = 0; 20 | entry_1->access_byte = 0; 21 | entry_1->base_high = 0; 22 | entry_1->base_low = base >> 48; 23 | entry_1->limit_low = base >> 32 & 0xFFFF; 24 | 25 | entry_2->base = base >> 24 & 0xFF; 26 | entry_2->flags = flags; 27 | entry_2->access_byte = access_byte; 28 | entry_2->base_high = base >> 8 & 0xFF; 29 | entry_2->base_low = base & 0xFFFF; 30 | entry_2->limit_low = limit; 31 | } 32 | 33 | void initGDT() 34 | { 35 | setGdtEntry(&entries[0], 0, 0, 0, 0); 36 | setGdtEntry(&entries[1], 0, 0xCF, 0x9A, 0xFFFF); 37 | setGdtEntry(&entries[2], 0, 0xCF, 0x92, 0xFFFF); 38 | setGdtEntry(&entries[3], 0, 0xCF, 0xFA, 0xFFFF); 39 | setGdtEntry(&entries[4], 0, 0xCF, 0xF2, 0xFFFF); 40 | asm volatile ("lgdt %0" :: "m"(entries)); 41 | } 42 | -------------------------------------------------------------------------------- /kernel/interfaces/acpi.h: -------------------------------------------------------------------------------- 1 | #ifndef ACPI_H 2 | #define ACPI_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct 8 | { 9 | char signature[8]; 10 | uint8_t checksum; 11 | char OEM_id[6]; 12 | uint8_t revision; 13 | uint32_t rsdt_address; 14 | } __attribute__ ((packed)) RSDPDescriptor; 15 | 16 | typedef struct RSDPDescriptor20 17 | { 18 | RSDPDescriptor descriptor10; 19 | uint32_t length; 20 | uint64_t xsdt_address; 21 | uint8_t extended_checksum; 22 | uint8_t reserved[3]; 23 | } __attribute__ ((packed)) RSDPDescriptor20; 24 | 25 | typedef struct ACPISDTHeader 26 | { 27 | char signature[4]; 28 | uint32_t length; 29 | uint8_t revision; 30 | uint8_t checksum; 31 | char OEM_id[6]; 32 | char OEM_table_id[8]; 33 | uint32_t OEM_revision; 34 | uint32_t creator_id; 35 | uint32_t creator_revision; 36 | } __attribute__ ((packed)) ACPISDTHeader; 37 | 38 | // uint64_t other_SDT[(h.Length - sizeof(h)) / 8]; 39 | typedef struct RSDT 40 | { 41 | ACPISDTHeader h; 42 | uint32_t other_SDT[]; 43 | } __attribute__ ((packed)) RSDT; 44 | 45 | typedef struct XSDT 46 | { 47 | ACPISDTHeader h; 48 | uint64_t other_SDT[]; 49 | } __attribute__ ((packed)) XSDT; 50 | 51 | XSDT* getXSDT(); 52 | RSDT* getRSDT(); 53 | uint8_t validateRSDPChecksum(); 54 | uint8_t validateSDTChecksum(ACPISDTHeader* table_header); 55 | ACPISDTHeader* findHeader(char* signature); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /kernel/devices/pit.c: -------------------------------------------------------------------------------- 1 | #include "pit.h" 2 | #include "pic.h" 3 | #include "../sys/io.h" 4 | #include "../interrupts/isr.h" 5 | #include "../kernel.h" 6 | 7 | uint32_t frequency = 1193180; 8 | 9 | static void PIT_callback(InterruptFrame* frame) 10 | { 11 | sendEOIPIC(0); 12 | } 13 | 14 | void initPIT(uint32_t new_frequency) 15 | { 16 | // Firstly, register our timer callback. 17 | term_write("Initializing timer\n", 19); 18 | registerInterruptHandler(32, &PIT_callback); 19 | 20 | // The value we send to the PIT is the value to divide it's input clock 21 | // (1193180 Hz) by, to get our required frequency. Important to note is 22 | // that the divisor must be small enough to fit into 16-bits. 23 | uint32_t divisor = 1193180 / new_frequency; 24 | frequency = new_frequency; 25 | 26 | // Send the command byte. 27 | outb(0x43, 0x36); 28 | 29 | // Divisor has to be sent byte-wise, so split here into upper/lower bytes. 30 | uint8_t l = (uint8_t)(divisor & 0xFF); 31 | uint8_t h = (uint8_t)((divisor>>8) & 0xFF); 32 | 33 | // Send the frequency divisor. 34 | outb(0x40, l); 35 | outb(0x40, h); 36 | term_write("Timer initialized\n", 18); 37 | clearMaskIRQ(0); 38 | } 39 | 40 | uint32_t PIT_sleep(uint64_t milleseconds) 41 | { 42 | uint32_t ticks = 0; 43 | 44 | while (ticks < (frequency / 1000 * milleseconds)) { 45 | ticks++; 46 | __asm__ ("hlt"); 47 | } 48 | return ticks; 49 | } 50 | -------------------------------------------------------------------------------- /kernel/devices/pic.h: -------------------------------------------------------------------------------- 1 | #ifndef PIC_H 2 | #define PIC_H 3 | 4 | #include 5 | 6 | #define PIC_EOI 0x20 7 | 8 | #define PIC1 0x20 /* IO base address for master PIC */ 9 | #define PIC2 0xA0 /* IO base address for slave PIC */ 10 | #define PIC1_COMMAND PIC1 11 | #define PIC1_DATA (PIC1+1) 12 | #define PIC2_COMMAND PIC2 13 | #define PIC2_DATA (PIC2+1) 14 | 15 | #define ICW1_ICW4 0x01 /* ICW4 (not) needed */ 16 | #define ICW1_SINGLE 0x02 /* Single (cascade) mode */ 17 | #define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ 18 | #define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ 19 | #define ICW1_INIT 0x10 /* Initialization - required! */ 20 | 21 | #define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ 22 | #define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 23 | #define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ 24 | #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ 25 | #define ICW4_SFNM 0x10 /* Special fully nested (not) */ 26 | #define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */ 27 | #define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */ 28 | 29 | void sendEOIPIC(uint8_t irq); 30 | 31 | void setMaskIRQ(uint8_t IRQline); 32 | void clearMaskIRQ(uint8_t IRQline); 33 | void disableAllIRQs(); 34 | 35 | void remapPIC(int offset1, int offset2); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an x86_64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-x86-64) 3 | OUTPUT_ARCH(i386:x86-64) 4 | 5 | /* We want the symbol _start to be our entry point */ 6 | ENTRY(_start) 7 | 8 | /* Define the program headers we want so the bootloader gives us the right */ 9 | /* MMU permissions */ 10 | PHDRS 11 | { 12 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 13 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 14 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 15 | } 16 | 17 | SECTIONS 18 | { 19 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the stivale2 spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | 25 | .text : { 26 | *(.text .text.*) 27 | } :text 28 | 29 | /* Move to the next memory page for .rodata */ 30 | . += CONSTANT(MAXPAGESIZE); 31 | 32 | .rodata : { 33 | *(.rodata .rodata.*) 34 | } :rodata 35 | 36 | /* Move to the next memory page for .data */ 37 | . += CONSTANT(MAXPAGESIZE); 38 | 39 | .data : { 40 | *(.data .data.*) 41 | } :data 42 | 43 | .bss : { 44 | *(COMMON) 45 | *(.bss .bss.*) 46 | } :data 47 | 48 | /DISCARD/ : { 49 | *(.eh_frame) 50 | *(.note .note.*) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kernel/filesystem/simplefs.c: -------------------------------------------------------------------------------- 1 | #include "simplefs.h" 2 | #include "../lib/string.h" 3 | #include "../memory/pmm.h" 4 | 5 | static inode_table table; 6 | 7 | inode_table* initRamFS() 8 | { 9 | table.v.id = 1; 10 | table.v.length = sizeof(table); 11 | table.v.inode_len = sizeof(table.inodes); 12 | table.v.data_blocks = sizeof(56 * 4096); 13 | // root directory 14 | table.inodes[0] = (inode) { 0,0,0,0,0,0,0,0,0,0,0,0 }; 15 | return &table; 16 | } 17 | 18 | inode* fopen(char* filename) 19 | { 20 | for (size_t i = 0; i < 16; i++) 21 | { 22 | if (!((table.inode_bitmap >> i) & 1)) 23 | { 24 | table.inode_bitmap |= (1 << i); 25 | return &table.inodes[i]; 26 | } 27 | } 28 | return NULL; 29 | } 30 | 31 | int f_write(inode* node, char* data, size_t len) 32 | { 33 | memcpy(&node->block[0], data, len); 34 | return 1; 35 | } 36 | 37 | int f_read(inode* node, char* buffer, size_t blocks, size_t length) 38 | { 39 | for (size_t i = 0; i < blocks; i++) 40 | { 41 | if (node->block[i] == NULL) 42 | { 43 | continue; 44 | } 45 | memcpy(buffer, &node->block[i], length); 46 | } 47 | return 1; 48 | } 49 | 50 | void* f_malloc(inode* node, size_t block_index) 51 | { 52 | for (size_t i = 0; i < 56; i++) 53 | { 54 | if (!((table.dnode_bitmap >> i) & 1)) 55 | { 56 | table.dnode_bitmap |= (1 << i); 57 | node->block[block_index] = k_malloc(4096); 58 | return node->block[block_index]; 59 | } 60 | } 61 | return NULL; 62 | } 63 | -------------------------------------------------------------------------------- /kernel/filesystem/file.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_H 2 | #define FILE_H 3 | 4 | #include 5 | #include 6 | 7 | #define FS_FILE 0x01 8 | #define FS_DIRECTORY 0x02 9 | #define FS_CHARDEVICE 0x03 10 | #define FS_BLOCKDEVICE 0x04 11 | #define FS_PIPE 0x05 12 | #define FS_SYMLINK 0x06 13 | #define FS_MOUNTPOINT 0x08 14 | 15 | struct dirent { 16 | char name[128]; 17 | uint32_t ino; 18 | }; 19 | 20 | typedef struct fs_node { 21 | uint32_t flags; 22 | uint32_t mask; // The permissions mask. 23 | uint32_t uid; // The owning user. 24 | uint32_t gid; // The owning group. 25 | uint32_t inode; // This is device-specific - provides a way for a filesystem to identify files. 26 | uint32_t length; // Size of the file, in bytes. 27 | uint32_t impl; // An implementation-defined number. 28 | size_t (*read)(struct fs_node*, size_t, size_t, uint8_t*); 29 | size_t (*write)(struct fs_node*, size_t, size_t, uint8_t*); 30 | void (*open)(struct fs_node*); 31 | void (*close)(struct fs_node*); 32 | struct dirent* (*readdir)(struct fs_node*, size_t); 33 | struct fs_node* (*finddir)(struct fs_node*, char*); 34 | struct fs_node* ptr; 35 | } fs_node_t; 36 | 37 | extern fs_node_t* fs_root; // The root of the filesystem. 38 | 39 | size_t read_fs(fs_node_t* node, size_t offset, uint32_t size, uint8_t* buffer); 40 | size_t write_fs(fs_node_t* node, size_t offset, size_t size, uint8_t* buffer); 41 | void open_fs(fs_node_t* node, uint8_t read, uint8_t write); 42 | void close_fs(fs_node_t* node); 43 | struct dirent* readdir_fs(fs_node_t* node, size_t index); 44 | fs_node_t* finddir_fs(fs_node_t* node, char* name); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /kernel/process/task.c: -------------------------------------------------------------------------------- 1 | #include "task.h" 2 | #include "../memory/paging.h" 3 | #include "../memory/pmm.h" 4 | #include "../kernel.h" 5 | 6 | static Task *runningTask; 7 | static Task mainTask; 8 | static Task otherTask; 9 | 10 | extern PageTable pml4; 11 | extern void switchTask(Registers* from, Registers* to); 12 | extern void switchTask2(Registers* from, Registers* to); 13 | 14 | void yield() 15 | { 16 | Task *last = runningTask; 17 | runningTask = runningTask->next; 18 | switchTask(&last->regs, &runningTask->regs); 19 | } 20 | 21 | void doIt() { 22 | term_write("Switching to otherTask... \n", 27); 23 | yield(); 24 | term_write("good\n",5); 25 | } 26 | 27 | void createTask(Task *task, void (*main)(), uint64_t flags, uint64_t cr3) 28 | { 29 | task->regs.rax = 0; 30 | task->regs.rbx = 0; 31 | task->regs.rcx = 0; 32 | task->regs.rdx = 0; 33 | task->regs.rsi = 0; 34 | task->regs.rdi = 0; 35 | task->regs.rflags = flags; 36 | task->regs.rip = (uint64_t) main; 37 | task->regs.cr3 = cr3; 38 | task->regs.rsp = (uint64_t) k_malloc(4096) + 0x1000; 39 | task->next = NULL; 40 | } 41 | 42 | static void otherMain() 43 | { 44 | 45 | term_write("Hello!\n", 7); 46 | term_write("Going back to main...\n", 22); 47 | yield(); 48 | } 49 | 50 | void initTasking() 51 | { 52 | // Get RFLAGS and CR3 53 | __asm__ volatile("movq %%cr3, %%rax; movq %%rax, %0;":"=m"(mainTask.regs.cr3)::"%rax"); 54 | __asm__ volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;":"=m"(mainTask.regs.rflags)::"%rax"); 55 | 56 | createTask(&otherTask, otherMain, mainTask.regs.rflags, mainTask.regs.cr3); 57 | mainTask.next = &otherTask; 58 | otherTask.next = &mainTask; 59 | 60 | runningTask = &mainTask; 61 | } 62 | -------------------------------------------------------------------------------- /kernel/filesystem/sfs.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct super { 5 | uint16_t inodes; 6 | uint16_t blocks; 7 | uint16_t id; 8 | }__attribute__((aligned(4096))); 9 | 10 | struct inode { 11 | uint16_t mode; 12 | uint16_t uid; 13 | uint32_t size; 14 | uint32_t time; 15 | uint32_t ctime; 16 | uint32_t mtime; 17 | uint32_t dtime; 18 | uint16_t gid; 19 | uint16_t links_count; 20 | uint32_t blocks; 21 | uint32_t flags; 22 | uint32_t osd1; 23 | void* block[15]; 24 | uint32_t generation; 25 | uint32_t file_acl; 26 | uint32_t dir_acl; 27 | }__attribute__((aligned(256))); 28 | 29 | struct table { 30 | struct super s; 31 | bool inode_bitmap[4096]; 32 | bool dnode_bitmap[4096]; 33 | inode inodes[80]; 34 | uint8_t data[4096 * 56]; 35 | }; 36 | 37 | struct directory_entry { 38 | uint8_t inum; 39 | uint8_t reclen; 40 | uint8_t strlen; 41 | char name[128]; 42 | }; 43 | -------------------------------------------------------------------------------- /kernel/interfaces/description_tables/madt.c: -------------------------------------------------------------------------------- 1 | #include "madt.h" 2 | 3 | MADT* madt; 4 | 5 | ProcessorAPIC* processor_apics[32]; 6 | IOAPIC* ioapics[32]; 7 | IOAPICSourceOverride* ioapic_source_overrides[32]; 8 | IOAPICNonMaskableInterruptSource* ioapic_interrupt_sources[32]; 9 | IOAPICNonMaskableInterrupt* ioapic_interrupts[32]; 10 | LAPICAddressOverride* lapic_address_overrides[32]; 11 | x2LAPIC* x2_lapics[32]; 12 | 13 | void initMADT() 14 | { 15 | madt = (MADT*) findHeader("APIC"); 16 | } 17 | 18 | void parseMADT() 19 | { 20 | APICRecordHeader* head; 21 | uint32_t length = sizeof(MADT); 22 | uintptr_t madt_address = (uintptr_t) madt; 23 | 24 | // array pointers 25 | int i, j, k, l, m, n, o; 26 | i = j = k = l = m = n = o = 0; 27 | while (length < madt->header.length) 28 | { 29 | head = (APICRecordHeader*)(madt_address + length); 30 | 31 | switch (head->entry_type) 32 | { 33 | case 0: 34 | processor_apics[i++] = (ProcessorAPIC*) head; 35 | length += head->record_length; 36 | break; 37 | case 1: 38 | ioapics[j++] = (IOAPIC*) head; 39 | length += head->record_length; 40 | break; 41 | case 2: 42 | ioapic_source_overrides[k++] = (IOAPICSourceOverride*) head; 43 | length += head->record_length; 44 | break; 45 | case 3: 46 | ioapic_interrupt_sources[l++] = (IOAPICNonMaskableInterruptSource*) head; 47 | length += head->record_length; 48 | break; 49 | case 4: 50 | ioapic_interrupts[m++] = (IOAPICNonMaskableInterrupt*) head; 51 | length += head->record_length; 52 | break; 53 | case 5: 54 | lapic_address_overrides[n++] = (LAPICAddressOverride*) head; 55 | length += head->record_length; 56 | break; 57 | case 9: 58 | x2_lapics[o++] = (x2LAPIC*) head; 59 | length += head->record_length; 60 | break; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /kernel/interfaces/description_tables/madt.h: -------------------------------------------------------------------------------- 1 | #ifndef MADT_H 2 | #define MADT_H 3 | 4 | #include "../acpi.h" 5 | #include 6 | #include 7 | 8 | typedef struct MADT 9 | { 10 | ACPISDTHeader header; 11 | // 0: Local APIC Address 12 | // 1: Flags () 13 | uint32_t APIC_address; 14 | uint32_t APIC_flags; 15 | // nth entry: Entry type 16 | // n+1th entry: Record length 17 | } __attribute__((packed)) MADT; 18 | 19 | typedef struct APICRecordHeader 20 | { 21 | uint8_t entry_type; 22 | uint8_t record_length; 23 | } APICRecordHeader; 24 | 25 | // Entry Type 0 26 | typedef struct ProcessorAPIC 27 | { 28 | APICRecordHeader head; 29 | uint8_t ACPI_processor_id; 30 | uint8_t id; 31 | uint32_t flags; 32 | } ProcessorAPIC; 33 | 34 | // Entry Type 1 35 | typedef struct IOAPIC 36 | { 37 | APICRecordHeader head; 38 | uint8_t id; 39 | uint8_t reserved; 40 | uint32_t address; 41 | uint32_t global_system_interrupt_base; 42 | } __attribute__((packed)) IOAPIC; 43 | 44 | // Entry Type 2 45 | typedef struct IOAPICSourceOverride 46 | { 47 | APICRecordHeader head; 48 | uint8_t bus_source; 49 | uint8_t IRQ_source; 50 | uint32_t global_system_interrupt; 51 | uint16_t flags; 52 | } __attribute__((packed)) IOAPICSourceOverride; 53 | 54 | // Entry Type 3 55 | typedef struct { 56 | APICRecordHeader head; 57 | uint8_t NMI_source; 58 | uint8_t reserved; 59 | uint16_t flags; 60 | uint32_t global_system_interrupt; 61 | } __attribute__((packed)) IOAPICNonMaskableInterruptSource; 62 | 63 | // Entry Type 4 64 | typedef struct { 65 | APICRecordHeader head; 66 | uint8_t ACPI_processor_id; 67 | uint16_t flags; 68 | uint8_t LINT; 69 | } __attribute__((packed)) IOAPICNonMaskableInterrupt; 70 | 71 | // Entry Type 5 72 | typedef struct { 73 | APICRecordHeader head; 74 | uint16_t reserved; 75 | uint64_t LAPIC_address; 76 | } __attribute__((packed)) LAPICAddressOverride; 77 | 78 | // Entry Type 9 79 | typedef struct x2LAPIC 80 | { 81 | APICRecordHeader head; 82 | uint16_t reserved; 83 | uint32_t id; 84 | uint32_t flags; 85 | uint32_t ACPI_id; 86 | } __attribute__((packed)) x2LAPIC; 87 | 88 | void initMADT(); 89 | void parseMADT(); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /kernel/devices/serial.c: -------------------------------------------------------------------------------- 1 | #include "../sys/io.h" 2 | #include "serial.h" 3 | #include "../interrupts/isr.h" 4 | #include "../lib/print.h" 5 | #include "../kernel.h" 6 | 7 | static void serialInterruptHandler(InterruptFrame* frame) 8 | { 9 | printNumber(frame->int_no); 10 | char x[2]; 11 | x[0] = readSerial(); 12 | term_write(x, 2); 13 | } 14 | 15 | bool initSerial() 16 | { 17 | registerInterruptHandler(0x24, &serialInterruptHandler); 18 | outb(COM1 + 1, 0x00); // Disable all interrupts 19 | outb(COM1 + 3, 0x80); // Enable DLAB (set baud rate divisor) 20 | outb(COM1 + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud 21 | outb(COM1 + 1, 0x00); 22 | outb(COM1 + 3, 0x03); // 8 bits, no parity, one stop bit 23 | outb(COM1 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold 24 | outb(COM1 + 4, 0x0B); // IRQs enabled, RTS/DSR set 25 | outb(COM1 + 4, 0x1E); // Set in loopback mode, test the serial chip 26 | outb(COM1 + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) 27 | 28 | // Check if serial is faulty (i.e: not same byte as sent) 29 | if(inb(COM1 + 0) != 0xAE) 30 | { 31 | term_write("bokre", 5); 32 | return true; 33 | } 34 | 35 | // If serial is not faulty set it in normal operation mode 36 | // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) 37 | outb(COM1 + 4, 0x0F); 38 | //outb(COM1 + 1, 0x0F); 39 | return false; 40 | } 41 | 42 | bool isSerialReceived() 43 | { 44 | return inb(COM1 + 5) & 1; 45 | } 46 | 47 | char readSerial() 48 | { 49 | while (!isSerialReceived()); 50 | return inb(COM1); 51 | } 52 | 53 | char* readSerialString(char* buffer, size_t len) 54 | { 55 | size_t i; 56 | for (i = 0; i < len; i++) 57 | { 58 | char new_char = readSerial(); 59 | if (new_char != '\0') 60 | { 61 | buffer[i++] = new_char; 62 | } 63 | } 64 | buffer[i] = '\0'; 65 | return buffer; 66 | } 67 | 68 | bool isTransmitEmpty() 69 | { 70 | return inb(COM1 + 5) & 0x20; 71 | } 72 | 73 | void writeSerial(char a) 74 | { 75 | while (!isTransmitEmpty()); 76 | 77 | outb(COM1, a); 78 | } 79 | 80 | void writeSerialString(const char* str) 81 | { 82 | size_t i = 0; 83 | while (str[i] != '\0') 84 | { 85 | writeSerial(str[i++]); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /kernel/interrupts/interrupt.asm: -------------------------------------------------------------------------------- 1 | extern exceptionHandler 2 | extern irqHandler 3 | 4 | %macro pushad 0 5 | push rax 6 | push rcx 7 | push rdx 8 | push rdi 9 | push rsi 10 | push r8 11 | push r9 12 | push r10 13 | push r11 14 | %endmacro 15 | 16 | %macro popad 0 17 | pop r11 18 | pop r10 19 | pop r9 20 | pop r8 21 | pop rsi 22 | pop rdi 23 | pop rdx 24 | pop rcx 25 | pop rax 26 | %endmacro 27 | 28 | isr_common_stub: 29 | pushad 30 | cld 31 | lea rdi, [rsp] 32 | call exceptionHandler 33 | popad 34 | add rsp, 0x10 35 | iretq 36 | 37 | %macro isr_err_stub 1 38 | isr_stub_%+%1: 39 | push %1 40 | jmp isr_common_stub 41 | %endmacro 42 | 43 | %macro isr_no_err_stub 1 44 | isr_stub_%+%1: 45 | push 0 46 | push %1 47 | jmp isr_common_stub 48 | %endmacro 49 | 50 | isr_no_err_stub 0 51 | isr_no_err_stub 1 52 | isr_no_err_stub 2 53 | isr_no_err_stub 3 54 | isr_no_err_stub 4 55 | isr_no_err_stub 5 56 | isr_no_err_stub 6 57 | isr_no_err_stub 7 58 | isr_err_stub 8 59 | isr_no_err_stub 9 60 | isr_err_stub 10 61 | isr_err_stub 11 62 | isr_err_stub 12 63 | isr_err_stub 13 64 | isr_err_stub 14 65 | isr_no_err_stub 15 66 | isr_no_err_stub 16 67 | isr_err_stub 17 68 | isr_no_err_stub 18 69 | isr_no_err_stub 19 70 | isr_no_err_stub 20 71 | isr_no_err_stub 21 72 | isr_no_err_stub 22 73 | isr_no_err_stub 23 74 | isr_no_err_stub 24 75 | isr_no_err_stub 25 76 | isr_no_err_stub 26 77 | isr_no_err_stub 27 78 | isr_no_err_stub 28 79 | isr_no_err_stub 29 80 | isr_err_stub 30 81 | isr_no_err_stub 31 82 | 83 | irq_common_stub: 84 | pushad 85 | cld 86 | lea rdi, [rsp] 87 | call irqHandler 88 | popad 89 | add rsp, 0x10 90 | iretq 91 | 92 | %assign i 32 93 | %rep 223 94 | irq_stub_%+i: 95 | cli 96 | push 0 97 | push i 98 | jmp irq_common_stub 99 | %assign i i+1 100 | %endrep 101 | 102 | global isr_stub_table 103 | isr_stub_table: 104 | %assign i 0 105 | %rep 32 106 | DQ isr_stub_%+i 107 | %assign i i+1 108 | %endrep 109 | 110 | global irq_stub_table 111 | irq_stub_table: 112 | %assign i 32 113 | %rep 223 114 | DQ irq_stub_%+i 115 | %assign i i+1 116 | %endrep 117 | -------------------------------------------------------------------------------- /kernel/interfaces/acpi.c: -------------------------------------------------------------------------------- 1 | #include "acpi.h" 2 | 3 | RSDPDescriptor20 *rsdp_descriptor; 4 | RSDT* rsdt; 5 | XSDT* xsdt; 6 | 7 | uint8_t validateRSDPChecksum() 8 | { 9 | uint64_t checksum = rsdp_descriptor->descriptor10.checksum; 10 | for (uint8_t i = 0; i < 8; i++) 11 | { 12 | checksum += rsdp_descriptor->descriptor10.signature[i]; 13 | } 14 | 15 | for (uint8_t i = 0; i < 6; i++) 16 | { 17 | checksum += rsdp_descriptor->descriptor10.OEM_id[i]; 18 | } 19 | 20 | uint32_t address_byte = rsdp_descriptor->descriptor10.rsdt_address; 21 | checksum += rsdp_descriptor->descriptor10.revision; 22 | 23 | // split address into bytes and add to the checksum 24 | for (int i = 0 ; i < 4; i++) 25 | { 26 | checksum += address_byte & 0xFF; 27 | address_byte >>= 8; 28 | } 29 | 30 | // add these bytes if ACPI is version 2 31 | if (rsdp_descriptor->descriptor10.revision == 2) 32 | { 33 | address_byte = rsdp_descriptor->length; 34 | for (int i = 0 ; i < 4; i++) 35 | { 36 | checksum += address_byte & 0xFF; 37 | address_byte >>= 8; 38 | } 39 | 40 | address_byte = rsdp_descriptor->xsdt_address; 41 | for (int i = 0 ; i < 8; i++) 42 | { 43 | checksum += address_byte & 0xFF; 44 | address_byte >>= 8; 45 | } 46 | 47 | checksum += rsdp_descriptor->extended_checksum; 48 | 49 | for (uint8_t i = 0; i < 3; i++) { 50 | checksum += rsdp_descriptor->reserved[i]; 51 | } 52 | } 53 | return checksum; 54 | } 55 | 56 | uint8_t validateSDTChecksum(ACPISDTHeader* table_header) 57 | { 58 | uint8_t sum = 0; 59 | 60 | for (uint32_t i = 0; i < table_header->length; i++) 61 | { 62 | sum += ((uint8_t *) table_header)[i]; 63 | } 64 | 65 | return sum == 0; 66 | } 67 | 68 | ACPISDTHeader* findHeader(char* signature) 69 | { 70 | uint32_t entries = (rsdt->h.length - sizeof(rsdt->h)) / 4; 71 | 72 | for (uint32_t i = 0; i < entries; i++) 73 | { 74 | ACPISDTHeader* header = (ACPISDTHeader*) (uintptr_t)rsdt->other_SDT[i]; 75 | if (header->signature[0] == signature[0] && header->signature[1] == signature[1] && header->signature[2] == signature[2] && header->signature[3] == signature[3]) 76 | return header; 77 | } 78 | 79 | // No header found 80 | return NULL; 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /kernel/lib/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void *memcpy(void *dest, const void *src, size_t n) { 6 | uint8_t *pdest = (uint8_t *)dest; 7 | const uint8_t *psrc = (const uint8_t *)src; 8 | 9 | for (size_t i = 0; i < n; i++) { 10 | pdest[i] = psrc[i]; 11 | } 12 | 13 | return dest; 14 | } 15 | 16 | void *memset(void *s, int c, size_t n) { 17 | uint8_t *p = (uint8_t *)s; 18 | 19 | for (size_t i = 0; i < n; i++) { 20 | p[i] = (uint8_t)c; 21 | } 22 | 23 | return s; 24 | } 25 | 26 | void *memmove(void *dest, const void *src, size_t n) { 27 | uint8_t *pdest = (uint8_t *)dest; 28 | const uint8_t *psrc = (const uint8_t *)src; 29 | 30 | if (src > dest) { 31 | for (size_t i = 0; i < n; i++) { 32 | pdest[i] = psrc[i]; 33 | } 34 | } else if (src < dest) { 35 | for (size_t i = n; i > 0; i--) { 36 | pdest[i-1] = psrc[i-1]; 37 | } 38 | } 39 | 40 | return dest; 41 | } 42 | 43 | int memcmp(const void *s1, const void *s2, size_t n) { 44 | const uint8_t *p1 = (const uint8_t *)s1; 45 | const uint8_t *p2 = (const uint8_t *)s2; 46 | 47 | for (size_t i = 0; i < n; i++) { 48 | if (p1[i] != p2[i]) { 49 | return p1[i] < p2[i] ? -1 : 1; 50 | } 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | // Our quick and dirty strlen() implementation. 57 | size_t strlen(const char *str) { 58 | size_t ret = 0; 59 | while (*str++) { 60 | ret++; 61 | } 62 | return ret; 63 | } 64 | 65 | int strcmp(const char* str1, const char* str2) 66 | { 67 | uint64_t i = 0; 68 | while (str1[i] != '\0' || str2[i] != '\0') 69 | { 70 | if (str1[i] != str2[i]) 71 | { 72 | return str1[i] - str2[i]; 73 | } 74 | i++; 75 | } 76 | return 0; 77 | } 78 | 79 | char* strcpy(char* destination, const char* source) 80 | { 81 | size_t length = strlen(source); 82 | for (size_t i = 0; i < length; i++) { 83 | destination[i] = source[i]; 84 | } 85 | return destination; 86 | } 87 | 88 | void* reverse(char* s) 89 | { 90 | int i, j; 91 | char tmp; 92 | for (i=0,j=strlen(s)-1; i < j; i++, j--) 93 | { 94 | tmp = s[i]; 95 | s[i] = s[j]; 96 | s[j] = tmp; 97 | } 98 | return s; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /kernel/devices/ps2_keyboard.c: -------------------------------------------------------------------------------- 1 | #include "ps2_keyboard.h" 2 | 3 | // Map code to corresponding character 4 | const char* set1_scancodes[] = { 5 | "", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", 6 | "-", "=", "\b \b", "\t", "q", "w", "e", "r", "t", "y", "u", "i", 7 | "o", "p", "[", "]", "\n", "^", "a", "s", "d", "f", "g", "h", 8 | "j", "k", "l", ";", "'", "`", "", "\\", "z", "x", "c", "v", 9 | "b", "n", "m", ",", ".", "/", "", "*", "", " ", "", "", "", 10 | "", "", "", "", "", "", "", "", "", "", "7", "8", 11 | "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", ".", "", 12 | "", "", "", "" 13 | }; 14 | 15 | const char* shift_set1_scancodes[] = { 16 | "", "", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", 17 | "_", "+", "\b \b", "\t", "Q", "W", "E", "R", "T", "Y", "U", "I", 18 | "O", "P", "{", "}", "\n", "^", "A", "S", "D", "F", "G", "H", 19 | "J", "K", "L", ":", "\"", "~", "", "|", "Z", "X", "C", "V", 20 | "B", "N", "M", "<", ">", "?", "", "*", "", " ", "", "", "", 21 | "", "", "", "", "", "", "", "", "", "", "", "", 22 | "", "", "", "", "", "", "", "", "", "", "", "", 23 | "", "", "", "" 24 | }; 25 | 26 | // TODO: Finish adding scancodes 27 | const char* set2_scancodes[] = { 28 | "", "", "", "", "", "", "", "", "", "", "", "", 29 | "", "\t", "`", "", "", "", "", "", "", "q", "1", "", 30 | "", "", "z", "s", "a", "w", "2", "", "", "c", "x", "d", 31 | "e", "4", "3", "", "", " ", "v", "f", "t", "r", "5", "", 32 | "g", "y", "6", "", "", "", "m", "j", "u", "7", "8", "", "", 33 | "", ",", "k", "i", "o", "0", "9", "", ".", "/", "l", ";", 34 | "p", "-", "", "", "", "'", "", "[", "=", "", "", "", 35 | "", "\n", "]", "", "\\" 36 | }; 37 | -------------------------------------------------------------------------------- /kernel/interfaces/description_tables/fadt.h: -------------------------------------------------------------------------------- 1 | #ifndef FADT_H 2 | #define FADT_H 3 | 4 | #include 5 | #include "../acpi.h" 6 | 7 | // see https://wiki.osdev.org/FADT for more info 8 | 9 | typedef struct GenericAddressStructure 10 | { 11 | uint8_t address_space; 12 | uint8_t bit_width; 13 | uint8_t bit_offset; 14 | uint8_t access_size; 15 | uint64_t address; 16 | } __attribute__((packed)) GenericAddressStructure; 17 | 18 | typedef struct FADT 19 | { 20 | ACPISDTHeader h; 21 | uint32_t firmware_control; 22 | uint32_t DSDT_address; 23 | 24 | // field used in ACPI 1.0; no longer in use, for compatibility only 25 | uint8_t reserved; 26 | 27 | uint8_t preferred_power_management_profile; 28 | uint16_t SCI_interrupt; 29 | uint32_t SMI_command_port; 30 | uint8_t ACPI_enable; 31 | uint8_t ACPI_disable; 32 | uint8_t S4BIOS_req; 33 | uint8_t PSTATE_control; 34 | uint32_t PM1a_event_block; 35 | uint32_t PM1b_event_block; 36 | uint32_t PM1a_control_block; 37 | uint32_t PM1b_control_block; 38 | uint32_t PM2_control_block; 39 | uint32_t PM_timer_block; 40 | uint32_t GPE0_block; 41 | uint32_t GPE1_block; 42 | uint8_t PM1_event_length; 43 | uint8_t PM1_control_length; 44 | uint8_t PM2_control_length; 45 | uint8_t PM_timer_length; 46 | uint8_t GPE0_length; 47 | uint8_t GPE1_length; 48 | uint8_t GPE1_base; 49 | uint8_t C_state_control; 50 | uint16_t worst_C2_latency; 51 | uint16_t worst_C3_latency; 52 | uint16_t flush_size; 53 | uint16_t flush_stride; 54 | uint8_t duty_offset; 55 | uint8_t duty_width; 56 | uint8_t day_alarm; 57 | uint8_t month_alarm; 58 | uint8_t century; 59 | 60 | // reserved in ACPI 1.0; used since ACPI 2.0+ 61 | uint16_t boot_architecture_flags; 62 | 63 | uint8_t reserved_2; 64 | uint32_t flags; 65 | 66 | GenericAddressStructure reset_reg; 67 | 68 | uint8_t reset_value; 69 | uint8_t reserved_3[3]; 70 | 71 | // 64bit pointers - Available on ACPI 2.0+ 72 | uint64_t x_firmware_control; 73 | uint64_t x_DSDT; 74 | 75 | GenericAddressStructure X_PM1a_event_block; 76 | GenericAddressStructure X_PM1b_event_block; 77 | GenericAddressStructure X_PM1a_control_block; 78 | GenericAddressStructure X_PM1b_control_block; 79 | GenericAddressStructure X_PM2_control_block; 80 | GenericAddressStructure X_PM_timer_block; 81 | GenericAddressStructure X_GPE0_block; 82 | GenericAddressStructure X_GPE1_block; 83 | }__attribute__((packed)) FADT; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /kernel/devices/ioapic.c: -------------------------------------------------------------------------------- 1 | #include "apic.h" 2 | #include "ioapic.h" 3 | #include "../interrupts/isr.h" 4 | #include "../kernel.h" 5 | #include "pic.h" 6 | #include "ps2.h" 7 | #include "serial.h" 8 | #include "../lib/print.h" 9 | #include "../lib/string.h" 10 | #include 11 | 12 | extern char* set1_scancodes[]; 13 | extern char* shift_set1_scancodes[]; 14 | 15 | uint32_t readIOAPIC(size_t ioapicaddr, uint32_t reg) 16 | { 17 | uint32_t volatile* ioapic = (uint32_t volatile*) (uintptr_t) ioapicaddr; 18 | ioapic[0] = (reg & 0xFF); 19 | return ioapic[4]; 20 | } 21 | 22 | void writeIOAPIC(size_t ioapicaddr, uint32_t reg, uint32_t value) 23 | { 24 | uint32_t volatile* ioapic = (uint32_t volatile*) (uintptr_t) ioapicaddr; 25 | ioapic[0] = (reg & 0xFF); 26 | ioapic[4] = value; 27 | } 28 | 29 | static bool shift = false; 30 | static bool caps_lock = false; 31 | static bool ctrl = false; 32 | 33 | static void keyboardHandler(InterruptFrame* frame) 34 | { 35 | size_t input = readDataPort(); 36 | if (input == 0x2A || input == 0x36) 37 | shift = true; 38 | 39 | else if (input == 0x3A) 40 | caps_lock ^= true; 41 | 42 | else if (input == 0x1D) 43 | ctrl = true; 44 | 45 | else if (input < 0x58) 46 | { 47 | if (ctrl) 48 | term_write("^", 1); 49 | 50 | if (shift ^ caps_lock) 51 | term_write(shift_set1_scancodes[input], strlen(shift_set1_scancodes[input])); 52 | else 53 | term_write(set1_scancodes[input], strlen(set1_scancodes[input])); 54 | } 55 | 56 | else if (input == 0xAA || input == 0xB6) 57 | shift = false; 58 | 59 | else if (input == 0x9D) 60 | ctrl = false; 61 | 62 | writeAPICRegister(0xB0, 0); 63 | } 64 | 65 | void readIOREDTBLs(size_t ioapicaddr) 66 | { 67 | uint32_t IOAPICID = readIOAPIC(ioapicaddr, 0x0); 68 | uint32_t IOAPICVER = readIOAPIC(ioapicaddr, 0x1); 69 | printNumber(IOAPICID); 70 | printNumber(IOAPICVER); 71 | for (uint8_t i=0; i < 8; i++) 72 | { 73 | term_write("IRQ ", 4); 74 | printNumber(i); 75 | uint32_t redirection_entry_1 = readIOAPIC(ioapicaddr, 0x10 + i * 2); 76 | uint32_t redirection_entry_2 = readIOAPIC(ioapicaddr, 0x11 + i * 2); 77 | printNumber(redirection_entry_1); 78 | printNumber(redirection_entry_2); 79 | } 80 | } 81 | 82 | void enableKeyboard(size_t ioapicaddr) 83 | { 84 | writeIOAPIC(ioapicaddr, 0x12, 0x21); 85 | registerInterruptHandler(0x21, &keyboardHandler); 86 | } 87 | 88 | void enableSerialCOM1(size_t ioapicaddr) 89 | { 90 | writeIOAPIC(ioapicaddr, 0x18, 0x24); 91 | initSerial(); 92 | } 93 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # Nuke built-in rules and variables. 2 | override MAKEFLAGS += -rR 3 | 4 | override IMAGE_NAME := MakenOS 5 | override TEST_DRIVE := test.img 6 | 7 | .PHONY: all 8 | all: $(IMAGE_NAME).iso 9 | 10 | .PHONY: all-hdd 11 | all-hdd: $(IMAGE_NAME).hdd 12 | 13 | .PHONY: run 14 | run: $(IMAGE_NAME).iso 15 | qemu-system-x86_64 -M q35 -m 2G -drive file=$(TEST_DRIVE),format=raw -cdrom $(IMAGE_NAME).iso -boot d 16 | 17 | .PHONY: debug 18 | debug: $(IMAGE_NAME).iso 19 | objcopy --only-keep-debug "./kernel/kernel.elf" "./kernel.sym" 20 | qemu-system-x86_64 -s -S -M q35 -m 2G -drive file=$(TEST_DRIVE),format=raw -cdrom $(IMAGE_NAME).iso -boot d 21 | 22 | .PHONY: run-uefi 23 | run-uefi: ovmf $(IMAGE_NAME).iso 24 | qemu-system-x86_64 -M q35 -m 2G -bios ovmf/OVMF.fd -cdrom $(IMAGE_NAME).iso -boot d 25 | 26 | .PHONY: run-hdd 27 | run-hdd: $(IMAGE_NAME).hdd 28 | qemu-system-x86_64 -M q35 -m 2G -hda $(IMAGE_NAME).hdd 29 | 30 | .PHONY: run-hdd-uefi 31 | run-hdd-uefi: ovmf $(IMAGE_NAME).hdd 32 | qemu-system-x86_64 -M q35 -m 2G -bios ovmf/OVMF.fd -hda $(IMAGE_NAME).hdd 33 | 34 | ovmf: 35 | mkdir -p ovmf 36 | cd ovmf && curl -Lo OVMF-X64.zip https://efi.akeo.ie/OVMF/OVMF-X64.zip && unzip OVMF-X64.zip 37 | 38 | limine: 39 | git clone https://github.com/limine-bootloader/limine.git --branch=v4.x-branch-binary --depth=1 40 | $(MAKE) -C limine 41 | 42 | .PHONY: kernel 43 | kernel: 44 | $(MAKE) -C kernel 45 | 46 | $(IMAGE_NAME).iso: limine kernel 47 | rm -rf iso_root 48 | mkdir -p iso_root 49 | cp kernel/kernel.elf \ 50 | limine.cfg limine/limine.sys limine/limine-cd.bin limine/limine-cd-efi.bin initrd.img iso_root/ 51 | xorriso -as mkisofs -b limine-cd.bin \ 52 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 53 | --efi-boot limine-cd-efi.bin \ 54 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 55 | iso_root -o $(IMAGE_NAME).iso 56 | limine/limine-deploy $(IMAGE_NAME).iso 57 | rm -rf iso_root 58 | 59 | $(IMAGE_NAME).hdd: limine kernel 60 | rm -f $(IMAGE_NAME).hdd 61 | dd if=/dev/zero bs=1M count=0 seek=64 of=$(IMAGE_NAME).hdd 62 | parted -s $(IMAGE_NAME).hdd mklabel gpt 63 | parted -s $(IMAGE_NAME).hdd mkpart ESP fat32 2048s 100% 64 | parted -s $(IMAGE_NAME).hdd set 1 esp on 65 | limine/limine-deploy $(IMAGE_NAME).hdd 66 | sudo losetup -Pf --show $(IMAGE_NAME).hdd >loopback_dev 67 | sudo mkfs.fat -F 32 `cat loopback_dev`p1 68 | mkdir -p img_mount 69 | sudo mount `cat loopback_dev`p1 img_mount 70 | sudo mkdir -p img_mount/EFI/BOOT 71 | sudo cp -v kernel/kernel.elf limine.cfg limine/limine.sys img_mount/ 72 | sudo cp -v limine/BOOTX64.EFI img_mount/EFI/BOOT/ 73 | sync 74 | sudo umount img_mount 75 | sudo losetup -d `cat loopback_dev` 76 | rm -rf loopback_dev img_mount 77 | 78 | .PHONY: clean 79 | clean: 80 | rm -rf iso_root $(IMAGE_NAME).iso $(IMAGE_NAME).hdd 81 | $(MAKE) -C kernel clean 82 | 83 | .PHONY: distclean 84 | distclean: clean 85 | rm -rf limine ovmf 86 | $(MAKE) -C kernel distclean 87 | -------------------------------------------------------------------------------- /kernel/devices/pic.c: -------------------------------------------------------------------------------- 1 | #include "pic.h" 2 | #include "../sys/io.h" 3 | 4 | /* reinitialize the PIC controllers, giving them specified vector offsets 5 | rather than 8h and 70h, as configured by default */ 6 | /* Helper func */ 7 | static uint16_t __pic_get_irq_reg(int ocw3) 8 | { 9 | /* OCW3 to PIC CMD to get the register values. PIC2 is chained, and 10 | * represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */ 11 | outb(PIC1_COMMAND, ocw3); 12 | outb(PIC2_COMMAND, ocw3); 13 | return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND); 14 | } 15 | 16 | /* Returns the combined value of the cascaded PICs irq request register */ 17 | uint16_t pic_get_irr(void) 18 | { 19 | return __pic_get_irq_reg(PIC_READ_IRR); 20 | } 21 | 22 | /* Returns the combined value of the cascaded PICs in-service register */ 23 | uint16_t pic_get_isr(void) 24 | { 25 | return __pic_get_irq_reg(PIC_READ_ISR); 26 | } 27 | 28 | void sendEOIPIC(uint8_t irq) 29 | { 30 | if(irq >= 8) 31 | outb(PIC2_COMMAND,PIC_EOI); 32 | 33 | outb(PIC1_COMMAND,PIC_EOI); 34 | } 35 | 36 | void setMaskIRQ(uint8_t IRQline) { 37 | uint16_t port; 38 | uint8_t value; 39 | 40 | if(IRQline < 8) { 41 | port = PIC1_DATA; 42 | } else { 43 | port = PIC2_DATA; 44 | IRQline -= 8; 45 | } 46 | value = inb(port) | (1 << IRQline); 47 | outb(port, value); 48 | } 49 | 50 | void clearMaskIRQ(uint8_t IRQline) { 51 | uint16_t port; 52 | uint8_t value; 53 | 54 | if(IRQline < 8) { 55 | port = PIC1_DATA; 56 | } else { 57 | port = PIC2_DATA; 58 | IRQline -= 8; 59 | } 60 | 61 | value = inb(port) & ~(1 << IRQline); 62 | outb(port, value); 63 | } 64 | 65 | void disableAllIRQs() { 66 | for (uint8_t i = 0; i < 16; i++) { 67 | setMaskIRQ(i); 68 | } 69 | } 70 | 71 | void enableAllIRQs() { 72 | for (uint8_t i = 0; i < 16; i++) { 73 | clearMaskIRQ(i); 74 | } 75 | } 76 | 77 | void remapPIC(int offset1, int offset2) 78 | { 79 | uint8_t a1, a2; 80 | 81 | a1 = inb(PIC1_DATA); // save masks 82 | a2 = inb(PIC2_DATA); 83 | 84 | outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) 85 | io_wait(); 86 | outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); 87 | io_wait(); 88 | outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset 89 | io_wait(); 90 | outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset 91 | io_wait(); 92 | outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) 93 | io_wait(); 94 | outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) 95 | io_wait(); 96 | 97 | outb(PIC1_DATA, ICW4_8086); 98 | io_wait(); 99 | outb(PIC2_DATA, ICW4_8086); 100 | io_wait(); 101 | 102 | outb(PIC1_DATA, a1); // restore saved masks. 103 | outb(PIC2_DATA, a2); 104 | } 105 | -------------------------------------------------------------------------------- /kernel/memory/pmm.c: -------------------------------------------------------------------------------- 1 | #include "pmm.h" 2 | #include "../lib/string.h" 3 | #include "../lib/print.h" 4 | #include "../kernel.h" 5 | #include "../limine.h" 6 | 7 | extern struct limine_memmap_response* memmap_info; 8 | static struct limine_memmap_entry* memmap; 9 | 10 | static const char* getMemoryMapType(uint32_t type) 11 | { 12 | switch (type) 13 | { 14 | case 0x1: 15 | return "Usable RAM"; 16 | case 0x2: 17 | return "Reserved"; 18 | case 0x3: 19 | return "ACPI reclaimable"; 20 | case 0x4: 21 | return "ACPI NVS"; 22 | case 0x5: 23 | return "Bad memory"; 24 | case 0x1000: 25 | return "Bootloader reclaimable"; 26 | case 0x1001: 27 | return "Kernel/Modules"; 28 | case 0x1002: 29 | return "Framebuffer"; 30 | default: 31 | return "???"; 32 | } 33 | } 34 | 35 | void printMemoryMaps() 36 | { 37 | for (size_t i = 0; i < memmap_info->entry_count; i++) 38 | { 39 | struct limine_memmap_entry* entry = memmap_info->entries[i]; 40 | switch (entry->type) 41 | { 42 | case 1: 43 | case 0x1000: 44 | case 3: 45 | term_write("Entry ", 6); 46 | printNumber(i); 47 | 48 | const char* type = getMemoryMapType(entry->type); 49 | term_write(type, strlen(type)); 50 | 51 | term_write("\nBase ", 6); 52 | printNumber(entry->base); 53 | 54 | term_write("Length ", 7); 55 | printNumber(entry->length); 56 | break; 57 | } 58 | } 59 | } 60 | 61 | void setMemoryMap(uint8_t selection) 62 | { 63 | memmap = memmap_info->entries[selection]; 64 | } 65 | 66 | void* getMemoryMapBase() 67 | { 68 | return (void*) memmap->base; 69 | } 70 | 71 | uint64_t getMemoryMapLength() 72 | { 73 | return memmap->length; 74 | } 75 | 76 | static void* b_malloc(uint64_t* base, size_t length, size_t size) 77 | { 78 | if (length <= BLOCK_SIZE) { 79 | if (size + 1 <= length && *((uint64_t*) base) == 0) { 80 | *base = size; 81 | memset(base + 1, 0, sizeof(void*) * size); 82 | return (void*) (base + 1); 83 | } 84 | else { 85 | return NULL; 86 | } 87 | } 88 | 89 | size_t half = length / 2; 90 | // Allocate if the current length is enough and unallocated 91 | if (half <= size + 1 && *((uint64_t*) base) == 0) { 92 | *base = size; 93 | memset(base + 1, 0, sizeof(void*) * size); 94 | return (void*) (base + 1); 95 | } 96 | // Try to find another block 97 | else if (half > size) 98 | { 99 | void* b = b_malloc(base, half, size); 100 | // If alloc is null, search blocks on the right side 101 | if (b == NULL) { 102 | b = b_malloc(base + half, half, size); 103 | } 104 | 105 | return b; 106 | } 107 | // Otherwise, the memory cannot be allocated 108 | return NULL; 109 | } 110 | 111 | void* k_malloc(size_t size) 112 | { 113 | return b_malloc((uint64_t*)memmap->base, memmap->length, size); 114 | } 115 | 116 | void k_free(void* base) 117 | { 118 | uint64_t size = *(((uint64_t*) base) - 1); 119 | memset(base, 0, size); 120 | } 121 | -------------------------------------------------------------------------------- /STIVALE_README.md: -------------------------------------------------------------------------------- 1 | # Stivale2 Bare Bones 2 | 3 | This repository will show you how to set up a simple 64-bit x86_64 Long Mode higher half stivale2 kernel using Limine. 4 | 5 | This project can be built using the host compiler on most Linux distros on x86_64, but it's recommended you set up an x86_64-elf [cross compiler](https://wiki.osdev.org/GCC_Cross-Compiler). 6 | 7 | It is also recommended to cross reference the contents of this repository with [the Stivale Bare Bones](https://wiki.osdev.org/Stivale_Bare_Bones) OSDev wiki page. 8 | 9 | ## Where to go from here 10 | 11 | You may be asking yourself: "what now?". So here's a list of things you may want to do to get started working 12 | on your new kernel: 13 | 14 | * Load an [IDT](https://wiki.osdev.org/Interrupt_Descriptor_Table) so that exceptions and interrupts can be handled. 15 | * Write a physical memory allocator, a good starting point is a bitmap allocator. 16 | * Write a virtual memory manager that can map, remap and unmap pages. 17 | * Begin parsing ACPI tables, the most important one is the MADT since it contains information about the APIC. 18 | * Start up the other CPUs. stivale2 provides a facility to make this less painful. 19 | * Set up an interrupt controller such as the APIC. 20 | * Configure a timer such as the Local APIC timer, the PIT, or the HPET. 21 | * Implement a scheduler to schedule threads in order make multitasking possible. 22 | * Design a virtual file system (VFS) and implement it. The traditional UNIX VFS works and saves headaches when porting software, but you can make your own thing too. 23 | * Implement a simple virtual file system like a memory-only tmpfs to avoid crippling the design of your VFS too much while implementing it alongside real storage filesystems. 24 | * Decide how to abstract devices. UNIX likes usually go for a `/dev` virtual filesystem containing device nodes and use `ioctl()` alongside standard FS calls to do operations on them. 25 | * Get a userland going by loading executables from your VFS and running them in ring 3. Set up a way to perform system calls. 26 | * Write a PCI driver. 27 | * Add support for a storage medium, the easiest and most common ones are AHCI and NVMe. 28 | 29 | 30 | At this point you should have decided what kind of interface your OS is going to provide to programs running on it, a common design that a lot of hobby operating systems use is POSIX (which derives from the UNIX design), which has both pros and cons: 31 | 32 | Pros: 33 | 34 | * Easier to port existing software that already runs on UNIX like operating systems like Linux. 35 | * The basic parts of POSIX are fairly easy to implement. 36 | * Pretty safe and sound design which has stood the test of time for over 40 years. 37 | 38 | Cons: 39 | 40 | * Restricts you to use an already existing design. 41 | * POSIX may get complex and has a lot of legacy cruft that software might rely on. 42 | 43 | Another point to consider is that a lot of software tends to depend on Linux or glibc specific features, but a portable C library like [mlibc](https://github.com/managarm/mlibc) can be used instead of implementing your own, as it provides good compatibility with POSIX/Linux software. 44 | 45 | Other options, instead of implementing POSIX in your kernel, is to add a POSIX compatibility layer on top of your native design (with the large downside of complicating the design of your OS). 46 | -------------------------------------------------------------------------------- /kernel/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Nuke built-in rules and variables. 2 | override MAKEFLAGS += -rR 3 | 4 | # This is the name that our final kernel executable will have. 5 | # Change as needed. 6 | override KERNEL := kernel.elf 7 | 8 | # Convenience macro to reliably declare user overridable variables. 9 | define DEFAULT_VAR = 10 | ifeq ($(origin $1),default) 11 | override $(1) := $(2) 12 | endif 13 | ifeq ($(origin $1),undefined) 14 | override $(1) := $(2) 15 | endif 16 | endef 17 | 18 | # It is highly recommended to use a custom built cross toolchain to build a kernel. 19 | # We are only using "cc" as a placeholder here. It may work by using 20 | # the host system's toolchain, but this is not guaranteed. 21 | $(eval $(call DEFAULT_VAR,CC,cc)) 22 | 23 | # Same thing for "ld" (the linker). 24 | $(eval $(call DEFAULT_VAR,LD,ld)) 25 | 26 | # User controllable C flags. 27 | $(eval $(call DEFAULT_VAR,CFLAGS,-g -O2 -pipe -Wall -Wextra)) 28 | 29 | # User controllable C preprocessor flags. We set none by default. 30 | $(eval $(call DEFAULT_VAR,CPPFLAGS,)) 31 | 32 | # User controllable nasm flags. 33 | $(eval $(call DEFAULT_VAR,NASMFLAGS,-F dwarf -g)) 34 | 35 | # User controllable linker flags. We set none by default. 36 | $(eval $(call DEFAULT_VAR,LDFLAGS,)) 37 | 38 | # Internal C flags that should not be changed by the user. 39 | override CFLAGS += \ 40 | -std=gnu11 \ 41 | -ffreestanding \ 42 | -fno-stack-protector \ 43 | -fno-stack-check \ 44 | -fno-lto \ 45 | -fno-pie \ 46 | -fno-pic \ 47 | -m64 \ 48 | -march=x86-64 \ 49 | -mabi=sysv \ 50 | -mno-80387 \ 51 | -mno-mmx \ 52 | -mno-sse \ 53 | -mno-sse2 \ 54 | -mno-red-zone \ 55 | -mcmodel=kernel 56 | 57 | # Internal C preprocessor flags that should not be changed by the user. 58 | override CPPFLAGS := \ 59 | -I. \ 60 | $(CPPFLAGS) \ 61 | -MMD \ 62 | -MP 63 | 64 | # Internal linker flags that should not be changed by the user. 65 | override LDFLAGS += \ 66 | -nostdlib \ 67 | -static \ 68 | -m elf_x86_64 \ 69 | -z max-page-size=0x1000 \ 70 | -T linker.ld 71 | 72 | # Check if the linker supports -no-pie and enable it if it does. 73 | ifeq ($(shell $(LD) --help 2>&1 | grep 'no-pie' >/dev/null 2>&1; echo $$?),0) 74 | override LDFLAGS += -no-pie 75 | endif 76 | 77 | # Internal nasm flags that should not be changed by the user. 78 | override NASMFLAGS += \ 79 | -f elf64 80 | 81 | # Use "find" to glob all *.c, *.S, and *.asm files in the tree and obtain the 82 | # object and header dependency file names. 83 | override CFILES := $(shell find -L . -type f -name '*.c') 84 | override ASFILES := $(shell find -L . -type f -name '*.S') 85 | override NASMFILES := $(shell find -L . -type f -name '*.asm') 86 | override OBJ := $(CFILES:.c=.o) $(ASFILES:.S=.o) $(NASMFILES:.asm=.o) 87 | override HEADER_DEPS := $(CFILES:.c=.d) $(ASFILES:.S=.d) 88 | 89 | # Default target. 90 | .PHONY: all 91 | all: $(KERNEL) 92 | 93 | limine.h: 94 | curl -Lo $@ https://github.com/limine-bootloader/limine/raw/trunk/limine.h 95 | 96 | # Link rules for the final kernel executable. 97 | $(KERNEL): $(OBJ) 98 | $(LD) $(OBJ) $(LDFLAGS) -o $@ 99 | 100 | # Include header dependencies. 101 | -include $(HEADER_DEPS) 102 | 103 | # Compilation rules for *.c files. 104 | %.o: %.c limine.h 105 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ 106 | 107 | # Compilation rules for *.S files. 108 | %.o: %.S limine.h 109 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ 110 | 111 | # Compilation rules for *.asm (nasm) files. 112 | %.o: %.asm 113 | nasm $(NASMFLAGS) $< -o $@ 114 | 115 | .PHONY: debug 116 | debug: $(KERNEL) 117 | 118 | %.o: %.c limine.h 119 | $(CC) $(CFLAGS) -g $(INTERNALCFLAGS) -c $< -o $@ 120 | 121 | # Remove object files and the final executable. 122 | .PHONY: clean 123 | clean: 124 | rm -rf $(KERNEL) $(OBJ) $(HEADER_DEPS) 125 | 126 | .PHONY: distclean 127 | distclean: clean 128 | rm -f limine.h 129 | -------------------------------------------------------------------------------- /kernel/memory/paging.c: -------------------------------------------------------------------------------- 1 | #include "paging.h" 2 | #include "pmm.h" 3 | #include "../lib/print.h" 4 | 5 | static PageTable* pml4; 6 | extern struct limine_memmap_entry** memmaps; 7 | 8 | static inline void flushTLB(void* page) 9 | { 10 | __asm__ volatile ("invlpg (%0)" :: "r" (page) : "memory"); 11 | } 12 | 13 | uint64_t readCR3(void) 14 | { 15 | uint64_t val; 16 | __asm__ volatile ( "mov %%cr3, %0" : "=r"(val) ); 17 | return val; 18 | } 19 | 20 | PageTable* initPML4() 21 | { 22 | uintptr_t cr3 = (uintptr_t) readCR3(); 23 | pml4 = (PageTable*) ((cr3 >> 12) << 12); 24 | 25 | return pml4; 26 | } 27 | 28 | void setPageTableEntry(PageEntry* entry, uint8_t flags, uintptr_t physical_address, uint16_t available) 29 | { 30 | entry->present = (flags >> 1) & 1; 31 | entry->writable = (flags >> 2) & 1; 32 | entry->user_accessible = (flags >> 3) & 1; 33 | entry->write_through_caching = (flags & 0x8) & 1; 34 | entry->disable_cache = (flags & 0x10) & 1; 35 | entry->null = (flags & 0x20) & 1; 36 | entry->global = (flags & 0x40) & 1; 37 | entry->avl1 = available & 0x3; 38 | entry->physical_address = physical_address; 39 | entry->avl2 = available >> 3; 40 | entry->no_execute = flags >> 7; 41 | } 42 | 43 | void* getPhysicalAddress(void* virtual_address) 44 | { 45 | uintptr_t address = (uintptr_t) virtual_address; 46 | 47 | uint64_t offset = address & 0xFFF; 48 | uint64_t page_table_index = (address >> 12) & 0x1FF; 49 | uint64_t page_directory_index = (address >> 21) & 0x1FF; 50 | uint64_t page_directory_pointer_index = (address >> 30) & 0x1FF; 51 | uint64_t pml4_index = (address >> 39) & 0x1FF; 52 | 53 | PageTable* page_directory_pointer = (PageTable*) ((uint64_t) (pml4->entries[pml4_index].physical_address) << 12); 54 | PageTable* page_directory = (PageTable*) ((uint64_t) (page_directory_pointer->entries[page_directory_pointer_index].physical_address) << 12); 55 | PageTable* page_table = (PageTable*) ((uint64_t) (page_directory->entries[page_directory_index].physical_address) << 12); 56 | 57 | return (void*) ((page_table->entries[page_table_index].physical_address << 12) + offset); 58 | } 59 | 60 | static void allocateEntry(PageTable* table, size_t index, uint8_t flags) 61 | { 62 | void* physical_address = k_malloc(4096); 63 | setPageTableEntry(&(table->entries[index]), flags, (uintptr_t) physical_address >> 12, 0); 64 | } 65 | 66 | 67 | void mapPage(void* virtual_address, void* physical_address, uint8_t flags) 68 | { 69 | // Make sure that both addresses are page-aligned. 70 | uintptr_t virtual_address_int = (uintptr_t) virtual_address; 71 | uintptr_t physical_address_int = (uintptr_t) physical_address; 72 | 73 | uint64_t pml4_index = (virtual_address_int >> 39) & 0x1FF; 74 | uint64_t page_directory_pointer_index = (virtual_address_int >> 30) & 0x1FF; 75 | uint64_t page_directory_index = (virtual_address_int >> 21) & 0x1FF; 76 | uint64_t page_table_index = (virtual_address_int >> 12) & 0x1FF; 77 | 78 | if (!pml4->entries[pml4_index].present) 79 | allocateEntry(pml4, pml4_index, flags); 80 | 81 | PageTable* page_directory_pointer = (PageTable*) (uint64_t) (pml4->entries[pml4_index].physical_address << 12); 82 | 83 | if (!page_directory_pointer->entries[page_directory_pointer_index].present) 84 | allocateEntry(page_directory_pointer, page_directory_pointer_index, flags); 85 | 86 | PageTable* page_directory = (PageTable*) (uint64_t) (page_directory_pointer->entries[page_directory_pointer_index].physical_address << 12); 87 | 88 | if (!page_directory->entries[page_directory_index].present) 89 | allocateEntry(page_directory, page_directory_index, flags); 90 | 91 | PageTable* page_table = (PageTable*) (uint64_t) (page_directory->entries[page_directory_index].physical_address << 12); 92 | 93 | setPageTableEntry(&(page_table->entries[page_table_index]), flags, physical_address_int >> 12, 0); 94 | 95 | // Now you need to flush the entry in the TLB 96 | flushTLB(virtual_address); 97 | } 98 | 99 | -------------------------------------------------------------------------------- /kernel/devices/pci.c: -------------------------------------------------------------------------------- 1 | #include "../interrupts/isr.h" 2 | #include "../sys/io.h" 3 | #include "pci.h" 4 | #include "ioapic.h" 5 | #include "../lib/print.h" 6 | #include "../kernel.h" 7 | #include 8 | 9 | uint16_t pciConfigReadWord(uint8_t bus, uint8_t device, uint8_t func, uint8_t offset) 10 | { 11 | uint32_t lbus = (uint32_t)bus; 12 | uint32_t ldevice = (uint32_t)device; 13 | uint32_t lfunc = (uint32_t)func; 14 | uint16_t tmp = 0; 15 | 16 | // Create configuration address 17 | uint32_t address = (uint32_t)((lbus << 16) | (ldevice << 11) | (lfunc << 8) | (offset & 0xFC) | ((uint32_t)0x80000000)); 18 | 19 | // Write out the address 20 | out(0xCF8, address); 21 | // Read in the data 22 | // (offset & 2) * 8) = 0 will choose the first word of the 32-bit register 23 | tmp = (uint16_t)((in(0xCFC) >> ((offset & 2) * 8)) & 0xFFFF); 24 | return tmp; 25 | } 26 | 27 | uint16_t getVendorID(uint16_t bus, uint16_t device, uint16_t function) 28 | { 29 | /* Try and read the first configuration register. Since there are no 30 | * vendors that == 0xFFFF, it must be a non-existent device. */ 31 | return pciConfigReadWord(bus, device, function, 0); 32 | } 33 | 34 | uint16_t getHeaderType(uint16_t bus, uint16_t device, uint16_t function) 35 | { 36 | /* Try and read the first configuration register. Since there are no 37 | * vendors that == 0xFFFF, it must be a non-existent device. */ 38 | return pciConfigReadWord(bus, device, function, (0xC + 2) & 0xFF); 39 | } 40 | 41 | bool checkFunction(uint8_t bus, uint8_t device, uint8_t function) 42 | { 43 | return getHeaderType(bus, device, function) != 0xFFFF; 44 | } 45 | 46 | void checkDevice(uint8_t bus, uint8_t device) 47 | { 48 | uint8_t function = 0; 49 | 50 | if (!checkFunction(bus, device, 0)) return; 51 | 52 | uint16_t headerType = getHeaderType(bus, device, function); 53 | if ((headerType & 0x80) != 0) 54 | { 55 | // It's a multi-function device, so check remaining functions 56 | for (function = 1; function < 8; function++) 57 | { 58 | if (getVendorID(bus, device, function) != 0xFFFF) 59 | { 60 | 61 | } 62 | } 63 | } 64 | } 65 | 66 | void checkAllBuses(void) 67 | { 68 | for (size_t bus = 0; bus < 256; bus++) 69 | { 70 | for (size_t device = 0; device < 32; device++) 71 | { 72 | checkDevice(bus, device); 73 | } 74 | } 75 | } 76 | 77 | static void pciInterruptHandler(InterruptFrame* frame) 78 | { 79 | printNumber(frame->int_no); 80 | } 81 | 82 | void enablePCIInterrupts(uint8_t bus, uint8_t device, uint8_t function, size_t ioapicaddr) 83 | { 84 | uint16_t interrupt = pciConfigReadWord(bus, device, function, 0x3E); 85 | uint16_t interrupt_line = interrupt & 0xFF; 86 | uint16_t interrupt_pin = interrupt >> 8; 87 | // TODO: use AML to figure out which interrupt line to use 88 | writeIOAPIC(ioapicaddr, interrupt_line * 2 + 0x10, 0x20 + interrupt_line); 89 | registerInterruptHandler(0x20 + interrupt_line, &pciInterruptHandler); 90 | } 91 | 92 | void checkMSI(uint8_t bus, uint8_t device, uint8_t func) 93 | { 94 | uint16_t status = pciConfigReadWord(bus, device, func, 0x4); 95 | printNumber(pciConfigReadWord(bus, device, func, 0x20)); 96 | printNumber(pciConfigReadWord(bus, device, func, 0x22)); 97 | printNumber(pciConfigReadWord(bus, device, func, 0x24)); 98 | printNumber(pciConfigReadWord(bus, device, func, 0x26)); 99 | if (status & 0x10) 100 | { 101 | uint16_t capabilities_pointer = pciConfigReadWord(bus, device, func, 0x36); 102 | uint16_t capability = pciConfigReadWord(bus, device, func, capabilities_pointer + 0x2); 103 | if ((capability & 0xFF) == 5) 104 | { 105 | term_write("MSI Enabled!", 13); 106 | } 107 | else 108 | { 109 | printNumber(capability >> 8); 110 | } 111 | } 112 | else 113 | { 114 | term_write("No MSI!", 8); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /kernel/filesystem/initrd.c: -------------------------------------------------------------------------------- 1 | #include "initrd.h" 2 | #include "file.h" 3 | #include "../lib/string.h" 4 | #include "../memory/pmm.h" 5 | #include "../limine.h" 6 | #include "../kernel.h" 7 | 8 | struct dirent* root_dirents; 9 | fs_node_t* root_nodes; 10 | 11 | initrd_header_t* initrd_header; 12 | initrd_file_header_t* file_headers; 13 | size_t nroot_nodes; 14 | 15 | static size_t initrd_read(fs_node_t* node, size_t offset, size_t size, uint8_t* buffer) 16 | { 17 | initrd_file_header_t header = file_headers[node->inode - 2]; 18 | if (offset > header.length) 19 | return 0; 20 | if (offset + size > header.length) 21 | size = header.length - offset; 22 | memcpy(buffer, (uint8_t*) (header.offset + offset), size); 23 | return size; 24 | } 25 | 26 | static struct dirent* initrd_readdir(fs_node_t* node, size_t index) 27 | { 28 | if (index >= nroot_nodes) { 29 | return NULL; 30 | } 31 | 32 | return root_dirents + index; 33 | } 34 | 35 | static fs_node_t* initrd_finddir(fs_node_t* node, char* name) 36 | { 37 | for (size_t i = 0; i < nroot_nodes; i++) { 38 | if (!strcmp(name, root_dirents[i].name)) 39 | return root_nodes + i; 40 | } 41 | return 0; 42 | } 43 | 44 | fs_node_t* initialise_initrd(size_t location) 45 | { 46 | // Initialise the main and file header pointers and populate the root directory. 47 | initrd_header = (initrd_header_t*) location; 48 | file_headers = (initrd_file_header_t*) (location + sizeof(initrd_header_t)); 49 | 50 | // Initialize root nodes and root directory entries 51 | root_nodes = (fs_node_t*) k_malloc(sizeof(fs_node_t) * (initrd_header->nfiles + 2)); 52 | root_dirents = (struct dirent*) k_malloc(sizeof(struct dirent) * (initrd_header->nfiles + 2)); 53 | 54 | // Add root directory entry 55 | strcpy(root_dirents[0].name, "/"); 56 | root_dirents[0].ino = 0; 57 | 58 | // Initialise the root directory. 59 | root_nodes[0].mask = 0; 60 | root_nodes[0].uid = 0; 61 | root_nodes[0].gid = 0; 62 | root_nodes[0].inode = 0; 63 | root_nodes[0].length = 0; 64 | root_nodes[0].flags = FS_DIRECTORY; 65 | root_nodes[0].read = NULL; 66 | root_nodes[0].write = NULL; 67 | root_nodes[0].open = NULL; 68 | root_nodes[0].close = NULL; 69 | root_nodes[0].readdir = &initrd_readdir; 70 | root_nodes[0].finddir = &initrd_finddir; 71 | root_nodes[0].ptr = 0; 72 | root_nodes[0].impl = 0; 73 | 74 | // Add dev directory entry 75 | strcpy(root_dirents[1].name, "dev"); 76 | root_dirents[1].ino = 1; 77 | 78 | // Initialise the /dev directory 79 | root_nodes[1].mask = 0; 80 | root_nodes[1].uid = 0; 81 | root_nodes[1].gid = 0; 82 | root_nodes[1].inode = 1; 83 | root_nodes[1].length = 0; 84 | root_nodes[1].flags = FS_DIRECTORY; 85 | root_nodes[1].read = NULL; 86 | root_nodes[1].write = NULL; 87 | root_nodes[1].open = NULL; 88 | root_nodes[1].close = NULL; 89 | root_nodes[1].readdir = &initrd_readdir; 90 | root_nodes[1].finddir = &initrd_finddir; 91 | root_nodes[1].ptr = NULL; 92 | root_nodes[1].impl = 0; 93 | nroot_nodes = initrd_header->nfiles + 2; 94 | // For every file... 95 | for (size_t i = 2; i < nroot_nodes; i++) 96 | { 97 | // Edit the file's header - currently it holds the file offset 98 | // relative to the start of the ramdisk. We want it relative to the start 99 | // of memory. 100 | file_headers[i - 2].offset += location; 101 | // Add file to the directory entries 102 | strcpy(root_dirents[i].name, file_headers[i - 2].name); 103 | root_dirents[i].ino = i; 104 | 105 | // Create a new file node. 106 | root_nodes[i].inode = i; 107 | root_nodes[i].mask = 0; 108 | root_nodes[i].uid = 0; 109 | root_nodes[i].gid = 0; 110 | root_nodes[i].length = file_headers[i - 2].length; 111 | root_nodes[i].inode = i; 112 | root_nodes[i].flags = FS_FILE; 113 | root_nodes[i].read = &initrd_read; 114 | root_nodes[i].write = NULL; 115 | root_nodes[i].readdir = NULL; 116 | root_nodes[i].finddir = NULL; 117 | root_nodes[i].open = NULL; 118 | root_nodes[i].close = NULL; 119 | root_nodes[i].impl = 0; 120 | } 121 | return root_nodes; 122 | } 123 | -------------------------------------------------------------------------------- /kernel/devices/apic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "apic.h" 5 | #include "../kernel.h" 6 | #include "pic.h" 7 | #include "pit.h" 8 | #include "../interrupts/isr.h" 9 | #include "../lib/print.h" 10 | 11 | #define IA32_APIC_BASE_MSR 0x1B 12 | #define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP 13 | #define IA32_APIC_BASE_MSR_ENABLE 0x800 14 | #define APIC_EOI_REGISTER 0xB0 15 | 16 | extern void term_write(const char*, size_t); 17 | 18 | enum 19 | { 20 | CPUID_FEAT_ECX_SSE3 = 1 << 0, 21 | CPUID_FEAT_ECX_PCLMUL = 1 << 1, 22 | CPUID_FEAT_ECX_DTES64 = 1 << 2, 23 | CPUID_FEAT_ECX_MONITOR = 1 << 3, 24 | CPUID_FEAT_ECX_DS_CPL = 1 << 4, 25 | CPUID_FEAT_ECX_VMX = 1 << 5, 26 | CPUID_FEAT_ECX_SMX = 1 << 6, 27 | CPUID_FEAT_ECX_EST = 1 << 7, 28 | CPUID_FEAT_ECX_TM2 = 1 << 8, 29 | CPUID_FEAT_ECX_SSSE3 = 1 << 9, 30 | CPUID_FEAT_ECX_CID = 1 << 10, 31 | CPUID_FEAT_ECX_SDBG = 1 << 11, 32 | CPUID_FEAT_ECX_FMA = 1 << 12, 33 | CPUID_FEAT_ECX_CX16 = 1 << 13, 34 | CPUID_FEAT_ECX_XTPR = 1 << 14, 35 | CPUID_FEAT_ECX_PDCM = 1 << 15, 36 | CPUID_FEAT_ECX_PCID = 1 << 17, 37 | CPUID_FEAT_ECX_DCA = 1 << 18, 38 | CPUID_FEAT_ECX_SSE4_1 = 1 << 19, 39 | CPUID_FEAT_ECX_SSE4_2 = 1 << 20, 40 | CPUID_FEAT_ECX_X2APIC = 1 << 21, 41 | CPUID_FEAT_ECX_MOVBE = 1 << 22, 42 | CPUID_FEAT_ECX_POPCNT = 1 << 23, 43 | CPUID_FEAT_ECX_TSC = 1 << 24, 44 | CPUID_FEAT_ECX_AES = 1 << 25, 45 | CPUID_FEAT_ECX_XSAVE = 1 << 26, 46 | CPUID_FEAT_ECX_OSXSAVE = 1 << 27, 47 | CPUID_FEAT_ECX_AVX = 1 << 28, 48 | CPUID_FEAT_ECX_F16C = 1 << 29, 49 | CPUID_FEAT_ECX_RDRAND = 1 << 30, 50 | CPUID_FEAT_ECX_HYPERVISOR = 1 << 31, 51 | 52 | CPUID_FEAT_EDX_FPU = 1 << 0, 53 | CPUID_FEAT_EDX_VME = 1 << 1, 54 | CPUID_FEAT_EDX_DE = 1 << 2, 55 | CPUID_FEAT_EDX_PSE = 1 << 3, 56 | CPUID_FEAT_EDX_TSC = 1 << 4, 57 | CPUID_FEAT_EDX_MSR = 1 << 5, 58 | CPUID_FEAT_EDX_PAE = 1 << 6, 59 | CPUID_FEAT_EDX_MCE = 1 << 7, 60 | CPUID_FEAT_EDX_CX8 = 1 << 8, 61 | CPUID_FEAT_EDX_APIC = 1 << 9, 62 | CPUID_FEAT_EDX_SEP = 1 << 11, 63 | CPUID_FEAT_EDX_MTRR = 1 << 12, 64 | CPUID_FEAT_EDX_PGE = 1 << 13, 65 | CPUID_FEAT_EDX_MCA = 1 << 14, 66 | CPUID_FEAT_EDX_CMOV = 1 << 15, 67 | CPUID_FEAT_EDX_PAT = 1 << 16, 68 | CPUID_FEAT_EDX_PSE36 = 1 << 17, 69 | CPUID_FEAT_EDX_PSN = 1 << 18, 70 | CPUID_FEAT_EDX_CLFLUSH = 1 << 19, 71 | CPUID_FEAT_EDX_DS = 1 << 21, 72 | CPUID_FEAT_EDX_ACPI = 1 << 22, 73 | CPUID_FEAT_EDX_MMX = 1 << 23, 74 | CPUID_FEAT_EDX_FXSR = 1 << 24, 75 | CPUID_FEAT_EDX_SSE = 1 << 25, 76 | CPUID_FEAT_EDX_SSE2 = 1 << 26, 77 | CPUID_FEAT_EDX_SS = 1 << 27, 78 | CPUID_FEAT_EDX_HTT = 1 << 28, 79 | CPUID_FEAT_EDX_TM = 1 << 29, 80 | CPUID_FEAT_EDX_IA64 = 1 << 30, 81 | CPUID_FEAT_EDX_PBE = 1 << 31 82 | }; 83 | 84 | const uint32_t CPUID_FLAG_MSR = 1 << 5; 85 | 86 | static int cpuHasMSR() 87 | { 88 | uint32_t eax, edx; 89 | cpuid(1, &eax, &edx); 90 | return edx & CPUID_FLAG_MSR; 91 | } 92 | 93 | void cpuGetMSR(uint32_t msr, uint32_t *lo, uint32_t *hi) 94 | { 95 | __asm__ volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); 96 | } 97 | 98 | void cpuSetMSR(uint32_t msr, uint32_t lo, uint32_t hi) 99 | { 100 | __asm__ volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); 101 | } 102 | 103 | uintptr_t cpuGetAPICBase() 104 | { 105 | uint32_t eax, edx; 106 | cpuGetMSR(IA32_APIC_BASE_MSR, &eax, &edx); 107 | 108 | #ifdef __PHYSICAL_MEMORY_EXTENSION__ 109 | return (eax & 0xfffff000) | ((edx & 0x0f) << 32); 110 | #else 111 | return (eax & 0xfffff000); 112 | #endif 113 | } 114 | 115 | /* Set the physical address for local APIC registers */ 116 | void cpuSetAPICBase(uintptr_t apic) 117 | { 118 | uint32_t edx = 0; 119 | uint32_t eax = (apic & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE; 120 | 121 | #ifdef __PHYSICAL_MEMORY_EXTENSION__ 122 | edx = (apic >> 32) & 0x0f; 123 | #endif 124 | 125 | cpuSetMSR(IA32_APIC_BASE_MSR, eax, edx); 126 | } 127 | 128 | static int getModel(void) 129 | { 130 | int ebx, unused; 131 | __cpuid(0, unused, ebx, unused, unused); 132 | return ebx; 133 | } 134 | 135 | static int checkAPIC(void) 136 | { 137 | unsigned int eax, unused, edx; 138 | __get_cpuid(1, &eax, &unused, &unused, &edx); 139 | return edx & CPUID_FEAT_EDX_APIC; 140 | } 141 | 142 | uint32_t readAPICRegister(uint32_t reg) 143 | { 144 | return *((volatile uint32_t *)(cpuGetAPICBase() + reg)); 145 | } 146 | 147 | void writeAPICRegister(uint32_t reg, uint32_t value) 148 | { 149 | *((volatile uint32_t *)(cpuGetAPICBase() + reg)) = value; 150 | } 151 | 152 | void enableAPIC(void) 153 | { 154 | term_write("Enabling APIC\n", 14); 155 | // disable PIC 156 | __asm__ volatile ("mov $0xff, %al;" 157 | "out %al, $0xa1;" 158 | "out %al, $0x21;"); 159 | disableAllIRQs(); 160 | /* Hardware enable the Local APIC if it wasn't enabled */ 161 | cpuSetAPICBase(cpuGetAPICBase()); 162 | printNumber(cpuGetAPICBase()); 163 | 164 | /* Set the Spurious Interrupt Vector Register bit 8 to start receiving interrupts */ 165 | writeAPICRegister(0xF0, readAPICRegister(0xF0) | 0x1FF); 166 | term_write("APIC enabled!\n", 14); 167 | } 168 | 169 | 170 | // APIC TIMER 171 | static void APIC_timer_callback(InterruptFrame* frame) 172 | { 173 | // do timer stuff 174 | writeAPICRegister(0xB0, 0); 175 | } 176 | 177 | void enableAPICTimer(uint32_t frequency) 178 | { 179 | initPIT(frequency); 180 | // Enable APIC Timer 181 | writeAPICRegister(0x3E0, 0x3); 182 | writeAPICRegister(0x380, 0xFFFFFFFF); 183 | // Sleep for 10 ms 184 | term_write("Calibrating APIC Timer\n", 24); 185 | PIT_sleep(10); 186 | term_write("Finished Calibration\n", 22); 187 | // Mask APIC Timer interrupt 188 | writeAPICRegister(0x320, 0x10000); 189 | // Reinitialize APIC timer with calculated APIC ticks 190 | uint32_t apic_ticks = 0xFFFFFFFF - readAPICRegister(0x390); 191 | 192 | writeAPICRegister(0x320, 0x20 | 0x60000); 193 | writeAPICRegister(0x3E0, 0x3); 194 | writeAPICRegister(0x380, apic_ticks); 195 | 196 | registerInterruptHandler(32, &APIC_timer_callback); 197 | } 198 | -------------------------------------------------------------------------------- /kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "devices/ahci.h" 2 | #include "devices/apic.h" 3 | #include "devices/ioapic.h" 4 | #include "devices/pci.h" 5 | #include "devices/pic.h" 6 | #include "devices/pit.h" 7 | #include "devices/ps2.h" 8 | #include "devices/serial.h" 9 | #include "filesystem/file.h" 10 | #include "filesystem/initrd.h" 11 | #include "interfaces/description_tables/madt.h" 12 | #include "interrupts/idt.h" 13 | #include "lib/print.h" 14 | #include "memory/paging.h" 15 | #include "memory/pmm.h" 16 | #include "process/task.h" 17 | #include "limine.h" 18 | #include "lib/string.h" 19 | 20 | static volatile struct limine_terminal_request terminal_request = { 21 | .id = LIMINE_TERMINAL_REQUEST, 22 | .revision = 0 23 | }; 24 | 25 | static volatile struct limine_rsdp_request rsdp_request = { 26 | .id = LIMINE_RSDP_REQUEST, 27 | .revision = 0 28 | }; 29 | 30 | static volatile struct limine_memmap_request memmap_request = { 31 | .id = LIMINE_MEMMAP_REQUEST, 32 | .revision = 0 33 | }; 34 | 35 | static volatile struct limine_smp_request smp_request = { 36 | .id = LIMINE_SMP_REQUEST, 37 | .revision = 0, 38 | .flags = 1 39 | }; 40 | 41 | static volatile struct limine_stack_size_request stack_size_request = { 42 | .id = LIMINE_STACK_SIZE_REQUEST, 43 | .revision = 0, 44 | .stack_size = 8192, 45 | }; 46 | 47 | static volatile struct limine_module_request module_request = { 48 | .id = LIMINE_MODULE_REQUEST, 49 | .revision = 0 50 | }; 51 | 52 | extern RSDPDescriptor20 *rsdp_descriptor; 53 | extern XSDT* xsdt; 54 | extern RSDT* rsdt; 55 | extern MADT* madt; 56 | 57 | extern ProcessorAPIC* processor_apics[]; 58 | extern IOAPIC* ioapics[]; 59 | extern IOAPICSourceOverride* ioapic_source_overrides[]; 60 | extern IOAPICNonMaskableInterruptSource* ioapic_interrupt_sources[]; 61 | extern IOAPICNonMaskableInterrupt* ioapic_interrupts[]; 62 | extern LAPICAddressOverride* lapic_address_overrides[]; 63 | extern x2LAPIC* x2_lapics[]; 64 | 65 | struct limine_terminal* main_terminal; 66 | struct limine_memmap_response* memmap_info; 67 | 68 | extern fs_node_t* fs_root; 69 | extern initrd_header_t* initrd_header; 70 | 71 | extern void term_write(const char *string, size_t length) { 72 | terminal_request.response->write(main_terminal, string, length); 73 | } 74 | 75 | // Kernel entrypoint 76 | void _start(void) { 77 | // set up terminal 78 | main_terminal = terminal_request.response->terminals[0]; 79 | 80 | // Access RSDP for ACPI 81 | rsdp_descriptor = (RSDPDescriptor20*) rsdp_request.response->address; 82 | if (rsdp_descriptor->descriptor10.revision == 2) 83 | { 84 | xsdt = (XSDT*)rsdp_descriptor->xsdt_address; 85 | } 86 | 87 | rsdt = (RSDT*)(uintptr_t)rsdp_descriptor->descriptor10.rsdt_address; 88 | 89 | if ((validateRSDPChecksum() & 0xFF) == 0) 90 | { 91 | term_write("ACPI ready to go\n", 18); 92 | printNumber(rsdp_descriptor->descriptor10.revision); 93 | } 94 | term_write(rsdt->h.signature, 4); 95 | 96 | // Find FADT and enable ACPI mode there 97 | ACPISDTHeader* fadt = findHeader("FACP"); 98 | if (fadt) 99 | { 100 | term_write(fadt->signature, 4); 101 | } 102 | 103 | // Initialize MADT 104 | initMADT(); 105 | term_write("\nfinding APICS\n", 15); 106 | parseMADT(); 107 | term_write("found APICS\n", 13); 108 | 109 | term_write("testing results\n", 16); 110 | 111 | term_write("my results\n", 12); 112 | printNumber(madt->header.length); 113 | printNumber(madt->APIC_address); 114 | printNumber(ioapics[0]->global_system_interrupt_base); 115 | 116 | // Get LAPIC info 117 | for (uint8_t i = 0; i < 5; i++) 118 | { 119 | term_write("IOAPIC\n", 7); 120 | printNumber(ioapic_source_overrides[i]->bus_source); 121 | printNumber(ioapic_source_overrides[i]->IRQ_source); 122 | printNumber(ioapic_source_overrides[i]->global_system_interrupt); 123 | } 124 | 125 | 126 | struct limine_smp_response* smp_response = smp_request.response; 127 | if (smp_response) 128 | { 129 | term_write("limine's results\n", 18); 130 | printNumber(smp_response->cpu_count); 131 | printNumber(smp_response->flags); 132 | for (uint64_t i = 0; i < smp_response->cpu_count; i++) { 133 | printNumber(smp_response->cpus[i]->lapic_id); 134 | printNumber(smp_response->cpus[i]->processor_id); 135 | } 136 | } 137 | 138 | term_write("results done\n", 14); 139 | 140 | // Initialize paging and memory management 141 | memmap_info = memmap_request.response; 142 | initPML4(); 143 | printMemoryMaps(); 144 | setMemoryMap(4); 145 | 146 | /* test that paging works 147 | 148 | uint64_t* p = getPhysicalAddress((void*) 0x9000); 149 | uint64_t* f = getPhysicalAddress((void*) 0xA000); 150 | 151 | mapPage(0x9000, (void*) 0x1000, 3); 152 | mapPage(0xA000, (void*) 0x1000, 3); 153 | 154 | term_write("goodbye\n", 8); 155 | p = getPhysicalAddress((void*) 0x9001); 156 | f = getPhysicalAddress((void*) 0xA000); 157 | 158 | uint64_t* y = (uint64_t*) 0x9000; 159 | *y = 100; 160 | printNumber(*y); 161 | uint64_t* z = (uint64_t*) 0xA000; 162 | printNumber(*z); 163 | 164 | 165 | printNumber(p); 166 | printNumber(f); 167 | 168 | */ 169 | 170 | enableSerialCOM1(ioapics[0]->address); 171 | checkMSI(0, 31, 2); 172 | 173 | // Test AHCI drivers for a successful read 174 | HBA_MEM* host = (HBA_MEM*) 0xFEBD5000; 175 | probePort(host); 176 | 177 | uint16_t* s = k_malloc(0x8000); 178 | if (ahci_read(&host->ports[0], 2, 0, 1, s)) 179 | { 180 | term_write("\nFile successfully read!\n", 25); 181 | } 182 | 183 | uint8_t* c = k_malloc(4096); 184 | c[0] = 'h'; 185 | c[1] = 'i'; 186 | c[2] = '_'; 187 | c[3] = 't'; 188 | c[4] = 'h'; 189 | c[5] = 'e'; 190 | c[6] = 'r'; 191 | c[7] = 'e'; 192 | c[8] = '\0'; 193 | 194 | struct limine_file* module = module_request.response->modules[0]; 195 | 196 | // start initrd 197 | size_t initrd_location = (size_t) module->address; 198 | fs_root = initialise_initrd(initrd_location); 199 | 200 | int i = 0; 201 | struct dirent* node = NULL; 202 | uint8_t buf[256]; 203 | while ((node = readdir_fs(fs_root, i)) != NULL) 204 | { 205 | term_write("Found file ", strlen("Found file ")); 206 | term_write(node->name, strlen(node->name)); 207 | fs_node_t* fsnode = finddir_fs(fs_root, node->name); 208 | 209 | if ((fsnode->flags & 0x7) == FS_DIRECTORY) 210 | term_write("\n\t(directory)\n", strlen("\n\t(directory)\n")); 211 | else 212 | { 213 | term_write("\n\t contents: \"", strlen("\n\t contents: \"")); 214 | read_fs(fsnode, 0, 256, buf); 215 | term_write(buf, strlen(buf)); 216 | term_write("\"\n", strlen("\"\n")); 217 | memset(buf, 0, 256); 218 | } 219 | i++; 220 | } 221 | 222 | // Initialize interrupt & interrupt devices 223 | remapPIC(0x20, 0x28); 224 | initIdt(); 225 | 226 | enableAPIC(); 227 | enableAPICTimer(10); 228 | enableKeyboard(ioapics[0]->address); 229 | initTasking(); 230 | doIt(); 231 | 232 | for (;;) 233 | { 234 | __asm__ volatile ("hlt"); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /kernel/devices/ahci.c: -------------------------------------------------------------------------------- 1 | #include "ahci.h" 2 | #include "../lib/string.h" 3 | #include "../lib/print.h" 4 | #include "../kernel.h" 5 | 6 | static void traceAHCI(const char* str) 7 | { 8 | term_write(str, strlen(str)); 9 | } 10 | 11 | // Check device type 12 | static int checkType(HBA_PORT* port) 13 | { 14 | uint32_t ssts = port->ssts; 15 | 16 | uint8_t ipm = (ssts >> 8) & 0x0F; 17 | uint8_t det = ssts & 0x0F; 18 | 19 | if (det != HBA_PORT_DET_PRESENT) // Check drive status 20 | return AHCI_DEV_NULL; 21 | if (ipm != HBA_PORT_IPM_ACTIVE) 22 | return AHCI_DEV_NULL; 23 | 24 | switch (port->sig) 25 | { 26 | case SATA_SIG_ATAPI: 27 | return AHCI_DEV_SATAPI; 28 | case SATA_SIG_SEMB: 29 | return AHCI_DEV_SEMB; 30 | case SATA_SIG_PM: 31 | return AHCI_DEV_PM; 32 | default: 33 | return AHCI_DEV_SATA; 34 | } 35 | } 36 | 37 | void probePort(HBA_MEM *abar) 38 | { 39 | // Search disk in implemented ports 40 | uint32_t pi = abar->pi; 41 | for (size_t i = 0; i < 32; i++) 42 | { 43 | if (pi & 1) 44 | { 45 | switch (checkType(&abar->ports[i])) 46 | { 47 | case AHCI_DEV_SATA: 48 | traceAHCI("SATA drive found at port \n"); 49 | break; 50 | case AHCI_DEV_SATAPI: 51 | traceAHCI("SATAPI drive found at port \n"); 52 | break; 53 | case AHCI_DEV_SEMB: 54 | traceAHCI("SEMB drive found at port \n"); 55 | break; 56 | case AHCI_DEV_PM: 57 | traceAHCI("PM drive found at port \n"); 58 | break; 59 | default: 60 | traceAHCI("No drive found at port \n"); 61 | } 62 | printNumber(i); 63 | } 64 | pi >>= 1; 65 | } 66 | } 67 | 68 | // Start command engine 69 | void startCMD(HBA_PORT *port) 70 | { 71 | // Wait until CR (bit15) is cleared 72 | while (port->cmd & HBA_PxCMD_CR); 73 | 74 | // Set FRE (bit4) and ST (bit0) 75 | port->cmd |= HBA_PxCMD_FRE; 76 | port->cmd |= HBA_PxCMD_ST; 77 | } 78 | 79 | // Stop command engine 80 | void stopCMD(HBA_PORT *port) 81 | { 82 | // Clear ST (bit0) 83 | port->cmd &= ~HBA_PxCMD_ST; 84 | 85 | // Clear FRE (bit4) 86 | port->cmd &= ~HBA_PxCMD_FRE; 87 | 88 | // Wait until FR (bit14), CR (bit15) are cleared 89 | while (true) 90 | { 91 | if (port->cmd & HBA_PxCMD_FR) 92 | continue; 93 | if (port->cmd & HBA_PxCMD_CR) 94 | continue; 95 | break; 96 | } 97 | } 98 | 99 | void portRebase(HBA_PORT *port, int port_no) 100 | { 101 | stopCMD(port); // Stop command engine 102 | 103 | // Command list offset: 1K * port_no 104 | // Command list entry size = 32 105 | // Command list entry maximum count = 32 106 | // Command list maximum size = 32 * 32 = 1K per port 107 | port->clb = AHCI_BASE + (port_no << 10); 108 | port->clbu = 0; 109 | memset((void*) (uint64_t) (port->clb), 0, 0x400); 110 | 111 | // FIS offset: 32K + 256 * port_no 112 | // FIS entry size = 256 bytes per port 113 | port->fb = AHCI_BASE + (32 << 10) + (port_no << 8); 114 | port->fbu = 0; 115 | memset((void*) (uint64_t) (port->fb), 0, 0x100); 116 | 117 | // Command table offset: 40K + 8K * port_no 118 | // Command table size = 256 * 32 = 8K per port 119 | HBA_CMD_HEADER* cmd_header = (HBA_CMD_HEADER*) (uint64_t) (port->clb); 120 | for (size_t i = 0; i < 32; i++) 121 | { 122 | // 8 prdt entries per command table 123 | // 256 bytes per command table, 64+16+48+16*8 124 | cmd_header[i].prdtl = 8; 125 | 126 | 127 | // Command table offset: 40K + 8K*port_no + cmd_header_index*256 128 | cmd_header[i].ctba = AHCI_BASE + (40 << 10) + (port_no << 13) + (i << 8); 129 | cmd_header[i].ctbau = 0; 130 | memset((void*) (uint64_t) cmd_header[i].ctba, 0, 0x100); 131 | } 132 | // Start command engine 133 | startCMD(port); 134 | } 135 | 136 | // Find a free command list slot 137 | int findCMDSlot(HBA_PORT* port, size_t cmd_slots) 138 | { 139 | // If not set in SACT and CI, the slot is free 140 | uint32_t slots = port->sact | port->ci; 141 | for (uint32_t i = 0; i < cmd_slots; i++) 142 | { 143 | if (!(slots & 1)) 144 | return i; 145 | slots >>= 1; 146 | } 147 | traceAHCI("Cannot find free command list entry\n"); 148 | return -1; 149 | } 150 | 151 | static bool runCommand(FIS_TYPE type, uint8_t write, HBA_PORT *port, uint32_t start_l, uint32_t start_h, uint32_t count, uint16_t* buf) 152 | { 153 | // Clear pending interrupt bits 154 | port->is = (uint32_t) -1; 155 | // Spin lock timeout counter 156 | int spin = 0; 157 | int slot = findCMDSlot(port, 32); 158 | 159 | if (slot == -1) 160 | return false; 161 | 162 | HBA_CMD_HEADER* cmd_header = (HBA_CMD_HEADER*) (uint64_t) port->clb; 163 | cmd_header += slot; 164 | // Command FIS size 165 | cmd_header->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t); 166 | // Read or write from device 167 | cmd_header->w = write; 168 | // PRDT entries count 169 | cmd_header->prdtl = (uint16_t) ((count - 1) >> 4) + 1; 170 | 171 | HBA_CMD_TBL* cmd_tbl = (HBA_CMD_TBL*) (uint64_t) (cmd_header->ctba); 172 | memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + (cmd_header->prdtl - 1) * sizeof(HBA_PRDT_ENTRY)); 173 | 174 | // 8K bytes (16 sectors) per PRDT 175 | uint16_t i; 176 | for (i = 0; i < cmd_header->prdtl - 1; i++) 177 | { 178 | cmd_tbl->prdt_entry[i].dba = (uint32_t) buf; 179 | // 8K bytes (this value should always be set to 1 less than the actual value) 180 | cmd_tbl->prdt_entry[i].dbc = 8 * 1024 - 1; 181 | cmd_tbl->prdt_entry[i].i = 1; 182 | // 4K words 183 | buf += 4 * 1024; 184 | // 16 sectors 185 | count -= 16; 186 | } 187 | // Last entry 188 | cmd_tbl->prdt_entry[i].dba = (uint32_t) buf; 189 | 190 | // 512 bytes per sector 191 | cmd_tbl->prdt_entry[i].dbc = (count << 9) - 1; 192 | cmd_tbl->prdt_entry[i].i = 1; 193 | 194 | // Setup command 195 | FIS_REG_H2D* cmd_fis = (FIS_REG_H2D*) (&cmd_tbl->cfis); 196 | cmd_fis->fis_type = FIS_TYPE_REG_H2D; 197 | 198 | // Command 199 | cmd_fis->c = 1; 200 | cmd_fis->command = type; 201 | 202 | // LBA mode 203 | cmd_fis->lba0 = (uint8_t) start_l; 204 | cmd_fis->lba1 = (uint8_t) (start_l >> 8); 205 | cmd_fis->lba2 = (uint8_t) (start_l >> 16); 206 | cmd_fis->device = 1 << 6; 207 | 208 | cmd_fis->lba3 = (uint8_t) (start_l >> 24); 209 | cmd_fis->lba4 = (uint8_t) start_h; 210 | cmd_fis->lba5 = (uint8_t) (start_h >> 8); 211 | 212 | cmd_fis->countl = count & 0xFF; 213 | cmd_fis->counth = (count >> 8) & 0xFF; 214 | 215 | // The below loop waits until the port is no longer busy before issuing a new command 216 | while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000) 217 | { 218 | spin++; 219 | } 220 | 221 | if (spin == 1000000) 222 | { 223 | traceAHCI("Port is hung\n"); 224 | return false; 225 | } 226 | 227 | // Issue command 228 | port->ci = 1 << slot; 229 | 230 | // Wait for completion 231 | while (true) 232 | { 233 | // In some longer duration reads, it may be helpful to spin on the DPS bit 234 | // in the PxIS port field as well (1 << 5) 235 | if (!(port->ci & (1 << slot))) 236 | break; 237 | 238 | // Task file error 239 | if (port->is & HBA_PxIS_TFES) 240 | { 241 | traceAHCI("Read disk error\n"); 242 | return false; 243 | } 244 | } 245 | 246 | // Check again 247 | if (port->is & HBA_PxIS_TFES) 248 | { 249 | traceAHCI("Read disk error\n"); 250 | return false; 251 | } 252 | 253 | return true; 254 | } 255 | 256 | inline bool ahci_read(HBA_PORT* port, uint32_t start_l, uint32_t start_h, uint32_t count, uint16_t* buf) { 257 | return runCommand(ATA_CMD_READ_DMA_EX, 0, port, start_l, start_h, count, buf); 258 | } 259 | 260 | inline bool ahci_write(HBA_PORT* port, uint32_t start_l, uint32_t start_h, uint32_t count, uint16_t* buf) { 261 | return runCommand(ATA_CMD_WRITE_DMA_EX, 1, port, start_l, start_h, count, buf); 262 | } 263 | 264 | -------------------------------------------------------------------------------- /kernel/devices/ps2.c: -------------------------------------------------------------------------------- 1 | #include "pic.h" 2 | #include "ps2.h" 3 | #include "../kernel.h" 4 | #include "../lib/print.h" 5 | #include "../sys/io.h" 6 | #include "../interrupts/isr.h" 7 | 8 | 9 | /* 10 | Status Register 11 | 12 | Bit 0 Output buffer status (0 = empty, 1 = full) 13 | (must be set before attempting to read data from IO port 0x60) 14 | 15 | Bit 1 Input buffer status (0 = empty, 1 = full) 16 | (must be clear before attempting to write data to IO port 0x60 or IO port 0x64) 17 | 18 | Bit 2 System Flag 19 | Meant to be cleared on reset and set by firmware (via. PS/2 Controller Configuration Byte) if the system passes self tests (POST) 20 | 21 | Bit 3 Command/data (0 = data written to input buffer is data for PS/2 device, 1 = data written to input buffer is data for PS/2 controller command) 22 | Bit 4 Unknown (chipset specific) 23 | May be "keyboard lock" (more likely unused on modern systems) 24 | 25 | Bit 5 Unknown (chipset specific) 26 | May be "receive time-out" or "second PS/2 port output buffer full" 27 | 28 | Bit 6 Time-out error (0 = no error, 1 = time-out error) 29 | Bit 7 Parity error (0 = no error, 1 = parity error) 30 | */ 31 | 32 | uint8_t readStatusRegister() { 33 | return inb(PS2_STATUS_REG); 34 | } 35 | 36 | void pollOutputBuffer() { 37 | // TODO: replace with timer 38 | // while (~(readStatusRegister() & 1)) {} 39 | }; 40 | 41 | void pollInputBuffer() { 42 | //TOOD: handle with timer 43 | // while (readStatusRegister() & 2) {} 44 | }; 45 | 46 | uint8_t readDataPort() { 47 | pollOutputBuffer(); 48 | return inb(PS2_DATA_PORT); 49 | } 50 | 51 | void writeDataPort(uint8_t byte) { 52 | pollInputBuffer(); 53 | outb(PS2_DATA_PORT, byte); 54 | } 55 | 56 | void writeCommandRegister(uint8_t command_byte, uint8_t next_byte) { 57 | outb(PS2_COMMAND_REG, command_byte); 58 | if (next_byte) { 59 | pollInputBuffer(); 60 | readDataPort(); 61 | writeDataPort(next_byte); 62 | } 63 | } 64 | 65 | uint8_t testPS2Controller() { 66 | writeCommandRegister(0xAA, 0); 67 | return readDataPort(); 68 | } 69 | 70 | uint8_t testPS2Port1() { 71 | writeCommandRegister(0xAB, 0); 72 | return readDataPort(); 73 | } 74 | 75 | uint8_t testPS2Port2() { 76 | writeCommandRegister(0xA9, 0); 77 | return readDataPort(); 78 | } 79 | 80 | void enablePS2Port1() { 81 | writeCommandRegister(0xAE, 0); 82 | } 83 | 84 | void disablePS2Port1() { 85 | writeCommandRegister(0xAD, 0); 86 | } 87 | 88 | void enablePS2Port2() { 89 | writeCommandRegister(0xA8, 0); 90 | } 91 | 92 | void disablePS2Port2() { 93 | writeCommandRegister(0xA7, 0); 94 | } 95 | 96 | /* 97 | Byte 0: Controller Configuration Byte 98 | 99 | Bit 0 First PS/2 port interrupt (1 = enabled, 0 = disabled) 100 | Bit 1 Second PS/2 port interrupt (1 = enabled, 0 = disabled, only if 2 PS/2 ports supported) 101 | Bit 2 System Flag (1 = system passed POST, 0 = your OS shouldn't be running) 102 | Bit 3 Should be zero 103 | Bit 4 First PS/2 port clock (1 = disabled, 0 = enabled) 104 | Bit 5 Second PS/2 port clock (1 = disabled, 0 = enabled, only if 2 PS/2 ports supported) 105 | Bit 6 First PS/2 port translation (1 = enabled, 0 = disabled) 106 | Bit 7 Must be zero 107 | 108 | Byte range: 0-0x1F 109 | */ 110 | 111 | 112 | uint8_t readPS2RAM(uint8_t byte) { 113 | writeCommandRegister(0x20+byte, 0); 114 | return readDataPort(); 115 | } 116 | 117 | void writePS2RAM(uint8_t byte) { 118 | writeCommandRegister(0x60+byte, byte); 119 | } 120 | 121 | void writeConfigurationByte(uint8_t byte) { 122 | writeCommandRegister(0x60, byte); 123 | } 124 | 125 | // copies bits from the input port to the status register 126 | // high = 1 for bits 7..4, 0 for bits 3...0 127 | void copyToStatus(uint8_t high) { 128 | writeCommandRegister(0xC1 + high, 0); 129 | } 130 | 131 | /* 132 | PS2 Controller Output Port 133 | 134 | Bit 0 System reset (output) WARNING always set to '1'. You need to pulse the reset line (e.g. using command 0xFE), and setting this bit to '0' can lock the computer up ("reset forever"). 135 | 136 | Bit 1 A20 gate (output) 137 | Bit 2 Second PS/2 port clock (output, only if 2 PS/2 ports supported) 138 | Bit 3 Second PS/2 port data (output, only if 2 PS/2 ports supported) 139 | Bit 4 Output buffer full with byte from first PS/2 port (connected to IRQ1) 140 | Bit 5 Output buffer full with byte from second PS/2 port (connected to IRQ12, only if 2 PS/2 ports supported) 141 | Bit 6 First PS/2 port clock (output) 142 | Bit 7 First PS/2 port data (output) 143 | 144 | controller output port: 0 145 | first PS2 port: 1 146 | second PS2 port: 2 147 | second PS2 port (with sender): 3 148 | */ 149 | 150 | uint8_t readControllerOutputBuffer() { 151 | writeCommandRegister(0xD0, 0); 152 | return readDataPort(); 153 | } 154 | 155 | void writeOutputBuffer(uint8_t byte, uint8_t port) { 156 | // sleep until status register is clear 157 | pollInputBuffer(); 158 | writeCommandRegister(0xD1 + port, byte); 159 | } 160 | 161 | // Lines are mapped by bit number 162 | // 0xF0-0xFF 163 | void pulseOutputLines(uint8_t lines) { 164 | writeCommandRegister(0xF0 + lines, 0); 165 | } 166 | 167 | static void akeyboardHandler(InterruptFrame* frame) { 168 | term_write("Handling interrupt\n", 20); 169 | uint8_t scan_code = readDataPort(); 170 | printNumber(scan_code); 171 | if (frame->int_no == 12) { 172 | sendEOIPIC(12); 173 | } 174 | } 175 | 176 | void initKeyboard() { 177 | registerInterruptHandler(33, &akeyboardHandler); 178 | 179 | // TODO: Initalise USB Controllers 180 | 181 | // TODO: Check ACPI for PS/2 Controller 182 | 183 | // Disable devices 184 | disablePS2Port1(); 185 | disablePS2Port2(); 186 | 187 | // Flush the output buffer 188 | readControllerOutputBuffer(); 189 | 190 | // Set Controller Configuration Byte 191 | disableAllIRQs(); 192 | uint8_t configuration_byte = readPS2RAM(0); 193 | uint8_t two_channel = ~(configuration_byte & 0b00010000); 194 | if (two_channel) { 195 | disablePS2Port2(); 196 | } 197 | configuration_byte &= 0b11011100; 198 | writeConfigurationByte(configuration_byte); 199 | 200 | // Perform controller self-test 201 | uint8_t controller_status = testPS2Controller(); 202 | 203 | // Determine if there are two channels 204 | if (two_channel) 205 | { 206 | enablePS2Port2(); 207 | configuration_byte = readPS2RAM(0); 208 | term_write("is dual channel!\n", 17); 209 | if (configuration_byte & 0b00010000) 210 | { 211 | two_channel = 0; 212 | } 213 | else 214 | { 215 | disablePS2Port2(); 216 | } 217 | } 218 | 219 | // Perform Inteface tests and enable devices 220 | uint8_t port1_status = testPS2Port1(); 221 | term_write("Port 1 Status:\n", 16); 222 | printNumber(port1_status); 223 | switch (port1_status) 224 | { 225 | case 0x00: 226 | enablePS2Port1(); 227 | configuration_byte = readPS2RAM(0); 228 | writeConfigurationByte(configuration_byte | 1); 229 | break; 230 | case 0x01: 231 | // clock line stuck low 232 | break; 233 | case 0x02: 234 | // clock line stuck high 235 | break; 236 | case 0x03: 237 | // data line stuck low 238 | break; 239 | case 0x04: 240 | // data line stuck high 241 | break; 242 | } 243 | 244 | configuration_byte = readPS2RAM(0); 245 | term_write("Configuration byte from 1\n", 26); 246 | printNumber(configuration_byte); 247 | if (two_channel) { 248 | uint8_t port2_status = testPS2Port2(); 249 | term_write("Port 2 Status:\n", 15); 250 | printNumber(port2_status); 251 | switch (port2_status) { 252 | case 0x00: 253 | enablePS2Port2(); 254 | configuration_byte = readPS2RAM(0); 255 | writeConfigurationByte(configuration_byte | 2); 256 | 257 | configuration_byte = readPS2RAM(0); 258 | term_write("Configuration byte from 2\n", 26); 259 | printNumber(configuration_byte); 260 | break; 261 | case 0x01: 262 | // clock line stuck low 263 | break; 264 | case 0x02: 265 | // clock line stuck high 266 | break; 267 | case 0x03: 268 | // data line stuck low 269 | break; 270 | case 0x04: 271 | // data line stuck high 272 | break; 273 | } 274 | } 275 | // Reset Devices 276 | writeOutputBuffer(0xFF, 0); 277 | writeOutputBuffer(0xFF, 1); 278 | if (two_channel) { 279 | writeOutputBuffer(0xFF, 2); 280 | readDataPort(); 281 | } 282 | 283 | clearMaskIRQ(1); 284 | } 285 | -------------------------------------------------------------------------------- /kernel/devices/ahci.h: -------------------------------------------------------------------------------- 1 | #ifndef AHCI_H 2 | #define AHCI_H 3 | 4 | // Reference: https://wiki.osdev.org/AHCI 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define SATA_SIG_ATA 0x00000101 // SATA drive 11 | #define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive 12 | #define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge 13 | #define SATA_SIG_PM 0x96690101 // Port multiplier 14 | 15 | #define ATA_DEV_BUSY 0x80 16 | #define ATA_DEV_DRQ 0x08 17 | #define ATA_CMD_READ_DMA_EX 0x25 18 | #define ATA_CMD_WRITE_DMA_EX 0x35 19 | 20 | #define AHCI_BASE 0x400000 // 4M 21 | #define AHCI_DEV_NULL 0 22 | #define AHCI_DEV_SATA 1 23 | #define AHCI_DEV_SEMB 2 24 | #define AHCI_DEV_PM 3 25 | #define AHCI_DEV_SATAPI 4 26 | 27 | #define HBA_PORT_IPM_ACTIVE 1 28 | #define HBA_PORT_DET_PRESENT 3 29 | 30 | #define HBA_PxCMD_ST 0x0001 31 | #define HBA_PxCMD_FRE 0x0010 32 | #define HBA_PxCMD_FR 0x4000 33 | #define HBA_PxCMD_CR 0x8000 34 | #define HBA_PxIS_TFES (1 << 30) /* TFES - Task File Error Status */ 35 | 36 | typedef enum 37 | { 38 | FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device 39 | FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host 40 | FIS_TYPE_DMA_ACT = 0x39, // DMA activate FIS - device to host 41 | FIS_TYPE_DMA_SETUP = 0x41, // DMA setup FIS - bidirectional 42 | FIS_TYPE_DATA = 0x46, // Data FIS - bidirectional 43 | FIS_TYPE_BIST = 0x58, // BIST activate FIS - bidirectional 44 | FIS_TYPE_PIO_SETUP = 0x5F, // PIO setup FIS - device to host 45 | FIS_TYPE_DEV_BITS = 0xA1, // Set device bits FIS - device to host 46 | } FIS_TYPE; 47 | 48 | typedef struct FIS_REG_H2D 49 | { 50 | // DWORD 0 51 | uint8_t fis_type; // FIS_TYPE_REG_H2D 52 | 53 | uint8_t pmport:4; // Port multiplier 54 | uint8_t rsv0:3; // Reserved 55 | uint8_t c:1; // 1: Command, 0: Control 56 | 57 | uint8_t command; // Command register 58 | uint8_t featurel; // Feature register, 7:0 59 | 60 | // DWORD 1 61 | uint8_t lba0; // LBA low register, 7:0 62 | uint8_t lba1; // LBA mid register, 15:8 63 | uint8_t lba2; // LBA high register, 23:16 64 | uint8_t device; // Device register 65 | 66 | // DWORD 2 67 | uint8_t lba3; // LBA register, 31:24 68 | uint8_t lba4; // LBA register, 39:32 69 | uint8_t lba5; // LBA register, 47:40 70 | uint8_t featureh; // Feature register, 15:8 71 | 72 | // DWORD 3 73 | uint8_t countl; // Count register, 7:0 74 | uint8_t counth; // Count register, 15:8 75 | uint8_t icc; // Isochronous command completion 76 | uint8_t control; // Control register 77 | 78 | // DWORD 4 79 | uint8_t rsv1[4]; // Reserved 80 | } FIS_REG_H2D; 81 | 82 | typedef struct FIS_REG_D2H 83 | { 84 | // DWORD 0 85 | uint8_t fis_type; // FIS_TYPE_REG_D2H 86 | 87 | uint8_t pmport:4; // Port multiplier 88 | uint8_t rsv0:2; // Reserved 89 | uint8_t i:1; // Interrupt bit 90 | uint8_t rsv1:1; // Reserved 91 | 92 | uint8_t status; // Status register 93 | uint8_t error; // Error register 94 | 95 | // DWORD 1 96 | uint8_t lba0; // LBA low register, 7:0 97 | uint8_t lba1; // LBA mid register, 15:8 98 | uint8_t lba2; // LBA high register, 23:16 99 | uint8_t device; // Device register 100 | 101 | // DWORD 2 102 | uint8_t lba3; // LBA register, 31:24 103 | uint8_t lba4; // LBA register, 39:32 104 | uint8_t lba5; // LBA register, 47:40 105 | uint8_t rsv2; // Reserved 106 | 107 | // DWORD 3 108 | uint8_t countl; // Count register, 7:0 109 | uint8_t counth; // Count register, 15:8 110 | uint8_t rsv3[2]; // Reserved 111 | 112 | // DWORD 4 113 | uint8_t rsv4[4]; // Reserved 114 | } FIS_REG_D2H; 115 | 116 | typedef struct FIS_DMA_SETUP 117 | { 118 | // DWORD 0 119 | uint8_t fis_type; // FIS_TYPE_DMA_SETUP 120 | 121 | uint8_t pmport:4; // Port multiplier 122 | uint8_t rsv0:1; // Reserved 123 | uint8_t d:1; // Data transfer direction, 1 - device to host 124 | uint8_t i:1; // Interrupt bit 125 | uint8_t a:1; // Auto-activate. Specifies if DMA Activate FIS is needed 126 | 127 | uint8_t rsved[2]; // Reserved 128 | 129 | //DWORD 1&2 130 | uint64_t DMAbufferID; // DMA Buffer Identifier. Used to Identify DMA buffer in host memory. 131 | // SATA Spec says host specific and not in Spec. Trying AHCI spec might work. 132 | 133 | //DWORD 3 134 | uint32_t rsvd; //More reserved 135 | 136 | //DWORD 4 137 | uint32_t DMAbufOffset; //Byte offset into buffer. First 2 bits must be 0 138 | 139 | //DWORD 5 140 | uint32_t TransferCount; //Number of bytes to transfer. Bit 0 must be 0 141 | 142 | //DWORD 6 143 | uint32_t resvd; //Reserved 144 | 145 | } FIS_DMA_SETUP; 146 | 147 | typedef struct FIS_DATA 148 | { 149 | // DWORD 0 150 | uint8_t fis_type; // FIS_TYPE_DATA 151 | 152 | uint8_t pmport:4; // Port multiplier 153 | uint8_t rsv0:4; // Reserved 154 | 155 | uint8_t rsv1[2]; // Reserved 156 | 157 | // DWORD 1 ~ N 158 | uint32_t data[1]; // Payload 159 | } FIS_DATA; 160 | 161 | typedef struct FIS_PIO_SETUP 162 | { 163 | // DWORD 0 164 | uint8_t fis_type; // FIS_TYPE_PIO_SETUP 165 | 166 | uint8_t pmport:4; // Port multiplier 167 | uint8_t rsv0:1; // Reserved 168 | uint8_t d:1; // Data transfer direction, 1 - device to host 169 | uint8_t i:1; // Interrupt bit 170 | uint8_t rsv1:1; 171 | 172 | uint8_t status; // Status register 173 | uint8_t error; // Error register 174 | 175 | // DWORD 1 176 | uint8_t lba0; // LBA low register, 7:0 177 | uint8_t lba1; // LBA mid register, 15:8 178 | uint8_t lba2; // LBA high register, 23:16 179 | uint8_t device; // Device register 180 | 181 | // DWORD 2 182 | uint8_t lba3; // LBA register, 31:24 183 | uint8_t lba4; // LBA register, 39:32 184 | uint8_t lba5; // LBA register, 47:40 185 | uint8_t rsv2; // Reserved 186 | 187 | // DWORD 3 188 | uint8_t countl; // Count register, 7:0 189 | uint8_t counth; // Count register, 15:8 190 | uint8_t rsv3; // Reserved 191 | uint8_t e_status; // New value of status register 192 | 193 | // DWORD 4 194 | uint16_t tc; // Transfer count 195 | uint8_t rsv4[2]; // Reserved 196 | } FIS_PIO_SETUP; 197 | 198 | typedef volatile struct tagHBA_PORT 199 | { 200 | uint32_t clb; // 0x00, command list base address, 1K-byte aligned 201 | uint32_t clbu; // 0x04, command list base address upper 32 bits 202 | uint32_t fb; // 0x08, FIS base address, 256-byte aligned 203 | uint32_t fbu; // 0x0C, FIS base address upper 32 bits 204 | uint32_t is; // 0x10, interrupt status 205 | uint32_t ie; // 0x14, interrupt enable 206 | uint32_t cmd; // 0x18, command and status 207 | uint32_t rsv0; // 0x1C, Reserved 208 | uint32_t tfd; // 0x20, task file data 209 | uint32_t sig; // 0x24, signature 210 | uint32_t ssts; // 0x28, SATA status (SCR0:SStatus) 211 | uint32_t sctl; // 0x2C, SATA control (SCR2:SControl) 212 | uint32_t serr; // 0x30, SATA error (SCR1:SError) 213 | uint32_t sact; // 0x34, SATA active (SCR3:SActive) 214 | uint32_t ci; // 0x38, command issue 215 | uint32_t sntf; // 0x3C, SATA notification (SCR4:SNotification) 216 | uint32_t fbs; // 0x40, FIS-based switch control 217 | uint32_t rsv1[11]; // 0x44 ~ 0x6F, Reserved 218 | uint32_t vendor[4]; // 0x70 ~ 0x7F, vendor specific 219 | } HBA_PORT; 220 | 221 | typedef volatile struct tagHBA_MEM 222 | { 223 | // 0x00 - 0x2B, Generic Host Control 224 | uint32_t cap; // 0x00, Host capability 225 | uint32_t ghc; // 0x04, Global host control 226 | uint32_t is; // 0x08, Interrupt status 227 | uint32_t pi; // 0x0C, Port implemented 228 | uint32_t vs; // 0x10, Version 229 | uint32_t ccc_ctl; // 0x14, Command completion coalescing control 230 | uint32_t ccc_pts; // 0x18, Command completion coalescing ports 231 | uint32_t em_loc; // 0x1C, Enclosure management location 232 | uint32_t em_ctl; // 0x20, Enclosure management control 233 | uint32_t cap2; // 0x24, Host capabilities extended 234 | uint32_t bohc; // 0x28, BIOS/OS handoff control and status 235 | 236 | // 0x2C - 0x9F, Reserved 237 | uint8_t rsv[0xA0-0x2C]; 238 | 239 | // 0xA0 - 0xFF, Vendor specific registers 240 | uint8_t vendor[0x100-0xA0]; 241 | 242 | // 0x100 - 0x10FF, Port control registers 243 | HBA_PORT ports[32]; // 1 ~ 32 244 | } HBA_MEM; 245 | 246 | typedef struct HBA_CMD_HEADER 247 | { 248 | // DW0 249 | uint8_t cfl:5; // Command FIS length in DWORDS, 2 ~ 16 250 | uint8_t a:1; // ATAPI 251 | uint8_t w:1; // Write, 1: H2D, 0: D2H 252 | uint8_t p:1; // Prefetchable 253 | 254 | uint8_t r:1; // Reset 255 | uint8_t b:1; // BIST 256 | uint8_t c:1; // Clear busy upon R_OK 257 | uint8_t rsv0:1; // Reserved 258 | uint8_t pmp:4; // Port multiplier port 259 | 260 | uint16_t prdtl; // Physical region descriptor table length in entries 261 | 262 | // DW1 263 | volatile uint32_t prdbc; // Physical region descriptor byte count transferred 264 | 265 | // DW2, 3 266 | uint32_t ctba; // Command table descriptor base address 267 | uint32_t ctbau; // Command table descriptor base address upper 32 bits 268 | 269 | // DW4 - 7 270 | uint32_t rsv1[4]; // Reserved 271 | } HBA_CMD_HEADER; 272 | 273 | typedef struct HBA_PRDT_ENTRY 274 | { 275 | uint32_t dba; // Data base address 276 | uint32_t dbau; // Data base address upper 32 bits 277 | uint32_t rsv0; // Reserved 278 | 279 | // DW3 280 | uint32_t dbc:22; // Byte count, 4M max 281 | uint32_t rsv1:9; // Reserved 282 | uint32_t i:1; // Interrupt on completion 283 | } HBA_PRDT_ENTRY; 284 | 285 | typedef struct HBA_CMD_TBL 286 | { 287 | // 0x00 288 | uint8_t cfis[64]; // Command FIS 289 | 290 | // 0x40 291 | uint8_t acmd[16]; // ATAPI command, 12 or 16 bytes 292 | 293 | // 0x50 294 | uint8_t rsv[48]; // Reserved 295 | 296 | // 0x80 297 | HBA_PRDT_ENTRY prdt_entry[1]; // Physical region descriptor table entries, 0 ~ 65535 298 | } HBA_CMD_TBL; 299 | 300 | void probePort(HBA_MEM* abar); 301 | void startCMD(HBA_PORT* port); 302 | void stopCMD(HBA_PORT* port); 303 | void portRebase(HBA_PORT* port, int portno); 304 | int findCMDSlot(HBA_PORT* port, size_t cmd_slots); 305 | bool ahci_read(HBA_PORT* port, uint32_t start_l, uint32_t start_h, uint32_t count, uint16_t* buf); 306 | bool ahci_write(HBA_PORT* port, uint32_t start_l, uint32_t start_h, uint32_t count, uint16_t* buf); 307 | 308 | #endif 309 | -------------------------------------------------------------------------------- /kernel/limine.h: -------------------------------------------------------------------------------- 1 | /* BSD Zero Clause License */ 2 | 3 | /* Copyright (C) 2022-2023 mintsuki and contributors. 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef _LIMINE_H 18 | #define _LIMINE_H 1 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | 26 | /* Misc */ 27 | 28 | #ifdef LIMINE_NO_POINTERS 29 | # define LIMINE_PTR(TYPE) uint64_t 30 | #else 31 | # define LIMINE_PTR(TYPE) TYPE 32 | #endif 33 | 34 | #define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b 35 | 36 | struct limine_uuid { 37 | uint32_t a; 38 | uint16_t b; 39 | uint16_t c; 40 | uint8_t d[8]; 41 | }; 42 | 43 | #define LIMINE_MEDIA_TYPE_GENERIC 0 44 | #define LIMINE_MEDIA_TYPE_OPTICAL 1 45 | #define LIMINE_MEDIA_TYPE_TFTP 2 46 | 47 | struct limine_file { 48 | uint64_t revision; 49 | LIMINE_PTR(void *) address; 50 | uint64_t size; 51 | LIMINE_PTR(char *) path; 52 | LIMINE_PTR(char *) cmdline; 53 | uint32_t media_type; 54 | uint32_t unused; 55 | uint32_t tftp_ip; 56 | uint32_t tftp_port; 57 | uint32_t partition_index; 58 | uint32_t mbr_disk_id; 59 | struct limine_uuid gpt_disk_uuid; 60 | struct limine_uuid gpt_part_uuid; 61 | struct limine_uuid part_uuid; 62 | }; 63 | 64 | /* Boot info */ 65 | 66 | #define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } 67 | 68 | struct limine_bootloader_info_response { 69 | uint64_t revision; 70 | LIMINE_PTR(char *) name; 71 | LIMINE_PTR(char *) version; 72 | }; 73 | 74 | struct limine_bootloader_info_request { 75 | uint64_t id[4]; 76 | uint64_t revision; 77 | LIMINE_PTR(struct limine_bootloader_info_response *) response; 78 | }; 79 | 80 | /* Stack size */ 81 | 82 | #define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } 83 | 84 | struct limine_stack_size_response { 85 | uint64_t revision; 86 | }; 87 | 88 | struct limine_stack_size_request { 89 | uint64_t id[4]; 90 | uint64_t revision; 91 | LIMINE_PTR(struct limine_stack_size_response *) response; 92 | uint64_t stack_size; 93 | }; 94 | 95 | /* HHDM */ 96 | 97 | #define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } 98 | 99 | struct limine_hhdm_response { 100 | uint64_t revision; 101 | uint64_t offset; 102 | }; 103 | 104 | struct limine_hhdm_request { 105 | uint64_t id[4]; 106 | uint64_t revision; 107 | LIMINE_PTR(struct limine_hhdm_response *) response; 108 | }; 109 | 110 | /* Framebuffer */ 111 | 112 | #define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } 113 | 114 | #define LIMINE_FRAMEBUFFER_RGB 1 115 | 116 | struct limine_video_mode { 117 | uint64_t pitch; 118 | uint64_t width; 119 | uint64_t height; 120 | uint16_t bpp; 121 | uint8_t memory_model; 122 | uint8_t red_mask_size; 123 | uint8_t red_mask_shift; 124 | uint8_t green_mask_size; 125 | uint8_t green_mask_shift; 126 | uint8_t blue_mask_size; 127 | uint8_t blue_mask_shift; 128 | }; 129 | 130 | struct limine_framebuffer { 131 | LIMINE_PTR(void *) address; 132 | uint64_t width; 133 | uint64_t height; 134 | uint64_t pitch; 135 | uint16_t bpp; 136 | uint8_t memory_model; 137 | uint8_t red_mask_size; 138 | uint8_t red_mask_shift; 139 | uint8_t green_mask_size; 140 | uint8_t green_mask_shift; 141 | uint8_t blue_mask_size; 142 | uint8_t blue_mask_shift; 143 | uint8_t unused[7]; 144 | uint64_t edid_size; 145 | LIMINE_PTR(void *) edid; 146 | /* Response revision 1 */ 147 | uint64_t mode_count; 148 | LIMINE_PTR(struct limine_video_mode **) modes; 149 | }; 150 | 151 | struct limine_framebuffer_response { 152 | uint64_t revision; 153 | uint64_t framebuffer_count; 154 | LIMINE_PTR(struct limine_framebuffer **) framebuffers; 155 | }; 156 | 157 | struct limine_framebuffer_request { 158 | uint64_t id[4]; 159 | uint64_t revision; 160 | LIMINE_PTR(struct limine_framebuffer_response *) response; 161 | }; 162 | 163 | /* Terminal */ 164 | 165 | #define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 } 166 | 167 | #define LIMINE_TERMINAL_CB_DEC 10 168 | #define LIMINE_TERMINAL_CB_BELL 20 169 | #define LIMINE_TERMINAL_CB_PRIVATE_ID 30 170 | #define LIMINE_TERMINAL_CB_STATUS_REPORT 40 171 | #define LIMINE_TERMINAL_CB_POS_REPORT 50 172 | #define LIMINE_TERMINAL_CB_KBD_LEDS 60 173 | #define LIMINE_TERMINAL_CB_MODE 70 174 | #define LIMINE_TERMINAL_CB_LINUX 80 175 | 176 | #define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1)) 177 | #define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2)) 178 | #define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3)) 179 | #define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4)) 180 | 181 | /* Response revision 1 */ 182 | #define LIMINE_TERMINAL_OOB_OUTPUT_GET ((uint64_t)(-10)) 183 | #define LIMINE_TERMINAL_OOB_OUTPUT_SET ((uint64_t)(-11)) 184 | 185 | #define LIMINE_TERMINAL_OOB_OUTPUT_OCRNL (1 << 0) 186 | #define LIMINE_TERMINAL_OOB_OUTPUT_OFDEL (1 << 1) 187 | #define LIMINE_TERMINAL_OOB_OUTPUT_OFILL (1 << 2) 188 | #define LIMINE_TERMINAL_OOB_OUTPUT_OLCUC (1 << 3) 189 | #define LIMINE_TERMINAL_OOB_OUTPUT_ONLCR (1 << 4) 190 | #define LIMINE_TERMINAL_OOB_OUTPUT_ONLRET (1 << 5) 191 | #define LIMINE_TERMINAL_OOB_OUTPUT_ONOCR (1 << 6) 192 | #define LIMINE_TERMINAL_OOB_OUTPUT_OPOST (1 << 7) 193 | 194 | struct limine_terminal; 195 | 196 | typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t); 197 | typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t); 198 | 199 | struct limine_terminal { 200 | uint64_t columns; 201 | uint64_t rows; 202 | LIMINE_PTR(struct limine_framebuffer *) framebuffer; 203 | }; 204 | 205 | struct limine_terminal_response { 206 | uint64_t revision; 207 | uint64_t terminal_count; 208 | LIMINE_PTR(struct limine_terminal **) terminals; 209 | LIMINE_PTR(limine_terminal_write) write; 210 | }; 211 | 212 | struct limine_terminal_request { 213 | uint64_t id[4]; 214 | uint64_t revision; 215 | LIMINE_PTR(struct limine_terminal_response *) response; 216 | LIMINE_PTR(limine_terminal_callback) callback; 217 | }; 218 | 219 | /* 5-level paging */ 220 | 221 | #define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 } 222 | 223 | struct limine_5_level_paging_response { 224 | uint64_t revision; 225 | }; 226 | 227 | struct limine_5_level_paging_request { 228 | uint64_t id[4]; 229 | uint64_t revision; 230 | LIMINE_PTR(struct limine_5_level_paging_response *) response; 231 | }; 232 | 233 | /* SMP */ 234 | 235 | #define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } 236 | 237 | struct limine_smp_info; 238 | 239 | typedef void (*limine_goto_address)(struct limine_smp_info *); 240 | 241 | #if defined (__x86_64__) || defined (__i386__) 242 | 243 | #define LIMINE_SMP_X2APIC (1 << 0) 244 | 245 | struct limine_smp_info { 246 | uint32_t processor_id; 247 | uint32_t lapic_id; 248 | uint64_t reserved; 249 | LIMINE_PTR(limine_goto_address) goto_address; 250 | uint64_t extra_argument; 251 | }; 252 | 253 | struct limine_smp_response { 254 | uint64_t revision; 255 | uint32_t flags; 256 | uint32_t bsp_lapic_id; 257 | uint64_t cpu_count; 258 | LIMINE_PTR(struct limine_smp_info **) cpus; 259 | }; 260 | 261 | #elif defined (__aarch64__) 262 | 263 | struct limine_smp_info { 264 | uint32_t processor_id; 265 | uint32_t gic_iface_no; 266 | uint64_t mpidr; 267 | uint64_t reserved; 268 | LIMINE_PTR(limine_goto_address) goto_address; 269 | uint64_t extra_argument; 270 | }; 271 | 272 | struct limine_smp_response { 273 | uint64_t revision; 274 | uint32_t flags; 275 | uint64_t bsp_mpidr; 276 | uint64_t cpu_count; 277 | LIMINE_PTR(struct limine_smp_info **) cpus; 278 | }; 279 | 280 | #else 281 | #error Unknown architecture 282 | #endif 283 | 284 | struct limine_smp_request { 285 | uint64_t id[4]; 286 | uint64_t revision; 287 | LIMINE_PTR(struct limine_smp_response *) response; 288 | uint64_t flags; 289 | }; 290 | 291 | /* Memory map */ 292 | 293 | #define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } 294 | 295 | #define LIMINE_MEMMAP_USABLE 0 296 | #define LIMINE_MEMMAP_RESERVED 1 297 | #define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2 298 | #define LIMINE_MEMMAP_ACPI_NVS 3 299 | #define LIMINE_MEMMAP_BAD_MEMORY 4 300 | #define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 301 | #define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 302 | #define LIMINE_MEMMAP_FRAMEBUFFER 7 303 | 304 | struct limine_memmap_entry { 305 | uint64_t base; 306 | uint64_t length; 307 | uint64_t type; 308 | }; 309 | 310 | struct limine_memmap_response { 311 | uint64_t revision; 312 | uint64_t entry_count; 313 | LIMINE_PTR(struct limine_memmap_entry **) entries; 314 | }; 315 | 316 | struct limine_memmap_request { 317 | uint64_t id[4]; 318 | uint64_t revision; 319 | LIMINE_PTR(struct limine_memmap_response *) response; 320 | }; 321 | 322 | /* Entry point */ 323 | 324 | #define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } 325 | 326 | typedef void (*limine_entry_point)(void); 327 | 328 | struct limine_entry_point_response { 329 | uint64_t revision; 330 | }; 331 | 332 | struct limine_entry_point_request { 333 | uint64_t id[4]; 334 | uint64_t revision; 335 | LIMINE_PTR(struct limine_entry_point_response *) response; 336 | LIMINE_PTR(limine_entry_point) entry; 337 | }; 338 | 339 | /* Kernel File */ 340 | 341 | #define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } 342 | 343 | struct limine_kernel_file_response { 344 | uint64_t revision; 345 | LIMINE_PTR(struct limine_file *) kernel_file; 346 | }; 347 | 348 | struct limine_kernel_file_request { 349 | uint64_t id[4]; 350 | uint64_t revision; 351 | LIMINE_PTR(struct limine_kernel_file_response *) response; 352 | }; 353 | 354 | /* Module */ 355 | 356 | #define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } 357 | 358 | #define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0) 359 | 360 | struct limine_internal_module { 361 | LIMINE_PTR(const char *) path; 362 | LIMINE_PTR(const char *) cmdline; 363 | uint64_t flags; 364 | }; 365 | 366 | struct limine_module_response { 367 | uint64_t revision; 368 | uint64_t module_count; 369 | LIMINE_PTR(struct limine_file **) modules; 370 | }; 371 | 372 | struct limine_module_request { 373 | uint64_t id[4]; 374 | uint64_t revision; 375 | LIMINE_PTR(struct limine_module_response *) response; 376 | 377 | /* Revision 1 */ 378 | uint64_t internal_module_count; 379 | LIMINE_PTR(struct limine_internal_module **) internal_modules; 380 | }; 381 | 382 | /* RSDP */ 383 | 384 | #define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } 385 | 386 | struct limine_rsdp_response { 387 | uint64_t revision; 388 | LIMINE_PTR(void *) address; 389 | }; 390 | 391 | struct limine_rsdp_request { 392 | uint64_t id[4]; 393 | uint64_t revision; 394 | LIMINE_PTR(struct limine_rsdp_response *) response; 395 | }; 396 | 397 | /* SMBIOS */ 398 | 399 | #define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } 400 | 401 | struct limine_smbios_response { 402 | uint64_t revision; 403 | LIMINE_PTR(void *) entry_32; 404 | LIMINE_PTR(void *) entry_64; 405 | }; 406 | 407 | struct limine_smbios_request { 408 | uint64_t id[4]; 409 | uint64_t revision; 410 | LIMINE_PTR(struct limine_smbios_response *) response; 411 | }; 412 | 413 | /* EFI system table */ 414 | 415 | #define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } 416 | 417 | struct limine_efi_system_table_response { 418 | uint64_t revision; 419 | LIMINE_PTR(void *) address; 420 | }; 421 | 422 | struct limine_efi_system_table_request { 423 | uint64_t id[4]; 424 | uint64_t revision; 425 | LIMINE_PTR(struct limine_efi_system_table_response *) response; 426 | }; 427 | 428 | /* Boot time */ 429 | 430 | #define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } 431 | 432 | struct limine_boot_time_response { 433 | uint64_t revision; 434 | int64_t boot_time; 435 | }; 436 | 437 | struct limine_boot_time_request { 438 | uint64_t id[4]; 439 | uint64_t revision; 440 | LIMINE_PTR(struct limine_boot_time_response *) response; 441 | }; 442 | 443 | /* Kernel address */ 444 | 445 | #define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } 446 | 447 | struct limine_kernel_address_response { 448 | uint64_t revision; 449 | uint64_t physical_base; 450 | uint64_t virtual_base; 451 | }; 452 | 453 | struct limine_kernel_address_request { 454 | uint64_t id[4]; 455 | uint64_t revision; 456 | LIMINE_PTR(struct limine_kernel_address_response *) response; 457 | }; 458 | 459 | /* Device Tree Blob */ 460 | 461 | #define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } 462 | 463 | struct limine_dtb_response { 464 | uint64_t revision; 465 | LIMINE_PTR(void *) dtb_ptr; 466 | }; 467 | 468 | struct limine_dtb_request { 469 | uint64_t id[4]; 470 | uint64_t revision; 471 | LIMINE_PTR(struct limine_dtb_response *) response; 472 | }; 473 | 474 | #ifdef __cplusplus 475 | } 476 | #endif 477 | 478 | #endif 479 | --------------------------------------------------------------------------------