diff options
| author | cpplover0 <osdev555@yandex.com> | 2026-03-12 07:23:06 +0300 |
|---|---|---|
| committer | cpplover0 <osdev555@yandex.com> | 2026-03-12 07:23:06 +0300 |
| commit | e31de642e987664a9765fb76fd782b9d7f838bda (patch) | |
| tree | 1ea6f14d0abfdc2ed601507bb28464aa0e222cd5 | |
| parent | a79fa41862d2f81e2a8d505fa994c16234e7e41b (diff) | |
x2apic, poweroff, nvme driver, mp, powerbutton
50 files changed, 2058 insertions, 112 deletions
@@ -1,4 +1,5 @@ /limine /edk2-ovmf *.iso -*.hdd
\ No newline at end of file +*.hdd +disk.img
\ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index 97c4632..e777f1f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -5,7 +5,7 @@ ARCH := x86_64 # Default user QEMU flags. These are appended to the QEMU command calls. -QEMUFLAGS := -m 256M -d int -no-reboot +QEMUFLAGS := -m 256M -d int -no-reboot -serial stdio override IMAGE_NAME := orange-$(ARCH) @@ -30,9 +30,9 @@ run-hdd: run-hdd-$(ARCH) .PHONY: run-x86_64 run-x86_64: edk2-ovmf $(IMAGE_NAME).iso - qemu-system-$(ARCH) \ - -enable-kvm \ + qemu-system-$(ARCH) \ -M q35 \ + -enable-kvm -cpu host,+x2apic,+la57 \ -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-$(ARCH).fd,readonly=on \ -cdrom $(IMAGE_NAME).iso \ $(QEMUFLAGS) diff --git a/kernel/src/arch/aarch64/aarch64.cpp b/kernel/src/arch/aarch64/aarch64.cpp index 3992ad7..52003f8 100644 --- a/kernel/src/arch/aarch64/aarch64.cpp +++ b/kernel/src/arch/aarch64/aarch64.cpp @@ -52,6 +52,10 @@ namespace arch { return 4; } + [[gnu::weak]] void memory_barrier() { + asm volatile("" ::: "memory"); + } + [[gnu::weak]] void init(int stage) { switch(stage) { case ARCH_INIT_EARLY: diff --git a/kernel/src/arch/riscv64/riscv64.cpp b/kernel/src/arch/riscv64/riscv64.cpp index 2db83bf..784cc94 100644 --- a/kernel/src/arch/riscv64/riscv64.cpp +++ b/kernel/src/arch/riscv64/riscv64.cpp @@ -16,6 +16,10 @@ namespace arch { asm volatile("wfi"); } + [[gnu::weak]] void memory_barrier() { + asm volatile("" ::: "memory"); + } + [[gnu::weak]] void hcf() { disable_interrupts(); while(true) { diff --git a/kernel/src/arch/x86_64/cpu/gdt.cpp b/kernel/src/arch/x86_64/cpu/gdt.cpp index dd406f2..0498c21 100644 --- a/kernel/src/arch/x86_64/cpu/gdt.cpp +++ b/kernel/src/arch/x86_64/cpu/gdt.cpp @@ -46,6 +46,7 @@ void x86_64::gdt::init() { auto cpudata = x86_64::cpu_data(); cpudata->timer_ist_stack = (std::uint64_t)(pmm::buddy::alloc(KERNEL_STACK_SIZE).phys + etc::hhdm()); - klibc::printf("GDT: tss->rsp0 0x%p tss->ist0 0x%p\r\n",tss->rsp[0],tss->ist[0],tss->ist[1],tss->ist[2],tss->ist[3],cpudata->timer_ist_stack); + static bool is_print = 0; + if(!is_print) { klibc::printf("GDT: tss->rsp0 0x%p tss->ist0 0x%p\r\n",tss->rsp[0],tss->ist[0],tss->ist[1],tss->ist[2],tss->ist[3],cpudata->timer_ist_stack); is_print = 1; } }
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpu/idt.asm b/kernel/src/arch/x86_64/cpu/idt.asm index 78bed7e..deb0164 100644 --- a/kernel/src/arch/x86_64/cpu/idt.asm +++ b/kernel/src/arch/x86_64/cpu/idt.asm @@ -194,7 +194,7 @@ irqStub: push rax mov rdi,rsp xor rbp,rbp - nop + call irqHandler pop rax mov cr3,rax pop rax diff --git a/kernel/src/arch/x86_64/cpu/idt.cpp b/kernel/src/arch/x86_64/cpu/idt.cpp index 19cbbcf..c62b1e6 100644 --- a/kernel/src/arch/x86_64/cpu/idt.cpp +++ b/kernel/src/arch/x86_64/cpu/idt.cpp @@ -4,6 +4,7 @@ #include <utils/bitmap.hpp> #include <klibc/stdio.hpp> #include <arch/x86_64/cpu/lapic.hpp> +#include <utils/assert.hpp> x86_64::idt::idt_entry_t idt_entries[255]; utils::bitmap* idt_bitmap; @@ -12,29 +13,37 @@ x86_64::idt::idtr_t idtr; extern "C" void* isrTable[]; extern "C" void ignoreStubC() { + assert(0,"ignore stub"); x86_64::lapic::eoi(); + } void x86_64::idt::init() { - klibc::memset(idt_entries,0,sizeof(idt_entries)); - idt_bitmap = new utils::bitmap(255); - idtr.base = (std::uint64_t)idt_entries; - idtr.limit = (std::uint16_t)sizeof(idt_entry_t) * 256 - 1; + static bool is_print = 0; - for(uint8_t vec = 0;vec <32;vec++){ - set_entry((std::uint64_t)isrTable[vec],vec,0x8E,1); - } + if(!is_print) { + klibc::memset(idt_entries,0,sizeof(idt_entries)); + idt_bitmap = new utils::bitmap(255); + + idtr.base = (std::uint64_t)idt_entries; + idtr.limit = (std::uint16_t)sizeof(idt_entry_t) * 256 - 1; + + for(uint8_t vec = 0;vec <32;vec++){ + set_entry((std::uint64_t)isrTable[vec],vec,0x8E,1); + } + + for(uint8_t vec = 32; vec < 255; vec++) { + set_entry((std::uint64_t)ignoreStub,vec,0x8E,4); + idt_bitmap->clear(vec); + } - for(uint8_t vec = 32; vec < 255; vec++) { - set_entry((std::uint64_t)ignoreStub,vec,0x8E,4); - idt_bitmap->clear(vec); + idt_bitmap->set(32); } - idt_bitmap->set(32); load(); - klibc::printf("IDT: IDTR is 0x%p\r\n",&idtr); + if(!is_print) {klibc::printf("IDT: IDTR is 0x%p\r\n",&idtr); is_print = 1;} } void x86_64::idt::load() { diff --git a/kernel/src/arch/x86_64/cpu/lapic.cpp b/kernel/src/arch/x86_64/cpu/lapic.cpp new file mode 100644 index 0000000..3bea37c --- /dev/null +++ b/kernel/src/arch/x86_64/cpu/lapic.cpp @@ -0,0 +1,61 @@ +#include <cstdint> +#include <arch/x86_64/cpu/lapic.hpp> +#include <arch/x86_64/assembly.hpp> +#include <generic/time.hpp> + +std::uint32_t x86_64::lapic::id() { + if(is_x2apic) { + return read(0x20); + } else { + return read(0x20) >> 24; + } +} + +void x86_64::lapic::eoi() { + write(0xB0,0); +} + +void x86_64::lapic::tick(std::uint64_t tick) { + write(0x380,tick); +} + +void x86_64::lapic::off() { + uint32_t icr_low = (0x3 << 18) | (0x5 << 8) | (1 << 14); + write(0x300, icr_low); +} + +std::uint64_t x86_64::lapic::init(std::uint32_t us) { + std::uint32_t a,b,c,d; + assembly::cpuid(1,0,&a,&b,&c,&d); + + static bool is_print = 0; + + if(c & (1 << 21)) { + // x2apic present + if(!is_print) klibc::printf("X2APIC: Enabling x2apic (base is 0x%p)\r\n",0x800); + assembly::wrmsr(0x1B,assembly::rdmsr(0x1B) | (1 << 10) | (1 << 11)); + is_x2apic = 1; + } else { + assembly::wrmsr(0x1B,assembly::rdmsr(0x1B)); + } + + + paging::map_range(gobject::kernel_root,assembly::rdmsr(0x1B) & 0xFFFFF000,(assembly::rdmsr(0x1B) & 0xFFFFF000) + etc::hhdm(),PAGE_SIZE, PAGING_PRESENT | PAGING_RW); + write(0xf0,0xff | 0x100); + write(0x3e0,1); + write(0x320,32 | (1 << 16)); + write(0x380,0xFFFFFFFF); + time::timer->sleep(us); + std::uint64_t ticks = 0xFFFFFFFF - read(0x390); + write(0x320, 32 | (1 << 17)); + write(0x3e0,1); + write(0x380,ticks); + + if(!is_lapic_init) { + if(!is_print) klibc::printf("LAPIC: Calibration time is %lli, ticks %lli\r\n",us,ticks); + is_print = 1; + is_lapic_init = 1; + } + + return ticks; +}
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpu/lapic.hpp b/kernel/src/arch/x86_64/cpu/lapic.hpp index a800116..d2380af 100644 --- a/kernel/src/arch/x86_64/cpu/lapic.hpp +++ b/kernel/src/arch/x86_64/cpu/lapic.hpp @@ -9,10 +9,10 @@ #include <utils/gobject.hpp> #include <arch/x86_64/assembly.hpp> #include <klibc/stdio.hpp> - namespace x86_64 { inline int is_lapic_init = 0; + inline int is_x2apic = 0; class lapic { @@ -21,46 +21,27 @@ namespace x86_64 { } static inline std::uint32_t read(std::uint32_t reg) { - return *(volatile std::uint32_t*)(base() + reg); + if(is_x2apic) { + return assembly::rdmsr(0x800 + (reg >> 4)); + } else { + return *(volatile std::uint32_t*)(base() + reg); + } } static inline void write(std::uint32_t reg,std::uint32_t value) { - *(volatile std::uint32_t*)(base() + reg) = value; + if(is_x2apic) { + assembly::wrmsr(0x800 + (reg >> 4), value); + } else { + *(volatile std::uint32_t*)(base() + reg) = value; + } } public: - static inline std::uint32_t id() { - return read(0x20) >> 24; - } - - static inline void eoi() { - write(0xB0,0); - } - - static inline void tick(std::uint64_t tick) { - write(0x380,tick); - } - - static inline std::uint64_t init(std::uint32_t us) { - assembly::wrmsr(0x1B,assembly::rdmsr(0x1B)); - paging::map_range(gobject::kernel_root,assembly::rdmsr(0x1B) & 0xFFFFF000,(assembly::rdmsr(0x1B) & 0xFFFFF000) + etc::hhdm(),PAGE_SIZE, PAGING_PRESENT | PAGING_RW); - write(0xf0,0xff | 0x100); - write(0x3e0,1); - write(0x320,32 | (1 << 16)); - write(0x380,0xFFFFFFFF); - time::timer->sleep(us); - std::uint64_t ticks = 0xFFFFFFFF - read(0x390); - write(0x320, 32 | (1 << 17)); - write(0x3e0,1); - write(0x380,0); - - if(!is_lapic_init) { - klibc::printf("LAPIC: Calibration time is %lli, ticks %lli\r\n",us,ticks); - is_lapic_init = 1; - } - - return ticks; - } + static std::uint32_t id(); + static void eoi(); + static void off(); + static void tick(std::uint64_t tick); + static std::uint64_t init(std::uint32_t us); }; };
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpu/sse.cpp b/kernel/src/arch/x86_64/cpu/sse.cpp new file mode 100644 index 0000000..73d53ae --- /dev/null +++ b/kernel/src/arch/x86_64/cpu/sse.cpp @@ -0,0 +1,133 @@ +#include <cstdint> +#include <arch/x86_64/cpu/sse.hpp> +#include <arch/x86_64/assembly.hpp> +#include <klibc/stdio.hpp> +using namespace x86_64; + +std::uint64_t __sse_size = 0; +char __sse_is_initializied = 0; +char __sse_legacy_save = 0; + +std::uint64_t __sse_cr4_read() { + uint64_t val; + asm volatile("mov %%cr4, %0" : "=r"(val)); + return val; +} + +void __sse_cr4_write(std::uint64_t val) { + asm volatile("mov %0, %%cr4" : : "r"(val) : "memory"); +} + +std::uint64_t __sse_cr0_read() { + uint64_t val; + asm volatile("mov %%cr0, %0" : "=r"(val)); + return val; +} + +void __sse_cr0_write(std::uint64_t val) { + asm volatile("mov %0, %%cr0" : : "r"(val) : "memory"); +} + + +void __sse_xsetbv(std::uint64_t val) { + asm volatile("xsetbv" : : "a"(val), "d"(val >> 32),"c"(0) : "memory"); +} + +std::uint64_t __sse_xgetbv() { + uint32_t a,d; + asm volatile("xgetbv" : "=a"(a),"=d"(d) : "c"(0) : "memory"); + return ((std::uint64_t)d << 32) | a; +} + +void __sse_xsave(void* buf) { + std::uint64_t xcr0 = __sse_xgetbv(); + asm volatile("xsave (%0)" :: "r"(buf), "a"(xcr0 & 0xFFFFFFFF), "d"(xcr0 >> 32), "c"(0): "memory"); +} + +void __sse_xrstor(void* buf) { + std::uint64_t xcr0 = __sse_xgetbv(); + asm volatile("xrstor (%0)" :: "r"(buf), "a"(xcr0 & 0xFFFFFFFF), "d"(xcr0 >> 32), "c"(0): "memory"); +} + +void sse::init() { + uint32_t a,b,c,d; + assembly::cpuid(1,0,&a,&b,&c,&d); + if(!__sse_is_initializied) { + if(c & SSE_XSAVE_SUPPORT) { + assembly::cpuid(13,0,&a,&b,&c,&d); + __sse_legacy_save = 0; + __sse_size = c; + } else { + __sse_legacy_save = 1; + __sse_size = 512; + } + __sse_is_initializied = 1; + } + std::uint64_t cr4 = __sse_cr4_read(); + + cr4 |= DEFAULT_SSE_FLAGS; + + std::uint64_t cr0 = __sse_cr0_read(); + + cr0 &= ~(1 << 2); + cr0 |= (1 << 1); + + __sse_cr0_write(cr0); + + std::uint64_t sse_control = 0; + + __sse_cr4_write(cr4); + + assembly::cpuid(1,0,&a,&b,&c,&d); + if(c & SSE_XSAVE_SUPPORT) + cr4 |= SSE_XSAVE_CR4; + else + return; + + __sse_cr4_write(cr4); + + assembly::cpuid(13,0,&a,&b,&c,&d); + + sse_control |= SSE_CONTROL_DEFAULT; + SSE_CHECK_AND_SET((1 << 2)); + SSE_CHECK_AND_SET((1 << 9)); + SSE_CHECK_AND_SET((0b11 < 3)); + SSE_CHECK_AND_SET((0b11 < 17)) + SSE_CHECK_AND_SET((0b111 < 5)); + + __sse_xsetbv(sse_control); +} + +std::uint64_t sse::size() { + return __sse_size; +} + +void sse::save(std::uint8_t* buf) { + if(__sse_legacy_save) + asm volatile("fxsave (%0)" : : "r"(buf)); + else + __sse_xsave(buf); +} + +void sse::load(std::uint8_t* buf) { + if(__sse_legacy_save) + asm volatile("fxrstor (%0)" : : "r"(buf)); + else + __sse_xrstor(buf); +} + +void x86_64::sse::print_sse_features() { + std::uint32_t a,b,c,d; + assembly::cpuid(1,0,&a,&b,&c,&d); + + klibc::printf( + "SSE: Supported features:" "%s%s%s%s%s%s\n", + (d & (1 << 25)) ? " SSE" : "\0", + (d & (1 << 26)) ? " SSE2" : "\0", + (c & (1 << 0)) ? " SSE3\0" : "\0", + (c & (1 << 19)) ? " SSE4.1" : "", + (c & (1 << 20)) ? " SSE4.2" : "", + (c & (1 << 26)) ? " XSAVE" : "", + (c & (1 << 28)) ? " AVX" : "" + ); +} diff --git a/kernel/src/arch/x86_64/cpu/sse.hpp b/kernel/src/arch/x86_64/cpu/sse.hpp new file mode 100644 index 0000000..2109937 --- /dev/null +++ b/kernel/src/arch/x86_64/cpu/sse.hpp @@ -0,0 +1,33 @@ +#pragma once +#include <cstdint> + +namespace x86_64 { + namespace sse { + + typedef struct { + std::uint16_t dumb0; + std::uint32_t dumb1; + std::uint16_t dumb2; + std::uint64_t dumb3; + std::uint64_t dumb4; + std::uint32_t dumb5; + } __attribute__((packed)) fpu_head_t; + + #define DEFAULT_SSE_FLAGS ((1 << 9) | (1 << 10) | (1 << 1)) + #define SSE_XSAVE_SUPPORT (1 << 26) + #define SSE_XSAVE_CR4 (1 << 18) + + #define SSE_CONTROL_DEFAULT ((1 << 0) | (1 << 1)) + + #define SSE_CHECK_AND_SET(bit) \ + if(a & bit) \ + sse_control |= bit; + + void init(); + std::uint64_t size(); + void save(std::uint8_t* buf); + void load(std::uint8_t* buf); + void print_sse_features(); + inline static void setup_headers(fpu_head_t* head) { head->dumb5 = 0b0001111110000000; } + }; +}
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpu/xapic.hpp b/kernel/src/arch/x86_64/cpu/xapic.hpp new file mode 100644 index 0000000..aa7c0cf --- /dev/null +++ b/kernel/src/arch/x86_64/cpu/xapic.hpp @@ -0,0 +1,10 @@ +#pragma once +#include <arch/x86_64/cpu/lapic.hpp> + +namespace x86_64 { + namespace apic { + static inline void eoi() { + lapic::eoi(); + } + }; +};
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpu_local.hpp b/kernel/src/arch/x86_64/cpu_local.hpp index 13bca71..e839413 100644 --- a/kernel/src/arch/x86_64/cpu_local.hpp +++ b/kernel/src/arch/x86_64/cpu_local.hpp @@ -3,8 +3,8 @@ #pragma once -#include <arch/x86_64/cpu_local.hpp> #include <arch/x86_64/assembly.hpp> +#include <generic/scheduling.hpp> #include <klibc/string.hpp> #include <klibc/stdio.hpp> #include <generic/arch.hpp> @@ -13,7 +13,9 @@ typedef struct { std::uint64_t user_stack; std::uint64_t kernel_stack; std::uint64_t timer_ist_stack; + std::uint32_t cpu; std::uint64_t tsc_freq; + thread* current_thread; } cpudata_t; namespace x86_64 { diff --git a/kernel/src/arch/x86_64/drivers/pci.cpp b/kernel/src/arch/x86_64/drivers/pci.cpp new file mode 100644 index 0000000..88d5f36 --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/pci.cpp @@ -0,0 +1,54 @@ +#include <arch/x86_64/drivers/pci.hpp> +#include <cstdint> + +pci_driver_t pci_drivers[256]; + +pci_t __pci_load(std::uint8_t bus, std::uint8_t num, std::uint8_t function) { + pci_t pciData; + std::uint16_t *p = (std::uint16_t *)&pciData; + for (std::uint8_t i = 0; i < 32; i++) { + p[i] = x86_64::pci::pci_read_config16(bus, num, function, i * 2); + } + return pciData; +} + +void x86_64::pci::reg(void (*pcidrv)(pci_t, std::uint8_t, std::uint8_t, std::uint8_t), std::uint8_t _class, std::uint8_t subclass) { + for (std::uint16_t i = 0; i < 256; i++) { + if (!pci_drivers[i].used) { + pci_drivers[i].used = true; + pci_drivers[i]._class = _class; + pci_drivers[i].subclass = subclass; + pci_drivers[i].pcidrv = pcidrv; + } + } +} + +void __pci_launch(pci_t pci, std::uint8_t bus, std::uint8_t device, std::uint8_t function) { + for (std::uint16_t i = 0; i < 256; i++) { + if (pci_drivers[i].used && pci_drivers[i]._class == pci._class && pci_drivers[i].subclass == pci.subclass) { + pci_drivers[i].pcidrv(pci, bus, device, function); + return; + } + } +} + +void x86_64::pci::initworkspace() { + pci_t c_pci; + for (std::uint16_t bus = 0; bus < 256; bus++) { + c_pci = __pci_load(bus, 0, 0); + if (c_pci.vendorID != 0xFFFF) { + for (std::uint8_t device = 0; device < 32; device++) { + c_pci = __pci_load(bus, device, 0); + if (c_pci.vendorID != 0xFFFF) { + __pci_launch(c_pci, bus, device, 0); + for (std::uint8_t function = 1; function < 8; function++) { + pci_t pci = __pci_load(bus, device, function); + if (pci.vendorID != 0xFFFF) { + __pci_launch(pci, bus, device, function); + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/drivers/pci.hpp b/kernel/src/arch/x86_64/drivers/pci.hpp index 4474997..7a597ed 100644 --- a/kernel/src/arch/x86_64/drivers/pci.hpp +++ b/kernel/src/arch/x86_64/drivers/pci.hpp @@ -1,8 +1,65 @@ +#pragma once #include <arch/x86_64/drivers/io.hpp> #include <cstdint> +typedef struct { + std::uint16_t vendorID; + std::uint16_t deviceID; + std::uint16_t command; + std::uint16_t status; + std::uint8_t revisionID; + std::uint8_t progIF; + std::uint8_t subclass; + std::uint8_t _class; + std::uint8_t cacheLineSize; + std::uint8_t latencyTimer; + std::uint8_t headerType; + std::uint8_t bist; + std::uint32_t bar0; + std::uint32_t bar1; + std::uint32_t bar2; + std::uint32_t bar3; + std::uint32_t bar4; + std::uint32_t bar5; + std::uint32_t cardbusCISPointer; + std::uint16_t subsystemVendorID; + std::uint16_t subsystemID; + std::uint32_t expansionROMBaseAddress; + std::uint8_t capabilitiesPointer; + std::uint8_t reserved0; + std::uint16_t reserved1; + std::uint32_t reserved2; + std::uint8_t irq; + std::uint8_t interruptPIN; + std::uint8_t minGrant; + std::uint8_t maxLatency; +} __attribute__((packed)) pci_t; + +typedef struct pci_cap { + std::uint8_t id; + std::uint8_t off; + std::uint8_t bus; + std::uint8_t num; + std::uint8_t func; + std::uint16_t venID; + std::uint16_t devID; + std::uint8_t data[32]; + struct pci_cap* next; +} __attribute__((packed)) pci_cap_t; + +typedef struct { + int used; + std::uint8_t _class; + std::uint8_t subclass; + void (*pcidrv)(pci_t, std::uint8_t, std::uint8_t, std::uint8_t); +} __attribute__((packed)) pci_driver_t; + namespace x86_64 { namespace pci { + + void reg(void (*pcidrv)(pci_t, std::uint8_t, std::uint8_t, std::uint8_t), std::uint8_t _class, std::uint8_t subclass); + void initworkspace(); + inline std::uint32_t pci_read_config32(std::uint8_t bus, std::uint8_t num, std::uint8_t function, std::uint8_t offset) { std::uint32_t address = (1 << 31) | (bus << 16) | (num << 11) | (function << 8) | (offset); x86_64::io::outd(0xCF8, address); diff --git a/kernel/src/arch/x86_64/drivers/serial.cpp b/kernel/src/arch/x86_64/drivers/serial.cpp new file mode 100644 index 0000000..bf1a954 --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/serial.cpp @@ -0,0 +1,48 @@ +#include <arch/x86_64/drivers/io.hpp> +#include <arch/x86_64/drivers/serial.hpp> + +#define PORT 0x3f8 +int is_success_init_serial = 0; + +void x86_64::serial::init() { + x86_64::io::outb(PORT + 1, 0x00); + x86_64::io::outb(PORT + 3, 0x80); + x86_64::io::outb(PORT + 0, 0x03); + x86_64::io::outb(PORT + 1, 0x00); + x86_64::io::outb(PORT + 3, 0x03); + x86_64::io::outb(PORT + 2, 0xC7); + x86_64::io::outb(PORT + 4, 0x0B); + x86_64::io::outb(PORT + 4, 0x1E); + x86_64::io::outb(PORT + 0, 0xAE); + + if(x86_64::io::inb(PORT + 0) != 0xAE) { + is_success_init_serial = 0; + return; + } + + is_success_init_serial = 1; + x86_64::io::outb(PORT + 4, 0x0F); +} + +int is_transmit_empty() { + return x86_64::io::inb(PORT + 5) & 0x20; +} + +void x86_64::serial::write(char c) { + if(!is_success_init_serial) + return; + while (is_transmit_empty() == 0); + x86_64::io::outb(PORT,c); +} + +int serial_received() { + return x86_64::io::inb(PORT + 5) & 1; +} + +char x86_64::serial::read() { + if(!is_success_init_serial) + return 0; + while (serial_received() == 0); + + return x86_64::io::inb(PORT); +} diff --git a/kernel/src/arch/x86_64/drivers/serial.hpp b/kernel/src/arch/x86_64/drivers/serial.hpp new file mode 100644 index 0000000..453ed3a --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/serial.hpp @@ -0,0 +1,14 @@ + +namespace x86_64 { + namespace serial { + void init(); + void write(char c); + char read(); + + static inline void write_data(char* buffer, int size) { + for(int i = 0;i < size;i++) { + write(buffer[i]); + } + } + } +};
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/drivers/tsc.cpp b/kernel/src/arch/x86_64/drivers/tsc.cpp index fbb6fad..0abcf6e 100644 --- a/kernel/src/arch/x86_64/drivers/tsc.cpp +++ b/kernel/src/arch/x86_64/drivers/tsc.cpp @@ -54,7 +54,8 @@ void drivers::tsc::init() { x86_64::cpu_data()->tsc_freq = tsc_freq; - klibc::printf("TSC: TSC Frequency is %llu\r\n", tsc_freq); + static bool is_print = 0; + if(!is_print) {klibc::printf("TSC: TSC Frequency is %llu\r\n", tsc_freq); is_print = 1;} drivers::tsc_timer* tsc_timer = new drivers::tsc_timer; time::setup_timer(tsc_timer); } diff --git a/kernel/src/arch/x86_64/irq.cpp b/kernel/src/arch/x86_64/irq.cpp index da709eb..eb576a8 100644 --- a/kernel/src/arch/x86_64/irq.cpp +++ b/kernel/src/arch/x86_64/irq.cpp @@ -2,6 +2,7 @@ #include <arch/x86_64/irq.hpp> #include <arch/x86_64/cpu/lapic.hpp> #include <arch/x86_64/drivers/ioapic.hpp> +#include <arch/x86_64/cpu/xapic.hpp> #include <cstdint> irq_t irq_table[255]; @@ -16,7 +17,7 @@ extern "C" void irqHandler(x86_64::idt::int_frame_t* ctx) { irq_table[ctx->vec - 1].func(irq_table[ctx->vec - 1].arg); - x86_64::lapic::eoi(); + x86_64::apic::eoi(); if(ctx->cs & 3) ctx->ss |= 3; diff --git a/kernel/src/arch/x86_64/panic.cpp b/kernel/src/arch/x86_64/panic.cpp index 0ae6697..df923df 100644 --- a/kernel/src/arch/x86_64/panic.cpp +++ b/kernel/src/arch/x86_64/panic.cpp @@ -33,12 +33,12 @@ std::uint8_t gaster[] = { #embed "src/gaster.txt" }; -void print_ascii_art() { +void x86_64::panic::print_ascii_art() { klibc::printf("%s\n",gaster); } extern "C" void CPUKernelPanic(x86_64::idt::int_frame_t* frame) { - print_ascii_art(); + x86_64::panic::print_ascii_art(); print_regs(frame); arch::hcf(); }
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/schedule_timer.asm b/kernel/src/arch/x86_64/schedule_timer.asm new file mode 100644 index 0000000..54c7131 --- /dev/null +++ b/kernel/src/arch/x86_64/schedule_timer.asm @@ -0,0 +1,46 @@ + +extern timer_tick +global scheduler_timer_asm +scheduler_timer_asm: + cli + push qword 0 + push qword 32 + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rbp + push rdi + push rsi + push rdx + push rcx + push rbx + push rax + mov rax,cr3 + push rax + mov rdi,rsp + xor rbp,rbp + call timer_tick + pop rax + mov cr3,rax + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop rbp + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + add rsp,16 + iretq
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/schedule_timer.cpp b/kernel/src/arch/x86_64/schedule_timer.cpp new file mode 100644 index 0000000..97882c7 --- /dev/null +++ b/kernel/src/arch/x86_64/schedule_timer.cpp @@ -0,0 +1,58 @@ +#include <arch/x86_64/schedule_timer.hpp> +#include <arch/x86_64/cpu/idt.hpp> +#include <arch/x86_64/cpu/xapic.hpp> +#include <arch/x86_64/cpu_local.hpp> +#include <generic/scheduling.hpp> +#include <klibc/stdio.hpp> +#include <generic/mp.hpp> +#include <generic/time.hpp> +#include <generic/lock/spinlock.hpp> +#include <atomic> + +extern "C" void scheduler_timer_asm(); + +int is_timer_off = 0; +std::atomic<int> stfu_cpus = 0; + +extern "C" void timer_tick(x86_64::idt::int_frame_t* ctx) { + + if(is_timer_off) { + stfu_cpus++; + arch::hcf(); + } + + process::schedule((void*)ctx); + static std::atomic<int> i = 0; + klibc::printf("timer tick %lli %d\r", i++,x86_64::cpu_data()->cpu); + x86_64::apic::eoi(); +} + +void x86_64::schedule_timer::off() { + is_timer_off = 1; + + if(mp::cpu_count() <= 1) + return; + + std::uint32_t timeout = 10; + + klibc::printf("Poweroff: Waiting for all cpus to done work\r\n"); // used from poweroff so + while(stfu_cpus != (std::int32_t)(mp::cpu_count() - 1)) { + arch::memory_barrier(); + + if(--timeout == 0) { + klibc::printf("Poweroff: Can't wait longer, forching them to be disabled\r\n"); + x86_64::lapic::off(); + return; + } + + if(time::timer) { + time::timer->sleep(50000); + } else { + arch::pause(); + } + } +} + +void x86_64::schedule_timer::init() { + x86_64::idt::set_entry((std::uint64_t)scheduler_timer_asm,32, 0x8E, 2); +}
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/schedule_timer.hpp b/kernel/src/arch/x86_64/schedule_timer.hpp new file mode 100644 index 0000000..3a9b3b6 --- /dev/null +++ b/kernel/src/arch/x86_64/schedule_timer.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace x86_64 { + namespace schedule_timer { + void init(); + void off(); + }; +};
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/scheduling.asm b/kernel/src/arch/x86_64/scheduling.asm new file mode 100644 index 0000000..859f1e9 --- /dev/null +++ b/kernel/src/arch/x86_64/scheduling.asm @@ -0,0 +1,36 @@ + +global context_switch +context_switch: + mov rsp, rdi + pop rax + mov cr3,rax + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop rbp + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + add rsp,16 + iretq + +extern scheduler_timer_asm +global yield +yield: + pop rax + mov rdx, rsp + mov rsp,[gs:16] + push qword 0 + push rdx + pushfq + push qword 0x08 + push qword rax + jmp scheduler_timer_asm
\ No newline at end of file diff --git a/kernel/src/arch/x86_64/x86_64.cpp b/kernel/src/arch/x86_64/x86_64.cpp index 804c54f..4d272da 100644 --- a/kernel/src/arch/x86_64/x86_64.cpp +++ b/kernel/src/arch/x86_64/x86_64.cpp @@ -12,6 +12,9 @@ #include <arch/x86_64/irq.hpp> #include <klibc/stdio.hpp> #include <arch/x86_64/drivers/ioapic.hpp> +#include <arch/x86_64/schedule_timer.hpp> +#include <utils/gobject.hpp> +#include <arch/x86_64/cpu/sse.hpp> namespace arch { [[gnu::weak]] void disable_interrupts() { @@ -37,6 +40,12 @@ namespace arch { asm volatile("pause"); } + [[gnu::weak]] std::uint64_t current_root() { + std::uint64_t cr3; + asm volatile("mov %%cr3, %0" : "=r"(cr3) : : "memory"); + return cr3; + } + [[gnu::weak]] void tlb_flush(std::uintptr_t hint, std::uintptr_t len) { if (len / PAGE_SIZE > 256 || len == 0) { std::uint64_t cr3 = 0; @@ -67,6 +76,19 @@ namespace arch { x86_64::idt::init(); x86_64::lapic::init(1500); drivers::ioapic::init(); + x86_64::schedule_timer::init(); + x86_64::sse::init(); + x86_64::sse::print_sse_features(); + return; + case ARCH_INIT_MP: + enable_paging(gobject::kernel_root); + x86_64::init_cpu_data(); + drivers::tsc::init(); + x86_64::gdt::init(); + x86_64::idt::init(); + x86_64::lapic::init(1500); + x86_64::schedule_timer::init(); + x86_64::sse::init(); return; case ARCH_INIT_COMMON: return; @@ -74,8 +96,12 @@ namespace arch { } [[gnu::weak]] void panic(char* msg) { - x86_64::panic::print_ascii_art(); klibc::printf("Panic with message \"%s\"\r\n",msg); + arch::hcf(); + } + + [[gnu::weak]] void memory_barrier() { + asm volatile("lfence" ::: "memory"); } [[gnu::weak]] int register_handler(int irq, int type, std::uint64_t flags, void (*func)(void* arg), void* arg) { diff --git a/kernel/src/drivers/disk.hpp b/kernel/src/drivers/disk.hpp new file mode 100644 index 0000000..584d94e --- /dev/null +++ b/kernel/src/drivers/disk.hpp @@ -0,0 +1,10 @@ +#pragma once +#include <cstdint> + +struct disk { + void* arg; + std::size_t lba_size; + std::size_t disk_size; + bool (*read)(void* arg, char* buffer, std::uint64_t lba, std::size_t len_in_blocks); + bool (*write)(void* arg, char* buffer, std::uint64_t lba, std::size_t len_in_blocks); +};
\ No newline at end of file diff --git a/kernel/src/drivers/nvme.cpp b/kernel/src/drivers/nvme.cpp new file mode 100644 index 0000000..4be38c6 --- /dev/null +++ b/kernel/src/drivers/nvme.cpp @@ -0,0 +1,528 @@ +#include <cstdint> +#include <drivers/disk.hpp> +#include <drivers/nvme.hpp> +#if defined(__x86_64__) +#include <arch/x86_64/drivers/pci.hpp> +#endif +#include <utils/assert.hpp> +#include <generic/paging.hpp> +#include <utils/gobject.hpp> +#include <generic/hhdm.hpp> +#include <generic/pmm.hpp> +#include <generic/arch.hpp> +#include <klibc/stdio.hpp> +#include <utils/align.hpp> +#include <generic/time.hpp> + +using namespace drivers; + +nvme_controller* nvme_controllers[256]; +int nvme_controller_ptr = 0; + +void nvme::disable() { + for(std::int32_t i = 0;i < nvme_controller_ptr; i++) { + nvme_controller* ctrl = nvme_controllers[i]; + std::uint32_t cc = nvme::read32(ctrl, NVME_REG_CC); + cc &= ~NVME_CC_EN; + cc |= NVME_CC_SHN_NORMAL; + nvme::write32(ctrl, NVME_REG_CC, cc); + while(read32(ctrl, NVME_REG_CSTS) & 1) arch::pause(); + } +} + +void nvme_submit_admin(nvme_controller* ctrl, nvme_command& cmd) { + nvme_command* sq = (nvme_command*)ctrl->admin_sq.address; + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Submitting admin command to controller 0x%p with opcode %d\r\n",ctrl, cmd.cdw0); +#endif + + klibc::memcpy((void*)(((std::uint64_t)&sq[ctrl->admin_sq_tail]) + etc::hhdm()), &cmd, sizeof(nvme_command)); + + ctrl->admin_sq_tail++; + if (ctrl->admin_sq_tail > 63) ctrl->admin_sq_tail = 0; + + arch::memory_barrier(); + + uint32_t* sq_db = (uint32_t*)((uint8_t*)ctrl->bar0 + 0x1000); + *sq_db = ctrl->admin_sq_tail; + arch::memory_barrier(); +} + +bool nvme_wait_admin(nvme_controller* ctrl, uint64_t timeout_ms) { + volatile nvme_completion* cq = (volatile nvme_completion*)(ctrl->admin_cq.address + etc::hhdm()); + uint64_t elapsed = 0; + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Waiting admin command on controller 0x%p with timeout %lli\r\n",ctrl, timeout_ms); +#endif + + while (elapsed < timeout_ms * 1000) { + arch::memory_barrier(); + + if ((cq[ctrl->admin_cq_head].status & 0x1) == ctrl->admin_phase) { + + uint16_t status_code = (cq[ctrl->admin_cq_head].status >> 1) & 0xFF; + + ctrl->admin_cq_head++; + if (ctrl->admin_cq_head > 63) { + ctrl->admin_cq_head = 0; + ctrl->admin_phase ^= 1; + } + + uint32_t* cq_db = (uint32_t*)((uint8_t*)ctrl->bar0 + 0x1000 + (1 * ctrl->stride)); + *cq_db = ctrl->admin_cq_head; + arch::memory_barrier(); + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Got status %d\r\n",status_code); +#endif + + if(status_code != 0) + return false; + + return true; + } + + time::timer->sleep(10); + elapsed += 10; + } + + klibc::printf("NVME: Timeout !\r\n"); + return false; +} + +bool nvme_send_io(nvme_pair_queue* queue, nvme_command* cmd) { + nvme_command* sq = (nvme_command*)queue->sq.address; + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Submitting io command to queue 0x%p with opcode %d\r\n",queue, cmd->cdw0); +#endif + + klibc::memcpy((void*)(((std::uint64_t)&sq[queue->sq_tail]) + etc::hhdm()), cmd, sizeof(nvme_command)); + + queue->sq_tail++; + if (queue->sq_tail > 1) queue->sq_tail = 0; + + arch::memory_barrier(); + + *queue->sq_doorbell = queue->sq_tail; + arch::memory_barrier(); + + return true; +} + +bool nvme_wait_io(nvme_pair_queue* queue, uint64_t timeout_ms) { + volatile nvme_completion* cq = (volatile nvme_completion*)(queue->cq.address + etc::hhdm()); + uint64_t elapsed = 0; + + while (elapsed < timeout_ms * 1000) { + arch::memory_barrier(); + + if ((cq[queue->cq_head].status & 0x1) == queue->phase) { + + uint16_t status_code = (cq[queue->cq_head].status >> 1) & 0xFF; + + queue->cq_head++; + if (queue->cq_head > 63) { + queue->cq_head = 0; + queue->phase ^= 1; + } + + *queue->cq_doorbell = queue->cq_head; + arch::memory_barrier(); + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Got status %d\r\n",status_code); +#endif + + if(status_code != 0) + return false; + + return true; + } + + time::timer->sleep(10); + elapsed += 10; + } + + klibc::printf("NVME: Timeout !\r\n"); + return false; +} + +bool nvme_send_io_cmd(nvme_pair_queue* queue, nvme_command* cmd) { + queue->lock.lock(); + nvme_send_io(queue,cmd); + bool status = nvme_wait_io(queue, 1000); + queue->lock.unlock(); + return status; +} + +bool nvme_identify_namespace(struct nvme_controller* ctrl, uint32_t nsid) { + struct nvme_namespace* ns = &ctrl->namespaces[nsid - 1]; + ns->ns_data = (nvme_id_ns*)(pmm::freelist::alloc_4k() + etc::hhdm()); + + nvme_command cmd; + klibc::memset(&cmd,0,sizeof(nvme_command)); + cmd.cdw0 = 0x06; + cmd.nsid = nsid; + cmd.cdw10 = 0x0; + cmd.prp1 = (std::uint64_t)ns->ns_data - etc::hhdm(); + + nvme_submit_admin(ctrl, cmd); + bool result = nvme_wait_admin(ctrl, 1000); + + if(result == false) + return false; + + ns->nsid = nsid; + ns->size = ns->ns_data->nsze; + + uint8_t lba_format = ns->ns_data->flbas & 0xF; + if (lba_format < ns->ns_data->nlbaf) { + ns->lba_size = 1 << ns->ns_data->lbaf[lba_format].ds; + } else { + ns->lba_size = 512; + } + ns->valid = true; + + klibc::printf("NVME: Detected namespace %d with lba_size %lli bytes and total size %lli bytes\r\n", nsid, ns->lba_size, ns->size * ns->lba_size); + return true; +} + +bool allocate_prp2_list(std::size_t num_pages, void** out) { + if(num_pages > 1) { + *out = (void*)(pmm::buddy::alloc(num_pages * PAGE_SIZE).phys + etc::hhdm()); + return true; + } else { + *out = (void*)(pmm::freelist::alloc_4k() + etc::hhdm()); + return false; + } + assert(0,"wtf"); + return false; +} + +bool nvme_write(void* arg, char* buffer, std::uint64_t lba, std::size_t len_in_blocks) { + nvme_arg_disk* disk = (nvme_arg_disk*)arg; + nvme_command cmd; + klibc::memset(&cmd, 0, sizeof(nvme_command)); + + cmd.cdw0 = 1; + cmd.nsid = disk->nsid; + cmd.cdw10 = (uint32_t)(lba & 0xFFFFFFFF); + cmd.cdw11_15[0] = (uint32_t)(lba >> 32); + cmd.cdw11_15[1] = (len_in_blocks - 1) & 0xFFFF; + + std::size_t lba_size = disk->ctrl->namespaces[disk->nsid - 1].lba_size; + + assert(((std::uint64_t)buffer & PAGE_SIZE) == 0, "Unaligned buffer\r\n"); + + std::size_t blocks = len_in_blocks; + std::size_t num_pages = ((blocks * lba_size) + PAGE_SIZE - 1) / PAGE_SIZE; + std::uint64_t current_root = arch::current_root(); + + cmd.prp1 = arch::get_phys_from_page(current_root,(std::uintptr_t)buffer); + + std::uint64_t* list = nullptr; + bool is_buddy = false; + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Trying to write lba %lli seek %lli num_pages %lli len %lli buffer 0x%p arg 0x%p controller 0x%p nsid %d\r\n", lba, lba, num_pages, len_in_blocks * lba_size, buffer, arg, disk->ctrl, disk->nsid); +#endif + + if(num_pages == 1) { + cmd.prp2 = 0; + } else if(num_pages == 2) { + cmd.prp2 = arch::get_phys_from_page(current_root,(std::uintptr_t)buffer); + } else { + is_buddy = allocate_prp2_list(num_pages, (void**)&list); + for (std::size_t i = 1; i < num_pages; i++) { + list[i - 1] = arch::get_phys_from_page(current_root,(std::uint64_t)buffer + (i * PAGE_SIZE)); + } + cmd.prp2 = (std::uint64_t)list - etc::hhdm(); + } + + bool status = nvme_send_io_cmd(disk->ctrl->main_io_queue, &cmd); + + if(list) { + if(is_buddy) pmm::buddy::free((std::uint64_t)list - etc::hhdm()); + else pmm::freelist::free((std::uint64_t)list - etc::hhdm()); + } + + return status; +} + +bool nvme_read(void* arg, char* buffer, std::uint64_t lba, std::size_t len_in_blocks) { + nvme_arg_disk* disk = (nvme_arg_disk*)arg; + nvme_command cmd; + klibc::memset(&cmd, 0, sizeof(nvme_command)); + + cmd.cdw0 = 2; + cmd.nsid = disk->nsid; + cmd.cdw10 = (uint32_t)(lba & 0xFFFFFFFF); + cmd.cdw11_15[0] = (uint32_t)(lba >> 32); + cmd.cdw11_15[1] = (len_in_blocks - 1); + + std::size_t lba_size = disk->ctrl->namespaces[disk->nsid - 1].lba_size; + + assert(((std::uint64_t)buffer & PAGE_SIZE) == 0, "Unaligned buffer\r\n"); + + std::size_t blocks = len_in_blocks; + std::size_t num_pages = ((blocks * lba_size) + PAGE_SIZE - 1) / PAGE_SIZE; + std::uint64_t current_root = arch::current_root(); + + cmd.prp1 = arch::get_phys_from_page(current_root,(std::uintptr_t)buffer); + + std::uint64_t* list = nullptr; + bool is_buddy = false; + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Trying to read lba %lli seek %lli num_pages %lli len %lli buffer 0x%p arg 0x%p controller 0x%p nsid %d\r\n", lba, lba, num_pages, len_in_blocks * lba_size, buffer, arg, disk->ctrl, disk->nsid); +#endif + + if(num_pages == 1) { + cmd.prp2 = 0; + } else if(num_pages == 2) { + cmd.prp2 = arch::get_phys_from_page(current_root,(std::uintptr_t)buffer); + } else { + is_buddy = allocate_prp2_list(num_pages, (void**)&list); + for (std::size_t i = 1; i < num_pages; i++) { + list[i - 1] = arch::get_phys_from_page(current_root,(std::uint64_t)buffer + (i * PAGE_SIZE)); + } + cmd.prp2 = (std::uint64_t)list - etc::hhdm(); + } + + bool status = nvme_send_io_cmd(disk->ctrl->main_io_queue, &cmd); + + if(list) { + if(is_buddy) pmm::buddy::free((std::uint64_t)list - etc::hhdm()); + else pmm::freelist::free((std::uint64_t)list - etc::hhdm()); + } + + return status; +} + +static inline int isprint(int c) { + return (c >= 0x20 && c <= 0x7E); +} + + +static inline void print_buffer(const unsigned char *buffer, std::size_t size) { + for (std::size_t i = 0; i < size; i++) { + if (isprint(buffer[i])) { + klibc::printf("%c ", buffer[i]); + } else { + klibc::printf("0x%02X ", buffer[i]); + } + } + klibc::printf("\r\n"); +} + +void nvme_init_namespace(nvme_controller* ctrl, std::uint32_t nsid) { + void* buffer_test = (void*)(pmm::freelist::alloc_4k() + etc::hhdm()); + nvme_arg_disk* new_disk = new nvme_arg_disk; + new_disk->ctrl = ctrl; + new_disk->nsid = nsid; + + klibc::memset(buffer_test,'a',4096); + nvme_write(new_disk, (char*)buffer_test, 0, 2); + nvme_read(new_disk, (char*)buffer_test, 0, 2); + + ((char*)buffer_test)[4095] = '\0'; + klibc::printf("test nvme dumping first 1024 byte \r\n", buffer_test, klibc::strlen((const char*)buffer_test)); + print_buffer((const unsigned char*)buffer_test, 1024); + +} + +bool nvme_init_namespaces(nvme_controller* ctrl) { + void* ns_list = (void*)(pmm::freelist::alloc_4k() + etc::hhdm()); + + nvme_command cmd; + klibc::memset(&cmd,0,sizeof(nvme_command)); + cmd.cdw0 = 0x06; + cmd.cdw10 = 0x2; + cmd.prp1 = (std::uint64_t)ns_list - etc::hhdm(); + + nvme_submit_admin(ctrl, cmd); + bool result = nvme_wait_admin(ctrl, 1000); + + if(result == false) + return false; + + uint32_t* ns_ids = (uint32_t*)ns_list; + for (int i = 0; i < 1024; i++) { + uint32_t nsid = ns_ids[i]; + if (nsid == 0) break; // End of list + + result = nvme_identify_namespace(ctrl, nsid); + if(result == true) { + nvme_init_namespace(ctrl, nsid); + ctrl->num_namespaces++; + } + } + + return true; +} + +void nvme_alloc_queue(nvme_queue* queue) { + queue->address = pmm::freelist::alloc_4k(); + queue->size = 2; +} + +std::uint8_t nvme_qid_ptr = 1; + +nvme_pair_queue* nvme_create_io_queue(nvme_controller* ctrl) { + nvme_pair_queue* queue = new nvme_pair_queue; + nvme_alloc_queue(&queue->cq); + nvme_alloc_queue(&queue->sq); + queue->phase = 1; + queue->qid = nvme_qid_ptr; + queue->lock.unlock(); + + nvme_command cmd; + klibc::memset(&cmd, 0, sizeof(nvme_command)); + + cmd.cdw0 = 0x05; + cmd.cdw10 = ((uint32_t)(1)) | (((uint32_t)queue->qid) << 16); + cmd.cdw11_15[0] = 1; + cmd.prp1 = queue->cq.address; + + nvme_submit_admin(ctrl, cmd); + bool status = nvme_wait_admin(ctrl, 1000); + + if(status == false) + return nullptr; + + klibc::memset(&cmd, 0, sizeof(nvme_command)); + + cmd.cdw0 = 0x01; + cmd.cdw10 = ((uint32_t)(1)) | (((uint32_t)queue->qid) << 16); + cmd.cdw11_15[0] = (((uint32_t)queue->qid) << 16) | 0x1; + cmd.prp1 = queue->sq.address; + + nvme_submit_admin(ctrl, cmd); + status = nvme_wait_admin(ctrl, 1000); + + if(status == false) + return nullptr; + + uint8_t* doorbell_base = (uint8_t*)ctrl->bar0 + 0x1000; + uint32_t stride_bytes = ctrl->stride; + queue->sq_doorbell = (uint32_t*)(doorbell_base + ((2 * queue->qid) * stride_bytes)); + queue->cq_doorbell = (uint32_t*)(doorbell_base + ((2 * queue->qid + 1) * stride_bytes)); + nvme_qid_ptr++; + + return queue; +} + +bool nvme_identify(nvme_controller* ctrl) { + uint64_t phys_buffer = pmm::freelist::alloc_4k(); + +#ifdef NVME_ORANGE_TRACE + klibc::printf("NVME Trace: Trying to identify controller 0x%p\r\n",ctrl); +#endif + + nvme_command cmd = {}; + cmd.cdw0 = 0x06; + cmd.prp1 = phys_buffer; + cmd.cdw10 = 1; + cmd.nsid = 0; + + nvme_submit_admin(ctrl, cmd); + bool status = nvme_wait_admin(ctrl, 1000); + if(status == false) + return false; + arch::memory_barrier(); + + char model[41]; + klibc::memcpy(model, (void*)(phys_buffer + etc::hhdm() + 24), 40); + model[40] = '\0'; + klibc::printf("NVME: Controller Model: %s\r\n", model); + + ctrl->identify = (void*)(phys_buffer + etc::hhdm()); + + return true; +} + +void nvme_init(std::uint64_t base) { + + if(!time::timer) { + klibc::printf("NVME: Can't init without timer !\r\n"); + return; + } + + paging::map_range(gobject::kernel_root, base, base + etc::hhdm(), PAGE_SIZE * 4, PAGING_PRESENT | PAGING_RW | PAGING_NC); + + std::uint64_t start = time::timer->current_nano(); + + nvme_controller* controller = new nvme_controller; + controller->bar0 = (void*)(base + etc::hhdm()); + std::uint64_t cap = nvme::read64(controller, NVME_REG_CAP); + controller->max_queue_entries = (cap & 0xffff) + 1; + controller->stride = 4 << ((cap >> 32) & 0xf); + controller->admin_phase = 1; + + std::uint8_t mpsmin = (cap >> 48) & 0xf; + controller->mpsmin = mpsmin; + controller->page_size = 1 << (12 + mpsmin); + if(!((cap >> 37) & 1)) { + klibc::printf("NVME: Impossible to init because nvm is not supported\r\n"); + return; + } + + nvme::write32(controller, 0x14, nvme::read32(controller, 0x14) & ~(1 << 0)); + while(nvme::read32(controller, NVME_REG_CSTS) & 1) arch::pause(); + + controller->admin_cq.address = pmm::freelist::alloc_4k(); + controller->admin_sq.address = pmm::freelist::alloc_4k(); + controller->admin_cq.size = 63; + controller->admin_sq.size = 63; + nvme::write64(controller, 0x30, controller->admin_cq.address); + nvme::write64(controller, 0x28, controller->admin_sq.address); + nvme::write32(controller, 0x24, 63 | (63 << 16)); + + nvme::write32(controller, 0x14, (1 << 0) | (4 << 20) | (6 << 16)); + while(!(nvme::read32(controller, NVME_REG_CSTS) & 1)) arch::pause(); + + if(!nvme_identify(controller)) { + klibc::printf("NVME: Failed to identify !\r\n"); + return; + } + + controller->main_io_queue = nvme_create_io_queue(controller); + if(controller->main_io_queue == nullptr) { + klibc::printf("NVME: Failed to create io queue\r\n"); + return; + } + + if(!nvme_init_namespaces(controller)) { + klibc::printf("NVME: Failed to init namespaces\r\n"); + } + + std::uint64_t end = time::timer->current_nano(); + + klibc::printf("NVME: Detected %d namespaces in %lli us\r\n", controller->num_namespaces, (end - start) / 1000); + nvme_controllers[nvme_controller_ptr++] = controller; +} + +#if defined(__x86_64__) +void nvme_pci_init(pci_t pci, std::uint8_t a, std::uint8_t b, std::uint8_t c) { + if(pci.progIF == 2) { // nvme + std::uint32_t cmd = x86_64::pci::pci_read_config32(a,b,c,0x4); + klibc::printf("NVME_PCI: Bus mastering %d, memory access %d\r\n",(cmd & (1 << 2)) ? 1 : 0, (cmd & (1 << 1)) ? 1 : 0); + cmd |= 1 << 2; + cmd |= 1 << 1; + x86_64::pci::pci_write_config32(a,b,c,0x4,cmd); + nvme_init((std::uint64_t)(((std::uint64_t)pci.bar1 << 32) | (pci.bar0 & 0xFFFFFFF0))); + } +} +#endif + +void drivers::nvme::init() { + klibc::memset(nvme_controllers,0,sizeof(nvme_controllers)); +#if defined(__x86_64__) + x86_64::pci::reg(nvme_pci_init,1,8); +#else + klibc::printf("todo implement nvme finding on other arches\r\n"); +#endif +}
\ No newline at end of file diff --git a/kernel/src/drivers/nvme.hpp b/kernel/src/drivers/nvme.hpp new file mode 100644 index 0000000..69d957c --- /dev/null +++ b/kernel/src/drivers/nvme.hpp @@ -0,0 +1,184 @@ +#pragma once +#include <cstdint> +#include <drivers/disk.hpp> +#include <generic/hhdm.hpp> +#include <generic/lock/spinlock.hpp> +#include <generic/arch.hpp> + +#define NVME_ORANGE_TRACE + +#define NVME_REG_CAP 0x00 // Controller Capabilities +#define NVME_REG_VS 0x08 // Version +#define NVME_REG_CC 0x14 // Controller Configuration +#define NVME_REG_CSTS 0x1C // Controller Status +#define NVME_REG_AQA 0x24 // Admin Queue Attributes +#define NVME_REG_ASQ 0x28 // Admin Submission Queue Base Address +#define NVME_REG_ACQ 0x30 // Admin Completion Queue Base Address +#define NVME_CAP_MQES_MASK 0xFFFF // Maximum Queue Entries Supported +#define NVME_CAP_CQR (1ULL << 16) // Contiguous Queues Required +#define NVME_CAP_DSTRD_MASK (0xFULL << 32) // Doorbell Stride +#define NVME_CAP_CSS_MASK (0xFFULL << 37) // Command Sets Supported +#define NVME_CAP_MPSMIN_MASK (0xFULL << 48) // Memory Page Size Minimum +#define NVME_CAP_MPSMAX_MASK (0xFULL << 52) // Memory Page Size Maximum +#define NVME_CC_EN (1 << 0) // Enable +#define NVME_CC_CSS_NVM (0 << 4) // NVM Command Set +#define NVME_CC_SHN_NORMAL (1 << 14) // Normal shutdown +#define NVME_CSTS_RDY (1 << 0) // Ready +#define NVME_CSTS_CFS (1 << 1) // Controller Fatal Status +#define NVME_MAX_IO_QUEUES (65535) +#define NVME_MAX_QUEUE_SIZE 64 +#define NVME_RESET_TIMEOUT_MS 30000 +#define NVME_ENABLE_TIMEOUT_MS 30000 + +struct nvme_lbaf { + std::uint16_t ms; + std::uint8_t ds; + std::uint8_t rp; +}; + +struct nvme_command { + uint32_t cdw0; + uint32_t nsid; + uint32_t reserved[2]; + uint64_t metadata; + uint64_t prp1; + uint64_t prp2; + uint32_t cdw10; + uint32_t cdw11_15[5]; +}; + +struct nvme_completion { + uint32_t result; + uint32_t reserved; + uint16_t sq_head; + uint16_t sq_id; + uint16_t command_id; + uint16_t status; +}; + +struct nvme_id_ns { + std::uint64_t nsze; // Namespace Size + std::uint64_t ncap; // Namespace Capacity + std::uint64_t nuse; // Namespace Utilization + std::uint8_t nsfeat; // Namespace Features + std::uint8_t nlbaf; // Number of LBA Formats + std::uint8_t flbas; // Formatted LBA Size + std::uint8_t mc; // Metadata Capabilities + std::uint8_t dpc; // End-to-end Data Protection Capabilities + std::uint8_t dps; // End-to-end Data Protection Type Settings + std::uint8_t nmic; // Namespace Multi-path I/O and Namespace Sharing Capabilities + std::uint8_t rescap; // Reservation Capabilities + std::uint8_t fpi; // Format Progress Indicator + std::uint8_t dlfeat; // Deallocate Logical Block Features + std::uint16_t nawun; // Namespace Atomic Write Unit Normal + std::uint16_t nawupf; // Namespace Atomic Write Unit Power Fail + std::uint16_t nacwu; // Namespace Atomic Compare & Write Unit + std::uint16_t nabsn; // Namespace Atomic Boundary Size Normal + std::uint16_t nabo; // Namespace Atomic Boundary Offset + std::uint16_t nabspf; // Namespace Atomic Boundary Size Power Fail + std::uint16_t noiob; // Namespace Optimal I/O Boundary + std::uint8_t nvmcap[16]; // NVM Capacity + std::uint16_t npwg; // Namespace Preferred Write Granularity + std::uint16_t npwa; // Namespace Preferred Write Alignment + std::uint16_t npdg; // Namespace Preferred Deallocate Granularity + std::uint16_t npda; // Namespace Preferred Deallocate Alignment + std::uint16_t nows; // Namespace Optimal Write Size + std::uint16_t mssrl; // Maximum Single Source Range Length + std::uint32_t mcl; // Maximum Copy Length + std::uint8_t msrc; // Maximum Source Range Count + std::uint8_t rsvd81[11]; + std::uint32_t anagrpid; // ANA Group Identifier + std::uint8_t rsvd96[3]; + std::uint8_t nsattr; // Namespace Attributes + std::uint16_t nvmsetid; // NVM Set Identifier + std::uint16_t endgid; // Endurance Group Identifier + std::uint8_t nguid[16]; // Namespace Globally Unique Identifier + std::uint8_t eui64[8]; // IEEE Extended Unique Identifier + nvme_lbaf lbaf[16]; // LBA Format Support + std::uint8_t rsvd192[192]; + std::uint8_t vs[3712]; // Vendor Specific +}; + +static_assert(sizeof(nvme_id_ns) == 4096, "nvme_id_ns must be exactly 4096 bytes"); + +struct nvme_queue { + std::uint64_t address; + std::size_t size; +}; + +struct nvme_namespace { + uint32_t nsid; + uint64_t size; + uint32_t lba_size; + uint16_t lba_shift; + bool valid; + struct nvme_id_ns* ns_data; +}; + +struct nvme_pair_queue { + nvme_queue sq; + nvme_queue cq; + std::uint64_t sq_tail; + std::uint64_t cq_head; + std::uint8_t phase; + std::uint8_t qid; + volatile std::uint32_t* sq_doorbell; + volatile std::uint32_t* cq_doorbell; + locks::spinlock lock; +}; + +struct nvme_controller { + void* bar0; + uint32_t stride; + uint32_t page_size; + uint16_t max_queue_entries; + nvme_queue admin_sq; + nvme_queue admin_cq; + std::uint64_t admin_sq_tail; + std::uint64_t admin_cq_head; + std::uint8_t admin_phase; + nvme_pair_queue* main_io_queue; + nvme_namespace namespaces[256]; + uint32_t num_namespaces; + uint8_t mpsmin; + void* identify; +}; + +struct nvme_arg_disk { + nvme_controller* ctrl; + std::uint32_t nsid; +}; + +namespace drivers { + namespace nvme { + + static inline std::uint32_t read32(struct nvme_controller* ctrl, std::uint32_t offset) { + volatile std::uint32_t* reg = (volatile std::uint32_t*)((std::uint8_t*)ctrl->bar0 + offset); + std::uint32_t value = *reg; + arch::memory_barrier(); + return value; + } + + static inline void write32(struct nvme_controller* ctrl, std::uint32_t offset, std::uint32_t value) { + volatile std::uint32_t* reg = (volatile std::uint32_t*)((std::uint8_t*)ctrl->bar0 + offset); + *reg = value; + arch::memory_barrier(); + } + + static inline std::uint64_t read64(struct nvme_controller* ctrl, uint32_t offset) { + volatile std::uint64_t* reg = (volatile std::uint64_t*)((std::uint8_t*)ctrl->bar0 + offset); + std::uint64_t value = *reg; + arch::memory_barrier(); + return value; + } + + static inline void write64(struct nvme_controller* ctrl, std::uint32_t offset, std::uint32_t value) { + volatile std::uint64_t* reg = (volatile std::uint64_t*)((std::uint8_t*)ctrl->bar0 + offset); + *reg = value; + arch::memory_barrier(); + } + + void init(); + void disable(); + }; +};
\ No newline at end of file diff --git a/kernel/src/drivers/powerbutton.cpp b/kernel/src/drivers/powerbutton.cpp new file mode 100644 index 0000000..f34bdb9 --- /dev/null +++ b/kernel/src/drivers/powerbutton.cpp @@ -0,0 +1,28 @@ +#include <cstdint> +#include <drivers/powerbutton.hpp> +#include <klibc/stdio.hpp> +#if defined(__x86_64__) +#include <uacpi/event.h> +#include <generic/poweroff.hpp> + +static uacpi_interrupt_ret handle_power_button(uacpi_handle ctx) { + (void)ctx; + poweroff::reboot(); + return UACPI_INTERRUPT_HANDLED; +} + +void drivers::powerbutton::init() { + uacpi_install_fixed_event_handler( + UACPI_FIXED_EVENT_POWER_BUTTON, + handle_power_button, UACPI_NULL + ); + klibc::printf("PowerButton: Registered powerbutton at ip 0x%p\r\n",(std::uint64_t)handle_power_button); +} + +#else + +void drivers::powerbutton::init() { + +} + +#endif
\ No newline at end of file diff --git a/kernel/src/drivers/powerbutton.hpp b/kernel/src/drivers/powerbutton.hpp new file mode 100644 index 0000000..f14c079 --- /dev/null +++ b/kernel/src/drivers/powerbutton.hpp @@ -0,0 +1,6 @@ +#pragma once +namespace drivers { + namespace powerbutton { + void init(); + }; +};
\ No newline at end of file diff --git a/kernel/src/drivers/uacpi_kernel_api.cpp b/kernel/src/drivers/uacpi_kernel_api.cpp index 532fe63..1a4c5cb 100644 --- a/kernel/src/drivers/uacpi_kernel_api.cpp +++ b/kernel/src/drivers/uacpi_kernel_api.cpp @@ -5,6 +5,7 @@ #if defined(__x86_64__) #include <arch/x86_64/drivers/io.hpp> #include <arch/x86_64/drivers/pci.hpp> +#include <arch/x86_64/irq.hpp> #endif #include <generic/bootloader/bootloader.hpp> #include <generic/hhdm.hpp> @@ -349,6 +350,21 @@ uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request*) { return UACPI_STATUS_OK; } +#if defined(__x86_64__) + +uacpi_status uacpi_kernel_install_interrupt_handler( + uacpi_u32 irq, uacpi_interrupt_handler base, uacpi_handle ctx, + uacpi_handle *out_irq_handle +) { + (void)ctx; + std::uint8_t vec = x86_64::irq::create(irq, IRQ_TYPE_LEGACY, (void (*)(void*))((void*)base), 0, 0); + *out_irq_handle = (uacpi_handle)((std::uint64_t)vec); + + return UACPI_STATUS_OK; +} + +#else + uacpi_status uacpi_kernel_install_interrupt_handler( uacpi_u32 irq, uacpi_interrupt_handler base, uacpi_handle ctx, uacpi_handle *out_irq_handle @@ -360,6 +376,20 @@ uacpi_status uacpi_kernel_install_interrupt_handler( return UACPI_STATUS_OK; } +#endif + +uacpi_interrupt_state uacpi_kernel_disable_interrupts(void) { + return 1; +} + +/* + * Restore the state of the interrupt flags to the kernel-defined value provided + * in 'state'. + */ +void uacpi_kernel_restore_interrupts(uacpi_interrupt_state state) { + (void)state; +} + uacpi_status uacpi_kernel_uninstall_interrupt_handler( uacpi_interrupt_handler, uacpi_handle irq_handle ) { diff --git a/kernel/src/generic/arch.hpp b/kernel/src/generic/arch.hpp index cd569ef..b983a1b 100644 --- a/kernel/src/generic/arch.hpp +++ b/kernel/src/generic/arch.hpp @@ -10,6 +10,7 @@ #define ARCH_INIT_EARLY 0 #define ARCH_INIT_COMMON 1 +#define ARCH_INIT_MP 2 #define IRQ_TYPE_OTHER 0 #define IRQ_TYPE_LEGACY 1 #define IRQ_TYPE_MSI 2 @@ -33,6 +34,10 @@ namespace arch { extern void copy_higher_half(std::uintptr_t root, std::uintptr_t src_root); extern int level_paging(); + extern std::uint64_t current_root(); + + extern void memory_barrier(); + extern int register_handler(int irq, int type, std::uint64_t flags, void (*func)(void* arg), void* arg); extern void panic(char* msg); diff --git a/kernel/src/generic/bootloader/bootloader.hpp b/kernel/src/generic/bootloader/bootloader.hpp index 21aa650..de9483e 100644 --- a/kernel/src/generic/bootloader/bootloader.hpp +++ b/kernel/src/generic/bootloader/bootloader.hpp @@ -12,6 +12,7 @@ namespace bootloader { virtual std::uint64_t get_kernel_phys() = 0; virtual std::uintptr_t get_kernel_virt() = 0; virtual limine_memmap_response* get_memory_map() = 0; + virtual limine_mp_response* get_mp_info() = 0; virtual bool is_5_level_paging() = 0; }; diff --git a/kernel/src/generic/bootloader/limine.cpp b/kernel/src/generic/bootloader/limine.cpp index e7175a4..00a7859 100644 --- a/kernel/src/generic/bootloader/limine.cpp +++ b/kernel/src/generic/bootloader/limine.cpp @@ -27,6 +27,7 @@ __attribute__((used, section(".limine_requests"))) volatile limine_hhdm_request __attribute__((used, section(".limine_requests"))) volatile limine_executable_address_request kaddr_request = { .id = LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, .revision = 0, .response = nullptr }; +__attribute__((used, section(".limine_requests"))) volatile limine_mp_request mp_request = { .id = LIMINE_MP_REQUEST_ID, .revision = 0, .response = nullptr, .flags = 0}; __attribute__((used, section(".limine_requests"))) volatile limine_rsdp_request rsdp_request = { .id = LIMINE_RSDP_REQUEST_ID, .revision = 0, .response = nullptr }; @@ -74,6 +75,10 @@ namespace bootloader { return memmap_request.response; } + limine_mp_response* limine::get_mp_info() { + return mp_request.response; + } + #if defined(__x86_64__) bool limine::is_5_level_paging() { return _5lvl_paging.response->mode == LIMINE_PAGING_MODE_X86_64_5LVL ? true : false; diff --git a/kernel/src/generic/bootloader/limine.hpp b/kernel/src/generic/bootloader/limine.hpp index c0fd78b..eac231d 100644 --- a/kernel/src/generic/bootloader/limine.hpp +++ b/kernel/src/generic/bootloader/limine.hpp @@ -10,6 +10,7 @@ public: std::uint64_t get_kernel_phys() override; std::uint64_t get_kernel_virt() override; limine_memmap_response* get_memory_map() override; + limine_mp_response* get_mp_info() override; bool is_5_level_paging() override; }; };
\ No newline at end of file diff --git a/kernel/src/generic/heap.cpp b/kernel/src/generic/heap.cpp index e096a68..6468a8a 100644 --- a/kernel/src/generic/heap.cpp +++ b/kernel/src/generic/heap.cpp @@ -23,7 +23,7 @@ void kheap::init() { } -int is_early = 1; +int is_early = 0; void kheap::opt_free(void* ptr) { if(!is_early) { diff --git a/kernel/src/generic/lock/spinlock.hpp b/kernel/src/generic/lock/spinlock.hpp index 12bbe16..782e682 100644 --- a/kernel/src/generic/lock/spinlock.hpp +++ b/kernel/src/generic/lock/spinlock.hpp @@ -1,14 +1,19 @@ +#pragma once #include <atomic> #include <cstdint> #include <generic/arch.hpp> namespace locks { + inline bool is_disabled = 0; class spinlock { private: std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { + if(is_disabled) + return; + while (flag.test_and_set(std::memory_order_acquire)) { arch::pause(); } diff --git a/kernel/src/generic/mp.cpp b/kernel/src/generic/mp.cpp new file mode 100644 index 0000000..0ab4678 --- /dev/null +++ b/kernel/src/generic/mp.cpp @@ -0,0 +1,66 @@ +#include <generic/bootloader/bootloader.hpp> +#include <generic/lock/spinlock.hpp> +#if defined(__x86_64__) +#include <arch/x86_64/cpu_local.hpp> +#endif +#include <generic/time.hpp> +#include <generic/arch.hpp> +#include <generic/mp.hpp> +#include <klibc/stdio.hpp> +#include <cstdint> +#include <atomic> + +std::uint32_t balance_how_much_cpus = 1; +std::uint32_t how_much_cpus = 0; +static mp::barrier mp_barrier; +locks::spinlock smp_lock; + +std::uint32_t mp::cpu_count() { + return how_much_cpus; +} + +void mp::sync() { + if (how_much_cpus <= 1) return; + + uint32_t current_gen = mp_barrier.generation.load(std::memory_order_acquire); + if (mp_barrier.count.fetch_add(1, std::memory_order_acq_rel) == (how_much_cpus - 1)) { + mp_barrier.count.store(0, std::memory_order_relaxed); + mp_barrier.generation.fetch_add(1, std::memory_order_release); + } else { + while (mp_barrier.generation.load(std::memory_order_acquire) == current_gen) { + arch::pause(); + } + } +} + +void smptrampoline(limine_mp_info* smp_info) { + smp_lock.lock(); + std::uint32_t enum_cpu = smp_info->processor_id; + arch::init(ARCH_INIT_MP); +#if defined(__x86_64__) + x86_64::cpu_data()->cpu = balance_how_much_cpus++; + enum_cpu = x86_64::cpu_data()->cpu; +#endif + klibc::printf("SMP: Cpu %d is online (%d)\r\n",smp_info->lapic_id,enum_cpu); + smp_lock.unlock(); + mp::sync(); + if(time::timer) time::timer->sleep(10000); + mp::sync(); + arch::enable_interrupts(); + while(true) { + arch::wait_for_interrupt(); + } +} + +void mp::init() { + limine_mp_response* mp_info = bootloader::bootloader->get_mp_info(); + if(!mp_info) + return; + + how_much_cpus = mp_info->cpu_count; + for(std::uint16_t i = 0;i < mp_info->cpu_count;i++) { + if(mp_info->bsp_lapic_id != i) { + mp_info->cpus[i]->goto_address = smptrampoline; + } + } +}
\ No newline at end of file diff --git a/kernel/src/generic/mp.hpp b/kernel/src/generic/mp.hpp new file mode 100644 index 0000000..4820311 --- /dev/null +++ b/kernel/src/generic/mp.hpp @@ -0,0 +1,15 @@ +#pragma once +#include <cstdint> +#include <atomic> + +namespace mp { + + struct barrier { + std::atomic<uint32_t> count{0}; + std::atomic<uint32_t> generation{0}; + }; + + void init(); + void sync(); + std::uint32_t cpu_count(); +}
\ No newline at end of file diff --git a/kernel/src/generic/poweroff.cpp b/kernel/src/generic/poweroff.cpp new file mode 100644 index 0000000..d81b3df --- /dev/null +++ b/kernel/src/generic/poweroff.cpp @@ -0,0 +1,51 @@ +#include <cstdint> +#include <generic/poweroff.hpp> +#include <drivers/nvme.hpp> +#if defined(__x86_64__) +#include <uacpi/sleep.h> +#include <arch/x86_64/cpu/lapic.hpp> +#include <arch/x86_64/schedule_timer.hpp> +#endif +#include <generic/arch.hpp> +#include <klibc/stdio.hpp> +#include <generic/time.hpp> +#include <generic/lock/spinlock.hpp> + +void poweroff::prepare_for_shutdown() { + drivers::nvme::disable(); + klibc::printf("Poweroff: NVME is disabled\r\n"); +#if defined(__x86_64__) + x86_64::schedule_timer::off(); +#endif + locks::is_disabled = true; + klibc::printf("Poweroff: Preparing for shutdown is successful\r\n"); +} + +void poweroff::off() { + arch::disable_interrupts(); + prepare_for_shutdown(); + if(time::timer) { + klibc::printf("Poweroff: Shutdowning after 3 seconds\r\n"); + time::timer->sleep(3 * (1000 * 1000)); + } +#if defined(__x86_64__) + uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5); + uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5); +#endif + klibc::printf("uhh its safe to shutdown yk\r\n"); + arch::hcf(); +} + +void poweroff::reboot() { + arch::disable_interrupts(); + prepare_for_shutdown(); + if(time::timer) { + klibc::printf("Poweroff: Rebooting after 3 seconds\r\n"); + time::timer->sleep(3 * (1000 * 1000)); + } +#if defined(__x86_64__) + uacpi_reboot(); +#endif + klibc::printf("uhh its safe to reboot yk\r\n"); + arch::hcf(); +}
\ No newline at end of file diff --git a/kernel/src/generic/poweroff.hpp b/kernel/src/generic/poweroff.hpp new file mode 100644 index 0000000..7eb482a --- /dev/null +++ b/kernel/src/generic/poweroff.hpp @@ -0,0 +1,6 @@ +#pragma once +namespace poweroff { + void prepare_for_shutdown(); + void off(); + void reboot(); +}
\ No newline at end of file diff --git a/kernel/src/generic/scheduling.cpp b/kernel/src/generic/scheduling.cpp new file mode 100644 index 0000000..22162fe --- /dev/null +++ b/kernel/src/generic/scheduling.cpp @@ -0,0 +1,80 @@ +#include <cstdint> +#include <generic/scheduling.hpp> +#include <generic/arch.hpp> +#include <generic/pmm.hpp> +#include <generic/paging.hpp> +#include <generic/hhdm.hpp> +#include <utils/gobject.hpp> +#include <generic/mp.hpp> +#define KERNEL_STACK_SIZE (1024 * 32) + +thread* head_proc = 0; +std::uint32_t last_id = 0; + +void scheduling_balance_cpus() { + std::uint32_t cpu_ptr = 0; + thread* proc = head_proc; + while(proc) { + if(proc->status != PROCESS_KILLED && proc->status != PROCESS_ZOMBIE) { + proc->cpu = cpu_ptr++; + if(cpu_ptr == mp::cpu_count()) cpu_ptr = 0; + } + proc = proc->next; + } +} + + +thread* process::create_process(bool is_user) { + (void)is_user; + return nullptr; +} + +thread* process::by_id(std::uint32_t id) { + thread* current = head_proc; + while(current) { + if(current->id == id) + return current; + } + return nullptr; +} + +thread* process::kthread(void (*func)(void*), void* arg) { + thread* new_thread = (thread*)(pmm::freelist::alloc_4k() + etc::hhdm()); + new_thread->id = ++last_id; + new_thread->lock.lock(); + new_thread->pid = new_thread->id; +#if defined(__x86_64__) + new_thread->ctx.ss = 0; + new_thread->ctx.cs = 0x08; + new_thread->ctx.rip = (std::uint64_t)func; + new_thread->ctx.rdi = (std::uint64_t)arg; + new_thread->ctx.rsp = (std::uint64_t)(pmm::buddy::alloc(KERNEL_STACK_SIZE).phys + etc::hhdm() + (KERNEL_STACK_SIZE - 1024)); + new_thread->ctx.cr3 = gobject::kernel_root; +#endif + new_thread->original_root = gobject::kernel_root; + return new_thread; +} + +void process::wakeup(thread* thread) { + thread->status = PROCESS_LIVE; + thread->lock.unlock(); +} + +void process::kill(thread* thread) { + thread->status = PROCESS_ZOMBIE; + thread->lock.try_lock(); + if(thread->syscall_stack) pmm::buddy::free(thread->syscall_stack - etc::hhdm()); + if(thread->name) pmm::freelist::free((std::uint64_t)thread->name - etc::hhdm()); + if(thread->chroot) pmm::freelist::free((std::uint64_t)thread->name - etc::hhdm()); + if(thread->cwd) pmm::freelist::free((std::uint64_t)thread->name - etc::hhdm()); + if(thread->sig) delete thread->sig; + thread->syscall_stack = 0; + thread->name = 0; + thread->chroot = 0; + thread->cwd = 0; + thread->sig = 0; +} + +void process::schedule(void* ctx) { + (void)ctx; +}
\ No newline at end of file diff --git a/kernel/src/generic/scheduling.hpp b/kernel/src/generic/scheduling.hpp new file mode 100644 index 0000000..c775448 --- /dev/null +++ b/kernel/src/generic/scheduling.hpp @@ -0,0 +1,64 @@ +#pragma once +#include <cstdint> +#if defined(__x86_64__) +#include <arch/x86_64/cpu/idt.hpp> +#elif defined(__aarch64__) +#include <arch/aarch64/cpu/el.hpp> +#endif +#include <utils/signal.hpp> +#include <generic/lock/spinlock.hpp> +#include <atomic> + +#define PROCESS_NONE 1 +#define PROCESS_LIVE 2 +#define PROCESS_KILLED 3 +#define PROCESS_ZOMBIE 4 +#define PROCESS_SLEEP 5 + +struct signal_member { + void* restorer; + void* handler; + std::uint32_t flags; +}; + +struct thread { + std::uint32_t id; + std::uint32_t pid; + std::uint32_t exit_code; +#if defined(__x86_64__) + std::uint8_t* sse_ctx; + std::uint64_t fs_base; + x86_64::idt::int_frame_t ctx; +#elif defined(__aarch64__) + aarch64::el::int_frame ctx; +#endif + signal_manager* sig; + signal_member signals_handlers[32]; + sig_stack signal_stack; + locks::spinlock lock; + std::atomic<std::uint32_t> futex; + std::atomic<std::uint32_t> status; + std::atomic<std::uint32_t> cpu; + std::uint64_t syscall_stack; + std::uint64_t original_root; + + char* name; + char* cwd; + char* chroot; + thread* next; +}; + +static_assert(sizeof(thread) < 4096, "thread struct is bigger than page size (bug)"); + +namespace process { + thread* create_process(bool is_user); + thread* by_id(std::uint32_t id); + thread* kthread(void (*func)(void*), void* arg); + void wakeup(thread* thread); + void kill(thread* thread); + + extern "C" void schedule(void* frame); + + extern "C" void switch_ctx(void* frame); + extern "C" void yield(); +};
\ No newline at end of file diff --git a/kernel/src/klibc/stdio.cpp b/kernel/src/klibc/stdio.cpp index 7e8e283..518e5e2 100644 --- a/kernel/src/klibc/stdio.cpp +++ b/kernel/src/klibc/stdio.cpp @@ -17,6 +17,12 @@ #include <cstdint> #include <cstddef> +#if defined(__x86_64__) +#include <arch/x86_64/drivers/serial.hpp> +#endif +#include <generic/lock/spinlock.hpp> + +locks::spinlock print_lock; int klibc::_snprintf(char *buffer, std::size_t bufsz, char const *fmt, va_list vlist) { int const rv = npf_vsnprintf(buffer, bufsz, fmt, vlist); @@ -24,11 +30,16 @@ int klibc::_snprintf(char *buffer, std::size_t bufsz, char const *fmt, va_list v } void klibc::printf(const char* fmt, ...) { + print_lock.lock(); va_list val; va_start(val, fmt); char buffer[4096]; memset(buffer,0,4096); int len = _snprintf(buffer,4096,fmt,val); utils::flanterm::write(buffer,len); +#if defined(__x86_64__) + x86_64::serial::write_data(buffer,len); +#endif va_end(val); + print_lock.unlock(); }
\ No newline at end of file diff --git a/kernel/src/klibc/string.cpp b/kernel/src/klibc/string.cpp index 27884ad..e4ead45 100644 --- a/kernel/src/klibc/string.cpp +++ b/kernel/src/klibc/string.cpp @@ -3,26 +3,43 @@ #include <klibc/string.hpp> void* klibc::memcpy(void *__restrict dest, const void *__restrict src, std::size_t n) { +#if defined(__x86_64__) + asm volatile( + "rep movsb" + : "+D"(dest), "+S"(src), "+c"(n) + : + : "memory" + ); + return dest; +#else std::uint8_t *__restrict pdest = static_cast<std::uint8_t *__restrict>(dest); const std::uint8_t *__restrict psrc = static_cast<const std::uint8_t *__restrict>(src); for (std::size_t i = 0; i < n; i++) { pdest[i] = psrc[i]; } - return dest; +#endif } void* klibc::memset(void *s, int c, std::size_t n) { +#if defined(__x86_64__) + void* original_s = s; + asm volatile( + "rep stosb" + : "+D"(s), "+c"(n) + : "a"((std::uint8_t)c) + : "memory" + ); + return original_s; +#else std::uint8_t *p = static_cast<std::uint8_t *>(s); - for (std::size_t i = 0; i < n; i++) { - p[i] = static_cast<uint8_t>(c); + p[i] = static_cast<std::uint8_t>(c); } - return s; +#endif } - void* klibc::memmove(void *dest, const void *src, std::size_t n) { std::uint8_t *pdest = static_cast<std::uint8_t *>(dest); const std::uint8_t *psrc = static_cast<const std::uint8_t *>(src); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 3722589..6af515e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -8,13 +8,25 @@ #include <generic/heap.hpp> #include <drivers/acpi.hpp> #include <generic/time.hpp> +#include <drivers/nvme.hpp> +#include <drivers/powerbutton.hpp> +#include <generic/mp.hpp> + +#if defined(__x86_64__) +#include <arch/x86_64/drivers/pci.hpp> +#include <arch/x86_64/drivers/serial.hpp> +#endif extern std::size_t memory_size; +extern int is_early; extern "C" void main() { utils::cxx::init_constructors(); bootloader::init(); utils::flanterm::init(); +#if defined(__x86_64__) + x86_64::serial::init(); +#endif pmm::init(); paging::init(); kheap::init(); @@ -26,13 +38,19 @@ extern "C" void main() { arch::init(ARCH_INIT_EARLY); acpi::full_init(); arch::init(ARCH_INIT_COMMON); - extern int is_early; - is_early = 0; + mp::init(); + mp::sync(); + drivers::powerbutton::init(); + drivers::nvme::init(); +#if defined(__x86_64__) + x86_64::pci::initworkspace(); + klibc::printf("PCI: launched all drivers\r\n"); +#endif klibc::printf("Boot is done\r\n"); - klibc::printf("current sec %lli, waiting 2 sec and page faulting\r\n",time::timer->current_nano() / (1000 * 1000 * 1000)); - klibc::printf("current sec %lli\r\n",time::timer->current_nano() / (1000 * 1000 * 1000)); - arch::tlb_flush(0x47afe000,PAGE_SIZE); - *(int*)0x47afe710 = 0; - - arch::hcf(); + mp::sync(); + arch::enable_interrupts(); + while(1) { + //klibc::printf("current sec %lli\r\n",time::timer->current_nano() / (1000 * 1000 * 1000)); + arch::wait_for_interrupt(); + } } diff --git a/kernel/src/utils/assert.hpp b/kernel/src/utils/assert.hpp new file mode 100644 index 0000000..554c7dc --- /dev/null +++ b/kernel/src/utils/assert.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include <generic/arch.hpp> +#include <klibc/stdio.hpp> + +#define assert(cond, msg,...) if(!(cond)) { klibc::printf("Failed assert at %s:%s:%d \"" msg "\"\n" , __FILE__ ,__FUNCTION__, __LINE__ , ##__VA_ARGS__); arch::panic((char*)"Failed assert"); }
\ No newline at end of file diff --git a/kernel/src/utils/cxx/cxx_stuff.cpp b/kernel/src/utils/cxx/cxx_stuff.cpp index c12546a..54e8b58 100644 --- a/kernel/src/utils/cxx/cxx_stuff.cpp +++ b/kernel/src/utils/cxx/cxx_stuff.cpp @@ -1,56 +1,22 @@ #include <cstdint> +#include <klibc/string.hpp> extern "C" { void *memcpy(void *__restrict dest, const void *__restrict src, std::size_t n) { - std::uint8_t *__restrict pdest = static_cast<std::uint8_t *__restrict>(dest); - const std::uint8_t *__restrict psrc = static_cast<const std::uint8_t *__restrict>(src); - - for (std::size_t i = 0; i < n; i++) { - pdest[i] = psrc[i]; - } - - return dest; + return klibc::memcpy(dest, src, n); } void *memset(void *s, int c, std::size_t n) { - std::uint8_t *p = static_cast<std::uint8_t *>(s); - - for (std::size_t i = 0; i < n; i++) { - p[i] = static_cast<uint8_t>(c); - } - - return s; + return klibc::memset(s, c, n); } void *memmove(void *dest, const void *src, std::size_t n) { - std::uint8_t *pdest = static_cast<std::uint8_t *>(dest); - const std::uint8_t *psrc = static_cast<const std::uint8_t *>(src); - - if (reinterpret_cast<std::uintptr_t>(src) > reinterpret_cast<std::uintptr_t>(dest)) { - for (std::size_t i = 0; i < n; i++) { - pdest[i] = psrc[i]; - } - } else if (reinterpret_cast<std::uintptr_t>(src) < reinterpret_cast<std::uintptr_t>(dest)) { - for (std::size_t i = n; i > 0; i--) { - pdest[i-1] = psrc[i-1]; - } - } - - return dest; + return klibc::memmove(dest, src, n); } int memcmp(const void *s1, const void *s2, std::size_t n) { - const std::uint8_t *p1 = static_cast<const std::uint8_t *>(s1); - const std::uint8_t *p2 = static_cast<const std::uint8_t *>(s2); - - for (std::size_t i = 0; i < n; i++) { - if (p1[i] != p2[i]) { - return p1[i] < p2[i] ? -1 : 1; - } - } - - return 0; + return klibc::memcmp(s1, s2, n); } } diff --git a/kernel/src/utils/errno.hpp b/kernel/src/utils/errno.hpp new file mode 100644 index 0000000..be57dd0 --- /dev/null +++ b/kernel/src/utils/errno.hpp @@ -0,0 +1,136 @@ +#pragma once + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define ENOTBLK 15 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define ETXTBSY 26 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 35 +#define ENAMETOOLONG 36 +#define ENOLCK 37 +#define ENOSYS 38 +#define ENOTEMPTY 39 +#define ELOOP 40 +#define EWOULDBLOCK EAGAIN +#define ENOMSG 42 +#define EIDRM 43 +#define ECHRNG 44 +#define EL2NSYNC 45 +#define EL3HLT 46 +#define EL3RST 47 +#define ELNRNG 48 +#define EUNATCH 49 +#define ENOCSI 50 +#define EL2HLT 51 +#define EBADE 52 +#define EBADR 53 +#define EXFULL 54 +#define ENOANO 55 +#define EBADRQC 56 +#define EBADSLT 57 +#define EDEADLOCK EDEADLK +#define EBFONT 59 +#define ENOSTR 60 +#define ENODATA 61 +#define ETIME 62 +#define ENOSR 63 +#define ENONET 64 +#define ENOPKG 65 +#define EREMOTE 66 +#define ENOLINK 67 +#define EADV 68 +#define ESRMNT 69 +#define ECOMM 70 +#define EPROTO 71 +#define EMULTIHOP 72 +#define EDOTDOT 73 +#define EBADMSG 74 +#define EOVERFLOW 75 +#define ENOTUNIQ 76 +#define EBADFD 77 +#define EREMCHG 78 +#define ELIBACC 79 +#define ELIBBAD 80 +#define ELIBSCN 81 +#define ELIBMAX 82 +#define ELIBEXEC 83 +#define EILSEQ 84 +#define ERESTART 85 +#define ESTRPIPE 86 +#define EUSERS 87 +#define ENOTSOCK 88 +#define EDESTADDRREQ 89 +#define EMSGSIZE 90 +#define EPROTOTYPE 91 +#define ENOPROTOOPT 92 +#define EPROTONOSUPPORT 93 +#define ESOCKTNOSUPPORT 94 +#define EOPNOTSUPP 95 +#define ENOTSUP EOPNOTSUPP +#define EPFNOSUPPORT 96 +#define EAFNOSUPPORT 97 +#define EADDRINUSE 98 +#define EADDRNOTAVAIL 99 +#define ENETDOWN 100 +#define ENETUNREACH 101 +#define ENETRESET 102 +#define ECONNABORTED 103 +#define ECONNRESET 104 +#define ENOBUFS 105 +#define EISCONN 106 +#define ENOTCONN 107 +#define ESHUTDOWN 108 +#define ETOOMANYREFS 109 +#define ETIMEDOUT 110 +#define ECONNREFUSED 111 +#define EHOSTDOWN 112 +#define EHOSTUNREACH 113 +#define EALREADY 114 +#define EINPROGRESS 115 +#define ESTALE 116 +#define EUCLEAN 117 +#define ENOTNAM 118 +#define ENAVAIL 119 +#define EISNAM 120 +#define EREMOTEIO 121 +#define EDQUOT 122 +#define ENOMEDIUM 123 +#define EMEDIUMTYPE 124 +#define ECANCELED 125 +#define ENOKEY 126 +#define EKEYEXPIRED 127 +#define EKEYREVOKED 128 +#define EKEYREJECTED 129 +#define EOWNERDEAD 130 +#define ENOTRECOVERABLE 131 +#define ERFKILL 132 +#define EHWPOISON 133
\ No newline at end of file diff --git a/kernel/src/utils/signal.hpp b/kernel/src/utils/signal.hpp new file mode 100644 index 0000000..ddad3f7 --- /dev/null +++ b/kernel/src/utils/signal.hpp @@ -0,0 +1,119 @@ +#pragma once +#include <cstdint> +#include <utils/bitmap.hpp> +#include <generic/lock/spinlock.hpp> + +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGTRAP 5 +#define SIGIOT SIGABRT +#define SIGBUS 7 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGABRT 6 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGPOLL 29 +#define SIGSYS 31 +#define SIGUNUSED SIGSYS +#define SIGCANCEL 32 +#define SIGTIMER 33 + +typedef struct +{ + unsigned long long __val; +} __sigset_t; + +typedef __sigset_t sigset_t; + +void signal_ret_sigmask(sigset_t* sigmask); + +#define __sigmask(sig) \ + (1UL << (((sig) - 1) % 64)) + +static inline int +__sigword (int sig) +{ + return (sig - 1) / 64; +} + +static inline int +__sigismember (sigset_t *set, int sig) +{ + return (set->__val & (1 << (sig - 1))) ? 1 : 0; +} + +struct sig_stack { + void *ss_sp; /* Base address of stack */ + int ss_flags; /* Flags */ + std::size_t ss_size; /* Number of bytes in stack */ +}; + +class signal_manager { +private: + locks::spinlock lock; + utils::bitmap* bitmap; + std::uint8_t* sigs; +public: + signal_manager() { + bitmap = new utils::bitmap(256); + sigs = new std::uint8_t[256]; + } + + std::int8_t pop(sigset_t* sigset) { + this->lock.lock(); + for(int i = 0;i < 256; i++) { + if(this->bitmap->test(i) && (this->sigs[i] == 9 || !__sigismember(sigset,this->sigs[i]))) { + std::uint8_t saved_sig = this->sigs[i]; + this->bitmap->clear(i); + this->lock.unlock(); + return (std::int8_t)saved_sig; + } + } + this->lock.unlock(); + return -1; + } + + void push(std::uint8_t sig) { + this->lock.lock(); + for(int i = 0;i < 256; i++) { + if(!this->bitmap->test(i)) { + this->bitmap->set(i); + this->sigs[i] = sig; + this->lock.unlock(); + return; + } + } + this->lock.unlock(); + } + + bool is_not_empty_sigset(sigset_t* sigsetz) { + this->lock.lock(); + + // ban sigset signals :p + for(int i = 0; i < 128; i++) { + if(this->bitmap->test(i) && !__sigismember(sigsetz,this->sigs[i])) { + this->lock.unlock(); // great there's unbanned pending signal + return 1; + } + } + + this->lock.unlock(); + return 0; + } + +}; |
