From b3489321729e2d89779beb855ab34727cade6c38 Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Wed, 20 Nov 2024 05:15:06 +0200 Subject: [PATCH] Merge remote-tracking branch 'Kernel/mb2_32_64_test' into Kernel-mb2_32_64_test --- Kernel/.github/workflows/flawfinder.yml | 38 + Kernel/.gitignore | 7 + Kernel/.vscode/c_boilerplates.code-snippets | 69 + Kernel/.vscode/c_cpp_properties.json | 219 + Kernel/.vscode/extensions.json | 13 + Kernel/.vscode/launch.json | 40 + Kernel/.vscode/settings.json | 19 + Kernel/Architecture/aarch64/Bootstrap/boot.S | 36 + Kernel/Architecture/aarch64/Entry.cpp | 27 + Kernel/Architecture/aarch64/SystemCalls.cpp | 31 + .../aarch64/cpu/SymmetricMultiprocessing.cpp | 59 + Kernel/Architecture/aarch64/linker.ld | 83 + Kernel/Architecture/aarch64/runtime/crt0.S | 17 + Kernel/Architecture/aarch64/runtime/crt1.S | 17 + .../Architecture/aarch64/runtime/crtbegin.c | 1 + Kernel/Architecture/aarch64/runtime/crtend.c | 1 + Kernel/Architecture/aarch64/runtime/crti.S | 13 + Kernel/Architecture/aarch64/runtime/crtn.S | 7 + ...AdvancedConfigurationAndPowerInterface.cpp | 155 + Kernel/Architecture/amd64/Bootstrap/GDT32.asm | 47 + .../amd64/Bootstrap/Header_Multiboot.asm | 5 + .../amd64/Bootstrap/Header_Multiboot2.asm | 64 + Kernel/Architecture/amd64/Bootstrap/Limine.c | 346 + .../amd64/Bootstrap/Multiboot.cpp | 299 + .../amd64/Bootstrap/Multiboot64bitMap.cpp | 287 + .../amd64/Bootstrap/Multiboot_Init.asm | 114 + .../amd64/Bootstrap/Multiboot_PageTable.asm | 45 + .../DifferentiatedSystemDescriptionTable.cpp | 255 + .../amd64/MultipleAPICDescriptionTable.cpp | 91 + Kernel/Architecture/amd64/SystemCalls.cpp | 92 + Kernel/Architecture/amd64/acpi.hpp | 294 + ...dvancedProgrammableInterruptController.cpp | 403 + .../amd64/cpu/GlobalDescriptorTable.cpp | 214 + .../amd64/cpu/InterruptDescriptorTable.cpp | 757 ++ .../Architecture/amd64/cpu/SMPTrampoline.asm | 136 + .../amd64/cpu/SymmetricMultiprocessing.cpp | 141 + Kernel/Architecture/amd64/cpu/apic.hpp | 354 + Kernel/Architecture/amd64/cpu/gdt.hpp | 164 + Kernel/Architecture/amd64/cpu/idt.hpp | 84 + Kernel/Architecture/amd64/linker.ld | 92 + Kernel/Architecture/amd64/runtime/crt0.c | 15 + Kernel/Architecture/amd64/runtime/crt1.c | 15 + Kernel/Architecture/amd64/runtime/crtbegin.c | 1 + Kernel/Architecture/amd64/runtime/crtend.c | 1 + Kernel/Architecture/amd64/runtime/crti.S | 13 + Kernel/Architecture/amd64/runtime/crtn.S | 7 + Kernel/Architecture/amd64/rust-target.json | 21 + .../Architecture/i386/ArithmeticOperations.c | 327 + .../i386/Bootstrap/Header_Multiboot.asm | 5 + .../i386/Bootstrap/Header_Multiboot2.asm | 41 + .../Architecture/i386/Bootstrap/Multiboot.cpp | 286 + .../i386/Bootstrap/Multiboot_Init.asm | 50 + .../i386/Bootstrap/Multiboot_PageTable.asm | 10 + .../Architecture/i386/Interrupts/8259PIC.cpp | 140 + Kernel/Architecture/i386/Interrupts/pic.hpp | 59 + Kernel/Architecture/i386/SystemCalls.cpp | 30 + Kernel/Architecture/i386/acpi.hpp | 294 + .../i386/cpu/GlobalDescriptorTable.cpp | 116 + .../i386/cpu/SymmetricMultiprocessing.cpp | 54 + Kernel/Architecture/i386/cpu/apic.hpp | 354 + Kernel/Architecture/i386/cpu/gdt.hpp | 162 + Kernel/Architecture/i386/cpu/idt.hpp | 28 + Kernel/Architecture/i386/linker.ld | 93 + Kernel/Architecture/i386/runtime/crt0.c | 15 + Kernel/Architecture/i386/runtime/crt1.c | 15 + Kernel/Architecture/i386/runtime/crtbegin.c | 1 + Kernel/Architecture/i386/runtime/crtend.c | 1 + Kernel/Architecture/i386/runtime/crti.S | 13 + Kernel/Architecture/i386/runtime/crtn.S | 7 + Kernel/Core/CPU.cpp | 492 + Kernel/Core/Crash/CrashDetails.cpp | 346 + Kernel/Core/Crash/CrashHandler.cpp | 1122 ++ Kernel/Core/Crash/KBDrv.cpp | 194 + Kernel/Core/Crash/SFrame.cpp | 148 + Kernel/Core/Crash/Screens/Console.cpp | 42 + Kernel/Core/Crash/Screens/Details.cpp | 267 + Kernel/Core/Crash/Screens/Main.cpp | 390 + Kernel/Core/Crash/Screens/StackFrame.cpp | 99 + Kernel/Core/Crash/Screens/Tasks.cpp | 87 + Kernel/Core/Crash/UserHandler.cpp | 398 + Kernel/Core/Crash/chfcts.hpp | 322 + Kernel/Core/Debugger.cpp | 155 + Kernel/Core/Disk.cpp | 169 + Kernel/Core/Driver/Driver.cpp | 343 + Kernel/Core/Driver/DriverAPI.cpp | 203 + .../Core/Driver/DriverBinding/BindInput.cpp | 73 + .../Driver/DriverBinding/BindInterrupt.cpp | 85 + Kernel/Core/Driver/DriverBinding/BindPCI.cpp | 201 + .../Core/Driver/DriverBinding/BindProcess.cpp | 42 + .../Core/Driver/DriverBinding/Input/Audio.cpp | 40 + .../Driver/DriverBinding/Input/Display.cpp | 40 + .../Driver/DriverBinding/Input/Filesystem.cpp | 40 + .../Driver/DriverBinding/Input/Generic.cpp | 40 + .../Core/Driver/DriverBinding/Input/Input.cpp | 67 + .../Driver/DriverBinding/Input/Network.cpp | 40 + .../Driver/DriverBinding/Input/Storage.cpp | 40 + .../Driver/DriverBinding/Interrupt/Audio.cpp | 81 + .../DriverBinding/Interrupt/Display.cpp | 81 + .../DriverBinding/Interrupt/Filesystem.cpp | 81 + .../DriverBinding/Interrupt/Generic.cpp | 88 + .../Driver/DriverBinding/Interrupt/Input.cpp | 124 + .../DriverBinding/Interrupt/Network.cpp | 81 + .../DriverBinding/Interrupt/Storage.cpp | 115 + .../Core/Driver/DriverBinding/PCI/Audio.cpp | 110 + .../Core/Driver/DriverBinding/PCI/Display.cpp | 82 + .../Driver/DriverBinding/PCI/Filesystem.cpp | 82 + .../Core/Driver/DriverBinding/PCI/Generic.cpp | 82 + .../Core/Driver/DriverBinding/PCI/Input.cpp | 82 + .../Core/Driver/DriverBinding/PCI/Network.cpp | 110 + .../Core/Driver/DriverBinding/PCI/Storage.cpp | 110 + .../Driver/DriverBinding/Process/Audio.cpp | 40 + .../Driver/DriverBinding/Process/Display.cpp | 40 + .../DriverBinding/Process/Filesystem.cpp | 40 + .../Driver/DriverBinding/Process/Generic.cpp | 40 + .../Driver/DriverBinding/Process/Input.cpp | 40 + .../Driver/DriverBinding/Process/Network.cpp | 40 + .../Driver/DriverBinding/Process/Storage.cpp | 40 + Kernel/Core/Driver/api.hpp | 27 + Kernel/Core/InterruptsManager.cpp | 235 + Kernel/Core/Lock.cpp | 207 + .../Memory/HeapAllocators/Xalloc/README.md | 137 + .../Memory/HeapAllocators/Xalloc/Wrapper.cpp | 40 + .../Memory/HeapAllocators/Xalloc/Xalloc.hpp | 119 + .../Memory/HeapAllocators/Xalloc/XallocV1.cpp | 290 + Kernel/Core/Memory/Memory.cpp | 818 ++ Kernel/Core/Memory/MemoryManager.cpp | 250 + Kernel/Core/Memory/PageMapIndexer.cpp | 45 + Kernel/Core/Memory/PhysicalMemoryManager.cpp | 541 + Kernel/Core/Memory/StackGuard.cpp | 110 + Kernel/Core/Memory/VirtualMemoryManager.cpp | 304 + .../Core/PeripheralComponentInterconnect.cpp | 886 ++ Kernel/Core/Power.cpp | 139 + Kernel/Core/README.md | 22 + Kernel/Core/Random.cpp | 143 + Kernel/Core/StackGuard.cpp | 96 + Kernel/Core/Symbols.cpp | 142 + Kernel/Core/SystemManagementBIOS.cpp | 85 + Kernel/Core/Time/HighPrecisionEventTimer.cpp | 89 + Kernel/Core/Time/Time.cpp | 105 + Kernel/Core/Time/TimeStampCounter.cpp | 81 + Kernel/Core/Time/Timer.cpp | 215 + Kernel/Core/UndefinedBehaviorSanitization.c | 610 ++ ...iversalAsynchronousReceiverTransmitter.cpp | 171 + Kernel/Core/Video/Display.cpp | 442 + Kernel/Core/Video/Font.cpp | 76 + Kernel/Core/crashhandler.hpp | 35 + Kernel/Core/smbios.hpp | 357 + Kernel/Core/ubsan.h | 111 + Kernel/DAPI.hpp | 431 + Kernel/Doxyfile | 2659 +++++ Kernel/Execute/Elf/BaseLoad.cpp | 287 + Kernel/Execute/Elf/Dyn.cpp | 44 + Kernel/Execute/Elf/Exec.cpp | 225 + Kernel/Execute/Elf/Parse.cpp | 310 + Kernel/Execute/Elf/Rel.cpp | 109 + Kernel/Execute/Elf/SharedObjects.cpp | 256 + Kernel/Execute/Fex/BaseLoad.cpp | 38 + Kernel/Execute/Parse.cpp | 102 + Kernel/Execute/Spawn.cpp | 109 + Kernel/Fex.hpp | 161 + Kernel/FileSystem/FS/ustar.cpp | 137 + Kernel/FileSystem/Filesystem.cpp | 644 ++ Kernel/Files/tamsyn-font-1.11/LICENSE | 10 + Kernel/Files/tamsyn-font-1.11/README | 82 + .../Files/tamsyn-font-1.11/Tamsyn10x20b.psf | Bin 0 -> 10823 bytes .../Files/tamsyn-font-1.11/Tamsyn10x20r.psf | Bin 0 -> 10823 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn5x9b.psf | Bin 0 -> 2887 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn5x9r.psf | Bin 0 -> 2887 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn6x12b.psf | Bin 0 -> 3655 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn6x12r.psf | Bin 0 -> 3655 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn7x13b.psf | Bin 0 -> 3911 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn7x13r.psf | Bin 0 -> 3911 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn7x14b.psf | Bin 0 -> 4167 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn7x14r.psf | Bin 0 -> 4165 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn8x15b.psf | Bin 0 -> 4423 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn8x15r.psf | Bin 0 -> 4421 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn8x16b.psf | Bin 0 -> 4679 bytes Kernel/Files/tamsyn-font-1.11/Tamsyn8x16r.psf | Bin 0 -> 4677 bytes Kernel/GUI/GUITools.cpp | 218 + Kernel/GUI/GraphicalUserInterface.cpp | 665 ++ Kernel/GUI/Icons.cpp | 253 + Kernel/GUI/WidgetEvents.cpp | 159 + Kernel/GUI/Widgets.cpp | 141 + Kernel/GUI/Window.cpp | 82 + Kernel/GUI/WindowEvents.cpp | 137 + Kernel/GUI/icons.hpp | 34 + Kernel/Kernel.cpp | 566 + Kernel/KernelConfig.cpp | 271 + Kernel/KernelThread.cpp | 473 + Kernel/LICENSE | 676 ++ Kernel/Library/Bitmap.cpp | 45 + Kernel/Library/Convert.cpp | 1081 ++ Kernel/Library/CyclicRedundancyCheck32.c | 96 + Kernel/Library/MemOp.c | 351 + Kernel/Library/MemoryCopySIMD.cpp | 195 + Kernel/Library/MemoryMoveSIMD.cpp | 60 + Kernel/Library/MemorySetSIMD.cpp | 81 + Kernel/Library/cargs.c | 512 + Kernel/Library/cwalk.c | 1469 +++ Kernel/Library/cxxabi.cpp | 294 + Kernel/Library/dumper.cpp | 91 + Kernel/Library/errno.cpp | 27 + Kernel/Library/liballoc_1_1.c | 793 ++ Kernel/Library/liballoc_1_1.h | 74 + Kernel/Library/liballocimpl.cpp | 14 + Kernel/Library/md5.c | 257 + Kernel/Library/printf.c | 1592 +++ Kernel/Library/targp.c | 47 + Kernel/Makefile | 196 + Kernel/Network/AddressResolutionProtocol.cpp | 245 + Kernel/Network/Checksum.cpp | 31 + Kernel/Network/DomainNameSystem.cpp | 35 + .../DynamicHostConfigurationProtocol.cpp | 186 + Kernel/Network/Ethernet.cpp | 148 + .../InternetControlMessageProtocol.cpp | 77 + Kernel/Network/InternetProtocol.cpp | 142 + Kernel/Network/NetworkController.cpp | 250 + Kernel/Network/NetworkDebugger.cpp | 100 + Kernel/Network/NetworkTimeProtocol.cpp | 75 + .../Network/TransmissionControlProtocol.cpp | 25 + Kernel/Network/UserDatagramProtocol.cpp | 145 + Kernel/Profiling/cyg.cpp | 112 + Kernel/Profiling/gcov.cpp | 88 + Kernel/Profiling/gprof.cpp | 38 + Kernel/README.md | 11 + Kernel/Recovery/RecoveryMain.cpp | 366 + Kernel/SystemCalls/Linux.cpp | 2458 +++++ Kernel/SystemCalls/Native.cpp | 466 + Kernel/SystemCalls/Syscalls.cpp | 65 + Kernel/Tasking/InterProcessCommunication.cpp | 259 + Kernel/Tasking/Scheduler.cpp | 727 ++ Kernel/Tasking/Security.cpp | 149 + Kernel/Tasking/Task.cpp | 874 ++ Kernel/Tests/CPUID.cpp | 155 + Kernel/Tests/Marco.cpp | 107 + Kernel/Tests/MemoryAllocation.cpp | 250 + Kernel/Tests/MemoryOperations.cpp | 138 + Kernel/Tests/RandomNumberGenerator.cpp | 68 + Kernel/Tests/String.cpp | 119 + Kernel/Tests/TypeSize.cpp | 123 + Kernel/Tests/std.cpp | 36 + Kernel/Tests/t.h | 28 + Kernel/dump.sh | 1 + Kernel/include/abi.h | 91 + Kernel/include/bitmap.hpp | 33 + Kernel/include/boot/binfo.h | 147 + Kernel/include/boot/protocols/multiboot2.h | 417 + Kernel/include/cargs.h | 187 + Kernel/include/convert.h | 133 + Kernel/include/cpu.hpp | 803 ++ Kernel/include/cpu/arm/.gitkeep | 0 Kernel/include/cpu/membar.hpp | 73 + Kernel/include/cpu/signatures.hpp | 108 + Kernel/include/cpu/x86/cpuid_amd.hpp | 2159 ++++ Kernel/include/cpu/x86/cpuid_intel.hpp | 1420 +++ Kernel/include/cpu/x86/exceptions.hpp | 54 + Kernel/include/cpu/x86/hypervisor.hpp | 80 + Kernel/include/cpu/x86/interrupts.hpp | 300 + Kernel/include/cpu/x86/x32/cr.hpp | 238 + Kernel/include/cpu/x86/x32/msr.hpp | 426 + Kernel/include/cpu/x86/x64/cr.hpp | 294 + Kernel/include/cpu/x86/x64/msr.hpp | 427 + Kernel/include/crc32.h | 25 + Kernel/include/cwalk.h | 521 + Kernel/include/debug.h | 106 + Kernel/include/disk.hpp | 182 + Kernel/include/display.hpp | 165 + Kernel/include/driver.hpp | 142 + Kernel/include/dumper.hpp | 25 + Kernel/include/elf.h | 494 + Kernel/include/exec.hpp | 148 + Kernel/include/filesystem.hpp | 182 + Kernel/include/filesystem/ext2.hpp | 91 + Kernel/include/filesystem/fat.hpp | 77 + Kernel/include/filesystem/initrd.hpp | 48 + Kernel/include/filesystem/mounts.hpp | 114 + Kernel/include/filesystem/ustar.hpp | 88 + Kernel/include/gui.hpp | 408 + Kernel/include/hashmap.hpp | 121 + Kernel/include/intrin.hpp | 80 + Kernel/include/ints.hpp | 78 + Kernel/include/io.h | 237 + Kernel/include/ipc.hpp | 92 + Kernel/include/kconfig.hpp | 40 + Kernel/include/lock.hpp | 159 + Kernel/include/md5.h | 54 + Kernel/include/memory.hpp | 898 ++ Kernel/include/msexec.h | 248 + Kernel/include/net/arp.hpp | 106 + Kernel/include/net/dhcp.hpp | 182 + Kernel/include/net/dns.hpp | 37 + Kernel/include/net/eth.hpp | 108 + Kernel/include/net/icmpv4.hpp | 71 + Kernel/include/net/icmpv6.hpp | 56 + Kernel/include/net/ipv4.hpp | 137 + Kernel/include/net/ipv6.hpp | 44 + Kernel/include/net/nc.hpp | 116 + Kernel/include/net/net.hpp | 232 + Kernel/include/net/ntp.hpp | 166 + Kernel/include/net/tcp.hpp | 27 + Kernel/include/net/udp.hpp | 97 + Kernel/include/pci.hpp | 240 + Kernel/include/power.hpp | 70 + Kernel/include/printf.h | 194 + Kernel/include/rand.hpp | 31 + Kernel/include/recovery.hpp | 41 + Kernel/include/smart_ptr.hpp | 19 + Kernel/include/smp.hpp | 81 + Kernel/include/stb/image.h | 9370 +++++++++++++++++ Kernel/include/stb/image_resize.h | 2946 ++++++ Kernel/include/symbols.hpp | 43 + Kernel/include/syscalls.hpp | 44 + Kernel/include/targp.h | 32 + Kernel/include/task.hpp | 356 + Kernel/include/time.hpp | 202 + Kernel/include/types.h | 372 + Kernel/include/uart.hpp | 88 + Kernel/include_std/assert.h | 50 + Kernel/include_std/atomic | 19 + Kernel/include_std/cstddef | 24 + Kernel/include_std/cstring | 19 + Kernel/include_std/dlfcn.h | 21 + Kernel/include_std/errno.h | 59 + Kernel/include_std/limits.h | 136 + Kernel/include_std/math.h | 21 + Kernel/include_std/pthread.h | 21 + Kernel/include_std/sched.h | 21 + Kernel/include_std/std.hpp | 77 + Kernel/include_std/std/atomic.hpp | 521 + Kernel/include_std/std/functional.hpp | 50 + Kernel/include_std/std/list.hpp | 224 + Kernel/include_std/std/smart_ptr.hpp | 292 + Kernel/include_std/std/stdexcept.hpp | 36 + Kernel/include_std/std/string.hpp | 553 + Kernel/include_std/std/unordered_map.hpp | 131 + Kernel/include_std/std/utility.hpp | 39 + Kernel/include_std/std/vector.hpp | 284 + Kernel/include_std/stddef.h | 23 + Kernel/include_std/stdint.h | 21 + Kernel/include_std/stdio.h | 21 + Kernel/include_std/stdlib.h | 21 + Kernel/include_std/string.h | 21 + Kernel/include_std/string.hpp | 19 + Kernel/include_std/strings.h | 21 + Kernel/include_std/sys/stat.h | 21 + Kernel/include_std/sys/time.h | 21 + Kernel/include_std/sys/types.h | 21 + Kernel/include_std/unistd.h | 21 + Kernel/include_std/vector | 19 + Kernel/include_std/wchar.h | 21 + Kernel/ipc.h | 109 + Kernel/kernel.h | 80 + Kernel/syscalls.h | 326 + 353 files changed, 77068 insertions(+) create mode 100644 Kernel/.github/workflows/flawfinder.yml create mode 100644 Kernel/.gitignore create mode 100644 Kernel/.vscode/c_boilerplates.code-snippets create mode 100644 Kernel/.vscode/c_cpp_properties.json create mode 100644 Kernel/.vscode/extensions.json create mode 100644 Kernel/.vscode/launch.json create mode 100644 Kernel/.vscode/settings.json create mode 100644 Kernel/Architecture/aarch64/Bootstrap/boot.S create mode 100644 Kernel/Architecture/aarch64/Entry.cpp create mode 100644 Kernel/Architecture/aarch64/SystemCalls.cpp create mode 100644 Kernel/Architecture/aarch64/cpu/SymmetricMultiprocessing.cpp create mode 100644 Kernel/Architecture/aarch64/linker.ld create mode 100644 Kernel/Architecture/aarch64/runtime/crt0.S create mode 100644 Kernel/Architecture/aarch64/runtime/crt1.S create mode 100644 Kernel/Architecture/aarch64/runtime/crtbegin.c create mode 100644 Kernel/Architecture/aarch64/runtime/crtend.c create mode 100644 Kernel/Architecture/aarch64/runtime/crti.S create mode 100644 Kernel/Architecture/aarch64/runtime/crtn.S create mode 100644 Kernel/Architecture/amd64/AdvancedConfigurationAndPowerInterface.cpp create mode 100644 Kernel/Architecture/amd64/Bootstrap/GDT32.asm create mode 100644 Kernel/Architecture/amd64/Bootstrap/Header_Multiboot.asm create mode 100644 Kernel/Architecture/amd64/Bootstrap/Header_Multiboot2.asm create mode 100644 Kernel/Architecture/amd64/Bootstrap/Limine.c create mode 100644 Kernel/Architecture/amd64/Bootstrap/Multiboot.cpp create mode 100644 Kernel/Architecture/amd64/Bootstrap/Multiboot64bitMap.cpp create mode 100644 Kernel/Architecture/amd64/Bootstrap/Multiboot_Init.asm create mode 100644 Kernel/Architecture/amd64/Bootstrap/Multiboot_PageTable.asm create mode 100644 Kernel/Architecture/amd64/DifferentiatedSystemDescriptionTable.cpp create mode 100644 Kernel/Architecture/amd64/MultipleAPICDescriptionTable.cpp create mode 100644 Kernel/Architecture/amd64/SystemCalls.cpp create mode 100644 Kernel/Architecture/amd64/acpi.hpp create mode 100644 Kernel/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp create mode 100644 Kernel/Architecture/amd64/cpu/GlobalDescriptorTable.cpp create mode 100644 Kernel/Architecture/amd64/cpu/InterruptDescriptorTable.cpp create mode 100644 Kernel/Architecture/amd64/cpu/SMPTrampoline.asm create mode 100644 Kernel/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp create mode 100644 Kernel/Architecture/amd64/cpu/apic.hpp create mode 100644 Kernel/Architecture/amd64/cpu/gdt.hpp create mode 100644 Kernel/Architecture/amd64/cpu/idt.hpp create mode 100644 Kernel/Architecture/amd64/linker.ld create mode 100644 Kernel/Architecture/amd64/runtime/crt0.c create mode 100644 Kernel/Architecture/amd64/runtime/crt1.c create mode 100644 Kernel/Architecture/amd64/runtime/crtbegin.c create mode 100644 Kernel/Architecture/amd64/runtime/crtend.c create mode 100644 Kernel/Architecture/amd64/runtime/crti.S create mode 100644 Kernel/Architecture/amd64/runtime/crtn.S create mode 100644 Kernel/Architecture/amd64/rust-target.json create mode 100644 Kernel/Architecture/i386/ArithmeticOperations.c create mode 100644 Kernel/Architecture/i386/Bootstrap/Header_Multiboot.asm create mode 100644 Kernel/Architecture/i386/Bootstrap/Header_Multiboot2.asm create mode 100644 Kernel/Architecture/i386/Bootstrap/Multiboot.cpp create mode 100644 Kernel/Architecture/i386/Bootstrap/Multiboot_Init.asm create mode 100644 Kernel/Architecture/i386/Bootstrap/Multiboot_PageTable.asm create mode 100644 Kernel/Architecture/i386/Interrupts/8259PIC.cpp create mode 100644 Kernel/Architecture/i386/Interrupts/pic.hpp create mode 100644 Kernel/Architecture/i386/SystemCalls.cpp create mode 100644 Kernel/Architecture/i386/acpi.hpp create mode 100644 Kernel/Architecture/i386/cpu/GlobalDescriptorTable.cpp create mode 100644 Kernel/Architecture/i386/cpu/SymmetricMultiprocessing.cpp create mode 100644 Kernel/Architecture/i386/cpu/apic.hpp create mode 100644 Kernel/Architecture/i386/cpu/gdt.hpp create mode 100644 Kernel/Architecture/i386/cpu/idt.hpp create mode 100644 Kernel/Architecture/i386/linker.ld create mode 100644 Kernel/Architecture/i386/runtime/crt0.c create mode 100644 Kernel/Architecture/i386/runtime/crt1.c create mode 100644 Kernel/Architecture/i386/runtime/crtbegin.c create mode 100644 Kernel/Architecture/i386/runtime/crtend.c create mode 100644 Kernel/Architecture/i386/runtime/crti.S create mode 100644 Kernel/Architecture/i386/runtime/crtn.S create mode 100644 Kernel/Core/CPU.cpp create mode 100644 Kernel/Core/Crash/CrashDetails.cpp create mode 100644 Kernel/Core/Crash/CrashHandler.cpp create mode 100644 Kernel/Core/Crash/KBDrv.cpp create mode 100644 Kernel/Core/Crash/SFrame.cpp create mode 100644 Kernel/Core/Crash/Screens/Console.cpp create mode 100644 Kernel/Core/Crash/Screens/Details.cpp create mode 100644 Kernel/Core/Crash/Screens/Main.cpp create mode 100644 Kernel/Core/Crash/Screens/StackFrame.cpp create mode 100644 Kernel/Core/Crash/Screens/Tasks.cpp create mode 100644 Kernel/Core/Crash/UserHandler.cpp create mode 100644 Kernel/Core/Crash/chfcts.hpp create mode 100644 Kernel/Core/Debugger.cpp create mode 100644 Kernel/Core/Disk.cpp create mode 100644 Kernel/Core/Driver/Driver.cpp create mode 100644 Kernel/Core/Driver/DriverAPI.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/BindInput.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/BindInterrupt.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/BindPCI.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/BindProcess.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Audio.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Display.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Filesystem.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Generic.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Input.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Network.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Input/Storage.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Audio.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Display.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Filesystem.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Generic.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Input.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Network.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Interrupt/Storage.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Audio.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Display.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Filesystem.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Generic.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Input.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Network.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/PCI/Storage.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Audio.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Display.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Filesystem.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Generic.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Input.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Network.cpp create mode 100644 Kernel/Core/Driver/DriverBinding/Process/Storage.cpp create mode 100644 Kernel/Core/Driver/api.hpp create mode 100644 Kernel/Core/InterruptsManager.cpp create mode 100644 Kernel/Core/Lock.cpp create mode 100644 Kernel/Core/Memory/HeapAllocators/Xalloc/README.md create mode 100644 Kernel/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp create mode 100644 Kernel/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp create mode 100644 Kernel/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp create mode 100644 Kernel/Core/Memory/Memory.cpp create mode 100644 Kernel/Core/Memory/MemoryManager.cpp create mode 100644 Kernel/Core/Memory/PageMapIndexer.cpp create mode 100644 Kernel/Core/Memory/PhysicalMemoryManager.cpp create mode 100644 Kernel/Core/Memory/StackGuard.cpp create mode 100644 Kernel/Core/Memory/VirtualMemoryManager.cpp create mode 100644 Kernel/Core/PeripheralComponentInterconnect.cpp create mode 100644 Kernel/Core/Power.cpp create mode 100644 Kernel/Core/README.md create mode 100644 Kernel/Core/Random.cpp create mode 100644 Kernel/Core/StackGuard.cpp create mode 100644 Kernel/Core/Symbols.cpp create mode 100644 Kernel/Core/SystemManagementBIOS.cpp create mode 100644 Kernel/Core/Time/HighPrecisionEventTimer.cpp create mode 100644 Kernel/Core/Time/Time.cpp create mode 100644 Kernel/Core/Time/TimeStampCounter.cpp create mode 100644 Kernel/Core/Time/Timer.cpp create mode 100644 Kernel/Core/UndefinedBehaviorSanitization.c create mode 100644 Kernel/Core/UniversalAsynchronousReceiverTransmitter.cpp create mode 100644 Kernel/Core/Video/Display.cpp create mode 100644 Kernel/Core/Video/Font.cpp create mode 100644 Kernel/Core/crashhandler.hpp create mode 100644 Kernel/Core/smbios.hpp create mode 100644 Kernel/Core/ubsan.h create mode 100644 Kernel/DAPI.hpp create mode 100644 Kernel/Doxyfile create mode 100644 Kernel/Execute/Elf/BaseLoad.cpp create mode 100644 Kernel/Execute/Elf/Dyn.cpp create mode 100644 Kernel/Execute/Elf/Exec.cpp create mode 100644 Kernel/Execute/Elf/Parse.cpp create mode 100644 Kernel/Execute/Elf/Rel.cpp create mode 100644 Kernel/Execute/Elf/SharedObjects.cpp create mode 100644 Kernel/Execute/Fex/BaseLoad.cpp create mode 100644 Kernel/Execute/Parse.cpp create mode 100644 Kernel/Execute/Spawn.cpp create mode 100644 Kernel/Fex.hpp create mode 100644 Kernel/FileSystem/FS/ustar.cpp create mode 100644 Kernel/FileSystem/Filesystem.cpp create mode 100644 Kernel/Files/tamsyn-font-1.11/LICENSE create mode 100644 Kernel/Files/tamsyn-font-1.11/README create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn10x20b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn10x20r.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn5x9b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn5x9r.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn6x12b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn6x12r.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn7x13b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn7x13r.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn7x14b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn7x14r.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn8x15b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn8x15r.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn8x16b.psf create mode 100644 Kernel/Files/tamsyn-font-1.11/Tamsyn8x16r.psf create mode 100644 Kernel/GUI/GUITools.cpp create mode 100644 Kernel/GUI/GraphicalUserInterface.cpp create mode 100644 Kernel/GUI/Icons.cpp create mode 100644 Kernel/GUI/WidgetEvents.cpp create mode 100644 Kernel/GUI/Widgets.cpp create mode 100644 Kernel/GUI/Window.cpp create mode 100644 Kernel/GUI/WindowEvents.cpp create mode 100644 Kernel/GUI/icons.hpp create mode 100644 Kernel/Kernel.cpp create mode 100644 Kernel/KernelConfig.cpp create mode 100644 Kernel/KernelThread.cpp create mode 100644 Kernel/LICENSE create mode 100644 Kernel/Library/Bitmap.cpp create mode 100644 Kernel/Library/Convert.cpp create mode 100644 Kernel/Library/CyclicRedundancyCheck32.c create mode 100644 Kernel/Library/MemOp.c create mode 100644 Kernel/Library/MemoryCopySIMD.cpp create mode 100644 Kernel/Library/MemoryMoveSIMD.cpp create mode 100644 Kernel/Library/MemorySetSIMD.cpp create mode 100644 Kernel/Library/cargs.c create mode 100644 Kernel/Library/cwalk.c create mode 100644 Kernel/Library/cxxabi.cpp create mode 100644 Kernel/Library/dumper.cpp create mode 100644 Kernel/Library/errno.cpp create mode 100644 Kernel/Library/liballoc_1_1.c create mode 100644 Kernel/Library/liballoc_1_1.h create mode 100644 Kernel/Library/liballocimpl.cpp create mode 100644 Kernel/Library/md5.c create mode 100644 Kernel/Library/printf.c create mode 100644 Kernel/Library/targp.c create mode 100644 Kernel/Makefile create mode 100644 Kernel/Network/AddressResolutionProtocol.cpp create mode 100644 Kernel/Network/Checksum.cpp create mode 100644 Kernel/Network/DomainNameSystem.cpp create mode 100644 Kernel/Network/DynamicHostConfigurationProtocol.cpp create mode 100644 Kernel/Network/Ethernet.cpp create mode 100644 Kernel/Network/InternetControlMessageProtocol.cpp create mode 100644 Kernel/Network/InternetProtocol.cpp create mode 100644 Kernel/Network/NetworkController.cpp create mode 100644 Kernel/Network/NetworkDebugger.cpp create mode 100644 Kernel/Network/NetworkTimeProtocol.cpp create mode 100644 Kernel/Network/TransmissionControlProtocol.cpp create mode 100644 Kernel/Network/UserDatagramProtocol.cpp create mode 100644 Kernel/Profiling/cyg.cpp create mode 100644 Kernel/Profiling/gcov.cpp create mode 100644 Kernel/Profiling/gprof.cpp create mode 100644 Kernel/README.md create mode 100644 Kernel/Recovery/RecoveryMain.cpp create mode 100644 Kernel/SystemCalls/Linux.cpp create mode 100644 Kernel/SystemCalls/Native.cpp create mode 100644 Kernel/SystemCalls/Syscalls.cpp create mode 100644 Kernel/Tasking/InterProcessCommunication.cpp create mode 100644 Kernel/Tasking/Scheduler.cpp create mode 100644 Kernel/Tasking/Security.cpp create mode 100644 Kernel/Tasking/Task.cpp create mode 100644 Kernel/Tests/CPUID.cpp create mode 100644 Kernel/Tests/Marco.cpp create mode 100644 Kernel/Tests/MemoryAllocation.cpp create mode 100644 Kernel/Tests/MemoryOperations.cpp create mode 100644 Kernel/Tests/RandomNumberGenerator.cpp create mode 100644 Kernel/Tests/String.cpp create mode 100644 Kernel/Tests/TypeSize.cpp create mode 100644 Kernel/Tests/std.cpp create mode 100644 Kernel/Tests/t.h create mode 100755 Kernel/dump.sh create mode 100644 Kernel/include/abi.h create mode 100644 Kernel/include/bitmap.hpp create mode 100644 Kernel/include/boot/binfo.h create mode 100644 Kernel/include/boot/protocols/multiboot2.h create mode 100644 Kernel/include/cargs.h create mode 100644 Kernel/include/convert.h create mode 100644 Kernel/include/cpu.hpp create mode 100644 Kernel/include/cpu/arm/.gitkeep create mode 100644 Kernel/include/cpu/membar.hpp create mode 100644 Kernel/include/cpu/signatures.hpp create mode 100644 Kernel/include/cpu/x86/cpuid_amd.hpp create mode 100644 Kernel/include/cpu/x86/cpuid_intel.hpp create mode 100644 Kernel/include/cpu/x86/exceptions.hpp create mode 100644 Kernel/include/cpu/x86/hypervisor.hpp create mode 100644 Kernel/include/cpu/x86/interrupts.hpp create mode 100644 Kernel/include/cpu/x86/x32/cr.hpp create mode 100644 Kernel/include/cpu/x86/x32/msr.hpp create mode 100644 Kernel/include/cpu/x86/x64/cr.hpp create mode 100644 Kernel/include/cpu/x86/x64/msr.hpp create mode 100644 Kernel/include/crc32.h create mode 100644 Kernel/include/cwalk.h create mode 100644 Kernel/include/debug.h create mode 100644 Kernel/include/disk.hpp create mode 100644 Kernel/include/display.hpp create mode 100644 Kernel/include/driver.hpp create mode 100644 Kernel/include/dumper.hpp create mode 100644 Kernel/include/elf.h create mode 100644 Kernel/include/exec.hpp create mode 100644 Kernel/include/filesystem.hpp create mode 100644 Kernel/include/filesystem/ext2.hpp create mode 100644 Kernel/include/filesystem/fat.hpp create mode 100644 Kernel/include/filesystem/initrd.hpp create mode 100644 Kernel/include/filesystem/mounts.hpp create mode 100644 Kernel/include/filesystem/ustar.hpp create mode 100644 Kernel/include/gui.hpp create mode 100644 Kernel/include/hashmap.hpp create mode 100644 Kernel/include/intrin.hpp create mode 100644 Kernel/include/ints.hpp create mode 100644 Kernel/include/io.h create mode 100644 Kernel/include/ipc.hpp create mode 100644 Kernel/include/kconfig.hpp create mode 100644 Kernel/include/lock.hpp create mode 100644 Kernel/include/md5.h create mode 100644 Kernel/include/memory.hpp create mode 100644 Kernel/include/msexec.h create mode 100644 Kernel/include/net/arp.hpp create mode 100644 Kernel/include/net/dhcp.hpp create mode 100644 Kernel/include/net/dns.hpp create mode 100644 Kernel/include/net/eth.hpp create mode 100644 Kernel/include/net/icmpv4.hpp create mode 100644 Kernel/include/net/icmpv6.hpp create mode 100644 Kernel/include/net/ipv4.hpp create mode 100644 Kernel/include/net/ipv6.hpp create mode 100644 Kernel/include/net/nc.hpp create mode 100644 Kernel/include/net/net.hpp create mode 100644 Kernel/include/net/ntp.hpp create mode 100644 Kernel/include/net/tcp.hpp create mode 100644 Kernel/include/net/udp.hpp create mode 100644 Kernel/include/pci.hpp create mode 100644 Kernel/include/power.hpp create mode 100644 Kernel/include/printf.h create mode 100644 Kernel/include/rand.hpp create mode 100644 Kernel/include/recovery.hpp create mode 100644 Kernel/include/smart_ptr.hpp create mode 100644 Kernel/include/smp.hpp create mode 100644 Kernel/include/stb/image.h create mode 100644 Kernel/include/stb/image_resize.h create mode 100644 Kernel/include/symbols.hpp create mode 100644 Kernel/include/syscalls.hpp create mode 100644 Kernel/include/targp.h create mode 100644 Kernel/include/task.hpp create mode 100644 Kernel/include/time.hpp create mode 100644 Kernel/include/types.h create mode 100644 Kernel/include/uart.hpp create mode 100644 Kernel/include_std/assert.h create mode 100644 Kernel/include_std/atomic create mode 100644 Kernel/include_std/cstddef create mode 100644 Kernel/include_std/cstring create mode 100644 Kernel/include_std/dlfcn.h create mode 100644 Kernel/include_std/errno.h create mode 100644 Kernel/include_std/limits.h create mode 100644 Kernel/include_std/math.h create mode 100644 Kernel/include_std/pthread.h create mode 100644 Kernel/include_std/sched.h create mode 100644 Kernel/include_std/std.hpp create mode 100644 Kernel/include_std/std/atomic.hpp create mode 100644 Kernel/include_std/std/functional.hpp create mode 100644 Kernel/include_std/std/list.hpp create mode 100644 Kernel/include_std/std/smart_ptr.hpp create mode 100644 Kernel/include_std/std/stdexcept.hpp create mode 100644 Kernel/include_std/std/string.hpp create mode 100644 Kernel/include_std/std/unordered_map.hpp create mode 100644 Kernel/include_std/std/utility.hpp create mode 100644 Kernel/include_std/std/vector.hpp create mode 100644 Kernel/include_std/stddef.h create mode 100644 Kernel/include_std/stdint.h create mode 100644 Kernel/include_std/stdio.h create mode 100644 Kernel/include_std/stdlib.h create mode 100644 Kernel/include_std/string.h create mode 100644 Kernel/include_std/string.hpp create mode 100644 Kernel/include_std/strings.h create mode 100644 Kernel/include_std/sys/stat.h create mode 100644 Kernel/include_std/sys/time.h create mode 100644 Kernel/include_std/sys/types.h create mode 100644 Kernel/include_std/unistd.h create mode 100644 Kernel/include_std/vector create mode 100644 Kernel/include_std/wchar.h create mode 100644 Kernel/ipc.h create mode 100644 Kernel/kernel.h create mode 100644 Kernel/syscalls.h diff --git a/Kernel/.github/workflows/flawfinder.yml b/Kernel/.github/workflows/flawfinder.yml new file mode 100644 index 00000000..9aab577d --- /dev/null +++ b/Kernel/.github/workflows/flawfinder.yml @@ -0,0 +1,38 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: flawfinder + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '21 1 * * 1' + +jobs: + flawfinder: + name: Flawfinder + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: flawfinder_scan + uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c + with: + arguments: '--sarif ./' + output: 'flawfinder_results.sarif' + + - name: Upload analysis results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: ${{github.workspace}}/flawfinder_results.sarif diff --git a/Kernel/.gitignore b/Kernel/.gitignore new file mode 100644 index 00000000..7379e127 --- /dev/null +++ b/Kernel/.gitignore @@ -0,0 +1,7 @@ +*.o +*.su +*.gcno +*.map +*.fsys +*.log +.dccache diff --git a/Kernel/.vscode/c_boilerplates.code-snippets b/Kernel/.vscode/c_boilerplates.code-snippets new file mode 100644 index 00000000..c91ee465 --- /dev/null +++ b/Kernel/.vscode/c_boilerplates.code-snippets @@ -0,0 +1,69 @@ +{ + "Fennix Kernel Header": { + "prefix": [ + "head", + ], + "body": [ + "/*", + " This file is part of Fennix Kernel.", + "", + " Fennix Kernel is free software: you can redistribute it and/or", + " modify it under the terms of the GNU General Public License as", + " published by the Free Software Foundation, either version 3 of", + " the License, or (at your option) any later version.", + "", + " Fennix Kernel is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", + " GNU General Public License for more details.", + "", + " You should have received a copy of the GNU General Public License", + " along with Fennix Kernel. If not, see .", + "*/", + "", + "#ifndef __FENNIX_KERNEL_${2:header}_H__", + "#define __FENNIX_KERNEL_${2:header}_H__", + "", + "#include ", + "", + "$0", + "", + "#endif // !__FENNIX_KERNEL_${2:header}_H__", + "" + ], + "description": "Create kernel header." + }, + "Fennix Kernel brief": { + "prefix": [ + "brief", + ], + "body": [ + "/** @brief $0 */" + ], + "description": "Create kernel documentation brief." + }, + "License": { + "prefix": [ + "license", + ], + "body": [ + "/*", + " This file is part of Fennix Kernel.", + "", + " Fennix Kernel is free software: you can redistribute it and/or", + " modify it under the terms of the GNU General Public License as", + " published by the Free Software Foundation, either version 3 of", + " the License, or (at your option) any later version.", + "", + " Fennix Kernel is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", + " GNU General Public License for more details.", + "", + " You should have received a copy of the GNU General Public License", + " along with Fennix Kernel. If not, see .", + "*/" + ], + "description": "Create kernel license." + } +} diff --git a/Kernel/.vscode/c_cpp_properties.json b/Kernel/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..ffff0894 --- /dev/null +++ b/Kernel/.vscode/c_cpp_properties.json @@ -0,0 +1,219 @@ +{ + "configurations": [ + { + "name": "Fennix x64 (Linux, GCC, debug)", + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/include/**", + "${workspaceFolder}/include_std", + "${workspaceFolder}/include_std/**" + ], + "defines": [ + "__debug_vscode__", + "KERNEL_NAME=\"Fennix\"", + "KERNEL_VERSION=\"1.0\"", + "GIT_COMMIT=\"0000000000000000000000000000000000000000\"", + "GIT_COMMIT_SHORT=\"0000000\"", + "a64", + "a86", + "DEBUG=\"1\"" + ], + "compilerPath": "${workspaceFolder}/../tools/cross/bin/amd64-elf-gcc", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "gcc-x64", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + // Compiler flags + "-fno-pic", + "-fno-pie", + "-mno-red-zone", + "-march=core2", + "-pipe", + "-mcmodel=kernel", + "-fno-builtin", + + // Warnings + "-Wall", + "-Wextra", + "-Wfloat-equal", + "-Wpointer-arith", + "-Wcast-align", + "-Wredundant-decls", + "-Winit-self", + "-Wswitch-default", + "-Wstrict-overflow=5", + "-Wconversion", + + // C++ flags + "-fno-rtti", + "-fexceptions", + + // Linker flags + "-T${workspaceFolder}/Architecture/amd64/linker.ld", + "-Wl,-static,--no-dynamic-linker,-ztext", + "-nostdlib", + "-nodefaultlibs", + "-nolibc", + "-zmax-page-size=0x1000", + "-shared", + + // Debug flags + "-ggdb3", + "-O0", + "-fdiagnostics-color=always", + "-fverbose-asm", + "-fstack-usage", + "-fstack-check", + "-fsanitize=undefined", + + // VSCode flags + "-ffreestanding", + "-nostdinc", + "-nostdinc++" + ] + }, + { + "name": "Fennix x32 (Linux, GCC, debug)", + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/include/**", + "${workspaceFolder}/include_std", + "${workspaceFolder}/include_std/**" + ], + "defines": [ + "__debug_vscode__", + "KERNEL_NAME=\"Fennix\"", + "KERNEL_VERSION=\"1.0\"", + "GIT_COMMIT=\"0000000000000000000000000000000000000000\"", + "GIT_COMMIT_SHORT=\"0000000\"", + "a32", + "a86", + "DEBUG=\"1\"" + ], + "compilerPath": "${workspaceFolder}/../tools/cross/bin/i386-elf-gcc", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "gcc-x86", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + // Compiler flags + "-fno-pic", + "-fno-pie", + "-mno-80387", + "-mno-mmx", + "-mno-3dnow", + "-mno-red-zone", + "-march=pentium", + "-pipe", + "-msoft-float", + "-fno-builtin", + + // Warnings + "-Wall", + "-Wextra", + "-Wfloat-equal", + "-Wpointer-arith", + "-Wcast-align", + "-Wredundant-decls", + "-Winit-self", + "-Wswitch-default", + "-Wstrict-overflow=5", + "-Wconversion", + + // C++ flags + "-fno-rtti", + "-fexceptions", + + // Linker flags + "-T${workspaceFolder}/Architecture/i386/linker.ld", + "-Wl,-static,--no-dynamic-linker,-ztext", + "-nostdlib", + "-nodefaultlibs", + "-nolibc", + "-zmax-page-size=0x1000", + "-shared", + + // Debug flags + "-ggdb3", + "-O0", + "-fdiagnostics-color=always", + "-fverbose-asm", + "-fstack-usage", + "-fstack-check", + "-fsanitize=undefined", + + // VSCode flags + "-ffreestanding", + "-nostdinc", + "-nostdinc++" + ] + }, + { + "name": "Fennix Aarch64 (Linux, GCC, debug)", + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/include/**", + "${workspaceFolder}/include_std", + "${workspaceFolder}/include_std/**" + ], + "defines": [ + "__debug_vscode__", + "KERNEL_NAME=\"Fennix\"", + "KERNEL_VERSION=\"1.0\"", + "GIT_COMMIT=\"0000000000000000000000000000000000000000\"", + "GIT_COMMIT_SHORT=\"0000000\"", + "aa64", + "DEBUG=\"1\"" + ], + "compilerPath": "${workspaceFolder}/../tools/cross/bin/aarch64-elf-gcc", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "linux-gcc-arm64", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + // Compiler flags + "-pipe", + "-fno-builtin", + "-msoft-float", + "-fPIC", + "-Wstack-protector", + + // Warnings + "-Wall", + "-Wextra", + "-Wfloat-equal", + "-Wpointer-arith", + "-Wcast-align", + "-Wredundant-decls", + "-Winit-self", + "-Wswitch-default", + "-Wstrict-overflow=5", + "-Wconversion", + + // C++ flags + "-fno-rtti", + "-fexceptions", + + // Linker flags + "-T${workspaceFolder}/Architecture/aarch64/linker.ld", + "-fPIC", + + // Debug flags + "-ggdb3", + "-O0", + "-fdiagnostics-color=always", + "-fverbose-asm", + "-fstack-usage", + "-fstack-check", + "-fsanitize=undefined", + + // VSCode flags + "-ffreestanding", + "-nostdinc", + "-nostdinc++" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/Kernel/.vscode/extensions.json b/Kernel/.vscode/extensions.json new file mode 100644 index 00000000..42ea9d06 --- /dev/null +++ b/Kernel/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + "recommendations": [ + "ms-vscode.cpptools", + "wayou.vscode-todo-highlight", + "gruntfuggly.todo-tree", + "13xforever.language-x86-64-assembly", + "webfreak.debug", + "zixuanwang.linkerscript", + "maziac.hex-hover-converter", + "cschlosser.doxdocgen", + "streetsidesoftware.code-spell-checker" + ] +} \ No newline at end of file diff --git a/Kernel/.vscode/launch.json b/Kernel/.vscode/launch.json new file mode 100644 index 00000000..80f2cb16 --- /dev/null +++ b/Kernel/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to a running VM instance", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/kernel.fsys", + "cwd": "${workspaceRoot}", + "args": [], + "targetArchitecture": "x64", + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "miDebuggerArgs": "", + "externalConsole": false, + "additionalSOLibSearchPath": "${workspaceRoot}", + "customLaunchSetupCommands": [ + { + "text": "target remote localhost:1234", + "description": "Connect to QEMU remote debugger" + } + ], + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "text": "set breakpoint pending on", + "description": "Make breakpoint pending on future shared library load." + }, + { + "text": "file ${workspaceRoot}/kernel.fsys", + "description": "Load binary." + }, + ] + } + ] +} \ No newline at end of file diff --git a/Kernel/.vscode/settings.json b/Kernel/.vscode/settings.json new file mode 100644 index 00000000..2d09fa10 --- /dev/null +++ b/Kernel/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "C_Cpp.errorSquiggles": "Enabled", + "C_Cpp.autocompleteAddParentheses": true, + "C_Cpp.codeAnalysis.clangTidy.enabled": true, + "C_Cpp.clang_format_style": "Visual Studio", + "C_Cpp.default.intelliSenseMode": "gcc-x64", + "C_Cpp.default.cStandard": "c17", + "C_Cpp.default.cppStandard": "c++20", + "C_Cpp.intelliSenseMemoryLimit": 16384, + "editor.smoothScrolling": true, + "editor.cursorSmoothCaretAnimation": "on", + "C_Cpp.codeAnalysis.clangTidy.checks.disabled": [ + "clang-analyzer-security.insecureAPI.strcpy", + "clang-diagnostic-unknown-warning-option", + "clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling", + "clang-diagnostic-implicit-exception-spec-mismatch", + "clang-diagnostic-unknown-attributes" + ] +} \ No newline at end of file diff --git a/Kernel/Architecture/aarch64/Bootstrap/boot.S b/Kernel/Architecture/aarch64/Bootstrap/boot.S new file mode 100644 index 00000000..45ec22f1 --- /dev/null +++ b/Kernel/Architecture/aarch64/Bootstrap/boot.S @@ -0,0 +1,36 @@ +/* Based on this tutorial: + https://github.com/s-matyukevich/raspberry-pi-os */ + +.section ".text.boot" + +.extern _bss_start +.extern _bss_end + +.extern arm64Entry +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret + +.global _start +_start: + mrs x0, mpidr_el1 + and x0, x0, #0xFF + cbz x0, _start2 + b CPU_Loop + +_start2: + adr x0, _bss_start + adr x1, _bss_end + sub x1, x1, x0 + bl memzero + mov sp, #0x200000 + bl arm64Entry + +Halt: + wfe + b Halt + +CPU_Loop: + b CPU_Loop diff --git a/Kernel/Architecture/aarch64/Entry.cpp b/Kernel/Architecture/aarch64/Entry.cpp new file mode 100644 index 00000000..857cea09 --- /dev/null +++ b/Kernel/Architecture/aarch64/Entry.cpp @@ -0,0 +1,27 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +EXTERNC void arm64Entry(uint64_t dtb_ptr32, uint64_t x1, uint64_t x2, uint64_t x3) +{ + trace("Hello, World!"); + CPU::Halt(true); +} diff --git a/Kernel/Architecture/aarch64/SystemCalls.cpp b/Kernel/Architecture/aarch64/SystemCalls.cpp new file mode 100644 index 00000000..aea86378 --- /dev/null +++ b/Kernel/Architecture/aarch64/SystemCalls.cpp @@ -0,0 +1,31 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include + +extern "C" __naked __used __no_stack_protector void SystemCallHandlerStub() +{ + +} + +extern "C" uint64_t SystemCallsHandler(SyscallsFrame *regs); + +void InitializeSystemCalls() +{ +} diff --git a/Kernel/Architecture/aarch64/cpu/SymmetricMultiprocessing.cpp b/Kernel/Architecture/aarch64/cpu/SymmetricMultiprocessing.cpp new file mode 100644 index 00000000..f6c89b95 --- /dev/null +++ b/Kernel/Architecture/aarch64/cpu/SymmetricMultiprocessing.cpp @@ -0,0 +1,59 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include + +#include "../../../kernel.h" + +volatile bool CPUEnabled = false; + +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +static __aligned(0x1000) CPUData CPUs[MAX_CPU] = {0}; + +CPUData *GetCPU(uint64_t id) { return &CPUs[id]; } + +CPUData *GetCurrentCPU() +{ + uint64_t ret = 0; + + if (!CPUs[ret].IsActive) + { + error("CPU %d is not active!", ret); + return &CPUs[0]; + } + + if (CPUs[ret].Checksum != CPU_DATA_CHECKSUM) + { + error("CPU %d data is corrupted!", ret); + return &CPUs[0]; + } + return &CPUs[ret]; +} + +namespace SMP +{ + int CPUCores = 0; + + void Initialize(void *madt) + { + fixme("SMP::Initialize() is not implemented!"); + } +} diff --git a/Kernel/Architecture/aarch64/linker.ld b/Kernel/Architecture/aarch64/linker.ld new file mode 100644 index 00000000..bb8c142e --- /dev/null +++ b/Kernel/Architecture/aarch64/linker.ld @@ -0,0 +1,83 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +ENTRY(_start) + +SECTIONS +{ + .text.boot : + { + *(.text.boot) + . += CONSTANT(MAXPAGESIZE); + _bss_start = .; + *(.text.bss) + _bss_end = .; + } + + _kernel_start = .; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + . = ALIGN(4096); + _kernel_text_end = .; + + .data : + { + *(.data .data.*) + } + . = ALIGN(4096); + _kernel_data_end = .; + + .rodata : + { + *(.rodata .rodata.*) + } + . = ALIGN(4096); + _kernel_rodata_end = .; + + .init_array : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(.init_array .ctors)) + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP(*(.fini_array .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .bss : + { + *(.bss .bss.*) + } + . = ALIGN(4096); + _kernel_end = .; + _bss_size = _kernel_end - _kernel_rodata_end; + + /DISCARD/ : + { + *(.comment*) + *(.note*) + } +} diff --git a/Kernel/Architecture/aarch64/runtime/crt0.S b/Kernel/Architecture/aarch64/runtime/crt0.S new file mode 100644 index 00000000..ec4b0f0c --- /dev/null +++ b/Kernel/Architecture/aarch64/runtime/crt0.S @@ -0,0 +1,17 @@ +// .section ".text.boot" +// +// .global _start +// .org 0x80000 +// _start: +// ldr x5, =_start +// mov sp, x5 +// ldr x5, =_kernel_rodata_end +// ldr w6, =_bss_size +// 1: cbz w6, 2f +// str xzr, [x5], #8 +// sub w6, w6, #1 +// cbnz w6, 1b +// 2: bl arm64Entry +// Halt: +// wfe +// b Halt diff --git a/Kernel/Architecture/aarch64/runtime/crt1.S b/Kernel/Architecture/aarch64/runtime/crt1.S new file mode 100644 index 00000000..d25abc91 --- /dev/null +++ b/Kernel/Architecture/aarch64/runtime/crt1.S @@ -0,0 +1,17 @@ +// .section ".text.boot" + +// .global _start +// .org 0x80000 +// _start: +// ldr x5, =_start +// mov sp, x5 +// ldr x5, =_kernel_rodata_end +// ldr w6, =_bss_size +// 1: cbz w6, 2f +// str xzr, [x5], #8 +// sub w6, w6, #1 +// cbnz w6, 1b +// 2: bl arm64Entry +// Halt: +// wfe +// b Halt diff --git a/Kernel/Architecture/aarch64/runtime/crtbegin.c b/Kernel/Architecture/aarch64/runtime/crtbegin.c new file mode 100644 index 00000000..507125b3 --- /dev/null +++ b/Kernel/Architecture/aarch64/runtime/crtbegin.c @@ -0,0 +1 @@ +// C++ constructor/destructor stuff \ No newline at end of file diff --git a/Kernel/Architecture/aarch64/runtime/crtend.c b/Kernel/Architecture/aarch64/runtime/crtend.c new file mode 100644 index 00000000..507125b3 --- /dev/null +++ b/Kernel/Architecture/aarch64/runtime/crtend.c @@ -0,0 +1 @@ +// C++ constructor/destructor stuff \ No newline at end of file diff --git a/Kernel/Architecture/aarch64/runtime/crti.S b/Kernel/Architecture/aarch64/runtime/crti.S new file mode 100644 index 00000000..d3886db5 --- /dev/null +++ b/Kernel/Architecture/aarch64/runtime/crti.S @@ -0,0 +1,13 @@ +// .section .init +// .global _init +// .type _init, @function +// _init: +// push %rbp +// movq %rsp, %rbp + +// .section .fini +// .global _fini +// .type _fini, @function +// _fini: +// push %rbp +// movq %rsp, %rbp diff --git a/Kernel/Architecture/aarch64/runtime/crtn.S b/Kernel/Architecture/aarch64/runtime/crtn.S new file mode 100644 index 00000000..21c198b8 --- /dev/null +++ b/Kernel/Architecture/aarch64/runtime/crtn.S @@ -0,0 +1,7 @@ +.section .init +// popq %rbp + ret + +.section .fini +// popq %rbp + ret diff --git a/Kernel/Architecture/amd64/AdvancedConfigurationAndPowerInterface.cpp b/Kernel/Architecture/amd64/AdvancedConfigurationAndPowerInterface.cpp new file mode 100644 index 00000000..2d6499ac --- /dev/null +++ b/Kernel/Architecture/amd64/AdvancedConfigurationAndPowerInterface.cpp @@ -0,0 +1,155 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "acpi.hpp" + +#include +#include + +#include "../../kernel.h" + +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" + +namespace ACPI +{ + void *ACPI::FindTable(ACPI::ACPIHeader *ACPIHeader, char *Signature) + { + for (uint64_t t = 0; t < ((ACPIHeader->Length - sizeof(ACPI::ACPIHeader)) / (XSDTSupported ? 8 : 4)); t++) + { + // TODO: Should I be concerned about unaligned memory access? + ACPI::ACPIHeader *SDTHdr = nullptr; + if (XSDTSupported) + SDTHdr = (ACPI::ACPIHeader *)(*(uint64_t *)((uint64_t)ACPIHeader + sizeof(ACPI::ACPIHeader) + (t * 8))); + else + SDTHdr = (ACPI::ACPIHeader *)(*(uint32_t *)((uint64_t)ACPIHeader + sizeof(ACPI::ACPIHeader) + (t * 4))); + + for (int i = 0; i < 4; i++) + { + if (SDTHdr->Signature[i] != Signature[i]) + break; + if (i == 3) + { + trace("%s found at address %p", Signature, (uintptr_t)SDTHdr); + return SDTHdr; + } + } + } + // warn("%s not found!", Signature); + return nullptr; + } + + void ACPI::SearchTables(ACPIHeader *Header) + { + if (!Header) + return; + + HPET = (HPETHeader *)FindTable(Header, (char *)"HPET"); + FADT = (FADTHeader *)FindTable(Header, (char *)"FACP"); + MCFG = (MCFGHeader *)FindTable(Header, (char *)"MCFG"); + BGRT = (BGRTHeader *)FindTable(Header, (char *)"BGRT"); + SRAT = (SRATHeader *)FindTable(Header, (char *)"SRAT"); + TPM2 = (TPM2Header *)FindTable(Header, (char *)"TPM2"); + TCPA = (TCPAHeader *)FindTable(Header, (char *)"TCPA"); + WAET = (WAETHeader *)FindTable(Header, (char *)"WAET"); + MADT = (MADTHeader *)FindTable(Header, (char *)"APIC"); + HEST = (HESTHeader *)FindTable(Header, (char *)"HEST"); + FindTable(Header, (char *)"BERT"); + FindTable(Header, (char *)"CPEP"); + FindTable(Header, (char *)"DSDT"); + FindTable(Header, (char *)"ECDT"); + FindTable(Header, (char *)"EINJ"); + FindTable(Header, (char *)"ERST"); + FindTable(Header, (char *)"FACS"); + FindTable(Header, (char *)"MSCT"); + FindTable(Header, (char *)"MPST"); + FindTable(Header, (char *)"OEMx"); + FindTable(Header, (char *)"PMTT"); + FindTable(Header, (char *)"PSDT"); + FindTable(Header, (char *)"RASF"); + FindTable(Header, (char *)"RSDT"); + FindTable(Header, (char *)"SBST"); + FindTable(Header, (char *)"SLIT"); + FindTable(Header, (char *)"SSDT"); + FindTable(Header, (char *)"XSDT"); + FindTable(Header, (char *)"DRTM"); + FindTable(Header, (char *)"FPDT"); + FindTable(Header, (char *)"GTDT"); + FindTable(Header, (char *)"PCCT"); + FindTable(Header, (char *)"S3PT"); + FindTable(Header, (char *)"MATR"); + FindTable(Header, (char *)"MSDM"); + FindTable(Header, (char *)"WPBT"); + FindTable(Header, (char *)"OSDT"); + FindTable(Header, (char *)"RSDP"); + FindTable(Header, (char *)"NFIT"); + FindTable(Header, (char *)"ASF!"); + FindTable(Header, (char *)"BOOT"); + FindTable(Header, (char *)"CSRT"); + FindTable(Header, (char *)"DBG2"); + FindTable(Header, (char *)"DBGP"); + FindTable(Header, (char *)"DMAR"); + FindTable(Header, (char *)"IBFT"); + FindTable(Header, (char *)"IORT"); + FindTable(Header, (char *)"IVRS"); + FindTable(Header, (char *)"LPIT"); + FindTable(Header, (char *)"MCHI"); + FindTable(Header, (char *)"MTMR"); + FindTable(Header, (char *)"SLIC"); + FindTable(Header, (char *)"SPCR"); + FindTable(Header, (char *)"SPMI"); + FindTable(Header, (char *)"UEFI"); + FindTable(Header, (char *)"VRTC"); + FindTable(Header, (char *)"WDAT"); + FindTable(Header, (char *)"WDDT"); + FindTable(Header, (char *)"WDRT"); + FindTable(Header, (char *)"ATKG"); + FindTable(Header, (char *)"GSCI"); + FindTable(Header, (char *)"IEIT"); + FindTable(Header, (char *)"HMAT"); + FindTable(Header, (char *)"CEDT"); + FindTable(Header, (char *)"AEST"); + } + + ACPI::ACPI() + { + trace("Initializing ACPI"); + if (bInfo.RSDP->Revision >= 2 && bInfo.RSDP->XSDTAddress) + { + debug("XSDT supported"); + XSDTSupported = true; + XSDT = (ACPIHeader *)(bInfo.RSDP->XSDTAddress); + } + else + { + debug("RSDT supported"); + XSDT = (ACPIHeader *)(uintptr_t)bInfo.RSDP->RSDTAddress; + } + + this->SearchTables(XSDT); + + if (FADT) + { + outb(s_cst(uint16_t, FADT->SMI_CommandPort), FADT->AcpiEnable); + while (!(inw(s_cst(uint16_t, FADT->PM1aControlBlock)) & 1)) + ; + } + } + + ACPI::~ACPI() + { + } +} diff --git a/Kernel/Architecture/amd64/Bootstrap/GDT32.asm b/Kernel/Architecture/amd64/Bootstrap/GDT32.asm new file mode 100644 index 00000000..e9fa9804 --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/GDT32.asm @@ -0,0 +1,47 @@ +[bits 32] +section .bootstrap.text + +align 32 +global gdtr +gdtr: + dw GDT32_END - GDT32 - 1 + dd GDT32 + +align 32 +GDT32: + dq 0x0 + + dw 0xffff + dw 0x0000 + db 0x00 + dw 0xcf9a + db 0x00 + + dw 0xffff + dw 0x0000 + db 0x00 + dw 0xcf92 + db 0x00 + + dw 0x0100 + dw 0x1000 + db 0x00 + dw 0x4092 + db 0x00 +GDT32_END: + +global LoadGDT32 +LoadGDT32: + lgdt [gdtr] + + jmp 0x8:ActivateGDT + ActivateGDT: + mov cx, 0x10 + mov ss, cx + mov ds, cx + mov es, cx + mov fs, cx + mov cx, 0x18 + mov gs, cx + + ret diff --git a/Kernel/Architecture/amd64/Bootstrap/Header_Multiboot.asm b/Kernel/Architecture/amd64/Bootstrap/Header_Multiboot.asm new file mode 100644 index 00000000..71a6efe8 --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Header_Multiboot.asm @@ -0,0 +1,5 @@ +section .multiboot +align 4 + dd 0x1BADB002 + dd 1 << 0 | 1 << 1 + dd -(0x1BADB002 + (1 << 0 | 1 << 1)) diff --git a/Kernel/Architecture/amd64/Bootstrap/Header_Multiboot2.asm b/Kernel/Architecture/amd64/Bootstrap/Header_Multiboot2.asm new file mode 100644 index 00000000..756dcb89 --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Header_Multiboot2.asm @@ -0,0 +1,64 @@ +; https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html +section .multiboot2 +align 4096 +HEADER_START: + dd 0xE85250D6 + dd 0 + dd (HEADER_END - HEADER_START) + dd 0x100000000 - (HEADER_END - HEADER_START) - 0 - 0xE85250D6 +align 8 +MB2_INFO_REQUEST_TAG_START: + dw 1 + dw 0 + dd MB2_INFO_REQUEST_TAG_END - MB2_INFO_REQUEST_TAG_START + dd 1 ; Command Line + dd 2 ; Boot Loader Name + dd 3 ; Module + dd 4 ; Basic Memory Information + dd 5 ; BIOS Boot Device + dd 6 ; Memory Map + dd 7 ; VBE + dd 8 ; Framebuffer + dd 9 ; ELF Sections + dd 10 ; APM Table + dd 11 ; EFI 32-bit System Table Pointer + dd 12 ; EFI 64-bit System Table Pointer + ; dd 13 ; SMBIOS + dd 14 ; ACPI Old + dd 15 ; ACPI New + dd 16 ; Network + dd 17 ; EFI Memory Map + dd 18 ; EFI Boot Services Notifier + dd 19 ; EFI 32-bit Image Handle Pointer + dd 20 ; EFI 64-bit Image Handle Pointer + dd 21 ; Load Base Address +MB2_INFO_REQUEST_TAG_END: +align 8 +MB2_FRAMEBUFFER_TAG_START: + dw 5 + dw 1 + dd MB2_FRAMEBUFFER_TAG_END - MB2_FRAMEBUFFER_TAG_START + dd 0 + dd 0 + dd 32 +MB2_FRAMEBUFFER_TAG_END: +align 8 +MB2_EGA_SUPPORT_TAG_START: + dw 4 + dw 0 + dd MB2_EGA_SUPPORT_TAG_END - MB2_EGA_SUPPORT_TAG_START + dd 1 ; https://www.gnu.org/software/grub/manual/multiboot2/html_node/Console-header-tags.html +MB2_EGA_SUPPORT_TAG_END: +align 8 +MB2_MODULE_ALIGN_TAG_START: + dw 6 + dw 0 + dd MB2_MODULE_ALIGN_TAG_END - MB2_MODULE_ALIGN_TAG_START +MB2_MODULE_ALIGN_TAG_END: +align 8 +MB2_TAG_START: + dw 0 + dw 0 + dd MB2_TAG_END - MB2_TAG_START +MB2_TAG_END: +HEADER_END: diff --git a/Kernel/Architecture/amd64/Bootstrap/Limine.c b/Kernel/Architecture/amd64/Bootstrap/Limine.c new file mode 100644 index 00000000..eceb740e --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Limine.c @@ -0,0 +1,346 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include +#include +#include + +#include "../../../../tools/limine/limine.h" +#include "../../../kernel.h" + +void InitLimine(); + +static volatile struct limine_entry_point_request EntryPointRequest = { + .id = LIMINE_ENTRY_POINT_REQUEST, + .revision = 0, + .response = NULL, + .entry = InitLimine}; +static volatile struct limine_bootloader_info_request BootloaderInfoRequest = { + .id = LIMINE_BOOTLOADER_INFO_REQUEST, + .revision = 0}; +static volatile struct limine_terminal_request TerminalRequest = { + .id = LIMINE_TERMINAL_REQUEST, + .revision = 0}; +static volatile struct limine_framebuffer_request FramebufferRequest = { + .id = LIMINE_FRAMEBUFFER_REQUEST, + .revision = 0}; +static volatile struct limine_memmap_request MemmapRequest = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 0}; +static volatile struct limine_kernel_address_request KernelAddressRequest = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST, + .revision = 0}; +static volatile struct limine_rsdp_request RsdpRequest = { + .id = LIMINE_RSDP_REQUEST, + .revision = 0}; +static volatile struct limine_kernel_file_request KernelFileRequest = { + .id = LIMINE_KERNEL_FILE_REQUEST, + .revision = 0}; +static volatile struct limine_module_request ModuleRequest = { + .id = LIMINE_MODULE_REQUEST, + .revision = 0}; +static volatile struct limine_smbios_request SmbiosRequest = { + .id = LIMINE_SMBIOS_REQUEST, + .revision = 0}; + +void *TempStackPtr = NULL; +__naked __used __no_stack_protector void InitLimine() +{ + asmv("mov %%rsp, %0" + : "=r"(TempStackPtr)); + + asmv("mov %0, %%rsp" + : + : "r"((uintptr_t)TempStackPtr - 0xFFFF800000000000)); + + asmv("mov $0, %rax\n" + "mov $0, %rbx\n" + "mov $0, %rcx\n" + "mov $0, %rdx\n" + "mov $0, %rsi\n" + "mov $0, %rdi\n" + "mov $0, %rbp\n" + "mov $0, %r8\n" + "mov $0, %r9\n" + "mov $0, %r10\n" + "mov $0, %r11\n" + "mov $0, %r12\n" + "mov $0, %r13\n" + "mov $0, %r14\n" + "mov $0, %r15"); + + asmv("jmp InitLimineAfterStack"); +} + +SafeFunction NIF void InitLimineAfterStack() +{ + struct BootInfo binfo = {}; + struct limine_bootloader_info_response *BootloaderInfoResponse = BootloaderInfoRequest.response; + info("Bootloader: %s %s", BootloaderInfoResponse->name, BootloaderInfoResponse->version); + + struct limine_terminal_response *TerminalResponse = TerminalRequest.response; + + if (TerminalResponse == NULL || TerminalResponse->terminal_count < 1) + { + warn("No terminal available."); + inf_loop asmv("hlt"); + } + TerminalResponse->write(TerminalResponse->terminals[0], "\033[37mPlease wait... ", 20); + + struct limine_framebuffer_response *FrameBufferResponse = FramebufferRequest.response; + struct limine_memmap_response *MemmapResponse = MemmapRequest.response; + struct limine_kernel_address_response *KernelAddressResponse = KernelAddressRequest.response; + struct limine_rsdp_response *RsdpResponse = RsdpRequest.response; + struct limine_kernel_file_response *KernelFileResponse = KernelFileRequest.response; + struct limine_module_response *ModuleResponse = ModuleRequest.response; + struct limine_smbios_response *SmbiosResponse = SmbiosRequest.response; + + if (FrameBufferResponse == NULL || FrameBufferResponse->framebuffer_count < 1) + { + error("No framebuffer available [%#lx;%ld]", FrameBufferResponse, + (FrameBufferResponse == NULL) ? 0 : FrameBufferResponse->framebuffer_count); + + TerminalResponse->write(TerminalResponse->terminals[0], "No framebuffer available", 24); + inf_loop asmv("hlt"); + } + + if (MemmapResponse == NULL || MemmapResponse->entry_count < 1) + { + error("No memory map available [%#lx;%ld]", MemmapResponse, + (MemmapResponse == NULL) ? 0 : MemmapResponse->entry_count); + + TerminalResponse->write(TerminalResponse->terminals[0], "No memory map available", 23); + inf_loop asmv("hlt"); + } + + if (KernelAddressResponse == NULL) + { + error("No kernel address available [%#lx]", KernelAddressResponse); + + TerminalResponse->write(TerminalResponse->terminals[0], "No kernel address available", 27); + inf_loop asmv("hlt"); + } + + if (RsdpResponse == NULL || RsdpResponse->address == 0) + { + error("No RSDP address available [%#lx;%#lx]", RsdpResponse, + (RsdpResponse == NULL) ? 0 : RsdpResponse->address); + + TerminalResponse->write(TerminalResponse->terminals[0], "No RSDP address available", 25); + inf_loop asmv("hlt"); + } + + if (KernelFileResponse == NULL || KernelFileResponse->kernel_file == NULL) + { + error("No kernel file available [%#lx;%#lx]", KernelFileResponse, + (KernelFileResponse == NULL) ? 0 : KernelFileResponse->kernel_file); + + TerminalResponse->write(TerminalResponse->terminals[0], "No kernel file available", 24); + inf_loop asmv("hlt"); + } + + if (ModuleResponse == NULL || ModuleResponse->module_count < 1) + { + error("No module information available [%#lx;%ld]", ModuleResponse, + (ModuleResponse == NULL) ? 0 : ModuleResponse->module_count); + + TerminalResponse->write(TerminalResponse->terminals[0], "No module information available", 31); + inf_loop asmv("hlt"); + } + + /* Actual parsing starts here */ + + for (uint64_t i = 0; i < FrameBufferResponse->framebuffer_count; i++) + { + struct limine_framebuffer *framebuffer = FrameBufferResponse->framebuffers[i]; + switch (framebuffer->memory_model) + { + case LIMINE_FRAMEBUFFER_RGB: + binfo.Framebuffer[i].Type = RGB; + break; + default: + { + error("Unsupported framebuffer memory model %d", framebuffer->memory_model); + TerminalResponse->write(TerminalResponse->terminals[0], "Unsupported framebuffer memory model", 37); + inf_loop asmv("hlt"); + } + } + binfo.Framebuffer[i].BaseAddress = (void *)((uintptr_t)framebuffer->address - 0xFFFF800000000000); + binfo.Framebuffer[i].Width = (uint32_t)framebuffer->width; + binfo.Framebuffer[i].Height = (uint32_t)framebuffer->height; + binfo.Framebuffer[i].Pitch = (uint32_t)framebuffer->pitch; + binfo.Framebuffer[i].BitsPerPixel = framebuffer->bpp; + binfo.Framebuffer[i].RedMaskSize = framebuffer->red_mask_size; + binfo.Framebuffer[i].RedMaskShift = framebuffer->red_mask_shift; + binfo.Framebuffer[i].GreenMaskSize = framebuffer->green_mask_size; + binfo.Framebuffer[i].GreenMaskShift = framebuffer->green_mask_shift; + binfo.Framebuffer[i].BlueMaskSize = framebuffer->blue_mask_size; + binfo.Framebuffer[i].BlueMaskShift = framebuffer->blue_mask_shift; + binfo.Framebuffer[i].ExtendedDisplayIdentificationData = framebuffer->edid; + binfo.Framebuffer[i].EDIDSize = framebuffer->edid_size; + + debug("Framebuffer %d: %dx%d %d bpp", i, + binfo.Framebuffer[i].Width, + binfo.Framebuffer[i].Height, + binfo.Framebuffer[i].BitsPerPixel); + + debug("More info:\nAddress: %#lx\nPitch: %ld\nType: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d\nEDID: %#lx\nEDIDSize: %d", + binfo.Framebuffer[i].BaseAddress, + binfo.Framebuffer[i].Pitch, + binfo.Framebuffer[i].Type, + binfo.Framebuffer[i].RedMaskSize, + binfo.Framebuffer[i].RedMaskShift, + binfo.Framebuffer[i].GreenMaskSize, + binfo.Framebuffer[i].GreenMaskShift, + binfo.Framebuffer[i].BlueMaskSize, + binfo.Framebuffer[i].BlueMaskShift, + binfo.Framebuffer[i].ExtendedDisplayIdentificationData, + binfo.Framebuffer[i].EDIDSize); + } + + binfo.Memory.Entries = MemmapResponse->entry_count; + for (uint64_t i = 0; i < MemmapResponse->entry_count; i++) + { + if (MemmapResponse->entry_count > MAX_MEMORY_ENTRIES) + { + warn("Too many memory entries, skipping the rest..."); + break; + } + + struct limine_memmap_entry *entry = MemmapResponse->entries[i]; + if (!entry) + { + warn("Null memory entry %ld (%#lx), skipping...", i, entry); + continue; + } + + binfo.Memory.Size += entry->length; + switch (entry->type) + { + case LIMINE_MEMMAP_USABLE: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Usable; + break; + case LIMINE_MEMMAP_RESERVED: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Reserved; + break; + case LIMINE_MEMMAP_ACPI_RECLAIMABLE: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = ACPIReclaimable; + break; + case LIMINE_MEMMAP_ACPI_NVS: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = ACPINVS; + break; + case LIMINE_MEMMAP_BAD_MEMORY: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = BadMemory; + break; + case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = BootloaderReclaimable; + break; + case LIMINE_MEMMAP_KERNEL_AND_MODULES: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = KernelAndModules; + break; + case LIMINE_MEMMAP_FRAMEBUFFER: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Framebuffer; + break; + default: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Unknown; + break; + } + } + + for (uint64_t i = 0; i < ModuleResponse->module_count; i++) + { + if (i > MAX_MODULES) + { + warn("Too many modules, skipping the rest..."); + break; + } + + binfo.Modules[i].Address = (void *)((uint64_t)ModuleResponse->modules[i]->address - 0xFFFF800000000000); + binfo.Modules[i].Size = ModuleResponse->modules[i]->size; + + strncpy(binfo.Modules[i].Path, + ModuleResponse->modules[i]->path, + strlen(ModuleResponse->modules[i]->path) + 1); + + strncpy(binfo.Modules[i].CommandLine, + ModuleResponse->modules[i]->cmdline, + strlen(ModuleResponse->modules[i]->cmdline) + 1); + + debug("Module %d:\nAddress: %#lx\nPath: \"%s\"\nCommand Line: \"%s\"\nSize: %ld", + i, + binfo.Modules[i].Address, + binfo.Modules[i].Path, + binfo.Modules[i].CommandLine, + binfo.Modules[i].Size); + } + + binfo.RSDP = (struct RSDPInfo *)((uintptr_t)RsdpResponse->address - 0xFFFF800000000000); + debug("RSDP: %#lx [Signature: \"%.8s\"] [OEM: \"%.6s\"]", + binfo.RSDP, binfo.RSDP->Signature, binfo.RSDP->OEMID); + + if (SmbiosResponse->entry_64 != NULL) + binfo.SMBIOSPtr = (void *)((uintptr_t)SmbiosResponse->entry_64 - 0xFFFF800000000000); + else if (SmbiosResponse->entry_32 != NULL) + binfo.SMBIOSPtr = (void *)((uintptr_t)SmbiosResponse->entry_32 - 0xFFFF800000000000); + else + binfo.SMBIOSPtr = NULL; + debug("SMBIOS: %#lx %#lx (binfo: %#lx)", + SmbiosResponse->entry_32, + SmbiosResponse->entry_64, + binfo.SMBIOSPtr); + + binfo.Kernel.PhysicalBase = (void *)KernelAddressResponse->physical_base; + binfo.Kernel.VirtualBase = (void *)KernelAddressResponse->virtual_base; + binfo.Kernel.FileBase = (void *)((uintptr_t)KernelFileResponse->kernel_file->address - 0xFFFF800000000000); + binfo.Kernel.Size = KernelFileResponse->kernel_file->size; + + strncpy(binfo.Kernel.CommandLine, + KernelFileResponse->kernel_file->cmdline, + strlen(KernelFileResponse->kernel_file->cmdline) + 1); + + debug("Kernel physical address: %#lx", binfo.Kernel.PhysicalBase); + debug("Kernel virtual address: %#lx", binfo.Kernel.VirtualBase); + + strncpy(binfo.Bootloader.Name, + BootloaderInfoResponse->name, + strlen(BootloaderInfoResponse->name) + 1); + + strncpy(binfo.Bootloader.Version, + BootloaderInfoResponse->version, + strlen(BootloaderInfoResponse->version) + 1); + + Entry(&binfo); +} diff --git a/Kernel/Architecture/amd64/Bootstrap/Multiboot.cpp b/Kernel/Architecture/amd64/Bootstrap/Multiboot.cpp new file mode 100644 index 00000000..68657920 --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Multiboot.cpp @@ -0,0 +1,299 @@ +#include + +#include +#include +#include + +#include "../../../kernel.h" + +EXTERNC void multiboot_main(uint64_t Magic, uint64_t Info) +{ + if (Info == NULL || Magic == NULL) + { + if (Magic == NULL) + error("Multiboot magic is NULL"); + if (Info == NULL) + error("Multiboot info is NULL"); + CPU::Stop(); + } + else if (Magic != MULTIBOOT2_BOOTLOADER_MAGIC) + { + error("Multiboot magic is invalid (%#x != %#x)", Magic, MULTIBOOT2_BOOTLOADER_MAGIC); + CPU::Stop(); + } + + BootInfo mb2binfo{}; + + // Clear the BSS + // memset_unsafe(&_kernel_rodata_end, 0, &_kernel_end - &_kernel_rodata_end); + + { + uint64_t div = 1193180 / 1000; + outb(0x43, 0xB6); + outb(0x42, (uint8_t)div); + outb(0x42, (uint8_t)(div >> 8)); + uint8_t tmp = inb(0x61); + if (tmp != (tmp | 3)) + outb(0x61, tmp | 3); + + int pos = 0; + auto InfoAddress = Info; + for (auto Tag = (struct multiboot_tag *)((uint8_t *)InfoAddress + 8); + ; + Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7))) + { + if (Tag->type == MULTIBOOT_TAG_TYPE_END) + { + debug("End of multiboot2 tags"); + break; + } + + switch (Tag->type) + { + case MULTIBOOT_TAG_TYPE_CMDLINE: + { + strncpy(mb2binfo.Kernel.CommandLine, + ((multiboot_tag_string *)Tag)->string, + strlen(((multiboot_tag_string *)Tag)->string)); + debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine); + break; + } + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + { + strncpy(mb2binfo.Bootloader.Name, + ((multiboot_tag_string *)Tag)->string, + strlen(((multiboot_tag_string *)Tag)->string)); + debug("Bootloader name: %s", mb2binfo.Bootloader.Name); + break; + } + case MULTIBOOT_TAG_TYPE_MODULE: + { + multiboot_tag_module *module = (multiboot_tag_module *)Tag; + static int module_count = 0; + mb2binfo.Modules[module_count].Address = (void *)(uint64_t)module->mod_start; + mb2binfo.Modules[module_count].Size = module->size; + strncpy(mb2binfo.Modules[module_count].Path, "(null)", 6); + strncpy(mb2binfo.Modules[module_count].CommandLine, module->cmdline, + strlen(module->cmdline)); + debug("Module: %s", mb2binfo.Modules[module_count].Path); + module_count++; + break; + } + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + { + multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag; + fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]", + meminfo->mem_lower, meminfo->mem_upper); + break; + } + case MULTIBOOT_TAG_TYPE_BOOTDEV: + { + multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag; + fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]", + bootdev->biosdev, bootdev->slice, bootdev->part); + break; + } + case MULTIBOOT_TAG_TYPE_MMAP: + { + multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag; + size_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry); + mb2binfo.Memory.Entries = EntryCount; + for (uint32_t i = 0; i < EntryCount; i++) + { + if (EntryCount > MAX_MEMORY_ENTRIES) + { + warn("Too many memory entries, skipping the rest..."); + break; + } + multiboot_mmap_entry entry = mmap->entries[i]; + mb2binfo.Memory.Size += entry.len; + switch (entry.type) + { + case MULTIBOOT_MEMORY_AVAILABLE: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Usable; + break; + case MULTIBOOT_MEMORY_RESERVED: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Reserved; + break; + case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = ACPIReclaimable; + break; + case MULTIBOOT_MEMORY_NVS: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = ACPINVS; + break; + case MULTIBOOT_MEMORY_BADRAM: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = BadMemory; + break; + default: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Unknown; + break; + } + debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]", + mb2binfo.Memory.Entry[i].BaseAddress, + mb2binfo.Memory.Entry[i].Length, + mb2binfo.Memory.Entry[i].Type); + } + break; + } + case MULTIBOOT_TAG_TYPE_VBE: + { + multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag; + fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]", + vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len); + break; + } + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + { + multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag; + static int fb_count = 0; + mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr; + mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width; + mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height; + mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch; + mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp; + switch (fb->common.framebuffer_type) + { + case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED: + { + mb2binfo.Framebuffer[fb_count].Type = Indexed; + break; + } + case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: + { + mb2binfo.Framebuffer[fb_count].Type = RGB; + mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size; + mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position; + mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size; + mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position; + mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size; + mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position; + break; + } + case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: + { + mb2binfo.Framebuffer[fb_count].Type = EGA; + break; + } + default: + { + mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type; + break; + } + } + debug("Framebuffer %d: %dx%d %d bpp", fb_count, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp); + debug("More info:\nAddress: %p\nPitch: %lld\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d", + fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type, + fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size, + fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position); + fb_count++; + break; + } + case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: + { + multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag; + fixme("elf_sections->[num=%d, size=%d, entsize=%d, shndx=%d]", + elf->num, elf->size, elf->entsize, elf->shndx); + break; + } + case MULTIBOOT_TAG_TYPE_APM: + { + multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag; + fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]", + apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len); + break; + } + case MULTIBOOT_TAG_TYPE_EFI32: + { + multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag; + fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size); + break; + } + case MULTIBOOT_TAG_TYPE_EFI64: + { + multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag; + fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size); + break; + } + case MULTIBOOT_TAG_TYPE_SMBIOS: + { + multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag; + fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor); + break; + } + case MULTIBOOT_TAG_TYPE_ACPI_OLD: + { + mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp; + debug("OLD ACPI RSDP: %p", mb2binfo.RSDP); + break; + } + case MULTIBOOT_TAG_TYPE_ACPI_NEW: + { + mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp; + debug("NEW ACPI RSDP: %p", mb2binfo.RSDP); + break; + } + case MULTIBOOT_TAG_TYPE_NETWORK: + { + multiboot_tag_network *net = (multiboot_tag_network *)Tag; + fixme("network->[dhcpack: %p]", net->dhcpack); + break; + } + case MULTIBOOT_TAG_TYPE_EFI_MMAP: + { + multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag; + fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]", + efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap); + break; + } + case MULTIBOOT_TAG_TYPE_EFI_BS: + { + fixme("efi_bs->[%p] (unknown structure)", Tag); + break; + } + case MULTIBOOT_TAG_TYPE_EFI32_IH: + { + multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag; + fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer); + break; + } + case MULTIBOOT_TAG_TYPE_EFI64_IH: + { + multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag; + fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer); + break; + } + case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR: + { + multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag; + mb2binfo.Kernel.PhysicalBase = (void *)(uint64_t)load_base_addr->load_base_addr; + mb2binfo.Kernel.VirtualBase = (void *)(uint64_t)(load_base_addr->load_base_addr + 0xFFFFFFFF80000000); + mb2binfo.Kernel.Size = ((uint64_t)&_kernel_end - (uint64_t)&_kernel_start) + ((uint64_t)&_bootstrap_end - (uint64_t)&_bootstrap_start); + debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase); + break; + } + default: + { + error("Unknown multiboot2 tag type: %d", Tag->type); + break; + } + } + } + + tmp = inb(0x61) & 0xFC; + outb(0x61, tmp); + } + + Entry(&mb2binfo); +} diff --git a/Kernel/Architecture/amd64/Bootstrap/Multiboot64bitMap.cpp b/Kernel/Architecture/amd64/Bootstrap/Multiboot64bitMap.cpp new file mode 100644 index 00000000..9cfcac91 --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Multiboot64bitMap.cpp @@ -0,0 +1,287 @@ +#include + +union __attribute__((packed)) PageTableEntry +{ + struct + { + bool Present : 1; // 0 + bool ReadWrite : 1; // 1 + bool UserSupervisor : 1; // 2 + bool WriteThrough : 1; // 3 + bool CacheDisable : 1; // 4 + bool Accessed : 1; // 5 + bool Dirty : 1; // 6 + bool PageAttributeTable : 1; // 7 + bool Global : 1; // 8 + uint8_t Available0 : 3; // 9-11 + uint64_t Address : 40; // 12-51 + uint32_t Available1 : 7; // 52-58 + uint8_t ProtectionKey : 4; // 59-62 + bool ExecuteDisable : 1; // 63 + }; + uint64_t raw; + + __always_inline inline SafeFunction NIF void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + __always_inline inline SafeFunction NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; } +}; + +struct __attribute__((packed)) PageTableEntryPtr +{ + PageTableEntry Entries[512]; +}; + +union __attribute__((packed)) PageDirectoryEntry +{ + struct + { + bool Present : 1; // 0 + bool ReadWrite : 1; // 1 + bool UserSupervisor : 1; // 2 + bool WriteThrough : 1; // 3 + bool CacheDisable : 1; // 4 + bool Accessed : 1; // 5 + bool Available0 : 1; // 6 + bool PageSize : 1; // 7 + uint8_t Available1 : 4; // 8-11 + uint64_t Address : 40; // 12-51 + uint32_t Available2 : 11; // 52-62 + bool ExecuteDisable : 1; // 63 + }; + uint64_t raw; + + __always_inline inline SafeFunction NIF void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + __always_inline inline SafeFunction NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; } +}; + +struct __attribute__((packed)) PageDirectoryEntryPtr +{ + PageDirectoryEntry Entries[512]; +}; + +union __attribute__((packed)) PageDirectoryPointerTableEntry +{ + struct + { + bool Present : 1; // 0 + bool ReadWrite : 1; // 1 + bool UserSupervisor : 1; // 2 + bool WriteThrough : 1; // 3 + bool CacheDisable : 1; // 4 + bool Accessed : 1; // 5 + bool Available0 : 1; // 6 + bool PageSize : 1; // 7 + uint8_t Available1 : 4; // 8-11 + uint64_t Address : 40; // 12-51 + uint32_t Available2 : 11; // 52-62 + bool ExecuteDisable : 1; // 63 + }; + uint64_t raw; + + __always_inline inline SafeFunction NIF void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + __always_inline inline SafeFunction NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; } +}; + +struct __attribute__((packed)) PageDirectoryPointerTableEntryPtr +{ + PageDirectoryPointerTableEntry Entries[512]; +}; + +union __attribute__((packed)) PageMapLevel4 +{ + struct + { + bool Present : 1; // 0 + bool ReadWrite : 1; // 1 + bool UserSupervisor : 1; // 2 + bool WriteThrough : 1; // 3 + bool CacheDisable : 1; // 4 + bool Accessed : 1; // 5 + bool Available0 : 1; // 6 + bool Reserved0 : 1; // 7 + uint8_t Available1 : 4; // 8-11 + uint64_t Address : 40; // 12-51 + uint32_t Available2 : 11; // 52-62 + bool ExecuteDisable : 1; // 63 + }; + uint64_t raw; + + __always_inline inline SafeFunction NIF void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + __always_inline inline SafeFunction NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; } +}; + +struct PageTable4 +{ + PageMapLevel4 Entries[512]; +} __attribute__((aligned(0x1000))); + +extern "C" char BootPageTable[]; +extern uintptr_t _kernel_start, _kernel_end; + +__attribute__((section(".bootstrap.data"))) static PageTable4 *BPTable = (PageTable4 *)BootPageTable; +__attribute__((section(".bootstrap.data"))) static size_t BPT_Allocated = 0x4000; + +__always_inline inline SafeFunction NIF void *RequestPage() +{ + void *Page = (void *)(BootPageTable + BPT_Allocated); + BPT_Allocated += 0x1000; + if (BPT_Allocated >= 0x10000) /* The length of BootPageTable */ + { + while (true) + ; + } + return Page; +} + +class PageMapIndexer +{ +public: + uintptr_t PMLIndex = 0; + uintptr_t PDPTEIndex = 0; + uintptr_t PDEIndex = 0; + uintptr_t PTEIndex = 0; + __always_inline inline SafeFunction NIF PageMapIndexer(uintptr_t VirtualAddress) + { + uintptr_t Address = VirtualAddress; + Address >>= 12; + this->PTEIndex = Address & 0x1FF; + Address >>= 9; + this->PDEIndex = Address & 0x1FF; + Address >>= 9; + this->PDPTEIndex = Address & 0x1FF; + Address >>= 9; + this->PMLIndex = Address & 0x1FF; + } +}; + +__attribute__((section(".bootstrap.text"))) SafeFunction NIF void MB2_64_Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags) +{ + PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress); + // Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only + uint64_t DirectoryFlags = Flags & 0x3F; + + PageMapLevel4 PML4 = BPTable->Entries[Index.PMLIndex]; + PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr; + if (!PML4.Present) + { + PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)RequestPage(); + if (PDPTEPtr == nullptr) + return; + { + void *ptr = PDPTEPtr; + uint8_t value = 0; + size_t num = 0x1000; + uint8_t *p = (uint8_t *)ptr; + for (size_t i = 0; i < num; i++) + p[i] = value; + } + PML4.Present = true; + PML4.SetAddress((uintptr_t)PDPTEPtr >> 12); + } + else + PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + PML4.raw |= DirectoryFlags; + BPTable->Entries[Index.PMLIndex] = PML4; + + PageDirectoryPointerTableEntry PDPTE = PDPTEPtr->Entries[Index.PDPTEIndex]; + PageDirectoryEntryPtr *PDEPtr = nullptr; + if (!PDPTE.Present) + { + PDEPtr = (PageDirectoryEntryPtr *)RequestPage(); + if (PDEPtr == nullptr) + return; + { + void *ptr = PDEPtr; + uint8_t value = 0; + size_t num = 0x1000; + uint8_t *p = (uint8_t *)ptr; + for (size_t i = 0; i < num; i++) + p[i] = value; + } + PDPTE.Present = true; + PDPTE.SetAddress((uintptr_t)PDEPtr >> 12); + } + else + PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE.GetAddress() << 12); + PDPTE.raw |= DirectoryFlags; + PDPTEPtr->Entries[Index.PDPTEIndex] = PDPTE; + + PageDirectoryEntry PDE = PDEPtr->Entries[Index.PDEIndex]; + PageTableEntryPtr *PTEPtr = nullptr; + if (!PDE.Present) + { + PTEPtr = (PageTableEntryPtr *)RequestPage(); + if (PTEPtr == nullptr) + return; + { + void *ptr = PTEPtr; + uint8_t value = 0; + size_t num = 0x1000; + uint8_t *p = (uint8_t *)ptr; + for (size_t i = 0; i < num; i++) + p[i] = value; + } + PDE.Present = true; + PDE.SetAddress((uintptr_t)PTEPtr >> 12); + } + else + PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE.GetAddress() << 12); + PDE.raw |= DirectoryFlags; + PDEPtr->Entries[Index.PDEIndex] = PDE; + + PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex]; + PTE.Present = true; + PTE.raw |= Flags; + PTE.SetAddress((uintptr_t)PhysicalAddress >> 12); + PTEPtr->Entries[Index.PTEIndex] = PTE; + asmv("invlpg (%0)" + : + : "r"(VirtualAddress) + : "memory"); +} + +EXTERNC __attribute__((section(".bootstrap.text"))) SafeFunction NIF __attribute__((section(".bootstrap.text"))) void UpdatePageTable64() +{ + BPTable = (PageTable4 *)BootPageTable; + + // for (size_t i = 0; i < 0x10000000; i += 0x1000) + // MB2_64_Map((void *)i, (void *)i, 0x3); + + uintptr_t KernelStart = (uintptr_t)&_kernel_start; + uintptr_t KernelEnd = (uintptr_t)&_kernel_end; + uintptr_t PhysicalStart = KernelStart - 0xFFFFFFFF80000000; + for (uintptr_t i = KernelStart; i < KernelEnd; i += 0x1000) + { + MB2_64_Map((void *)i, (void *)PhysicalStart, 0x3); + PhysicalStart += 0x1000; + } + + asmv("mov %%cr3, %%rax\n" + "mov %%rax, %%cr3\n" + : + : + : "rax"); +} diff --git a/Kernel/Architecture/amd64/Bootstrap/Multiboot_Init.asm b/Kernel/Architecture/amd64/Bootstrap/Multiboot_Init.asm new file mode 100644 index 00000000..a72c3fcc --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Multiboot_Init.asm @@ -0,0 +1,114 @@ +[bits 32] +KERNEL_STACK_SIZE equ 0x4000 ; 16KB + +extern multiboot_main +extern LoadGDT32 +extern BootPageTable +extern UpdatePageTable + +section .bootstrap.data + +MB_HeaderMagic: + dq 0 + +MB_HeaderInfo: + dq 0 + +section .bootstrap.text + +global _start +_start: + cli + + mov [MB_HeaderMagic], eax + mov [MB_HeaderInfo], ebx + + mov ecx, cr4 + or ecx, 0x00000010 ; Set PSE in CR4 + or ecx, 0x00000020 ; Set PAE in CR4 + mov cr4, ecx + + call LoadGDT32 + call UpdatePageTable + + mov ecx, BootPageTable + mov cr3, ecx + + mov ecx, 0xC0000080 ; EFER + rdmsr + or eax, 0x800 | 0x100 | 0x1 ; Set LME, LMA, SCE + wrmsr + + mov ecx, cr0 + or ecx, 0x80000000 | 0x1 ; Set PG and PE in CR0 + mov cr0, ecx + + lgdt [GDT64.Ptr] + + jmp GDT64.code:HigherHalfStart + +extern UpdatePageTable64 + +[bits 64] +HigherHalfStart: + mov ax, GDT64.data + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + call UpdatePageTable64 + + mov rsp, KernelStack + KERNEL_STACK_SIZE + mov rdi, [MB_HeaderMagic] + mov rsi, [MB_HeaderInfo] + push rsi + push rdi + call multiboot_main +.Hang: + hlt + jmp .Hang + + + +; Access bits +PRESENT equ 1 << 7 +NOT_SYS equ 1 << 4 +EXEC equ 1 << 3 +DC equ 1 << 2 +RW equ 1 << 1 +ACCESSED equ 1 << 0 + +; Flags bits +GRAN_4K equ 1 << 7 +SZ_32 equ 1 << 6 +LONG_MODE equ 1 << 5 + +section .bootstrap.data +GDT64: + .null: equ $ - GDT64 + dq 0 + .code: equ $ - GDT64 + dd 0xFFFF + db 0 + db PRESENT | NOT_SYS | EXEC | RW + db GRAN_4K | LONG_MODE | 0xF + db 0 + .data: equ $ - GDT64 + dd 0xFFFF + db 0 + db PRESENT | NOT_SYS | RW + db GRAN_4K | SZ_32 | 0xF + db 0 + .tss: equ $ - GDT64 + dd 0x00000068 + dd 0x00CF8900 + .Ptr: + dw $ - GDT64 - 1 + dq GDT64 + +section .bootstrap.bss +align 16 +KernelStack: + resb KERNEL_STACK_SIZE diff --git a/Kernel/Architecture/amd64/Bootstrap/Multiboot_PageTable.asm b/Kernel/Architecture/amd64/Bootstrap/Multiboot_PageTable.asm new file mode 100644 index 00000000..cc5caf3f --- /dev/null +++ b/Kernel/Architecture/amd64/Bootstrap/Multiboot_PageTable.asm @@ -0,0 +1,45 @@ +PAGE_TABLE_SIZE equ 0x4 ; 1GB +[bits 32] + +section .bootstrap.data +align 0x1000 +global BootPageTable +BootPageTable: + times (0x10000) dq 0 ; 0x4000 + +section .bootstrap.text +global UpdatePageTable +UpdatePageTable: + mov edi, (BootPageTable + 0x0000) ; First PML4E + mov eax, (BootPageTable + 0x1000) ; First PDPTE + or eax, 11b ; Bitwise OR on rax (PDPTE) with 11b (Present, Write) + mov dword [edi], eax ; Write 11b to PML4E + + mov edi, (BootPageTable + 0x1000) ; First PDPTE + mov eax, (BootPageTable + 0x2000) ; First PDE + or eax, 11b ; Bitwise OR on rax (PDE) with 11b (Present, Write) + + mov ecx, PAGE_TABLE_SIZE ; For loop instruction + mov ebx, 0x0 ; Value to store in the next 4 bytes + .FillPageTableLevel3: + mov dword [edi], eax ; Store modified PDE in PDPTE + mov dword [edi + 4], ebx ; Store the rbx value in the next 4 bytes + add eax, 0x1000 ; Increment (page size) + adc ebx, 0 ; Add 0 to carry flag + add edi, 8 ; Add 8 to rdi (next PDE) + loop .FillPageTableLevel3 ; Loop until rcx is 0 + + mov edi, (BootPageTable + 0x2000) ; First PDE + mov eax, 10000011b ; Present, Write, Large Page + + mov ecx, (512 * PAGE_TABLE_SIZE) ; For loop instruction + mov ebx, 0x0 ; Value to store in the next 4 bytes + .FillPageTableLevel2: + mov dword [edi], eax ; Store modified PDE in PDPTE + mov dword [edi + 4], ebx ; Store the rbx value in the next 4 bytes + add eax, 1 << 21 ; Increment (page size) + adc ebx, 0 ; Add 0 (carry flag) to rbx to increment if there was a carry + add edi, 8 ; Add 8 to rdi (next PDE) + loop .FillPageTableLevel2 ; Loop until rcx is 0 + + ret diff --git a/Kernel/Architecture/amd64/DifferentiatedSystemDescriptionTable.cpp b/Kernel/Architecture/amd64/DifferentiatedSystemDescriptionTable.cpp new file mode 100644 index 00000000..202fb25c --- /dev/null +++ b/Kernel/Architecture/amd64/DifferentiatedSystemDescriptionTable.cpp @@ -0,0 +1,255 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "acpi.hpp" + +#include +#include +#include +#include + +#include "cpu/apic.hpp" +#include "../../kernel.h" + +#define ACPI_TIMER 0x0001 +#define ACPI_BUSMASTER 0x0010 +#define ACPI_GLOBAL 0x0020 +#define ACPI_POWER_BUTTON 0x0100 +#define ACPI_SLEEP_BUTTON 0x0200 +#define ACPI_RTC_ALARM 0x0400 +#define ACPI_PCIE_WAKE 0x4000 +#define ACPI_WAKE 0x8000 + +namespace ACPI +{ + __always_inline inline bool IsCanonical(uint64_t Address) + { + return ((Address <= 0x00007FFFFFFFFFFF) || ((Address >= 0xFFFF800000000000) && (Address <= 0xFFFFFFFFFFFFFFFF))); + } + +#define ACPI_ENABLED 0x0001 +#define ACPI_SLEEP 0x2000 + +#define ACPI_GAS_MMIO 0 +#define ACPI_GAS_IO 1 +#define ACPI_GAS_PCI 2 + + void DSDT::OnInterruptReceived(CPU::x64::TrapFrame *Frame) + { + debug("SCI Handle Triggered"); + uint16_t Event = 0; + { + uint16_t a = 0, b = 0; + if (acpi->FADT->PM1aEventBlock) + { + a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock)); + outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a); + } + if (acpi->FADT->PM1bEventBlock) + { + b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock)); + outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b); + } + Event = a | b; + } + + debug("SCI Event: %#lx", Event); + if (Event & ACPI_BUSMASTER) + { + fixme("ACPI Busmaster"); + } + else if (Event & ACPI_GLOBAL) + { + fixme("ACPI Global"); + } + else if (Event & ACPI_POWER_BUTTON) + { + if (TaskManager) + { + TaskManager->CreateThread(TaskManager->CreateProcess(nullptr, + "Shutdown", + Tasking::TaskTrustLevel::Kernel), + (Tasking::IP)KST_Shutdown); + } + else + KernelShutdownThread(false); + } + else if (Event & ACPI_SLEEP_BUTTON) + { + fixme("ACPI Sleep Button"); + } + else if (Event & ACPI_RTC_ALARM) + { + fixme("ACPI RTC Alarm"); + } + else if (Event & ACPI_PCIE_WAKE) + { + fixme("ACPI PCIe Wake"); + } + else if (Event & ACPI_WAKE) + { + fixme("ACPI Wake"); + } + else if (Event & ACPI_TIMER) + { + fixme("ACPI Timer"); + } + else + { + error("ACPI unknown event %#lx on CPU %d", Event, GetCurrentCPU()->ID); + CPU::Stop(); + } + UNUSED(Frame); + } + + void DSDT::Shutdown() + { + trace("Shutting down..."); + if (SCI_EN == 1) + { + outw(s_cst(uint16_t, acpi->FADT->PM1aControlBlock), + s_cst(uint16_t, + (inw(s_cst(uint16_t, + acpi->FADT->PM1aControlBlock)) & + 0xE3FF) | + ((SLP_TYPa << 10) | ACPI_SLEEP))); + + if (acpi->FADT->PM1bControlBlock) + outw(s_cst(uint16_t, acpi->FADT->PM1bControlBlock), + s_cst(uint16_t, + (inw( + s_cst(uint16_t, acpi->FADT->PM1bControlBlock)) & + 0xE3FF) | + ((SLP_TYPb << 10) | ACPI_SLEEP))); + + outw(s_cst(uint16_t, PM1a_CNT), SLP_TYPa | SLP_EN); + if (PM1b_CNT) + outw(s_cst(uint16_t, PM1b_CNT), SLP_TYPb | SLP_EN); + } + } + + void DSDT::Reboot() + { + trace("Rebooting..."); + switch (acpi->FADT->ResetReg.AddressSpace) + { + case ACPI_GAS_MMIO: + { + *(uint8_t *)(acpi->FADT->ResetReg.Address) = acpi->FADT->ResetValue; + break; + } + case ACPI_GAS_IO: + { + outb(s_cst(uint16_t, acpi->FADT->ResetReg.Address), acpi->FADT->ResetValue); + break; + } + case ACPI_GAS_PCI: + { + fixme("ACPI_GAS_PCI not supported."); + /* + seg - 0 + bus - 0 + dev - (FADT->ResetReg.Address >> 32) & 0xFFFF + function - (FADT->ResetReg.Address >> 16) & 0xFFFF + offset - FADT->ResetReg.Address & 0xFFFF + value - FADT->ResetValue + */ + break; + } + default: + { + error("Unknown reset register address space: %d", acpi->FADT->ResetReg.AddressSpace); + break; + } + } + } + + DSDT::DSDT(ACPI *acpi) : Interrupts::Handler(acpi->FADT->SCI_Interrupt) + { + this->acpi = acpi; + uint64_t Address = ((IsCanonical(acpi->FADT->X_Dsdt) && acpi->XSDTSupported) ? acpi->FADT->X_Dsdt : acpi->FADT->Dsdt); + uint8_t *S5Address = (uint8_t *)(Address) + 36; + ACPI::ACPI::ACPIHeader *Header = (ACPI::ACPI::ACPIHeader *)Address; + uint64_t Length = Header->Length; + while (Length-- > 0) + { + if (!memcmp(S5Address, "_S5_", 4)) + break; + S5Address++; + } + if (Length <= 0) + { + warn("_S5 not present in ACPI"); + return; + } + if ((*(S5Address - 1) == 0x08 || (*(S5Address - 2) == 0x08 && *(S5Address - 1) == '\\')) && *(S5Address + 4) == 0x12) + { + S5Address += 5; + S5Address += ((*S5Address & 0xC0) >> 6) + 2; + if (*S5Address == 0x0A) + S5Address++; + SLP_TYPa = s_cst(uint16_t, *(S5Address) << 10); + S5Address++; + if (*S5Address == 0x0A) + S5Address++; + SLP_TYPb = s_cst(uint16_t, *(S5Address) << 10); + SMI_CMD = acpi->FADT->SMI_CommandPort; + ACPI_ENABLE = acpi->FADT->AcpiEnable; + ACPI_DISABLE = acpi->FADT->AcpiDisable; + PM1a_CNT = acpi->FADT->PM1aControlBlock; + PM1b_CNT = acpi->FADT->PM1bControlBlock; + PM1_CNT_LEN = acpi->FADT->PM1ControlLength; + SLP_EN = 1 << 13; + SCI_EN = 1; + trace("ACPI Shutdown is supported"); + ACPIShutdownSupported = true; + + uint16_t value = ACPI_POWER_BUTTON | ACPI_SLEEP_BUTTON | ACPI_WAKE; + { + uint16_t a = s_cst(uint16_t, acpi->FADT->PM1aEventBlock + (acpi->FADT->PM1EventLength / 2)); + uint16_t b = s_cst(uint16_t, acpi->FADT->PM1bEventBlock + (acpi->FADT->PM1EventLength / 2)); + debug("SCI Event: %#llx [a:%#x b:%#x]", value, a, b); + if (acpi->FADT->PM1aEventBlock) + outw(a, value); + if (acpi->FADT->PM1bEventBlock) + outw(b, value); + } + + { + uint16_t a = 0, b = 0; + if (acpi->FADT->PM1aEventBlock) + { + a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock)); + outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a); + } + if (acpi->FADT->PM1bEventBlock) + { + b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock)); + outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b); + } + } + ((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, acpi->FADT->SCI_Interrupt, 1); + return; + } + warn("Failed to parse _S5 in ACPI"); + SCI_EN = 0; + } + + DSDT::~DSDT() + { + } +} diff --git a/Kernel/Architecture/amd64/MultipleAPICDescriptionTable.cpp b/Kernel/Architecture/amd64/MultipleAPICDescriptionTable.cpp new file mode 100644 index 00000000..70177fc3 --- /dev/null +++ b/Kernel/Architecture/amd64/MultipleAPICDescriptionTable.cpp @@ -0,0 +1,91 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "acpi.hpp" + +#include +#include + +#include "../../kernel.h" + +namespace ACPI +{ + MADT::MADT(ACPI::MADTHeader *madt) + { + trace("Initializing MADT"); + CPUCores = 0; + LAPICAddress = (LAPIC *)(uintptr_t)madt->LocalControllerAddress; + for (uint8_t *ptr = (uint8_t *)(madt->Entries); + (uintptr_t)(ptr) < (uintptr_t)(madt) + madt->Header.Length; + ptr += *(ptr + 1)) + { + switch (*(ptr)) + { + case 0: + { + if (ptr[4] & 1) + { + lapic.push_back((LocalAPIC *)ptr); + KPrint("Local APIC \e8888FF%d\eCCCCCC (APIC \e8888FF%d\eCCCCCC) found.", lapic.back()->ACPIProcessorId, lapic.back()->APICId); + CPUCores++; + } + break; + } + case 1: + { + ioapic.push_back((MADTIOApic *)ptr); + KPrint("I/O APIC \e8888FF%d\eCCCCCC (Address \e8888FF%#lx\eCCCCCC) found.", ioapic.back()->APICID, ioapic.back()->Address); + Memory::Virtual(KernelPageTable).Map((void *)(uintptr_t)ioapic.back()->Address, (void *)(uintptr_t)ioapic.back()->Address, Memory::PTFlag::RW | Memory::PTFlag::PCD); // Make sure that the address is mapped. + break; + } + case 2: + { + iso.push_back((MADTIso *)ptr); + KPrint("ISO (IRQ:\e8888FF%#lx\eCCCCCC, BUS:\e8888FF%#lx\eCCCCCC, GSI:\e8888FF%#lx\eCCCCCC, %s\eCCCCCC/%s\eCCCCCC) found.", + iso.back()->IRQSource, iso.back()->BuSSource, iso.back()->GSI, + iso.back()->Flags & 0x00000004 ? "\e1770FFActive High" : "\e475EFFActive Low", + iso.back()->Flags & 0x00000100 ? "\e00962DEdge Triggered" : "\e008F58Level Triggered"); + break; + } + case 4: + { + nmi.push_back((MADTNmi *)ptr); + KPrint("NMI \e8888FF%#lx\eCCCCCC (lint:\e8888FF%#lx\eCCCCCC) found.", nmi.back()->processor, nmi.back()->lint); + break; + } + case 5: + { + LAPICAddress = (LAPIC *)ptr; + KPrint("APIC found at \e8888FF%#lx\eCCCCCC", LAPICAddress); + break; + } + default: + { + KPrint("Unknown MADT entry \e8888FF%#lx\eCCCCCC", *(ptr)); + break; + } + } + Memory::Virtual(KernelPageTable).Map((void *)LAPICAddress, (void *)LAPICAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD); // I should map more than one page? + } + CPUCores--; // We start at 0 (BSP) and end at 11 (APs), so we have 12 cores. + KPrint("Total CPU cores: %d", CPUCores + 1); + } + + MADT::~MADT() + { + } +} diff --git a/Kernel/Architecture/amd64/SystemCalls.cpp b/Kernel/Architecture/amd64/SystemCalls.cpp new file mode 100644 index 00000000..25775b34 --- /dev/null +++ b/Kernel/Architecture/amd64/SystemCalls.cpp @@ -0,0 +1,92 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include + +#include "cpu/gdt.hpp" + +// https://supercip971.github.io/02-wingos-syscalls.html +using namespace CPU::x64; + +// "Core/SystemCalls.cpp" +extern "C" uint64_t SystemCallsHandler(SyscallsFrame *regs); + +extern "C" void SystemCallHandlerStub(); + +extern "C" __naked __used __no_stack_protector __aligned(16) void SystemCallHandlerStub() +{ + asmv("swapgs\n" + + "mov %rsp, %gs:0x8\n" // CPUData->TempStack + "mov %gs:0x0, %rsp\n" // CPUData->SystemCallStack + "push $0x1b\n" // user data segment + "push %gs:0x8\n" // saved stack + "push %r11\n" // saved rflags + "push $0x23\n" // user code segment + "push %rcx\n" // Current RIP + + "push %rax\n" + "push %rbx\n" + "push %rcx\n" + "push %rdx\n" + "push %rsi\n" + "push %rdi\n" + "push %rbp\n" + "push %r8\n" + "push %r9\n" + "push %r10\n" + "push %r11\n" + "push %r12\n" + "push %r13\n" + "push %r14\n" + "push %r15\n" + + "mov %rsp, %rdi\n" + "mov $0, %rbp\n" + "call SystemCallsHandler\n" + + "pop %r15\n" + "pop %r14\n" + "pop %r13\n" + "pop %r12\n" + "pop %r11\n" + "pop %r10\n" + "pop %r9\n" + "pop %r8\n" + "pop %rbp\n" + "pop %rdi\n" + "pop %rsi\n" + "pop %rdx\n" + "pop %rcx\n" + "pop %rbx\n" + + "mov %gs:0x8, %rsp\n" // CPUData->TempStack + + "swapgs\n" + "sti\n" + "sysretq\n"); +} + +void InitializeSystemCalls() +{ + wrmsr(MSR_EFER, rdmsr(MSR_EFER) | 1); + wrmsr(MSR_STAR, ((uint64_t)(GDT_KERNEL_CODE) << 32) | ((uint64_t)(GDT_KERNEL_DATA | 3) << 48)); + wrmsr(MSR_LSTAR, (uint64_t)SystemCallHandlerStub); + wrmsr(MSR_SYSCALL_MASK, (uint64_t)(1 << 9)); +} diff --git a/Kernel/Architecture/amd64/acpi.hpp b/Kernel/Architecture/amd64/acpi.hpp new file mode 100644 index 00000000..1a71f712 --- /dev/null +++ b/Kernel/Architecture/amd64/acpi.hpp @@ -0,0 +1,294 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_ACPI_H__ +#define __FENNIX_KERNEL_ACPI_H__ + +#include + +#include +#include +#include +#include + +namespace ACPI +{ + class ACPI + { + public: + struct ACPIHeader + { + unsigned char Signature[4]; + uint32_t Length; + uint8_t Revision; + uint8_t Checksum; + uint8_t OEMID[6]; + uint8_t OEMTableID[8]; + uint32_t OEMRevision; + uint32_t CreatorID; + uint32_t CreatorRevision; + } __packed; + + struct GenericAddressStructure + { + uint8_t AddressSpace; + uint8_t BitWidth; + uint8_t BitOffset; + uint8_t AccessSize; + uint64_t Address; + } __packed; + + struct MCFGHeader + { + struct ACPIHeader Header; + uint64_t Reserved; + } __packed; + + struct HPETHeader + { + ACPIHeader Header; + uint8_t HardwareRevID; + uint8_t ComparatorCount : 5; + uint8_t CounterSize : 1; + uint8_t Reserved : 1; + uint8_t LegacyReplacement : 1; + uint16_t PCIVendorID; + struct GenericAddressStructure Address; + uint8_t HPETNumber; + uint16_t MinimumTick; + uint8_t PageProtection; + } __packed; + + struct FADTHeader + { + ACPIHeader Header; + uint32_t FirmwareCtrl; + uint32_t Dsdt; + uint8_t Reserved; + uint8_t PreferredPowerManagementProfile; + uint16_t SCI_Interrupt; + uint32_t SMI_CommandPort; + uint8_t AcpiEnable; + uint8_t AcpiDisable; + uint8_t S4BIOS_REQ; + uint8_t PSTATE_Control; + uint32_t PM1aEventBlock; + uint32_t PM1bEventBlock; + uint32_t PM1aControlBlock; + uint32_t PM1bControlBlock; + uint32_t PM2ControlBlock; + uint32_t PMTimerBlock; + uint32_t GPE0Block; + uint32_t GPE1Block; + uint8_t PM1EventLength; + uint8_t PM1ControlLength; + uint8_t PM2ControlLength; + uint8_t PMTimerLength; + uint8_t GPE0Length; + uint8_t GPE1Length; + uint8_t GPE1Base; + uint8_t CStateControl; + uint16_t WorstC2Latency; + uint16_t WorstC3Latency; + uint16_t FlushSize; + uint16_t FlushStride; + uint8_t DutyOffset; + uint8_t DutyWidth; + uint8_t DayAlarm; + uint8_t MonthAlarm; + uint8_t Century; + uint16_t BootArchitectureFlags; + uint8_t Reserved2; + uint32_t Flags; + struct GenericAddressStructure ResetReg; + uint8_t ResetValue; + uint8_t Reserved3[3]; + uint64_t X_FirmwareControl; + uint64_t X_Dsdt; + struct GenericAddressStructure X_PM1aEventBlock; + struct GenericAddressStructure X_PM1bEventBlock; + struct GenericAddressStructure X_PM1aControlBlock; + struct GenericAddressStructure X_PM1bControlBlock; + struct GenericAddressStructure X_PM2ControlBlock; + struct GenericAddressStructure X_PMTimerBlock; + struct GenericAddressStructure X_GPE0Block; + struct GenericAddressStructure X_GPE1Block; + } __packed; + + struct BGRTHeader + { + ACPIHeader Header; + uint16_t Version; + uint8_t Status; + uint8_t ImageType; + uint64_t ImageAddress; + uint32_t ImageOffsetX; + uint32_t ImageOffsetY; + }; + + struct SRATHeader + { + ACPIHeader Header; + uint32_t TableRevision; // Must be value 1 + uint64_t Reserved; // Reserved, must be zero + }; + + struct TPM2Header + { + ACPIHeader Header; + uint32_t Flags; + uint64_t ControlAddress; + uint32_t StartMethod; + }; + + struct TCPAHeader + { + ACPIHeader Header; + uint16_t Reserved; + uint32_t MaxLogLength; + uint64_t LogAddress; + }; + + struct WAETHeader + { + ACPIHeader Header; + uint32_t Flags; + }; + + struct HESTHeader + { + ACPIHeader Header; + uint32_t ErrorSourceCount; + }; + + struct MADTHeader + { + ACPIHeader Header; + uint32_t LocalControllerAddress; + uint32_t Flags; + char Entries[]; + } __packed; + + ACPIHeader *XSDT = nullptr; + MCFGHeader *MCFG = nullptr; + HPETHeader *HPET = nullptr; + FADTHeader *FADT = nullptr; + BGRTHeader *BGRT = nullptr; + SRATHeader *SRAT = nullptr; + TPM2Header *TPM2 = nullptr; + TCPAHeader *TCPA = nullptr; + WAETHeader *WAET = nullptr; + MADTHeader *MADT = nullptr; + HESTHeader *HEST = nullptr; + bool XSDTSupported = false; + + void *FindTable(ACPIHeader *ACPIHeader, char *Signature); + void SearchTables(ACPIHeader *Header); + ACPI(); + ~ACPI(); + }; + + class MADT + { + public: + struct APICHeader + { + uint8_t Type; + uint8_t Length; + } __packed; + + struct MADTIOApic + { + struct APICHeader Header; + uint8_t APICID; + uint8_t reserved; + uint32_t Address; + uint32_t GSIBase; + } __packed; + + struct MADTIso + { + struct APICHeader Header; + uint8_t BuSSource; + uint8_t IRQSource; + uint32_t GSI; + uint16_t Flags; + } __packed; + + struct MADTNmi + { + struct APICHeader Header; + uint8_t processor; + uint16_t flags; + uint8_t lint; + } __packed; + + struct LocalAPIC + { + struct APICHeader Header; + uint8_t ACPIProcessorId; + uint8_t APICId; + uint32_t Flags; + } __packed; + + struct LAPIC + { + uint8_t id; + uintptr_t PhysicalAddress; + void *VirtualAddress; + }; + + std::vector ioapic; + std::vector iso; + std::vector nmi; + std::vector lapic; + struct LAPIC *LAPICAddress; + uint16_t CPUCores; + + MADT(ACPI::MADTHeader *madt); + ~MADT(); + }; + + class DSDT : public Interrupts::Handler + { + private: + uint32_t SMI_CMD = 0; + uint8_t ACPI_ENABLE = 0; + uint8_t ACPI_DISABLE = 0; + uint32_t PM1a_CNT = 0; + uint32_t PM1b_CNT = 0; + uint16_t SLP_TYPa = 0; + uint16_t SLP_TYPb = 0; + uint16_t SLP_EN = 0; + uint16_t SCI_EN = 0; + uint8_t PM1_CNT_LEN = 0; + + ACPI *acpi; + void OnInterruptReceived(CPU::x64::TrapFrame *Frame); + + public: + bool ACPIShutdownSupported = false; + + void Reboot(); + void Shutdown(); + + DSDT(ACPI *acpi); + ~DSDT(); + }; +} + +#endif // !__FENNIX_KERNEL_ACPI_H__ diff --git a/Kernel/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp b/Kernel/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp new file mode 100644 index 00000000..c19b7358 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp @@ -0,0 +1,403 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "apic.hpp" + +#include +#include +#include +#include +#include +#include + +#include "../../../kernel.h" +#include "../acpi.hpp" + +NewLock(APICLock); + +using namespace CPU::x64; +using namespace CPU::x86; + +/* +In constructor ‘APIC::APIC::APIC(int)’: +warning: left shift count >= width of type +| APICBaseAddress = BaseStruct.ApicBaseLo << 12u | BaseStruct.ApicBaseHi << 32u; +| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~ +*/ +#pragma GCC diagnostic ignored "-Wshift-count-overflow" + +namespace APIC +{ + // headache + // https://www.amd.com/system/files/TechDocs/24593.pdf + // https://www.naic.edu/~phil/software/intel/318148.pdf + + uint32_t APIC::Read(uint32_t Register) + { +#ifdef DEBUG + if (Register != APIC_ICRLO && + Register != APIC_ICRHI && + Register != APIC_ID) + debug("APIC::Read(%#lx) [x2=%d]", Register, x2APICSupported ? 1 : 0); +#endif + if (x2APICSupported) + { + if (Register != APIC_ICRHI) + return s_cst(uint32_t, rdmsr((Register >> 4) + 0x800)); + else + return s_cst(uint32_t, rdmsr(0x30 + 0x800)); + } + else + { + CPU::MemBar::Barrier(); + uint32_t ret = *((volatile uint32_t *)((uintptr_t)APICBaseAddress + Register)); + CPU::MemBar::Barrier(); + return ret; + } + } + + void APIC::Write(uint32_t Register, uint32_t Value) + { +#ifdef DEBUG + if (Register != APIC_EOI && + Register != APIC_TDCR && + Register != APIC_TIMER && + Register != APIC_TICR && + Register != APIC_ICRLO && + Register != APIC_ICRHI) + debug("APIC::Write(%#lx, %#lx) [x2=%d]", Register, Value, x2APICSupported ? 1 : 0); +#endif + if (x2APICSupported) + { + if (Register != APIC_ICRHI) + wrmsr((Register >> 4) + 0x800, Value); + else + wrmsr(MSR_X2APIC_ICR, Value); + } + else + { + CPU::MemBar::Barrier(); + *((volatile uint32_t *)(((uintptr_t)APICBaseAddress) + Register)) = Value; + CPU::MemBar::Barrier(); + } + } + + void APIC::IOWrite(uint64_t Base, uint32_t Register, uint32_t Value) + { + debug("APIC::IOWrite(%#lx, %#lx, %#lx)", Base, Register, Value); + CPU::MemBar::Barrier(); + *((volatile uint32_t *)(((uintptr_t)Base))) = Register; + CPU::MemBar::Barrier(); + *((volatile uint32_t *)(((uintptr_t)Base + 16))) = Value; + CPU::MemBar::Barrier(); + } + + uint32_t APIC::IORead(uint64_t Base, uint32_t Register) + { + debug("APIC::IORead(%#lx, %#lx)", Base, Register); + CPU::MemBar::Barrier(); + *((volatile uint32_t *)(((uintptr_t)Base))) = Register; + CPU::MemBar::Barrier(); + uint32_t ret = *((volatile uint32_t *)(((uintptr_t)Base + 16))); + CPU::MemBar::Barrier(); + return ret; + } + + void APIC::EOI() { this->Write(APIC_EOI, 0); } + + void APIC::WaitForIPI() + { + InterruptCommandRegisterLow icr = {.raw = 0}; + do + { + icr.raw = this->Read(APIC_ICRLO); + } while (icr.DeliveryStatus != Idle); + } + + void APIC::IPI(int CPU, InterruptCommandRegisterLow icr) + { + SmartCriticalSection(APICLock); + if (x2APICSupported) + { + wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw)); + this->WaitForIPI(); + } + else + { + this->Write(APIC_ICRHI, (CPU << 24)); + this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw)); + this->WaitForIPI(); + } + } + + void APIC::SendInitIPI(int CPU) + { + SmartCriticalSection(APICLock); + if (x2APICSupported) + { + InterruptCommandRegisterLow icr = {.raw = 0}; + icr.DeliveryMode = INIT; + icr.Level = Assert; + wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw)); + this->WaitForIPI(); + } + else + { + InterruptCommandRegisterLow icr = {.raw = 0}; + icr.DeliveryMode = INIT; + icr.Level = Assert; + this->Write(APIC_ICRHI, (CPU << 24)); + this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw)); + this->WaitForIPI(); + } + } + + void APIC::SendStartupIPI(int CPU, uint64_t StartupAddress) + { + SmartCriticalSection(APICLock); + if (x2APICSupported) + { + InterruptCommandRegisterLow icr = {.raw = 0}; + icr.Vector = s_cst(uint8_t, StartupAddress >> 12); + icr.DeliveryMode = Startup; + icr.Level = Assert; + wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw)); + this->WaitForIPI(); + } + else + { + InterruptCommandRegisterLow icr = {.raw = 0}; + icr.Vector = s_cst(uint8_t, StartupAddress >> 12); + icr.DeliveryMode = Startup; + icr.Level = Assert; + this->Write(APIC_ICRHI, (CPU << 24)); + this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw)); + this->WaitForIPI(); + } + } + + uint32_t APIC::IOGetMaxRedirect(uint32_t APICID) + { + uint32_t TableAddress = (this->IORead((((ACPI::MADT *)PowerManager->GetMADT())->ioapic[APICID]->Address), GetIOAPICVersion)); + return ((IOAPICVersion *)&TableAddress)->MaximumRedirectionEntry; + } + + void APIC::RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status) + { + uint64_t Value = Vector; + + int64_t IOAPICTarget = -1; + for (uint64_t i = 0; ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[i] != 0; i++) + if (((ACPI::MADT *)PowerManager->GetMADT())->ioapic[i]->GSIBase <= GSI) + if (((ACPI::MADT *)PowerManager->GetMADT())->ioapic[i]->GSIBase + IOGetMaxRedirect(s_cst(uint32_t, i)) > GSI) + { + IOAPICTarget = i; + break; + } + + if (IOAPICTarget == -1) + { + error("No ISO table found for I/O APIC"); + return; + } + + // TODO: IOAPICRedirectEntry Entry = {.raw = 0}; + + if (Flags & ActiveHighLow) + Value |= (1 << 13); + + if (Flags & EdgeLevel) + Value |= (1 << 15); + + if (!Status) + Value |= (1 << 16); + + Value |= (((uintptr_t)CPU) << 56); + uint32_t IORegister = (GSI - ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[IOAPICTarget]->GSIBase) * 2 + 16; + + this->IOWrite(((ACPI::MADT *)PowerManager->GetMADT())->ioapic[IOAPICTarget]->Address, IORegister, (uint32_t)Value); + this->IOWrite(((ACPI::MADT *)PowerManager->GetMADT())->ioapic[IOAPICTarget]->Address, IORegister + 1, (uint32_t)(Value >> 32)); + } + + void APIC::RedirectIRQ(int CPU, uint16_t IRQ, int Status) + { + for (uint64_t i = 0; i < ((ACPI::MADT *)PowerManager->GetMADT())->iso.size(); i++) + if (((ACPI::MADT *)PowerManager->GetMADT())->iso[i]->IRQSource == IRQ) + { + debug("[ISO %d] Mapping to source IRQ%#d GSI:%#lx on CPU %d", + i, ((ACPI::MADT *)PowerManager->GetMADT())->iso[i]->IRQSource, ((ACPI::MADT *)PowerManager->GetMADT())->iso[i]->GSI, CPU); + + this->RawRedirectIRQ(((ACPI::MADT *)PowerManager->GetMADT())->iso[i]->IRQSource + 0x20, ((ACPI::MADT *)PowerManager->GetMADT())->iso[i]->GSI, ((ACPI::MADT *)PowerManager->GetMADT())->iso[i]->Flags, CPU, Status); + return; + } + debug("Mapping IRQ%d on CPU %d", IRQ, CPU); + this->RawRedirectIRQ(IRQ + 0x20, IRQ, 0, CPU, Status); + } + + void APIC::RedirectIRQs(int CPU) + { + SmartCriticalSection(APICLock); + debug("Redirecting IRQs..."); + for (uint8_t i = 0; i < 16; i++) + this->RedirectIRQ(CPU, i, 1); + debug("Redirecting IRQs completed."); + } + + APIC::APIC(int Core) + { + SmartCriticalSection(APICLock); + APIC_BASE BaseStruct = {.raw = rdmsr(MSR_APIC_BASE)}; + uint64_t BaseLow = BaseStruct.ApicBaseLo; + uint64_t BaseHigh = BaseStruct.ApicBaseHi; + this->APICBaseAddress = BaseLow << 12u | BaseHigh << 32u; + trace("APIC Address: %#lx", this->APICBaseAddress); + Memory::Virtual().Map((void *)this->APICBaseAddress, (void *)this->APICBaseAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD); + + bool x2APICSupported = false; + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid; + cpuid.Get(); + if (cpuid.ECX.x2APIC) + { + // x2APICSupported = cpuid.ECX.x2APIC; + fixme("x2APIC is supported"); + } + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid; + cpuid.Get(); + if (cpuid.ECX.x2APIC) + { + // x2APICSupported = cpuid.ECX.x2APIC; + fixme("x2APIC is supported"); + } + } + + if (x2APICSupported) + { + this->x2APICSupported = true; + wrmsr(MSR_APIC_BASE, (rdmsr(MSR_APIC_BASE) | (1 << 11)) & ~(1 << 10)); + BaseStruct.EN = 1; + wrmsr(MSR_APIC_BASE, BaseStruct.raw); + } + else + { + BaseStruct.EN = 1; + wrmsr(MSR_APIC_BASE, BaseStruct.raw); + } + + this->Write(APIC_TPR, 0x0); + // this->Write(APIC_SVR, this->Read(APIC_SVR) | 0x100); // 0x1FF or 0x100 ? on https://wiki.osdev.org/APIC is 0x100 + + if (!this->x2APICSupported) + { + this->Write(APIC_DFR, 0xF0000000); + this->Write(APIC_LDR, this->Read(APIC_ID)); + } + + ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT(); + + for (size_t i = 0; i < madt->nmi.size(); i++) + { + if (madt->nmi[i]->processor != 0xFF && Core != madt->nmi[i]->processor) + return; + + uint32_t nmi = 0x402; + if (madt->nmi[i]->flags & 2) + nmi |= 1 << 13; + if (madt->nmi[i]->flags & 8) + nmi |= 1 << 15; + if (madt->nmi[i]->lint == 0) + this->Write(APIC_LINT0, nmi); + else if (madt->nmi[i]->lint == 1) + this->Write(APIC_LINT1, nmi); + } + + // Setup the spurrious interrupt vector + Spurious Spurious = {.raw = this->Read(APIC_SVR)}; + Spurious.Vector = IRQ223; // TODO: Should I map the IRQ to something? + Spurious.Software = 1; + this->Write(APIC_SVR, s_cst(uint32_t, Spurious.raw)); + + static int once = 0; + if (!once++) + { + // Disable PIT + outb(0x43, 0x28); + outb(0x40, 0x0); + + // Disable PIC + outb(0x21, 0xFF); + outb(0xA1, 0xFF); + } + } + + APIC::~APIC() {} + + void Timer::OnInterruptReceived(TrapFrame *Frame) { UNUSED(Frame); } + + void Timer::OneShot(uint32_t Vector, uint64_t Miliseconds) + { + SmartCriticalSection(APICLock); + LVTTimer timer = {.raw = 0}; + timer.Vector = s_cst(uint8_t, Vector); + timer.TimerMode = 0; + if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0) + this->lapic->Write(APIC_TDCR, DivideBy128); + else + this->lapic->Write(APIC_TDCR, DivideBy16); + this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks * Miliseconds)); + this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw)); + } + + Timer::Timer(APIC *apic) : Interrupts::Handler(0) /* IRQ0 */ + { + SmartCriticalSection(APICLock); + this->lapic = apic; + LVTTimerDivide Divider = DivideBy16; + + trace("Initializing APIC timer on CPU %d", GetCurrentCPU()->ID); + + this->lapic->Write(APIC_TDCR, Divider); + this->lapic->Write(APIC_TICR, 0xFFFFFFFF); + + TimeManager->Sleep(1, Time::Units::Milliseconds); + + // Mask the timer + this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */); + Ticks = 0xFFFFFFFF - this->lapic->Read(APIC_TCCR); + + // Config for IRQ0 timer + LVTTimer timer = {.raw = 0}; + timer.Vector = IRQ0; + timer.Mask = Unmasked; + timer.TimerMode = LVTTimerMode::OneShot; + + // Initialize APIC timer + this->lapic->Write(APIC_TDCR, Divider); + this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks)); + this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw)); + trace("%d APIC Timer %d ticks in.", GetCurrentCPU()->ID, Ticks); + KPrint("APIC Timer: \e8888FF%ld\eCCCCCC ticks.", Ticks); + } + + Timer::~Timer() + { + } +} diff --git a/Kernel/Architecture/amd64/cpu/GlobalDescriptorTable.cpp b/Kernel/Architecture/amd64/cpu/GlobalDescriptorTable.cpp new file mode 100644 index 00000000..5dc43dea --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/GlobalDescriptorTable.cpp @@ -0,0 +1,214 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "gdt.hpp" + +#include +#include +#include +#include + +namespace GlobalDescriptorTable +{ + static GlobalDescriptorTableEntries GDTEntriesTemplate = { + .Null = + { + .Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = {.Raw = 0x0}, + .Flags = {.Raw = 0x0}, + .BaseHigh = 0x0, + }, + + .Code = + { + .Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = { + .A = 0, + .RW = 1, + .DC = 0, + .E = 1, + .S = 1, + .DPL = 0, + .P = 1, + }, + .Flags = { + .Unknown = 0x0, + .L = 1, + }, + .BaseHigh = 0x0, + }, + + .Data = { + .Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = { + .A = 0, + .RW = 1, + .DC = 0, + .E = 0, + .S = 1, + .DPL = 0, + .P = 1, + }, + .Flags = {.Raw = 0x0}, + .BaseHigh = 0x0, + }, + + .UserData = { + .Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = { + .A = 0, + .RW = 1, + .DC = 0, + .E = 0, + .S = 1, + .DPL = 3, + .P = 1, + }, + .Flags = { + .Raw = 0x0, + }, + .BaseHigh = 0x0, + }, + + .UserCode = { + .Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = { + .A = 0, + .RW = 1, + .DC = 0, + .E = 1, + .S = 1, + .DPL = 3, + .P = 1, + }, + .Flags = { + .Unknown = 0x0, + .L = 1, + }, + .BaseHigh = 0x0, + }, + + .TaskStateSegment = {}, + }; + + GlobalDescriptorTableEntries GDTEntries[MAX_CPU] __aligned(16); + GlobalDescriptorTableDescriptor gdt[MAX_CPU] __aligned(16); + + TaskStateSegment tss[MAX_CPU] = { + 0, + {0, 0, 0}, + 0, + {0, 0, 0, 0, 0, 0, 0}, + 0, + 0, + 0, + }; + + void *CPUStackPointer[MAX_CPU]; + + SafeFunction void Init(int Core) + { + memcpy(&GDTEntries[Core], &GDTEntriesTemplate, sizeof(GlobalDescriptorTableEntries)); + gdt[Core] = {.Length = sizeof(GlobalDescriptorTableEntries) - 1, .Entries = &GDTEntries[Core]}; + + debug("Kernel: Code Access: %ld; Data Access: %ld", GDTEntries[Core].Code.Access.Raw, GDTEntries[Core].Data.Access.Raw); + debug("Kernel: Code Flags: %ld; Data Flags: %ld", GDTEntries[Core].Code.Flags.Raw, GDTEntries[Core].Data.Flags.Raw); + debug("User: Code Access: %ld; Data Access: %ld", GDTEntries[Core].UserCode.Access.Raw, GDTEntries[Core].UserData.Access.Raw); + debug("User: Code Flags: %ld; Data Flags: %ld", GDTEntries[Core].UserCode.Flags.Raw, GDTEntries[Core].UserData.Flags.Raw); + CPU::x64::lgdt(&gdt[Core]); + + asmv("movq %%rsp, %%rax\n" + "pushq $16\n" + "pushq %%rax\n" + "pushfq\n" + "pushq $8\n" + "pushq $1f\n" + "iretq\n" + "1:\n" + "movw $16, %%ax\n" + "movw %%ax, %%ds\n" + "movw %%ax, %%es\n" :: + : "memory", "rax"); + + CPUStackPointer[Core] = KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1)); + memset(CPUStackPointer[Core], 0, STACK_SIZE); + debug("CPU %d Stack Pointer: %#lx-%#lx (%d pages)", Core, + CPUStackPointer[Core], (uintptr_t)CPUStackPointer[Core] + STACK_SIZE, + TO_PAGES(STACK_SIZE + 1)); + + uintptr_t Base = (uintptr_t)&tss[Core]; + size_t Limit = Base + sizeof(TaskStateSegment); + gdt[Core].Entries->TaskStateSegment.Length = Limit & 0xFFFF; + gdt[Core].Entries->TaskStateSegment.BaseLow = Base & 0xFFFF; + gdt[Core].Entries->TaskStateSegment.BaseMiddle = (Base >> 16) & 0xFF; + gdt[Core].Entries->TaskStateSegment.BaseHigh = (Base >> 24) & 0xFF; + gdt[Core].Entries->TaskStateSegment.BaseUpper = s_cst(uint32_t, (Base >> 32) & 0xFFFFFFFF); + gdt[Core].Entries->TaskStateSegment.Flags = {.A = 1, .RW = 0, .DC = 0, .E = 1, .S = 0, .DPL = 0, .P = 1}; + gdt[Core].Entries->TaskStateSegment.Granularity = (0 << 4) | ((Limit >> 16) & 0xF); + + tss[Core].IOMapBaseAddressOffset = sizeof(TaskStateSegment); + tss[Core].StackPointer[0] = (uint64_t)CPUStackPointer[Core] + STACK_SIZE; + + for (size_t i = 0; i < sizeof(tss[Core].InterruptStackTable) / sizeof(tss[Core].InterruptStackTable[7]); i++) + { + void *NewStack = KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1)); + + tss[Core].InterruptStackTable[i] = (uint64_t)NewStack + STACK_SIZE; + memset((void *)(tss[Core].InterruptStackTable[i] - STACK_SIZE), 0, STACK_SIZE); + debug("IST-%d: %#lx-%#lx", i, NewStack, (uintptr_t)NewStack + STACK_SIZE); + } + + CPU::x64::ltr(GDT_TSS); + + debug("GDT_KERNEL_CODE: %#lx", GDT_KERNEL_CODE); + debug("GDT_KERNEL_DATA: %#lx", GDT_KERNEL_DATA); + debug("GDT_USER_CODE: %#lx", GDT_USER_CODE); + debug("GDT_USER_DATA: %#lx", GDT_USER_DATA); + debug("GDT_TSS: %#lx", GDT_TSS); + debug("Global Descriptor Table initialized"); + } + + SafeFunction void SetKernelStack(void *Stack) + { + long CPUID = GetCurrentCPU()->ID; + if (Stack != nullptr) + tss[CPUID].StackPointer[0] = (uint64_t)Stack; + else + tss[CPUID].StackPointer[0] = (uint64_t)CPUStackPointer[CPUID] + STACK_SIZE; + + /* + FIXME: There's a bug in kernel which if + we won't update "tss[CPUID].StackPointer[0]" + with the current stack pointer, the kernel + will crash. + */ + asmv("mov %%rsp, %0" + : "=r"(tss[CPUID].StackPointer[0])); + } + + void *GetKernelStack() { return (void *)tss[GetCurrentCPU()->ID].StackPointer[0]; } +} diff --git a/Kernel/Architecture/amd64/cpu/InterruptDescriptorTable.cpp b/Kernel/Architecture/amd64/cpu/InterruptDescriptorTable.cpp new file mode 100644 index 00000000..3cf6f9c3 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/InterruptDescriptorTable.cpp @@ -0,0 +1,757 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "idt.hpp" + +#include +#include +#include +#include + +#include "gdt.hpp" +#include "../../../kernel.h" + +/* conversion from ‘uint64_t’ {aka ‘long unsigned int’} to ‘unsigned char:2’ may change value */ +#pragma GCC diagnostic ignored "-Wconversion" + +extern "C" void MainInterruptHandler(void *Data); +extern "C" void ExceptionHandler(void *Data); + +namespace InterruptDescriptorTable +{ + static InterruptDescriptorTableEntry Entries[0x100]; + + InterruptDescriptorTableDescriptor idtd = { + .Length = sizeof(Entries) - 1, + .Entries = Entries, + }; + + void SetEntry(uint8_t Index, + void (*Base)(), + InterruptStackTableType InterruptStackTable, + InterruptGateType Gate, + InterruptRingType Ring, + bool Present, + uint16_t SegmentSelector) + { + Entries[Index].BaseLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)); + Entries[Index].BaseHigh = s_cst(uint64_t, ((uint64_t)Base >> 16 /* & 0xFFFF */)); + Entries[Index].SegmentSelector = SegmentSelector; + Entries[Index].Flags = Gate; + Entries[Index].Reserved1 = 0; + Entries[Index].Reserved2 = 0; + Entries[Index].Reserved3 = 0; + Entries[Index].InterruptStackTable = InterruptStackTable; + Entries[Index].Ring = Ring; + Entries[Index].Present = Present; + } + + extern "C" __naked __used __no_stack_protector __aligned(16) void ExceptionHandlerStub() + { + asm("cld\n" + "cli\n" + + "pushq %rax\n" + "pushq %rbx\n" + "pushq %rcx\n" + "pushq %rdx\n" + "pushq %rsi\n" + "pushq %rdi\n" + "pushq %rbp\n" + "pushq %r8\n" + "pushq %r9\n" + "pushq %r10\n" + "pushq %r11\n" + "pushq %r12\n" + "pushq %r13\n" + "pushq %r14\n" + "pushq %r15\n" + + "movq %rsp, %rdi\n" + "call ExceptionHandler\n" + + "popq %r15\n" + "popq %r14\n" + "popq %r13\n" + "popq %r12\n" + "popq %r11\n" + "popq %r10\n" + "popq %r9\n" + "popq %r8\n" + "popq %rbp\n" + "popq %rdi\n" + "popq %rsi\n" + "popq %rdx\n" + "popq %rcx\n" + "popq %rbx\n" + "popq %rax\n" + + "addq $16, %rsp\n" + + "iretq"); // pop CS RIP RFLAGS SS RSP + } + + extern "C" __naked __used __no_stack_protector __aligned(16) void InterruptHandlerStub() + { + asm("cld\n" + "cli\n" + + "pushq %rax\n" + "pushq %rbx\n" + "pushq %rcx\n" + "pushq %rdx\n" + "pushq %rsi\n" + "pushq %rdi\n" + "pushq %rbp\n" + "pushq %r8\n" + "pushq %r9\n" + "pushq %r10\n" + "pushq %r11\n" + "pushq %r12\n" + "pushq %r13\n" + "pushq %r14\n" + "pushq %r15\n" + + "movq %rsp, %rdi\n" + "call MainInterruptHandler\n" + + "popq %r15\n" + "popq %r14\n" + "popq %r13\n" + "popq %r12\n" + "popq %r11\n" + "popq %r10\n" + "popq %r9\n" + "popq %r8\n" + "popq %rbp\n" + "popq %rdi\n" + "popq %rsi\n" + "popq %rdx\n" + "popq %rcx\n" + "popq %rbx\n" + "popq %rax\n" + + "addq $16, %rsp\n" + + "sti\n" + "iretq"); // pop CS RIP RFLAGS SS RSP + } + +#pragma region Exceptions + +#define EXCEPTION_HANDLER(num) \ + __naked __used __no_stack_protector __aligned(16) static void InterruptHandler_##num() \ + { \ + asm("pushq $0\npushq $" #num "\n" \ + "jmp ExceptionHandlerStub"); \ + } + +#define EXCEPTION_ERROR_HANDLER(num) \ + __naked __used __no_stack_protector __aligned(16) static void InterruptHandler_##num() \ + { \ + asm("pushq $" #num "\n" \ + "jmp ExceptionHandlerStub"); \ + } + +#define INTERRUPT_HANDLER(num) \ + __naked __used __no_stack_protector __aligned(16) void InterruptHandler_##num() \ + { \ + asm("pushq $0\npushq $" #num "\n" \ + "jmp InterruptHandlerStub\n"); \ + } + + /* ISR */ + + EXCEPTION_HANDLER(0x0); + EXCEPTION_HANDLER(0x1); + EXCEPTION_HANDLER(0x2); + EXCEPTION_HANDLER(0x3); + EXCEPTION_HANDLER(0x4); + EXCEPTION_HANDLER(0x5); + EXCEPTION_HANDLER(0x6); + EXCEPTION_HANDLER(0x7); + EXCEPTION_ERROR_HANDLER(0x8); + EXCEPTION_HANDLER(0x9); + EXCEPTION_ERROR_HANDLER(0xa); + EXCEPTION_ERROR_HANDLER(0xb); + EXCEPTION_ERROR_HANDLER(0xc); + EXCEPTION_ERROR_HANDLER(0xd); + EXCEPTION_ERROR_HANDLER(0xe); + EXCEPTION_HANDLER(0xf); + EXCEPTION_ERROR_HANDLER(0x10); + EXCEPTION_HANDLER(0x11); + EXCEPTION_HANDLER(0x12); + EXCEPTION_HANDLER(0x13); + EXCEPTION_HANDLER(0x14); + EXCEPTION_HANDLER(0x15); + EXCEPTION_HANDLER(0x16); + EXCEPTION_HANDLER(0x17); + EXCEPTION_HANDLER(0x18); + EXCEPTION_HANDLER(0x19); + EXCEPTION_HANDLER(0x1a); + EXCEPTION_HANDLER(0x1b); + EXCEPTION_HANDLER(0x1c); + EXCEPTION_HANDLER(0x1d); + EXCEPTION_HANDLER(0x1e); + EXCEPTION_HANDLER(0x1f); + + /* IRQ */ + + INTERRUPT_HANDLER(0x20) + INTERRUPT_HANDLER(0x21) + INTERRUPT_HANDLER(0x22) + INTERRUPT_HANDLER(0x23) + INTERRUPT_HANDLER(0x24) + INTERRUPT_HANDLER(0x25) + INTERRUPT_HANDLER(0x26) + INTERRUPT_HANDLER(0x27) + INTERRUPT_HANDLER(0x28) + INTERRUPT_HANDLER(0x29) + INTERRUPT_HANDLER(0x2a) + INTERRUPT_HANDLER(0x2b) + INTERRUPT_HANDLER(0x2c) + INTERRUPT_HANDLER(0x2d) + INTERRUPT_HANDLER(0x2e) + INTERRUPT_HANDLER(0x2f) + + /* Reserved by OS */ + + INTERRUPT_HANDLER(0x30) + INTERRUPT_HANDLER(0x31) + INTERRUPT_HANDLER(0x32) + INTERRUPT_HANDLER(0x33) + INTERRUPT_HANDLER(0x34) + INTERRUPT_HANDLER(0x35) + INTERRUPT_HANDLER(0x36) + INTERRUPT_HANDLER(0x37) + INTERRUPT_HANDLER(0x38) + INTERRUPT_HANDLER(0x39) + INTERRUPT_HANDLER(0x3a) + INTERRUPT_HANDLER(0x3b) + INTERRUPT_HANDLER(0x3c) + INTERRUPT_HANDLER(0x3d) + + /* Free */ + + INTERRUPT_HANDLER(0x3e) + INTERRUPT_HANDLER(0x3f) + INTERRUPT_HANDLER(0x40) + INTERRUPT_HANDLER(0x41) + INTERRUPT_HANDLER(0x42) + INTERRUPT_HANDLER(0x43) + INTERRUPT_HANDLER(0x44) + INTERRUPT_HANDLER(0x45) + INTERRUPT_HANDLER(0x46) + INTERRUPT_HANDLER(0x47) + INTERRUPT_HANDLER(0x48) + INTERRUPT_HANDLER(0x49) + INTERRUPT_HANDLER(0x4a) + INTERRUPT_HANDLER(0x4b) + INTERRUPT_HANDLER(0x4c) + INTERRUPT_HANDLER(0x4d) + INTERRUPT_HANDLER(0x4e) + INTERRUPT_HANDLER(0x4f) + INTERRUPT_HANDLER(0x50) + INTERRUPT_HANDLER(0x51) + INTERRUPT_HANDLER(0x52) + INTERRUPT_HANDLER(0x53) + INTERRUPT_HANDLER(0x54) + INTERRUPT_HANDLER(0x55) + INTERRUPT_HANDLER(0x56) + INTERRUPT_HANDLER(0x57) + INTERRUPT_HANDLER(0x58) + INTERRUPT_HANDLER(0x59) + INTERRUPT_HANDLER(0x5a) + INTERRUPT_HANDLER(0x5b) + INTERRUPT_HANDLER(0x5c) + INTERRUPT_HANDLER(0x5d) + INTERRUPT_HANDLER(0x5e) + INTERRUPT_HANDLER(0x5f) + INTERRUPT_HANDLER(0x60) + INTERRUPT_HANDLER(0x61) + INTERRUPT_HANDLER(0x62) + INTERRUPT_HANDLER(0x63) + INTERRUPT_HANDLER(0x64) + INTERRUPT_HANDLER(0x65) + INTERRUPT_HANDLER(0x66) + INTERRUPT_HANDLER(0x67) + INTERRUPT_HANDLER(0x68) + INTERRUPT_HANDLER(0x69) + INTERRUPT_HANDLER(0x6a) + INTERRUPT_HANDLER(0x6b) + INTERRUPT_HANDLER(0x6c) + INTERRUPT_HANDLER(0x6d) + INTERRUPT_HANDLER(0x6e) + INTERRUPT_HANDLER(0x6f) + INTERRUPT_HANDLER(0x70) + INTERRUPT_HANDLER(0x71) + INTERRUPT_HANDLER(0x72) + INTERRUPT_HANDLER(0x73) + INTERRUPT_HANDLER(0x74) + INTERRUPT_HANDLER(0x75) + INTERRUPT_HANDLER(0x76) + INTERRUPT_HANDLER(0x77) + INTERRUPT_HANDLER(0x78) + INTERRUPT_HANDLER(0x79) + INTERRUPT_HANDLER(0x7a) + INTERRUPT_HANDLER(0x7b) + INTERRUPT_HANDLER(0x7c) + INTERRUPT_HANDLER(0x7d) + INTERRUPT_HANDLER(0x7e) + INTERRUPT_HANDLER(0x7f) + INTERRUPT_HANDLER(0x80) + INTERRUPT_HANDLER(0x81) + INTERRUPT_HANDLER(0x82) + INTERRUPT_HANDLER(0x83) + INTERRUPT_HANDLER(0x84) + INTERRUPT_HANDLER(0x85) + INTERRUPT_HANDLER(0x86) + INTERRUPT_HANDLER(0x87) + INTERRUPT_HANDLER(0x88) + INTERRUPT_HANDLER(0x89) + INTERRUPT_HANDLER(0x8a) + INTERRUPT_HANDLER(0x8b) + INTERRUPT_HANDLER(0x8c) + INTERRUPT_HANDLER(0x8d) + INTERRUPT_HANDLER(0x8e) + INTERRUPT_HANDLER(0x8f) + INTERRUPT_HANDLER(0x90) + INTERRUPT_HANDLER(0x91) + INTERRUPT_HANDLER(0x92) + INTERRUPT_HANDLER(0x93) + INTERRUPT_HANDLER(0x94) + INTERRUPT_HANDLER(0x95) + INTERRUPT_HANDLER(0x96) + INTERRUPT_HANDLER(0x97) + INTERRUPT_HANDLER(0x98) + INTERRUPT_HANDLER(0x99) + INTERRUPT_HANDLER(0x9a) + INTERRUPT_HANDLER(0x9b) + INTERRUPT_HANDLER(0x9c) + INTERRUPT_HANDLER(0x9d) + INTERRUPT_HANDLER(0x9e) + INTERRUPT_HANDLER(0x9f) + INTERRUPT_HANDLER(0xa0) + INTERRUPT_HANDLER(0xa1) + INTERRUPT_HANDLER(0xa2) + INTERRUPT_HANDLER(0xa3) + INTERRUPT_HANDLER(0xa4) + INTERRUPT_HANDLER(0xa5) + INTERRUPT_HANDLER(0xa6) + INTERRUPT_HANDLER(0xa7) + INTERRUPT_HANDLER(0xa8) + INTERRUPT_HANDLER(0xa9) + INTERRUPT_HANDLER(0xaa) + INTERRUPT_HANDLER(0xab) + INTERRUPT_HANDLER(0xac) + INTERRUPT_HANDLER(0xad) + INTERRUPT_HANDLER(0xae) + INTERRUPT_HANDLER(0xaf) + INTERRUPT_HANDLER(0xb0) + INTERRUPT_HANDLER(0xb1) + INTERRUPT_HANDLER(0xb2) + INTERRUPT_HANDLER(0xb3) + INTERRUPT_HANDLER(0xb4) + INTERRUPT_HANDLER(0xb5) + INTERRUPT_HANDLER(0xb6) + INTERRUPT_HANDLER(0xb7) + INTERRUPT_HANDLER(0xb8) + INTERRUPT_HANDLER(0xb9) + INTERRUPT_HANDLER(0xba) + INTERRUPT_HANDLER(0xbb) + INTERRUPT_HANDLER(0xbc) + INTERRUPT_HANDLER(0xbd) + INTERRUPT_HANDLER(0xbe) + INTERRUPT_HANDLER(0xbf) + INTERRUPT_HANDLER(0xc0) + INTERRUPT_HANDLER(0xc1) + INTERRUPT_HANDLER(0xc2) + INTERRUPT_HANDLER(0xc3) + INTERRUPT_HANDLER(0xc4) + INTERRUPT_HANDLER(0xc5) + INTERRUPT_HANDLER(0xc6) + INTERRUPT_HANDLER(0xc7) + INTERRUPT_HANDLER(0xc8) + INTERRUPT_HANDLER(0xc9) + INTERRUPT_HANDLER(0xca) + INTERRUPT_HANDLER(0xcb) + INTERRUPT_HANDLER(0xcc) + INTERRUPT_HANDLER(0xcd) + INTERRUPT_HANDLER(0xce) + INTERRUPT_HANDLER(0xcf) + INTERRUPT_HANDLER(0xd0) + INTERRUPT_HANDLER(0xd1) + INTERRUPT_HANDLER(0xd2) + INTERRUPT_HANDLER(0xd3) + INTERRUPT_HANDLER(0xd4) + INTERRUPT_HANDLER(0xd5) + INTERRUPT_HANDLER(0xd6) + INTERRUPT_HANDLER(0xd7) + INTERRUPT_HANDLER(0xd8) + INTERRUPT_HANDLER(0xd9) + INTERRUPT_HANDLER(0xda) + INTERRUPT_HANDLER(0xdb) + INTERRUPT_HANDLER(0xdc) + INTERRUPT_HANDLER(0xdd) + INTERRUPT_HANDLER(0xde) + INTERRUPT_HANDLER(0xdf) + INTERRUPT_HANDLER(0xe0) + INTERRUPT_HANDLER(0xe1) + INTERRUPT_HANDLER(0xe2) + INTERRUPT_HANDLER(0xe3) + INTERRUPT_HANDLER(0xe4) + INTERRUPT_HANDLER(0xe5) + INTERRUPT_HANDLER(0xe6) + INTERRUPT_HANDLER(0xe7) + INTERRUPT_HANDLER(0xe8) + INTERRUPT_HANDLER(0xe9) + INTERRUPT_HANDLER(0xea) + INTERRUPT_HANDLER(0xeb) + INTERRUPT_HANDLER(0xec) + INTERRUPT_HANDLER(0xed) + INTERRUPT_HANDLER(0xee) + INTERRUPT_HANDLER(0xef) + INTERRUPT_HANDLER(0xf0) + INTERRUPT_HANDLER(0xf1) + INTERRUPT_HANDLER(0xf2) + INTERRUPT_HANDLER(0xf3) + INTERRUPT_HANDLER(0xf4) + INTERRUPT_HANDLER(0xf5) + INTERRUPT_HANDLER(0xf6) + INTERRUPT_HANDLER(0xf7) + INTERRUPT_HANDLER(0xf8) + INTERRUPT_HANDLER(0xf9) + INTERRUPT_HANDLER(0xfa) + INTERRUPT_HANDLER(0xfb) + INTERRUPT_HANDLER(0xfc) + INTERRUPT_HANDLER(0xfd) + INTERRUPT_HANDLER(0xfe) + INTERRUPT_HANDLER(0xff) + +#pragma endregion Exceptions + + void Init(int Core) + { + if (Core == 0) /* Disable PIC using BSP */ + { + // PIC + outb(0x20, 0x10 | 0x1); + outb(0x80, 0); + outb(0xA0, 0x10 | 0x10); + outb(0x80, 0); + + outb(0x21, 0x20); + outb(0x80, 0); + outb(0xA1, 0x28); + outb(0x80, 0); + + outb(0x21, 0x04); + outb(0x80, 0); + outb(0xA1, 0x02); + outb(0x80, 0); + + outb(0x21, 1); + outb(0x80, 0); + outb(0xA1, 1); + outb(0x80, 0); + + // Masking and disabling PIC + outb(0x21, 0xff); + outb(0x80, 0); + outb(0xA1, 0xff); + } + + /* ISR */ + +#ifdef DEBUG + // if (!DebuggerIsAttached) + if (true) + { +#endif + SetEntry(0x0, InterruptHandler_0x0, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1, InterruptHandler_0x1, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2, InterruptHandler_0x2, IST2, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x3, InterruptHandler_0x3, IST1, TRAP_32BIT, RING3, (!DebuggerIsAttached), GDT_KERNEL_CODE); /* Do not handle breakpoints if we are debugging the kernel. */ + SetEntry(0x4, InterruptHandler_0x4, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5, InterruptHandler_0x5, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6, InterruptHandler_0x6, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7, InterruptHandler_0x7, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8, InterruptHandler_0x8, IST3, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9, InterruptHandler_0x9, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa, InterruptHandler_0xa, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb, InterruptHandler_0xb, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc, InterruptHandler_0xc, IST3, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd, InterruptHandler_0xd, IST3, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe, InterruptHandler_0xe, IST3, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf, InterruptHandler_0xf, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x10, InterruptHandler_0x10, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x11, InterruptHandler_0x11, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x12, InterruptHandler_0x12, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x13, InterruptHandler_0x13, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x14, InterruptHandler_0x14, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x15, InterruptHandler_0x15, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x16, InterruptHandler_0x16, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x17, InterruptHandler_0x17, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x18, InterruptHandler_0x18, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x19, InterruptHandler_0x19, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1a, InterruptHandler_0x1a, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1b, InterruptHandler_0x1b, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1c, InterruptHandler_0x1c, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1d, InterruptHandler_0x1d, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1e, InterruptHandler_0x1e, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x1f, InterruptHandler_0x1f, IST1, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); +#ifdef DEBUG + } + else + KPrint("\eFFA500The debugger is attached, not setting up the ISR."); +#endif + + /* IRQ */ + + SetEntry(0x20, InterruptHandler_0x20, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x21, InterruptHandler_0x21, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x22, InterruptHandler_0x22, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x23, InterruptHandler_0x23, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x24, InterruptHandler_0x24, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x25, InterruptHandler_0x25, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x26, InterruptHandler_0x26, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x27, InterruptHandler_0x27, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x28, InterruptHandler_0x28, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x29, InterruptHandler_0x29, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2a, InterruptHandler_0x2a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2b, InterruptHandler_0x2b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2c, InterruptHandler_0x2c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2d, InterruptHandler_0x2d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2e, InterruptHandler_0x2e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x2f, InterruptHandler_0x2f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + + /* Reserved by OS */ + + SetEntry(0x30, InterruptHandler_0x30, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x31, InterruptHandler_0x31, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x32, InterruptHandler_0x32, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x33, InterruptHandler_0x33, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x34, InterruptHandler_0x34, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x35, InterruptHandler_0x35, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x36, InterruptHandler_0x36, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x37, InterruptHandler_0x37, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x38, InterruptHandler_0x38, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x39, InterruptHandler_0x39, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x3a, InterruptHandler_0x3a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x3b, InterruptHandler_0x3b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x3c, InterruptHandler_0x3c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x3d, InterruptHandler_0x3d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + + /* Free */ + + SetEntry(0x3e, InterruptHandler_0x3e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x3f, InterruptHandler_0x3f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x40, InterruptHandler_0x40, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x41, InterruptHandler_0x41, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x42, InterruptHandler_0x42, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x43, InterruptHandler_0x43, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x44, InterruptHandler_0x44, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x45, InterruptHandler_0x45, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x46, InterruptHandler_0x46, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x47, InterruptHandler_0x47, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x48, InterruptHandler_0x48, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x49, InterruptHandler_0x49, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x4a, InterruptHandler_0x4a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x4b, InterruptHandler_0x4b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x4c, InterruptHandler_0x4c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x4d, InterruptHandler_0x4d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x4e, InterruptHandler_0x4e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x4f, InterruptHandler_0x4f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x50, InterruptHandler_0x50, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x51, InterruptHandler_0x51, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x52, InterruptHandler_0x52, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x53, InterruptHandler_0x53, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x54, InterruptHandler_0x54, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x55, InterruptHandler_0x55, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x56, InterruptHandler_0x56, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x57, InterruptHandler_0x57, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x58, InterruptHandler_0x58, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x59, InterruptHandler_0x59, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5a, InterruptHandler_0x5a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5b, InterruptHandler_0x5b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5c, InterruptHandler_0x5c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5d, InterruptHandler_0x5d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5e, InterruptHandler_0x5e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x5f, InterruptHandler_0x5f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x60, InterruptHandler_0x60, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x61, InterruptHandler_0x61, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x62, InterruptHandler_0x62, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x63, InterruptHandler_0x63, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x64, InterruptHandler_0x64, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x65, InterruptHandler_0x65, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x66, InterruptHandler_0x66, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x67, InterruptHandler_0x67, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x68, InterruptHandler_0x68, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x69, InterruptHandler_0x69, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6a, InterruptHandler_0x6a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6b, InterruptHandler_0x6b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6c, InterruptHandler_0x6c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6d, InterruptHandler_0x6d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6e, InterruptHandler_0x6e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x6f, InterruptHandler_0x6f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x70, InterruptHandler_0x70, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x71, InterruptHandler_0x71, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x72, InterruptHandler_0x72, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x73, InterruptHandler_0x73, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x74, InterruptHandler_0x74, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x75, InterruptHandler_0x75, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x76, InterruptHandler_0x76, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x77, InterruptHandler_0x77, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x78, InterruptHandler_0x78, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x79, InterruptHandler_0x79, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7a, InterruptHandler_0x7a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7b, InterruptHandler_0x7b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7c, InterruptHandler_0x7c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7d, InterruptHandler_0x7d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7e, InterruptHandler_0x7e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x7f, InterruptHandler_0x7f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x80, InterruptHandler_0x80, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x81, InterruptHandler_0x81, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x82, InterruptHandler_0x82, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x83, InterruptHandler_0x83, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x84, InterruptHandler_0x84, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x85, InterruptHandler_0x85, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x86, InterruptHandler_0x86, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x87, InterruptHandler_0x87, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x88, InterruptHandler_0x88, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x89, InterruptHandler_0x89, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8a, InterruptHandler_0x8a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8b, InterruptHandler_0x8b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8c, InterruptHandler_0x8c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8d, InterruptHandler_0x8d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8e, InterruptHandler_0x8e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x8f, InterruptHandler_0x8f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x90, InterruptHandler_0x90, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x91, InterruptHandler_0x91, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x92, InterruptHandler_0x92, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x93, InterruptHandler_0x93, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x94, InterruptHandler_0x94, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x95, InterruptHandler_0x95, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x96, InterruptHandler_0x96, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x97, InterruptHandler_0x97, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x98, InterruptHandler_0x98, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x99, InterruptHandler_0x99, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9a, InterruptHandler_0x9a, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9b, InterruptHandler_0x9b, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9c, InterruptHandler_0x9c, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9d, InterruptHandler_0x9d, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9e, InterruptHandler_0x9e, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0x9f, InterruptHandler_0x9f, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa0, InterruptHandler_0xa0, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa1, InterruptHandler_0xa1, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa2, InterruptHandler_0xa2, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa3, InterruptHandler_0xa3, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa4, InterruptHandler_0xa4, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa5, InterruptHandler_0xa5, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa6, InterruptHandler_0xa6, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa7, InterruptHandler_0xa7, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa8, InterruptHandler_0xa8, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xa9, InterruptHandler_0xa9, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xaa, InterruptHandler_0xaa, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xab, InterruptHandler_0xab, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xac, InterruptHandler_0xac, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xad, InterruptHandler_0xad, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xae, InterruptHandler_0xae, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xaf, InterruptHandler_0xaf, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb0, InterruptHandler_0xb0, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb1, InterruptHandler_0xb1, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb2, InterruptHandler_0xb2, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb3, InterruptHandler_0xb3, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb4, InterruptHandler_0xb4, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb5, InterruptHandler_0xb5, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb6, InterruptHandler_0xb6, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb7, InterruptHandler_0xb7, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb8, InterruptHandler_0xb8, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xb9, InterruptHandler_0xb9, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xba, InterruptHandler_0xba, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xbb, InterruptHandler_0xbb, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xbc, InterruptHandler_0xbc, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xbd, InterruptHandler_0xbd, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xbe, InterruptHandler_0xbe, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xbf, InterruptHandler_0xbf, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc0, InterruptHandler_0xc0, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc1, InterruptHandler_0xc1, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc2, InterruptHandler_0xc2, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc3, InterruptHandler_0xc3, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc4, InterruptHandler_0xc4, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc5, InterruptHandler_0xc5, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc6, InterruptHandler_0xc6, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc7, InterruptHandler_0xc7, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc8, InterruptHandler_0xc8, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xc9, InterruptHandler_0xc9, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xca, InterruptHandler_0xca, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xcb, InterruptHandler_0xcb, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xcc, InterruptHandler_0xcc, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xcd, InterruptHandler_0xcd, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xce, InterruptHandler_0xce, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xcf, InterruptHandler_0xcf, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd0, InterruptHandler_0xd0, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd1, InterruptHandler_0xd1, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd2, InterruptHandler_0xd2, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd3, InterruptHandler_0xd3, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd4, InterruptHandler_0xd4, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd5, InterruptHandler_0xd5, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd6, InterruptHandler_0xd6, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd7, InterruptHandler_0xd7, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd8, InterruptHandler_0xd8, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xd9, InterruptHandler_0xd9, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xda, InterruptHandler_0xda, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xdb, InterruptHandler_0xdb, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xdc, InterruptHandler_0xdc, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xdd, InterruptHandler_0xdd, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xde, InterruptHandler_0xde, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xdf, InterruptHandler_0xdf, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe0, InterruptHandler_0xe0, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe1, InterruptHandler_0xe1, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe2, InterruptHandler_0xe2, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe3, InterruptHandler_0xe3, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe4, InterruptHandler_0xe4, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe5, InterruptHandler_0xe5, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe6, InterruptHandler_0xe6, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe7, InterruptHandler_0xe7, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe8, InterruptHandler_0xe8, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xe9, InterruptHandler_0xe9, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xea, InterruptHandler_0xea, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xeb, InterruptHandler_0xeb, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xec, InterruptHandler_0xec, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xed, InterruptHandler_0xed, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xee, InterruptHandler_0xee, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xef, InterruptHandler_0xef, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf0, InterruptHandler_0xf0, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf1, InterruptHandler_0xf1, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf2, InterruptHandler_0xf2, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf3, InterruptHandler_0xf3, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf4, InterruptHandler_0xf4, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf5, InterruptHandler_0xf5, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf6, InterruptHandler_0xf6, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf7, InterruptHandler_0xf7, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf8, InterruptHandler_0xf8, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xf9, InterruptHandler_0xf9, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xfa, InterruptHandler_0xfa, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xfb, InterruptHandler_0xfb, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xfc, InterruptHandler_0xfc, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xfd, InterruptHandler_0xfd, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xfe, InterruptHandler_0xfe, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + SetEntry(0xff, InterruptHandler_0xff, IST0, TRAP_32BIT, RING0, true, GDT_KERNEL_CODE); + CPU::x64::lidt(&idtd); + } +} diff --git a/Kernel/Architecture/amd64/cpu/SMPTrampoline.asm b/Kernel/Architecture/amd64/cpu/SMPTrampoline.asm new file mode 100644 index 00000000..ca690e67 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/SMPTrampoline.asm @@ -0,0 +1,136 @@ +; This file is part of Fennix Kernel. +; +; Fennix Kernel is free software: you can redistribute it and/or +; modify it under the terms of the GNU General Public License as +; published by the Free Software Foundation, either version 3 of +; the License, or (at your option) any later version. +; +; Fennix Kernel is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with Fennix Kernel. If not, see . + +; This has to be the same as enum SMPTrampolineAddress. +TRAMPOLINE_PAGE_TABLE equ 0x500 +TRAMPOLINE_START_ADDR equ 0x520 +TRAMPOLINE_STACK equ 0x570 +TRAMPOLINE_GDT equ 0x580 +TRAMPOLINE_IDT equ 0x590 +TRAMPOLINE_CORE equ 0x600 +TRAMPOLINE_START equ 0x2000 + +[bits 16] + +extern StartCPU +global _trampoline_start +_trampoline_start: + cli + mov ax, 0x0 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + o32 lgdt [ProtectedMode_gdtr - _trampoline_start + TRAMPOLINE_START] + mov eax, cr0 + or al, 0x1 + mov cr0, eax + jmp 0x8:(Trampoline32 - _trampoline_start + TRAMPOLINE_START) + +[bits 32] +section .text +Trampoline32: + mov bx, 0x10 + mov ds, bx + mov es, bx + mov ss, bx + mov eax, dword [TRAMPOLINE_PAGE_TABLE] + mov cr3, eax + mov eax, cr4 + or eax, 1 << 5 ; Set the PAE-bit, which is the 6th bit (bit 5). + or eax, 1 << 7 + mov cr4, eax + mov ecx, 0xc0000080 + rdmsr + or eax,1 << 8 ; LME + wrmsr + mov eax, cr0 + or eax, 1 << 31 + mov cr0, eax + lgdt [LongMode_gdtr - _trampoline_start + TRAMPOLINE_START] + jmp 0x8:(Trampoline64 - _trampoline_start + TRAMPOLINE_START) + +[bits 64] +Trampoline64: + mov ax, 0x10 + mov ds, ax + mov es, ax + mov ss, ax + mov ax, 0x0 + mov fs, ax + mov gs, ax + lgdt [TRAMPOLINE_GDT] + lidt [TRAMPOLINE_IDT] + mov rsp, [TRAMPOLINE_STACK] + mov rbp, 0x0 ; Terminate stack traces here. + ; Reset RFLAGS. + push 0x0 + popf + mov rax, qword vcode64 + call vcode64 + +vcode64: + push rbp + ; Set up SSE + mov rax, cr0 + ; btr eax, 2 + ; bts eax, 1 + ; mov cr0, rax + mov rax, cr4 + bts eax, 9 + bts eax, 10 + mov cr4, rax + mov rax, qword TrampolineExit + call rax + +align 16 +LongMode_gdtr: + dw LongModeGDTEnd - LongModeGDTStart - 1 + dq LongModeGDTStart - _trampoline_start + TRAMPOLINE_START + +align 16 +LongModeGDTStart: + dq 0 ; NULL segment + dq 0x00AF98000000FFFF ; Code segment + dq 0x00CF92000000FFFF ; Data segment +LongModeGDTEnd: + +align 16 +ProtectedMode_gdtr: + dw ProtectedModeGDTEnd - ProtectedModeGDTStart - 1 + dd ProtectedModeGDTStart - _trampoline_start + TRAMPOLINE_START + +align 16 +ProtectedModeGDTStart: + dq 0 ; NULL segment + dq 0x00CF9A000000FFFF ; Code segment + dq 0x00CF92000000FFFF ; Data segment +ProtectedModeGDTEnd: + +align 16 +ProtectedMode_idtr: + dw 0 + dd 0 + dd 0 + align 16 + +global _trampoline_end +_trampoline_end: + +TrampolineExit: + call StartCPU + +times 512 - ($-$$) db 0 diff --git a/Kernel/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp b/Kernel/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp new file mode 100644 index 00000000..fce30d26 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp @@ -0,0 +1,141 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include + +#include "../../../kernel.h" +#include "../acpi.hpp" +#include "apic.hpp" + +extern "C" uint64_t _trampoline_start, _trampoline_end; + +/* https://wiki.osdev.org/Memory_Map_(x86) */ +enum SMPTrampolineAddress +{ + PAGE_TABLE = 0x500, + START_ADDR = 0x520, + STACK = 0x570, + GDT = 0x580, + IDT = 0x590, + CORE = 0x600, + TRAMPOLINE_START = 0x2000 +}; + +std::atomic_bool CPUEnabled = false; + +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +static __aligned(PAGE_SIZE) CPUData CPUs[MAX_CPU] = {0}; + +SafeFunction CPUData *GetCPU(long id) { return &CPUs[id]; } + +SafeFunction CPUData *GetCurrentCPU() +{ + if (unlikely(!Interrupts::apic[0])) + return &CPUs[0]; /* No APIC means we are on the BSP. */ + + int CoreID = ((APIC::APIC *)Interrupts::apic[0])->Read(APIC::APIC_ID) >> 24; + + if (unlikely((&CPUs[CoreID])->IsActive != true)) + { + error("CPU %d is not active!", CoreID); + assert((&CPUs[0])->IsActive == true); /* We can't continue without the BSP. */ + return &CPUs[0]; + } + + assert((&CPUs[CoreID])->Checksum == CPU_DATA_CHECKSUM); /* This should never happen. */ + return &CPUs[CoreID]; +} + +extern "C" void StartCPU() +{ + CPU::Interrupts(CPU::Disable); + int CoreID = (int)*reinterpret_cast(CORE); + CPU::InitializeFeatures(CoreID); + // Initialize GDT and IDT + Interrupts::Initialize(CoreID); + Interrupts::Enable(CoreID); + Interrupts::InitializeTimer(CoreID); + asmv("mov %0, %%rsp" ::"r"((&CPUs[CoreID])->Stack)); + + CPU::Interrupts(CPU::Enable); + KPrint("\e058C19CPU \e8888FF%d \e058C19is online", CoreID); + CPUEnabled.store(true, std::memory_order_release); + CPU::Halt(true); +} + +namespace SMP +{ + int CPUCores = 0; + + void Initialize(void *madt) + { + int Cores = ((ACPI::MADT *)madt)->CPUCores + 1; + + if (Config.Cores > ((ACPI::MADT *)madt)->CPUCores + 1) + KPrint("More cores requested than available. Using %d cores", ((ACPI::MADT *)madt)->CPUCores + 1); + else if (Config.Cores != 0) + Cores = Config.Cores; + + CPUCores = Cores; + + uint64_t TrampolineLength = (uintptr_t)&_trampoline_end - (uintptr_t)&_trampoline_start; + Memory::Virtual().Map(0x0, 0x0, Memory::PTFlag::RW); + /* We reserved the TRAMPOLINE_START address inside Physical class. */ + Memory::Virtual().Map((void *)TRAMPOLINE_START, (void *)TRAMPOLINE_START, TrampolineLength, Memory::PTFlag::RW); + memcpy((void *)TRAMPOLINE_START, &_trampoline_start, TrampolineLength); + debug("Trampoline address: %#lx-%#lx", TRAMPOLINE_START, TRAMPOLINE_START + TrampolineLength); + + void *CPUTmpStack = KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1)); + asmv("sgdt [0x580]\n" + "sidt [0x590]\n"); + VPOKE(uintptr_t, STACK) = (uintptr_t)CPUTmpStack + STACK_SIZE; + VPOKE(uintptr_t, PAGE_TABLE) = (uintptr_t)KernelPageTable; + VPOKE(uint64_t, START_ADDR) = (uintptr_t)&StartCPU; + + for (int i = 0; i < Cores; i++) + { + debug("Initializing CPU %d", i); + if ((((APIC::APIC *)Interrupts::apic[0])->Read(APIC::APIC_ID) >> 24) != ((ACPI::MADT *)madt)->lapic[i]->ACPIProcessorId) + { + VPOKE(int, CORE) = i; + + ((APIC::APIC *)Interrupts::apic[0])->Write(APIC::APIC_ICRHI, (((ACPI::MADT *)madt)->lapic[i]->APICId << 24)); + ((APIC::APIC *)Interrupts::apic[0])->Write(APIC::APIC_ICRLO, 0x500); + + ((APIC::APIC *)Interrupts::apic[0])->SendInitIPI(((ACPI::MADT *)madt)->lapic[i]->APICId); + ((APIC::APIC *)Interrupts::apic[0])->SendStartupIPI(((ACPI::MADT *)madt)->lapic[i]->APICId, TRAMPOLINE_START); + + while (!CPUEnabled.load(std::memory_order_acquire)) + CPU::Pause(); + CPUEnabled.store(false, std::memory_order_release); + trace("CPU %d loaded.", ((ACPI::MADT *)madt)->lapic[i]->APICId); + } + else + KPrint("\e058C19CPU \e8888FF%d \e058C19is the BSP", ((ACPI::MADT *)madt)->lapic[i]->APICId); + } + + KernelAllocator.FreePages(CPUTmpStack, TO_PAGES(STACK_SIZE + 1)); + /* We are going to unmap the page after we are done with it. */ + Memory::Virtual().Unmap(0x0); + } +} diff --git a/Kernel/Architecture/amd64/cpu/apic.hpp b/Kernel/Architecture/amd64/cpu/apic.hpp new file mode 100644 index 00000000..e93c33a0 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/apic.hpp @@ -0,0 +1,354 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_APIC_H__ +#define __FENNIX_KERNEL_APIC_H__ + +#include + +#include +#include + +namespace APIC +{ + enum APICRegisters + { + // source from: https://github.com/pdoane/osdev/blob/master/intr/local_apic.c + APIC_ID = 0x20, // Local APIC ID + APIC_VER = 0x30, // Local APIC Version + APIC_TPR = 0x80, // Task Priority + APIC_APR = 0x90, // Arbitration Priority + APIC_PPR = 0xA0, // Processor Priority + APIC_EOI = 0xB0, // EOI + APIC_RRD = 0xC0, // Remote Read + APIC_LDR = 0xD0, // Logical Destination + APIC_DFR = 0xE0, // Destination Format + APIC_SVR = 0xF0, // Spurious Interrupt Vector + APIC_ISR = 0x100, // In-Service (8 registers) + APIC_TMR = 0x180, // Trigger Mode (8 registers) + APIC_IRR = 0x200, // Interrupt Request (8 registers) + APIC_ESR = 0x280, // Error Status + APIC_ICRLO = 0x300, // Interrupt Command + APIC_ICRHI = 0x310, // Interrupt Command [63:32] + APIC_TIMER = 0x320, // LVT Timer + APIC_THERMAL = 0x330, // LVT Thermal Sensor + APIC_PERF = 0x340, // LVT Performance Counter + APIC_LINT0 = 0x350, // LVT LINT0 + APIC_LINT1 = 0x360, // LVT LINT1 + APIC_ERROR = 0x370, // LVT Error + APIC_TICR = 0x380, // Initial Count (for Timer) + APIC_TCCR = 0x390, // Current Count (for Timer) + APIC_TDCR = 0x3E0, // Divide Configuration (for Timer) + }; + + enum IOAPICRegisters + { + GetIOAPICVersion = 0x1 + }; + + enum IOAPICFlags + { + ActiveHighLow = 2, + EdgeLevel = 8 + }; + + enum APICDeliveryMode + { + Fixed = 0b000, + LowestPriority = 0b001, /* Reserved */ + SMI = 0b010, + APIC_DELIVERY_MODE_RESERVED0 = 0b011, /* Reserved */ + NMI = 0b100, + INIT = 0b101, + Startup = 0b110, + ExtINT = 0b111 /* Reserved */ + }; + + enum APICDestinationMode + { + Physical = 0b0, + Logical = 0b1 + }; + + enum APICDeliveryStatus + { + Idle = 0b0, + SendPending = 0b1 + }; + + enum APICLevel + { + DeAssert = 0b0, + Assert = 0b1 + }; + + enum APICTriggerMode + { + Edge = 0b0, + Level = 0b1 + }; + + enum APICDestinationShorthand + { + NoShorthand = 0b00, + Self = 0b01, + AllIncludingSelf = 0b10, + AllExcludingSelf = 0b11 + }; + + enum LVTTimerDivide + { + DivideBy2 = 0b000, + DivideBy4 = 0b001, + DivideBy8 = 0b010, + DivideBy16 = 0b011, + DivideBy32 = 0b100, + DivideBy64 = 0b101, + DivideBy128 = 0b110, + DivideBy1 = 0b111 + }; + + enum LVTTimerMask + { + Unmasked = 0b0, + Masked = 0b1 + }; + + enum LVTTimerMode + { + OneShot = 0b00, + Periodic = 0b01, + TSCDeadline = 0b10 + }; + + typedef union + { + struct + { + /** @brief Interrupt Vector */ + uint64_t Vector : 8; + /** @brief Reserved */ + uint64_t Reserved0 : 4; + /** + * @brief Delivery Status + * + * 0: Idle + * 1: Send Pending + */ + uint64_t DeliveryStatus : 1; + /** @brief Reserved */ + uint64_t Reserved1 : 3; + /** + * @brief Mask + * + * 0: Not masked + * 1: Masked + */ + uint64_t Mask : 1; + /** @brief Timer Mode + * + * 0: One-shot + * 1: Periodic + * 2: TSC-Deadline + */ + uint64_t TimerMode : 1; + /** @brief Reserved */ + uint64_t Reserved2 : 14; + }; + uint64_t raw; + } __packed LVTTimer; + + typedef union + { + struct + { + /** @brief Spurious Vector */ + uint64_t Vector : 8; + /** @brief Enable or disable APIC software */ + uint64_t Software : 1; + /** @brief Focus Processor Checking */ + uint64_t FocusProcessorChecking : 1; + /** @brief Reserved */ + uint64_t Reserved : 2; + /** @brief Disable EOI Broadcast */ + uint64_t DisableEOIBroadcast : 1; + /** @brief Reserved */ + uint64_t Reserved1 : 19; + }; + uint64_t raw; + } __packed Spurious; + + typedef union + { + struct + { + /** @brief Interrupt Vector */ + uint64_t Vector : 8; + /** @brief Delivery Mode */ + uint64_t DeliveryMode : 3; + /** @brief Destination Mode + * + * 0: Physical + * 1: Logical + */ + uint64_t DestinationMode : 1; + /** @brief Delivery Status + * + * @note Reserved when in x2APIC mode + */ + uint64_t DeliveryStatus : 1; + /** @brief Reserved */ + uint64_t Reserved0 : 1; + /** @brief Level + * + * 0: Deassert + * 1: Assert + */ + uint64_t Level : 1; + /** @brief Trigger Mode + * + * 0: Edge + * 1: Level + */ + uint64_t TriggerMode : 1; + /** @brief Reserved */ + uint64_t Reserved1 : 2; + /** @brief Destination Shorthand + * + * 0: No shorthand + * 1: Self + * 2: All including self + * 3: All excluding self + */ + uint64_t DestinationShorthand : 2; + /** @brief Reserved */ + uint64_t Reserved2 : 12; + }; + uint64_t raw; + } __packed InterruptCommandRegisterLow; + + typedef union + { + struct + { + /** @brief Reserved */ + uint64_t Reserved0 : 24; + /** @brief Destination */ + uint64_t Destination : 8; + }; + uint64_t raw; + } __packed InterruptCommandRegisterHigh; + + typedef union + { + struct + { + /** @brief Interrupt Vector */ + uint64_t Vector : 8; + /** @brief Delivery Mode */ + uint64_t DeliveryMode : 3; + /** @brief Destination Mode + * + * 0: Physical + * 1: Logical + */ + uint64_t DestinationMode : 1; + /** @brief Delivery Status */ + uint64_t DeliveryStatus : 1; + /** @brief Interrupt Input Pin Polarity + * + * 0: Active High + * 1: Active Low + */ + uint64_t Polarity : 1; + /** @brief Remote IRR */ + uint64_t RemoteIRR : 1; + /** @brief Trigger Mode + * + * 0: Edge + * 1: Level + */ + uint64_t TriggerMode : 1; + /** @brief Mask */ + uint64_t Mask : 1; + /** @brief Reserved */ + uint64_t Reserved0 : 15; + /** @brief Reserved */ + uint64_t Reserved1 : 24; + /** @brief Destination */ + uint64_t DestinationID : 8; + }; + struct + { + uint64_t Low; + uint64_t High; + } split; + uint64_t raw; + } __packed IOAPICRedirectEntry; + + typedef union + { + struct + { + uint64_t Version : 8; + uint64_t Reserved : 8; + uint64_t MaximumRedirectionEntry : 8; + uint64_t Reserved2 : 8; + }; + uint64_t raw; + } __packed IOAPICVersion; + + class APIC + { + private: + bool x2APICSupported = false; + uint64_t APICBaseAddress = 0; + + public: + uint32_t Read(uint32_t Register); + void Write(uint32_t Register, uint32_t Value); + void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value); + uint32_t IORead(uint64_t Base, uint32_t Register); + void EOI(); + void RedirectIRQs(int CPU = 0); + void WaitForIPI(); + void IPI(int CPU, InterruptCommandRegisterLow icr); + void SendInitIPI(int CPU); + void SendStartupIPI(int CPU, uint64_t StartupAddress); + uint32_t IOGetMaxRedirect(uint32_t APICID); + void RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status); + void RedirectIRQ(int CPU, uint16_t IRQ, int Status); + APIC(int Core); + ~APIC(); + }; + + class Timer : public Interrupts::Handler + { + private: + APIC *lapic; + uint64_t Ticks = 0; + void OnInterruptReceived(CPU::x64::TrapFrame *Frame); + + public: + uint64_t GetTicks() { return Ticks; } + void OneShot(uint32_t Vector, uint64_t Miliseconds); + Timer(APIC *apic); + ~Timer(); + }; +} + +#endif // !__FENNIX_KERNEL_APIC_H__ diff --git a/Kernel/Architecture/amd64/cpu/gdt.hpp b/Kernel/Architecture/amd64/cpu/gdt.hpp new file mode 100644 index 00000000..7eec05d6 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/gdt.hpp @@ -0,0 +1,164 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_GDT_H__ +#define __FENNIX_KERNEL_GDT_H__ + +#include + +namespace GlobalDescriptorTable +{ + /** @brief The GDT Access Table + * @details For more information, see https://wiki.osdev.org/Global_Descriptor_Table + */ + union GlobalDescriptorTableAccess + { + struct + { + /** @brief Access bit. + * @note The CPU sets this bit to 1 when the segment is accessed. + */ + uint64_t A : 1; + + /** @brief Readable bit for code segments, writable bit for data segments. + * @details For code segments, this bit must be 1 for the segment to be readable. + * @details For data segments, this bit must be 1 for the segment to be writable. + */ + uint64_t RW : 1; + + /** @brief Direction bit for data segments, conforming bit for code segments. + * @details For data segments, this bit must be 1 for the segment to grow up (higher addresses). + * @details For code segments, this bit must be 1 for code in the segment to be able to be executed from an equal or lower privilege level. + */ + uint64_t DC : 1; + + /** @brief Executable bit. + * @details This bit must be 1 for code-segment descriptors. + * @details This bit must be 0 for data-segment and system descriptors. + */ + uint64_t E : 1; + + /** @brief Descriptor type. + * @details This bit must be 0 for system descriptors. + * @details This bit must be 1 for code or data segment descriptor. + */ + uint64_t S : 1; + + /** @brief Descriptor privilege level. + * @details This field determines the privilege level of the segment. + * @details 0 = kernel mode, 3 = user mode. + */ + uint64_t DPL : 2; + + /** @brief Present bit. + * @details This bit must be 1 for all valid descriptors. + */ + uint64_t P : 1; + } __packed; + uint8_t Raw; + }; + + union GlobalDescriptorTableFlags + { + // TODO: Add more flags. + struct + { + /** @brief Unknown. */ + uint64_t Unknown : 5; + + /** @brief Long mode. + * @details If the long mode bit is clear, the segment is in 32-bit protected mode. + * @details If the long mode bit is set, the segment is in 64-bit long mode. + */ + uint64_t L : 1; + } __packed; + uint8_t Raw; + }; + + typedef struct _TaskStateSegmentEntry + { + /* LOW */ + uint16_t Length; + uint16_t BaseLow; + uint8_t BaseMiddle; + GlobalDescriptorTableAccess Flags; + uint8_t Granularity; + uint8_t BaseHigh; + /* HIGH */ + uint32_t BaseUpper; + uint32_t Reserved; + } __packed TaskStateSegmentEntry; + + typedef struct _TaskStateSegment + { + uint32_t Reserved0 __aligned(16); + uint64_t StackPointer[3]; + uint64_t Reserved1; + uint64_t InterruptStackTable[7]; + uint64_t Reserved2; + uint16_t Reserved3; + uint16_t IOMapBaseAddressOffset; + } __packed TaskStateSegment; + + typedef struct _GlobalDescriptorTableEntry + { + /** @brief Length */ + uint16_t Length; + /** @brief Low Base */ + uint16_t BaseLow; + /** @brief Middle Base */ + uint8_t BaseMiddle; + /** @brief Access */ + GlobalDescriptorTableAccess Access; + /** @brief Flags */ + GlobalDescriptorTableFlags Flags; + /** @brief High Base */ + uint8_t BaseHigh; + } __packed GlobalDescriptorTableEntry; + + typedef struct _GlobalDescriptorTableEntries + { + GlobalDescriptorTableEntry Null; + GlobalDescriptorTableEntry Code; + GlobalDescriptorTableEntry Data; + GlobalDescriptorTableEntry UserData; + GlobalDescriptorTableEntry UserCode; + TaskStateSegmentEntry TaskStateSegment; + } __packed GlobalDescriptorTableEntries; + + typedef struct _GlobalDescriptorTableDescriptor + { + /** @brief GDT entries length */ + uint16_t Length; + /** @brief GDT entries address */ + GlobalDescriptorTableEntries *Entries; + } __packed GlobalDescriptorTableDescriptor; + + extern void *CPUStackPointer[]; + extern TaskStateSegment tss[]; + void Init(int Core); + void SetKernelStack(void *Stack); + void *GetKernelStack(); +} + +#define GDT_KERNEL_CODE offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Code) +#define GDT_KERNEL_DATA offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Data) +#define GDT_USER_CODE (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserCode) | 3) +#define GDT_USER_DATA (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserData) | 3) +#define GDT_TSS (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, TaskStateSegment) | 3) + +#endif // !__FENNIX_KERNEL_GDT_H__ diff --git a/Kernel/Architecture/amd64/cpu/idt.hpp b/Kernel/Architecture/amd64/cpu/idt.hpp new file mode 100644 index 00000000..ccc1da87 --- /dev/null +++ b/Kernel/Architecture/amd64/cpu/idt.hpp @@ -0,0 +1,84 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_IDT_H__ +#define __FENNIX_KERNEL_IDT_H__ + +#include + +namespace InterruptDescriptorTable +{ + typedef enum _InterruptGateType + { + TASK = 0b101, + INT_16BIT = 0b110, + TRAP_16BIT = 0b111, + INT_32BIT = 0b1110, + TRAP_32BIT = 0b1111, + } InterruptGateType; + + typedef enum _InterruptRingType + { + RING0 = 0b0, + RING1 = 0b1, + RING2 = 0b10, + RING3 = 0b11, + } InterruptRingType; + + typedef enum _InterruptStackTableType + { + IST0 = 0b0, + IST1 = 0b1, + IST2 = 0b10, + IST3 = 0b11, + IST4 = 0b100, + IST5 = 0b101, + IST6 = 0b110, + } InterruptStackTableType; + + typedef struct _InterruptDescriptorTableEntry + { + uint64_t BaseLow : 16; + uint64_t SegmentSelector : 16; + uint64_t InterruptStackTable : 3; + uint64_t Reserved1 : 5; + uint64_t Flags : 4; + uint64_t Reserved2 : 1; + uint64_t Ring : 2; + uint64_t Present : 1; + uint64_t BaseHigh : 48; + uint64_t Reserved3 : 32; + } __packed InterruptDescriptorTableEntry; + + typedef struct _InterruptDescriptorTableDescriptor + { + uint16_t Length; + InterruptDescriptorTableEntry *Entries; + } __packed InterruptDescriptorTableDescriptor; + + void SetEntry(uint8_t Index, + void (*Base)(), + InterruptStackTableType InterruptStackTable, + InterruptGateType Gate, + InterruptRingType Ring, + bool Present, + uint16_t SegmentSelector); + + void Init(int Core); +} + +#endif // !__FENNIX_KERNEL_IDT_H__ diff --git a/Kernel/Architecture/amd64/linker.ld b/Kernel/Architecture/amd64/linker.ld new file mode 100644 index 00000000..47dbe0e5 --- /dev/null +++ b/Kernel/Architecture/amd64/linker.ld @@ -0,0 +1,92 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) + +KERNEL_VMA = 0xFFFFFFFF80000000; + +ENTRY(_start) + +SECTIONS +{ + . = 0x100000; + + _bootstrap_start = .; + .bootstrap : + { + *(.multiboot) + *(.multiboot2) + *(.bootstrap .bootstrap.*) + } + . += CONSTANT(MAXPAGESIZE); + _bootstrap_end = ALIGN(CONSTANT(MAXPAGESIZE)); + + . += KERNEL_VMA; + . += CONSTANT(MAXPAGESIZE); + _kernel_start = ALIGN(CONSTANT(MAXPAGESIZE)); + .text : AT(ADDR(.text) - KERNEL_VMA) + { + *(.text .text.*) + } + _kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + + .data : AT(ADDR(.data) - KERNEL_VMA) + { + *(.data .data.*) + } + _kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + + .rodata : AT(ADDR(.rodata) - KERNEL_VMA) + { + *(.rodata .rodata.*) + } + + .init_array : AT(ADDR(.init_array) - KERNEL_VMA) + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(.init_array .ctors)) + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : AT(ADDR(.fini_array) - KERNEL_VMA) + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP(*(.fini_array .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + _kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + + .bss : AT(ADDR(.bss) - KERNEL_VMA) + { + *(COMMON) + *(.bss .bss.*) + } + . += CONSTANT(MAXPAGESIZE); + _kernel_end = ALIGN(CONSTANT(MAXPAGESIZE)); + + /DISCARD/ : + { + *(.comment*) + *(.note*) + } +} diff --git a/Kernel/Architecture/amd64/runtime/crt0.c b/Kernel/Architecture/amd64/runtime/crt0.c new file mode 100644 index 00000000..35cf1abd --- /dev/null +++ b/Kernel/Architecture/amd64/runtime/crt0.c @@ -0,0 +1,15 @@ +// #include + +// #include + +// int Entry(void *Info); + +// void _start(void *Raw) +// { +// error("Todo"); +// while (1) +// asmv("hlt"); +// Entry(NULL); +// return; +// } +// C stuff \ No newline at end of file diff --git a/Kernel/Architecture/amd64/runtime/crt1.c b/Kernel/Architecture/amd64/runtime/crt1.c new file mode 100644 index 00000000..1369bfeb --- /dev/null +++ b/Kernel/Architecture/amd64/runtime/crt1.c @@ -0,0 +1,15 @@ +#include + +#include + +int Entry(void *Info); + +// void _start(void *Raw) +// { +// UNUSED(Raw); +// error("ERROR! INVALID BOOT PROTOCOL!"); +// while (1) +// asmv("hlt"); +// Entry(NULL); +// return; +// } diff --git a/Kernel/Architecture/amd64/runtime/crtbegin.c b/Kernel/Architecture/amd64/runtime/crtbegin.c new file mode 100644 index 00000000..507125b3 --- /dev/null +++ b/Kernel/Architecture/amd64/runtime/crtbegin.c @@ -0,0 +1 @@ +// C++ constructor/destructor stuff \ No newline at end of file diff --git a/Kernel/Architecture/amd64/runtime/crtend.c b/Kernel/Architecture/amd64/runtime/crtend.c new file mode 100644 index 00000000..507125b3 --- /dev/null +++ b/Kernel/Architecture/amd64/runtime/crtend.c @@ -0,0 +1 @@ +// C++ constructor/destructor stuff \ No newline at end of file diff --git a/Kernel/Architecture/amd64/runtime/crti.S b/Kernel/Architecture/amd64/runtime/crti.S new file mode 100644 index 00000000..93855715 --- /dev/null +++ b/Kernel/Architecture/amd64/runtime/crti.S @@ -0,0 +1,13 @@ +.section .init +.global _init +.type _init, @function +_init: + push %rbp + movq %rsp, %rbp + +.section .fini +.global _fini +.type _fini, @function +_fini: + push %rbp + movq %rsp, %rbp diff --git a/Kernel/Architecture/amd64/runtime/crtn.S b/Kernel/Architecture/amd64/runtime/crtn.S new file mode 100644 index 00000000..2fe55b4d --- /dev/null +++ b/Kernel/Architecture/amd64/runtime/crtn.S @@ -0,0 +1,7 @@ +.section .init + popq %rbp + ret + +.section .fini + popq %rbp + ret diff --git a/Kernel/Architecture/amd64/rust-target.json b/Kernel/Architecture/amd64/rust-target.json new file mode 100644 index 00000000..f3697f10 --- /dev/null +++ b/Kernel/Architecture/amd64/rust-target.json @@ -0,0 +1,21 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "cpu": "x86-64", + "arch": "x86_64", + "features": "-mmx,-sse,+soft-float", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "linker-flavor": "ld", + "pre-link-args": { + "ld": [ + "-m64" + ] + }, + "no-compiler-rt": true, + "disable-redzone": true, + "eliminate-frame-pointer": false, + "morestack": false +} \ No newline at end of file diff --git a/Kernel/Architecture/i386/ArithmeticOperations.c b/Kernel/Architecture/i386/ArithmeticOperations.c new file mode 100644 index 00000000..91a9db63 --- /dev/null +++ b/Kernel/Architecture/i386/ArithmeticOperations.c @@ -0,0 +1,327 @@ +/* Source: https://github.com/glitchub/arith64 */ +#define arith64_u64 unsigned long long int +#define arith64_s64 signed long long int +#define arith64_u32 unsigned int +#define arith64_s32 int + +typedef union +{ + arith64_u64 u64; + arith64_s64 s64; + struct + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_u32 hi; + arith64_u32 lo; +#else + arith64_u32 lo; + arith64_u32 hi; +#endif + } u32; + struct + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_s32 hi; + arith64_s32 lo; +#else + arith64_s32 lo; + arith64_s32 hi; +#endif + } s32; +} arith64_word; + +#define arith64_hi(n) (arith64_word){.u64 = n}.u32.hi +#define arith64_lo(n) (arith64_word){.u64 = n}.u32.lo +#define arith64_neg(a, b) (((a) ^ ((((arith64_s64)(b)) >= 0) - 1)) + (((arith64_s64)(b)) < 0)) +#define arith64_abs(a) arith64_neg(a, a) + +arith64_s64 __absvdi2(arith64_s64 a) +{ + return arith64_abs(a); +} + +arith64_s64 __ashldi3(arith64_s64 a, int b) +{ + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.hi = w.u32.lo << (b - 32); + w.u32.lo = 0; + } + else if (b) + { + w.u32.hi = (w.u32.lo >> (32 - b)) | (w.u32.hi << b); + w.u32.lo <<= b; + } + return w.s64; +} + +arith64_s64 __ashrdi3(arith64_s64 a, int b) +{ + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.s32.lo = w.s32.hi >> (b - 32); + w.s32.hi >>= 31; // 0xFFFFFFFF or 0 + } + else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.s32.hi >>= b; + } + return w.s64; +} + +int __clzsi2(arith64_u32 a) +{ + int b, n = 0; + b = !(a & 0xffff0000) << 4; + n += b; + a <<= b; + b = !(a & 0xff000000) << 3; + n += b; + a <<= b; + b = !(a & 0xf0000000) << 2; + n += b; + a <<= b; + b = !(a & 0xc0000000) << 1; + n += b; + a <<= b; + return n + !(a & 0x80000000); +} + +int __clzdi2(arith64_u64 a) +{ + int b, n = 0; + b = !(a & 0xffffffff00000000ULL) << 5; + n += b; + a <<= b; + b = !(a & 0xffff000000000000ULL) << 4; + n += b; + a <<= b; + b = !(a & 0xff00000000000000ULL) << 3; + n += b; + a <<= b; + b = !(a & 0xf000000000000000ULL) << 2; + n += b; + a <<= b; + b = !(a & 0xc000000000000000ULL) << 1; + n += b; + a <<= b; + return n + !(a & 0x8000000000000000ULL); +} + +int __ctzsi2(arith64_u32 a) +{ + int b, n = 0; + b = !(a & 0x0000ffff) << 4; + n += b; + a >>= b; + b = !(a & 0x000000ff) << 3; + n += b; + a >>= b; + b = !(a & 0x0000000f) << 2; + n += b; + a >>= b; + b = !(a & 0x00000003) << 1; + n += b; + a >>= b; + return n + !(a & 0x00000001); +} + +int __ctzdi2(arith64_u64 a) +{ + int b, n = 0; + b = !(a & 0x00000000ffffffffULL) << 5; + n += b; + a >>= b; + b = !(a & 0x000000000000ffffULL) << 4; + n += b; + a >>= b; + b = !(a & 0x00000000000000ffULL) << 3; + n += b; + a >>= b; + b = !(a & 0x000000000000000fULL) << 2; + n += b; + a >>= b; + b = !(a & 0x0000000000000003ULL) << 1; + n += b; + a >>= b; + return n + !(a & 0x0000000000000001ULL); +} + +arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c) +{ + if (b > a) + { + if (c) + *c = a; + return 0; + } + if (!arith64_hi(b)) + { + if (b == 0) + { + volatile char x = 0; + x = 1 / x; + } + if (b == 1) + { + if (c) + *c = 0; + return a; + } + if (!arith64_hi(a)) + { + if (c) + *c = arith64_lo(a) % arith64_lo(b); + return arith64_lo(a) / arith64_lo(b); + } + } + + char bits = __clzdi2(b) - __clzdi2(a) + 1; + arith64_u64 rem = a >> bits; + a <<= 64 - bits; + arith64_u64 wrap = 0; + while (bits-- > 0) + { + rem = (rem << 1) | (a >> 63); + a = (a << 1) | (wrap & 1); + wrap = ((arith64_s64)(b - rem - 1) >> 63); + rem -= b & wrap; + } + if (c) + *c = rem; + return (a << 1) | (wrap & 1); +} + +arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b) +{ + arith64_u64 q = __divmoddi4(arith64_abs(a), arith64_abs(b), (void *)0); + return arith64_neg(q, a ^ b); +} + +int __ffsdi2(arith64_u64 a) { return a ? __ctzdi2(a) + 1 : 0; } + +arith64_u64 __lshrdi3(arith64_u64 a, int b) +{ + arith64_word w = {.u64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.lo = w.u32.hi >> (b - 32); + w.u32.hi = 0; + } + else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.u32.hi >>= b; + } + return w.u64; +} + +arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b) +{ + arith64_u64 r; + __divmoddi4(arith64_abs(a), arith64_abs(b), &r); + return arith64_neg(r, a); +} + +int __popcountsi2(arith64_u32 a) +{ + + a = a - ((a >> 1) & 0x55555555); + a = ((a >> 2) & 0x33333333) + (a & 0x33333333); + a = (a + (a >> 4)) & 0x0F0F0F0F; + a = (a + (a >> 16)); + + return (a + (a >> 8)) & 63; +} + +int __popcountdi2(arith64_u64 a) +{ + + a = a - ((a >> 1) & 0x5555555555555555ULL); + a = ((a >> 2) & 0x3333333333333333ULL) + (a & 0x3333333333333333ULL); + a = (a + (a >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + a = (a + (a >> 32)); + a = (a + (a >> 16)); + + return (a + (a >> 8)) & 127; +} + +arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b) { return __divmoddi4(a, b, (void *)0); } + +arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b) +{ + arith64_u64 r; + __divmoddi4(a, b, &r); + return r; +} + +/* Good documentation: https://splichal.eu/scripts/sphinx/gccint/_build/html/the-gcc-low-level-runtime-library/routines-for-floating-point-emulation.html */ + +double __adddf3(double a, double b) { return a + b; } +double __muldf3(double a, double b) { return a * b; } +double __floatsidf(int i) { return (double)i; } +int __ltdf2(double a, double b) { return a < b; } +int __gtdf2(double a, double b) { return a > b; } +int __nedf2(double a, double b) { return a != b; } +int __eqdf2(double a, double b) { return a == b; } +double __floatdidf(long i) { return (double)i; } +double __divdf3(double a, double b) { return a / b; } +double __subdf3(double a, double b) { return a - b; } +int __gedf2(double a, double b) { return a >= b; } +int __fixdfsi(double a) { return (int)a; } +long __fixdfdi(double a) { return (long)a; } +int __ledf2(double a, double b) { return a <= b; } + +/* FIXME: Check if these functions are implemented correctly */ + +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef unsigned int uint32_t; +typedef struct +{ + uint64_t value; +} atomic_uint64_t; + +uint64_t __atomic_load_8(const atomic_uint64_t *p) +{ + uint64_t value; + __asm__ volatile("lock cmpxchg8b %1" + : "=A"(value) + : "m"(*p) + : "memory"); + return value; +} + +void __atomic_store_8(atomic_uint64_t *p, uint64_t value) +{ + __asm__ volatile("lock cmpxchg8b %0" + : "=m"(p->value) + : "a"((uint32_t)value), "d"((uint32_t)(value >> 32)), "m"(*p) + : "memory"); +} + +/* FIXME: __fixsfsi is not implemented correctly(?) */ +int __fixsfsi(float a) { return (int)a; } + +int __ltsf2(float a, float b) { return a < b; } +int __eqsf2(float a, float b) { return a == b; } +float __divsf3(float a, float b) { return a / b; } +double __extendsfdf2(float a) { return (double)a; } +float __truncdfsf2(double a) { return (float)a; } +float __subsf3(float a, float b) { return a - b; } +float __floatsisf(int a) { return (float)a; } +int __fixunssfsi(float a) { return (int)a; } +float __mulsf3(float a, float b) { return a * b; } +float __addsf3(float a, float b) { return a + b; } diff --git a/Kernel/Architecture/i386/Bootstrap/Header_Multiboot.asm b/Kernel/Architecture/i386/Bootstrap/Header_Multiboot.asm new file mode 100644 index 00000000..71a6efe8 --- /dev/null +++ b/Kernel/Architecture/i386/Bootstrap/Header_Multiboot.asm @@ -0,0 +1,5 @@ +section .multiboot +align 4 + dd 0x1BADB002 + dd 1 << 0 | 1 << 1 + dd -(0x1BADB002 + (1 << 0 | 1 << 1)) diff --git a/Kernel/Architecture/i386/Bootstrap/Header_Multiboot2.asm b/Kernel/Architecture/i386/Bootstrap/Header_Multiboot2.asm new file mode 100644 index 00000000..a0a9a71e --- /dev/null +++ b/Kernel/Architecture/i386/Bootstrap/Header_Multiboot2.asm @@ -0,0 +1,41 @@ +section .multiboot2 +align 4096 +HEADER_START: + dd 0xE85250D6 + dd 0 + dd (HEADER_END - HEADER_START) + dd 0x100000000 - (HEADER_END - HEADER_START) - 0 - 0xE85250D6 +align 8 +MB2_INFO_REQUEST_TAG_START: + dw 1 + dw 0 + dd MB2_INFO_REQUEST_TAG_END - MB2_INFO_REQUEST_TAG_START + dd 1 ; Command Line + dd 2 ; Boot Loader Name + dd 3 ; Module + dd 4 ; Basic Memory Information + dd 5 ; BIOS Boot Device + dd 6 ; Memory Map + dd 7 ; VBE + dd 8 ; Framebuffer + dd 9 ; ELF Sections + dd 10 ; APM Table + dd 11 ; EFI 32-bit System Table Pointer + dd 12 ; EFI 64-bit System Table Pointer + ; dd 13 ; SMBIOS + dd 14 ; ACPI Old + dd 15 ; ACPI New + dd 16 ; Network + dd 17 ; EFI Memory Map + dd 18 ; EFI Boot Services Notifier + dd 19 ; EFI 32-bit Image Handle Pointer + dd 20 ; EFI 64-bit Image Handle Pointer + dd 21 ; Load Base Address +MB2_INFO_REQUEST_TAG_END: +align 8 +MB2_TAG_START: + dw 0 + dw 0 + dd MB2_TAG_END - MB2_TAG_START +MB2_TAG_END: +HEADER_END: diff --git a/Kernel/Architecture/i386/Bootstrap/Multiboot.cpp b/Kernel/Architecture/i386/Bootstrap/Multiboot.cpp new file mode 100644 index 00000000..a048bdf9 --- /dev/null +++ b/Kernel/Architecture/i386/Bootstrap/Multiboot.cpp @@ -0,0 +1,286 @@ +#include + +#include +#include +#include + +#include "../../../kernel.h" + +EXTERNC void multiboot_main(uint32_t Magic, uint32_t Info) +{ + if (Info == NULL || Magic == NULL) + { + if (Magic == NULL) + error("Multiboot magic is NULL"); + if (Info == NULL) + error("Multiboot info is NULL"); + CPU::Stop(); + } + else if (Magic != MULTIBOOT2_BOOTLOADER_MAGIC) + { + error("Multiboot magic is invalid (%#x != %#x)", Magic, MULTIBOOT2_BOOTLOADER_MAGIC); + CPU::Stop(); + } + + uint64_t div = 1193180 / 1000; + outb(0x43, 0xB6); + outb(0x42, (uint8_t)div); + outb(0x42, (uint8_t)(div >> 8)); + uint8_t tmp = inb(0x61); + if (tmp != (tmp | 3)) + outb(0x61, tmp | 3); + + BootInfo mb2binfo; + int pos = 0; + auto InfoAddress = Info; + for (auto Tag = (struct multiboot_tag *)((uint8_t *)InfoAddress + 8); + ; + Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7))) + { + if (Tag->type == MULTIBOOT_TAG_TYPE_END) + { + debug("End of multiboot2 tags"); + break; + } + + switch (Tag->type) + { + case MULTIBOOT_TAG_TYPE_CMDLINE: + { + strncpy(mb2binfo.Kernel.CommandLine, + ((multiboot_tag_string *)Tag)->string, + strlen(((multiboot_tag_string *)Tag)->string)); + debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine); + break; + } + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + { + strncpy(mb2binfo.Bootloader.Name, + ((multiboot_tag_string *)Tag)->string, + strlen(((multiboot_tag_string *)Tag)->string)); + debug("Bootloader name: %s", mb2binfo.Bootloader.Name); + break; + } + case MULTIBOOT_TAG_TYPE_MODULE: + { + multiboot_tag_module *module = (multiboot_tag_module *)Tag; + static int module_count = 0; + mb2binfo.Modules[module_count++].Address = (void *)(uint64_t)module->mod_start; + mb2binfo.Modules[module_count++].Size = module->size; + strncpy(mb2binfo.Modules[module_count++].Path, "(null)", 6); + strncpy(mb2binfo.Modules[module_count++].CommandLine, module->cmdline, + strlen(module->cmdline)); + debug("Module: %s", mb2binfo.Modules[module_count++].Path); + break; + } + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + { + multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag; + fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]", + meminfo->mem_lower, meminfo->mem_upper); + break; + } + case MULTIBOOT_TAG_TYPE_BOOTDEV: + { + multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag; + fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]", + bootdev->biosdev, bootdev->slice, bootdev->part); + break; + } + case MULTIBOOT_TAG_TYPE_MMAP: + { + multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag; + uint32_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry); + mb2binfo.Memory.Entries = EntryCount; + for (uint32_t i = 0; i < EntryCount; i++) + { + if (EntryCount > MAX_MEMORY_ENTRIES) + { + warn("Too many memory entries, skipping the rest..."); + break; + } + multiboot_mmap_entry entry = mmap->entries[i]; + mb2binfo.Memory.Size += entry.len; + switch (entry.type) + { + case MULTIBOOT_MEMORY_AVAILABLE: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Usable; + break; + case MULTIBOOT_MEMORY_RESERVED: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Reserved; + break; + case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = ACPIReclaimable; + break; + case MULTIBOOT_MEMORY_NVS: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = ACPINVS; + break; + case MULTIBOOT_MEMORY_BADRAM: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = BadMemory; + break; + default: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Unknown; + break; + } + debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]", + mb2binfo.Memory.Entry[i].BaseAddress, + mb2binfo.Memory.Entry[i].Length, + mb2binfo.Memory.Entry[i].Type); + } + break; + } + case MULTIBOOT_TAG_TYPE_VBE: + { + multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag; + fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]", + vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len); + break; + } + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + { + multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag; + static int fb_count = 0; + mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr; + mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width; + mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height; + mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch; + mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp; + switch (fb->common.framebuffer_type) + { + case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED: + { + mb2binfo.Framebuffer[fb_count].Type = Indexed; + break; + } + case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: + { + mb2binfo.Framebuffer[fb_count].Type = RGB; + mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size; + mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position; + mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size; + mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position; + mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size; + mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position; + break; + } + case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: + { + mb2binfo.Framebuffer[fb_count].Type = EGA; + break; + } + } + debug("Framebuffer %d: %dx%d %d bpp", fb_count, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp); + debug("More info:\nAddress: %p\nPitch: %lld\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d", + fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type, + fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size, + fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position); + fb_count++; + break; + } + case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: + { + multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag; + fixme("elf_sections->[num=%d, size=%d, entsize=%d, shndx=%d]", + elf->num, elf->size, elf->entsize, elf->shndx); + break; + } + case MULTIBOOT_TAG_TYPE_APM: + { + multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag; + fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]", + apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len); + break; + } + case MULTIBOOT_TAG_TYPE_EFI32: + { + multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag; + fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size); + break; + } + case MULTIBOOT_TAG_TYPE_EFI64: + { + multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag; + fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size); + break; + } + case MULTIBOOT_TAG_TYPE_SMBIOS: + { + multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag; + fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor); + break; + } + case MULTIBOOT_TAG_TYPE_ACPI_OLD: + { + mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp; + debug("OLD ACPI RSDP: %p", mb2binfo.RSDP); + break; + } + case MULTIBOOT_TAG_TYPE_ACPI_NEW: + { + mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp; + debug("NEW ACPI RSDP: %p", mb2binfo.RSDP); + break; + } + case MULTIBOOT_TAG_TYPE_NETWORK: + { + multiboot_tag_network *net = (multiboot_tag_network *)Tag; + fixme("network->[dhcpack: %p]", net->dhcpack); + break; + } + case MULTIBOOT_TAG_TYPE_EFI_MMAP: + { + multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag; + fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]", + efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap); + break; + } + case MULTIBOOT_TAG_TYPE_EFI_BS: + { + fixme("efi_bs->[%p] (unknown structure)", Tag); + break; + } + case MULTIBOOT_TAG_TYPE_EFI32_IH: + { + multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag; + fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer); + break; + } + case MULTIBOOT_TAG_TYPE_EFI64_IH: + { + multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag; + fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer); + break; + } + case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR: + { + multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag; + mb2binfo.Kernel.PhysicalBase = (void *)(uint64_t)load_base_addr->load_base_addr; + mb2binfo.Kernel.VirtualBase = (void *)(uint64_t)(load_base_addr->load_base_addr + 0xFFFFFFFF80000000); + debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase); + break; + } + default: + { + error("Unknown multiboot2 tag type: %d", Tag->type); + break; + } + } + } + + tmp = inb(0x61) & 0xFC; + outb(0x61, tmp); + + Entry(&mb2binfo); +} diff --git a/Kernel/Architecture/i386/Bootstrap/Multiboot_Init.asm b/Kernel/Architecture/i386/Bootstrap/Multiboot_Init.asm new file mode 100644 index 00000000..c8212a96 --- /dev/null +++ b/Kernel/Architecture/i386/Bootstrap/Multiboot_Init.asm @@ -0,0 +1,50 @@ +[bits 32] +KERNEL_VIRTUAL_BASE equ 0xC0000000 ; 3GB +KERNEL_STACK_SIZE equ 0x4000 ; 16KB + +extern multiboot_main +extern BootPageTable +global _start + +section .text + +MB_HeaderMagic: + dq 0 + +MB_HeaderInfo: + dq 0 + +_start: + cli + mov ecx, (BootPageTable - KERNEL_VIRTUAL_BASE) + mov cr3, ecx + + mov ecx, cr4 + or ecx, 0x00000010 ; Set PSE in CR4 + mov cr4, ecx + + mov ecx, cr0 + or ecx, 0x80000000 ; Set PG in CR0 + mov cr0, ecx + + lea ecx, [HigherHalfStart] + jmp ecx + +HigherHalfStart: + mov [MB_HeaderMagic], eax + mov [MB_HeaderInfo], ebx + mov esp, KernelStack + KERNEL_STACK_SIZE + mov eax, [MB_HeaderMagic] + mov ebx, [MB_HeaderInfo] + push ebx ; Multiboot2 Header + add ebx, KERNEL_VIRTUAL_BASE + push eax ; Multiboot2 Magic + call multiboot_main +.Hang: + hlt + jmp .Hang + +section .bss +align 16 +KernelStack: + resb KERNEL_STACK_SIZE diff --git a/Kernel/Architecture/i386/Bootstrap/Multiboot_PageTable.asm b/Kernel/Architecture/i386/Bootstrap/Multiboot_PageTable.asm new file mode 100644 index 00000000..9df80899 --- /dev/null +++ b/Kernel/Architecture/i386/Bootstrap/Multiboot_PageTable.asm @@ -0,0 +1,10 @@ +KERNEL_PAGE_NUMBER equ 768 ; 0xC0000000 + +section .data +global BootPageTable +align 0x1000 +BootPageTable: + dd 0x00000083 + times (KERNEL_PAGE_NUMBER - 1) dd 0 + dd 0x00000083 + times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0 diff --git a/Kernel/Architecture/i386/Interrupts/8259PIC.cpp b/Kernel/Architecture/i386/Interrupts/8259PIC.cpp new file mode 100644 index 00000000..1be69a88 --- /dev/null +++ b/Kernel/Architecture/i386/Interrupts/8259PIC.cpp @@ -0,0 +1,140 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "pic.hpp" + +#include + +namespace PIC +{ + PIC::PIC(uint8_t MasterCommandPort, uint8_t MasterDataPort, uint8_t SlaveCommandPort, uint8_t SlaveDataPort, uint8_t MasterOffset, uint8_t SlaveOffset) + { + this->MasterCommandPort = MasterCommandPort; + this->MasterDataPort = MasterDataPort; + this->SlaveCommandPort = SlaveCommandPort; + this->SlaveDataPort = SlaveDataPort; + this->MasterOffset = MasterOffset; + this->SlaveOffset = SlaveOffset; + + MasterMask = 0xFF; + SlaveMask = 0xFF; + + // ICW1 + outb(MasterCommandPort, 0x11); + outb(SlaveCommandPort, 0x11); + + // ICW2 + outb(MasterDataPort, MasterOffset); + outb(SlaveDataPort, SlaveOffset); + + // ICW3 + outb(MasterDataPort, 0x04); + outb(SlaveDataPort, 0x02); + + // ICW4 + outb(MasterDataPort, 0x01); + outb(SlaveDataPort, 0x01); + + // OCW1 + outb(MasterDataPort, MasterMask); + outb(SlaveDataPort, SlaveMask); + } + + PIC::~PIC() + { + outb(MasterDataPort, 0xFF); + outb(SlaveDataPort, 0xFF); + } + + void PIC::Mask(uint8_t IRQ) + { + uint16_t Port; + uint8_t Value; + + if (IRQ < 8) + { + Port = MasterDataPort; + Value = MasterMask & ~(1 << IRQ); + MasterMask = Value; + } + else + { + Port = SlaveDataPort; + Value = SlaveMask & ~(1 << (IRQ - 8)); + SlaveMask = Value; + } + + outb(Port, Value); + } + + void PIC::Unmask(uint8_t IRQ) + { + uint16_t Port; + uint8_t Value; + + if (IRQ < 8) + { + Port = MasterDataPort; + Value = MasterMask | (1 << IRQ); + MasterMask = Value; + } + else + { + Port = SlaveDataPort; + Value = SlaveMask | (1 << (IRQ - 8)); + SlaveMask = Value; + } + + outb(Port, Value); + } + + void PIC::SendEOI(uint8_t IRQ) + { + if (IRQ >= 8) + outb(SlaveCommandPort, 0x20); + + outb(MasterCommandPort, 0x20); + } + + PIT::PIT(uint16_t Port, uint16_t Frequency) + { + this->Port = Port; + this->Frequency = Frequency; + } + + PIT::~PIT() + { + } + + void PIT::PrepareSleep(uint32_t Milliseconds) + { + uint16_t Divisor = 1193182 / Frequency; + uint8_t Low = (uint8_t)(Divisor & 0xFF); + uint8_t High = (uint8_t)((Divisor >> 8) & 0xFF); + + outb(Port + 3, 0x36); + outb(Port + 0, Low); + outb(Port + 1, High); + } + + void PIT::PerformSleep() + { + uint8_t Value = inb(Port + 0); + while (Value != 0) + Value = inb(Port + 0); + } +} diff --git a/Kernel/Architecture/i386/Interrupts/pic.hpp b/Kernel/Architecture/i386/Interrupts/pic.hpp new file mode 100644 index 00000000..fb0e5f1a --- /dev/null +++ b/Kernel/Architecture/i386/Interrupts/pic.hpp @@ -0,0 +1,59 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_8259PIC_H__ +#define __FENNIX_KERNEL_8259PIC_H__ + +#include + +namespace PIC +{ + class PIC + { + private: + uint8_t MasterCommandPort; + uint8_t MasterDataPort; + uint8_t SlaveCommandPort; + uint8_t SlaveDataPort; + uint8_t MasterOffset; + uint8_t SlaveOffset; + uint8_t MasterMask; + uint8_t SlaveMask; + + public: + PIC(uint8_t MasterCommandPort, uint8_t MasterDataPort, uint8_t SlaveCommandPort, uint8_t SlaveDataPort, uint8_t MasterOffset, uint8_t SlaveOffset); + ~PIC(); + void Mask(uint8_t IRQ); + void Unmask(uint8_t IRQ); + void SendEOI(uint8_t IRQ); + }; + + class PIT + { + private: + uint16_t Port; + uint16_t Frequency; + + public: + PIT(uint16_t Port, uint16_t Frequency); + ~PIT(); + void PrepareSleep(uint32_t Milliseconds); + void PerformSleep(); + }; +} + +#endif // !__FENNIX_KERNEL_8259PIC_H__ diff --git a/Kernel/Architecture/i386/SystemCalls.cpp b/Kernel/Architecture/i386/SystemCalls.cpp new file mode 100644 index 00000000..cc9c5007 --- /dev/null +++ b/Kernel/Architecture/i386/SystemCalls.cpp @@ -0,0 +1,30 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include + +#include "cpu/gdt.hpp" + +using namespace CPU::x32; + +extern "C" uint32_t SystemCallsHandler(SyscallsFrame *regs); + +void InitializeSystemCalls() +{ +} diff --git a/Kernel/Architecture/i386/acpi.hpp b/Kernel/Architecture/i386/acpi.hpp new file mode 100644 index 00000000..1a71f712 --- /dev/null +++ b/Kernel/Architecture/i386/acpi.hpp @@ -0,0 +1,294 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_ACPI_H__ +#define __FENNIX_KERNEL_ACPI_H__ + +#include + +#include +#include +#include +#include + +namespace ACPI +{ + class ACPI + { + public: + struct ACPIHeader + { + unsigned char Signature[4]; + uint32_t Length; + uint8_t Revision; + uint8_t Checksum; + uint8_t OEMID[6]; + uint8_t OEMTableID[8]; + uint32_t OEMRevision; + uint32_t CreatorID; + uint32_t CreatorRevision; + } __packed; + + struct GenericAddressStructure + { + uint8_t AddressSpace; + uint8_t BitWidth; + uint8_t BitOffset; + uint8_t AccessSize; + uint64_t Address; + } __packed; + + struct MCFGHeader + { + struct ACPIHeader Header; + uint64_t Reserved; + } __packed; + + struct HPETHeader + { + ACPIHeader Header; + uint8_t HardwareRevID; + uint8_t ComparatorCount : 5; + uint8_t CounterSize : 1; + uint8_t Reserved : 1; + uint8_t LegacyReplacement : 1; + uint16_t PCIVendorID; + struct GenericAddressStructure Address; + uint8_t HPETNumber; + uint16_t MinimumTick; + uint8_t PageProtection; + } __packed; + + struct FADTHeader + { + ACPIHeader Header; + uint32_t FirmwareCtrl; + uint32_t Dsdt; + uint8_t Reserved; + uint8_t PreferredPowerManagementProfile; + uint16_t SCI_Interrupt; + uint32_t SMI_CommandPort; + uint8_t AcpiEnable; + uint8_t AcpiDisable; + uint8_t S4BIOS_REQ; + uint8_t PSTATE_Control; + uint32_t PM1aEventBlock; + uint32_t PM1bEventBlock; + uint32_t PM1aControlBlock; + uint32_t PM1bControlBlock; + uint32_t PM2ControlBlock; + uint32_t PMTimerBlock; + uint32_t GPE0Block; + uint32_t GPE1Block; + uint8_t PM1EventLength; + uint8_t PM1ControlLength; + uint8_t PM2ControlLength; + uint8_t PMTimerLength; + uint8_t GPE0Length; + uint8_t GPE1Length; + uint8_t GPE1Base; + uint8_t CStateControl; + uint16_t WorstC2Latency; + uint16_t WorstC3Latency; + uint16_t FlushSize; + uint16_t FlushStride; + uint8_t DutyOffset; + uint8_t DutyWidth; + uint8_t DayAlarm; + uint8_t MonthAlarm; + uint8_t Century; + uint16_t BootArchitectureFlags; + uint8_t Reserved2; + uint32_t Flags; + struct GenericAddressStructure ResetReg; + uint8_t ResetValue; + uint8_t Reserved3[3]; + uint64_t X_FirmwareControl; + uint64_t X_Dsdt; + struct GenericAddressStructure X_PM1aEventBlock; + struct GenericAddressStructure X_PM1bEventBlock; + struct GenericAddressStructure X_PM1aControlBlock; + struct GenericAddressStructure X_PM1bControlBlock; + struct GenericAddressStructure X_PM2ControlBlock; + struct GenericAddressStructure X_PMTimerBlock; + struct GenericAddressStructure X_GPE0Block; + struct GenericAddressStructure X_GPE1Block; + } __packed; + + struct BGRTHeader + { + ACPIHeader Header; + uint16_t Version; + uint8_t Status; + uint8_t ImageType; + uint64_t ImageAddress; + uint32_t ImageOffsetX; + uint32_t ImageOffsetY; + }; + + struct SRATHeader + { + ACPIHeader Header; + uint32_t TableRevision; // Must be value 1 + uint64_t Reserved; // Reserved, must be zero + }; + + struct TPM2Header + { + ACPIHeader Header; + uint32_t Flags; + uint64_t ControlAddress; + uint32_t StartMethod; + }; + + struct TCPAHeader + { + ACPIHeader Header; + uint16_t Reserved; + uint32_t MaxLogLength; + uint64_t LogAddress; + }; + + struct WAETHeader + { + ACPIHeader Header; + uint32_t Flags; + }; + + struct HESTHeader + { + ACPIHeader Header; + uint32_t ErrorSourceCount; + }; + + struct MADTHeader + { + ACPIHeader Header; + uint32_t LocalControllerAddress; + uint32_t Flags; + char Entries[]; + } __packed; + + ACPIHeader *XSDT = nullptr; + MCFGHeader *MCFG = nullptr; + HPETHeader *HPET = nullptr; + FADTHeader *FADT = nullptr; + BGRTHeader *BGRT = nullptr; + SRATHeader *SRAT = nullptr; + TPM2Header *TPM2 = nullptr; + TCPAHeader *TCPA = nullptr; + WAETHeader *WAET = nullptr; + MADTHeader *MADT = nullptr; + HESTHeader *HEST = nullptr; + bool XSDTSupported = false; + + void *FindTable(ACPIHeader *ACPIHeader, char *Signature); + void SearchTables(ACPIHeader *Header); + ACPI(); + ~ACPI(); + }; + + class MADT + { + public: + struct APICHeader + { + uint8_t Type; + uint8_t Length; + } __packed; + + struct MADTIOApic + { + struct APICHeader Header; + uint8_t APICID; + uint8_t reserved; + uint32_t Address; + uint32_t GSIBase; + } __packed; + + struct MADTIso + { + struct APICHeader Header; + uint8_t BuSSource; + uint8_t IRQSource; + uint32_t GSI; + uint16_t Flags; + } __packed; + + struct MADTNmi + { + struct APICHeader Header; + uint8_t processor; + uint16_t flags; + uint8_t lint; + } __packed; + + struct LocalAPIC + { + struct APICHeader Header; + uint8_t ACPIProcessorId; + uint8_t APICId; + uint32_t Flags; + } __packed; + + struct LAPIC + { + uint8_t id; + uintptr_t PhysicalAddress; + void *VirtualAddress; + }; + + std::vector ioapic; + std::vector iso; + std::vector nmi; + std::vector lapic; + struct LAPIC *LAPICAddress; + uint16_t CPUCores; + + MADT(ACPI::MADTHeader *madt); + ~MADT(); + }; + + class DSDT : public Interrupts::Handler + { + private: + uint32_t SMI_CMD = 0; + uint8_t ACPI_ENABLE = 0; + uint8_t ACPI_DISABLE = 0; + uint32_t PM1a_CNT = 0; + uint32_t PM1b_CNT = 0; + uint16_t SLP_TYPa = 0; + uint16_t SLP_TYPb = 0; + uint16_t SLP_EN = 0; + uint16_t SCI_EN = 0; + uint8_t PM1_CNT_LEN = 0; + + ACPI *acpi; + void OnInterruptReceived(CPU::x64::TrapFrame *Frame); + + public: + bool ACPIShutdownSupported = false; + + void Reboot(); + void Shutdown(); + + DSDT(ACPI *acpi); + ~DSDT(); + }; +} + +#endif // !__FENNIX_KERNEL_ACPI_H__ diff --git a/Kernel/Architecture/i386/cpu/GlobalDescriptorTable.cpp b/Kernel/Architecture/i386/cpu/GlobalDescriptorTable.cpp new file mode 100644 index 00000000..d7cb3d0d --- /dev/null +++ b/Kernel/Architecture/i386/cpu/GlobalDescriptorTable.cpp @@ -0,0 +1,116 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "gdt.hpp" + +#include +#include +#include +#include + +namespace GlobalDescriptorTable +{ + static GlobalDescriptorTableEntries GDTEntriesTemplate = { + // null + {.Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = {.Raw = 0x0}, + .Flags = {.Raw = 0x0}, + .BaseHigh = 0x0}, + + // kernel code + {.Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = {.A = 0, + .RW = 1, + .DC = 0, + .E = 1, + .S = 1, + .DPL = 0, + .P = 1}, + .Flags = {.Unknown = 0x0, .L = 1}, + .BaseHigh = 0x0}, + + // kernel data + {.Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = {.A = 0, + .RW = 1, + .DC = 0, + .E = 0, + .S = 1, + .DPL = 0, + .P = 1}, + .Flags = {.Raw = 0x0}, + .BaseHigh = 0x0}, + + // user data + {.Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = {.A = 0, + .RW = 1, + .DC = 0, + .E = 0, + .S = 1, + .DPL = 3, + .P = 1}, + .Flags = {.Raw = 0x0}, + .BaseHigh = 0x0}, + + // user code + {.Length = 0x0, + .BaseLow = 0x0, + .BaseMiddle = 0x0, + .Access = {.A = 0, + .RW = 1, + .DC = 0, + .E = 1, + .S = 1, + .DPL = 3, + .P = 1}, + .Flags = {.Unknown = 0x0, .L = 1}, + .BaseHigh = 0x0}, + + // tss + {}}; + + GlobalDescriptorTableEntries GDTEntries[MAX_CPU]; + GlobalDescriptorTableDescriptor gdt[MAX_CPU]; + + TaskStateSegment tss[MAX_CPU] = { + 0, + {0, 0, 0}, + 0, + {0, 0, 0, 0, 0, 0, 0}, + 0, + 0, + }; + + void *CPUStackPointer[MAX_CPU]; + + SafeFunction void Init(int Core) + { + } + + SafeFunction void SetKernelStack(void *Stack) + { + } +} diff --git a/Kernel/Architecture/i386/cpu/SymmetricMultiprocessing.cpp b/Kernel/Architecture/i386/cpu/SymmetricMultiprocessing.cpp new file mode 100644 index 00000000..eff12718 --- /dev/null +++ b/Kernel/Architecture/i386/cpu/SymmetricMultiprocessing.cpp @@ -0,0 +1,54 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include +#include + +#include "../../../kernel.h" + +volatile bool CPUEnabled = false; + +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +static __aligned(0x1000) CPUData CPUs[MAX_CPU] = {0}; + +CPUData *GetCPU(uint64_t id) { return &CPUs[id]; } + +CPUData *GetCurrentCPU() +{ + uint64_t ret = 0; + if (!(&CPUs[ret])->IsActive) + { + // error("CPU %d is not active!", ret); + return &CPUs[0]; + } + assert((&CPUs[ret])->Checksum == CPU_DATA_CHECKSUM); + return &CPUs[ret]; +} + +namespace SMP +{ + int CPUCores = 0; + + void Initialize(void *madt) + { + fixme("SMP::Initialize() is not implemented!"); + } +} diff --git a/Kernel/Architecture/i386/cpu/apic.hpp b/Kernel/Architecture/i386/cpu/apic.hpp new file mode 100644 index 00000000..24887b48 --- /dev/null +++ b/Kernel/Architecture/i386/cpu/apic.hpp @@ -0,0 +1,354 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_APIC_H__ +#define __FENNIX_KERNEL_APIC_H__ + +#include + +#include +#include + +namespace APIC +{ + enum APICRegisters + { + // source from: https://github.com/pdoane/osdev/blob/master/intr/local_apic.c + APIC_ID = 0x20, // Local APIC ID + APIC_VER = 0x30, // Local APIC Version + APIC_TPR = 0x80, // Task Priority + APIC_APR = 0x90, // Arbitration Priority + APIC_PPR = 0xA0, // Processor Priority + APIC_EOI = 0xB0, // EOI + APIC_RRD = 0xC0, // Remote Read + APIC_LDR = 0xD0, // Logical Destination + APIC_DFR = 0xE0, // Destination Format + APIC_SVR = 0xF0, // Spurious Interrupt Vector + APIC_ISR = 0x100, // In-Service (8 registers) + APIC_TMR = 0x180, // Trigger Mode (8 registers) + APIC_IRR = 0x200, // Interrupt Request (8 registers) + APIC_ESR = 0x280, // Error Status + APIC_ICRLO = 0x300, // Interrupt Command + APIC_ICRHI = 0x310, // Interrupt Command [63:32] + APIC_TIMER = 0x320, // LVT Timer + APIC_THERMAL = 0x330, // LVT Thermal Sensor + APIC_PERF = 0x340, // LVT Performance Counter + APIC_LINT0 = 0x350, // LVT LINT0 + APIC_LINT1 = 0x360, // LVT LINT1 + APIC_ERROR = 0x370, // LVT Error + APIC_TICR = 0x380, // Initial Count (for Timer) + APIC_TCCR = 0x390, // Current Count (for Timer) + APIC_TDCR = 0x3E0, // Divide Configuration (for Timer) + }; + + enum IOAPICRegisters + { + GetIOAPICVersion = 0x1 + }; + + enum IOAPICFlags + { + ActiveHighLow = 2, + EdgeLevel = 8 + }; + + enum APICDeliveryMode + { + Fixed = 0b000, + LowestPriority = 0b001, /* Reserved */ + SMI = 0b010, + APIC_DELIVERY_MODE_RESERVED0 = 0b011, /* Reserved */ + NMI = 0b100, + INIT = 0b101, + Startup = 0b110, + ExtINT = 0b111 /* Reserved */ + }; + + enum APICDestinationMode + { + Physical = 0b0, + Logical = 0b1 + }; + + enum APICDeliveryStatus + { + Idle = 0b0, + SendPending = 0b1 + }; + + enum APICLevel + { + DeAssert = 0b0, + Assert = 0b1 + }; + + enum APICTriggerMode + { + Edge = 0b0, + Level = 0b1 + }; + + enum APICDestinationShorthand + { + NoShorthand = 0b00, + Self = 0b01, + AllIncludingSelf = 0b10, + AllExcludingSelf = 0b11 + }; + + enum LVTTimerDivide + { + DivideBy2 = 0b000, + DivideBy4 = 0b001, + DivideBy8 = 0b010, + DivideBy16 = 0b011, + DivideBy32 = 0b100, + DivideBy64 = 0b101, + DivideBy128 = 0b110, + DivideBy1 = 0b111 + }; + + enum LVTTimerMask + { + Unmasked = 0b0, + Masked = 0b1 + }; + + enum LVTTimerMode + { + OneShot = 0b00, + Periodic = 0b01, + TSCDeadline = 0b10 + }; + + typedef union + { + struct + { + /** @brief Interrupt Vector */ + uint64_t Vector : 8; + /** @brief Reserved */ + uint64_t Reserved0 : 4; + /** + * @brief Delivery Status + * + * 0: Idle + * 1: Send Pending + */ + uint64_t DeliveryStatus : 1; + /** @brief Reserved */ + uint64_t Reserved1 : 3; + /** + * @brief Mask + * + * 0: Not masked + * 1: Masked + */ + uint64_t Mask : 1; + /** @brief Timer Mode + * + * 0: One-shot + * 1: Periodic + * 2: TSC-Deadline + */ + uint64_t TimerMode : 1; + /** @brief Reserved */ + uint64_t Reserved2 : 14; + }; + uint64_t raw; + } __packed LVTTimer; + + typedef union + { + struct + { + /** @brief Spurious Vector */ + uint64_t Vector : 8; + /** @brief Enable or disable APIC software */ + uint64_t Software : 1; + /** @brief Focus Processor Checking */ + uint64_t FocusProcessorChecking : 1; + /** @brief Reserved */ + uint64_t Reserved : 2; + /** @brief Disable EOI Broadcast */ + uint64_t DisableEOIBroadcast : 1; + /** @brief Reserved */ + uint64_t Reserved1 : 19; + }; + uint64_t raw; + } __packed Spurious; + + typedef union + { + struct + { + /** @brief Interrupt Vector */ + uint64_t Vector : 8; + /** @brief Delivery Mode */ + uint64_t DeliveryMode : 3; + /** @brief Destination Mode + * + * 0: Physical + * 1: Logical + */ + uint64_t DestinationMode : 1; + /** @brief Delivery Status + * + * @note Reserved when in x2APIC mode + */ + uint64_t DeliveryStatus : 1; + /** @brief Reserved */ + uint64_t Reserved0 : 1; + /** @brief Level + * + * 0: Deassert + * 1: Assert + */ + uint64_t Level : 1; + /** @brief Trigger Mode + * + * 0: Edge + * 1: Level + */ + uint64_t TriggerMode : 1; + /** @brief Reserved */ + uint64_t Reserved1 : 2; + /** @brief Destination Shorthand + * + * 0: No shorthand + * 1: Self + * 2: All including self + * 3: All excluding self + */ + uint64_t DestinationShorthand : 2; + /** @brief Reserved */ + uint64_t Reserved2 : 12; + }; + uint64_t raw; + } __packed InterruptCommandRegisterLow; + + typedef union + { + struct + { + /** @brief Reserved */ + uint64_t Reserved0 : 24; + /** @brief Destination */ + uint64_t Destination : 8; + }; + uint64_t raw; + } __packed InterruptCommandRegisterHigh; + + typedef union + { + struct + { + /** @brief Interrupt Vector */ + uint64_t Vector : 8; + /** @brief Delivery Mode */ + uint64_t DeliveryMode : 3; + /** @brief Destination Mode + * + * 0: Physical + * 1: Logical + */ + uint64_t DestinationMode : 1; + /** @brief Delivery Status */ + uint64_t DeliveryStatus : 1; + /** @brief Interrupt Input Pin Polarity + * + * 0: Active High + * 1: Active Low + */ + uint64_t Polarity : 1; + /** @brief Remote IRR */ + uint64_t RemoteIRR : 1; + /** @brief Trigger Mode + * + * 0: Edge + * 1: Level + */ + uint64_t TriggerMode : 1; + /** @brief Mask */ + uint64_t Mask : 1; + /** @brief Reserved */ + uint64_t Reserved0 : 15; + /** @brief Reserved */ + uint64_t Reserved1 : 24; + /** @brief Destination */ + uint64_t DestinationID : 8; + }; + struct + { + uint64_t Low; + uint64_t High; + } split; + uint64_t raw; + } __packed IOAPICRedirectEntry; + + typedef union + { + struct + { + uint64_t Version : 8; + uint64_t Reserved : 8; + uint64_t MaximumRedirectionEntry : 8; + uint64_t Reserved2 : 8; + }; + uint64_t raw; + } __packed IOAPICVersion; + + class APIC + { + private: + bool x2APICSupported = false; + uint64_t APICBaseAddress = 0; + + public: + uint32_t Read(uint32_t Register); + void Write(uint32_t Register, uint32_t Value); + void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value); + uint32_t IORead(uint64_t Base, uint32_t Register); + void EOI(); + void RedirectIRQs(int CPU = 0); + void WaitForIPI(); + void IPI(uint8_t CPU, InterruptCommandRegisterLow icr); + void SendInitIPI(uint8_t CPU); + void SendStartupIPI(uint8_t CPU, uint64_t StartupAddress); + uint32_t IOGetMaxRedirect(uint32_t APICID); + void RawRedirectIRQ(uint8_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status); + void RedirectIRQ(int CPU, uint16_t IRQ, int Status); + APIC(int Core); + ~APIC(); + }; + + class Timer : public Interrupts::Handler + { + private: + APIC *lapic; + uint64_t Ticks = 0; + void OnInterruptReceived(CPU::x32::TrapFrame *Frame); + + public: + uint64_t GetTicks() { return Ticks; } + void OneShot(uint32_t Vector, uint64_t Miliseconds); + Timer(APIC *apic); + ~Timer(); + }; +} + +#endif // !__FENNIX_KERNEL_APIC_H__ diff --git a/Kernel/Architecture/i386/cpu/gdt.hpp b/Kernel/Architecture/i386/cpu/gdt.hpp new file mode 100644 index 00000000..92e43015 --- /dev/null +++ b/Kernel/Architecture/i386/cpu/gdt.hpp @@ -0,0 +1,162 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_GDT_H__ +#define __FENNIX_KERNEL_GDT_H__ + +#include + +namespace GlobalDescriptorTable +{ + /** @brief The GDT Access Table + * @details For more information, see https://wiki.osdev.org/Global_Descriptor_Table + */ + union GlobalDescriptorTableAccess + { + struct + { + /** @brief Access bit. + * @note The CPU sets this bit to 1 when the segment is accessed. + */ + uint8_t A : 1; + + /** @brief Readable bit for code segments, writable bit for data segments. + * @details For code segments, this bit must be 1 for the segment to be readable. + * @details For data segments, this bit must be 1 for the segment to be writable. + */ + uint8_t RW : 1; + + /** @brief Direction bit for data segments, conforming bit for code segments. + * @details For data segments, this bit must be 1 for the segment to grow up (higher addresses). + * @details For code segments, this bit must be 1 for code in the segment to be able to be executed from an equal or lower privilege level. + */ + uint8_t DC : 1; + + /** @brief Executable bit. + * @details This bit must be 1 for code-segment descriptors. + * @details This bit must be 0 for data-segment and system descriptors. + */ + uint8_t E : 1; + + /** @brief Descriptor type. + * @details This bit must be 0 for system descriptors. + * @details This bit must be 1 for code or data segment descriptor. + */ + uint8_t S : 1; + + /** @brief Descriptor privilege level. + * @details This field determines the privilege level of the segment. + * @details 0 = kernel mode, 3 = user mode. + */ + uint8_t DPL : 2; + + /** @brief Present bit. + * @details This bit must be 1 for all valid descriptors. + */ + uint8_t P : 1; + } __packed; + uint8_t Raw; + }; + + union GlobalDescriptorTableFlags + { + // TODO: Add more flags. + struct + { + /** @brief Unknown. */ + uint8_t Unknown : 5; + + /** @brief Long mode. + * @details If the long mode bit is clear, the segment is in 32-bit protected mode. + * @details If the long mode bit is set, the segment is in 64-bit long mode. + */ + uint8_t L : 1; + } __packed; + uint8_t Raw; + }; + + typedef struct _TaskStateSegmentEntry + { + /* LOW */ + uint16_t Length; + uint16_t BaseLow; + uint8_t BaseMiddle; + GlobalDescriptorTableAccess Flags; + uint8_t Granularity; + uint8_t BaseHigh; + /* HIGH */ + uint32_t BaseUpper; + uint32_t Reserved; + } __packed TaskStateSegmentEntry; + + typedef struct _TaskStateSegment + { + uint32_t Reserved0 __aligned(16); + uint64_t StackPointer[3]; + uint64_t Reserved1; + uint64_t InterruptStackTable[7]; + uint16_t Reserved2; + uint16_t IOMapBaseAddressOffset; + } __packed TaskStateSegment; + + typedef struct _GlobalDescriptorTableEntry + { + /** @brief Length */ + uint16_t Length; + /** @brief Low Base */ + uint16_t BaseLow; + /** @brief Middle Base */ + uint8_t BaseMiddle; + /** @brief Access */ + GlobalDescriptorTableAccess Access; + /** @brief Flags */ + GlobalDescriptorTableFlags Flags; + /** @brief High Base */ + uint8_t BaseHigh; + } __packed GlobalDescriptorTableEntry; + + typedef struct _GlobalDescriptorTableEntries + { + GlobalDescriptorTableEntry Null; + GlobalDescriptorTableEntry Code; + GlobalDescriptorTableEntry Data; + GlobalDescriptorTableEntry UserData; + GlobalDescriptorTableEntry UserCode; + TaskStateSegmentEntry TaskStateSegment; + } __packed GlobalDescriptorTableEntries; + + typedef struct _GlobalDescriptorTableDescriptor + { + /** @brief GDT entries length */ + uint16_t Length; + /** @brief GDT entries address */ + GlobalDescriptorTableEntries *Entries; + } __packed GlobalDescriptorTableDescriptor; + + extern void *CPUStackPointer[]; + extern TaskStateSegment tss[]; + void Init(int Core); + void SetKernelStack(void *Stack); +} + +#define GDT_KERNEL_CODE offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Code) +#define GDT_KERNEL_DATA offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Data) +#define GDT_USER_CODE (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserCode) | 3) +#define GDT_USER_DATA (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserData) | 3) +#define GDT_TSS (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, TaskStateSegment) | 3) + +#endif // !__FENNIX_KERNEL_GDT_H__ diff --git a/Kernel/Architecture/i386/cpu/idt.hpp b/Kernel/Architecture/i386/cpu/idt.hpp new file mode 100644 index 00000000..204072aa --- /dev/null +++ b/Kernel/Architecture/i386/cpu/idt.hpp @@ -0,0 +1,28 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_IDT_H__ +#define __FENNIX_KERNEL_IDT_H__ + +#include + +namespace InterruptDescriptorTable +{ + void Init(int Core); +} + +#endif // !__FENNIX_KERNEL_IDT_H__ diff --git a/Kernel/Architecture/i386/linker.ld b/Kernel/Architecture/i386/linker.ld new file mode 100644 index 00000000..2e427dd4 --- /dev/null +++ b/Kernel/Architecture/i386/linker.ld @@ -0,0 +1,93 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) + +KERNEL_VMA = 0xC0000000; + +ENTRY(_start) + +SECTIONS +{ + . = 0x100000; + + _bootstrap_start = .; + .bootstrap : + { + *(.multiboot) + *(.multiboot2) + *(.bootstrap .bootstrap.*) + } + . += CONSTANT(MAXPAGESIZE); + _bootstrap_end = ALIGN(CONSTANT(MAXPAGESIZE)); + + . += KERNEL_VMA; + . += CONSTANT(MAXPAGESIZE); + _kernel_start = ALIGN(CONSTANT(MAXPAGESIZE)); + .text : AT(ADDR(.text) - KERNEL_VMA) + { + *(.text .text.*) + } + _kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + + .data : AT(ADDR(.data) - KERNEL_VMA) + { + *(.data .data.*) + } + _kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + + .rodata : AT(ADDR(.rodata) - KERNEL_VMA) + { + *(.rodata .rodata.*) + } + _kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + + .init_array : AT(ADDR(.init_array) - KERNEL_VMA) + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(.init_array .ctors)) + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : AT(ADDR(.fini_array) - KERNEL_VMA) + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP(*(.fini_array .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + . += CONSTANT(MAXPAGESIZE); + + .bss : AT(ADDR(.bss) - KERNEL_VMA) + { + *(COMMON) + *(.bss .bss.*) + } + . += CONSTANT(MAXPAGESIZE); + _kernel_end = ALIGN(CONSTANT(MAXPAGESIZE)); + + /DISCARD/ : + { + *(.comment*) + *(.note*) + } +} diff --git a/Kernel/Architecture/i386/runtime/crt0.c b/Kernel/Architecture/i386/runtime/crt0.c new file mode 100644 index 00000000..35cf1abd --- /dev/null +++ b/Kernel/Architecture/i386/runtime/crt0.c @@ -0,0 +1,15 @@ +// #include + +// #include + +// int Entry(void *Info); + +// void _start(void *Raw) +// { +// error("Todo"); +// while (1) +// asmv("hlt"); +// Entry(NULL); +// return; +// } +// C stuff \ No newline at end of file diff --git a/Kernel/Architecture/i386/runtime/crt1.c b/Kernel/Architecture/i386/runtime/crt1.c new file mode 100644 index 00000000..1369bfeb --- /dev/null +++ b/Kernel/Architecture/i386/runtime/crt1.c @@ -0,0 +1,15 @@ +#include + +#include + +int Entry(void *Info); + +// void _start(void *Raw) +// { +// UNUSED(Raw); +// error("ERROR! INVALID BOOT PROTOCOL!"); +// while (1) +// asmv("hlt"); +// Entry(NULL); +// return; +// } diff --git a/Kernel/Architecture/i386/runtime/crtbegin.c b/Kernel/Architecture/i386/runtime/crtbegin.c new file mode 100644 index 00000000..507125b3 --- /dev/null +++ b/Kernel/Architecture/i386/runtime/crtbegin.c @@ -0,0 +1 @@ +// C++ constructor/destructor stuff \ No newline at end of file diff --git a/Kernel/Architecture/i386/runtime/crtend.c b/Kernel/Architecture/i386/runtime/crtend.c new file mode 100644 index 00000000..507125b3 --- /dev/null +++ b/Kernel/Architecture/i386/runtime/crtend.c @@ -0,0 +1 @@ +// C++ constructor/destructor stuff \ No newline at end of file diff --git a/Kernel/Architecture/i386/runtime/crti.S b/Kernel/Architecture/i386/runtime/crti.S new file mode 100644 index 00000000..7f9924ad --- /dev/null +++ b/Kernel/Architecture/i386/runtime/crti.S @@ -0,0 +1,13 @@ +.section .init +.global _init +.type _init, @function +_init: + push %ebp + mov %esp, %ebp + +.section .fini +.global _fini +.type _fini, @function +_fini: + push %ebp + mov %esp, %ebp diff --git a/Kernel/Architecture/i386/runtime/crtn.S b/Kernel/Architecture/i386/runtime/crtn.S new file mode 100644 index 00000000..aa67ce13 --- /dev/null +++ b/Kernel/Architecture/i386/runtime/crtn.S @@ -0,0 +1,7 @@ +.section .init + pop %ebp + ret + +.section .fini + pop %ebp + ret diff --git a/Kernel/Core/CPU.cpp b/Kernel/Core/CPU.cpp new file mode 100644 index 00000000..8c5d5547 --- /dev/null +++ b/Kernel/Core/CPU.cpp @@ -0,0 +1,492 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include +#include + +#include "../kernel.h" + +namespace CPU +{ + static bool SSEEnabled = false; + + char *Vendor() + { + static char Vendor[13] = {0}; + if (Vendor[0] != 0) + return Vendor; +#if defined(a64) + uint32_t eax, ebx, ecx, edx; + x64::cpuid(0x0, &eax, &ebx, &ecx, &edx); + memcpy(Vendor + 0, &ebx, 4); + memcpy(Vendor + 4, &edx, 4); + memcpy(Vendor + 8, &ecx, 4); +#elif defined(a32) + uint32_t eax, ebx, ecx, edx; + x32::cpuid(0x0, &eax, &ebx, &ecx, &edx); + memcpy(Vendor + 0, &ebx, 4); + memcpy(Vendor + 4, &edx, 4); + memcpy(Vendor + 8, &ecx, 4); +#elif defined(aa64) + asmv("mrs %0, MIDR_EL1" + : "=r"(Vendor[0])); +#endif + return Vendor; + } + + char *Name() + { + static char Name[49] = {0}; + if (Name[0] != 0) + return Name; +#if defined(a64) + uint32_t eax, ebx, ecx, edx; + x64::cpuid(0x80000002, &eax, &ebx, &ecx, &edx); + memcpy(Name + 0, &eax, 4); + memcpy(Name + 4, &ebx, 4); + memcpy(Name + 8, &ecx, 4); + memcpy(Name + 12, &edx, 4); + x64::cpuid(0x80000003, &eax, &ebx, &ecx, &edx); + memcpy(Name + 16, &eax, 4); + memcpy(Name + 20, &ebx, 4); + memcpy(Name + 24, &ecx, 4); + memcpy(Name + 28, &edx, 4); + x64::cpuid(0x80000004, &eax, &ebx, &ecx, &edx); + memcpy(Name + 32, &eax, 4); + memcpy(Name + 36, &ebx, 4); + memcpy(Name + 40, &ecx, 4); + memcpy(Name + 44, &edx, 4); +#elif defined(a32) + uint32_t eax, ebx, ecx, edx; + x32::cpuid(0x80000002, &eax, &ebx, &ecx, &edx); + memcpy(Name + 0, &eax, 4); + memcpy(Name + 4, &ebx, 4); + memcpy(Name + 8, &ecx, 4); + memcpy(Name + 12, &edx, 4); + x32::cpuid(0x80000003, &eax, &ebx, &ecx, &edx); + memcpy(Name + 16, &eax, 4); + memcpy(Name + 20, &ebx, 4); + memcpy(Name + 24, &ecx, 4); + memcpy(Name + 28, &edx, 4); + x32::cpuid(0x80000004, &eax, &ebx, &ecx, &edx); + memcpy(Name + 32, &eax, 4); + memcpy(Name + 36, &ebx, 4); + memcpy(Name + 40, &ecx, 4); + memcpy(Name + 44, &edx, 4); +#elif defined(aa64) + asmv("mrs %0, MIDR_EL1" + : "=r"(Name[0])); +#endif + return Name; + } + + char *Hypervisor() + { + static char Hypervisor[13] = {0}; + if (Hypervisor[0] != 0) + return Hypervisor; +#if defined(a64) + uint32_t eax, ebx, ecx, edx; + x64::cpuid(0x40000000, &eax, &ebx, &ecx, &edx); + memcpy(Hypervisor + 0, &ebx, 4); + memcpy(Hypervisor + 4, &ecx, 4); + memcpy(Hypervisor + 8, &edx, 4); +#elif defined(a32) + uint32_t eax, ebx, ecx, edx; + x64::cpuid(0x40000000, &eax, &ebx, &ecx, &edx); + memcpy(Hypervisor + 0, &ebx, 4); + memcpy(Hypervisor + 4, &ecx, 4); + memcpy(Hypervisor + 8, &edx, 4); +#elif defined(aa64) + asmv("mrs %0, MIDR_EL1" + : "=r"(Hypervisor[0])); +#endif + return Hypervisor; + } + + bool Interrupts(InterruptsType Type) + { + switch (Type) + { + case Check: + { + uintptr_t Flags; +#if defined(a64) + asmv("pushfq"); + asmv("popq %0" + : "=r"(Flags)); + return Flags & (1 << 9); +#elif defined(a32) + asmv("pushfl"); + asmv("popl %0" + : "=r"(Flags)); + return Flags & (1 << 9); +#elif defined(aa64) + asmv("mrs %0, daif" + : "=r"(Flags)); + return !(Flags & (1 << 2)); +#endif + } + case Enable: + { +#if defined(a86) + asmv("sti"); +#elif defined(aa64) + asmv("msr daifclr, #2"); +#endif + return true; + } + case Disable: + { +#if defined(a86) + asmv("cli"); +#elif defined(aa64) + asmv("msr daifset, #2"); +#endif + return true; + } + default: + break; + } + return false; + } + + void *PageTable(void *PT) + { +#if defined(a64) + if (PT) + asmv("movq %0, %%cr3" + : + : "r"(PT)); + else + asmv("movq %%cr3, %0" + : "=r"(PT)); +#elif defined(a32) + if (PT) + asmv("movl %0, %%cr3" + : + : "r"(PT)); + else + asmv("movl %%cr3, %0" + : "=r"(PT)); +#elif defined(aa64) + if (PT) + asmv("msr ttbr0_el1, %0" + : + : "r"(PT)); + else + asmv("mrs %0, ttbr0_el1" + : "=r"(PT)); +#endif + return PT; + } + + void InitializeFeatures(long Core) + { +#if defined(a64) + bool PGESupport = false; + bool SSESupport = false; + bool UMIPSupport = false; + bool SMEPSupport = false; + bool SMAPSupport = false; + + static int BSP = 0; + x64::CR0 cr0 = x64::readcr0(); + x64::CR4 cr4 = x64::readcr4(); + + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid1; + CPU::x86::AMD::CPUID0x00000007 cpuid7; + cpuid1.Get(); + cpuid7.Get(); + + PGESupport = cpuid1.EDX.PGE; + SSESupport = cpuid1.EDX.SSE; + SMEPSupport = cpuid7.EBX.SMEP; + SMAPSupport = cpuid7.EBX.SMAP; + UMIPSupport = cpuid7.ECX.UMIP; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid1; + CPU::x86::Intel::CPUID0x00000007_0 cpuid7_0; + cpuid1.Get(); + cpuid7_0.Get(); + PGESupport = cpuid1.EDX.PGE; + SSESupport = cpuid1.EDX.SSE; + SMEPSupport = cpuid7_0.EBX.SMEP; + SMAPSupport = cpuid7_0.EBX.SMAP; + UMIPSupport = cpuid7_0.ECX.UMIP; + } + + if (Config.SIMD == false) + { + debug("Disabling SSE support..."); + SSESupport = false; + } + + if (PGESupport) + { + debug("Enabling global pages support..."); + if (!BSP) + KPrint("Global Pages is supported."); + cr4.PGE = 1; + } + + bool SSEEnableAfter = false; + + /* Not sure if my code is not working properly or something else is the issue. */ + if ((strcmp(Hypervisor(), x86_CPUID_VENDOR_TCG) != 0 && + strcmp(Hypervisor(), x86_CPUID_VENDOR_VIRTUALBOX) != 0) && + SSESupport) + { + debug("Enabling SSE support..."); + if (!BSP) + KPrint("SSE is supported."); + cr0.EM = 0; + cr0.MP = 1; + cr4.OSFXSR = 1; + cr4.OSXMMEXCPT = 1; + + CPUData *CoreData = GetCPU(Core); + CoreData->Data.FPU = (CPU::x64::FXState *)KernelAllocator.RequestPages(TO_PAGES(sizeof(CPU::x64::FXState) + 1)); + memset(CoreData->Data.FPU, 0, FROM_PAGES(TO_PAGES(sizeof(CPU::x64::FXState)))); + CoreData->Data.FPU->mxcsr = 0b0001111110000000; + CoreData->Data.FPU->mxcsrmask = 0b1111111110111111; + CoreData->Data.FPU->fcw = 0b0000001100111111; + CPU::x64::fxrstor(CoreData->Data.FPU); + + SSEEnableAfter = true; + } + + cr0.NW = 0; + cr0.CD = 0; + cr0.WP = 1; + + x64::writecr0(cr0); + + if (strcmp(Hypervisor(), x86_CPUID_VENDOR_VIRTUALBOX) != 0 && + strcmp(Hypervisor(), x86_CPUID_VENDOR_TCG) != 0) + { + // FIXME: I don't think this is reporting correctly. This has to be fixed asap. + debug("Enabling UMIP, SMEP & SMAP support..."); + if (UMIPSupport) + { + if (!BSP) + KPrint("UMIP is supported."); + debug("UMIP is supported."); + // cr4.UMIP = 1; + } + + if (SMEPSupport) + { + if (!BSP) + KPrint("SMEP is supported."); + debug("SMEP is supported."); + // cr4.SMEP = 1; + } + + if (SMAPSupport) + { + if (!BSP) + KPrint("SMAP is supported."); + debug("SMAP is supported."); + // cr4.SMAP = 1; + } + } + else + { + if (!BSP) + { + if (strcmp(Hypervisor(), x86_CPUID_VENDOR_VIRTUALBOX) == 0) + KPrint("VirtualBox detected. Not using UMIP, SMEP & SMAP"); + else if (strcmp(Hypervisor(), x86_CPUID_VENDOR_TCG) == 0) + KPrint("QEMU (TCG) detected. Not using UMIP, SMEP & SMAP"); + } + } + + debug("Writing CR4..."); + x64::writecr4(cr4); + debug("Wrote CR4."); + + debug("Enabling PAT support..."); + x64::wrmsr(x64::MSR_CR_PAT, 0x6 | (0x0 << 8) | (0x1 << 16)); + if (!BSP++) + trace("Features for BSP initialized."); + if (SSEEnableAfter) + SSEEnabled = true; +#elif defined(a32) +#elif defined(aa64) +#endif + } + + uintptr_t Counter() + { + // TODO: Get the counter from the x2APIC or any other timer that is available. (TSC is not available on all CPUs) + uintptr_t Counter; +#if defined(a64) + asmv("rdtsc" + : "=A"(Counter)); +#elif defined(a32) + asmv("rdtsc" + : "=A"(Counter)); +#elif defined(aa64) + asmv("mrs %0, cntvct_el0" + : "=r"(Counter)); +#endif + return Counter; + } + + uint64_t CheckSIMD() + { +#if defined(a32) + return SIMD_NONE; /* TODO: Support x86 SIMD on x32 */ +#endif + + if (unlikely(!SSEEnabled)) + return SIMD_NONE; + + // return SIMD_SSE; + +#if defined(a86) + static uint64_t SIMDType = SIMD_NONE; + + if (likely(SIMDType != SIMD_NONE)) + return SIMDType; + + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid; + asmv("cpuid" + : "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw) + : "a"(0x1)); + + if (cpuid.ECX.SSE42) + SIMDType |= SIMD_SSE42; + else if (cpuid.ECX.SSE41) + SIMDType |= SIMD_SSE41; + else if (cpuid.ECX.SSE3) + SIMDType |= SIMD_SSE3; + else if (cpuid.EDX.SSE2) + SIMDType |= SIMD_SSE2; + else if (cpuid.EDX.SSE) + SIMDType |= SIMD_SSE; + +#ifdef DEBUG + if (cpuid.ECX.SSE42) + debug("SSE4.2 is supported."); + if (cpuid.ECX.SSE41) + debug("SSE4.1 is supported."); + if (cpuid.ECX.SSE3) + debug("SSE3 is supported."); + if (cpuid.EDX.SSE2) + debug("SSE2 is supported."); + if (cpuid.EDX.SSE) + debug("SSE is supported."); +#endif + + return SIMDType; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid; + asmv("cpuid" + : "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw) + : "a"(0x1)); + + if (cpuid.ECX.SSE4_2) + SIMDType |= SIMD_SSE42; + else if (cpuid.ECX.SSE4_1) + SIMDType |= SIMD_SSE41; + else if (cpuid.ECX.SSE3) + SIMDType |= SIMD_SSE3; + else if (cpuid.EDX.SSE2) + SIMDType |= SIMD_SSE2; + else if (cpuid.EDX.SSE) + SIMDType |= SIMD_SSE; + +#ifdef DEBUG + if (cpuid.ECX.SSE4_2) + debug("SSE4.2 is supported."); + if (cpuid.ECX.SSE4_1) + debug("SSE4.1 is supported."); + if (cpuid.ECX.SSE3) + debug("SSE3 is supported."); + if (cpuid.EDX.SSE2) + debug("SSE2 is supported."); + if (cpuid.EDX.SSE) + debug("SSE is supported."); +#endif + return SIMDType; + } + + debug("No SIMD support."); +#endif // a64 || a32 + return SIMD_NONE; + } + + bool CheckSIMD(x86SIMDType Type) + { + if (unlikely(!SSEEnabled)) + return false; + +#if defined(a86) + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid; + asmv("cpuid" + : "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw) + : "a"(0x1)); + + if (Type == SIMD_SSE42) + return cpuid.ECX.SSE42; + else if (Type == SIMD_SSE41) + return cpuid.ECX.SSE41; + else if (Type == SIMD_SSE3) + return cpuid.ECX.SSE3; + else if (Type == SIMD_SSE2) + return cpuid.EDX.SSE2; + else if (Type == SIMD_SSE) + return cpuid.EDX.SSE; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid; + asmv("cpuid" + : "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw) + : "a"(0x1)); + + if (Type == SIMD_SSE42) + return cpuid.ECX.SSE4_2; + else if (Type == SIMD_SSE41) + return cpuid.ECX.SSE4_1; + else if (Type == SIMD_SSE3) + return cpuid.ECX.SSE3; + else if (Type == SIMD_SSE2) + return cpuid.EDX.SSE2; + else if (Type == SIMD_SSE) + return cpuid.EDX.SSE; + } +#endif // a64 || a32 + return false; + } +} diff --git a/Kernel/Core/Crash/CrashDetails.cpp b/Kernel/Core/Crash/CrashDetails.cpp new file mode 100644 index 00000000..bc1b85ec --- /dev/null +++ b/Kernel/Core/Crash/CrashDetails.cpp @@ -0,0 +1,346 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../crashhandler.hpp" +#include "chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +static const char *PageFaultDescriptions[8] = { + "Supervisory process tried to read a non-present page entry\n", + "Supervisory process tried to read a page and caused a protection fault\n", + "Supervisory process tried to write to a non-present page entry\n", + "Supervisory process tried to write a page and caused a protection fault\n", + "User process tried to read a non-present page entry\n", + "User process tried to read a page and caused a protection fault\n", + "User process tried to write to a non-present page entry\n", + "User process tried to write a page and caused a protection fault\n"}; + +SafeFunction void DivideByZeroExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Divide by zero exception\n"); + UNUSED(Frame); +} + +SafeFunction void DebugExceptionHandler(CHArchTrapFrame *Frame) +{ + CrashHandler::EHPrint("Kernel triggered debug exception.\n"); + UNUSED(Frame); +} + +SafeFunction void NonMaskableInterruptExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("NMI exception"); + UNUSED(Frame); +} + +SafeFunction void BreakpointExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Breakpoint exception"); + UNUSED(Frame); +} + +SafeFunction void OverflowExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Overflow exception"); + UNUSED(Frame); +} + +SafeFunction void BoundRangeExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Bound range exception"); + UNUSED(Frame); +} + +SafeFunction void InvalidOpcodeExceptionHandler(CHArchTrapFrame *Frame) +{ + CrashHandler::EHPrint("Kernel tried to execute an invalid opcode.\n"); + UNUSED(Frame); +} + +SafeFunction void DeviceNotAvailableExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Device not available exception"); + UNUSED(Frame); +} + +SafeFunction void DoubleFaultExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Double fault exception"); + UNUSED(Frame); +} + +SafeFunction void CoprocessorSegmentOverrunExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Coprocessor segment overrun exception"); + UNUSED(Frame); +} + +SafeFunction void InvalidTSSExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Invalid TSS exception"); + UNUSED(Frame); +} + +SafeFunction void SegmentNotPresentExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Segment not present exception"); + UNUSED(Frame); +} + +SafeFunction void StackFaultExceptionHandler(CHArchTrapFrame *Frame) +{ + CPU::x64::SelectorErrorCode SelCode = {.raw = Frame->ErrorCode}; +#if defined(a64) + CrashHandler::EHPrint("Stack segment fault at address %#lx\n", Frame->rip); +#elif defined(a32) + CrashHandler::EHPrint("Stack segment fault at address %#lx\n", Frame->eip); +#elif defined(aa64) +#endif + CrashHandler::EHPrint("External: %d\n", SelCode.External); + CrashHandler::EHPrint("Table: %d\n", SelCode.Table); + CrashHandler::EHPrint("Index: %#x\n", SelCode.Idx); + CrashHandler::EHPrint("Error code: %#lx\n", Frame->ErrorCode); +} + +SafeFunction void GeneralProtectionExceptionHandler(CHArchTrapFrame *Frame) +{ + CPU::x64::SelectorErrorCode SelCode = {.raw = Frame->ErrorCode}; + // switch (SelCode.Table) + // { + // case CPU::x64::0b00: + // memcpy(desc_tmp, "GDT", 3); + // break; + // case CPU::x64::0b01: + // memcpy(desc_tmp, "IDT", 3); + // break; + // case CPU::x64::0b10: + // memcpy(desc_tmp, "LDT", 3); + // break; + // case CPU::x64::0b11: + // memcpy(desc_tmp, "IDT", 3); + // break; + // default: + // memcpy(desc_tmp, "Unknown", 7); + // break; + // } + CrashHandler::EHPrint("Kernel performed an illegal operation.\n"); + CrashHandler::EHPrint("External: %d\n", SelCode.External); + CrashHandler::EHPrint("Table: %d\n", SelCode.Table); + CrashHandler::EHPrint("Index: %#x\n", SelCode.Idx); +} + +SafeFunction void PageFaultExceptionHandler(CHArchTrapFrame *Frame) +{ + CPU::x64::PageFaultErrorCode params = {.raw = (uint32_t)Frame->ErrorCode}; +#if defined(a64) + CrashHandler::EHPrint("\eAFAFAFAn exception occurred at %#lx by %#lx\n", CrashHandler::PageFaultAddress, Frame->rip); +#elif defined(a32) + CrashHandler::EHPrint("\eAFAFAFAn exception occurred at %#lx by %#lx\n", CrashHandler::PageFaultAddress, Frame->eip); +#elif defined(aa64) +#endif + CrashHandler::EHPrint("Page: %s\n", params.P ? "Present" : "Not Present"); + CrashHandler::EHPrint("Write Operation: %s\n", params.W ? "Read-Only" : "Read-Write"); + CrashHandler::EHPrint("Processor Mode: %s\n", params.U ? "User-Mode" : "Kernel-Mode"); + CrashHandler::EHPrint("CPU Reserved Bits: %s\n", params.R ? "Reserved" : "Unreserved"); + CrashHandler::EHPrint("Caused By An Instruction Fetch: %s\n", params.I ? "Yes" : "No"); + CrashHandler::EHPrint("Caused By A Protection-Key Violation: %s\n", params.PK ? "Yes" : "No"); + CrashHandler::EHPrint("Caused By A Shadow Stack Access: %s\n", params.SS ? "Yes" : "No"); + CrashHandler::EHPrint("Caused By An SGX Violation: %s\n", params.SGX ? "Yes" : "No"); + if (Frame->ErrorCode & 0x00000008) + CrashHandler::EHPrint("One or more page directory entries contain reserved bits which are set to 1.\n"); + else + CrashHandler::EHPrint(PageFaultDescriptions[Frame->ErrorCode & 0b111]); + +#ifdef DEBUG + uintptr_t CheckPageFaultAddress = 0; + CheckPageFaultAddress = CrashHandler::PageFaultAddress; + if (CheckPageFaultAddress == 0) +#ifdef a64 + CheckPageFaultAddress = Frame->rip; +#elif defined(a32) + CheckPageFaultAddress = Frame->eip; +#elif defined(aa64) + CheckPageFaultAddress = 0; +#endif + +#if defined(a64) + Memory::Virtual vma = Memory::Virtual(((Memory::PageTable4 *)CPU::x64::readcr3().raw)); +#elif defined(a32) + Memory::Virtual vma = Memory::Virtual(((Memory::PageTable4 *)CPU::x32::readcr3().raw)); +#elif defined(aa64) + Memory::Virtual vma = Memory::Virtual(); +#warning "TODO: aa64" +#endif + + bool PageAvailable = vma.Check((void *)CheckPageFaultAddress); + debug("Page available (Check(...)): %s. %s", + PageAvailable ? "Yes" : "No", + (params.P && !PageAvailable) ? "CR2 == Present; Check() != Present??????" : "CR2 confirms Check() result."); + + if (PageAvailable) + { + bool Present = vma.Check((void *)CheckPageFaultAddress); + bool ReadWrite = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::RW); + bool User = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::US); + bool WriteThrough = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::PWT); + bool CacheDisabled = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::PCD); + bool Accessed = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::A); + bool Dirty = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::D); + bool Global = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::G); + /* ... */ + + debug("Page available: %s", Present ? "Yes" : "No"); + debug("Page read/write: %s", ReadWrite ? "Yes" : "No"); + debug("Page user/kernel: %s", User ? "User" : "Kernel"); + debug("Page write-through: %s", WriteThrough ? "Yes" : "No"); + debug("Page cache disabled: %s", CacheDisabled ? "Yes" : "No"); + debug("Page accessed: %s", Accessed ? "Yes" : "No"); + debug("Page dirty: %s", Dirty ? "Yes" : "No"); + debug("Page global: %s", Global ? "Yes" : "No"); + + if (Present) + { + uintptr_t CheckPageFaultLinearAddress = (uintptr_t)CheckPageFaultAddress; + CheckPageFaultLinearAddress &= 0xFFFFFFFFFFFFF000; + debug("%#lx -> %#lx", CheckPageFaultAddress, CheckPageFaultLinearAddress); + + Memory::Virtual::PageMapIndexer Index = Memory::Virtual::PageMapIndexer((uintptr_t)CheckPageFaultLinearAddress); + debug("Index for %#lx is PML:%d PDPTE:%d PDE:%d PTE:%d", + CheckPageFaultLinearAddress, + Index.PMLIndex, + Index.PDPTEIndex, + Index.PDEIndex, + Index.PTEIndex); +#if defined(a64) + Memory::PageMapLevel4 PML4 = ((Memory::PageTable4 *)CPU::x64::readcr3().raw)->Entries[Index.PMLIndex]; +#elif defined(a32) + Memory::PageMapLevel4 PML4 = ((Memory::PageTable4 *)CPU::x32::readcr3().raw)->Entries[Index.PMLIndex]; +#elif defined(aa64) + Memory::PageMapLevel4 PML4 = {.raw = 0}; +#warning "TODO: aa64" +#endif + + Memory::PageDirectoryPointerTableEntryPtr *PDPTE = (Memory::PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + Memory::PageDirectoryEntryPtr *PDE = (Memory::PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + Memory::PageTableEntryPtr *PTE = (Memory::PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:%#lx", + Index.PMLIndex, 0, 0, 0, + PML4.Present ? "1" : "0", + PML4.ReadWrite ? "1" : "0", + PML4.UserSupervisor ? "1" : "0", + PML4.WriteThrough ? "1" : "0", + PML4.CacheDisable ? "1" : "0", + PML4.Accessed ? "1" : "0", + PML4.ExecuteDisable ? "1" : "0", + PML4.GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:%#lx", + Index.PMLIndex, Index.PDPTEIndex, 0, 0, + PDPTE->Entries[Index.PDPTEIndex].Present ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].ReadWrite ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].UserSupervisor ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].WriteThrough ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].CacheDisable ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].Accessed ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].ExecuteDisable ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:%#lx", + Index.PMLIndex, Index.PDPTEIndex, Index.PDEIndex, 0, + PDE->Entries[Index.PDEIndex].Present ? "1" : "0", + PDE->Entries[Index.PDEIndex].ReadWrite ? "1" : "0", + PDE->Entries[Index.PDEIndex].UserSupervisor ? "1" : "0", + PDE->Entries[Index.PDEIndex].WriteThrough ? "1" : "0", + PDE->Entries[Index.PDEIndex].CacheDisable ? "1" : "0", + PDE->Entries[Index.PDEIndex].Accessed ? "1" : "0", + PDE->Entries[Index.PDEIndex].ExecuteDisable ? "1" : "0", + PDE->Entries[Index.PDEIndex].GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s D:%s PAT:%s G:%s PK:%d NX:%s Address:%#lx", + Index.PMLIndex, Index.PDPTEIndex, Index.PDEIndex, Index.PTEIndex, + PTE->Entries[Index.PTEIndex].Present ? "1" : "0", + PTE->Entries[Index.PTEIndex].ReadWrite ? "1" : "0", + PTE->Entries[Index.PTEIndex].UserSupervisor ? "1" : "0", + PTE->Entries[Index.PTEIndex].WriteThrough ? "1" : "0", + PTE->Entries[Index.PTEIndex].CacheDisable ? "1" : "0", + PTE->Entries[Index.PTEIndex].Accessed ? "1" : "0", + PTE->Entries[Index.PTEIndex].Dirty ? "1" : "0", + PTE->Entries[Index.PTEIndex].PageAttributeTable ? "1" : "0", + PTE->Entries[Index.PTEIndex].Global ? "1" : "0", + PTE->Entries[Index.PTEIndex].ProtectionKey, + PTE->Entries[Index.PTEIndex].ExecuteDisable ? "1" : "0", + PTE->Entries[Index.PTEIndex].GetAddress() << 12); + } + } +#endif +} + +SafeFunction void x87FloatingPointExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("x87 floating point exception"); + UNUSED(Frame); +} + +SafeFunction void AlignmentCheckExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Alignment check exception"); + UNUSED(Frame); +} + +SafeFunction void MachineCheckExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Machine check exception"); + UNUSED(Frame); +} + +SafeFunction void SIMDFloatingPointExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("SIMD floating point exception"); + UNUSED(Frame); +} + +SafeFunction void VirtualizationExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Virtualization exception"); + UNUSED(Frame); +} + +SafeFunction void SecurityExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Security exception"); + UNUSED(Frame); +} + +SafeFunction void UnknownExceptionHandler(CHArchTrapFrame *Frame) +{ + fixme("Unknown exception"); + UNUSED(Frame); +} diff --git a/Kernel/Core/Crash/CrashHandler.cpp b/Kernel/Core/Crash/CrashHandler.cpp new file mode 100644 index 00000000..6eabd5b0 --- /dev/null +++ b/Kernel/Core/Crash/CrashHandler.cpp @@ -0,0 +1,1122 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../crashhandler.hpp" +#include "chfcts.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/cpu/gdt.hpp" +#include "../Architecture/amd64/cpu/apic.hpp" +#elif defined(a32) +#include "../../Architecture/i386/cpu/gdt.hpp" +#include "../Architecture/i386/cpu/apic.hpp" +#elif defined(aa64) +#endif + +#include "../../kernel.h" +#include "../../DAPI.hpp" + +NewLock(UserInputLock); + +namespace CrashHandler +{ + uintptr_t PageFaultAddress = 0; + void *EHIntFrames[INT_FRAMES_MAX]; + static bool ExceptionOccurred = false; + int SBIdx = 255; + + SafeFunction void printfWrapper(char c, void *unused) + { + Display->Print(c, SBIdx, true); + UNUSED(unused); + } + + SafeFunction void EHPrint(const char *Format, ...) + { + va_list args; + va_start(args, Format); + vfctprintf(printfWrapper, NULL, Format, args); + va_end(args); + } + + SafeFunction void EHDumpData(void *Address, unsigned long Length) + { + EHPrint("-------------------------------------------------------------------------\n"); + Display->SetBuffer(SBIdx); + unsigned char *AddressChar = (unsigned char *)Address; + unsigned char Buffer[17]; + unsigned long Iterate; + for (Iterate = 0; Iterate < Length; Iterate++) + { + if ((Iterate % 16) == 0) + { + if (Iterate != 0) + EHPrint(" \e8A78FF%s\eAABBCC\n", Buffer); + EHPrint(" \e9E9E9E%04x\eAABBCC ", Iterate); + Display->SetBuffer(SBIdx); + } + EHPrint(" \e4287f5%02x\eAABBCC", AddressChar[Iterate]); + if ((AddressChar[Iterate] < 0x20) || (AddressChar[Iterate] > 0x7e)) + Buffer[Iterate % 16] = '.'; + else + Buffer[Iterate % 16] = AddressChar[Iterate]; + Buffer[(Iterate % 16) + 1] = '\0'; + } + + while ((Iterate % 16) != 0) + { + EHPrint(" "); + Display->SetBuffer(SBIdx); + Iterate++; + } + + EHPrint(" \e8A78FF%s\eAABBCC\n", Buffer); + EHPrint("-------------------------------------------------------------------------\n\n."); + Display->SetBuffer(SBIdx); + } + + SafeFunction char *TrimWhiteSpace(char *str) + { + char *end; + while (*str == ' ') + str++; + if (*str == 0) + return str; + end = str + strlen(str) - 1; + while (end > str && *end == ' ') + end--; + *(end + 1) = 0; + return str; + } + + CRData crashdata = {}; + + SafeFunction void DisplayTopOverlay() + { + Video::ScreenBuffer *sb = Display->GetBuffer(SBIdx); + Video::Font *f = Display->GetCurrentFont(); + Video::FontInfo fi = f->GetInfo(); + + for (uint32_t i = 0; i < sb->Width; i++) + for (uint32_t j = 0; j < fi.Height + 8; j++) + Display->SetPixel(i, j, 0x282828, SBIdx); + + Display->SetBufferCursor(SBIdx, 8, (fi.Height + 8) / 6); + switch (SBIdx) + { + case 255: + { + EHPrint("\eAAAAAAMAIN \e606060DETAILS \e606060FRAMES \e606060TASKS \e606060CONSOLE"); + break; + } + case 254: + { + EHPrint("\e606060MAIN \eAAAAAADETAILS \e606060FRAMES \e606060TASKS \e606060CONSOLE"); + break; + } + case 253: + { + EHPrint("\e606060MAIN \e606060DETAILS \eAAAAAAFRAMES \e606060TASKS \e606060CONSOLE"); + break; + } + case 252: + { + EHPrint("\e606060MAIN \e606060DETAILS \e606060FRAMES \eAAAAAATASKS \e606060CONSOLE"); + break; + } + case 251: + { + EHPrint("\e606060MAIN \e606060DETAILS \e606060FRAMES \e606060TASKS \eAAAAAACONSOLE"); + break; + } + default: + { + EHPrint("\e606060MAIN \e606060DETAILS \e606060FRAMES \e606060TASKS \e606060CONSOLE"); + break; + } + } + EHPrint(" \e00AAFF%ldMB / %ldMB (%ldMB Reserved)", + TO_MB(KernelAllocator.GetUsedMemory()), + TO_MB(KernelAllocator.GetTotalMemory()), + TO_MB(KernelAllocator.GetReservedMemory())); + EHPrint(" \eAA0F0F%s", CPU::Hypervisor()); + EHPrint(" \eAAF00F%s", CPU::Vendor()); + EHPrint(" \eAA00FF%s", CPU::Name()); + Display->SetBufferCursor(SBIdx, 0, fi.Height + 10); + + /* https://imgflip.com/i/77slbl */ + if ((Random::rand32() % 100) >= 98) + { + debug("Easter egg activated!"); + int BaseXOffset = sb->Width - 14; + int BaseYOffset = 8; + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 0, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 0, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 0, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 7, BaseYOffset + 0, 0x21852E, SBIdx); + + Display->SetPixel(BaseXOffset + 2, BaseYOffset + 1, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 1, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 1, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 5, BaseYOffset + 1, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 1, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 7, BaseYOffset + 1, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 8, BaseYOffset + 1, 0x21852E, SBIdx); + + Display->SetPixel(BaseXOffset + 1, BaseYOffset + 2, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 2, BaseYOffset + 2, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 2, 0xFFFFFF, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 2, 0x000000, SBIdx); + Display->SetPixel(BaseXOffset + 5, BaseYOffset + 2, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 2, 0xFFFFFF, SBIdx); + Display->SetPixel(BaseXOffset + 7, BaseYOffset + 2, 0x000000, SBIdx); + Display->SetPixel(BaseXOffset + 8, BaseYOffset + 2, 0x21852E, SBIdx); + + Display->SetPixel(BaseXOffset + 1, BaseYOffset + 3, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 2, BaseYOffset + 3, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 3, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 3, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 5, BaseYOffset + 3, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 3, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 7, BaseYOffset + 3, 0x21852E, SBIdx); + + Display->SetPixel(BaseXOffset + 0, BaseYOffset + 4, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 1, BaseYOffset + 4, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 2, BaseYOffset + 4, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 4, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 4, 0xA84832, SBIdx); + Display->SetPixel(BaseXOffset + 5, BaseYOffset + 4, 0xA84832, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 4, 0xA84832, SBIdx); + Display->SetPixel(BaseXOffset + 7, BaseYOffset + 4, 0xA84832, SBIdx); + + Display->SetPixel(BaseXOffset + 0, BaseYOffset + 5, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 1, BaseYOffset + 5, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 2, BaseYOffset + 5, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 5, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 5, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 5, BaseYOffset + 5, 0x21852E, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 5, 0x21852E, SBIdx); + + Display->SetPixel(BaseXOffset + 0, BaseYOffset + 6, 0x1216FF, SBIdx); + Display->SetPixel(BaseXOffset + 1, BaseYOffset + 6, 0x1216FF, SBIdx); + Display->SetPixel(BaseXOffset + 2, BaseYOffset + 6, 0x1216FF, SBIdx); + Display->SetPixel(BaseXOffset + 3, BaseYOffset + 6, 0x1216FF, SBIdx); + Display->SetPixel(BaseXOffset + 4, BaseYOffset + 6, 0x1216FF, SBIdx); + Display->SetPixel(BaseXOffset + 5, BaseYOffset + 6, 0x1216FF, SBIdx); + Display->SetPixel(BaseXOffset + 6, BaseYOffset + 6, 0x1216FF, SBIdx); + + Display->SetBuffer(SBIdx); + } + } + + SafeFunction void DisplayBottomOverlay() + { + Video::ScreenBuffer *sb = Display->GetBuffer(SBIdx); + Video::Font *f = Display->GetCurrentFont(); + Video::FontInfo fi = f->GetInfo(); + + for (uint32_t i = 0; i < sb->Width; i++) + for (uint32_t j = sb->Height - fi.Height - 8; j < sb->Height; j++) + Display->SetPixel(i, j, 0x282828, SBIdx); + + Display->SetBufferCursor(SBIdx, 8, sb->Height - fi.Height - 4); + EHPrint("\eAAAAAA> \eFAFAFA"); + } + + SafeFunction void ArrowInput(uint8_t key) + { + switch (key) + { + case KEY_D_UP: + if (SBIdx < 255) + SBIdx++; + else + return; + break; + case KEY_D_LEFT: + if (SBIdx < 255) + SBIdx++; + else + return; + break; + case KEY_D_RIGHT: + if (SBIdx > 251) + SBIdx--; + else + return; + break; + case KEY_D_DOWN: + if (SBIdx > 251) + SBIdx--; + else + return; + break; + default: + break; + } + Display->ClearBuffer(SBIdx); + DisplayTopOverlay(); + EHPrint("\eFAFAFA"); + + switch (SBIdx) + { + case 255: + { + DisplayMainScreen(crashdata); + break; + } + case 254: + { + DisplayDetailsScreen(crashdata); + break; + } + case 253: + { + DisplayStackFrameScreen(crashdata); + break; + } + case 252: + { + DisplayTasksScreen(crashdata); + break; + } + case 251: + { + DisplayConsoleScreen(crashdata); + break; + } + default: + { + break; + } + } + DisplayBottomOverlay(); + Display->SetBuffer(SBIdx); + } + + SafeFunction void UserInput(char *Input) + { + SmartCriticalSection(UserInputLock); + Display->ClearBuffer(SBIdx); + DisplayTopOverlay(); + EHPrint("\eFAFAFA"); + + if (strcmp(Input, "help") == 0) + { + EHPrint("Available commands are:\n"); + EHPrint("exit - Shutdown the OS.\n"); + EHPrint("reboot - Reboot the OS.\n"); + EHPrint("help - Display this help message.\n"); + EHPrint("showbuf,sb - Display the contents of a screen buffer.\n"); + EHPrint(" - A sleep timer will be enabled. This will cause the OS to sleep for an unknown amount of time.\n"); + EHPrint(" - \eFF4400WARNING: This can crash the system if a wrong buffer is selected.\eFAFAFA\n"); + EHPrint("ifr - Show interrupt frames.\n"); + EHPrint("tlb
- Print the page table entries\n"); + EHPrint("bitmap - Print the memory bitmap\n"); + EHPrint("mem - Print the memory allocation\n"); + EHPrint("cr - Print the CPU control register\n"); + EHPrint("tss - Print the CPU task state segment\n"); + EHPrint("dump
- Dump memory\n"); + EHPrint(" - \eFF4400WARNING: This can crash the system if you try to read from an unmapped page.\eFAFAFA\n"); + EHPrint("uartmemdmp - Dump the memory of a UART.\n"); + EHPrint("main - Show the main screen.\n"); + EHPrint("details - Show the details screen.\n"); + EHPrint("frames - Show the stack frame screen.\n"); + EHPrint("tasks - Show the tasks screen.\n"); + EHPrint("console - Show the console screen.\n"); + EHPrint("Also, you can use the arrow keys to navigate between the screens.\n"); + EHPrint("=========================================================================\n"); + EHPrint("Kernel Compiled at: %s %s with C++ Standard: %d\n", __DATE__, __TIME__, CPP_LANGUAGE_STANDARD); + EHPrint("C++ Language Version (__cplusplus): %ld\n", __cplusplus); + } + else if (strcmp(Input, "exit") == 0) + { + PowerManager->Shutdown(); + EHPrint("\eFFFFFFNow it's safe to turn off your computer."); + Display->SetBuffer(SBIdx); + CPU::Stop(); + } + else if (strcmp(Input, "reboot") == 0) + { + PowerManager->Reboot(); + EHPrint("\eFFFFFFNow it's safe to reboot your computer."); + Display->SetBuffer(SBIdx); + CPU::Stop(); + } + else if (strncmp(Input, "showbuf", 7) == 0 || strncmp(Input, "sb", 2) == 0) + { + char *arg = TrimWhiteSpace(Input + 7); + int tmpidx = SBIdx; + SBIdx = atoi(arg); + Display->SetBuffer(SBIdx); +#if defined(a86) + for (int i = 0; i < 5000000; i++) + inb(0x80); +#endif // a64 || a32 + SBIdx = tmpidx; + Display->SetBuffer(SBIdx); + } + else if (strncmp(Input, "ifr", 3) == 0) + { + char *arg = TrimWhiteSpace(Input + 3); + int CountI = atoi(arg); + int TotalCount = sizeof(EHIntFrames) / sizeof(EHIntFrames[0]); + + debug("Printing %ld interrupt frames.", CountI); + + if (CountI > TotalCount) + { + EHPrint("\eFF4400Count too big! Maximum allowed is %ld\eFAFAFA\n", TotalCount); + Display->SetBuffer(SBIdx); + } + else + { + for (int i = 0; i < CountI; i++) + { + if (EHIntFrames[i]) + { + if (!Memory::Virtual().Check(EHIntFrames[i])) + continue; + EHPrint("\n\e2565CC%p", EHIntFrames[i]); + EHPrint("\e7925CC-"); +#if defined(a64) + if ((uintptr_t)EHIntFrames[i] >= 0xFFFFFFFF80000000 && (uintptr_t)EHIntFrames[i] <= (uintptr_t)&_kernel_end) +#elif defined(a32) + if ((uintptr_t)EHIntFrames[i] >= 0xC0000000 && (uintptr_t)EHIntFrames[i] <= (uintptr_t)&_kernel_end) +#elif defined(aa64) + if ((uintptr_t)EHIntFrames[i] >= 0xFFFFFFFF80000000 && (uintptr_t)EHIntFrames[i] <= (uintptr_t)&_kernel_end) +#endif + EHPrint("\e25CCC9%s", KernelSymbolTable->GetSymbolFromAddress((uintptr_t)EHIntFrames[i])); + else + EHPrint("\eFF4CA9Outside Kernel"); +#if defined(a86) + for (int i = 0; i < 20000; i++) + inb(0x80); +#endif // a64 || a32 + Display->SetBuffer(SBIdx); + } + } + } + } + else if (strncmp(Input, "tlb", 3) == 0) + { + char *arg = TrimWhiteSpace(Input + 3); + uintptr_t Address = NULL; + Address = strtol(arg, NULL, 16); + debug("Converted %s to %#lx", arg, Address); + Memory::PageTable4 *BasePageTable = (Memory::PageTable4 *)Address; + if (Memory::Virtual().Check(BasePageTable)) + { + for (int PMLIndex = 0; PMLIndex < 512; PMLIndex++) + { + Memory::PageMapLevel4 PML4 = BasePageTable->Entries[PMLIndex]; + EHPrint("\e888888# \eAABBCC%03d-%03d-%03d-%03d\e4500F5: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:\e888888%#lx\n", + PMLIndex, 0, 0, 0, + PML4.Present ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.ReadWrite ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.UserSupervisor ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.WriteThrough ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.CacheDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.Accessed ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.ExecuteDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PML4.GetAddress() << 12); + Display->SetBuffer(SBIdx); + if (PML4.Present) + { + Memory::PageDirectoryPointerTableEntryPtr *PDPTE = (Memory::PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + if (PDPTE) + { + for (int PDPTEIndex = 0; PDPTEIndex < 512; PDPTEIndex++) + { + EHPrint("\e888888# \eAABBCC%03d-%03d-%03d-%03d\e4500F5: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:\e888888%#lx\n", + PMLIndex, PDPTEIndex, 0, 0, + PDPTE->Entries[PDPTEIndex].Present ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].ReadWrite ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].UserSupervisor ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].WriteThrough ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].CacheDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].Accessed ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].ExecuteDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDPTE->Entries[PDPTEIndex].GetAddress() << 12); + Display->SetBuffer(SBIdx); + if ((PDPTE->Entries[PDPTEIndex].Present)) + { + Memory::PageDirectoryEntryPtr *PDE = (Memory::PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[PDPTEIndex].GetAddress() << 12); + if (PDE) + { + for (int PDEIndex = 0; PDEIndex < 512; PDEIndex++) + { + EHPrint("\e888888# \eAABBCC%03d-%03d-%03d-%03d\e4500F5: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:\e888888%#lx\n", + PMLIndex, PDPTEIndex, PDEIndex, 0, + PDE->Entries[PDEIndex].Present ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].ReadWrite ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].UserSupervisor ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].WriteThrough ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].CacheDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].Accessed ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].ExecuteDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PDE->Entries[PDEIndex].GetAddress() << 12); + Display->SetBuffer(SBIdx); + if ((PDE->Entries[PDEIndex].Present)) + { + Memory::PageTableEntryPtr *PTE = (Memory::PageTableEntryPtr *)((uintptr_t)PDE->Entries[PDEIndex].GetAddress() << 12); + if (PTE) + { + for (int PTEIndex = 0; PTEIndex < 512; PTEIndex++) + { + EHPrint("\e888888# \eAABBCC%03d-%03d-%03d-%03d\e4500F5: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s D:%s PAT:%s G:%s PK:%d NX:%s Address:\e888888%#lx\n", + PMLIndex, PDPTEIndex, PDEIndex, PTEIndex, + PTE->Entries[PTEIndex].Present ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].ReadWrite ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].UserSupervisor ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].WriteThrough ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].CacheDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].Accessed ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].Dirty ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].PageAttributeTable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].Global ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].ProtectionKey, + PTE->Entries[PTEIndex].ExecuteDisable ? "\e00FF001\e4500F5" : "\eFF00000\e4500F5", + PTE->Entries[PTEIndex].GetAddress() << 12); + Display->SetBuffer(SBIdx); + } + } + } + } + } + } + } + } + } + } + } + } + else if (strncmp(Input, "bitmap", 6) == 0) + { + Bitmap bm = KernelAllocator.GetPageBitmap(); + + EHPrint("\n\eFAFAFA[0%%] %08ld: ", 0); + for (size_t i = 0; i < bm.Size; i++) + { + if (bm.Get(i)) + EHPrint("\eFF00001"); + else + EHPrint("\e00FF000"); + if (i % 128 == 127) + { + short Percentage = s_cst(short, (i * 100) / bm.Size); + EHPrint("\n\eFAFAFA[%03ld%%] %08ld: ", Percentage, i); + Display->SetBuffer(SBIdx); + } + } + EHPrint("\n\e22AA44--- END OF BITMAP ---\nBitmap size: %ld\n\n.", bm.Size); + Display->SetBuffer(SBIdx); + } + else if (strcmp(Input, "mem") == 0) + { + uint64_t Total = KernelAllocator.GetTotalMemory(); + uint64_t Used = KernelAllocator.GetUsedMemory(); + uint64_t Free = KernelAllocator.GetFreeMemory(); + uint64_t Reserved = KernelAllocator.GetReservedMemory(); + + EHPrint("\e22AA44Total: %ld bytes\n\eFF0000Used: %ld bytes\n\e00FF00Free: %ld bytes\n\eFF00FFReserved: %ld bytes\n", Total, Used, Free, Reserved); + int Progress = s_cst(int, (Used * 100) / Total); + int ReservedProgress = s_cst(int, (Reserved * 100) / Total); + EHPrint("\e22AA44%3d%% \eCCCCCC[", Progress); + for (int i = 0; i < Progress; i++) + EHPrint("\eFF0000|"); + for (int i = 0; i < 100 - Progress; i++) + EHPrint("\e00FF00|"); + for (int i = 0; i < ReservedProgress; i++) + EHPrint("\eFF00FF|"); + EHPrint("\eCCCCCC]\n"); + + Display->SetBuffer(SBIdx); + } + else if (strncmp(Input, "cr", 2) == 0) + { + char *cr = TrimWhiteSpace(Input + 2); + switch (cr[0]) + { + case '0': + { +#if defined(a64) + EHPrint("\e44AA000: %#lx\n", CPU::x64::readcr0()); +#elif defined(a32) + EHPrint("\e44AA000: %#lx\n", CPU::x32::readcr0()); +#endif + break; + } + case '2': + { +#if defined(a64) + EHPrint("\e44AA002: %#lx\n", PageFaultAddress); +#elif defined(a32) + EHPrint("\e44AA002: %#lx\n", CPU::x32::readcr2()); +#endif + break; + } + case '3': + { +#if defined(a64) + EHPrint("\e44AA003: %#lx\n", CPU::x64::readcr3()); +#elif defined(a32) + EHPrint("\e44AA003: %#lx\n", CPU::x32::readcr3()); +#endif + break; + } + case '4': + { +#if defined(a64) + EHPrint("\e44AA004: %#lx\n", CPU::x64::readcr4()); +#elif defined(a32) + EHPrint("\e44AA004: %#lx\n", CPU::x32::readcr4()); +#endif + break; + } + case '8': + { +#if defined(a64) + EHPrint("\e44AA008: %#lx\n", CPU::x64::readcr8()); +#elif defined(a32) + EHPrint("\e44AA008: %#lx\n", CPU::x32::readcr8()); +#endif + break; + } + default: + EHPrint("\eFF0000Invalid CR\n"); + break; + } + } + else if (strncmp(Input, "tss", 3) == 0) + { + char *arg = TrimWhiteSpace(Input + 3); + int TSSIndex = atoi(arg); + if (TSSIndex > SMP::CPUCores) + { + EHPrint("\eFF0000Invalid TSS index\n"); + } + else + { +#if defined(a86) + GlobalDescriptorTable::TaskStateSegment tss = GlobalDescriptorTable::tss[TSSIndex]; + EHPrint("\eFAFAFAStack Pointer 0: \eAABB22%#lx\n", tss.StackPointer[0]); + EHPrint("\eFAFAFAStack Pointer 1: \eAABB22%#lx\n", tss.StackPointer[1]); + EHPrint("\eFAFAFAStack Pointer 2: \eAABB22%#lx\n", tss.StackPointer[2]); + + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[0]); + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[1]); + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[2]); + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[3]); + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[4]); + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[5]); + EHPrint("\eFAFAFAInterrupt Stack Table: \eAABB22%#lx\n", tss.InterruptStackTable[6]); + + EHPrint("\eFAFAFAI/O Map Base Address Offset: \eAABB22%#lx\n", tss.IOMapBaseAddressOffset); + + EHPrint("\eFAFAFAReserved 0: \eAABB22%#lx\n", tss.Reserved0); + EHPrint("\eFAFAFAReserved 1: \eAABB22%#lx\n", tss.Reserved1); + EHPrint("\eFAFAFAReserved 2: \eAABB22%#lx\n", tss.Reserved2); +#elif defined(aa64) + EHPrint("\eFF0000AArch64 does not have TSS\n"); +#endif + } + } + else if (strncmp(Input, "dump", 4) == 0) + { + char *arg = TrimWhiteSpace(Input + 4); + char *addr = strtok(arg, " "); + char *len = strtok(NULL, " "); + if (addr == NULL || len == NULL) + { + EHPrint("\eFF0000Invalid arguments\n"); + } + else + { + uint64_t Address = strtoul(addr, NULL, 16); + uint64_t Length = strtoul(len, NULL, 10); + debug("Dumping %ld bytes from %#lx\n", Length, Address); + EHDumpData((void *)Address, Length); + } + } + else if (strncmp(Input, "uartmemdmp", 10) == 0) + { + char *arg = TrimWhiteSpace(Input + 10); + char *cPort = strtok(arg, " "); + char *cBoolSkip = strtok(NULL, " "); + UniversalAsynchronousReceiverTransmitter::SerialPorts port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM1; + switch (cPort[0]) + { + case '1': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM1; + break; + case '2': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM2; + break; + case '3': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM3; + break; + case '4': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM4; + break; + case '5': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM5; + break; + case '6': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM6; + break; + case '7': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM7; + break; + case '8': + port = UniversalAsynchronousReceiverTransmitter::SerialPorts::COM8; + break; + default: + EHPrint("\eFF0000Invalid port! Defaulting to 1.\n"); + break; + } + EHPrint("\eF8F8F8Dumping memory to UART port %c (%#lx) and %s inaccessible pages.\n", cPort[0], port, cBoolSkip[0] == '1' ? "skipping" : "zeroing"); + Display->SetBuffer(SBIdx); + uint64_t Length = KernelAllocator.GetTotalMemory(); + uint64_t ProgressLength = Length; + UniversalAsynchronousReceiverTransmitter::UART uart(port); + Memory::Virtual vma; + uint8_t *Address = reinterpret_cast(0x0); + int Progress = 0; + for (size_t i = 0; i < Length; i++) + { + if (vma.Check(Address)) + uart.Write(*Address); + else if (cBoolSkip[0] == '0') + uart.Write((uint8_t)0); + else + ProgressLength--; + Address++; + + if (unlikely(i % 0x1000 == 0)) + { + int NewProgress = (int)((i * 100) / ProgressLength); + if (unlikely(NewProgress != Progress)) + { + Progress = NewProgress; + EHPrint("\n%d%%\n", Progress); + Display->SetBuffer(SBIdx); + } + Display->Print('.', SBIdx); + if (unlikely(i % 0x500 == 0)) + Display->SetBuffer(SBIdx); + } + } + EHPrint("\nDone.\n"); + } + else if (strcmp(Input, "main") == 0) + { + SBIdx = 255; + DisplayTopOverlay(); + DisplayMainScreen(crashdata); + Display->SetBuffer(SBIdx); + } + else if (strcmp(Input, "details") == 0) + { + SBIdx = 254; + DisplayTopOverlay(); + DisplayDetailsScreen(crashdata); + Display->SetBuffer(SBIdx); + } + else if (strcmp(Input, "frames") == 0) + { + SBIdx = 253; + DisplayTopOverlay(); + DisplayStackFrameScreen(crashdata); + Display->SetBuffer(SBIdx); + } + else if (strcmp(Input, "tasks") == 0) + { + SBIdx = 252; + DisplayTopOverlay(); + DisplayTasksScreen(crashdata); + Display->SetBuffer(SBIdx); + } + else if (strcmp(Input, "console") == 0) + { + SBIdx = 251; + DisplayTopOverlay(); + DisplayConsoleScreen(crashdata); + Display->SetBuffer(SBIdx); + } + else if (strlen(Input) > 0) + EHPrint("Unknown command: %s", Input); + + DisplayBottomOverlay(); + Display->SetBuffer(SBIdx); + } + + SafeFunction void StopAllCores() + { +#if defined(a86) + /* FIXME: Can't send IPIs to other cores + * because it causes another exception on + * the other cores. + * + * Also it makes every core to stay at 100% usage for some reason. + */ + + // if (SMP::CPUCores > 1) + // { + // for (int i = 1; i < SMP::CPUCores; i++) + // { + // APIC::InterruptCommandRegisterLow icr; + // icr.Vector = CPU::x86::IRQ29; + // icr.Level = APIC::APICLevel::Assert; + // ((APIC::APIC *)Interrupts::apic[i])->IPI(i, icr); + // __sync; + // } + // } + // APIC::InterruptCommandRegisterLow icr; + // icr.Vector = CPU::x86::IRQ29; + // icr.Level = APIC::APICLevel::Assert; + // icr.DestinationShorthand = APIC::APICDestinationShorthand::AllExcludingSelf; + // ((APIC::APIC *)Interrupts::apic[0])->IPI(0, icr); + // CPU::Interrupts(CPU::Enable); + __sync; + CPU::Interrupts(CPU::Disable); + // } +#elif defined(aa64) +#endif + } + + SafeFunction void Handle(void *Data) + { + // TODO: SUPPORT SMP + CPU::Interrupts(CPU::Disable); + SBIdx = 255; + CHArchTrapFrame *Frame = (CHArchTrapFrame *)Data; +#if defined(a64) + debug("-----------------------------------------------------------------------------------"); + error("Exception: %#llx", Frame->InterruptNumber); + for (size_t i = 0; i < INT_FRAMES_MAX; i++) + EHIntFrames[i] = Interrupts::InterruptFrames[i]; + PageFaultAddress = CPU::x64::readcr2().PFLA; + + if (Frame->cs != GDT_USER_CODE && Frame->cs != GDT_USER_DATA) + { + if (PageFaultAddress) + { + debug("Exception in kernel mode (ip: %#lx cr2: %#lx (%s))", + Frame->rip, PageFaultAddress, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress(Frame->rip) : "No symbol"); + } + else + { + debug("Exception in kernel mode (ip: %#lx (%s))", + Frame->rip, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress(Frame->rip) : "No symbol"); + } + + CPUData *data = GetCurrentCPU(); + if (data) + { + if (data->CurrentThread) + { + if (!data->CurrentThread->Security.IsCritical) + { + fixme("Exception in non-critical thread (kernel mode)"); + } + } + } + + if (TaskManager) + TaskManager->Panic(); + ForceUnlock = true; + Display->CreateBuffer(0, 0, SBIdx); + StopAllCores(); + } + else + { + if (PageFaultAddress) + { + debug("Exception in user mode (ip: %#lx cr2: %#lx (%s))", + Frame->rip, PageFaultAddress, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress(Frame->rip) : "No symbol"); + } + else + { + debug("Exception in user mode (ip: %#lx (%s))", + Frame->rip, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress(Frame->rip) : "No symbol"); + } + CPUData *data = GetCurrentCPU(); + if (!data) + { + ForceUnlock = true; + Display->CreateBuffer(0, 0, SBIdx); + StopAllCores(); + EHPrint("\eFF0000Cannot get CPU data! This results in a kernel crash!"); + error("Cannot get CPU data! This results in a kernel crash!"); + error("This should never happen!"); + } + else + { + debug("CPU %ld data is valid", data->ID); + if (data->CurrentThread->Security.IsCritical) + { + debug("Critical thread \"%s\"(%d) died", data->CurrentThread->Name, data->CurrentThread->ID); + if (TaskManager) + TaskManager->Panic(); + ForceUnlock = true; + Display->CreateBuffer(0, 0, SBIdx); + StopAllCores(); + } + else + { + debug("Current thread is valid %#lx", data->CurrentThread); + UserModeExceptionHandler(Frame); + return; + } + } + } + + if (ExceptionOccurred) + { + SBIdx = 255; + Display->ClearBuffer(SBIdx); + Display->SetBufferCursor(SBIdx, 0, 0); + + CPU::x64::CR0 cr0 = CPU::x64::readcr0(); + CPU::x64::CR2 cr2 = CPU::x64::CR2{.PFLA = PageFaultAddress}; + CPU::x64::CR3 cr3 = CPU::x64::readcr3(); + CPU::x64::CR4 cr4 = CPU::x64::readcr4(); + CPU::x64::CR8 cr8 = CPU::x64::readcr8(); + CPU::x64::EFER efer; + efer.raw = CPU::x64::rdmsr(CPU::x64::MSR_EFER); + uintptr_t ds; + asmv("mov %%ds, %0" + : "=r"(ds)); + + EHPrint("\eFF2525FS=%#llx GS=%#llx SS=%#llx CS=%#llx DS=%#llx\n", + CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), + Frame->ss, Frame->cs, ds); + EHPrint("R8=%#llx R9=%#llx R10=%#llx R11=%#llx\n", Frame->r8, Frame->r9, Frame->r10, Frame->r11); + EHPrint("R12=%#llx R13=%#llx R14=%#llx R15=%#llx\n", Frame->r12, Frame->r13, Frame->r14, Frame->r15); + EHPrint("RAX=%#llx RBX=%#llx RCX=%#llx RDX=%#llx\n", Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); + EHPrint("RSI=%#llx RDI=%#llx RBP=%#llx RSP=%#llx\n", Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); + EHPrint("RIP=%#llx RFL=%#llx INT=%#llx ERR=%#llx EFER=%#llx\n", Frame->rip, Frame->rflags.raw, Frame->InterruptNumber, Frame->ErrorCode, efer.raw); + EHPrint("CR0=%#llx CR2=%#llx CR3=%#llx CR4=%#llx CR8=%#llx\n", cr0.raw, cr2.raw, cr3.raw, cr4.raw, cr8.raw); + EHPrint("CR0: PE:%s MP:%s EM:%s TS:%s\n ET:%s NE:%s WP:%s AM:%s\n NW:%s CD:%s PG:%s\n R0:%#x R1:%#x R2:%#x\n", + cr0.PE ? "True " : "False", cr0.MP ? "True " : "False", cr0.EM ? "True " : "False", cr0.TS ? "True " : "False", + cr0.ET ? "True " : "False", cr0.NE ? "True " : "False", cr0.WP ? "True " : "False", cr0.AM ? "True " : "False", + cr0.NW ? "True " : "False", cr0.CD ? "True " : "False", cr0.PG ? "True " : "False", + cr0.Reserved0, cr0.Reserved1, cr0.Reserved2); + EHPrint("CR2: PFLA: %#llx\n", + cr2.PFLA); + EHPrint("CR3: PWT:%s PCD:%s PDBR:%#llx\n", + cr3.PWT ? "True " : "False", cr3.PCD ? "True " : "False", cr3.PDBR); + EHPrint("CR4: VME:%s PVI:%s TSD:%s DE:%s\n PSE:%s PAE:%s MCE:%s PGE:%s\n PCE:%s UMIP:%s OSFXSR:%s OSXMMEXCPT:%s\n LA57:%s VMXE:%s SMXE:%s PCIDE:%s\n OSXSAVE:%s SMEP:%s SMAP:%s PKE:%s\n R0:%#x R1:%#x R2:%#x\n", + cr4.VME ? "True " : "False", cr4.PVI ? "True " : "False", cr4.TSD ? "True " : "False", cr4.DE ? "True " : "False", + cr4.PSE ? "True " : "False", cr4.PAE ? "True " : "False", cr4.MCE ? "True " : "False", cr4.PGE ? "True " : "False", + cr4.PCE ? "True " : "False", cr4.UMIP ? "True " : "False", cr4.OSFXSR ? "True " : "False", cr4.OSXMMEXCPT ? "True " : "False", + cr4.LA57 ? "True " : "False", cr4.VMXE ? "True " : "False", cr4.SMXE ? "True " : "False", cr4.PCIDE ? "True " : "False", + cr4.OSXSAVE ? "True " : "False", cr4.SMEP ? "True " : "False", cr4.SMAP ? "True " : "False", cr4.PKE ? "True " : "False", + cr4.Reserved0, cr4.Reserved1, cr4.Reserved2); + EHPrint("CR8: TPL:%d\n", cr8.TPL); + EHPrint("RFL: CF:%s PF:%s AF:%s ZF:%s\n SF:%s TF:%s IF:%s DF:%s\n OF:%s IOPL:%s NT:%s RF:%s\n VM:%s AC:%s VIF:%s VIP:%s\n ID:%s AlwaysOne:%d\n R0:%#x R1:%#x R2:%#x R3:%#x\n", + Frame->rflags.CF ? "True " : "False", Frame->rflags.PF ? "True " : "False", Frame->rflags.AF ? "True " : "False", Frame->rflags.ZF ? "True " : "False", + Frame->rflags.SF ? "True " : "False", Frame->rflags.TF ? "True " : "False", Frame->rflags.IF ? "True " : "False", Frame->rflags.DF ? "True " : "False", + Frame->rflags.OF ? "True " : "False", Frame->rflags.IOPL ? "True " : "False", Frame->rflags.NT ? "True " : "False", Frame->rflags.RF ? "True " : "False", + Frame->rflags.VM ? "True " : "False", Frame->rflags.AC ? "True " : "False", Frame->rflags.VIF ? "True " : "False", Frame->rflags.VIP ? "True " : "False", + Frame->rflags.ID ? "True " : "False", Frame->rflags.AlwaysOne, + Frame->rflags.Reserved0, Frame->rflags.Reserved1, Frame->rflags.Reserved2, Frame->rflags.Reserved3); + EHPrint("EFER: SCE:%s LME:%s LMA:%s NXE:%s\n SVME:%s LMSLE:%s FFXSR:%s TCE:%s\n R0:%#x R1:%#x R2:%#x\n", + efer.SCE ? "True " : "False", efer.LME ? "True " : "False", efer.LMA ? "True " : "False", efer.NXE ? "True " : "False", + efer.SVME ? "True " : "False", efer.LMSLE ? "True " : "False", efer.FFXSR ? "True " : "False", efer.TCE ? "True " : "False", + efer.Reserved0, efer.Reserved1, efer.Reserved2); + + EHPrint("\nException occurred while handling exception! HALTED!"); + Display->SetBuffer(SBIdx); + Interrupts::RemoveAll(); + CPU::Stop(); + } + + ExceptionOccurred = true; + + if (DriverManager) + DriverManager->Panic(); + + debug("Reading control registers..."); + crashdata.Frame = Frame; + crashdata.cr0 = CPU::x64::readcr0(); + crashdata.cr2 = CPU::x64::CR2{.PFLA = PageFaultAddress}; + crashdata.cr3 = CPU::x64::readcr3(); + crashdata.cr4 = CPU::x64::readcr4(); + crashdata.cr8 = CPU::x64::readcr8(); + crashdata.efer.raw = CPU::x64::rdmsr(CPU::x64::MSR_EFER); + uintptr_t ds; + asmv("mov %%ds, %0" + : "=r"(ds)); + + // Get debug registers + asmv("movq %%dr0, %0" + : "=r"(crashdata.dr0)); + asmv("movq %%dr1, %0" + : "=r"(crashdata.dr1)); + asmv("movq %%dr2, %0" + : "=r"(crashdata.dr2)); + asmv("movq %%dr3, %0" + : "=r"(crashdata.dr3)); + asmv("movq %%dr6, %0" + : "=r"(crashdata.dr6)); + asmv("movq %%dr7, %0" + : "=r"(crashdata.dr7)); + + CPUData *cpudata = GetCurrentCPU(); + + if (cpudata == nullptr) + { + EHPrint("\eFFA500Invalid CPU data!\n"); + for (long i = 0; i < MAX_CPU; i++) + { + cpudata = GetCPU(i); + if (cpudata != nullptr) + break; + if (i == MAX_CPU - 1) + { + EHPrint("\eFF0000No CPU data found!\n"); + cpudata = nullptr; + } + } + debug("CPU ptr %#lx", cpudata); + } + + if (cpudata != nullptr) + { + crashdata.ID = cpudata->ID; + crashdata.CPUData = cpudata; + error("Technical Informations on CPU %lld:", cpudata->ID); + } + + if (TaskManager && cpudata != nullptr) + { + crashdata.Process = cpudata->CurrentProcess.load(); + crashdata.Thread = cpudata->CurrentThread.load(); + + error("Current Process: %s(%ld)", + cpudata->CurrentProcess->Name, + cpudata->CurrentProcess->ID); + error("Current Thread: %s(%ld)", + cpudata->CurrentThread->Name, + cpudata->CurrentThread->ID); + } + + { + error("FS=%#llx GS=%#llx SS=%#llx CS=%#llx DS=%#llx", + CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), + Frame->ss, Frame->cs, ds); + error("R8=%#llx R9=%#llx R10=%#llx R11=%#llx", Frame->r8, Frame->r9, Frame->r10, Frame->r11); + error("R12=%#llx R13=%#llx R14=%#llx R15=%#llx", Frame->r12, Frame->r13, Frame->r14, Frame->r15); + error("RAX=%#llx RBX=%#llx RCX=%#llx RDX=%#llx", Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); + error("RSI=%#llx RDI=%#llx RBP=%#llx RSP=%#llx", Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); + error("RIP=%#llx RFL=%#llx INT=%#llx ERR=%#llx EFER=%#llx", Frame->rip, Frame->rflags.raw, Frame->InterruptNumber, Frame->ErrorCode, crashdata.efer.raw); + error("CR0=%#llx CR2=%#llx CR3=%#llx CR4=%#llx CR8=%#llx", crashdata.cr0.raw, crashdata.cr2.raw, crashdata.cr3.raw, crashdata.cr4.raw, crashdata.cr8.raw); + error("DR0=%#llx DR1=%#llx DR2=%#llx DR3=%#llx DR6=%#llx DR7=%#llx", crashdata.dr0, crashdata.dr1, crashdata.dr2, crashdata.dr3, crashdata.dr6, crashdata.dr7.raw); + + error("CR0: PE:%s MP:%s EM:%s TS:%s ET:%s NE:%s WP:%s AM:%s NW:%s CD:%s PG:%s R0:%#x R1:%#x R2:%#x", + crashdata.cr0.PE ? "True " : "False", crashdata.cr0.MP ? "True " : "False", crashdata.cr0.EM ? "True " : "False", crashdata.cr0.TS ? "True " : "False", + crashdata.cr0.ET ? "True " : "False", crashdata.cr0.NE ? "True " : "False", crashdata.cr0.WP ? "True " : "False", crashdata.cr0.AM ? "True " : "False", + crashdata.cr0.NW ? "True " : "False", crashdata.cr0.CD ? "True " : "False", crashdata.cr0.PG ? "True " : "False", + crashdata.cr0.Reserved0, crashdata.cr0.Reserved1, crashdata.cr0.Reserved2); + + error("CR2: PFLA: %#llx", + crashdata.cr2.PFLA); + + error("CR3: PWT:%s PCD:%s PDBR:%#llx", + crashdata.cr3.PWT ? "True " : "False", crashdata.cr3.PCD ? "True " : "False", crashdata.cr3.PDBR); + + error("CR4: VME:%s PVI:%s TSD:%s DE:%s PSE:%s PAE:%s MCE:%s PGE:%s PCE:%s UMIP:%s OSFXSR:%s OSXMMEXCPT:%s LA57:%s VMXE:%s SMXE:%s PCIDE:%s OSXSAVE:%s SMEP:%s SMAP:%s PKE:%s R0:%#x R1:%#x R2:%#x", + crashdata.cr4.VME ? "True " : "False", crashdata.cr4.PVI ? "True " : "False", crashdata.cr4.TSD ? "True " : "False", crashdata.cr4.DE ? "True " : "False", + crashdata.cr4.PSE ? "True " : "False", crashdata.cr4.PAE ? "True " : "False", crashdata.cr4.MCE ? "True " : "False", crashdata.cr4.PGE ? "True " : "False", + crashdata.cr4.PCE ? "True " : "False", crashdata.cr4.UMIP ? "True " : "False", crashdata.cr4.OSFXSR ? "True " : "False", crashdata.cr4.OSXMMEXCPT ? "True " : "False", + crashdata.cr4.LA57 ? "True " : "False", crashdata.cr4.VMXE ? "True " : "False", crashdata.cr4.SMXE ? "True " : "False", crashdata.cr4.PCIDE ? "True " : "False", + crashdata.cr4.OSXSAVE ? "True " : "False", crashdata.cr4.SMEP ? "True " : "False", crashdata.cr4.SMAP ? "True " : "False", crashdata.cr4.PKE ? "True " : "False", + crashdata.cr4.Reserved0, crashdata.cr4.Reserved1, crashdata.cr4.Reserved2); + + error("CR8: TPL:%d", crashdata.cr8.TPL); + + error("RFL: CF:%s PF:%s AF:%s ZF:%s SF:%s TF:%s IF:%s DF:%s OF:%s IOPL:%s NT:%s RF:%s VM:%s AC:%s VIF:%s VIP:%s ID:%s AlwaysOne:%d R0:%#x R1:%#x R2:%#x R3:%#x", + Frame->rflags.CF ? "True " : "False", Frame->rflags.PF ? "True " : "False", Frame->rflags.AF ? "True " : "False", Frame->rflags.ZF ? "True " : "False", + Frame->rflags.SF ? "True " : "False", Frame->rflags.TF ? "True " : "False", Frame->rflags.IF ? "True " : "False", Frame->rflags.DF ? "True " : "False", + Frame->rflags.OF ? "True " : "False", Frame->rflags.IOPL ? "True " : "False", Frame->rflags.NT ? "True " : "False", Frame->rflags.RF ? "True " : "False", + Frame->rflags.VM ? "True " : "False", Frame->rflags.AC ? "True " : "False", Frame->rflags.VIF ? "True " : "False", Frame->rflags.VIP ? "True " : "False", + Frame->rflags.ID ? "True " : "False", Frame->rflags.AlwaysOne, + Frame->rflags.Reserved0, Frame->rflags.Reserved1, Frame->rflags.Reserved2, Frame->rflags.Reserved3); + + error("DR7: LDR0:%s GDR0:%s LDR1:%s GDR1:%s LDR2:%s GDR2:%s LDR3:%s GDR3:%s CDR0:%s SDR0:%s CDR1:%s SDR1:%s CDR2:%s SDR2:%s CDR3:%s SDR3:%s R:%#x", + crashdata.dr7.LocalDR0 ? "True " : "False", crashdata.dr7.GlobalDR0 ? "True " : "False", crashdata.dr7.LocalDR1 ? "True " : "False", crashdata.dr7.GlobalDR1 ? "True " : "False", + crashdata.dr7.LocalDR2 ? "True " : "False", crashdata.dr7.GlobalDR2 ? "True " : "False", crashdata.dr7.LocalDR3 ? "True " : "False", crashdata.dr7.GlobalDR3 ? "True " : "False", + crashdata.dr7.ConditionsDR0 ? "True " : "False", crashdata.dr7.SizeDR0 ? "True " : "False", crashdata.dr7.ConditionsDR1 ? "True " : "False", crashdata.dr7.SizeDR1 ? "True " : "False", + crashdata.dr7.ConditionsDR2 ? "True " : "False", crashdata.dr7.SizeDR2 ? "True " : "False", crashdata.dr7.ConditionsDR3 ? "True " : "False", crashdata.dr7.SizeDR3 ? "True " : "False", + crashdata.dr7.Reserved); + + error("EFER: SCE:%s LME:%s LMA:%s NXE:%s SVME:%s LMSLE:%s FFXSR:%s TCE:%s R0:%#x R1:%#x R2:%#x", + crashdata.efer.SCE ? "True " : "False", crashdata.efer.LME ? "True " : "False", crashdata.efer.LMA ? "True " : "False", crashdata.efer.NXE ? "True " : "False", + crashdata.efer.SVME ? "True " : "False", crashdata.efer.LMSLE ? "True " : "False", crashdata.efer.FFXSR ? "True " : "False", crashdata.efer.TCE ? "True " : "False", + crashdata.efer.Reserved0, crashdata.efer.Reserved1, crashdata.efer.Reserved2); + } + goto CrashEnd; + +#elif defined(a32) + goto CrashEnd; +#elif defined(aa64) + goto CrashEnd; +#endif + + CrashEnd: + if (Config.InterruptsOnCrash) + { + // 255 // Main + Display->CreateBuffer(0, 0, 254); // Details + Display->CreateBuffer(0, 0, 253); // Frames + Display->CreateBuffer(0, 0, 252); // Tasks + Display->CreateBuffer(0, 0, 251); // Console + Display->CreateBuffer(0, 0, 250); // Empty + + DisplayTopOverlay(); + DisplayMainScreen(crashdata); + DisplayBottomOverlay(); + Display->SetBuffer(255); + debug("Interrupts are enabled, waiting for user input"); + CPU::Interrupts(CPU::Enable); + HookKeyboard(); + } + else + { + /* + TODO: Stuff that should be done when IOC is disabled. + */ + Display->SetBuffer(255); + } + + CPU::Halt(true); + } +} diff --git a/Kernel/Core/Crash/KBDrv.cpp b/Kernel/Core/Crash/KBDrv.cpp new file mode 100644 index 00000000..19a1e62f --- /dev/null +++ b/Kernel/Core/Crash/KBDrv.cpp @@ -0,0 +1,194 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../crashhandler.hpp" +#include "chfcts.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +const char sc_ascii_low[] = {'?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '?', '?', 'q', 'w', 'e', 'r', 't', 'y', + 'u', 'i', 'o', 'p', '[', ']', '?', '?', 'a', 's', 'd', 'f', 'g', + 'h', 'j', 'k', 'l', ';', '\'', '`', '?', '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' '}; + +const char sc_ascii_high[] = {'?', '?', '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '{', '}', '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ';', '\"', '~', '?', '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', '?', '?', '?', ' '}; + +static int LowerCase = true; + +static inline int GetLetterFromScanCode(uint8_t ScanCode) +{ + if (ScanCode & 0x80) + { + switch (ScanCode) + { + case KEY_U_LSHIFT: + LowerCase = true; + return KEY_INVALID; + case KEY_U_RSHIFT: + LowerCase = true; + return KEY_INVALID; + default: + return KEY_INVALID; + } + } + else + { + switch (ScanCode) + { + case KEY_D_RETURN: + return '\n'; + case KEY_D_LSHIFT: + LowerCase = false; + return KEY_INVALID; + case KEY_D_RSHIFT: + LowerCase = false; + return KEY_INVALID; + case KEY_D_BACKSPACE: + return ScanCode; + default: + { + if (ScanCode > 0x39) + break; + if (LowerCase) + return sc_ascii_low[ScanCode]; + else + return sc_ascii_high[ScanCode]; + } + } + } + return KEY_INVALID; +} + +namespace CrashHandler +{ + CrashKeyboardDriver::CrashKeyboardDriver() : Interrupts::Handler(1) /* IRQ1 */ + { +#if defined(a86) + while (inb(0x64) & 0x1) + inb(0x60); + + outb(0x64, 0xAE); + outb(0x64, 0x20); + uint8_t ret = (inb(0x60) | 1) & ~0x10; + outb(0x64, 0x60); + outb(0x60, ret); + outb(0x60, 0xF4); + + outb(0x21, 0xFD); + outb(0xA1, 0xFF); +#endif // defined(a86) + + CPU::Interrupts(CPU::Enable); // Just to be sure. + } + + CrashKeyboardDriver::~CrashKeyboardDriver() + { + error("CrashKeyboardDriver::~CrashKeyboardDriver() called!"); + } + + int BackSpaceLimit = 0; + static char UserInputBuffer[1024]; + +#if defined(a64) + SafeFunction void CrashKeyboardDriver::OnInterruptReceived(CPU::x64::TrapFrame *Frame) +#elif defined(a32) + SafeFunction void CrashKeyboardDriver::OnInterruptReceived(CPU::x32::TrapFrame *Frame) +#elif defined(aa64) + SafeFunction void CrashKeyboardDriver::OnInterruptReceived(CPU::aarch64::TrapFrame *Frame) +#endif + { +#if defined(a86) + UNUSED(Frame); + uint8_t scanCode = inb(0x60); + if (scanCode == KEY_D_TAB || + scanCode == KEY_D_LCTRL || + scanCode == KEY_D_LALT || + scanCode == KEY_U_LCTRL || + scanCode == KEY_U_LALT) + return; + + switch (scanCode) + { + case KEY_D_UP: + case KEY_D_LEFT: + case KEY_D_RIGHT: + case KEY_D_DOWN: + ArrowInput(scanCode); + break; + default: + break; + } + + int key = GetLetterFromScanCode(scanCode); + if (key != KEY_INVALID) + { + if (key == KEY_D_BACKSPACE) + { + if (BackSpaceLimit > 0) + { + Display->Print('\b', SBIdx); + backspace(UserInputBuffer); + BackSpaceLimit--; + } + } + else if (key == '\n') + { + UserInput(UserInputBuffer); + BackSpaceLimit = 0; + UserInputBuffer[0] = '\0'; + } + else + { + append(UserInputBuffer, s_cst(char, key)); + Display->Print((char)key, SBIdx); + BackSpaceLimit++; + } + Display->SetBuffer(SBIdx); // Update as we type. + } +#endif // a64 || a32 + } + + SafeFunction void HookKeyboard() + { + CrashKeyboardDriver kbd; // We don't want to allocate memory. +#if defined(a86) + asmv("KeyboardHookLoop: nop; jmp KeyboardHookLoop;"); +#elif defined(aa64) + asmv("KeyboardHookLoop: nop; b KeyboardHookLoop;"); +#endif + // CPU::Halt(true); // This is an infinite loop. + } +} diff --git a/Kernel/Core/Crash/SFrame.cpp b/Kernel/Core/Crash/SFrame.cpp new file mode 100644 index 00000000..026177f4 --- /dev/null +++ b/Kernel/Core/Crash/SFrame.cpp @@ -0,0 +1,148 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../crashhandler.hpp" +#include "chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +namespace CrashHandler +{ + struct StackFrame + { + struct StackFrame *rbp; + uintptr_t rip; + }; + + SafeFunction void TraceFrames(CRData data, int Count, SymbolResolver::Symbols *SymHandle, bool Kernel) + { + if (!Memory::Virtual().Check(data.Frame)) + { + EHPrint("Invalid frame pointer: %p\n", data.Frame); + return; + } + + if (!Memory::Virtual().Check(SymHandle)) + { + EHPrint("Invalid symbol handle: %p\n", SymHandle); + return; + } + + bool TriedRetryBP = false; + struct StackFrame *frames = nullptr; + RetryBP: +#if defined(a64) + if (TriedRetryBP == false) + frames = (struct StackFrame *)data.Frame->rbp; +#elif defined(a32) + if (TriedRetryBP == false) + frames = (struct StackFrame *)data.Frame->ebp; +#elif defined(aa64) +#endif + if (!Memory::Virtual().Check((void *)frames)) + { + if (TriedRetryBP == false) + { + frames = (struct StackFrame *)Memory::Virtual(data.Process->PageTable).GetPhysical((void *)frames); + TriedRetryBP = true; + goto RetryBP; + } +#if defined(a64) + EHPrint("Invalid rbp pointer: %p\n", data.Frame->rbp); +#elif defined(a32) + EHPrint("Invalid ebp pointer: %p\n", data.Frame->ebp); +#elif defined(aa64) +#endif + return; + } + + debug("\nStack tracing... %p %d %p %d", data.Frame, Count, frames, Kernel); + EHPrint("\e7981FC\nStack Trace:\n"); + if (!frames || !frames->rip || !frames->rbp) + { +#if defined(a64) + EHPrint("\e2565CC%p", (void *)data.Frame->rip); +#elif defined(a32) + EHPrint("\e2565CC%p", (void *)data.Frame->eip); +#elif defined(aa64) +#endif + EHPrint("\e7925CC-"); +#if defined(a64) + EHPrint("\eAA25CC%s", SymHandle->GetSymbolFromAddress(data.Frame->rip)); +#elif defined(a32) + EHPrint("\eAA25CC%s", SymHandle->GetSymbolFromAddress(data.Frame->eip)); +#elif defined(aa64) +#endif + EHPrint("\e7981FC <- Exception"); + EHPrint("\eFF0000\n< No stack trace available. >\n"); + } + else + { +#if defined(a64) + EHPrint("\e2565CC%p", (void *)data.Frame->rip); + EHPrint("\e7925CC-"); + if ((data.Frame->rip >= 0xFFFFFFFF80000000 && data.Frame->rip <= (uintptr_t)&_kernel_end) || !Kernel) + EHPrint("\eAA25CC%s", SymHandle->GetSymbolFromAddress(data.Frame->rip)); + else + EHPrint("Outside Kernel"); +#elif defined(a32) + EHPrint("\e2565CC%p", (void *)data.Frame->eip); + EHPrint("\e7925CC-"); + if ((data.Frame->eip >= 0xC0000000 && data.Frame->eip <= (uintptr_t)&_kernel_end) || !Kernel) + EHPrint("\eAA25CC%s", SymHandle->GetSymbolFromAddress(data.Frame->eip)); + else + EHPrint("Outside Kernel"); +#elif defined(aa64) +#endif + EHPrint("\e7981FC <- Exception"); + for (int frame = 0; frame < Count; ++frame) + { + if (!frames->rip) + break; + EHPrint("\n\e2565CC%p", (void *)frames->rip); + EHPrint("\e7925CC-"); +#if defined(a64) + if ((frames->rip >= 0xFFFFFFFF80000000 && frames->rip <= (uintptr_t)&_kernel_end) || !Kernel) +#elif defined(a32) + if ((frames->rip >= 0xC0000000 && frames->rip <= (uintptr_t)&_kernel_end) || !Kernel) +#elif defined(aa64) + if ((frames->rip >= 0xFFFFFFFF80000000 && frames->rip <= (uintptr_t)&_kernel_end) || !Kernel) +#endif + EHPrint("\e25CCC9%s", SymHandle->GetSymbolFromAddress(frames->rip)); + else + EHPrint("\eFF4CA9Outside Kernel"); + + if (!Memory::Virtual().Check(frames->rbp)) + return; + frames = frames->rbp; + } + } + EHPrint("\n"); + } +} diff --git a/Kernel/Core/Crash/Screens/Console.cpp b/Kernel/Core/Crash/Screens/Console.cpp new file mode 100644 index 00000000..44da0909 --- /dev/null +++ b/Kernel/Core/Crash/Screens/Console.cpp @@ -0,0 +1,42 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../crashhandler.hpp" +#include "../chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../../kernel.h" + +namespace CrashHandler +{ + SafeFunction void DisplayConsoleScreen(CRData data) + { + EHPrint("TODO"); + UNUSED(data); + } +} \ No newline at end of file diff --git a/Kernel/Core/Crash/Screens/Details.cpp b/Kernel/Core/Crash/Screens/Details.cpp new file mode 100644 index 00000000..f23e97db --- /dev/null +++ b/Kernel/Core/Crash/Screens/Details.cpp @@ -0,0 +1,267 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../crashhandler.hpp" +#include "../chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../../kernel.h" + +namespace CrashHandler +{ + SafeFunction void DisplayDetailsScreen(CRData data) + { + if (data.Process) + EHPrint("\e7981FCCurrent Process: %s(%ld)\n", + data.Process->Name, + data.Process->ID); + if (data.Thread) + EHPrint("\e7981FCCurrent Thread: %s(%ld)\n", + data.Thread->Name, + data.Thread->ID); + EHPrint("\e7981FCTechnical Informations on CPU %lld:\n", data.ID); + uintptr_t ds; +#if defined(a64) + + CPUData *cpu = (CPUData *)data.CPUData; + if (cpu) + { + EHPrint("\eE46CEBCPU Data Address: %#lx\n", cpu); + EHPrint("Syscalls Stack: %#lx, TempStack: %#lx\n", cpu->SystemCallStack, cpu->TempStack); + EHPrint("Core Stack: %#lx, Core ID: %ld, Error Code: %ld\n", cpu->Stack, cpu->ID, cpu->ErrorCode); + EHPrint("Is Active: %s\n", cpu->IsActive ? "true" : "false"); + EHPrint("Current Process: %#lx, Current Thread: %#lx\n", cpu->CurrentProcess, cpu->CurrentThread); + EHPrint("Arch Specific Data: %#lx\n", cpu->Data); + EHPrint("Checksum: 0x%X\n", cpu->Checksum); + } + + asmv("mov %%ds, %0" + : "=r"(ds)); +#elif defined(a32) + asmv("mov %%ds, %0" + : "=r"(ds)); +#elif defined(aa64) +#endif + +#if defined(a64) + EHPrint("\e7981FCFS=%#llx GS=%#llx SS=%#llx CS=%#llx DS=%#llx\n", + CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), + data.Frame->ss, data.Frame->cs, ds); + EHPrint("R8=%#llx R9=%#llx R10=%#llx R11=%#llx\n", data.Frame->r8, data.Frame->r9, data.Frame->r10, data.Frame->r11); + EHPrint("R12=%#llx R13=%#llx R14=%#llx R15=%#llx\n", data.Frame->r12, data.Frame->r13, data.Frame->r14, data.Frame->r15); + EHPrint("RAX=%#llx RBX=%#llx RCX=%#llx RDX=%#llx\n", data.Frame->rax, data.Frame->rbx, data.Frame->rcx, data.Frame->rdx); + EHPrint("RSI=%#llx RDI=%#llx RBP=%#llx RSP=%#llx\n", data.Frame->rsi, data.Frame->rdi, data.Frame->rbp, data.Frame->rsp); + EHPrint("RIP=%#llx RFL=%#llx INT=%#llx ERR=%#llx EFER=%#llx\n", data.Frame->rip, data.Frame->rflags.raw, data.Frame->InterruptNumber, data.Frame->ErrorCode, data.efer.raw); +#elif defined(a32) + EHPrint("\e7981FCFS=%#llx GS=%#llx SS=%#llx CS=%#llx DS=%#llx\n", + CPU::x32::rdmsr(CPU::x32::MSR_FS_BASE), CPU::x32::rdmsr(CPU::x32::MSR_GS_BASE), + data.Frame->ss, data.Frame->cs, ds); + EHPrint("EAX=%#llx EBX=%#llx ECX=%#llx EDX=%#llx\n", data.Frame->eax, data.Frame->ebx, data.Frame->ecx, data.Frame->edx); + EHPrint("ESI=%#llx EDI=%#llx EBP=%#llx ESP=%#llx\n", data.Frame->esi, data.Frame->edi, data.Frame->ebp, data.Frame->esp); + EHPrint("EIP=%#llx EFL=%#llx INT=%#llx ERR=%#llx EFER=%#llx\n", data.Frame->eip, data.Frame->eflags.raw, data.Frame->InterruptNumber, data.Frame->ErrorCode, data.efer.raw); +#elif defined(aa64) +#endif + +#if defined(a86) + EHPrint("CR0=%#llx CR2=%#llx CR3=%#llx CR4=%#llx CR8=%#llx\n", data.cr0.raw, data.cr2.raw, data.cr3.raw, data.cr4.raw, data.cr8.raw); + EHPrint("DR0=%#llx DR1=%#llx DR2=%#llx DR3=%#llx DR6=%#llx DR7=%#llx\n", data.dr0, data.dr1, data.dr2, data.dr3, data.dr6, data.dr7.raw); + + EHPrint("\eFC797BCR0: PE:%s MP:%s EM:%s TS:%s\n ET:%s NE:%s WP:%s AM:%s\n NW:%s CD:%s PG:%s\n R0:%#x R1:%#x R2:%#x\n", + data.cr0.PE ? "True " : "False", data.cr0.MP ? "True " : "False", data.cr0.EM ? "True " : "False", data.cr0.TS ? "True " : "False", + data.cr0.ET ? "True " : "False", data.cr0.NE ? "True " : "False", data.cr0.WP ? "True " : "False", data.cr0.AM ? "True " : "False", + data.cr0.NW ? "True " : "False", data.cr0.CD ? "True " : "False", data.cr0.PG ? "True " : "False", + data.cr0.Reserved0, data.cr0.Reserved1, data.cr0.Reserved2); + + EHPrint("\eFCBD79CR2: PFLA: %#llx\n", + data.cr2.PFLA); + + EHPrint("\e79FC84CR3: PWT:%s PCD:%s PDBR:%#llx\n", + data.cr3.PWT ? "True " : "False", data.cr3.PCD ? "True " : "False", data.cr3.PDBR); + + EHPrint("\eBD79FCCR4: VME:%s PVI:%s TSD:%s DE:%s\n PSE:%s PAE:%s MCE:%s PGE:%s\n PCE:%s UMIP:%s OSFXSR:%s OSXMMEXCPT:%s\n LA57:%s VMXE:%s SMXE:%s PCIDE:%s\n OSXSAVE:%s SMEP:%s SMAP:%s PKE:%s\n R0:%#x R1:%#x R2:%#x\n", + data.cr4.VME ? "True " : "False", data.cr4.PVI ? "True " : "False", data.cr4.TSD ? "True " : "False", data.cr4.DE ? "True " : "False", + data.cr4.PSE ? "True " : "False", data.cr4.PAE ? "True " : "False", data.cr4.MCE ? "True " : "False", data.cr4.PGE ? "True " : "False", + data.cr4.PCE ? "True " : "False", data.cr4.UMIP ? "True " : "False", data.cr4.OSFXSR ? "True " : "False", data.cr4.OSXMMEXCPT ? "True " : "False", + data.cr4.LA57 ? "True " : "False", data.cr4.VMXE ? "True " : "False", data.cr4.SMXE ? "True " : "False", data.cr4.PCIDE ? "True " : "False", + data.cr4.OSXSAVE ? "True " : "False", data.cr4.SMEP ? "True " : "False", data.cr4.SMAP ? "True " : "False", data.cr4.PKE ? "True " : "False", +#if defined(a64) + data.cr4.Reserved0, data.cr4.Reserved1, data.cr4.Reserved2); +#elif defined(a32) + data.cr4.Reserved0, data.cr4.Reserved1, 0); +#endif + EHPrint("\e79FCF5CR8: TPL:%d\n", data.cr8.TPL); +#endif // a64 || a32 + +#if defined(a64) + EHPrint("\eFCFC02RFL: CF:%s PF:%s AF:%s ZF:%s\n SF:%s TF:%s IF:%s DF:%s\n OF:%s IOPL:%s NT:%s RF:%s\n VM:%s AC:%s VIF:%s VIP:%s\n ID:%s AlwaysOne:%d\n R0:%#x R1:%#x R2:%#x R3:%#x\n", + data.Frame->rflags.CF ? "True " : "False", data.Frame->rflags.PF ? "True " : "False", data.Frame->rflags.AF ? "True " : "False", data.Frame->rflags.ZF ? "True " : "False", + data.Frame->rflags.SF ? "True " : "False", data.Frame->rflags.TF ? "True " : "False", data.Frame->rflags.IF ? "True " : "False", data.Frame->rflags.DF ? "True " : "False", + data.Frame->rflags.OF ? "True " : "False", data.Frame->rflags.IOPL ? "True " : "False", data.Frame->rflags.NT ? "True " : "False", data.Frame->rflags.RF ? "True " : "False", + data.Frame->rflags.VM ? "True " : "False", data.Frame->rflags.AC ? "True " : "False", data.Frame->rflags.VIF ? "True " : "False", data.Frame->rflags.VIP ? "True " : "False", + data.Frame->rflags.ID ? "True " : "False", data.Frame->rflags.AlwaysOne, + data.Frame->rflags.Reserved0, data.Frame->rflags.Reserved1, data.Frame->rflags.Reserved2, data.Frame->rflags.Reserved3); +#elif defined(a32) + EHPrint("\eFCFC02EFL: CF:%s PF:%s AF:%s ZF:%s\n SF:%s TF:%s IF:%s DF:%s\n OF:%s IOPL:%s NT:%s RF:%s\n VM:%s AC:%s VIF:%s VIP:%s\n ID:%s AlwaysOne:%d\n R0:%#x R1:%#x R2:%#x\n", + data.Frame->eflags.CF ? "True " : "False", data.Frame->eflags.PF ? "True " : "False", data.Frame->eflags.AF ? "True " : "False", data.Frame->eflags.ZF ? "True " : "False", + data.Frame->eflags.SF ? "True " : "False", data.Frame->eflags.TF ? "True " : "False", data.Frame->eflags.IF ? "True " : "False", data.Frame->eflags.DF ? "True " : "False", + data.Frame->eflags.OF ? "True " : "False", data.Frame->eflags.IOPL ? "True " : "False", data.Frame->eflags.NT ? "True " : "False", data.Frame->eflags.RF ? "True " : "False", + data.Frame->eflags.VM ? "True " : "False", data.Frame->eflags.AC ? "True " : "False", data.Frame->eflags.VIF ? "True " : "False", data.Frame->eflags.VIP ? "True " : "False", + data.Frame->eflags.ID ? "True " : "False", data.Frame->eflags.AlwaysOne, + data.Frame->eflags.Reserved0, data.Frame->eflags.Reserved1, data.Frame->eflags.Reserved2); +#elif defined(aa64) +#endif + +#if defined(a86) + EHPrint("\eA0F0F0DR7: LDR0:%s GDR0:%s LDR1:%s GDR1:%s\n LDR2:%s GDR2:%s LDR3:%s GDR3:%s\n CDR0:%s SDR0:%s CDR1:%s SDR1:%s\n CDR2:%s SDR2:%s CDR3:%s SDR3:%s\n R:%#x\n", + data.dr7.LocalDR0 ? "True " : "False", data.dr7.GlobalDR0 ? "True " : "False", data.dr7.LocalDR1 ? "True " : "False", data.dr7.GlobalDR1 ? "True " : "False", + data.dr7.LocalDR2 ? "True " : "False", data.dr7.GlobalDR2 ? "True " : "False", data.dr7.LocalDR3 ? "True " : "False", data.dr7.GlobalDR3 ? "True " : "False", + data.dr7.ConditionsDR0 ? "True " : "False", data.dr7.SizeDR0 ? "True " : "False", data.dr7.ConditionsDR1 ? "True " : "False", data.dr7.SizeDR1 ? "True " : "False", + data.dr7.ConditionsDR2 ? "True " : "False", data.dr7.SizeDR2 ? "True " : "False", data.dr7.ConditionsDR3 ? "True " : "False", data.dr7.SizeDR3 ? "True " : "False", + data.dr7.Reserved); + + EHPrint("\e009FF0EFER: SCE:%s LME:%s LMA:%s NXE:%s\n SVME:%s LMSLE:%s FFXSR:%s TCE:%s\n R0:%#x R1:%#x R2:%#x\n", + data.efer.SCE ? "True " : "False", data.efer.LME ? "True " : "False", data.efer.LMA ? "True " : "False", data.efer.NXE ? "True " : "False", + data.efer.SVME ? "True " : "False", data.efer.LMSLE ? "True " : "False", data.efer.FFXSR ? "True " : "False", data.efer.TCE ? "True " : "False", + data.efer.Reserved0, data.efer.Reserved1, data.efer.Reserved2); +#endif + + switch (data.Frame->InterruptNumber) + { + case CPU::x86::DivideByZero: + { + DivideByZeroExceptionHandler(data.Frame); + break; + } + case CPU::x86::Debug: + { + DebugExceptionHandler(data.Frame); + break; + } + case CPU::x86::NonMaskableInterrupt: + { + NonMaskableInterruptExceptionHandler(data.Frame); + break; + } + case CPU::x86::Breakpoint: + { + BreakpointExceptionHandler(data.Frame); + break; + } + case CPU::x86::Overflow: + { + OverflowExceptionHandler(data.Frame); + break; + } + case CPU::x86::BoundRange: + { + BoundRangeExceptionHandler(data.Frame); + break; + } + case CPU::x86::InvalidOpcode: + { + InvalidOpcodeExceptionHandler(data.Frame); + break; + } + case CPU::x86::DeviceNotAvailable: + { + DeviceNotAvailableExceptionHandler(data.Frame); + break; + } + case CPU::x86::DoubleFault: + { + DoubleFaultExceptionHandler(data.Frame); + break; + } + case CPU::x86::CoprocessorSegmentOverrun: + { + CoprocessorSegmentOverrunExceptionHandler(data.Frame); + break; + } + case CPU::x86::InvalidTSS: + { + InvalidTSSExceptionHandler(data.Frame); + break; + } + case CPU::x86::SegmentNotPresent: + { + SegmentNotPresentExceptionHandler(data.Frame); + break; + } + case CPU::x86::StackSegmentFault: + { + StackFaultExceptionHandler(data.Frame); + break; + } + case CPU::x86::GeneralProtectionFault: + { + GeneralProtectionExceptionHandler(data.Frame); + break; + } + case CPU::x86::PageFault: + { + PageFaultExceptionHandler(data.Frame); + break; + } + case CPU::x86::x87FloatingPoint: + { + x87FloatingPointExceptionHandler(data.Frame); + break; + } + case CPU::x86::AlignmentCheck: + { + AlignmentCheckExceptionHandler(data.Frame); + break; + } + case CPU::x86::MachineCheck: + { + MachineCheckExceptionHandler(data.Frame); + break; + } + case CPU::x86::SIMDFloatingPoint: + { + SIMDFloatingPointExceptionHandler(data.Frame); + break; + } + case CPU::x86::Virtualization: + { + VirtualizationExceptionHandler(data.Frame); + break; + } + case CPU::x86::Security: + { + SecurityExceptionHandler(data.Frame); + break; + } + default: + { + UnknownExceptionHandler(data.Frame); + break; + } + } + } +} diff --git a/Kernel/Core/Crash/Screens/Main.cpp b/Kernel/Core/Crash/Screens/Main.cpp new file mode 100644 index 00000000..437324fc --- /dev/null +++ b/Kernel/Core/Crash/Screens/Main.cpp @@ -0,0 +1,390 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../crashhandler.hpp" +#include "../chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../../kernel.h" + +static const char *PagefaultDescriptions[8] = { + "Supervisory process tried to read a non-present page entry\n", + "Supervisory process tried to read a page and caused a protection fault\n", + "Supervisory process tried to write to a non-present page entry\n", + "Supervisory process tried to write a page and caused a protection fault\n", + "User process tried to read a non-present page entry\n", + "User process tried to read a page and caused a protection fault\n", + "User process tried to write to a non-present page entry\n", + "User process tried to write a page and caused a protection fault\n"}; + +namespace CrashHandler +{ + SafeFunction void DisplayMainScreen(CRData data) + { + CHArchTrapFrame *Frame = data.Frame; + + /* + _______ ___ ___ _______ _______ _______ _______ ______ ______ _______ _______ _______ _______ _____ +| __| | | __|_ _| ___| | | | | __ \ _ | __| | | ___| \ +|__ |\ /|__ | | | | ___| | | ---| < |__ | | ___| -- | +|_______| |___| |_______| |___| |_______|__|_|__| |______|___|__|___|___|_______|___|___|_______|_____/ + */ + EHPrint("\eFF5500 _______ ___ ___ _______ _______ _______ _______ ______ ______ _______ _______ _______ _______ _____ \n"); + EHPrint("| __| | | __|_ _| ___| | | | | __ \\ _ | __| | | ___| \\ \n"); + EHPrint("|__ |\\ /|__ | | | | ___| | | ---| < |__ | | ___| -- |\n"); + EHPrint("|_______| |___| |_______| |___| |_______|__|_|__| |______|___|__|___|___|_______|___|___|_______|_____/ \n\eFAFAFA"); + + switch (Frame->InterruptNumber) + { + case CPU::x86::DivideByZero: + { + EHPrint("Exception: Divide By Zero\n"); + EHPrint("The processor attempted to divide a number by zero.\n"); + break; + } + case CPU::x86::Debug: + { + EHPrint("Exception: Debug\n"); + EHPrint("A debug exception has occurred.\n"); + break; + } + case CPU::x86::NonMaskableInterrupt: + { + EHPrint("Exception: Non-Maskable Interrupt\n"); + EHPrint("A non-maskable interrupt was received.\n"); + break; + } + case CPU::x86::Breakpoint: + { + EHPrint("Exception: Breakpoint\n"); + EHPrint("The processor encountered a breakpoint.\n"); + break; + } + case CPU::x86::Overflow: + { + EHPrint("Exception: Overflow\n"); + EHPrint("The processor attempted to add a number to a number that was too large.\n"); + break; + } + case CPU::x86::BoundRange: + { + EHPrint("Exception: Bound Range\n"); + EHPrint("The processor attempted to access an array element that is out of bounds.\n"); + break; + } + case CPU::x86::InvalidOpcode: + { + EHPrint("Exception: Invalid Opcode\n"); + EHPrint("The processor attempted to execute an invalid opcode.\n"); + break; + } + case CPU::x86::DeviceNotAvailable: + { + EHPrint("Exception: Device Not Available\n"); + EHPrint("The processor attempted to use a device that is not available.\n"); + break; + } + case CPU::x86::DoubleFault: + { + EHPrint("Exception: Double Fault\n"); + EHPrint("The processor encountered a double fault.\n"); + break; + } + case CPU::x86::CoprocessorSegmentOverrun: + { + EHPrint("Exception: Coprocessor Segment Overrun\n"); + EHPrint("The processor attempted to access a segment that is not available.\n"); + break; + } + case CPU::x86::InvalidTSS: + { + EHPrint("Exception: Invalid TSS\n"); + EHPrint("The processor attempted to access a task state segment that is not available or valid.\n"); + CPU::x64::SelectorErrorCode SelCode = {.raw = Frame->ErrorCode}; + EHPrint("External? %s\n", SelCode.External ? "Yes" : "No"); + EHPrint("GDT IDT LDT IDT\n"); + switch (SelCode.Table) + { + case 0b00: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b01: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b10: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b11: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + default: + { + EHPrint(" ? \n"); + EHPrint(" ? \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + } + break; + } + case CPU::x86::SegmentNotPresent: + { + EHPrint("Exception: Segment Not Present\n"); + EHPrint("The processor attempted to access a segment that is not present.\n"); + CPU::x64::SelectorErrorCode SelCode = {.raw = Frame->ErrorCode}; + EHPrint("External? %s\n", SelCode.External ? "Yes" : "No"); + EHPrint("GDT IDT LDT IDT\n"); + switch (SelCode.Table) + { + case 0b00: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b01: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b10: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b11: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + default: + { + EHPrint(" ? \n"); + EHPrint(" ? \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + } + break; + } + case CPU::x86::StackSegmentFault: + { + EHPrint("Exception: Stack Segment Fault\n"); + CPU::x64::SelectorErrorCode SelCode = {.raw = Frame->ErrorCode}; + EHPrint("External? %s\n", SelCode.External ? "Yes" : "No"); + EHPrint("GDT IDT LDT IDT\n"); + switch (SelCode.Table) + { + case 0b00: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b01: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b10: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b11: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + default: + { + EHPrint(" ? \n"); + EHPrint(" ? \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + } + break; + } + case CPU::x86::GeneralProtectionFault: + { + EHPrint("Exception: General Protection Fault\n"); + EHPrint("Kernel performed an illegal operation.\n"); + CPU::x64::SelectorErrorCode SelCode = {.raw = Frame->ErrorCode}; + EHPrint("External? %s\n", SelCode.External ? "Yes" : "No"); + EHPrint("GDT IDT LDT IDT\n"); + switch (SelCode.Table) + { + case 0b00: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b01: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b10: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + case 0b11: + { + EHPrint(" ^ \n"); + EHPrint(" | \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + default: + { + EHPrint(" ? \n"); + EHPrint(" ? \n"); + EHPrint(" %ld\n", SelCode.Idx); + break; + } + } + break; + } + case CPU::x86::PageFault: + { + EHPrint("Exception: Page Fault\n"); + EHPrint("The processor attempted to access a page that is not present.\n"); + + CPU::x64::PageFaultErrorCode params = {.raw = (uint32_t)Frame->ErrorCode}; +#if defined(a64) + EHPrint("At \e8888FF%#lx \eFAFAFAby \e8888FF%#lx\eFAFAFA\n", PageFaultAddress, Frame->rip); +#elif defined(a32) + EHPrint("At \e8888FF%#lx \eFAFAFAby \e8888FF%#lx\eFAFAFA\n", PageFaultAddress, Frame->eip); +#elif defined(aa64) +#endif + EHPrint("Page: %s\eFAFAFA\n", params.P ? "\e058C19Present" : "\eE85230Not Present"); + EHPrint("Write Operation: \e8888FF%s\eFAFAFA\n", params.W ? "Read-Only" : "Read-Write"); + EHPrint("Processor Mode: \e8888FF%s\eFAFAFA\n", params.U ? "User-Mode" : "Kernel-Mode"); + EHPrint("CPU Reserved Bits: %s\eFAFAFA\n", params.R ? "\eE85230Reserved" : "\e058C19Unreserved"); + EHPrint("Caused By An Instruction Fetch: %s\eFAFAFA\n", params.I ? "\eE85230Yes" : "\e058C19No"); + EHPrint("Caused By A Protection-Key Violation: %s\eFAFAFA\n", params.PK ? "\eE85230Yes" : "\e058C19No"); + EHPrint("Caused By A Shadow Stack Access: %s\eFAFAFA\n", params.SS ? "\eE85230Yes" : "\e058C19No"); + EHPrint("Caused By An SGX Violation: %s\eFAFAFA\n", params.SGX ? "\eE85230Yes" : "\e058C19No"); + EHPrint("More Info: \e8888FF"); + if (Frame->ErrorCode & 0x00000008) + EHPrint("One or more page directory entries contain reserved bits which are set to 1.\n"); + else + EHPrint(PagefaultDescriptions[Frame->ErrorCode & 0b111]); + EHPrint("\eFAFAFA"); + break; + } + case CPU::x86::x87FloatingPoint: + { + EHPrint("Exception: x87 Floating Point\n"); + EHPrint("The x87 FPU generated an error.\n"); + break; + } + case CPU::x86::AlignmentCheck: + { + EHPrint("Exception: Alignment Check\n"); + EHPrint("The CPU detected an unaligned memory access.\n"); + break; + } + case CPU::x86::MachineCheck: + { + EHPrint("Exception: Machine Check\n"); + EHPrint("The CPU detected a hardware error.\n"); + break; + } + case CPU::x86::SIMDFloatingPoint: + { + EHPrint("Exception: SIMD Floating Point\n"); + EHPrint("The CPU detected an error in the SIMD unit.\n"); + break; + } + case CPU::x86::Virtualization: + { + EHPrint("Exception: Virtualization\n"); + EHPrint("The CPU detected a virtualization error.\n"); + break; + } + case CPU::x86::Security: + { + EHPrint("Exception: Security\n"); + EHPrint("The CPU detected a security violation.\n"); + break; + } + default: + { + EHPrint("Exception: Unknown\n"); + EHPrint("The CPU generated an unknown exception.\n"); + break; + } + } + +#if defined(a64) + EHPrint("The exception happened at \e8888FF%#lx\eFAFAFA\n", Frame->rip); +#elif defined(a32) + EHPrint("The exception happened at \e8888FF%#lx\eFAFAFA\n", Frame->eip); +#elif defined(aa64) +#endif + } +} diff --git a/Kernel/Core/Crash/Screens/StackFrame.cpp b/Kernel/Core/Crash/Screens/StackFrame.cpp new file mode 100644 index 00000000..0b23cf5b --- /dev/null +++ b/Kernel/Core/Crash/Screens/StackFrame.cpp @@ -0,0 +1,99 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../crashhandler.hpp" +#include "../chfcts.hpp" + +#include +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../../kernel.h" + +namespace CrashHandler +{ + SafeFunction void DisplayStackFrameScreen(CRData data) + { + EHPrint("\eFAFAFATracing 10 frames..."); + TraceFrames(data, 10, KernelSymbolTable, true); + if (data.Process) + { + EHPrint("\n\eFAFAFATracing 10 process frames..."); + SymbolResolver::Symbols *sh = data.Process->ELFSymbolTable; + if (!sh) + EHPrint("\n\eFF0000< No symbol table available. >\n"); + else + TraceFrames(data, 10, sh, false); + } + EHPrint("\n\eFAFAFATracing interrupt frames..."); + for (short i = 0; i < 8; i++) + { + if (EHIntFrames[i]) + { + if (!Memory::Virtual().Check(EHIntFrames[i])) + continue; + EHPrint("\n\e2565CC%p", EHIntFrames[i]); + EHPrint("\e7925CC-"); +#if defined(a64) + if ((uintptr_t)EHIntFrames[i] >= 0xFFFFFFFF80000000 && (uintptr_t)EHIntFrames[i] <= (uintptr_t)&_kernel_end) +#elif defined(a32) + if ((uintptr_t)EHIntFrames[i] >= 0xC0000000 && (uintptr_t)EHIntFrames[i] <= (uintptr_t)&_kernel_end) +#elif defined(aa64) + if ((uintptr_t)EHIntFrames[i] >= 0xFFFFFFFF80000000 && (uintptr_t)EHIntFrames[i] <= (uintptr_t)&_kernel_end) +#endif + EHPrint("\e25CCC9%s", KernelSymbolTable->GetSymbolFromAddress((uintptr_t)EHIntFrames[i])); + else + EHPrint("\eFF4CA9Outside Kernel"); + } + } + if (data.Process && data.Thread) + { + EHPrint("\n\n\eFAFAFATracing thread instruction pointer history..."); + SymbolResolver::Symbols *sh = data.Process->ELFSymbolTable; + if (!sh) + EHPrint("\n\eFFA500Warning: No symbol table available."); + int SameItr = 0; + uintptr_t LastRIP = 0; + for (size_t i = 0; i < sizeof(data.Thread->IPHistory) / sizeof(data.Thread->IPHistory[0]); i++) + { + if (data.Thread->IPHistory[i] == LastRIP) + { + SameItr++; + if (SameItr > 2) + continue; + } + else + SameItr = 0; + LastRIP = data.Thread->IPHistory[i]; + if (!sh) + EHPrint("\n\eCCCCCC%d: \e2565CC%p", i, data.Thread->IPHistory[i]); + else + EHPrint("\n\eCCCCCC%d: \e2565CC%p\e7925CC-\e25CCC9%s", i, data.Thread->IPHistory[i], sh->GetSymbolFromAddress(data.Thread->IPHistory[i])); + } + EHPrint("\n\e7925CCNote: \e2565CCSame instruction pointers are not shown more than 3 times.\n"); + } + } +} diff --git a/Kernel/Core/Crash/Screens/Tasks.cpp b/Kernel/Core/Crash/Screens/Tasks.cpp new file mode 100644 index 00000000..27be8327 --- /dev/null +++ b/Kernel/Core/Crash/Screens/Tasks.cpp @@ -0,0 +1,87 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../crashhandler.hpp" +#include "../chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../../kernel.h" + +namespace CrashHandler +{ + SafeFunction void DisplayTasksScreen(CRData data) + { + const char *StatusColor[7] = { + "FF0000", // Unknown + "AAFF00", // Ready + "00AA00", // Running + "FFAA00", // Sleeping + "FFAA00", // Waiting + "FF0088", // Stopped + "FF0000", // Terminated + }; + + const char *StatusString[7] = { + "Unknown", // Unknown + "Ready", // Ready + "Running", // Running + "Sleeping", // Sleeping + "Waiting", // Waiting + "Stopped", // Stopped + "Terminated", // Terminated + }; + + std::vector Plist = TaskManager->GetProcessList(); + + if (TaskManager) + { + if (data.Thread) +#if defined(a64) + EHPrint("\eFAFAFACrash occurred in thread \eAA0F0F%s\eFAFAFA(%ld) at \e00AAAA%#lx\n", data.Thread->Name, data.Thread->ID, data.Frame->rip); +#elif defined(a32) + EHPrint("\eFAFAFACrash occurred in thread \eAA0F0F%s\eFAFAFA(%ld) at \e00AAAA%#lx\n", data.Thread->Name, data.Thread->ID, data.Frame->eip); +#elif defined(aa64) +#endif + + EHPrint("\eFAFAFAProcess list (%ld):\n", Plist.size()); + foreach (auto Process in Plist) + { + EHPrint("\e%s-> \eFAFAFA%s\eCCCCCC(%ld) \e00AAAA%s\eFAFAFA PT:\e00AAAA%#lx\n", + StatusColor[Process->Status], Process->Name, Process->ID, StatusString[Process->Status], + Process->PageTable); + + foreach (auto Thread in Process->Threads) + EHPrint("\e%s -> \eFAFAFA%s\eCCCCCC(%ld) \e00AAAA%s\eFAFAFA Stack:\e00AAAA%#lx\n", + StatusColor[Thread->Status], Thread->Name, Thread->ID, StatusString[Thread->Status], + Thread->Stack); + } + } + else + EHPrint("\eFAFAFATaskManager is not initialized!\n"); + } +} diff --git a/Kernel/Core/Crash/UserHandler.cpp b/Kernel/Core/Crash/UserHandler.cpp new file mode 100644 index 00000000..89e3958a --- /dev/null +++ b/Kernel/Core/Crash/UserHandler.cpp @@ -0,0 +1,398 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../crashhandler.hpp" +#include "chfcts.hpp" + +#include +#include +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/cpu/gdt.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +static const char *PageFaultDescriptions[8] = { + "Supervisory process tried to read a non-present page entry\n", + "Supervisory process tried to read a page and caused a protection fault\n", + "Supervisory process tried to write to a non-present page entry\n", + "Supervisory process tried to write a page and caused a protection fault\n", + "User process tried to read a non-present page entry\n", + "User process tried to read a page and caused a protection fault\n", + "User process tried to write to a non-present page entry\n", + "User process tried to write a page and caused a protection fault\n"}; + +SafeFunction void UserModeExceptionHandler(CHArchTrapFrame *Frame) +{ + CriticalSection cs; + debug("Interrupts? %s.", cs.IsInterruptsEnabled() ? "Yes" : "No"); + fixme("Handling user mode exception"); + TaskManager->GetCurrentThread()->Status = Tasking::TaskStatus::Stopped; + CPUData *CurCPU = GetCurrentCPU(); + + { +#if defined(a64) + CPU::x64::CR0 cr0 = CPU::x64::readcr0(); + CPU::x64::CR2 cr2 = CPU::x64::CR2{.PFLA = CrashHandler::PageFaultAddress}; + CPU::x64::CR3 cr3 = CPU::x64::readcr3(); + CPU::x64::CR4 cr4 = CPU::x64::readcr4(); + CPU::x64::CR8 cr8 = CPU::x64::readcr8(); + CPU::x64::EFER efer; + efer.raw = CPU::x64::rdmsr(CPU::x64::MSR_EFER); + + error("Technical Informations on CPU %lld:", CurCPU->ID); + uintptr_t ds; + asmv("mov %%ds, %0" + : "=r"(ds)); +#elif defined(a32) + CPU::x32::CR0 cr0 = CPU::x32::readcr0(); + CPU::x32::CR2 cr2 = CPU::x32::CR2{.PFLA = CrashHandler::PageFaultAddress}; + CPU::x32::CR3 cr3 = CPU::x32::readcr3(); + CPU::x32::CR4 cr4 = CPU::x32::readcr4(); + CPU::x32::CR8 cr8 = CPU::x32::readcr8(); + CPU::x32::EFER efer; + efer.raw = CPU::x32::rdmsr(CPU::x32::MSR_EFER); + + error("Technical Informations on CPU %lld:", CurCPU->ID); + uintptr_t ds; + asmv("mov %%ds, %0" + : "=r"(ds)); +#elif defined(aa64) +#endif + +#if defined(a64) + error("FS=%#llx GS=%#llx SS=%#llx CS=%#llx DS=%#llx", + CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), + Frame->ss, Frame->cs, ds); + error("R8=%#llx R9=%#llx R10=%#llx R11=%#llx", Frame->r8, Frame->r9, Frame->r10, Frame->r11); + error("R12=%#llx R13=%#llx R14=%#llx R15=%#llx", Frame->r12, Frame->r13, Frame->r14, Frame->r15); + error("RAX=%#llx RBX=%#llx RCX=%#llx RDX=%#llx", Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); + error("RSI=%#llx RDI=%#llx RBP=%#llx RSP=%#llx", Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); + error("RIP=%#llx RFL=%#llx INT=%#llx ERR=%#llx EFER=%#llx", Frame->rip, Frame->rflags.raw, Frame->InterruptNumber, Frame->ErrorCode, efer.raw); +#elif defined(a32) + error("FS=%#llx GS=%#llx SS=%#llx CS=%#llx DS=%#llx", + CPU::x32::rdmsr(CPU::x32::MSR_FS_BASE), CPU::x32::rdmsr(CPU::x32::MSR_GS_BASE), + Frame->ss, Frame->cs, ds); + error("EAX=%#llx EBX=%#llx ECX=%#llx EDX=%#llx", Frame->eax, Frame->ebx, Frame->ecx, Frame->edx); + error("ESI=%#llx EDI=%#llx EBP=%#llx ESP=%#llx", Frame->esi, Frame->edi, Frame->ebp, Frame->esp); + error("EIP=%#llx EFL=%#llx INT=%#llx ERR=%#llx EFER=%#llx", Frame->eip, Frame->eflags.raw, Frame->InterruptNumber, Frame->ErrorCode, efer.raw); +#elif defined(aa64) +#endif + +#if defined(a86) + error("CR0=%#llx CR2=%#llx CR3=%#llx CR4=%#llx CR8=%#llx", cr0.raw, cr2.raw, cr3.raw, cr4.raw, cr8.raw); + + error("CR0: PE:%s MP:%s EM:%s TS:%s ET:%s NE:%s WP:%s AM:%s NW:%s CD:%s PG:%s R0:%#x R1:%#x R2:%#x", + cr0.PE ? "True " : "False", cr0.MP ? "True " : "False", cr0.EM ? "True " : "False", cr0.TS ? "True " : "False", + cr0.ET ? "True " : "False", cr0.NE ? "True " : "False", cr0.WP ? "True " : "False", cr0.AM ? "True " : "False", + cr0.NW ? "True " : "False", cr0.CD ? "True " : "False", cr0.PG ? "True " : "False", + cr0.Reserved0, cr0.Reserved1, cr0.Reserved2); + + error("CR2: PFLA: %#llx", + cr2.PFLA); + + error("CR3: PWT:%s PCD:%s PDBR:%#llx", + cr3.PWT ? "True " : "False", cr3.PCD ? "True " : "False", cr3.PDBR); +#endif // defined(a86) + +#if defined(a64) + error("CR4: VME:%s PVI:%s TSD:%s DE:%s PSE:%s PAE:%s MCE:%s PGE:%s PCE:%s UMIP:%s OSFXSR:%s OSXMMEXCPT:%s LA57:%s VMXE:%s SMXE:%s PCIDE:%s OSXSAVE:%s SMEP:%s SMAP:%s PKE:%s R0:%#x R1:%#x R2:%#x", + cr4.VME ? "True " : "False", cr4.PVI ? "True " : "False", cr4.TSD ? "True " : "False", cr4.DE ? "True " : "False", + cr4.PSE ? "True " : "False", cr4.PAE ? "True " : "False", cr4.MCE ? "True " : "False", cr4.PGE ? "True " : "False", + cr4.PCE ? "True " : "False", cr4.UMIP ? "True " : "False", cr4.OSFXSR ? "True " : "False", cr4.OSXMMEXCPT ? "True " : "False", + cr4.LA57 ? "True " : "False", cr4.VMXE ? "True " : "False", cr4.SMXE ? "True " : "False", cr4.PCIDE ? "True " : "False", + cr4.OSXSAVE ? "True " : "False", cr4.SMEP ? "True " : "False", cr4.SMAP ? "True " : "False", cr4.PKE ? "True " : "False", + cr4.Reserved0, cr4.Reserved1, cr4.Reserved2); +#elif defined(a32) + error("CR4: VME:%s PVI:%s TSD:%s DE:%s PSE:%s PAE:%s MCE:%s PGE:%s PCE:%s UMIP:%s OSFXSR:%s OSXMMEXCPT:%s LA57:%s VMXE:%s SMXE:%s PCIDE:%s OSXSAVE:%s SMEP:%s SMAP:%s PKE:%s R0:%#x R1:%#x", + cr4.VME ? "True " : "False", cr4.PVI ? "True " : "False", cr4.TSD ? "True " : "False", cr4.DE ? "True " : "False", + cr4.PSE ? "True " : "False", cr4.PAE ? "True " : "False", cr4.MCE ? "True " : "False", cr4.PGE ? "True " : "False", + cr4.PCE ? "True " : "False", cr4.UMIP ? "True " : "False", cr4.OSFXSR ? "True " : "False", cr4.OSXMMEXCPT ? "True " : "False", + cr4.LA57 ? "True " : "False", cr4.VMXE ? "True " : "False", cr4.SMXE ? "True " : "False", cr4.PCIDE ? "True " : "False", + cr4.OSXSAVE ? "True " : "False", cr4.SMEP ? "True " : "False", cr4.SMAP ? "True " : "False", cr4.PKE ? "True " : "False", + cr4.Reserved0, cr4.Reserved1); +#endif + +#if defined(a86) + error("CR8: TPL:%d", cr8.TPL); +#endif // defined(a86) + +#if defined(a64) + error("RFL: CF:%s PF:%s AF:%s ZF:%s SF:%s TF:%s IF:%s DF:%s OF:%s IOPL:%s NT:%s RF:%s VM:%s AC:%s VIF:%s VIP:%s ID:%s AlwaysOne:%d R0:%#x R1:%#x R2:%#x R3:%#x", + Frame->rflags.CF ? "True " : "False", Frame->rflags.PF ? "True " : "False", Frame->rflags.AF ? "True " : "False", Frame->rflags.ZF ? "True " : "False", + Frame->rflags.SF ? "True " : "False", Frame->rflags.TF ? "True " : "False", Frame->rflags.IF ? "True " : "False", Frame->rflags.DF ? "True " : "False", + Frame->rflags.OF ? "True " : "False", Frame->rflags.IOPL ? "True " : "False", Frame->rflags.NT ? "True " : "False", Frame->rflags.RF ? "True " : "False", + Frame->rflags.VM ? "True " : "False", Frame->rflags.AC ? "True " : "False", Frame->rflags.VIF ? "True " : "False", Frame->rflags.VIP ? "True " : "False", + Frame->rflags.ID ? "True " : "False", Frame->rflags.AlwaysOne, + Frame->rflags.Reserved0, Frame->rflags.Reserved1, Frame->rflags.Reserved2, Frame->rflags.Reserved3); +#elif defined(a32) + error("EFL: CF:%s PF:%s AF:%s ZF:%s SF:%s TF:%s IF:%s DF:%s OF:%s IOPL:%s NT:%s RF:%s VM:%s AC:%s VIF:%s VIP:%s ID:%s AlwaysOne:%d R0:%#x R1:%#x R2:%#x", + Frame->eflags.CF ? "True " : "False", Frame->eflags.PF ? "True " : "False", Frame->eflags.AF ? "True " : "False", Frame->eflags.ZF ? "True " : "False", + Frame->eflags.SF ? "True " : "False", Frame->eflags.TF ? "True " : "False", Frame->eflags.IF ? "True " : "False", Frame->eflags.DF ? "True " : "False", + Frame->eflags.OF ? "True " : "False", Frame->eflags.IOPL ? "True " : "False", Frame->eflags.NT ? "True " : "False", Frame->eflags.RF ? "True " : "False", + Frame->eflags.VM ? "True " : "False", Frame->eflags.AC ? "True " : "False", Frame->eflags.VIF ? "True " : "False", Frame->eflags.VIP ? "True " : "False", + Frame->eflags.ID ? "True " : "False", Frame->eflags.AlwaysOne, + Frame->eflags.Reserved0, Frame->eflags.Reserved1, Frame->eflags.Reserved2); +#elif defined(aa64) +#endif + +#if defined(a86) + error("EFER: SCE:%s LME:%s LMA:%s NXE:%s SVME:%s LMSLE:%s FFXSR:%s TCE:%s R0:%#x R1:%#x R2:%#x", + efer.SCE ? "True " : "False", efer.LME ? "True " : "False", efer.LMA ? "True " : "False", efer.NXE ? "True " : "False", + efer.SVME ? "True " : "False", efer.LMSLE ? "True " : "False", efer.FFXSR ? "True " : "False", efer.TCE ? "True " : "False", + efer.Reserved0, efer.Reserved1, efer.Reserved2); +#endif // a64 || a32 + } + + switch (Frame->InterruptNumber) + { + case CPU::x86::DivideByZero: + { + break; + } + case CPU::x86::Debug: + { + break; + } + case CPU::x86::NonMaskableInterrupt: + { + break; + } + case CPU::x86::Breakpoint: + { + break; + } + case CPU::x86::Overflow: + { + break; + } + case CPU::x86::BoundRange: + { + break; + } + case CPU::x86::InvalidOpcode: + { + break; + } + case CPU::x86::DeviceNotAvailable: + { + break; + } + case CPU::x86::DoubleFault: + { + break; + } + case CPU::x86::CoprocessorSegmentOverrun: + { + break; + } + case CPU::x86::InvalidTSS: + { + break; + } + case CPU::x86::SegmentNotPresent: + { + break; + } + case CPU::x86::StackSegmentFault: + { + break; + } + case CPU::x86::GeneralProtectionFault: + { + break; + } + case CPU::x86::PageFault: + { + uintptr_t CheckPageFaultAddress = 0; + CPU::x64::PageFaultErrorCode params = {.raw = (uint32_t)Frame->ErrorCode}; +#if defined(a64) + CheckPageFaultAddress = CrashHandler::PageFaultAddress; + if (CheckPageFaultAddress == 0) + CheckPageFaultAddress = Frame->rip; + + error("An exception occurred at %#lx by %#lx", CrashHandler::PageFaultAddress, Frame->rip); +#elif defined(a32) + error("An exception occurred at %#lx by %#lx", CrashHandler::PageFaultAddress, Frame->eip); +#elif defined(aa64) +#endif + error("Page: %s", params.P ? "Present" : "Not Present"); + error("Write Operation: %s", params.W ? "Read-Only" : "Read-Write"); + error("Processor Mode: %s", params.U ? "User-Mode" : "Kernel-Mode"); + error("CPU Reserved Bits: %s", params.R ? "Reserved" : "Unreserved"); + error("Caused By An Instruction Fetch: %s", params.I ? "Yes" : "No"); + error("Caused By A Protection-Key Violation: %s", params.PK ? "Yes" : "No"); + error("Caused By A Shadow Stack Access: %s", params.SS ? "Yes" : "No"); + error("Caused By An SGX Violation: %s", params.SGX ? "Yes" : "No"); + if (Frame->ErrorCode & 0x00000008) + error("One or more page directory entries contain reserved bits which are set to 1."); + else + error(PageFaultDescriptions[Frame->ErrorCode & 0b111]); + +#ifdef DEBUG + if (CurCPU) + { + Memory::Virtual vma = Memory::Virtual(CurCPU->CurrentProcess->PageTable); + bool PageAvailable = vma.Check((void *)CheckPageFaultAddress); + debug("Page available (Check(...)): %s. %s", + PageAvailable ? "Yes" : "No", + (params.P && !PageAvailable) ? "CR2 == Present; Check() != Present??????" : "CR2 confirms Check() result."); + + if (PageAvailable) + { + bool Present = vma.Check((void *)CheckPageFaultAddress); + bool ReadWrite = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::RW); + bool User = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::US); + bool WriteThrough = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::PWT); + bool CacheDisabled = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::PCD); + bool Accessed = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::A); + bool Dirty = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::D); + bool Global = vma.Check((void *)CheckPageFaultAddress, Memory::PTFlag::G); + /* ... */ + + debug("Page available: %s", Present ? "Yes" : "No"); + debug("Page read/write: %s", ReadWrite ? "Yes" : "No"); + debug("Page user/kernel: %s", User ? "User" : "Kernel"); + debug("Page write-through: %s", WriteThrough ? "Yes" : "No"); + debug("Page cache disabled: %s", CacheDisabled ? "Yes" : "No"); + debug("Page accessed: %s", Accessed ? "Yes" : "No"); + debug("Page dirty: %s", Dirty ? "Yes" : "No"); + debug("Page global: %s", Global ? "Yes" : "No"); + + if (Present) + { + uintptr_t CheckPageFaultLinearAddress = (uintptr_t)CheckPageFaultAddress; + CheckPageFaultLinearAddress &= 0xFFFFFFFFFFFFF000; + debug("%#lx -> %#lx", CheckPageFaultAddress, CheckPageFaultLinearAddress); + + Memory::Virtual::PageMapIndexer Index = Memory::Virtual::PageMapIndexer((uintptr_t)CheckPageFaultLinearAddress); + debug("Index for %#lx is PML:%d PDPTE:%d PDE:%d PTE:%d", + CheckPageFaultLinearAddress, + Index.PMLIndex, + Index.PDPTEIndex, + Index.PDEIndex, + Index.PTEIndex); + Memory::PageMapLevel4 PML4 = CurCPU->CurrentProcess->PageTable->Entries[Index.PMLIndex]; + + Memory::PageDirectoryPointerTableEntryPtr *PDPTE = (Memory::PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + Memory::PageDirectoryEntryPtr *PDE = (Memory::PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + Memory::PageTableEntryPtr *PTE = (Memory::PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:%#lx", + Index.PMLIndex, 0, 0, 0, + PML4.Present ? "1" : "0", + PML4.ReadWrite ? "1" : "0", + PML4.UserSupervisor ? "1" : "0", + PML4.WriteThrough ? "1" : "0", + PML4.CacheDisable ? "1" : "0", + PML4.Accessed ? "1" : "0", + PML4.ExecuteDisable ? "1" : "0", + PML4.GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:%#lx", + Index.PMLIndex, Index.PDPTEIndex, 0, 0, + PDPTE->Entries[Index.PDPTEIndex].Present ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].ReadWrite ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].UserSupervisor ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].WriteThrough ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].CacheDisable ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].Accessed ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].ExecuteDisable ? "1" : "0", + PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s NX:%s Address:%#lx", + Index.PMLIndex, Index.PDPTEIndex, Index.PDEIndex, 0, + PDE->Entries[Index.PDEIndex].Present ? "1" : "0", + PDE->Entries[Index.PDEIndex].ReadWrite ? "1" : "0", + PDE->Entries[Index.PDEIndex].UserSupervisor ? "1" : "0", + PDE->Entries[Index.PDEIndex].WriteThrough ? "1" : "0", + PDE->Entries[Index.PDEIndex].CacheDisable ? "1" : "0", + PDE->Entries[Index.PDEIndex].Accessed ? "1" : "0", + PDE->Entries[Index.PDEIndex].ExecuteDisable ? "1" : "0", + PDE->Entries[Index.PDEIndex].GetAddress() << 12); + + debug("# %03d-%03d-%03d-%03d: P:%s RW:%s US:%s PWT:%s PCB:%s A:%s D:%s PAT:%s G:%s PK:%d NX:%s Address:%#lx", + Index.PMLIndex, Index.PDPTEIndex, Index.PDEIndex, Index.PTEIndex, + PTE->Entries[Index.PTEIndex].Present ? "1" : "0", + PTE->Entries[Index.PTEIndex].ReadWrite ? "1" : "0", + PTE->Entries[Index.PTEIndex].UserSupervisor ? "1" : "0", + PTE->Entries[Index.PTEIndex].WriteThrough ? "1" : "0", + PTE->Entries[Index.PTEIndex].CacheDisable ? "1" : "0", + PTE->Entries[Index.PTEIndex].Accessed ? "1" : "0", + PTE->Entries[Index.PTEIndex].Dirty ? "1" : "0", + PTE->Entries[Index.PTEIndex].PageAttributeTable ? "1" : "0", + PTE->Entries[Index.PTEIndex].Global ? "1" : "0", + PTE->Entries[Index.PTEIndex].ProtectionKey, + PTE->Entries[Index.PTEIndex].ExecuteDisable ? "1" : "0", + PTE->Entries[Index.PTEIndex].GetAddress() << 12); + } + } + } +#endif + + if (CurCPU) + if (CurCPU->CurrentThread->Stack->Expand(CrashHandler::PageFaultAddress)) + { + debug("Stack expanded"); + TaskManager->GetCurrentThread()->Status = Tasking::TaskStatus::Ready; + return; + } + break; + } + case CPU::x86::x87FloatingPoint: + { + break; + } + case CPU::x86::AlignmentCheck: + { + break; + } + case CPU::x86::MachineCheck: + { + break; + } + case CPU::x86::SIMDFloatingPoint: + { + break; + } + case CPU::x86::Virtualization: + { + break; + } + case CPU::x86::Security: + { + break; + } + default: + { + break; + } + } + + TaskManager->GetCurrentThread()->Status = Tasking::TaskStatus::Terminated; + __sync; + error("End of report."); + CPU::Interrupts(CPU::Enable); + debug("Interrupts enabled back."); + return; +} diff --git a/Kernel/Core/Crash/chfcts.hpp b/Kernel/Core/Crash/chfcts.hpp new file mode 100644 index 00000000..41a7a700 --- /dev/null +++ b/Kernel/Core/Crash/chfcts.hpp @@ -0,0 +1,322 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_CRASH_HANDLERS_FUNCTIONS_H__ +#define __FENNIX_KERNEL_CRASH_HANDLERS_FUNCTIONS_H__ + +#include + +#include +#include +#include + +#if defined(a64) +typedef struct CPU::x64::TrapFrame CHArchTrapFrame; + +struct CRData +{ + CHArchTrapFrame *Frame; + + CPU::x64::CR0 cr0; + CPU::x64::CR2 cr2; + CPU::x64::CR3 cr3; + CPU::x64::CR4 cr4; + CPU::x64::CR8 cr8; + CPU::x64::EFER efer; + uintptr_t dr0, dr1, dr2, dr3, dr6; + CPU::x64::DR7 dr7; + + long ID; + void *CPUData; + Tasking::PCB *Process; + Tasking::TCB *Thread; +}; + +#elif defined(a32) +typedef struct CPU::x32::TrapFrame CHArchTrapFrame; + +struct CRData +{ + CHArchTrapFrame *Frame; + + CPU::x32::CR0 cr0; + CPU::x32::CR2 cr2; + CPU::x32::CR3 cr3; + CPU::x32::CR4 cr4; + CPU::x32::CR8 cr8; + CPU::x32::EFER efer; + uintptr_t dr0, dr1, dr2, dr3, dr6; + CPU::x32::DR7 dr7; + + long ID; + Tasking::PCB *Process; + Tasking::TCB *Thread; +}; +#elif defined(aa64) +typedef struct CPU::aarch64::TrapFrame CHArchTrapFrame; + +struct CRData +{ + CHArchTrapFrame *Frame; + + long ID; + Tasking::PCB *Process; + Tasking::TCB *Thread; +}; +#endif + +enum Keys +{ + KEY_INVALID = 0x0, + KEY_D_ESCAPE = 0x1, + KEY_D_1 = 0x2, + KEY_D_2 = 0x3, + KEY_D_3 = 0x4, + KEY_D_4 = 0x5, + KEY_D_5 = 0x6, + KEY_D_6 = 0x7, + KEY_D_7 = 0x8, + KEY_D_8 = 0x9, + KEY_D_9 = 0xa, + KEY_D_0 = 0xb, + KEY_D_MINUS = 0xc, + KEY_D_EQUALS = 0xd, + KEY_D_BACKSPACE = 0xe, + KEY_D_TAB = 0xf, + KEY_D_Q = 0x10, + KEY_D_W = 0x11, + KEY_D_E = 0x12, + KEY_D_R = 0x13, + KEY_D_T = 0x14, + KEY_D_Y = 0x15, + KEY_D_U = 0x16, + KEY_D_I = 0x17, + KEY_D_O = 0x18, + KEY_D_P = 0x19, + KEY_D_LBRACKET = 0x1a, + KEY_D_RBRACKET = 0x1b, + KEY_D_RETURN = 0x1c, + KEY_D_LCTRL = 0x1d, + KEY_D_A = 0x1e, + KEY_D_S = 0x1f, + KEY_D_D = 0x20, + KEY_D_F = 0x21, + KEY_D_G = 0x22, + KEY_D_H = 0x23, + KEY_D_J = 0x24, + KEY_D_K = 0x25, + KEY_D_L = 0x26, + KEY_D_SEMICOLON = 0x27, + KEY_D_APOSTROPHE = 0x28, + KEY_D_GRAVE = 0x29, + KEY_D_LSHIFT = 0x2a, + KEY_D_BACKSLASH = 0x2b, + KEY_D_Z = 0x2c, + KEY_D_X = 0x2d, + KEY_D_C = 0x2e, + KEY_D_V = 0x2f, + KEY_D_B = 0x30, + KEY_D_N = 0x31, + KEY_D_M = 0x32, + KEY_D_COMMA = 0x33, + KEY_D_PERIOD = 0x34, + KEY_D_SLASH = 0x35, + KEY_D_RSHIFT = 0x36, + KEY_D_PRTSC = 0x37, + KEY_D_LALT = 0x38, + KEY_D_SPACE = 0x39, + KEY_D_CAPSLOCK = 0x3a, + KEY_D_NUMLOCK = 0x45, + KEY_D_SCROLLLOCK = 0x46, + + KEY_D_KP_MULTIPLY = 0x37, + KEY_D_KP_7 = 0x47, + KEY_D_KP_8 = 0x48, + KEY_D_KP_9 = 0x49, + KEY_D_KP_MINUS = 0x4a, + KEY_D_KP_4 = 0x4b, + KEY_D_KP_5 = 0x4c, + KEY_D_KP_6 = 0x4d, + KEY_D_KP_PLUS = 0x4e, + KEY_D_KP_1 = 0x4f, + KEY_D_KP_2 = 0x50, + KEY_D_KP_3 = 0x51, + KEY_D_KP_0 = 0x52, + KEY_D_KP_PERIOD = 0x53, + + KEY_D_F1 = 0x3b, + KEY_D_F2 = 0x3c, + KEY_D_F3 = 0x3d, + KEY_D_F4 = 0x3e, + KEY_D_F5 = 0x3f, + KEY_D_F6 = 0x40, + KEY_D_F7 = 0x41, + KEY_D_F8 = 0x42, + KEY_D_F9 = 0x43, + KEY_D_F10 = 0x44, + KEY_D_F11 = 0x57, + KEY_D_F12 = 0x58, + + KEY_D_UP = 0x48, + KEY_D_LEFT = 0x4b, + KEY_D_RIGHT = 0x4d, + KEY_D_DOWN = 0x50, + + KEY_U_ESCAPE = 0x81, + KEY_U_1 = 0x82, + KEY_U_2 = 0x83, + KEY_U_3 = 0x84, + KEY_U_4 = 0x85, + KEY_U_5 = 0x86, + KEY_U_6 = 0x87, + KEY_U_7 = 0x88, + KEY_U_8 = 0x89, + KEY_U_9 = 0x8a, + KEY_U_0 = 0x8b, + KEY_U_MINUS = 0x8c, + KEY_U_EQUALS = 0x8d, + KEY_U_BACKSPACE = 0x8e, + KEY_U_TAB = 0x8f, + KEY_U_Q = 0x90, + KEY_U_W = 0x91, + KEY_U_E = 0x92, + KEY_U_R = 0x93, + KEY_U_T = 0x94, + KEY_U_Y = 0x95, + KEY_U_U = 0x96, + KEY_U_I = 0x97, + KEY_U_O = 0x98, + KEY_U_P = 0x99, + KEY_U_LBRACKET = 0x9a, + KEY_U_RBRACKET = 0x9b, + KEY_U_RETURN = 0x9c, + KEY_U_LCTRL = 0x9d, + KEY_U_A = 0x9e, + KEY_U_S = 0x9f, + KEY_U_D = 0xa0, + KEY_U_F = 0xa1, + KEY_U_G = 0xa2, + KEY_U_H = 0xa3, + KEY_U_J = 0xa4, + KEY_U_K = 0xa5, + KEY_U_L = 0xa6, + KEY_U_SEMICOLON = 0xa7, + KEY_U_APOSTROPHE = 0xa8, + KEY_U_GRAVE = 0xa9, + KEY_U_LSHIFT = 0xaa, + KEY_U_BACKSLASH = 0xab, + KEY_U_Z = 0xac, + KEY_U_X = 0xad, + KEY_U_C = 0xae, + KEY_U_V = 0xaf, + KEY_U_B = 0xb0, + KEY_U_N = 0xb1, + KEY_U_M = 0xb2, + KEY_U_COMMA = 0xb3, + KEY_U_PERIOD = 0xb4, + KEY_U_SLASH = 0xb5, + KEY_U_RSHIFT = 0xb6, + KEY_U_KP_MULTIPLY = 0xb7, + KEY_U_LALT = 0xb8, + KEY_U_SPACE = 0xb9, + KEY_U_CAPSLOCK = 0xba, + KEY_U_F1 = 0xbb, + KEY_U_F2 = 0xbc, + KEY_U_F3 = 0xbd, + KEY_U_F4 = 0xbe, + KEY_U_F5 = 0xbf, + KEY_U_F6 = 0xc0, + KEY_U_F7 = 0xc1, + KEY_U_F8 = 0xc2, + KEY_U_F9 = 0xc3, + KEY_U_F10 = 0xc4, + KEY_U_NUMLOCK = 0xc5, + KEY_U_SCROLLLOCK = 0xc6, + KEY_U_KP_7 = 0xc7, + KEY_U_KP_8 = 0xc8, + KEY_U_KP_9 = 0xc9, + KEY_U_KP_MINUS = 0xca, + KEY_U_KP_4 = 0xcb, + KEY_U_KP_5 = 0xcc, + KEY_U_KP_6 = 0xcd, + KEY_U_KP_PLUS = 0xce, + KEY_U_KP_1 = 0xcf, + KEY_U_KP_2 = 0xd0, + KEY_U_KP_3 = 0xd1, + KEY_U_KP_0 = 0xd2, + KEY_U_KP_PERIOD = 0xd3, + KEY_U_F11 = 0xd7, + KEY_U_F12 = 0xd8, +}; + +namespace CrashHandler +{ + extern int SBIdx; + + class CrashKeyboardDriver : public Interrupts::Handler + { + private: +#if defined(a64) + void OnInterruptReceived(CPU::x64::TrapFrame *Frame); +#elif defined(a32) + void OnInterruptReceived(CPU::x32::TrapFrame *Frame); +#elif defined(aa64) + void OnInterruptReceived(CPU::aarch64::TrapFrame *Frame); +#endif + public: + CrashKeyboardDriver(); + ~CrashKeyboardDriver(); + }; + + void TraceFrames(CRData data, int Count, SymbolResolver::Symbols *SymHandle, bool Kernel); + + void ArrowInput(uint8_t key); + void UserInput(char *Input); + void HookKeyboard(); + + void DisplayMainScreen(CRData data); + void DisplayDetailsScreen(CRData data); + void DisplayStackFrameScreen(CRData data); + void DisplayTasksScreen(CRData data); + void DisplayConsoleScreen(CRData data); +} + +void DivideByZeroExceptionHandler(CHArchTrapFrame *Frame); +void DebugExceptionHandler(CHArchTrapFrame *Frame); +void NonMaskableInterruptExceptionHandler(CHArchTrapFrame *Frame); +void BreakpointExceptionHandler(CHArchTrapFrame *Frame); +void OverflowExceptionHandler(CHArchTrapFrame *Frame); +void BoundRangeExceptionHandler(CHArchTrapFrame *Frame); +void InvalidOpcodeExceptionHandler(CHArchTrapFrame *Frame); +void DeviceNotAvailableExceptionHandler(CHArchTrapFrame *Frame); +void DoubleFaultExceptionHandler(CHArchTrapFrame *Frame); +void CoprocessorSegmentOverrunExceptionHandler(CHArchTrapFrame *Frame); +void InvalidTSSExceptionHandler(CHArchTrapFrame *Frame); +void SegmentNotPresentExceptionHandler(CHArchTrapFrame *Frame); +void StackFaultExceptionHandler(CHArchTrapFrame *Frame); +void GeneralProtectionExceptionHandler(CHArchTrapFrame *Frame); +void PageFaultExceptionHandler(CHArchTrapFrame *Frame); +void x87FloatingPointExceptionHandler(CHArchTrapFrame *Frame); +void AlignmentCheckExceptionHandler(CHArchTrapFrame *Frame); +void MachineCheckExceptionHandler(CHArchTrapFrame *Frame); +void SIMDFloatingPointExceptionHandler(CHArchTrapFrame *Frame); +void VirtualizationExceptionHandler(CHArchTrapFrame *Frame); +void SecurityExceptionHandler(CHArchTrapFrame *Frame); +void UnknownExceptionHandler(CHArchTrapFrame *Frame); +void UserModeExceptionHandler(CHArchTrapFrame *Frame); + +#endif // !__FENNIX_KERNEL_CRASH_HANDLERS_FUNCTIONS_H__ diff --git a/Kernel/Core/Debugger.cpp b/Kernel/Core/Debugger.cpp new file mode 100644 index 00000000..7d29c4ee --- /dev/null +++ b/Kernel/Core/Debugger.cpp @@ -0,0 +1,155 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include + +NewLock(DebuggerLock); + +using namespace UniversalAsynchronousReceiverTransmitter; + +static inline NIF void uart_wrapper(char c, void *unused) +{ + UART(COM1).Write(c); + UNUSED(unused); +} + +static inline NIF void WritePrefix(DebugLevel Level, const char *File, int Line, const char *Function) +{ + const char *DbgLvlString; + switch (Level) + { + case DebugLevelError: + DbgLvlString = "ERROR"; + break; + case DebugLevelWarning: + DbgLvlString = "WARN "; + break; + case DebugLevelInfo: + DbgLvlString = "INFO "; + break; + case DebugLevelDebug: + DbgLvlString = "DEBUG"; + break; + case DebugLevelTrace: + DbgLvlString = "TRACE"; + break; + case DebugLevelFixme: + DbgLvlString = "FIXME"; + break; + case DebugLevelUbsan: + { + DbgLvlString = "UBSAN"; + fctprintf(uart_wrapper, nullptr, "%s|%s: ", DbgLvlString, Function); + return; + } + default: + DbgLvlString = "UNKNW"; + break; + } + fctprintf(uart_wrapper, nullptr, "%s|%s->%s:%d: ", DbgLvlString, File, Function, Line); +} + +namespace SysDbg +{ + NIF void Write(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) + { + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + } + + NIF void WriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) + { + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + uart_wrapper('\n', nullptr); + } + + NIF void LockedWrite(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) + { + SmartTimeoutLock(DebuggerLock, 1000); + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + } + + NIF void LockedWriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) + { + SmartTimeoutLock(DebuggerLock, 1000); + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + uart_wrapper('\n', nullptr); + } +} + +// C compatibility +extern "C" NIF void SysDbgWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) +{ + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); +} + +// C compatibility +extern "C" NIF void SysDbgWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) +{ + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + uart_wrapper('\n', nullptr); +} + +// C compatibility +extern "C" NIF void SysDbgLockedWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) +{ + SmartTimeoutLock(DebuggerLock, 1000); + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); +} + +// C compatibility +extern "C" NIF void SysDbgLockedWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) +{ + SmartTimeoutLock(DebuggerLock, 1000); + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + uart_wrapper('\n', nullptr); +} diff --git a/Kernel/Core/Disk.cpp b/Kernel/Core/Disk.cpp new file mode 100644 index 00000000..0a734ad9 --- /dev/null +++ b/Kernel/Core/Disk.cpp @@ -0,0 +1,169 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +#include "../kernel.h" +#include "../DAPI.hpp" +#include "../Fex.hpp" + +namespace Disk +{ + void Manager::FetchDisks(unsigned long DriverUID) + { + KernelCallback callback{}; + callback.Reason = FetchReason; + DriverManager->IOCB(DriverUID, &callback); + this->AvailablePorts = callback.DiskCallback.Fetch.Ports; + this->BytesPerSector = callback.DiskCallback.Fetch.BytesPerSector; + debug("AvailablePorts:%ld BytesPerSector:%ld", this->AvailablePorts, this->BytesPerSector); + + if (this->AvailablePorts <= 0) + return; + + uint8_t *RWBuffer = (uint8_t *)KernelAllocator.RequestPages(TO_PAGES(this->BytesPerSector + 1)); + + for (unsigned char ItrPort = 0; ItrPort < this->AvailablePorts; ItrPort++) + { + Drive drive{}; + sprintf(drive.Name, "sd%ld-%d", DriverUID, this->AvailablePorts); + debug("Drive Name: %s", drive.Name); + // TODO: Implement disk type detection. Very useful in the future. + drive.MechanicalDisk = true; + + memset(RWBuffer, 0, this->BytesPerSector); + callback.Reason = ReceiveReason; + callback.DiskCallback.RW = { + .Sector = 0, + .SectorCount = 2, + .Port = ItrPort, + .Buffer = RWBuffer, + .Write = false, + }; + DriverManager->IOCB(DriverUID, &callback); + memcpy(&drive.Table, RWBuffer, sizeof(PartitionTable)); + + /* + TODO: Add to devfs the disk + */ + + if (drive.Table.GPT.Signature == GPT_MAGIC) + { + drive.Style = GPT; + uint32_t Entries = 512 / drive.Table.GPT.EntrySize; + uint32_t Sectors = drive.Table.GPT.PartCount / Entries; + for (uint32_t Block = 0; Block < Sectors; Block++) + { + memset(RWBuffer, 0, this->BytesPerSector); + callback.Reason = ReceiveReason; + callback.DiskCallback.RW = { + .Sector = 2 + Block, + .SectorCount = 1, + .Port = ItrPort, + .Buffer = RWBuffer, + .Write = false, + }; + DriverManager->IOCB(DriverUID, &callback); + + for (uint32_t e = 0; e < Entries; e++) + { + GUIDPartitionTablePartition GPTPartition = reinterpret_cast(RWBuffer)[e]; + if (GPTPartition.TypeLow || GPTPartition.TypeHigh) + { + Partition partition{}; + memcpy(partition.Label, GPTPartition.Label, sizeof(partition.Label)); + partition.StartLBA = GPTPartition.StartLBA; + partition.EndLBA = GPTPartition.EndLBA; + partition.Sectors = partition.EndLBA - partition.StartLBA; + partition.Port = ItrPort; + partition.Flags = Present; + partition.Style = GPT; + if (GPTPartition.Attributes & 1) + partition.Flags |= EFISystemPartition; + partition.Index = drive.Partitions.size(); + // why there is NUL (\0) between every char????? + char PartName[72]; + memcpy(PartName, GPTPartition.Label, 72); + for (int i = 0; i < 72; i++) + if (PartName[i] == '\0') + PartName[i] = ' '; + PartName[71] = '\0'; + trace("GPT partition \"%s\" found with %lld sectors", PartName, partition.Sectors); + drive.Partitions.push_back(partition); + + // char *PartitionName = new char[64]; + // sprintf(PartitionName, "sd%ldp%ld", drives.size() - 1, partition.Index); + + /* + TODO: Add to devfs the disk + */ + + // delete[] PartitionName; + } + } + } + trace("%d GPT partitions found.", drive.Partitions.size()); + } + else if (drive.Table.MBR.Signature[0] == MBR_MAGIC0 && drive.Table.MBR.Signature[1] == MBR_MAGIC1) + { + drive.Style = MBR; + for (size_t p = 0; p < 4; p++) + if (drive.Table.MBR.Partitions[p].LBAFirst != 0) + { + Partition partition{}; + partition.StartLBA = drive.Table.MBR.Partitions[p].LBAFirst; + partition.EndLBA = drive.Table.MBR.Partitions[p].LBAFirst + drive.Table.MBR.Partitions[p].Sectors; + partition.Sectors = drive.Table.MBR.Partitions[p].Sectors; + partition.Port = ItrPort; + partition.Flags = Present; + partition.Style = MBR; + partition.Index = drive.Partitions.size(); + trace("Partition \"%#llx\" found with %lld sectors.", drive.Table.MBR.UniqueID, partition.Sectors); + drive.Partitions.push_back(partition); + + // char *PartitionName = new char[64]; + // sprintf(PartitionName, "sd%ldp%ld", drives.size() - 1, partition.Index); + + /* + TODO: Add to devfs the disk + */ + + // delete[] PartitionName; + } + trace("%d MBR partitions found.", drive.Partitions.size()); + } + else + warn("No partition table found on port %d!", ItrPort); + + drives.push_back(drive); + } + + KernelAllocator.FreePages(RWBuffer, TO_PAGES(this->BytesPerSector + 1)); + } + + Manager::Manager() + { + } + + Manager::~Manager() + { + debug("Destructor called"); + } +} diff --git a/Kernel/Core/Driver/Driver.cpp b/Kernel/Core/Driver/Driver.cpp new file mode 100644 index 00000000..0f392e37 --- /dev/null +++ b/Kernel/Core/Driver/Driver.cpp @@ -0,0 +1,343 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../kernel.h" +#include "../../DAPI.hpp" +#include "../../Fex.hpp" +#include "api.hpp" + +NewLock(DriverInitLock); +NewLock(DriverInterruptLock); + +namespace Driver +{ + void Driver::Panic() + { +#ifdef DEBUG + size_t DriversNum = Drivers.size(); + debug("%ld drivers loaded, [DUIDs: %ld]", DriversNum, DriverUIDs); + debug("driver size %ld", DriversNum); +#endif + + foreach (auto drv in Drivers) + { + KernelCallback callback{}; + callback.Reason = StopReason; + DriverManager->IOCB(drv.DriverUID, &callback); + + for (size_t j = 0; j < sizeof(drv.InterruptHook) / sizeof(drv.InterruptHook[0]); j++) + { + if (!drv.InterruptHook[j]) + continue; + drv.InterruptHook[j]->Disable(); + debug("Interrupt hook %#lx disabled", drv.InterruptHook[j]); + } + } + } + + void Driver::UnloadAllDrivers() + { +#ifdef DEBUG + size_t DriversNum = Drivers.size(); + debug("%ld drivers loaded, [DUIDs: %ld]", DriversNum, DriverUIDs); + debug("driver size %ld", DriversNum); +#endif + + foreach (auto drv in Drivers) + { + KernelCallback callback{}; + callback.Reason = StopReason; + debug("Stopping & unloading driver %ld [%#lx]", drv.DriverUID, drv.Address); + DriverManager->IOCB(drv.DriverUID, &callback); + + for (size_t j = 0; j < sizeof(drv.InterruptHook) / sizeof(drv.InterruptHook[0]); j++) + { + if (!drv.InterruptHook[j]) + continue; + debug("Interrupt hook %#lx", drv.InterruptHook[j]); + delete drv.InterruptHook[j], drv.InterruptHook[j] = nullptr; + } + if (drv.MemTrk) + delete drv.MemTrk, drv.MemTrk = nullptr; + } + Drivers.clear(); + } + + bool Driver::UnloadDriver(unsigned long DUID) + { + debug("Searching for driver %ld", DUID); + + foreach (auto drv in Drivers) + { + if (drv.DriverUID == DUID) + { + KernelCallback callback{}; + callback.Reason = StopReason; + debug("Stopping and unloading driver %ld [%#lx]", drv.DriverUID, drv.Address); + this->IOCB(drv.DriverUID, &callback); + + for (size_t j = 0; j < sizeof(drv.InterruptHook) / sizeof(drv.InterruptHook[0]); j++) + { + if (!drv.InterruptHook[j]) + continue; + debug("Interrupt hook %#lx", drv.InterruptHook[j]); + delete drv.InterruptHook[j], drv.InterruptHook[j] = nullptr; + } + delete drv.MemTrk, drv.MemTrk = nullptr; + Drivers.remove(drv); + return true; + } + } + return false; + } + + int Driver::IOCB(unsigned long DUID, void *KCB) + { + foreach (auto Drv in Drivers) + { + if (Drv.DriverUID == DUID) + { + FexExtended *DrvExtHdr = (FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS); + int ret = ((int (*)(void *))((uintptr_t)DrvExtHdr->Driver.Callback + (uintptr_t)Drv.Address))(KCB); + __sync; + return ret; + } + } + return -1; + } + + DriverCode Driver::CallDriverEntryPoint(void *fex, void *KAPIAddress) + { + memcpy(KAPIAddress, &KernelAPITemplate, sizeof(KernelAPI)); + + ((KernelAPI *)KAPIAddress)->Info.Offset = (unsigned long)fex; + ((KernelAPI *)KAPIAddress)->Info.DriverUID = DriverUIDs++; + ((KernelAPI *)KAPIAddress)->Info.KernelDebug = DebuggerIsAttached; + +#ifdef DEBUG + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + debug("DRIVER: %s HAS DRIVER ID %ld", fexExtended->Driver.Name, ((KernelAPI *)KAPIAddress)->Info.DriverUID); +#endif + + debug("Calling driver entry point ( %#lx %ld )", (unsigned long)fex, ((KernelAPI *)KAPIAddress)->Info.DriverUID); + int ret = ((int (*)(KernelAPI *))((uintptr_t)((Fex *)fex)->EntryPoint + (uintptr_t)fex))(((KernelAPI *)KAPIAddress)); + + if (DriverReturnCode::OK != ret) + return DriverCode::DRIVER_RETURNED_ERROR; + return DriverCode::OK; + } + + DriverCode Driver::LoadDriver(uintptr_t DriverAddress, uintptr_t Size) + { + Fex *DrvHdr = (Fex *)DriverAddress; + if (DrvHdr->Magic[0] != 'F' || DrvHdr->Magic[1] != 'E' || DrvHdr->Magic[2] != 'X' || DrvHdr->Magic[3] != '\0') + { + if (Size > 0x1000) + { + Fex *ElfDrvHdr = (Fex *)(DriverAddress + 0x1000); + if (ElfDrvHdr->Magic[0] != 'F' || ElfDrvHdr->Magic[1] != 'E' || ElfDrvHdr->Magic[2] != 'X' || ElfDrvHdr->Magic[3] != '\0') + return DriverCode::INVALID_FEX_HEADER; + else + { + debug("Fex Magic: \"%s\"; Type: %d; OS: %d; EntryPoint: %#lx", ElfDrvHdr->Magic, ElfDrvHdr->Type, ElfDrvHdr->OS, ElfDrvHdr->EntryPoint); + + if (ElfDrvHdr->Type == FexFormatType::FexFormatType_Driver) + { + FexExtended *ElfDrvExtHdr = (FexExtended *)((uintptr_t)ElfDrvHdr + EXTENDED_SECTION_ADDRESS); + debug("Name: \"%s\"; Type: %d; Callback: %#lx", ElfDrvExtHdr->Driver.Name, ElfDrvExtHdr->Driver.Type, ElfDrvExtHdr->Driver.Callback); + + if (ElfDrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_PCI) + return this->DriverLoadBindPCI(ElfDrvExtHdr, DriverAddress, Size, true); + else if (ElfDrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_INTERRUPT) + return this->DriverLoadBindInterrupt(ElfDrvExtHdr, DriverAddress, Size, true); + else if (ElfDrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_PROCESS) + return this->DriverLoadBindProcess(ElfDrvExtHdr, DriverAddress, Size, true); + else if (ElfDrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_INPUT) + return this->DriverLoadBindInput(ElfDrvExtHdr, DriverAddress, Size, true); + else + error("Unknown driver bind type: %d", ElfDrvExtHdr->Driver.Bind.Type); + } + else + return DriverCode::NOT_DRIVER; + } + } + else + return DriverCode::INVALID_FEX_HEADER; + } + debug("Fex Magic: \"%s\"; Type: %d; OS: %d; EntryPoint: %#lx", DrvHdr->Magic, DrvHdr->Type, DrvHdr->OS, DrvHdr->EntryPoint); + + if (DrvHdr->Type == FexFormatType::FexFormatType_Driver) + { + FexExtended *DrvExtHdr = (FexExtended *)((uintptr_t)DrvHdr + EXTENDED_SECTION_ADDRESS); + debug("Name: \"%s\"; Type: %d; Callback: %#lx", DrvExtHdr->Driver.Name, DrvExtHdr->Driver.Type, DrvExtHdr->Driver.Callback); + + if (DrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_PCI) + return this->DriverLoadBindPCI(DrvExtHdr, DriverAddress, Size); + else if (DrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_INTERRUPT) + return this->DriverLoadBindInterrupt(DrvExtHdr, DriverAddress, Size); + else if (DrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_PROCESS) + return this->DriverLoadBindProcess(DrvExtHdr, DriverAddress, Size); + else if (DrvExtHdr->Driver.Bind.Type == DriverBindType::BIND_INPUT) + return this->DriverLoadBindInput(DrvExtHdr, DriverAddress, Size); + else + error("Unknown driver bind type: %d", DrvExtHdr->Driver.Bind.Type); + } + else + return DriverCode::NOT_DRIVER; + return DriverCode::ERROR; + } + + Driver::Driver() + { + SmartCriticalSection(DriverInitLock); + + std::string DriverConfigFile = Config.DriverDirectory; + DriverConfigFile << "/config.ini"; + fixme("Loading driver config file: %s", DriverConfigFile.c_str()); + + VirtualFileSystem::File DriverDirectory = vfs->Open(Config.DriverDirectory); + if (DriverDirectory.IsOK()) + { + foreach (auto driver in DriverDirectory.node->Children) + if (driver->Flags == VirtualFileSystem::NodeFlags::FILE) + if (cwk_path_has_extension(driver->Name)) + { + const char *extension; + size_t extension_length; + cwk_path_get_extension(driver->Name, &extension, &extension_length); + debug("Driver: %s; Extension: %s", driver->Name, extension); + if (strcmp(extension, ".fex") == 0 || strcmp(extension, ".elf") == 0) + { + uintptr_t ret = this->LoadDriver(driver->Address, driver->Length); + char RetString[128]; + if (ret == DriverCode::OK) + strncpy(RetString, "\e058C19OK", 10); + else if (ret == DriverCode::NOT_AVAILABLE) + strncpy(RetString, "\eFF7900NOT AVAILABLE", 21); + else + sprintf(RetString, "\eE85230FAILED (%#lx)", ret); + KPrint("%s %s", driver->Name, RetString); + } + } + } + else + { + KPrint("\eE85230Failed to open driver directory: %s! (Status: %#lx)", Config.DriverDirectory, DriverDirectory.Status); + CPU::Stop(); + } + vfs->Close(DriverDirectory); + } + + Driver::~Driver() + { + debug("Destructor called"); + this->UnloadAllDrivers(); + } + +#if defined(a64) + SafeFunction void DriverInterruptHook::OnInterruptReceived(CPU::x64::TrapFrame *Frame) +#elif defined(a32) + SafeFunction void DriverInterruptHook::OnInterruptReceived(CPU::x32::TrapFrame *Frame) +#elif defined(aa64) + SafeFunction void DriverInterruptHook::OnInterruptReceived(CPU::aarch64::TrapFrame *Frame) +#endif + { + SmartLock(DriverInterruptLock); /* Lock in case of multiple interrupts firing at the same time */ + if (!this->Enabled) + { + debug("Interrupt hook is not enabled"); + return; + } + + if (!Handle.InterruptCallback) + { +#if defined(a86) + uint64_t IntNum = Frame->InterruptNumber - 32; +#elif defined(aa64) + uint64_t IntNum = Frame->InterruptNumber; +#endif + warn("Interrupt callback for %ld is not set for driver %ld!", IntNum, Handle.DriverUID); + return; + } + CPURegisters regs; +#if defined(a64) + regs.r15 = Frame->r15; + regs.r14 = Frame->r14; + regs.r13 = Frame->r13; + regs.r12 = Frame->r12; + regs.r11 = Frame->r11; + regs.r10 = Frame->r10; + regs.r9 = Frame->r9; + regs.r8 = Frame->r8; + + regs.rbp = Frame->rbp; + regs.rdi = Frame->rdi; + regs.rsi = Frame->rsi; + regs.rdx = Frame->rdx; + regs.rcx = Frame->rcx; + regs.rbx = Frame->rbx; + regs.rax = Frame->rax; + + regs.InterruptNumber = Frame->InterruptNumber; + regs.ErrorCode = Frame->ErrorCode; + regs.rip = Frame->rip; + regs.cs = Frame->cs; + regs.rflags = Frame->rflags.raw; + regs.rsp = Frame->rsp; + regs.ss = Frame->ss; +#elif defined(a32) + regs.ebp = Frame->ebp; + regs.edi = Frame->edi; + regs.esi = Frame->esi; + regs.edx = Frame->edx; + regs.ecx = Frame->ecx; + regs.ebx = Frame->ebx; + regs.eax = Frame->eax; + + regs.InterruptNumber = Frame->InterruptNumber; + regs.ErrorCode = Frame->ErrorCode; + regs.eip = Frame->eip; + regs.cs = Frame->cs; + regs.eflags = Frame->eflags.raw; + regs.esp = Frame->esp; + regs.ss = Frame->ss; +#elif defined(aa64) +#endif + ((int (*)(void *))(Handle.InterruptCallback))(®s); + UNUSED(Frame); + } + + DriverInterruptHook::DriverInterruptHook(int Interrupt, DriverFile Handle) : Interrupts::Handler(Interrupt) + { + this->Handle = Handle; +#if defined(a86) + trace("Interrupt %d hooked to driver %ld", Interrupt, Handle.DriverUID); +#elif defined(aa64) + trace("Interrupt %d hooked to driver %ld", Interrupt, Handle.DriverUID); +#endif + } +} diff --git a/Kernel/Core/Driver/DriverAPI.cpp b/Kernel/Core/Driver/DriverAPI.cpp new file mode 100644 index 00000000..e9e26407 --- /dev/null +++ b/Kernel/Core/Driver/DriverAPI.cpp @@ -0,0 +1,203 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +#include "../../kernel.h" +#include "../../Fex.hpp" +#include "api.hpp" + +// show debug messages +// #define DEBUG_DRIVER_API 1 + +#ifdef DEBUG_DRIVER_API +#define drvdbg(m, ...) debug(m, ##__VA_ARGS__) +#else +#define drvdbg(m, ...) +#endif + +NewLock(DriverDisplayPrintLock); + +void DriverDebugPrint(char *String, unsigned long DriverUID) { trace("[%ld] %s", DriverUID, String); } + +void DriverDisplayPrint(char *String) +{ + SmartLock(DriverDisplayPrintLock); + for (unsigned long i = 0; i < strlen(String); i++) + Display->Print(String[i], 0, true); +} + +void *RequestPage(unsigned long Size) +{ + void *ret = KernelAllocator.RequestPages(Size + 1); + drvdbg("Allocated %ld pages (%#lx-%#lx)", Size, (unsigned long)ret, (unsigned long)ret + FROM_PAGES(Size)); + return ret; +} + +void FreePage(void *Page, unsigned long Size) +{ + drvdbg("Freeing %ld pages (%#lx-%#lx)", Size, (unsigned long)Page, (unsigned long)Page + FROM_PAGES(Size)); + KernelAllocator.FreePages(Page, Size + 1); +} + +void MapMemory(void *VirtualAddress, void *PhysicalAddress, unsigned long Flags) +{ + SmartLock(DriverDisplayPrintLock); + drvdbg("Mapping %#lx to %#lx with flags %#lx...", (unsigned long)VirtualAddress, (unsigned long)PhysicalAddress, Flags); + Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags); +} + +void UnmapMemory(void *VirtualAddress) +{ + SmartLock(DriverDisplayPrintLock); + drvdbg("Unmapping %#lx...", (unsigned long)VirtualAddress); + Memory::Virtual(KernelPageTable).Unmap(VirtualAddress); +} + +void *Drivermemcpy(void *Destination, void *Source, unsigned long Size) +{ + SmartLock(DriverDisplayPrintLock); + drvdbg("Copying %ld bytes from %#lx-%#lx to %#lx-%#lx...", Size, + (unsigned long)Source, (unsigned long)Source + Size, + (unsigned long)Destination, (unsigned long)Destination + Size); + return memcpy(Destination, Source, Size); +} + +void *Drivermemset(void *Destination, int Value, unsigned long Size) +{ + SmartLock(DriverDisplayPrintLock); + drvdbg("Setting value %#x at %#lx-%#lx (%ld bytes)...", Value, + (unsigned long)Destination, (unsigned long)Destination + Size, + Size); + return memset(Destination, Value, Size); +} + +void DriverNetSend(unsigned int DriverID, unsigned char *Data, unsigned short Size) +{ + // This is useless I guess... + if (NIManager) + NIManager->DrvSend(DriverID, Data, Size); +} + +void DriverNetReceive(unsigned int DriverID, unsigned char *Data, unsigned short Size) +{ + if (NIManager) + NIManager->DrvReceive(DriverID, Data, Size); +} + +void DriverAHCIDiskRead(unsigned int DriverID, unsigned long Sector, unsigned char *Data, unsigned int SectorCount, unsigned char Port) +{ + DumpData("DriverDiskRead", Data, SectorCount * 512); + UNUSED(DriverID); + UNUSED(Sector); + UNUSED(Port); +} + +void DriverAHCIDiskWrite(unsigned int DriverID, unsigned long Sector, unsigned char *Data, unsigned int SectorCount, unsigned char Port) +{ + DumpData("DriverDiskWrite", Data, SectorCount * 512); + UNUSED(DriverID); + UNUSED(Sector); + UNUSED(Port); +} + +char *DriverPCIGetDeviceName(unsigned int VendorID, unsigned int DeviceID) +{ + UNUSED(VendorID); + UNUSED(DeviceID); + return (char *)"Unknown"; +} + +unsigned int DriverGetWidth() +{ + /* TODO: We won't rely only on display buffers, what about graphics drivers and changing resolutions? */ + return Display->GetBuffer(0)->Width; +} + +unsigned int DriverGetHeight() +{ + /* TODO: We won't rely only on display buffers, what about graphics drivers and changing resolutions? */ + return Display->GetBuffer(0)->Height; +} + +void DriverSleep(unsigned long Milliseconds) +{ + SmartLock(DriverDisplayPrintLock); + drvdbg("Sleeping for %ld milliseconds...", Milliseconds); + if (TaskManager) + TaskManager->Sleep(Milliseconds); + else + TimeManager->Sleep(Milliseconds, Time::Units::Milliseconds); +} + +int Driversprintf(char *Buffer, const char *Format, ...) +{ + va_list args; + va_start(args, Format); + int ret = vsprintf(Buffer, Format, args); + va_end(args); + return ret; +} + +KernelAPI KernelAPITemplate = { + .Version = { + .Major = 0, + .Minor = 0, + .Patch = 1}, + .Info = { + .Offset = 0, + .DriverUID = 0, + .KernelDebug = false, + }, + .Memory = { + .PageSize = PAGE_SIZE, + .RequestPage = RequestPage, + .FreePage = FreePage, + .Map = MapMemory, + .Unmap = UnmapMemory, + }, + .PCI = { + .GetDeviceName = DriverPCIGetDeviceName, + }, + .Util = { + .DebugPrint = DriverDebugPrint, + .DisplayPrint = DriverDisplayPrint, + .memcpy = Drivermemcpy, + .memset = Drivermemset, + .Sleep = DriverSleep, + .sprintf = Driversprintf, + }, + .Command = { + .Network = { + .SendPacket = DriverNetSend, + .ReceivePacket = DriverNetReceive, + }, + .Disk = { + .AHCI = { + .ReadSector = DriverAHCIDiskRead, + .WriteSector = DriverAHCIDiskWrite, + }, + }, + }, + .Display = { + .GetWidth = DriverGetWidth, + .GetHeight = DriverGetHeight, + }, +}; diff --git a/Kernel/Core/Driver/DriverBinding/BindInput.cpp b/Kernel/Core/Driver/DriverBinding/BindInput.cpp new file mode 100644 index 00000000..b93d02af --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/BindInput.cpp @@ -0,0 +1,73 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../kernel.h" +#include "../../../DAPI.hpp" +#include "../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::DriverLoadBindInput(void *DrvExtHdr, uintptr_t DriverAddress, size_t Size, bool IsElf) + { + UNUSED(DrvExtHdr); + UNUSED(IsElf); + Memory::MemMgr *mem = new Memory::MemMgr(nullptr, TaskManager->GetCurrentProcess()->memDirectory); + Fex *fex = (Fex *)mem->RequestPages(TO_PAGES(Size + 1)); + memcpy(fex, (void *)DriverAddress, Size); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + debug("Driver allocated at %#lx-%#lx", fex, (uintptr_t)fex + Size); +#ifdef DEBUG + uint8_t *result = md5File((uint8_t *)fex, Size); + debug("MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]); + kfree(result); +#endif + KernelAPI *KAPI = (KernelAPI *)mem->RequestPages(TO_PAGES(sizeof(KernelAPI) + 1)); + + if (CallDriverEntryPoint(fex, KAPI) != DriverCode::OK) + { + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + debug("Starting driver %s (offset: %#lx)", fexExtended->Driver.Name, fex); + + switch (fexExtended->Driver.Type) + { + case FexDriverType::FexDriverType_Input: + return BindInputInput(mem, fex); + default: + { + warn("Unknown driver type: %d", fexExtended->Driver.Type); + delete mem, mem = nullptr; + return DriverCode::UNKNOWN_DRIVER_TYPE; + } + } + + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/BindInterrupt.cpp b/Kernel/Core/Driver/DriverBinding/BindInterrupt.cpp new file mode 100644 index 00000000..7bab3886 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/BindInterrupt.cpp @@ -0,0 +1,85 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../kernel.h" +#include "../../../DAPI.hpp" +#include "../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::DriverLoadBindInterrupt(void *DrvExtHdr, uintptr_t DriverAddress, size_t Size, bool IsElf) + { + UNUSED(DrvExtHdr); + UNUSED(IsElf); + Memory::MemMgr *mem = new Memory::MemMgr(nullptr, TaskManager->GetCurrentProcess()->memDirectory); + Fex *fex = (Fex *)mem->RequestPages(TO_PAGES(Size + 1)); + memcpy(fex, (void *)DriverAddress, Size); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + debug("Driver allocated at %#lx-%#lx", fex, (uintptr_t)fex + Size); +#ifdef DEBUG + uint8_t *result = md5File((uint8_t *)fex, Size); + debug("MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]); + kfree(result); +#endif + KernelAPI *KAPI = (KernelAPI *)mem->RequestPages(TO_PAGES(sizeof(KernelAPI) + 1)); + + if (CallDriverEntryPoint(fex, KAPI) != DriverCode::OK) + { + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + debug("Starting driver %s (offset: %#lx)", fexExtended->Driver.Name, fex); + + switch (fexExtended->Driver.Type) + { + case FexDriverType::FexDriverType_Generic: + return BindInterruptGeneric(mem, fex); + case FexDriverType::FexDriverType_Display: + return BindInterruptDisplay(mem, fex); + case FexDriverType::FexDriverType_Network: + return BindInterruptNetwork(mem, fex); + case FexDriverType::FexDriverType_Storage: + return BindInterruptStorage(mem, fex); + case FexDriverType::FexDriverType_FileSystem: + return BindInterruptFileSystem(mem, fex); + case FexDriverType::FexDriverType_Input: + return BindInterruptInput(mem, fex); + case FexDriverType::FexDriverType_Audio: + return BindInterruptAudio(mem, fex); + default: + { + warn("Unknown driver type: %d", fexExtended->Driver.Type); + delete mem, mem = nullptr; + return DriverCode::UNKNOWN_DRIVER_TYPE; + } + } + + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/BindPCI.cpp b/Kernel/Core/Driver/DriverBinding/BindPCI.cpp new file mode 100644 index 00000000..c432885f --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/BindPCI.cpp @@ -0,0 +1,201 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../kernel.h" +#include "../../../DAPI.hpp" +#include "../../../Fex.hpp" + +namespace Driver +{ + void Driver::MapPCIAddresses(PCI::PCIDeviceHeader *PCIDevice) + { + debug("Header Type: %d", PCIDevice->HeaderType); + switch (PCIDevice->HeaderType) + { + case 0: // PCI Header 0 + { + uint32_t BAR[6] = {0}; + size_t BARsSize[6] = {0}; + + BAR[0] = ((PCI::PCIHeader0 *)PCIDevice)->BAR0; + BAR[1] = ((PCI::PCIHeader0 *)PCIDevice)->BAR1; + BAR[2] = ((PCI::PCIHeader0 *)PCIDevice)->BAR2; + BAR[3] = ((PCI::PCIHeader0 *)PCIDevice)->BAR3; + BAR[4] = ((PCI::PCIHeader0 *)PCIDevice)->BAR4; + BAR[5] = ((PCI::PCIHeader0 *)PCIDevice)->BAR5; + +#ifdef DEBUG + uintptr_t BAR_Type = BAR[0] & 1; + uintptr_t BAR_IOBase = BAR[1] & (~3); + uintptr_t BAR_MemoryBase = BAR[0] & (~15); + + debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx", BAR_Type, BAR_IOBase, BAR_MemoryBase); +#endif + + /* BARs Size */ + for (short i = 0; i < 6; i++) + { + if (BAR[i] == 0) + continue; + + if ((BAR[i] & 1) == 0) // Memory Base + { + ((PCI::PCIHeader0 *)PCIDevice)->BAR0 = 0xFFFFFFFF; + size_t size = ((PCI::PCIHeader0 *)PCIDevice)->BAR0; + ((PCI::PCIHeader0 *)PCIDevice)->BAR0 = BAR[i]; + BARsSize[i] = size & (~15); + BARsSize[i] = ~BARsSize[i] + 1; + BARsSize[i] = BARsSize[i] & 0xFFFFFFFF; + debug("BAR%d %#lx size: %d", i, BAR[i], BARsSize[i]); + } + else if ((BAR[i] & 1) == 1) // I/O Base + { + ((PCI::PCIHeader0 *)PCIDevice)->BAR1 = 0xFFFFFFFF; + size_t size = ((PCI::PCIHeader0 *)PCIDevice)->BAR1; + ((PCI::PCIHeader0 *)PCIDevice)->BAR1 = BAR[i]; + BARsSize[i] = size & (~3); + BARsSize[i] = ~BARsSize[i] + 1; + BARsSize[i] = BARsSize[i] & 0xFFFF; + debug("BAR%d %#lx size: %d", i, BAR[i], BARsSize[i]); + } + } + + /* Mapping the BARs */ + for (short i = 0; i < 6; i++) + { + if (BAR[i] == 0) + continue; + + if ((BAR[i] & 1) == 0) // Memory Base + { + uintptr_t BARBase = BAR[i] & (~15); + size_t BARSize = BARsSize[i]; + + debug("Mapping BAR%d %#lx-%#lx", i, BARBase, BARBase + BARSize); + Memory::Virtual().Map((void *)BARBase, (void *)BARBase, BARSize, Memory::PTFlag::RW | Memory::PTFlag::PWT); + } + else if ((BAR[i] & 1) == 1) // I/O Base + { + uintptr_t BARBase = BAR[i] & (~3); + size_t BARSize = BARsSize[i]; + + debug("Mapping BAR%d %#x-%#x", i, BARBase, BARBase + BARSize); + Memory::Virtual().Map((void *)BARBase, (void *)BARBase, BARSize, Memory::PTFlag::RW | Memory::PTFlag::PWT); + } + } + break; + } + case 1: // PCI Header 1 (PCI-to-PCI Bridge) + { + fixme("PCI Header 1 (PCI-to-PCI Bridge) not implemented yet"); + break; + } + case 2: // PCI Header 2 (PCI-to-CardBus Bridge) + { + fixme("PCI Header 2 (PCI-to-CardBus Bridge) not implemented yet"); + break; + } + default: + { + error("Unknown header type %d", PCIDevice->HeaderType); + return; + } + } + } + + DriverCode Driver::DriverLoadBindPCI(void *DrvExtHdr, uintptr_t DriverAddress, size_t Size, bool IsElf) + { + UNUSED(IsElf); + for (unsigned long Vidx = 0; Vidx < sizeof(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.VendorID) / sizeof(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.VendorID[0]); Vidx++) + { + for (unsigned long Didx = 0; Didx < sizeof(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.DeviceID) / sizeof(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.DeviceID[0]); Didx++) + { + if (Vidx >= sizeof(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.VendorID) && Didx >= sizeof(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.DeviceID)) + break; + + if (((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.VendorID[Vidx] == 0 || ((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.DeviceID[Didx] == 0) + continue; + + std::vector devices = PCIManager->FindPCIDevice(((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.VendorID[Vidx], ((FexExtended *)DrvExtHdr)->Driver.Bind.PCI.DeviceID[Didx]); + if (devices.size() == 0) + continue; + + foreach (auto PCIDevice in devices) + { + debug("[%ld] VendorID: %#x; DeviceID: %#x", devices.size(), PCIDevice->VendorID, PCIDevice->DeviceID); + Memory::MemMgr *mem = new Memory::MemMgr(nullptr, TaskManager->GetCurrentProcess()->memDirectory); + Fex *fex = (Fex *)mem->RequestPages(TO_PAGES(Size + 1)); + memcpy(fex, (void *)DriverAddress, Size); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + debug("Driver allocated at %#lx-%#lx", fex, (uintptr_t)fex + Size); +#ifdef DEBUG + uint8_t *result = md5File((uint8_t *)fex, Size); + debug("MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]); + kfree(result); +#endif + KernelAPI *KAPI = (KernelAPI *)mem->RequestPages(TO_PAGES(sizeof(KernelAPI) + 1)); + + if (CallDriverEntryPoint(fex, KAPI) != DriverCode::OK) + { + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + debug("Starting driver %s", fexExtended->Driver.Name); + + MapPCIAddresses(PCIDevice); + + switch (fexExtended->Driver.Type) + { + case FexDriverType::FexDriverType_Generic: + return BindPCIGeneric(mem, fex, PCIDevice); + case FexDriverType::FexDriverType_Display: + return BindPCIDisplay(mem, fex, PCIDevice); + case FexDriverType::FexDriverType_Network: + return BindPCINetwork(mem, fex, PCIDevice); + case FexDriverType::FexDriverType_Storage: + return BindPCIStorage(mem, fex, PCIDevice); + case FexDriverType::FexDriverType_FileSystem: + return BindPCIFileSystem(mem, fex, PCIDevice); + case FexDriverType::FexDriverType_Input: + return BindPCIInput(mem, fex, PCIDevice); + case FexDriverType::FexDriverType_Audio: + return BindPCIAudio(mem, fex, PCIDevice); + default: + { + warn("Unknown driver type: %d", fexExtended->Driver.Type); + delete mem, mem = nullptr; + return DriverCode::UNKNOWN_DRIVER_TYPE; + } + } + } + } + } + return DriverCode::PCI_DEVICE_NOT_FOUND; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/BindProcess.cpp b/Kernel/Core/Driver/DriverBinding/BindProcess.cpp new file mode 100644 index 00000000..98cadbfe --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/BindProcess.cpp @@ -0,0 +1,42 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../kernel.h" +#include "../../../DAPI.hpp" +#include "../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::DriverLoadBindProcess(void *DrvExtHdr, uintptr_t DriverAddress, size_t Size, bool IsElf) + { + fixme("Process driver: %s", ((FexExtended *)DrvExtHdr)->Driver.Name); + UNUSED(Size); + UNUSED(DriverAddress); + UNUSED(IsElf); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Audio.cpp b/Kernel/Core/Driver/DriverBinding/Input/Audio.cpp new file mode 100644 index 00000000..659680a2 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Audio.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputAudio(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Display.cpp b/Kernel/Core/Driver/DriverBinding/Input/Display.cpp new file mode 100644 index 00000000..7b4d6979 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Display.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputDisplay(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Filesystem.cpp b/Kernel/Core/Driver/DriverBinding/Input/Filesystem.cpp new file mode 100644 index 00000000..140f038c --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Filesystem.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputFileSystem(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Generic.cpp b/Kernel/Core/Driver/DriverBinding/Input/Generic.cpp new file mode 100644 index 00000000..5a17fea1 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Generic.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputGeneric(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Input.cpp b/Kernel/Core/Driver/DriverBinding/Input/Input.cpp new file mode 100644 index 00000000..11169046 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Input.cpp @@ -0,0 +1,67 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputInput(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + KernelCallback KCallback{}; + + fixme("Input driver: %s", fexExtended->Driver.Name); + KCallback.RawPtr = nullptr; + KCallback.Reason = CallbackReason::ConfigurationReason; + int CallbackRet = ((int (*)(KernelCallback *))((uintptr_t)fexExtended->Driver.Callback + (uintptr_t)fex))(&KCallback); + if (CallbackRet == DriverReturnCode::NOT_IMPLEMENTED) + { + delete mem, mem = nullptr; + error("Driver %s is not implemented", fexExtended->Driver.Name); + return DriverCode::NOT_IMPLEMENTED; + } + else if (CallbackRet != DriverReturnCode::OK) + { + delete mem, mem = nullptr; + error("Driver %s returned error %d", fexExtended->Driver.Name, CallbackRet); + return DriverCode::DRIVER_RETURNED_ERROR; + } + + fixme("Input driver: %s", fexExtended->Driver.Name); + + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .MemTrk = mem, + }; + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Network.cpp b/Kernel/Core/Driver/DriverBinding/Input/Network.cpp new file mode 100644 index 00000000..4ddbe241 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Network.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputNetwork(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Input/Storage.cpp b/Kernel/Core/Driver/DriverBinding/Input/Storage.cpp new file mode 100644 index 00000000..d051fd10 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Input/Storage.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInputStorage(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Audio.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Audio.cpp new file mode 100644 index 00000000..bdf20d3a --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Audio.cpp @@ -0,0 +1,81 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptAudio(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Audio driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Display.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Display.cpp new file mode 100644 index 00000000..8db09606 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Display.cpp @@ -0,0 +1,81 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptDisplay(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Display driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Filesystem.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Filesystem.cpp new file mode 100644 index 00000000..751920af --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Filesystem.cpp @@ -0,0 +1,81 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptFileSystem(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Filesystem driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Generic.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Generic.cpp new file mode 100644 index 00000000..1a01cbd2 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Generic.cpp @@ -0,0 +1,88 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptGeneric(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Generic driver: %s", fexExtended->Driver.Name); + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .InterruptCallback = (void *)((uintptr_t)fex + (uintptr_t)fexExtended->Driver.InterruptCallback), + .MemTrk = mem, + }; + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Input.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Input.cpp new file mode 100644 index 00000000..520630f1 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Input.cpp @@ -0,0 +1,124 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptInput(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + debug("Searching for conflicting drivers..."); + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if ((fe->Driver.TypeFlags & FexDriverInputTypes_Mouse && + fexExtended->Driver.TypeFlags & FexDriverInputTypes_Mouse) || + (fe->Driver.TypeFlags & FexDriverInputTypes_Keyboard && + fexExtended->Driver.TypeFlags & FexDriverInputTypes_Keyboard)) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + if (fe->Driver.OverrideOnConflict) + return DriverCode::DRIVER_CONFLICT; + + DriversToRemove.push_back(Drv.DriverUID); + } + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if ((fe->Driver.TypeFlags & FexDriverInputTypes_Mouse && + fexExtended->Driver.TypeFlags & FexDriverInputTypes_Mouse) || + (fe->Driver.TypeFlags & FexDriverInputTypes_Keyboard && + fexExtended->Driver.TypeFlags & FexDriverInputTypes_Keyboard)) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + if (fe->Driver.OverrideOnConflict) + return DriverCode::DRIVER_CONFLICT; + } + } + } + + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .InterruptCallback = (void *)((uintptr_t)fex + (uintptr_t)fexExtended->Driver.InterruptCallback), + .MemTrk = mem, + }; + if (fexExtended->Driver.InterruptCallback) + { + for (unsigned long i = 0; i < sizeof(fexExtended->Driver.Bind.Interrupt.Vector) / sizeof(fexExtended->Driver.Bind.Interrupt.Vector[0]); i++) + { + if (fexExtended->Driver.Bind.Interrupt.Vector[i] == 0) + break; + DrvFile.InterruptHook[i] = new DriverInterruptHook(fexExtended->Driver.Bind.Interrupt.Vector[i], DrvFile); + } + } + + KernelCallback KCallback{}; + KCallback.RawPtr = nullptr; + KCallback.Reason = CallbackReason::ConfigurationReason; + int CallbackRet = ((int (*)(KernelCallback *))((uintptr_t)fexExtended->Driver.Callback + (uintptr_t)fex))(&KCallback); + + if (CallbackRet == DriverReturnCode::NOT_IMPLEMENTED) + { + error("Driver %s is not implemented", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } + else if (CallbackRet != DriverReturnCode::OK) + { + error("Driver %s returned error %d", fexExtended->Driver.Name, CallbackRet); + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Network.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Network.cpp new file mode 100644 index 00000000..8e718933 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Network.cpp @@ -0,0 +1,81 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptNetwork(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Network driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Interrupt/Storage.cpp b/Kernel/Core/Driver/DriverBinding/Interrupt/Storage.cpp new file mode 100644 index 00000000..470cf71f --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Interrupt/Storage.cpp @@ -0,0 +1,115 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindInterruptStorage(Memory::MemMgr *mem, void *fex) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .InterruptCallback = (void *)((uintptr_t)fex + (uintptr_t)fexExtended->Driver.InterruptCallback), + .MemTrk = mem, + }; + if (fexExtended->Driver.InterruptCallback) + { + for (unsigned long i = 0; i < sizeof(fexExtended->Driver.Bind.Interrupt.Vector) / sizeof(fexExtended->Driver.Bind.Interrupt.Vector[0]); i++) + { + if (fexExtended->Driver.Bind.Interrupt.Vector[i] == 0) + break; + DrvFile.InterruptHook[i] = new DriverInterruptHook(fexExtended->Driver.Bind.Interrupt.Vector[i], DrvFile); + } + } + + KernelCallback KCallback{}; + KCallback.RawPtr = nullptr; + KCallback.Reason = CallbackReason::ConfigurationReason; + int CallbackRet = ((int (*)(KernelCallback *))((uintptr_t)fexExtended->Driver.Callback + (uintptr_t)fex))(&KCallback); + + if (CallbackRet == DriverReturnCode::NOT_IMPLEMENTED) + { + error("Driver %s is not implemented", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } + else if (CallbackRet != DriverReturnCode::OK) + { + error("Driver %s returned error %d", fexExtended->Driver.Name, CallbackRet); + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Audio.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Audio.cpp new file mode 100644 index 00000000..1e657572 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Audio.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCIAudio(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .InterruptCallback = (void *)((uintptr_t)fex + (uintptr_t)fexExtended->Driver.InterruptCallback), + .MemTrk = mem, + }; + if (fexExtended->Driver.InterruptCallback) + DrvFile.InterruptHook[0] = new DriverInterruptHook(((int)((PCI::PCIHeader0 *)PCIDevice)->InterruptLine), DrvFile); + + KernelCallback KCallback{}; + KCallback.RawPtr = PCIDevice; + KCallback.Reason = CallbackReason::ConfigurationReason; + int CallbackRet = ((int (*)(KernelCallback *))((uintptr_t)fexExtended->Driver.Callback + (uintptr_t)fex))(&KCallback); + + if (CallbackRet == DriverReturnCode::NOT_IMPLEMENTED) + { + error("Driver %s is not implemented", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } + else if (CallbackRet == DriverReturnCode::OK) + trace("Device found for driver: %s", fexExtended->Driver.Name); + else + { + error("Driver %s returned error %d", fexExtended->Driver.Name, CallbackRet); + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Display.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Display.cpp new file mode 100644 index 00000000..46fa64cd --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Display.cpp @@ -0,0 +1,82 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCIDisplay(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + UNUSED(PCIDevice); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Display driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Filesystem.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Filesystem.cpp new file mode 100644 index 00000000..decd4757 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Filesystem.cpp @@ -0,0 +1,82 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCIFileSystem(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + UNUSED(PCIDevice); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Filesystem driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Generic.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Generic.cpp new file mode 100644 index 00000000..4dba1562 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Generic.cpp @@ -0,0 +1,82 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCIGeneric(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + UNUSED(PCIDevice); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Generic driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Input.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Input.cpp new file mode 100644 index 00000000..01e4b95f --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Input.cpp @@ -0,0 +1,82 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCIInput(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + UNUSED(PCIDevice); + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + fixme("Input driver: %s", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Network.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Network.cpp new file mode 100644 index 00000000..5e28fdcc --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Network.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCINetwork(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .InterruptCallback = (void *)((uintptr_t)fex + (uintptr_t)fexExtended->Driver.InterruptCallback), + .MemTrk = mem, + }; + if (fexExtended->Driver.InterruptCallback) + DrvFile.InterruptHook[0] = new DriverInterruptHook(((int)((PCI::PCIHeader0 *)PCIDevice)->InterruptLine), DrvFile); + + KernelCallback KCallback{}; + KCallback.RawPtr = PCIDevice; + KCallback.Reason = CallbackReason::ConfigurationReason; + int CallbackRet = ((int (*)(KernelCallback *))((uintptr_t)fexExtended->Driver.Callback + (uintptr_t)fex))(&KCallback); + + if (CallbackRet == DriverReturnCode::NOT_IMPLEMENTED) + { + error("Driver %s is not implemented", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } + else if (CallbackRet == DriverReturnCode::OK) + trace("Device found for driver: %s", fexExtended->Driver.Name); + else + { + error("Driver %s returned error %d", fexExtended->Driver.Name, CallbackRet); + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/PCI/Storage.cpp b/Kernel/Core/Driver/DriverBinding/PCI/Storage.cpp new file mode 100644 index 00000000..2e3a3eaa --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/PCI/Storage.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindPCIStorage(Memory::MemMgr *mem, void *fex, PCI::PCIDeviceHeader *PCIDevice) + { + FexExtended *fexExtended = (FexExtended *)((uintptr_t)fex + EXTENDED_SECTION_ADDRESS); + + if (fexExtended->Driver.OverrideOnConflict) + { + std::vector DriversToRemove = std::vector(); + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + + DriversToRemove.push_back(Drv.DriverUID); + } + + foreach (auto DrvID in DriversToRemove) + { + if (!this->UnloadDriver(DrvID)) + { + error("Failed to unload conflicting driver %d", DrvID); + return DriverCode::DRIVER_CONFLICT; + } + } + } + else + { + foreach (auto Drv in Drivers) + { + FexExtended *fe = ((FexExtended *)((uintptr_t)Drv.Address + EXTENDED_SECTION_ADDRESS)); + + if (fe->Driver.OverrideOnConflict) + { + debug("Driver %s is conflicting with %s", fe->Driver.Name, fexExtended->Driver.Name); + return DriverCode::DRIVER_CONFLICT; + } + } + } + + DriverFile DrvFile = { + .Enabled = true, + .DriverUID = this->DriverUIDs - 1, + .Address = (void *)fex, + .InterruptCallback = (void *)((uintptr_t)fex + (uintptr_t)fexExtended->Driver.InterruptCallback), + .MemTrk = mem, + }; + if (fexExtended->Driver.InterruptCallback) + DrvFile.InterruptHook[0] = new DriverInterruptHook(((int)((PCI::PCIHeader0 *)PCIDevice)->InterruptLine), DrvFile); + + KernelCallback KCallback{}; + KCallback.RawPtr = PCIDevice; + KCallback.Reason = CallbackReason::ConfigurationReason; + int CallbackRet = ((int (*)(KernelCallback *))((uintptr_t)fexExtended->Driver.Callback + (uintptr_t)fex))(&KCallback); + + if (CallbackRet == DriverReturnCode::NOT_IMPLEMENTED) + { + error("Driver %s is not implemented", fexExtended->Driver.Name); + delete mem, mem = nullptr; + return DriverCode::NOT_IMPLEMENTED; + } + else if (CallbackRet == DriverReturnCode::OK) + trace("Device found for driver: %s", fexExtended->Driver.Name); + else + { + error("Driver %s returned error %d", fexExtended->Driver.Name, CallbackRet); + delete mem, mem = nullptr; + return DriverCode::DRIVER_RETURNED_ERROR; + } + + Drivers.push_back(DrvFile); + return DriverCode::OK; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Audio.cpp b/Kernel/Core/Driver/DriverBinding/Process/Audio.cpp new file mode 100644 index 00000000..c821bb0c --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Audio.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessAudio(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Display.cpp b/Kernel/Core/Driver/DriverBinding/Process/Display.cpp new file mode 100644 index 00000000..12d221fb --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Display.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessDisplay(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Filesystem.cpp b/Kernel/Core/Driver/DriverBinding/Process/Filesystem.cpp new file mode 100644 index 00000000..079182dc --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Filesystem.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessFileSystem(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Generic.cpp b/Kernel/Core/Driver/DriverBinding/Process/Generic.cpp new file mode 100644 index 00000000..22a46c3f --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Generic.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessGeneric(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Input.cpp b/Kernel/Core/Driver/DriverBinding/Process/Input.cpp new file mode 100644 index 00000000..c7285f88 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Input.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessInput(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Network.cpp b/Kernel/Core/Driver/DriverBinding/Process/Network.cpp new file mode 100644 index 00000000..a67d2241 --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Network.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessNetwork(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/DriverBinding/Process/Storage.cpp b/Kernel/Core/Driver/DriverBinding/Process/Storage.cpp new file mode 100644 index 00000000..139c258a --- /dev/null +++ b/Kernel/Core/Driver/DriverBinding/Process/Storage.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "../../api.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../kernel.h" +#include "../../../../DAPI.hpp" +#include "../../../../Fex.hpp" + +namespace Driver +{ + DriverCode Driver::BindProcessStorage(Memory::MemMgr *mem, void *fex) + { + UNUSED(mem); + UNUSED(fex); + return DriverCode::NOT_IMPLEMENTED; + } +} diff --git a/Kernel/Core/Driver/api.hpp b/Kernel/Core/Driver/api.hpp new file mode 100644 index 00000000..46367260 --- /dev/null +++ b/Kernel/Core/Driver/api.hpp @@ -0,0 +1,27 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_DRIVER_API_H__ +#define __FENNIX_KERNEL_DRIVER_API_H__ + +#include + +#include "../../DAPI.hpp" + +extern KernelAPI KernelAPITemplate; + +#endif // !__FENNIX_KERNEL_DRIVER_API_H__ diff --git a/Kernel/Core/InterruptsManager.cpp b/Kernel/Core/InterruptsManager.cpp new file mode 100644 index 00000000..c169c0c0 --- /dev/null +++ b/Kernel/Core/InterruptsManager.cpp @@ -0,0 +1,235 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include +#include + +#if defined(a64) +#include "../Architecture/amd64/cpu/gdt.hpp" +#include "../Architecture/amd64/cpu/idt.hpp" +#include "../Architecture/amd64/acpi.hpp" +#include "../Architecture/amd64/cpu/apic.hpp" +#elif defined(a32) +#include "../Architecture/i386/cpu/gdt.hpp" +#include "../Architecture/i386/cpu/idt.hpp" +#elif defined(aa64) +#endif + +#include "crashhandler.hpp" +#include "../kernel.h" + +extern "C" SafeFunction void ExceptionHandler(void *Data) { CrashHandler::Handle(Data); } + +namespace Interrupts +{ + struct Event + { + int ID; + void *Data; + }; + std::vector RegisteredEvents; + +#if defined(a64) + /* APIC::APIC */ void *apic[MAX_CPU]; + /* APIC::Timer */ void *apicTimer[MAX_CPU]; +#elif defined(a32) + /* APIC::APIC */ void *apic[MAX_CPU]; +#elif defined(aa64) +#endif + void *InterruptFrames[INT_FRAMES_MAX]; + + void Initialize(int Core) + { +#if defined(a64) + GlobalDescriptorTable::Init(Core); + InterruptDescriptorTable::Init(Core); + CPUData *CoreData = GetCPU(Core); + CoreData->Checksum = CPU_DATA_CHECKSUM; + CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, (uint64_t)CoreData); + CPU::x64::wrmsr(CPU::x64::MSR_SHADOW_GS_BASE, (uint64_t)CoreData); + CoreData->ID = Core; + CoreData->IsActive = true; + CoreData->SystemCallStack = (uint8_t *)((uintptr_t)KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1)) + STACK_SIZE); + CoreData->Stack = (uintptr_t)KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1)) + STACK_SIZE; + if (CoreData->Checksum != CPU_DATA_CHECKSUM) + { + KPrint("CPU %d checksum mismatch! %x != %x", Core, CoreData->Checksum, CPU_DATA_CHECKSUM); + CPU::Stop(); + } + debug("Stack for core %d is %#lx (Address: %#lx)", Core, CoreData->Stack, CoreData->Stack - STACK_SIZE); + InitializeSystemCalls(); +#elif defined(a32) + warn("i386 is not supported yet"); +#elif defined(aa64) + warn("aarch64 is not supported yet"); +#endif + } + + void Enable(int Core) + { +#if defined(a64) + if (((ACPI::MADT *)PowerManager->GetMADT())->LAPICAddress != nullptr) + { + // TODO: This function is called by SMP too. Do not initialize timers that doesn't support multiple cores. + apic[Core] = new APIC::APIC(Core); + if (Core == Config.IOAPICInterruptCore) // Redirect IRQs to the specified core. + ((APIC::APIC *)apic[Core])->RedirectIRQs(Core); + } + else + { + error("LAPIC not found"); + // TODO: PIC + } +#elif defined(a32) + warn("i386 is not supported yet"); +#elif defined(aa64) + warn("aarch64 is not supported yet"); +#endif + } + + void InitializeTimer(int Core) + { + // TODO: This function is called by SMP too. Do not initialize timers that doesn't support multiple cores. +#if defined(a64) + if (apic[Core] != nullptr) + apicTimer[Core] = new APIC::Timer((APIC::APIC *)apic[Core]); + else + { + fixme("apic not found"); + } +#elif defined(a32) + warn("i386 is not supported yet"); +#elif defined(aa64) + warn("aarch64 is not supported yet"); +#endif + } + + SafeFunction void RemoveAll() + { + RegisteredEvents.clear(); + } + + extern "C" SafeFunction void MainInterruptHandler(void *Data) + { +#if defined(a64) + CPU::x64::TrapFrame *Frame = (CPU::x64::TrapFrame *)Data; + // debug("IRQ%ld", Frame->InterruptNumber - 32); + + memmove(InterruptFrames + 1, InterruptFrames, sizeof(InterruptFrames) - sizeof(InterruptFrames[0])); + InterruptFrames[0] = (void *)Frame->rip; + + CPUData *CoreData = GetCurrentCPU(); + int Core = 0; + if (likely(CoreData != nullptr)) + Core = CoreData->ID; + + /* If this is false, we have a big problem. */ + if (likely(Frame->InterruptNumber < CPU::x86::IRQ223 && Frame->InterruptNumber > CPU::x86::ISR0)) + { + /* Halt core interrupt */ + if (unlikely(Frame->InterruptNumber == CPU::x86::IRQ29)) + CPU::Stop(); + + bool InterruptHandled = false; + foreach (auto ev in RegisteredEvents) + { +#if defined(a86) + if ((ev.ID + CPU::x86::IRQ0) == static_cast(Frame->InterruptNumber)) +#elif defined(aa64) + if (ev.ID == static_cast(Frame->InterruptNumber)) +#endif + { + ((Handler *)ev.Data)->OnInterruptReceived(Frame); + InterruptHandled = true; + } + } + + if (!InterruptHandled) + { + error("IRQ%ld is unhandled on CPU %d.", Frame->InterruptNumber - 32, Core); + if (Frame->InterruptNumber == CPU::x86::IRQ1) + { + uint8_t scancode = inb(0x60); + warn("IRQ1 is the keyboard interrupt. Scancode: %#x", scancode); + } + } + + if (likely(apic[Core])) + { + ((APIC::APIC *)Interrupts::apic[Core])->EOI(); + // TODO: Handle PIC too + return; + } + // TODO: PIC + } +#elif defined(a32) + void *Frame = Data; +#elif defined(aa64) + CPU::aarch64::TrapFrame *Frame = (CPU::aarch64::TrapFrame *)Data; +#endif + error("HALT HALT HALT HALT HALT HALT HALT HALT HALT"); + CPU::Stop(); + } + + Handler::Handler(int InterruptNumber) + { + foreach (auto ev in RegisteredEvents) + { + if (ev.ID == InterruptNumber) + { + warn("IRQ%d is already registered.", InterruptNumber); + } + } + + debug("Registering interrupt handler for IRQ%d.", InterruptNumber); + this->InterruptNumber = InterruptNumber; + RegisteredEvents.push_back({InterruptNumber, this}); + } + + Handler::~Handler() + { + debug("Unregistering interrupt handler for IRQ%d.", this->InterruptNumber); + for (size_t i = 0; i < RegisteredEvents.size(); i++) + { + if (RegisteredEvents[i].ID == this->InterruptNumber) + { + RegisteredEvents.remove(i); + return; + } + } + warn("Event %d not found.", this->InterruptNumber); + } + +#if defined(a64) + void Handler::OnInterruptReceived(CPU::x64::TrapFrame *Frame) + { + trace("Unhandled interrupt IRQ%d", Frame->InterruptNumber - 32); +#elif defined(a32) + void Handler::OnInterruptReceived(CPU::x32::TrapFrame *Frame) + { + trace("Unhandled interrupt received"); +#elif defined(aa64) + void Handler::OnInterruptReceived(CPU::aarch64::TrapFrame *Frame) + { + trace("Unhandled interrupt received"); +#endif + } +} diff --git a/Kernel/Core/Lock.cpp b/Kernel/Core/Lock.cpp new file mode 100644 index 00000000..f1b0cfb6 --- /dev/null +++ b/Kernel/Core/Lock.cpp @@ -0,0 +1,207 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +#include "../kernel.h" + +// #define PRINT_BACKTRACE + +#ifdef PRINT_BACKTRACE +#pragma GCC diagnostic ignored "-Wframe-address" +#endif + +bool ForceUnlock = false; +std::atomic_size_t LocksCount = 0; + +size_t GetLocksCount() { return LocksCount.load(); } + +void LockClass::DeadLock(SpinLockData Lock) +{ + if (ForceUnlock) + { + warn("Unlocking lock '%s' which it was held by '%s'...", Lock.AttemptingToGet, Lock.CurrentHolder); + this->DeadLocks = 0; + this->Unlock(); + return; + } + + CPUData *CoreData = GetCurrentCPU(); + long CCore = 0xdead; + if (CoreData != nullptr) + CCore = CoreData->ID; + + warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. (%ld times happened)", + Lock.AttemptingToGet, Lock.CurrentHolder, + Lock.Count, Lock.Count > 1 ? "locks" : "lock", + CPU::Interrupts(CPU::Check) ? "enabled" : "disabled", + CCore, Lock.Core, this->DeadLocks); + +#ifdef PRINT_BACKTRACE + if (KernelSymbolTable) + { + debug("\t\t%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s", + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(1)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(2)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(3)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(4)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(5)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(6)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(7)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(8)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(9))); + } +#endif + + // TODO: Print on screen too. + + this->DeadLocks++; + + if (Config.UnlockDeadLock && this->DeadLocks.load() > 10) + { + warn("Unlocking lock '%s' to prevent deadlock. (this is enabled in the kernel config)", Lock.AttemptingToGet); + this->DeadLocks = 0; + this->Unlock(); + } + + if (TaskManager) + TaskManager->Schedule(); +} + +int LockClass::Lock(const char *FunctionName) +{ + LockData.AttemptingToGet = FunctionName; + LockData.StackPointerAttempt = (uintptr_t)__builtin_frame_address(0); + +Retry: + int i = 0; + while (IsLocked.exchange(true, std::memory_order_acquire) && ++i < (DebuggerIsAttached ? 0x100000 : 0x10000000)) + CPU::Pause(); + + if (i >= (DebuggerIsAttached ? 0x100000 : 0x10000000)) + { + DeadLock(LockData); + goto Retry; + } + + LockData.Count++; + LockData.CurrentHolder = FunctionName; + LockData.StackPointerHolder = (uintptr_t)__builtin_frame_address(0); + + CPUData *CoreData = GetCurrentCPU(); + if (CoreData != nullptr) + LockData.Core = CoreData->ID; + + LocksCount++; + + __sync; + return 0; +} + +int LockClass::Unlock() +{ + __sync; + + IsLocked.store(false, std::memory_order_release); + LockData.Count--; + LocksCount--; + + return 0; +} + +void LockClass::TimeoutDeadLock(SpinLockData Lock, uint64_t Timeout) +{ + CPUData *CoreData = GetCurrentCPU(); + long CCore = 0xdead; + + if (CoreData != nullptr) + CCore = CoreData->ID; + + uint64_t Counter = TimeManager->GetCounter(); + + warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. Timeout in %ld (%ld ticks remaining).", + Lock.AttemptingToGet, Lock.CurrentHolder, + Lock.Count, Lock.Count > 1 ? "locks" : "lock", + CPU::Interrupts(CPU::Check) ? "enabled" : "disabled", + CCore, Lock.Core, Timeout, Timeout - Counter); + +#ifdef PRINT_BACKTRACE + if (KernelSymbolTable) + { + debug("\t\t%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s", + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(1)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(2)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(3)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(4)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(5)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(6)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(7)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(8)), + KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(9))); + } +#endif + + if (Timeout < Counter) + { + warn("Unlocking lock '%s' because of timeout. (%ld < %ld)", Lock.AttemptingToGet, Timeout, Counter); + this->Unlock(); + } + + if (TaskManager) + TaskManager->Schedule(); +} + +int LockClass::TimeoutLock(const char *FunctionName, uint64_t Timeout) +{ + if (!TimeManager) + return Lock(FunctionName); + + LockData.AttemptingToGet = FunctionName; + LockData.StackPointerAttempt = (uintptr_t)__builtin_frame_address(0); + + std::atomic_uint64_t Target = 0; +Retry: + int i = 0; + while (IsLocked.exchange(true, std::memory_order_acquire) && ++i < (DebuggerIsAttached ? 0x100000 : 0x10000000)) + CPU::Pause(); + + if (i >= (DebuggerIsAttached ? 0x100000 : 0x10000000)) + { + if (Target.load() == 0) + Target.store(TimeManager->CalculateTarget(Timeout, Time::Units::Milliseconds)); + TimeoutDeadLock(LockData, Target.load()); + goto Retry; + } + + LockData.Count++; + LockData.CurrentHolder = FunctionName; + LockData.StackPointerHolder = (uintptr_t)__builtin_frame_address(0); + + CPUData *CoreData = GetCurrentCPU(); + if (CoreData != nullptr) + LockData.Core = CoreData->ID; + + LocksCount++; + + __sync; + return 0; +} diff --git a/Kernel/Core/Memory/HeapAllocators/Xalloc/README.md b/Kernel/Core/Memory/HeapAllocators/Xalloc/README.md new file mode 100644 index 00000000..4c54e9ca --- /dev/null +++ b/Kernel/Core/Memory/HeapAllocators/Xalloc/README.md @@ -0,0 +1,137 @@ +# Xalloc + +Xalloc is a custom memory allocator designed for hobby operating systems. It is written in C++ and provides a simple and efficient way to manage memory in your hobby OS. + +#### ❗ This project is still in development and is not ready for use in production environments. ❗ + +--- + +## Features + +- **Simple API** - Xalloc provides a simple API for allocating and freeing memory. It is designed to be easy to use and understand. + +- [ ] todo complete this + +--- + +## Getting Started + +### Implementing missing functions + +You will need to implement the following functions in your OS: + +##### Wrapper.cpp +```cpp +extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages) +{ + // ... +} + +extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages) +{ + // ... +} + +extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags) +{ + // ... +} + +extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress) +{ + // ... +} +``` + +##### Xalloc.hpp +```cpp +#define Xalloc_PAGE_SIZE /* <-- Replace with your page size */ +#define Xalloc_trace(m, ...) +#define Xalloc_warn(m, ...) +#define Xalloc_err(m, ...) +#define Xalloc_def /* eg. std::mutex Xalloc_lock; */ +#define Xalloc_lock +#define Xalloc_unlock +``` + +### Typical usage + +```cpp +#include "Xalloc.hpp" + +Xalloc::V1 *XallocV1Allocator = nullptr; + +int main() +{ + /* Virtual Base User SMAP */ + XallocV1Allocator = new Xalloc::V1((void *)0xFFFFA00000000000, false, false); + + void *p = XallocV1Allocator->malloc(1234); + /* ... */ + XallocV1Allocator->free(p); + delete XallocV1Allocator; + return 0; +} +``` + +or + +```cpp +#include "Xalloc.hpp" + +int main() +{ + /* Virtual Base User SMAP */ + Xalloc::V1 XallocV1Allocator((void *)0xFFFFA00000000000, false, false); + + void *p = XallocV1Allocator.malloc(1234); + /* ... */ + XallocV1Allocator.free(p); + return 0; +} +``` + +--- + +## API + +### Xalloc::V1 + +```cpp +void *malloc(Xsize_t Size); +``` +Allocates a block of memory of size `Size` bytes. +If `Size` is 0, then `nullptr` is returned. +- `Size` - The size of the block to allocate in bytes. + +

+ +```cpp +void free(void *Address); +``` +Frees the memory block pointed to by `Address`. +If `Address` is `nullptr`, then no operation is performed. +- `Address` - The address of the memory block to free. + +

+ +```cpp +void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size); +``` +Allocates a block of memory for an array of `NumberOfBlocks` elements, each of them `Size` bytes long. +If `NumberOfBlocks` or `Size` is 0, then `nullptr` is returned. +- `NumberOfBlocks` - The number of elements to allocate. +- `Size` - The size of each element in bytes. + +

+ +```cpp +void *realloc(void *Address, Xsize_t Size); +``` +Changes the size of the memory block pointed to by `Address` to `Size` bytes. +If `Address` is `nullptr`, then the call is equivalent to `malloc(Size)`. +If `Size` is equal to zero, and `Address` is not `nullptr`, then the call is equivalent to `free(Address)`. +- `Address` - The address of the memory block to resize. +- `Size` - The new size of the memory block in bytes. + +--- diff --git a/Kernel/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp b/Kernel/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp new file mode 100644 index 00000000..68258d70 --- /dev/null +++ b/Kernel/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp @@ -0,0 +1,40 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "Xalloc.hpp" + +#include + +extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages) +{ + return KernelAllocator.RequestPages(Pages); +} + +extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages) +{ + KernelAllocator.FreePages(Address, Pages); +} + +extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags) +{ + Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags); +} + +extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress) +{ + Memory::Virtual(KernelPageTable).Unmap(VirtualAddress); +} diff --git a/Kernel/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp b/Kernel/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp new file mode 100644 index 00000000..07006a96 --- /dev/null +++ b/Kernel/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp @@ -0,0 +1,119 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_Xalloc_H__ +#define __FENNIX_KERNEL_Xalloc_H__ + +#include +#include +#include + +typedef long unsigned Xuint64_t; +typedef long unsigned Xsize_t; + +#define Xalloc_StopOnFail true +#define Xalloc_PAGE_SIZE PAGE_SIZE +#define Xalloc_trace(m, ...) trace(m, ##__VA_ARGS__) +#define Xalloc_warn(m, ...) warn(m, ##__VA_ARGS__) +#define Xalloc_err(m, ...) error(m, ##__VA_ARGS__) +#define Xalloc_def NewLock(XallocLock) +#define Xalloc_lock XallocLock.Lock(__FUNCTION__) +#define Xalloc_unlock XallocLock.Unlock() + +namespace Xalloc +{ + class V1 + { + private: + void *BaseVirtualAddress = nullptr; + void *FirstBlock = nullptr; + void *LastBlock = nullptr; + + bool UserMapping = false; + bool SMAPUsed = false; + + public: + /** @brief Execute "stac" instruction if the kernel has SMAP enabled */ + void Xstac(); + + /** @brief Execute "clac" instruction if the kernel has SMAP enabled */ + void Xclac(); + + /** + * @brief Arrange the blocks to optimize the memory usage + * The allocator is not arranged by default + * to avoid performance issues. + * This function will defragment the memory + * and free the unused blocks. + * + * You should call this function when the + * kernel is idle or when is not using + * the allocator. + */ + void Arrange(); + + /** + * @brief Allocate a new memory block + * + * @param Size Size of the block to allocate. + * @return void* Pointer to the allocated block. + */ + void *malloc(Xsize_t Size); + + /** + * @brief Free a previously allocated block + * + * @param Address Address of the block to free. + */ + void free(void *Address); + + /** + * @brief Allocate a new memory block + * + * @param NumberOfBlocks Number of blocks to allocate. + * @param Size Size of the block to allocate. + * @return void* Pointer to the allocated block. + */ + void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size); + + /** + * @brief Reallocate a previously allocated block + * + * @param Address Address of the block to reallocate. + * @param Size New size of the block. + * @return void* Pointer to the reallocated block. + */ + void *realloc(void *Address, Xsize_t Size); + + /** + * @brief Construct a new Allocator object + * + * @param BaseVirtualAddress Virtual address to map the pages. + * @param UserMode Map the new pages with USER flag? + * @param SMAPEnabled Does the kernel has Supervisor Mode Access Prevention enabled? + */ + V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled); + + /** + * @brief Destroy the Allocator object + * + */ + ~V1(); + }; +} + +#endif // !__FENNIX_KERNEL_Xalloc_H__ diff --git a/Kernel/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp b/Kernel/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp new file mode 100644 index 00000000..12f38372 --- /dev/null +++ b/Kernel/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp @@ -0,0 +1,290 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "Xalloc.hpp" + +Xalloc_def; + +#define XALLOC_CONCAT(x, y) x##y +#define XStoP(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE) +#define XPtoS(d) ((d)*PAGE_SIZE) +#define Xalloc_BlockChecksum 0xA110C + +extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages); +extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages); +extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags); +extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress); + +// TODO: Change memcpy with an optimized version +void *Xmemcpy(void *__restrict__ Destination, const void *__restrict__ Source, Xuint64_t Length) +{ + unsigned char *dst = (unsigned char *)Destination; + const unsigned char *src = (const unsigned char *)Source; + for (Xuint64_t i = 0; i < Length; i++) + dst[i] = src[i]; + return Destination; +} + +// TODO: Change memset with an optimized version +void *Xmemset(void *__restrict__ Destination, int Data, Xuint64_t Length) +{ + unsigned char *Buffer = (unsigned char *)Destination; + for (Xuint64_t i = 0; i < Length; i++) + Buffer[i] = (unsigned char)Data; + return Destination; +} + +namespace Xalloc +{ + class Block + { + public: + void *Address = nullptr; + + int Checksum = Xalloc_BlockChecksum; + Xsize_t Size = 0; + Block *Next = nullptr; + Block *Last = nullptr; + bool IsFree = true; + + bool Check() + { + if (this->Checksum != Xalloc_BlockChecksum) + return false; + return true; + } + + Block(Xsize_t Size) + { + this->Address = Xalloc_REQUEST_PAGES(XStoP(Size + 1)); + this->Size = Size; + Xmemset(this->Address, 0, Size); + } + + ~Block() + { + Xalloc_FREE_PAGES(this->Address, XStoP(this->Size + 1)); + } + + /** + * @brief Overload new operator to allocate memory from the heap + * @param Size Unused + * @return void* Pointer to the allocated memory + */ + void *operator new(Xsize_t Size) + { + void *ptr = Xalloc_REQUEST_PAGES(XStoP(sizeof(Block))); + return ptr; + (void)(Size); + } + + /** + * @brief Overload delete operator to free memory from the heap + * @param Address Pointer to the memory to free + */ + void operator delete(void *Address) + { + Xalloc_FREE_PAGES(Address, XStoP(sizeof(Block))); + } + } __attribute__((packed, aligned((16)))); + + class SmartSMAPClass + { + private: + V1 *allocator = nullptr; + + public: + SmartSMAPClass(V1 *allocator) + { + this->allocator = allocator; + this->allocator->Xstac(); + } + ~SmartSMAPClass() { this->allocator->Xclac(); } + }; +#define SmartSMAP SmartSMAPClass XALLOC_CONCAT(SmartSMAP##_, __COUNTER__)(this) + + void V1::Xstac() + { + if (this->SMAPUsed) + { +#if defined(a86) + asm volatile("stac" :: + : "cc"); +#endif + } + } + + void V1::Xclac() + { + if (this->SMAPUsed) + { +#if defined(a86) + asm volatile("clac" :: + : "cc"); +#endif + } + } + + void V1::Arrange() + { + Xalloc_err("Arrange() is not implemented yet!"); + } + + void *V1::malloc(Xsize_t Size) + { + if (Size == 0) + { + Xalloc_warn("Attempted to allocate 0 bytes!"); + return nullptr; + } + + SmartSMAP; + Xalloc_lock; + + if (this->FirstBlock == nullptr) + { + this->FirstBlock = new Block(Size); + ((Block *)this->FirstBlock)->IsFree = false; + Xalloc_unlock; + return ((Block *)this->FirstBlock)->Address; + } + + Block *CurrentBlock = ((Block *)this->FirstBlock); + while (CurrentBlock != nullptr) + { + if (!CurrentBlock->Check()) + { + Xalloc_err("Block %#lx has an invalid checksum! (%#x != %#x)", + (Xuint64_t)CurrentBlock, CurrentBlock->Checksum, Xalloc_BlockChecksum); + while (Xalloc_StopOnFail) + ; + } + else if (CurrentBlock->IsFree && CurrentBlock->Size >= Size) + { + CurrentBlock->IsFree = false; + Xmemset(CurrentBlock->Address, 0, Size); + Xalloc_unlock; + return CurrentBlock->Address; + } + CurrentBlock = CurrentBlock->Next; + } + + CurrentBlock = ((Block *)this->FirstBlock); + while (CurrentBlock->Next != nullptr) + CurrentBlock = CurrentBlock->Next; + + CurrentBlock->Next = new Block(Size); + ((Block *)CurrentBlock->Next)->Last = CurrentBlock; + ((Block *)CurrentBlock->Next)->IsFree = false; + Xalloc_unlock; + return ((Block *)CurrentBlock->Next)->Address; + } + + void V1::free(void *Address) + { + if (Address == nullptr) + { + Xalloc_warn("Attempted to free a null pointer!"); + return; + } + + SmartSMAP; + Xalloc_lock; + + Block *CurrentBlock = ((Block *)this->FirstBlock); + while (CurrentBlock != nullptr) + { + if (!CurrentBlock->Check()) + { + Xalloc_err("Block %#lx has an invalid checksum! (%#x != %#x)", + (Xuint64_t)CurrentBlock, CurrentBlock->Checksum, Xalloc_BlockChecksum); + while (Xalloc_StopOnFail) + ; + } + else if (CurrentBlock->Address == Address) + { + if (CurrentBlock->IsFree) + { + Xalloc_warn("Attempted to free an already freed pointer!"); + Xalloc_unlock; + return; + } + + CurrentBlock->IsFree = true; + Xalloc_unlock; + return; + } + CurrentBlock = CurrentBlock->Next; + } + + Xalloc_err("Invalid address %#lx.", Address); + Xalloc_unlock; + } + + void *V1::calloc(Xsize_t NumberOfBlocks, Xsize_t Size) + { + if (NumberOfBlocks == 0 || Size == 0) + { + Xalloc_warn("The %s%s%s is 0!", + NumberOfBlocks == 0 ? "NumberOfBlocks" : "", + NumberOfBlocks == 0 && Size == 0 ? " and " : "", + Size == 0 ? "Size" : ""); + return nullptr; + } + + return this->malloc(NumberOfBlocks * Size); + } + + void *V1::realloc(void *Address, Xsize_t Size) + { + if (Address == nullptr) + return this->malloc(Size); + + if (Size == 0) + { + this->free(Address); + return nullptr; + } + + // SmartSMAP; + // Xalloc_lock; + // ... + // Xalloc_unlock; + + // TODO: Implement realloc + this->free(Address); + return this->malloc(Size); + } + + V1::V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled) + { + SmartSMAP; + Xalloc_lock; + this->SMAPUsed = SMAPEnabled; + this->UserMapping = UserMode; + this->BaseVirtualAddress = BaseVirtualAddress; + Xalloc_unlock; + } + + V1::~V1() + { + SmartSMAP; + Xalloc_lock; + Xalloc_trace("Destructor not implemented yet."); + Xalloc_unlock; + } +} diff --git a/Kernel/Core/Memory/Memory.cpp b/Kernel/Core/Memory/Memory.cpp new file mode 100644 index 00000000..4ad7c63b --- /dev/null +++ b/Kernel/Core/Memory/Memory.cpp @@ -0,0 +1,818 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include "HeapAllocators/Xalloc/Xalloc.hpp" +#include "../Library/liballoc_1_1.h" +#include "../../kernel.h" + +// #define DEBUG_ALLOCATIONS_SL 1 +// #define DEBUG_ALLOCATIONS 1 + +#ifdef DEBUG_ALLOCATIONS +#define memdbg(m, ...) \ + debug(m, ##__VA_ARGS__); \ + __sync +#else +#define memdbg(m, ...) +#endif + +#ifdef DEBUG_ALLOCATIONS_SL +NewLock(AllocatorLock); +NewLock(OperatorAllocatorLock); +#endif + +using namespace Memory; + +Physical KernelAllocator; +PageTable4 *KernelPageTable = nullptr; +bool Page1GBSupport = false; +bool PSESupport = false; + +static MemoryAllocatorType AllocatorType = MemoryAllocatorType::Pages; +Xalloc::V1 *XallocV1Allocator = nullptr; + +#ifdef DEBUG +NIF void tracepagetable(PageTable4 *pt) +{ + for (int i = 0; i < 512; i++) + { +#if defined(a64) + if (pt->Entries[i].Present) + debug("Entry %03d: %x %x %x %x %x %x %x %p-%#llx", i, + pt->Entries[i].Present, pt->Entries[i].ReadWrite, + pt->Entries[i].UserSupervisor, pt->Entries[i].WriteThrough, + pt->Entries[i].CacheDisable, pt->Entries[i].Accessed, + pt->Entries[i].ExecuteDisable, pt->Entries[i].Address << 12, + pt->Entries[i]); +#elif defined(a32) +#elif defined(aa64) +#endif + } +} +#endif + +NIF void MapFromZero(PageTable4 *PT, BootInfo *Info) +{ + debug("Mapping from 0x0 to %#llx", Info->Memory.Size); + Virtual va = Virtual(PT); + size_t MemSize = Info->Memory.Size; + + if (Page1GBSupport && PSESupport) + { + /* Map the first 100MB of memory as 4KB pages */ + + // uintptr_t Physical4KBSectionStart = 0x10000000; + // va.Map((void *)0, + // (void *)0, + // Physical4KBSectionStart, + // PTFlag::RW); + + // va.Map((void *)Physical4KBSectionStart, + // (void *)Physical4KBSectionStart, + // MemSize - Physical4KBSectionStart, + // PTFlag::RW, + // Virtual::MapType::OneGB); + + va.Map((void *)0, (void *)0, MemSize, PTFlag::RW); + } + else + va.Map((void *)0, (void *)0, MemSize, PTFlag::RW); + + va.Unmap((void *)0); +} + +NIF void MapFramebuffer(PageTable4 *PT, BootInfo *Info) +{ + debug("Mapping Framebuffer"); + Virtual va = Virtual(PT); + int itrfb = 0; + while (1) + { + if (!Info->Framebuffer[itrfb].BaseAddress) + break; + + va.OptimizedMap((void *)Info->Framebuffer[itrfb].BaseAddress, + (void *)Info->Framebuffer[itrfb].BaseAddress, + Info->Framebuffer[itrfb].Pitch * Info->Framebuffer[itrfb].Height, + PTFlag::RW | PTFlag::US | PTFlag::G); + itrfb++; + +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "Rsrv( %p %ld )\n\r", + Info->Framebuffer[itrfb].BaseAddress, + (Info->Framebuffer[itrfb].Pitch * Info->Framebuffer[itrfb].Height) + PAGE_SIZE); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + } +} + +NIF void MapKernel(PageTable4 *PT, BootInfo *Info) +{ + debug("Mapping Kernel"); + uintptr_t BootstrapStart = (uintptr_t)&_bootstrap_start; + uintptr_t KernelStart = (uintptr_t)&_kernel_start; + uintptr_t KernelTextEnd = (uintptr_t)&_kernel_text_end; + uintptr_t KernelDataEnd = (uintptr_t)&_kernel_data_end; + uintptr_t KernelRoDataEnd = (uintptr_t)&_kernel_rodata_end; + uintptr_t KernelEnd = (uintptr_t)&_kernel_end; + uintptr_t KernelFileStart = (uintptr_t)Info->Kernel.FileBase; + uintptr_t KernelFileEnd = KernelFileStart + Info->Kernel.Size; + + debug("Kernel physical address: %#lx - %#lx", Info->Kernel.PhysicalBase, (uintptr_t)Info->Kernel.PhysicalBase + Info->Kernel.Size); + debug("Kernel file base: %#lx - %#lx", KernelFileStart, KernelFileEnd); + + debug("File size: %ld KB", TO_KB(Info->Kernel.Size)); + debug(".text size: %ld KB", TO_KB(KernelTextEnd - KernelStart)); + debug(".data size: %ld KB", TO_KB(KernelDataEnd - KernelTextEnd)); + debug(".rodata size: %ld KB", TO_KB(KernelRoDataEnd - KernelDataEnd)); + debug(".bss size: %ld KB", TO_KB(KernelEnd - KernelRoDataEnd)); + + uintptr_t BaseKernelMapAddress = (uintptr_t)Info->Kernel.PhysicalBase; + uintptr_t k; + Virtual va = Virtual(PT); + + /* Bootstrap section */ + for (k = BootstrapStart; k < KernelStart - KERNEL_VMA_OFFSET; k += PAGE_SIZE) + { +#ifdef DEBUG /* vscode debugging */ + void *BKMA = (void *)BaseKernelMapAddress, *K_ = (void *)k; +#endif + va.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW | PTFlag::G); + KernelAllocator.ReservePage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + /* Text section */ + for (k = KernelStart; k < KernelTextEnd; k += PAGE_SIZE) + { +#ifdef DEBUG /* vscode debugging */ + void *BKMA = (void *)BaseKernelMapAddress, *K_ = (void *)k; +#endif + va.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW | PTFlag::G); + KernelAllocator.ReservePage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + /* Data section */ + for (k = KernelTextEnd; k < KernelDataEnd; k += PAGE_SIZE) + { +#ifdef DEBUG /* vscode debugging */ + void *BKMA = (void *)BaseKernelMapAddress, *K_ = (void *)k; +#endif + va.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW | PTFlag::G); + KernelAllocator.ReservePage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + /* Read only data section */ + for (k = KernelDataEnd; k < KernelRoDataEnd; k += PAGE_SIZE) + { +#ifdef DEBUG /* vscode debugging */ + void *BKMA = (void *)BaseKernelMapAddress, *K_ = (void *)k; +#endif + va.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::G); + KernelAllocator.ReservePage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + /* BSS section */ + for (k = KernelRoDataEnd; k < KernelEnd; k += PAGE_SIZE) + { +#ifdef DEBUG /* vscode debugging */ + void *BKMA = (void *)BaseKernelMapAddress, *K_ = (void *)k; +#endif + va.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW | PTFlag::G); + KernelAllocator.ReservePage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + debug("BaseKernelMapAddress: %#lx - %#lx", Info->Kernel.PhysicalBase, BaseKernelMapAddress); + + /* Kernel file */ + if (KernelFileStart != 0) + for (k = KernelFileStart; k < KernelFileEnd; k += PAGE_SIZE) + { + va.Map((void *)k, (void *)k, PTFlag::G); + KernelAllocator.ReservePage((void *)k); + } + +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "Rsrv( %p %ld )\n\r", + Info->Kernel.PhysicalBase, + Info->Kernel.Size); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + + sprintf(mExtTrkLog, "Rsrv( %p %ld )\n\r", + Info->Kernel.VirtualBase, + Info->Kernel.Size); + mExtTrkLock.Unlock(); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + } +#endif +} + +NIF void InitializeMemoryManagement(BootInfo *Info) +{ +#ifdef DEBUG + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + uintptr_t Base = r_cst(uintptr_t, Info->Memory.Entry[i].BaseAddress); + size_t Length = Info->Memory.Entry[i].Length; + uintptr_t End = Base + Length; + const char *Type = "Unknown"; + + switch (Info->Memory.Entry[i].Type) + { + case likely(Usable): + Type = "Usable"; + break; + case Reserved: + Type = "Reserved"; + break; + case ACPIReclaimable: + Type = "ACPI Reclaimable"; + break; + case ACPINVS: + Type = "ACPI NVS"; + break; + case BadMemory: + Type = "Bad Memory"; + break; + case BootloaderReclaimable: + Type = "Bootloader Reclaimable"; + break; + case KernelAndModules: + Type = "Kernel and Modules"; + break; + case Framebuffer: + Type = "Framebuffer"; + break; + default: + break; + } + + debug("%ld: %p-%p %s", + i, + Base, + End, + Type); + } +#endif + trace("Initializing Physical Memory Manager"); + // KernelAllocator = Physical(); <- Already called in the constructor + KernelAllocator.Init(Info); + debug("Memory Info: %lldMB / %lldMB (%lldMB reserved)", + TO_MB(KernelAllocator.GetUsedMemory()), + TO_MB(KernelAllocator.GetTotalMemory()), + TO_MB(KernelAllocator.GetReservedMemory())); + + /* -- Debugging -- + size_t bmap_size = KernelAllocator.GetPageBitmap().Size; + for (size_t i = 0; i < bmap_size; i++) + { + bool idx = KernelAllocator.GetPageBitmap().Get(i); + if (idx == true) + debug("Page %04d: %#lx", i, i * PAGE_SIZE); + } + + inf_loop debug("Alloc.: %#lx", KernelAllocator.RequestPage()); + */ + + trace("Initializing Virtual Memory Manager"); + KernelPageTable = (PageTable4 *)KernelAllocator.RequestPages(TO_PAGES(PAGE_SIZE + 1)); + debug("Page table allocated at %#lx", KernelPageTable); + memset(KernelPageTable, 0, PAGE_SIZE); + + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x80000001 cpuid; + cpuid.Get(); + PSESupport = cpuid.EDX.PSE; + Page1GBSupport = cpuid.EDX.Page1GB; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x80000001 cpuid; + cpuid.Get(); + fixme("Intel PSE support"); + } + + if (Page1GBSupport && PSESupport) + { + debug("1GB Page Support Enabled"); +#if defined(a64) + CPU::x64::CR4 cr4 = CPU::x64::readcr4(); + cr4.PSE = 1; + CPU::x64::writecr4(cr4); +#elif defined(a32) + CPU::x32::CR4 cr4 = CPU::x32::readcr4(); + cr4.PSE = 1; + CPU::x32::writecr4(cr4); +#elif defined(aa64) +#endif + } + + MapFromZero(KernelPageTable, Info); + MapFramebuffer(KernelPageTable, Info); + MapKernel(KernelPageTable, Info); + + trace("Applying new page table from address %#lx", KernelPageTable); +#ifdef DEBUG + tracepagetable(KernelPageTable); +#endif +#if defined(a86) + asmv("mov %0, %%cr3" ::"r"(KernelPageTable)); +#elif defined(aa64) + asmv("msr ttbr0_el1, %0" ::"r"(KernelPageTable)); +#endif + debug("Page table updated."); + if (strstr(Info->Kernel.CommandLine, "xallocv1")) + { + XallocV1Allocator = new Xalloc::V1((void *)KERNEL_HEAP_BASE, false, false); + AllocatorType = MemoryAllocatorType::XallocV1; + trace("XallocV1 Allocator initialized (%p)", XallocV1Allocator); + } + else if (strstr(Info->Kernel.CommandLine, "liballoc11")) + { + AllocatorType = MemoryAllocatorType::liballoc11; + } +} + +void *malloc(size_t Size) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(AllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("malloc(%d)->[%s]", Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + void *ret = nullptr; + switch (AllocatorType) + { + case MemoryAllocatorType::Pages: + { + ret = KernelAllocator.RequestPages(TO_PAGES(Size + 1)); + memset(ret, 0, Size); + break; + } + case MemoryAllocatorType::XallocV1: + { + ret = XallocV1Allocator->malloc(Size); + break; + } + case MemoryAllocatorType::liballoc11: + { + ret = PREFIX(malloc)(Size); + memset(ret, 0, Size); + break; + } + default: + throw; + } +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "malloc( %ld )=%p~%p\n\r", + Size, + ret, __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return ret; +} + +void *calloc(size_t n, size_t Size) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(AllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("calloc(%d, %d)->[%s]", n, Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + void *ret = nullptr; + switch (AllocatorType) + { + case MemoryAllocatorType::Pages: + { + ret = KernelAllocator.RequestPages(TO_PAGES(n * Size + 1)); + memset(ret, 0, n * Size); + break; + } + case MemoryAllocatorType::XallocV1: + { + ret = XallocV1Allocator->calloc(n, Size); + break; + } + case MemoryAllocatorType::liballoc11: + { + void *ret = PREFIX(calloc)(n, Size); + memset(ret, 0, Size); + return ret; + } + default: + throw; + } +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "calloc( %ld %ld )=%p~%p\n\r", + n, Size, + ret, __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return ret; +} + +void *realloc(void *Address, size_t Size) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(AllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("realloc(%#lx, %d)->[%s]", Address, Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + void *ret = nullptr; + switch (AllocatorType) + { + case unlikely(MemoryAllocatorType::Pages): + { + ret = KernelAllocator.RequestPages(TO_PAGES(Size + 1)); // WARNING: Potential memory leak + memset(ret, 0, Size); + break; + } + case MemoryAllocatorType::XallocV1: + { + ret = XallocV1Allocator->realloc(Address, Size); + break; + } + case MemoryAllocatorType::liballoc11: + { + void *ret = PREFIX(realloc)(Address, Size); + memset(ret, 0, Size); + return ret; + } + default: + throw; + } +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "realloc( %p %ld )=%p~%p\n\r", + Address, Size, + ret, __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return ret; +} + +void free(void *Address) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(AllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("free(%#lx)->[%s]", Address, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + switch (AllocatorType) + { + case unlikely(MemoryAllocatorType::Pages): + { + KernelAllocator.FreePage(Address); // WARNING: Potential memory leak + break; + } + case MemoryAllocatorType::XallocV1: + { + XallocV1Allocator->free(Address); + break; + } + case MemoryAllocatorType::liballoc11: + { + PREFIX(free) + (Address); + break; + } + default: + throw; + } +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "free( %p )~%p\n\r", + Address, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif +} + +void *operator new(size_t Size) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("new(%d)->[%s]", Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + void *ret = malloc(Size); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "new( %ld )=%p~%p\n\r", + Size, + ret, __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return ret; +} + +void *operator new[](size_t Size) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("new[](%d)->[%s]", Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + void *ret = malloc(Size); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "new[]( %ld )=%p~%p\n\r", + Size, + ret, __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return ret; +} + +void *operator new(unsigned long Size, std::align_val_t Alignment) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("new(%d, %d)->[%s]", Size, Alignment, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + fixme("operator new with alignment(%#lx) is not implemented", Alignment); + + void *ret = malloc(Size); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "new( %ld %#lx )=%p~%p\n\r", + Size, (uintptr_t)Alignment, + ret, __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return ret; +} + +void operator delete(void *Pointer) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("delete(%#lx)->[%s]", Pointer, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + free(Pointer); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "delete( %p )~%p\n\r", + Pointer, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif +} + +void operator delete[](void *Pointer) +{ +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("delete[](%#lx)->[%s]", Pointer, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + free(Pointer); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "delete[]( %p )~%p\n\r", + Pointer, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif +} + +void operator delete(void *Pointer, long unsigned int Size) +{ + UNUSED(Size); +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("delete(%#lx, %d)->[%s]", Pointer, Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + free(Pointer); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "delete( %p %ld )~%p\n\r", + Pointer, Size, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif +} + +void operator delete[](void *Pointer, long unsigned int Size) +{ + UNUSED(Size); +#ifdef DEBUG_ALLOCATIONS_SL + SmartLockClass lock___COUNTER__(OperatorAllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); +#endif + memdbg("delete[](%#lx, %d)->[%s]", Pointer, Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); + + free(Pointer); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "delete[]( %p %ld )~%p\n\r", + Pointer, Size, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif +} diff --git a/Kernel/Core/Memory/MemoryManager.cpp b/Kernel/Core/Memory/MemoryManager.cpp new file mode 100644 index 00000000..11d7ee8a --- /dev/null +++ b/Kernel/Core/Memory/MemoryManager.cpp @@ -0,0 +1,250 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include + +#include "../../kernel.h" + +namespace Memory +{ + ReadFSFunction(MEM_Read) + { + if (!Size) + Size = node->Length; + if (Offset > node->Length) + return 0; + if (Offset + Size > node->Length) + Size = node->Length - Offset; + memcpy(Buffer, (uint8_t *)(node->Address + Offset), Size); + return Size; + } + + WriteFSFunction(MEM_Write) + { + if (!Size) + Size = node->Length; + if (Offset > node->Length) + return 0; + if (Offset + Size > node->Length) + Size = node->Length - Offset; + memcpy((uint8_t *)(node->Address + Offset), Buffer, Size); + return Size; + } + + VirtualFileSystem::FileSystemOperations mem_op = { + .Name = "mem", + .Read = MEM_Read, + .Write = MEM_Write, + }; + + uint64_t MemMgr::GetAllocatedMemorySize() + { + uint64_t Size = 0; + foreach (auto ap in AllocatedPagesList) + Size += ap.PageCount; + return FROM_PAGES(Size); + } + + bool MemMgr::Add(void *Address, size_t Count) + { + if (Address == nullptr) + { + error("Address is null!"); + return false; + } + + if (Count == 0) + { + error("Count is 0!"); + return false; + } + + for (size_t i = 0; i < AllocatedPagesList.size(); i++) + { + if (AllocatedPagesList[i].Address == Address) + { + error("Address already exists!"); + return false; + } + else if ((uintptr_t)Address < (uintptr_t)AllocatedPagesList[i].Address) + { + if ((uintptr_t)Address + (Count * PAGE_SIZE) > (uintptr_t)AllocatedPagesList[i].Address) + { + error("Address intersects with an allocated page!"); + return false; + } + } + else + { + if ((uintptr_t)AllocatedPagesList[i].Address + (AllocatedPagesList[i].PageCount * PAGE_SIZE) > (uintptr_t)Address) + { + error("Address intersects with an allocated page!"); + return false; + } + } + } + + if (this->Directory) + { + char FileName[64]; + sprintf(FileName, "%lx-%ld", (uintptr_t)Address, Count); + VirtualFileSystem::Node *n = vfs->Create(FileName, VirtualFileSystem::NodeFlags::FILE, this->Directory); + if (n) + { + n->Address = (uintptr_t)Address; + n->Length = Count * PAGE_SIZE; + n->Operator = &mem_op; + } + } + + AllocatedPagesList.push_back({Address, Count}); + return true; + } + + void *MemMgr::RequestPages(size_t Count, bool User) + { + void *Address = KernelAllocator.RequestPages(Count); + for (size_t i = 0; i < Count; i++) + { + int Flags = Memory::PTFlag::RW; + if (User) + Flags |= Memory::PTFlag::US; + + Memory::Virtual(this->PageTable).Remap((void *)((uintptr_t)Address + (i * PAGE_SIZE)), (void *)((uint64_t)Address + (i * PAGE_SIZE)), Flags); + } + + if (this->Directory) + { + char FileName[64]; + sprintf(FileName, "%lx-%ld", (uintptr_t)Address, Count); + VirtualFileSystem::Node *n = vfs->Create(FileName, VirtualFileSystem::NodeFlags::FILE, this->Directory); + if (n) // If null, error or file already exists + { + n->Address = (uintptr_t)Address; + n->Length = Count * PAGE_SIZE; + n->Operator = &mem_op; + } + } + + AllocatedPagesList.push_back({Address, Count}); + + /* For security reasons, we clear the allocated page + if it's a user page. */ + if (User) + memset(Address, 0, Count * PAGE_SIZE); + + return Address; + } + + void MemMgr::FreePages(void *Address, size_t Count) + { + for (size_t i = 0; i < AllocatedPagesList.size(); i++) + { + if (AllocatedPagesList[i].Address == Address) + { + /** TODO: Advanced checks. Allow if the page count is less than the requested one. + * This will allow the user to free only a part of the allocated pages. + * + * But this will be in a separate function because we need to specify if we + * want to free from the start or from the end and return the new address. + */ + if (AllocatedPagesList[i].PageCount != Count) + { + error("Page count mismatch! (Allocated: %lld, Requested: %lld)", AllocatedPagesList[i].PageCount, Count); + return; + } + + KernelAllocator.FreePages(Address, Count); + + for (size_t i = 0; i < Count; i++) + { + Memory::Virtual(this->PageTable).Remap((void *)((uintptr_t)Address + (i * PAGE_SIZE)), (void *)((uint64_t)Address + (i * PAGE_SIZE)), Memory::PTFlag::RW); + // Memory::Virtual(this->PageTable).Unmap((void *)((uintptr_t)Address + (i * PAGE_SIZE))); + } + + if (this->Directory) + { + char FileName[64]; + sprintf(FileName, "%lx-%ld", (uintptr_t)Address, Count); + VirtualFileSystem::FileStatus s = vfs->Delete(FileName, false, this->Directory); + if (s != VirtualFileSystem::FileStatus::OK) + error("Failed to delete file %s", FileName); + } + + AllocatedPagesList.remove(i); + return; + } + } + } + + void MemMgr::DetachAddress(void *Address) + { + for (size_t i = 0; i < AllocatedPagesList.size(); i++) + { + if (AllocatedPagesList[i].Address == Address) + { + if (this->Directory) + { + char FileName[64]; + sprintf(FileName, "%lx-%ld", (uintptr_t)Address, AllocatedPagesList[i].PageCount); + VirtualFileSystem::FileStatus s = vfs->Delete(FileName, false, this->Directory); + if (s != VirtualFileSystem::FileStatus::OK) + error("Failed to delete file %s", FileName); + } + + AllocatedPagesList.remove(i); + return; + } + } + } + + MemMgr::MemMgr(PageTable4 *PageTable, VirtualFileSystem::Node *Directory) + { + if (PageTable) + this->PageTable = PageTable; + else + { +#if defined(a64) + this->PageTable = (PageTable4 *)CPU::x64::readcr3().raw; +#elif defined(a32) + this->PageTable = (PageTable4 *)CPU::x32::readcr3().raw; +#endif + } + + this->Directory = Directory; + debug("+ %#lx", this); + } + + MemMgr::~MemMgr() + { + foreach (auto ap in AllocatedPagesList) + { + KernelAllocator.FreePages(ap.Address, ap.PageCount); + for (size_t i = 0; i < ap.PageCount; i++) + Memory::Virtual(this->PageTable).Remap((void *)((uintptr_t)ap.Address + (i * PAGE_SIZE)), (void *)((uintptr_t)ap.Address + (i * PAGE_SIZE)), Memory::PTFlag::RW); + } + + if (this->Directory) + { + foreach (auto Child in this->Directory->Children) + vfs->Delete(Child, true); + } + + debug("- %#lx", this); + } +} diff --git a/Kernel/Core/Memory/PageMapIndexer.cpp b/Kernel/Core/Memory/PageMapIndexer.cpp new file mode 100644 index 00000000..d16212e3 --- /dev/null +++ b/Kernel/Core/Memory/PageMapIndexer.cpp @@ -0,0 +1,45 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +namespace Memory +{ + Virtual::PageMapIndexer::PageMapIndexer(uintptr_t VirtualAddress) + { +#if defined(a64) + uintptr_t Address = VirtualAddress; + Address >>= 12; + this->PTEIndex = Address & 0x1FF; + Address >>= 9; + this->PDEIndex = Address & 0x1FF; + Address >>= 9; + this->PDPTEIndex = Address & 0x1FF; + Address >>= 9; + this->PMLIndex = Address & 0x1FF; +#elif defined(a32) + uintptr_t Address = VirtualAddress; + Address >>= 12; + this->PTEIndex = Address & 0x3FF; + Address >>= 10; + this->PDEIndex = Address & 0x3FF; + Address >>= 10; + this->PDPTEIndex = Address & 0x3FF; +#elif defined(aa64) +#endif + } +} diff --git a/Kernel/Core/Memory/PhysicalMemoryManager.cpp b/Kernel/Core/Memory/PhysicalMemoryManager.cpp new file mode 100644 index 00000000..67e3280b --- /dev/null +++ b/Kernel/Core/Memory/PhysicalMemoryManager.cpp @@ -0,0 +1,541 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#ifdef DEBUG +#include +#endif + +#include "../../Architecture/amd64/acpi.hpp" + +#include "../../kernel.h" + +extern "C" char BootPageTable[]; // 0x10000 in length + +namespace Memory +{ + uint64_t Physical::GetTotalMemory() + { + SmartLock(this->MemoryLock); + return this->TotalMemory; + } + + uint64_t Physical::GetFreeMemory() + { + SmartLock(this->MemoryLock); + return this->FreeMemory; + } + + uint64_t Physical::GetReservedMemory() + { + SmartLock(this->MemoryLock); + return this->ReservedMemory; + } + + uint64_t Physical::GetUsedMemory() + { + SmartLock(this->MemoryLock); + return this->UsedMemory; + } + + bool Physical::SwapPage(void *Address) + { + fixme("%p", Address); + return false; + } + + bool Physical::SwapPages(void *Address, size_t PageCount) + { + for (size_t i = 0; i < PageCount; i++) + { + if (!this->SwapPage((void *)((uintptr_t)Address + (i * PAGE_SIZE)))) + return false; + } + return false; + } + + bool Physical::UnswapPage(void *Address) + { + fixme("%p", Address); + return false; + } + + bool Physical::UnswapPages(void *Address, size_t PageCount) + { + for (size_t i = 0; i < PageCount; i++) + { + if (!this->UnswapPage((void *)((uintptr_t)Address + (i * PAGE_SIZE)))) + return false; + } + return false; + } + + void *Physical::RequestPage() + { + SmartLock(this->MemoryLock); + + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + + this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE)); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "RequestPage( )=%p~%p\n\r", + (void *)(PageBitmapIndex * PAGE_SIZE), __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return (void *)(PageBitmapIndex * PAGE_SIZE); + } + + if (this->SwapPage((void *)(PageBitmapIndex * PAGE_SIZE))) + { + this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE)); + return (void *)(PageBitmapIndex * PAGE_SIZE); + } + + error("Out of memory! (Free: %ldMB; Used: %ldMB; Reserved: %ldMB)", TO_MB(FreeMemory), TO_MB(UsedMemory), TO_MB(ReservedMemory)); + CPU::Stop(); + __builtin_unreachable(); + } + + void *Physical::RequestPages(size_t Count) + { + SmartLock(this->MemoryLock); + + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + + for (uint64_t Index = PageBitmapIndex; Index < PageBitmap.Size * 8; Index++) + { + if (PageBitmap[Index] == true) + continue; + + for (size_t i = 0; i < Count; i++) + { + if (PageBitmap[Index + i] == true) + goto NextPage; + } + + this->LockPages((void *)(Index * PAGE_SIZE), Count); +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "RequestPages( %ld )=%p~%p\n\r", + Count, + (void *)(Index * PAGE_SIZE), __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + return (void *)(Index * PAGE_SIZE); + + NextPage: + Index += Count; + continue; + } + } + + if (this->SwapPages((void *)(PageBitmapIndex * PAGE_SIZE), Count)) + { + this->LockPages((void *)(PageBitmapIndex * PAGE_SIZE), Count); + return (void *)(PageBitmapIndex * PAGE_SIZE); + } + + error("Out of memory! (Free: %ldMB; Used: %ldMB; Reserved: %ldMB)", TO_MB(FreeMemory), TO_MB(UsedMemory), TO_MB(ReservedMemory)); + CPU::Halt(true); + __builtin_unreachable(); + } + + void Physical::FreePage(void *Address) + { + SmartLock(this->MemoryLock); + + if (unlikely(Address == nullptr)) + { + warn("Null pointer passed to FreePage."); + return; + } + + size_t Index = (size_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + { + warn("Tried to free an already free page. (%p)", Address); + return; + } + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + UsedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "FreePage( %p )~%p\n\r", + Address, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + } + + void Physical::FreePages(void *Address, size_t Count) + { + if (unlikely(Address == nullptr || Count == 0)) + { + warn("%s%s%s passed to FreePages.", Address == nullptr ? "Null pointer " : "", Address == nullptr && Count == 0 ? "and " : "", Count == 0 ? "Zero count" : ""); + return; + } +#ifdef DEBUG + if (EnableExternalMemoryTracer) + { + char LockTmpStr[64]; + strcpy_unsafe(LockTmpStr, __FUNCTION__); + strcat_unsafe(LockTmpStr, "_memTrk"); + mExtTrkLock.TimeoutLock(LockTmpStr, 10000); + sprintf(mExtTrkLog, "!FreePages( %p %ld )~%p\n\r", + Address, Count, + __builtin_return_address(0)); + UniversalAsynchronousReceiverTransmitter::UART mTrkUART = UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM3); + for (short i = 0; i < MEM_TRK_MAX_SIZE; i++) + { + if (mExtTrkLog[i] == '\r') + break; + mTrkUART.Write(mExtTrkLog[i]); + } + mExtTrkLock.Unlock(); + } +#endif + for (size_t t = 0; t < Count; t++) + this->FreePage((void *)((uintptr_t)Address + (t * PAGE_SIZE))); + } + + void Physical::LockPage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to lock null address."); + + uintptr_t Index = (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + UsedMemory += PAGE_SIZE; + } + } + + void Physical::LockPages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to lock %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t i = 0; i < PageCount; i++) + this->LockPage((void *)((uintptr_t)Address + (i * PAGE_SIZE))); + } + + void Physical::ReservePage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to reserve null address."); + + uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + + void Physical::ReservePages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to reserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t t = 0; t < PageCount; t++) + { + uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + } + + void Physical::UnreservePage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to unreserve null address."); + + uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + ReservedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + + void Physical::UnreservePages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to unreserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t t = 0; t < PageCount; t++) + { + uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + ReservedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + } + + void Physical::Init(BootInfo *Info) + { + SmartLock(this->MemoryLock); + + uint64_t MemorySize = Info->Memory.Size; + debug("Memory size: %lld bytes (%ld pages)", MemorySize, TO_PAGES(MemorySize)); + TotalMemory = MemorySize; + FreeMemory = MemorySize; + + size_t BitmapSize = (MemorySize / PAGE_SIZE) / 8 + 1; + void *LargestFreeMemorySegment = nullptr; + uint64_t LargestFreeMemorySegmentSize = 0; + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + if (Info->Memory.Entry[i].Type == Usable) + { + if (Info->Memory.Entry[i].Length > LargestFreeMemorySegmentSize) + { + /* We don't want to use 0 as a memory address. */ + if (Info->Memory.Entry[i].BaseAddress == 0x0) + { + debug("Ignoring memory segment at 0x0"); + continue; + } + + if (Info->Memory.Entry[i].Length > BitmapSize + 0x1000) + { + LargestFreeMemorySegment = (void *)Info->Memory.Entry[i].BaseAddress; + LargestFreeMemorySegmentSize = Info->Memory.Entry[i].Length; + +#define ROUND_UP(N, S) ((((N) + (S)-1) / (S)) * (S)) + if (LargestFreeMemorySegment >= Info->Kernel.PhysicalBase && + LargestFreeMemorySegment <= (void *)((uintptr_t)Info->Kernel.PhysicalBase + Info->Kernel.Size)) + { + debug("Kernel range: %#lx-%#lx", Info->Kernel.PhysicalBase, (void *)((uintptr_t)Info->Kernel.PhysicalBase + Info->Kernel.Size)); + + void *NewLargestFreeMemorySegment = (void *)((uintptr_t)Info->Kernel.PhysicalBase + Info->Kernel.Size); + void *RoundNewLargestFreeMemorySegment = (void *)ROUND_UP((uintptr_t)NewLargestFreeMemorySegment, PAGE_SIZE); + RoundNewLargestFreeMemorySegment = (void *)((uintptr_t)RoundNewLargestFreeMemorySegment + PAGE_SIZE); /* Leave a page between the kernel and the bitmap */ + + debug("Rounding %p to %p", NewLargestFreeMemorySegment, RoundNewLargestFreeMemorySegment); + info("Memory bitmap's memory segment is in the kernel, moving it to %p", RoundNewLargestFreeMemorySegment); + LargestFreeMemorySegmentSize = (uintptr_t)LargestFreeMemorySegmentSize - ((uintptr_t)RoundNewLargestFreeMemorySegment - (uintptr_t)LargestFreeMemorySegment); + LargestFreeMemorySegment = RoundNewLargestFreeMemorySegment; + } +#undef ROUND_UP + + if (LargestFreeMemorySegmentSize < BitmapSize + 0x1000) + { + trace("Largest free memory segment is too small (%lld bytes), skipping...", + LargestFreeMemorySegmentSize); + continue; + } + + debug("Found a memory segment of %lld bytes (%lldMB) at %llp (out segment is %lld bytes (%lldKB)))", + LargestFreeMemorySegmentSize, + TO_MB(LargestFreeMemorySegmentSize), + LargestFreeMemorySegment, + BitmapSize, + TO_KB(BitmapSize)); + break; + } + + // LargestFreeMemorySegment = (void *)Info->Memory.Entry[i].BaseAddress; + // LargestFreeMemorySegmentSize = Info->Memory.Entry[i].Length; + + // debug("Largest free memory segment: %llp (%lldMB)", + // (void *)Info->Memory.Entry[i].BaseAddress, + // TO_MB(Info->Memory.Entry[i].Length)); + } + } + } + + if (LargestFreeMemorySegment == nullptr) + { + error("No free memory found!"); + CPU::Stop(); + } + + /* TODO: Read swap config and make the configure the bitmap size correctly */ + debug("Initializing Bitmap at %llp-%llp (%lld Bytes)", + LargestFreeMemorySegment, + (void *)((uintptr_t)LargestFreeMemorySegment + BitmapSize), + BitmapSize); + + PageBitmap.Size = BitmapSize; + PageBitmap.Buffer = (uint8_t *)LargestFreeMemorySegment; + for (size_t i = 0; i < BitmapSize; i++) + *(uint8_t *)(PageBitmap.Buffer + i) = 0; + + debug("Reserving pages..."); + this->ReservePages(0, TO_PAGES(Info->Memory.Size)); + debug("Unreserving usable pages..."); + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + if (Info->Memory.Entry[i].Type == Usable && Info->Memory.Entry[i].BaseAddress != 0x0) + this->UnreservePages(Info->Memory.Entry[i].BaseAddress, TO_PAGES(Info->Memory.Entry[i].Length)); + } + + debug("Reserving pages for SMP..."); + this->ReservePage((void *)0x0); /* Trampoline stack, gdt, idt, etc... */ + this->ReservePages((void *)0x2000, 4); /* TRAMPOLINE_START */ + + debug("Reserving bitmap region %#lx-%#lx...", PageBitmap.Buffer, (void *)((uintptr_t)PageBitmap.Buffer + PageBitmap.Size)); + this->ReservePages(PageBitmap.Buffer, TO_PAGES(PageBitmap.Size)); + // debug("Reserving page table..."); + // this->ReservePages(BootPageTable, TO_PAGES(0x10000)); << in the bootstrap region + debug("Reserving kernel bootstrap region %#lx-%#lx...", &_bootstrap_start, &_bootstrap_end); + this->ReservePages(&_bootstrap_start, TO_PAGES((uintptr_t)&_bootstrap_end - (uintptr_t)&_bootstrap_start)); + void *KernelPhysicalStart = (void *)(((uintptr_t)&_kernel_start - KERNEL_VMA_OFFSET)); + void *KernelPhysicalEnd = (void *)(((uintptr_t)&_kernel_end - KERNEL_VMA_OFFSET)); + debug("Reserving kernel region %#lx-%#lx...", KernelPhysicalStart, KernelPhysicalEnd); + this->ReservePages((void *)KernelPhysicalStart, TO_PAGES((uintptr_t)&_kernel_end - (uintptr_t)&_kernel_start)); + + ACPI::ACPI::ACPIHeader *hdr = nullptr; + bool XSDT = false; + + if (Info->RSDP->Revision >= 2 && Info->RSDP->XSDTAddress) + { + hdr = (ACPI::ACPI::ACPIHeader *)(Info->RSDP->XSDTAddress); + XSDT = true; + } + else + { + hdr = (ACPI::ACPI::ACPIHeader *)(uintptr_t)Info->RSDP->RSDTAddress; + } + + debug("Reserving RSDT..."); + this->ReservePages((void*)Info->RSDP, TO_PAGES(sizeof(BootInfo::RSDPInfo))); + + debug("Reserving ACPI tables..."); + + uint64_t TableSize = ((hdr->Length - sizeof(ACPI::ACPI::ACPIHeader)) / (XSDT ? 8 : 4)); + debug("Table size: %lld", TableSize); + + for (uint64_t t = 0; t < TableSize; t++) + { + // TODO: Should I be concerned about unaligned memory access? + ACPI::ACPI::ACPIHeader *SDTHdr = nullptr; + if (XSDT) + SDTHdr = (ACPI::ACPI::ACPIHeader *)(*(uint64_t *)((uint64_t)hdr + sizeof(ACPI::ACPI::ACPIHeader) + (t * 8))); + else + SDTHdr = (ACPI::ACPI::ACPIHeader *)(*(uint32_t *)((uint64_t)hdr + sizeof(ACPI::ACPI::ACPIHeader) + (t * 4))); + + this->ReservePages(SDTHdr, TO_PAGES(SDTHdr->Length)); + } + + debug("Reserving kernel modules..."); + + for (uint64_t i = 0; i < MAX_MODULES; i++) + { + if (Info->Modules[i].Address == 0x0) + continue; + + debug("Reserving module %s (%#lx-%#lx)...", Info->Modules[i].CommandLine, + Info->Modules[i].Address, (void *)((uintptr_t)Info->Modules[i].Address + Info->Modules[i].Size)); + + this->ReservePages((void *)Info->Modules[i].Address, TO_PAGES(Info->Modules[i].Size)); + } + + } + + Physical::Physical() {} + Physical::~Physical() {} +} diff --git a/Kernel/Core/Memory/StackGuard.cpp b/Kernel/Core/Memory/StackGuard.cpp new file mode 100644 index 00000000..e9ac4024 --- /dev/null +++ b/Kernel/Core/Memory/StackGuard.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include + +namespace Memory +{ + StackGuard::StackGuard(bool User, PageTable4 *Table) + { + this->UserMode = User; + this->Table = Table; + + if (this->UserMode) + { + void *AllocatedStack = KernelAllocator.RequestPages(TO_PAGES(USER_STACK_SIZE + 1)); + memset(AllocatedStack, 0, USER_STACK_SIZE); + debug("AllocatedStack: %p", AllocatedStack); + + Virtual va = Virtual(Table); + for (size_t i = 0; i < TO_PAGES(USER_STACK_SIZE); i++) + { + va.Map((void *)(USER_STACK_BASE + (i * PAGE_SIZE)), + (void *)((uintptr_t)AllocatedStack + (i * PAGE_SIZE)), + PTFlag::RW | PTFlag::US); + + debug("Mapped %p to %p", (void *)(USER_STACK_BASE + (i * PAGE_SIZE)), + (void *)((uintptr_t)AllocatedStack + (i * PAGE_SIZE))); + } + + this->StackBottom = (void *)USER_STACK_BASE; + this->StackTop = (void *)(USER_STACK_BASE + USER_STACK_SIZE); + + this->StackPhyiscalBottom = AllocatedStack; + this->StackPhyiscalTop = (void *)((uintptr_t)AllocatedStack + USER_STACK_SIZE); + + this->Size = USER_STACK_SIZE; + } + else + { + this->StackBottom = KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1)); + memset(this->StackBottom, 0, STACK_SIZE); + debug("StackBottom: %p", this->StackBottom); + + this->StackTop = (void *)((uintptr_t)this->StackBottom + STACK_SIZE); + + this->StackPhyiscalBottom = this->StackBottom; + this->StackPhyiscalTop = this->StackTop; + + this->Size = STACK_SIZE; + } + + debug("Allocated stack at %p", this->StackBottom); + } + + StackGuard::~StackGuard() + { + fixme("Temporarily disabled stack guard deallocation"); + // KernelAllocator.FreePages(this->StackBottom, TO_PAGES(this->Size + 1)); + // debug("Freed stack at %p", this->StackBottom); + } + + bool StackGuard::Expand(uintptr_t FaultAddress) + { + if (this->UserMode) + { + if (FaultAddress < (uintptr_t)this->StackBottom - USER_STACK_SIZE || + FaultAddress > (uintptr_t)this->StackTop) + { + return false; /* It's not about the stack. */ + } + else + { + void *AllocatedStack = KernelAllocator.RequestPages(TO_PAGES(USER_STACK_SIZE + 1)); + debug("AllocatedStack: %p", AllocatedStack); + memset(AllocatedStack, 0, USER_STACK_SIZE); + Virtual va = Virtual(this->Table); + for (uintptr_t i = 0; i < TO_PAGES(USER_STACK_SIZE); i++) + { + va.Map((void *)((uintptr_t)this->StackBottom - (i * PAGE_SIZE)), (void *)((uintptr_t)AllocatedStack + (i * PAGE_SIZE)), PTFlag::RW | PTFlag::US); + debug("Mapped %p to %p", (void *)((uintptr_t)this->StackBottom - (i * PAGE_SIZE)), (void *)((uintptr_t)AllocatedStack + (i * PAGE_SIZE))); + } + this->StackBottom = (void *)((uintptr_t)this->StackBottom - USER_STACK_SIZE); + this->Size += USER_STACK_SIZE; + info("Stack expanded to %p", this->StackBottom); + return true; + } + } + else + { + fixme("Not implemented and probably not needed"); + return false; + } + } +} diff --git a/Kernel/Core/Memory/VirtualMemoryManager.cpp b/Kernel/Core/Memory/VirtualMemoryManager.cpp new file mode 100644 index 00000000..a758c71a --- /dev/null +++ b/Kernel/Core/Memory/VirtualMemoryManager.cpp @@ -0,0 +1,304 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +namespace Memory +{ + bool Virtual::Check(void *VirtualAddress, PTFlag Flag, MapType Type) + { + // 0x1000 aligned + uintptr_t Address = (uintptr_t)VirtualAddress; + Address &= 0xFFFFFFFFFFFFF000; + + PageMapIndexer Index = PageMapIndexer(Address); + PageMapLevel4 PML4 = this->Table->Entries[Index.PMLIndex]; + + PageDirectoryPointerTableEntryPtr *PDPTE = nullptr; + PageDirectoryEntryPtr *PDE = nullptr; + PageTableEntryPtr *PTE = nullptr; + + if ((PML4.raw & Flag) > 0) + { + PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + if (PDPTE) + { + if ((PDPTE->Entries[Index.PDPTEIndex].Present)) + { + if (Type == MapType::OneGB && PDPTE->Entries[Index.PDPTEIndex].PageSize) + return true; + + PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + if (PDE) + { + if (Type == MapType::TwoMB && PDE->Entries[Index.PDEIndex].PageSize) + return true; + + if ((PDE->Entries[Index.PDEIndex].Present)) + { + PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + if (PTE) + { + if ((PTE->Entries[Index.PTEIndex].Present)) + return true; + } + } + } + } + } + } + return false; + } + + void *Virtual::GetPhysical(void *VirtualAddress) + { + // 0x1000 aligned + uintptr_t Address = (uintptr_t)VirtualAddress; + Address &= 0xFFFFFFFFFFFFF000; + + PageMapIndexer Index = PageMapIndexer(Address); + PageMapLevel4 PML4 = this->Table->Entries[Index.PMLIndex]; + + PageDirectoryPointerTableEntryPtr *PDPTE = nullptr; + PageDirectoryEntryPtr *PDE = nullptr; + PageTableEntryPtr *PTE = nullptr; + + if (PML4.Present) + { + PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + if (PDPTE) + { + if (PDPTE->Entries[Index.PDPTEIndex].Present) + { + if (PDPTE->Entries[Index.PDPTEIndex].PageSize) + return (void *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + + PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + if (PDE) + { + if (PDE->Entries[Index.PDEIndex].Present) + { + if (PDE->Entries[Index.PDEIndex].PageSize) + return (void *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + + PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + if (PTE) + { + if (PTE->Entries[Index.PTEIndex].Present) + return (void *)((uintptr_t)PTE->Entries[Index.PTEIndex].GetAddress() << 12); + } + } + } + } + } + } + return nullptr; + } + + void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type) + { + SmartLock(this->MemoryLock); + if (unlikely(!this->Table)) + { + error("No page table"); + return; + } + + Flags |= PTFlag::P; + + PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress); + // Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only + uint64_t DirectoryFlags = Flags & 0x3F; + + PageMapLevel4 *PML4 = &this->Table->Entries[Index.PMLIndex]; + PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr; + if (!PML4->Present) + { + PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryPointerTableEntryPtr) + 1)); + memset(PDPTEPtr, 0, sizeof(PageDirectoryPointerTableEntryPtr)); + PML4->Present = true; + PML4->SetAddress((uintptr_t)PDPTEPtr >> 12); + } + else + PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12); + PML4->raw |= DirectoryFlags; + + PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex]; + if (Type == MapType::OneGB) + { + PDPTE->raw |= Flags; + PDPTE->PageSize = true; + PDPTE->SetAddress((uintptr_t)PhysicalAddress >> 12); + debug("Mapped 1GB page at %p to %p", VirtualAddress, PhysicalAddress); + return; + } + + PageDirectoryEntryPtr *PDEPtr = nullptr; + if (!PDPTE->Present) + { + PDEPtr = (PageDirectoryEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryEntryPtr) + 1)); + memset(PDEPtr, 0, sizeof(PageDirectoryEntryPtr)); + PDPTE->Present = true; + PDPTE->SetAddress((uintptr_t)PDEPtr >> 12); + } + else + PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12); + PDPTE->raw |= DirectoryFlags; + + PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex]; + if (Type == MapType::TwoMB) + { + PDE->raw |= Flags; + PDE->PageSize = true; + PDE->SetAddress((uintptr_t)PhysicalAddress >> 12); + debug("Mapped 2MB page at %p to %p", VirtualAddress, PhysicalAddress); + return; + } + + PageTableEntryPtr *PTEPtr = nullptr; + if (!PDE->Present) + { + PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1)); + memset(PTEPtr, 0, sizeof(PageTableEntryPtr)); + PDE->Present = true; + PDE->SetAddress((uintptr_t)PTEPtr >> 12); + } + else + PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12); + PDE->raw |= DirectoryFlags; + + PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex]; + PTE->Present = true; + PTE->raw |= Flags; + PTE->SetAddress((uintptr_t)PhysicalAddress >> 12); + +#if defined(a64) + CPU::x64::invlpg(VirtualAddress); +#elif defined(a32) + CPU::x32::invlpg(VirtualAddress); +#elif defined(aa64) + asmv("dsb sy"); + asmv("tlbi vae1is, %0" + : + : "r"(VirtualAddress) + : "memory"); + asmv("dsb sy"); + asmv("isb"); +#endif + +#ifdef DEBUG +/* https://stackoverflow.com/a/3208376/9352057 */ +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') + + if (!this->Check(VirtualAddress, (PTFlag)Flags, Type)) // quick workaround just to see where it fails + warn("Failed to map v:%#lx p:%#lx with flags: " BYTE_TO_BINARY_PATTERN, VirtualAddress, PhysicalAddress, BYTE_TO_BINARY(Flags)); +#endif + } + + void Virtual::Unmap(void *VirtualAddress, MapType Type) + { + SmartLock(this->MemoryLock); + if (!this->Table) + { + error("No page table"); + return; + } + + PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress); + PageMapLevel4 *PML4 = &this->Table->Entries[Index.PMLIndex]; + if (!PML4->Present) + { + error("Page %#lx not present", PML4->GetAddress()); + return; + } + + PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12); + PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex]; + if (!PDPTE->Present) + { + error("Page %#lx not present", PDPTE->GetAddress()); + return; + } + + if (Type == MapType::OneGB && PDPTE->PageSize) + { + PDPTE->Present = false; + return; + } + + PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Address << 12); + PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex]; + if (!PDE->Present) + { + error("Page %#lx not present", PDE->GetAddress()); + return; + } + + if (Type == MapType::TwoMB && PDE->PageSize) + { + PDE->Present = false; + return; + } + + PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE->Address << 12); + PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex]; + if (!PTE.Present) + { + error("Page %#lx not present", PTE.GetAddress()); + return; + } + + PTE.Present = false; + PTEPtr->Entries[Index.PTEIndex] = PTE; + +#if defined(a64) + CPU::x64::invlpg(VirtualAddress); +#elif defined(a32) + CPU::x32::invlpg(VirtualAddress); +#elif defined(aa64) + asmv("dsb sy"); + asmv("tlbi vae1is, %0" + : + : "r"(VirtualAddress) + : "memory"); + asmv("dsb sy"); + asmv("isb"); +#endif + } + + Virtual::Virtual(PageTable4 *Table) + { + if (Table) + this->Table = Table; + else + this->Table = (PageTable4 *)CPU::PageTable(); + } + + Virtual::~Virtual() {} +} diff --git a/Kernel/Core/PeripheralComponentInterconnect.cpp b/Kernel/Core/PeripheralComponentInterconnect.cpp new file mode 100644 index 00000000..1ea91fd5 --- /dev/null +++ b/Kernel/Core/PeripheralComponentInterconnect.cpp @@ -0,0 +1,886 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#if defined(a64) +#include "../Architecture/amd64/acpi.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../kernel.h" + +namespace PCI +{ + namespace Descriptors + { + const char *u8ToHexString(uint8_t Value) + { + static char Buffer[3]; + memset(Buffer, 0, 3); + for (size_t i = 0; i < 2; i++) + { + uint8_t Digit = (Value >> (4 - (i * 4))) & 0xF; + if (Digit < 10) + Buffer[i] = s_cst(char, '0' + Digit); + else + Buffer[i] = s_cst(char, 'A' + (Digit - 10)); + } + return Buffer; + } + + const char *u32ToHexString(uint32_t Value) + { + static char Buffer[9]; + memset(Buffer, 0, 9); + for (size_t i = 0; i < 8; i++) + { + uint8_t Digit = (Value >> (28 - (i * 4))) & 0xF; + if (Digit < 10) + Buffer[i] = s_cst(char, '0' + Digit); + else + Buffer[i] = s_cst(char, 'A' + (Digit - 10)); + } + return Buffer; + } + + const char *MassStorageControllerSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x00: + return "SCSI Bus Controller"; + case 0x01: + return "IDE Controller"; + case 0x02: + return "Floppy Disk Controller"; + case 0x03: + return "IPI Bus Controller"; + case 0x04: + return "RAID Controller"; + case 0x05: + return "ATA Controller"; + case 0x06: + return "Serial ATA"; + case 0x07: + return "Serial Attached SCSI Controller"; + case 0x08: + return "Non-Volatile Memory Controller"; + case 0x80: + return "Mass Storage Controller"; + default: + break; + } + fixme("Unknown mass storage controller %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *NetworkControllerSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x00: + return "Ethernet Controller"; + case 0x01: + return "Token Ring Controller"; + case 0x02: + return "FDDI Controller"; + case 0x03: + return "ATM Controller"; + case 0x04: + return "ISDN Controller"; + case 0x05: + return "WorldFip Controller"; + case 0x06: + return "PICMG HyperCard Controller"; + case 0x07: + return "Infiniband Controller"; + case 0x08: + return "Fabric Controller"; + case 0x80: + return "Network Controller"; + default: + break; + } + fixme("Unknown network controller %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *DisplayControllerSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x00: + return "VGA Compatible Controller"; + case 0x01: + return "XGA Controller"; + case 0x02: + return "3D Controller"; + case 0x80: + return "Display Controller"; + default: + break; + } + fixme("Unknown display controller %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *CommunicationControllerSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x00: + return "Serial Controller"; + case 0x01: + return "Parallel Controller"; + case 0x02: + return "Multi-Serial Controller"; + case 0x03: + return "IEEE-1284 Controller"; + case 0x04: + return "ATM Controller"; + case 0x05: + return "Object Storage Controller"; + case 0x80: + return "Communication controller"; + default: + break; + } + fixme("Unknown communication controller %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *BaseSystemPeripheralSubclassName(uint8_t SubclassCode) + { + // not sure if it's right + switch (SubclassCode) + { + case 0x00: + return "Unclassified"; + case 0x01: + return "Keyboard"; + case 0x02: + return "Pointing Device"; + case 0x03: + return "Mouse"; + case 0x04: + return "Scanner"; + case 0x05: + return "Gameport"; + case 0x80: + return "Unclassified"; + default: + break; + } + fixme("Unknown base system peripheral %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *SerialBusControllerSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x00: + return "FireWire (IEEE 1394) Controller"; + case 0x01: + return "ACCESS Bus Controller"; + case 0x02: + return "SSA Controller"; + case 0x03: + return "USB Controller"; + case 0x04: + return "Fibre Channel Controller"; + case 0x05: + return "SMBus Controller"; + case 0x06: + return "Infiniband Controller"; + case 0x07: + return "IPMI Interface Controller"; + case 0x08: + return "SERCOS Interface (IEC 61491) Controller"; + case 0x09: + return "CANbus Controller"; + case 0x80: + return "Serial Bus Controller"; + default: + break; + } + fixme("Unknown serial bus controller %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *BridgeDeviceSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x00: + return "Host Bridge"; + case 0x01: + return "ISA Bridge"; + case 0x02: + return "EISA Bridge"; + case 0x03: + return "MCA Bridge"; + case 0x04: + return "PCI-to-PCI Bridge"; + case 0x05: + return "PCMCIA Bridge"; + case 0x06: + return "NuBus Bridge"; + case 0x07: + return "CardBus Bridge"; + case 0x08: + return "RACEway Bridge"; + case 0x09: + return "PCI-to-PCI Bridge"; + case 0x0A: + return "InfiniBand-to-PCI Host Bridge"; + case 0x80: + return "Bridge Device"; + default: + break; + } + fixme("Unknown bridge device %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *WirelessControllerSubclassName(uint8_t SubclassCode) + { + switch (SubclassCode) + { + case 0x11: + return "Bluetooth"; + case 0x20: + return "802.1a controller"; + case 0x21: + return "802.1b controller"; + case 0x80: + return "Wireless controller"; + default: + break; + } + fixme("Unknown wireless controller %02x", SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *GetVendorName(uint32_t VendorID) + { + switch (VendorID) + { + case 0x1000: + return "Symbios Logic"; + case 0x1B36: + case 0x1AF4: + return "Red Hat, Inc."; + case 0x10EC: + return "Realtek Semiconductor Co., Ltd."; + case 0x80EE: + return "VirtualBox"; + case 0x1274: + return "Ensoniq"; + case 0x1234: + return "QEMU"; + case 0x15AD: + return "VMware"; + case 0x8086: + return "Intel Corporation"; + case 0x1022: + return "Advanced Micro Devices, Inc."; + case 0x10DE: + return "NVIDIA Corporation"; + case 0x1AE0: + return "Google, Inc."; + case 0x1a58: + return "Razer USA Ltd."; + case 0x1414: + return "Microsoft Corporation"; + default: + break; + } + fixme("Unknown vendor %04x", VendorID); + return u32ToHexString(VendorID); + } + + const char *GetDeviceName(uint32_t VendorID, uint32_t DeviceID) + { + switch (VendorID) + { + case SymbiosLogic: + { + switch (DeviceID) + { + case 0x30: + return "53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI"; + case 0x1000: + return "63C815"; + default: + break; + } + break; + } + case RedHat: + { + switch (DeviceID) + { + case 0x1000: + case 0x1041: + return "Virtio network device"; + case 0x1001: + case 0x1042: + return "Virtio block device"; + case 0x1002: + case 0x1045: + return "Virtio memory balloon"; + case 0x1003: + case 0x1043: + return "Virtio console"; + case 0x1004: + case 0x1048: + return "Virtio SCSI"; + case 0x1005: + case 0x1044: + return "Virtio RNG"; + case 0x1009: + case 0x1049: + case 0x105a: + return "Virtio filesystem"; + case 0x1050: + return "Virtio GPU"; + case 0x1052: + return "Virtio input"; + case 0x1053: + return "Virtio socket"; + case 1110: + return "Inter-VM shared memory"; + case 0x1af41100: + return "QEMU Virtual Machine"; + default: + break; + } + break; + } + case REDHat2: + { + switch (DeviceID) + { + case 0x0001: + return "QEMU PCI-PCI bridge"; + case 0x0002: + return "QEMU PCI 16550A Adapter"; + case 0x0003: + return "QEMU PCI Dual-port 16550A Adapter"; + case 0x0004: + return "QEMU PCI Quad-port 16550A Adapter"; + case 0x0005: + return "QEMU PCI Test Device"; + case 0x0006: + return "PCI Rocker Ethernet switch device"; + case 0x0007: + return "PCI SD Card Host Controller Interface"; + case 0x0008: + return "QEMU PCIe Host bridge"; + case 0x0009: + return "QEMU PCI Expander bridge"; + case 0x000A: + return "PCI-PCI bridge (multiseat)"; + case 0x000B: + return "QEMU PCIe Expander bridge"; + case 0x000C: + return "QEMU PCIe Root Port"; + case 0x000D: + return "QEMU XHCI Host Controller"; + case 0x0010: + return "QEMU NVM Express Controller"; + case 0x0100: + return "QXL Paravirtual Graphic Card"; + case 0x1AF41100: + return "QEMU Virtual Machine"; + default: + break; + } + break; + } + case Realtek: + { + switch (DeviceID) + { + case 0x8029: + return "RTL-8029(AS)"; + case 0x8139: + return "RTL-8139/8139C/8139C+ Ethernet Controller"; + default: + break; + } + break; + } + case VirtualBox: + { + switch (DeviceID) + { + case 0xCAFE: + return "VirtualBox Guest Service"; + case 0xBEEF: + return "VirtualBox Graphics Adapter"; + case 0x0021: + return "USB Tablet"; + case 0x0022: + return "Multitouch tablet"; + case 0x4E56: + return "NVM Express"; + default: + break; + } + break; + } + case Ensoniq: + { + switch (DeviceID) + { + case 0x1371: + return "ES1371/ES1373 / Creative Labs CT2518"; + case 0x5000: + return "ES1370 [AudioPCI]"; + default: + break; + } + break; + } + case QEMU: + { + switch (DeviceID) + { + case 0x1111: + return "QEMU Display"; + default: + break; + } + break; + } + case VMware: + { + switch (DeviceID) + { + case 0x0740: + return "Virtual Machine Communication Interface"; + case 0x0405: + return "SVGA II Adapter"; + case 0x0790: + return "PCI bridge"; + case 0x07A0: + return "PCI Express Root Port"; + case 0x0774: + return "USB1.1 UHCI Controller"; + case 0x0770: + return "USB2 EHCI Controller"; + case 0x0779: + return "USB3 xHCI 1.0 Controller"; + case 0x07E0: + return "SATA AHCI controller"; + case 0x07F0: + return "NVM Express"; + default: + break; + } + break; + } + case IntelCorporation: + { + switch (DeviceID) + { + case 0x1229: + return "82557/8/9/0/1 Ethernet Pro 100"; + case 0x1209: + return "8255xER/82551IT Fast Ethernet Controller"; + case 0x100E: + return "82540EM Gigabit Ethernet Controller"; + case 0x7190: + return "440BX/ZX/DX - 82443BX/ZX/DX Host bridge"; + case 0x7191: + return "440BX/ZX/DX - 82443BX/ZX/DX AGP bridge"; + case 0x7110: + return "82371AB/EB/MB PIIX4 ISA"; + case 0x7111: + return "82371AB/EB/MB PIIX4 IDE"; + case 0x7113: + return "82371AB/EB/MB PIIX4 ACPI"; + case 0x1e31: + return "7 Series/C210 Series Chipset Family USB xHCI Host Controller"; + case 0x100F: + return "82545EM Gigabit Ethernet Controller (Copper)"; + case 0x1371: + return "ES1371/ES1373 / Creative Labs CT2518"; + case 0x27b9: + return "82801GBM (ICH7-M) LPC Interface Bridge"; + case 0x07E0: + return "SATA AHCI controller"; + case 0x293E: + return "82801I (ICH9 Family) HD Audio Controller"; + case 0x2935: + return "82801I (ICH9 Family) USB UHCI Controller #2"; + case 0x2936: + return "82801I (ICH9 Family) USB UHCI Controller #3"; + case 0x293A: + return "82801I (ICH9 Family) USB2 EHCI Controller #1"; + case 0x2934: + return "82801I (ICH9 Family) USB UHCI Controller #1"; + case 0x2668: + return "82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller"; + case 0x2415: + return "82801AA AC'97 Audio Controller"; + case 0x10D3: + return "82574L Gigabit Network Connection"; + case 0x29C0: + return "82G33/G31/P35/P31 Express DRAM Controller"; + case 0x2918: + return "82801IB (ICH9) LPC Interface Controller"; + case 0x2829: + return "82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode]"; + case 0x2922: + return "82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode]"; + case 0x2930: + return "82801I (ICH9 Family) SMBus Controller"; + default: + break; + } + break; + } + case AdvancedMicroDevices: + { + switch (DeviceID) + { + case 0x2000: + return "79C970 [PCnet32 LANCE]"; + default: + break; + } + break; + } + default: + break; + } + fixme("Unknown device %04x:%04x", VendorID, DeviceID); + return u32ToHexString(DeviceID); + } + + const char *GetSubclassName(uint8_t ClassCode, uint8_t SubclassCode) + { + switch (ClassCode) + { + case 0x00: + return "Unclassified"; + case 0x01: + return MassStorageControllerSubclassName(SubclassCode); + case 0x02: + return NetworkControllerSubclassName(SubclassCode); + case 0x03: + return DisplayControllerSubclassName(SubclassCode); + case 0x04: + return "Multimedia controller"; + case 0x05: + return "Memory Controller"; + case 0x06: + return BridgeDeviceSubclassName(SubclassCode); + case 0x07: + return CommunicationControllerSubclassName(SubclassCode); + case 0x08: + return BaseSystemPeripheralSubclassName(SubclassCode); + case 0x09: + return "Input device controller"; + case 0x0A: + return "Docking station"; + case 0x0B: + return "Processor"; + case 0x0C: + return SerialBusControllerSubclassName(SubclassCode); + case 0x0D: + return WirelessControllerSubclassName(SubclassCode); + case 0x0E: + return "Intelligent controller"; + case 0x0F: + return "Satellite communication controller"; + case 0x10: + return "Encryption controller"; + case 0x11: + return "Signal processing accelerators"; + case 0x12: + return "Processing accelerators"; + case 0x13: + return "Non-Essential Instrumentation"; + case 0x40: + return "Coprocessor"; + default: + break; + } + fixme("Unknown subclass name %02x:%02x", ClassCode, SubclassCode); + return u8ToHexString(SubclassCode); + } + + const char *GetProgIFName(uint8_t ClassCode, uint8_t SubclassCode, uint8_t ProgIF) + { + switch (ClassCode) + { + case 0x01: + { + switch (SubclassCode) + { + case 0x06: + { + switch (ProgIF) + { + case 0: + return "Vendor Specific SATA Controller"; + case 1: + return "AHCI SATA Controller"; + case 2: + return "Serial Storage Bus SATA Controller"; + default: + return "SATA controller"; + } + break; + } + case 0x08: + { + switch (ProgIF) + { + case 0x01: + return "NVMHCI Controller"; + case 0x02: + return "NVM Express Controller"; + default: + return "Non-Volatile Memory Controller"; + } + break; + } + default: + break; + } + default: + break; + } + case 0x03: + { + switch (SubclassCode) + { + case 0x00: + switch (ProgIF) + { + case 0x00: + return "VGA Controller"; + case 0x01: + return "8514-Compatible Controller"; + default: + return "VGA Compatible Controller"; + } + break; + default: + break; + } + break; + } + case 0x07: + { + switch (SubclassCode) + { + case 0x00: + { + switch (ProgIF) + { + case 0x00: + return "Serial controller <8250>"; + case 0x01: + return "Serial controller <16450>"; + case 0x02: + return "Serial controller <16550>"; + case 0x03: + return "Serial controller <16650>"; + case 0x04: + return "Serial controller <16750>"; + case 0x05: + return "Serial controller <16850>"; + case 0x06: + return "Serial controller <16950"; + default: + return "Serial controller"; + } + break; + } + default: + break; + } + break; + } + case 0x0C: + { + switch (SubclassCode) + { + case 0x00: + { + switch (ProgIF) + { + case 0x00: + return "Generic FireWire (IEEE 1394) Controller"; + case 0x10: + return "OHCI FireWire (IEEE 1394) Controller"; + default: + break; + } + break; + } + case 0x03: + { + switch (ProgIF) + { + case 0x00: + return "UHCI (USB1) Controller"; + case 0x10: + return "OHCI (USB1) Controller"; + case 0x20: + return "EHCI (USB2) Controller"; + case 0x30: + return "XHCI (USB3) Controller"; + case 0x80: + return "Unspecified"; + case 0xFE: + return "USB Device"; + default: + break; + } + break; + } + default: + break; + } + break; + } + } + // not really a fixme + // fixme("Unknown prog IF name %02x:%02x:%02x", ClassCode, SubclassCode, ProgIF); + return u8ToHexString(ProgIF); + } + } + +#ifdef DEBUG + void e(PCIDeviceHeader *hdr) + { + debug("%#x:%#x\t\t%s / %s / %s / %s / %s", + hdr->VendorID, hdr->DeviceID, + Descriptors::GetVendorName(hdr->VendorID), + Descriptors::GetDeviceName(hdr->VendorID, hdr->DeviceID), + Descriptors::DeviceClasses[hdr->Class], + Descriptors::GetSubclassName(hdr->Class, hdr->Subclass), + Descriptors::GetProgIFName(hdr->Class, hdr->Subclass, hdr->ProgIF)); + } +#endif + + void PCI::EnumerateFunction(uintptr_t DeviceAddress, uint64_t Function) + { + uintptr_t Offset = Function << 12; + uintptr_t FunctionAddress = DeviceAddress + Offset; + Memory::Virtual(KernelPageTable).Map((void *)FunctionAddress, (void *)FunctionAddress, Memory::PTFlag::RW); + PCIDeviceHeader *PCIDeviceHdr = (PCIDeviceHeader *)FunctionAddress; + if (PCIDeviceHdr->DeviceID == 0) + return; + if (PCIDeviceHdr->DeviceID == 0xFFFF) + return; + Devices.push_back(PCIDeviceHdr); +#ifdef DEBUG + e(PCIDeviceHdr); +#endif + } + + void PCI::EnumerateDevice(uintptr_t BusAddress, uint64_t Device) + { + uintptr_t Offset = Device << 15; + uintptr_t DeviceAddress = BusAddress + Offset; + Memory::Virtual(KernelPageTable).Map((void *)DeviceAddress, (void *)DeviceAddress, Memory::PTFlag::RW); + PCIDeviceHeader *PCIDeviceHdr = (PCIDeviceHeader *)DeviceAddress; + if (PCIDeviceHdr->DeviceID == 0) + return; + if (PCIDeviceHdr->DeviceID == 0xFFFF) + return; + for (uintptr_t Function = 0; Function < 8; Function++) + EnumerateFunction(DeviceAddress, Function); + } + + void PCI::EnumerateBus(uintptr_t BaseAddress, uint64_t Bus) + { + uintptr_t Offset = Bus << 20; + uintptr_t BusAddress = BaseAddress + Offset; + Memory::Virtual(KernelPageTable).Map((void *)BusAddress, (void *)BusAddress, Memory::PTFlag::RW); + PCIDeviceHeader *PCIDeviceHdr = (PCIDeviceHeader *)BusAddress; + if (Bus != 0) // TODO: VirtualBox workaround (UNTESTED ON REAL HARDWARE!) + { + if (PCIDeviceHdr->DeviceID == 0) + return; + if (PCIDeviceHdr->DeviceID == 0xFFFF) + return; + } + debug("PCI Bus DeviceID:%#llx VendorID:%#llx BIST:%#llx Cache:%#llx Class:%#llx Cmd:%#llx HdrType:%#llx LatencyTimer:%#llx ProgIF:%#llx RevID:%#llx Status:%#llx SubClass:%#llx ", + PCIDeviceHdr->DeviceID, PCIDeviceHdr->VendorID, PCIDeviceHdr->BIST, + PCIDeviceHdr->CacheLineSize, PCIDeviceHdr->Class, PCIDeviceHdr->Command, + PCIDeviceHdr->HeaderType, PCIDeviceHdr->LatencyTimer, PCIDeviceHdr->ProgIF, + PCIDeviceHdr->RevisionID, PCIDeviceHdr->Status, PCIDeviceHdr->Subclass); + for (uintptr_t Device = 0; Device < 32; Device++) + EnumerateDevice(BusAddress, Device); + } + + std::vector PCI::FindPCIDevice(uint8_t Class, uint8_t Subclass, uint8_t ProgIF) + { + std::vector DeviceFound; + for (auto var : Devices) + if (var->Class == Class && var->Subclass == Subclass && var->ProgIF == ProgIF) + DeviceFound.push_back(var); + return DeviceFound; + } + + std::vector PCI::FindPCIDevice(int VendorID, int DeviceID) + { + std::vector DeviceFound; + for (auto var : Devices) + if (var->VendorID == VendorID && var->DeviceID == DeviceID) + DeviceFound.push_back(var); + return DeviceFound; + } + + PCI::PCI() + { +#if defined(a64) + int Entries = s_cst(int, ((((ACPI::ACPI *)PowerManager->GetACPI())->MCFG->Header.Length) - sizeof(ACPI::ACPI::MCFGHeader)) / sizeof(DeviceConfig)); + Memory::Virtual vma = Memory::Virtual(KernelPageTable); + for (int t = 0; t < Entries; t++) + { + DeviceConfig *NewDeviceConfig = (DeviceConfig *)((uintptr_t)((ACPI::ACPI *)PowerManager->GetACPI())->MCFG + sizeof(ACPI::ACPI::MCFGHeader) + (sizeof(DeviceConfig) * t)); + vma.Map((void *)NewDeviceConfig->BaseAddress, (void *)NewDeviceConfig->BaseAddress, Memory::PTFlag::RW); + debug("PCI Entry %d Address:%#llx BUS:%#llx-%#llx", t, NewDeviceConfig->BaseAddress, + NewDeviceConfig->StartBus, NewDeviceConfig->EndBus); + for (uintptr_t Bus = NewDeviceConfig->StartBus; Bus < NewDeviceConfig->EndBus; Bus++) + EnumerateBus(NewDeviceConfig->BaseAddress, Bus); + } +#elif defined(a32) + error("PCI not implemented on i386"); +#elif defined(aa64) + error("PCI not implemented on aarch64"); +#endif + } + + PCI::~PCI() + { + } +} diff --git a/Kernel/Core/Power.cpp b/Kernel/Core/Power.cpp new file mode 100644 index 00000000..c1cb86cc --- /dev/null +++ b/Kernel/Core/Power.cpp @@ -0,0 +1,139 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +#include "../kernel.h" + +#if defined(a64) +#include + +#include "../Architecture/amd64/acpi.hpp" + +namespace Power +{ + void Power::Reboot() + { + if (((ACPI::ACPI *)this->acpi)->FADT) + if (((ACPI::DSDT *)this->dsdt)->ACPIShutdownSupported) + ((ACPI::DSDT *)this->dsdt)->Reboot(); + + uint8_t val = 0x02; + while (val & 0x02) + val = inb(0x64); + outb(0x64, 0xFE); + + warn("Executing the second attempt to reboot..."); + + // second attempt to reboot + // https://wiki.osdev.org/Reboot + uint8_t temp; + asmv("cli"); + do + { + temp = inb(0x64); + if (((temp) & (1 << (0))) != 0) + inb(0x60); + } while (((temp) & (1 << (1))) != 0); + outb(0x64, 0xFE); + + CPU::Stop(); + } + + void Power::Shutdown() + { + if (((ACPI::ACPI *)this->acpi)->FADT) + if (((ACPI::DSDT *)this->dsdt)->ACPIShutdownSupported) + ((ACPI::DSDT *)this->dsdt)->Shutdown(); + + outl(0xB004, 0x2000); // for qemu + outl(0x604, 0x2000); // if qemu not working, bochs and older versions of qemu + outl(0x4004, 0x3400); // virtual box + CPU::Stop(); + } + + void Power::InitDSDT() + { + if (((ACPI::ACPI *)this->acpi)->FADT) + this->dsdt = new ACPI::DSDT((ACPI::ACPI *)acpi); + } + + Power::Power() + { + this->acpi = new ACPI::ACPI; + this->madt = new ACPI::MADT(((ACPI::ACPI *)acpi)->MADT); + trace("Power manager initialized"); + } + + Power::~Power() + { + debug("Destructor called"); + } +} + +#elif defined(a32) + +namespace Power +{ + void Power::Reboot() + { + warn("Reboot not implemented for i386"); + } + + void Power::Shutdown() + { + warn("Shutdown not implemented for i386"); + } + + Power::Power() + { + error("Power not implemented for i386"); + } + + Power::~Power() + { + } +} + +#elif defined(aa64) + +namespace Power +{ + void Power::Reboot() + { + warn("Reboot not implemented for aarch64"); + } + + void Power::Shutdown() + { + warn("Shutdown not implemented for aarch64"); + } + + Power::Power() + { + error("Power not implemented for aarch64"); + } + + Power::~Power() + { + } +} + +#endif diff --git a/Kernel/Core/README.md b/Kernel/Core/README.md new file mode 100644 index 00000000..a2ef85f5 --- /dev/null +++ b/Kernel/Core/README.md @@ -0,0 +1,22 @@ +# Core components + +This directory contains the core components of the project. These components are used by the kernel to provide the basic functionality of the operating system. + +--- + +## 💾 Memory + +Contains the memory management code. +It is responsible for allocating and freeing memory. +It also provides the `kmalloc`, `kcalloc`, `krealloc` and `kfree` functions that are used by the rest of the kernel. + +## 📺 Video + +Contains the video management code. +It is responsible for printing text to the screen. + +## 🖥 CPU + +Contains the CPU management code. +It is responsible for initializing the GDT and IDT. +More code related is in the `Architecture` directory. diff --git a/Kernel/Core/Random.cpp b/Kernel/Core/Random.cpp new file mode 100644 index 00000000..10c160e9 --- /dev/null +++ b/Kernel/Core/Random.cpp @@ -0,0 +1,143 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include + +namespace Random +{ + static uint64_t Seed = 0xdeadbeef; + + uint16_t rand16() + { +#if defined(a86) + static int RDRANDFlag = 0x1A1A; + if (unlikely(RDRANDFlag == 0x1A1A)) + { + if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0) + { + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid; + cpuid.Get(); + RDRANDFlag = cpuid.ECX.RDRAND; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid; + cpuid.Get(); + RDRANDFlag = cpuid.ECX.RDRAND; + } + } + else + RDRANDFlag = 0; + } + + if (RDRANDFlag) + { + uint16_t RDRANDValue = 0; + asmv("1: rdrand %0; jnc 1b" + : "=r"(RDRANDValue)); + return RDRANDValue; + } + + Seed = Seed * 1103515245 + 12345; + return (uint16_t)(Seed / 65536) % __UINT16_MAX__; +#endif + return 0; + } + + uint32_t rand32() + { +#if defined(a86) + static int RDRANDFlag = 0x1A1A; + if (unlikely(RDRANDFlag == 0x1A1A)) + { + if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0) + { + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid; + cpuid.Get(); + RDRANDFlag = cpuid.ECX.RDRAND; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid; + cpuid.Get(); + RDRANDFlag = cpuid.ECX.RDRAND; + } + } + else + RDRANDFlag = 0; + } + + if (RDRANDFlag) + { + uint32_t RDRANDValue = 0; + asmv("1: rdrand %0; jnc 1b" + : "=r"(RDRANDValue)); + return RDRANDValue; + } + + Seed = Seed * 1103515245 + 12345; + return (uint32_t)(Seed / 65536) % __UINT32_MAX__; +#endif + return 0; + } + + uint64_t rand64() + { +#if defined(a86) + static int RDRANDFlag = 0x1A1A; + if (unlikely(RDRANDFlag == 0x1A1A)) + { + if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0) + { + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x00000001 cpuid; + cpuid.Get(); + RDRANDFlag = cpuid.ECX.RDRAND; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + CPU::x86::Intel::CPUID0x00000001 cpuid; + cpuid.Get(); + RDRANDFlag = cpuid.ECX.RDRAND; + } + } + else + RDRANDFlag = 0; + } + + if (RDRANDFlag) + { + uint64_t RDRANDValue = 0; + asmv("1: rdrand %0; jnc 1b" + : "=r"(RDRANDValue)); + return RDRANDValue; + } + + Seed = Seed * 1103515245 + 12345; + return (uint64_t)(Seed / 65536) % __UINT64_MAX__; +#endif + return 0; + } + + void ChangeSeed(uint64_t CustomSeed) { Seed = CustomSeed; } +} diff --git a/Kernel/Core/StackGuard.cpp b/Kernel/Core/StackGuard.cpp new file mode 100644 index 00000000..2a139f94 --- /dev/null +++ b/Kernel/Core/StackGuard.cpp @@ -0,0 +1,96 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include +#include + +#include "../kernel.h" + +/* EXTERNC */ __weak uintptr_t __stack_chk_guard = 0; + +EXTERNC __weak __no_stack_protector uintptr_t __stack_chk_guard_init(void) +{ + int MaxRetries = 0; +#if UINTPTR_MAX == UINT32_MAX + uint32_t num; +Retry: + num = Random::rand32(); + if (num < 0x1000 && MaxRetries++ < 10) + goto Retry; + return num; + +#else + uint64_t num; +Retry: + num = Random::rand64(); + if (num < 0x100000 && MaxRetries++ < 10) + goto Retry; + return num; +#endif +} + +EXTERNC __constructor __no_stack_protector void __guard_setup(void) +{ + debug("StackGuard: __guard_setup"); + if (__stack_chk_guard == 0) + __stack_chk_guard = __stack_chk_guard_init(); + debug("Stack guard value: %ld", __stack_chk_guard); +} + +EXTERNC __weak __noreturn __no_stack_protector void __stack_chk_fail(void) +{ + TaskingPanic(); + for (short i = 0; i < 10; i++) + error("Stack smashing detected!"); + debug("Current stack check guard value: %#lx", __stack_chk_guard); + KPrint("\eFF0000Stack smashing detected!"); + +#if defined(a86) + void *Stack = nullptr; +#if defined(a64) + asmv("movq %%rsp, %0" + : "=r"(Stack)); +#elif defined(a32) + asmv("movl %%esp, %0" + : "=r"(Stack)); +#endif + error("Stack address: %#lx", Stack); + + while (1) + asmv("cli; hlt"); +#elif defined(aa64) + asmv("wfe"); +#endif + CPU::Stop(); +} + +// https://github.com/gcc-mirror/gcc/blob/master/libssp/ssp.c +EXTERNC __weak __noreturn __no_stack_protector void __chk_fail(void) +{ + TaskingPanic(); + for (short i = 0; i < 10; i++) + error("Buffer overflow detected!"); + KPrint("\eFF0000Buffer overflow detected!"); + +#if defined(a86) + while (1) + asmv("cli; hlt"); +#elif defined(aa64) + asmv("wfe"); +#endif +} diff --git a/Kernel/Core/Symbols.cpp b/Kernel/Core/Symbols.cpp new file mode 100644 index 00000000..6d362b9d --- /dev/null +++ b/Kernel/Core/Symbols.cpp @@ -0,0 +1,142 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include +#include +#include +#include + +// #pragma GCC diagnostic ignored "-Wignored-qualifiers" + +namespace SymbolResolver +{ + Symbols::Symbols(uintptr_t ImageAddress) + { + debug("Solving symbols for address: %#llx", ImageAddress); + + if (ImageAddress == 0) + { + error("Invalid image address"); + return; + } + + Elf64_Ehdr *Header = (Elf64_Ehdr *)ImageAddress; + if (Header->e_ident[0] != 0x7F && + Header->e_ident[1] != 'E' && + Header->e_ident[2] != 'L' && + Header->e_ident[3] != 'F') + { + error("Invalid ELF header"); + return; + } + Elf64_Shdr *ElfSections = (Elf64_Shdr *)(ImageAddress + Header->e_shoff); + Elf64_Sym *ElfSymbols = nullptr; + char *strtab = nullptr; + + for (uint16_t i = 0; i < Header->e_shnum; i++) + switch (ElfSections[i].sh_type) + { + case SHT_SYMTAB: + ElfSymbols = (Elf64_Sym *)(ImageAddress + ElfSections[i].sh_offset); + this->TotalEntries = ElfSections[i].sh_size / sizeof(Elf64_Sym); + if (this->TotalEntries >= 0x10000) + this->TotalEntries = 0x10000 - 1; + + debug("Symbol table found, %d entries", this->TotalEntries); + break; + case SHT_STRTAB: + if (Header->e_shstrndx == i) + { + debug("String table found, %d entries", ElfSections[i].sh_size); + } + else + { + strtab = (char *)(ImageAddress + ElfSections[i].sh_offset); + debug("String table found, %d entries", ElfSections[i].sh_size); + } + break; + default: + break; + } + + if (ElfSymbols != nullptr && strtab != nullptr) + { + uintptr_t Index, MinimumIndex; + for (uintptr_t i = 0; i < this->TotalEntries - 1; i++) + { + MinimumIndex = i; + for (Index = i + 1; Index < this->TotalEntries; Index++) + if (ElfSymbols[Index].st_value < ElfSymbols[MinimumIndex].st_value) + MinimumIndex = Index; + Elf64_Sym tmp = ElfSymbols[MinimumIndex]; + ElfSymbols[MinimumIndex] = ElfSymbols[i]; + ElfSymbols[i] = tmp; + } + + while (ElfSymbols[0].st_value == 0) + { + ElfSymbols++; + this->TotalEntries--; + } + +#ifdef DEBUG + static int once = 0; +#endif + + trace("Symbol table loaded, %d entries (%ldKB)", this->TotalEntries, TO_KB(this->TotalEntries * sizeof(SymbolTable))); + for (uintptr_t i = 0, g = this->TotalEntries; i < g; i++) + { + this->SymTable[i].Address = ElfSymbols[i].st_value; + this->SymTable[i].FunctionName = &strtab[ElfSymbols[i].st_name]; +#ifdef DEBUG + if (once) + debug("Symbol %d: %#llx %s", i, this->SymTable[i].Address, this->SymTable[i].FunctionName); +#endif + } + +#ifdef DEBUG + if (!once) + once++; +#endif + } + } + + Symbols::~Symbols() {} + + const NIF char *Symbols::GetSymbolFromAddress(uintptr_t Address) + { + Symbols::SymbolTable Result{0, (char *)""}; + for (uintptr_t i = 0; i < this->TotalEntries; i++) + if (this->SymTable[i].Address <= Address && this->SymTable[i].Address > Result.Address) + Result = this->SymTable[i]; + return Result.FunctionName; + } + + NIF void Symbols::AddSymbol(uintptr_t Address, const char *Name) + { + if (this->TotalEntries >= 0x10000) + { + error("Symbol table is full"); + return; + } + + this->SymTable[this->TotalEntries].Address = Address; + strcpy(this->SymTable[this->TotalEntries].FunctionName, Name); + this->TotalEntries++; + } +} diff --git a/Kernel/Core/SystemManagementBIOS.cpp b/Kernel/Core/SystemManagementBIOS.cpp new file mode 100644 index 00000000..a58caa43 --- /dev/null +++ b/Kernel/Core/SystemManagementBIOS.cpp @@ -0,0 +1,85 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "smbios.hpp" + +#include + +#include "../kernel.h" + +namespace SMBIOS +{ + bool CheckSMBIOS() + { + if (bInfo.SMBIOSPtr != nullptr && bInfo.SMBIOSPtr < (void *)0xFFFFFFFFFFFF0000) + { + debug("SMBIOS is available (%#lx).", bInfo.SMBIOSPtr); + return true; + } + debug("SMBIOS is not available. (%#lx)", bInfo.SMBIOSPtr); + return false; + } + + SMBIOSEntryPoint *GetSMBIOSEntryPoint() { return (SMBIOSEntryPoint *)bInfo.SMBIOSPtr; } + + static inline int SMBIOSTableLength(SMBIOSHeader *Hdr) + { + int i; + const char *strtab = (char *)Hdr + Hdr->Length; + for (i = 1; strtab[i - 1] != '\0' || strtab[i] != '\0'; i++) + ; + return Hdr->Length + i + 1; + } + + void *GetSMBIOSHeader(SMBIOSType Type) + { + if (!CheckSMBIOS()) + return nullptr; + + SMBIOSEntryPoint *Header = (SMBIOSEntryPoint *)bInfo.SMBIOSPtr; + debug("Getting SMBIOS header for type %d", Type); + + struct SMBIOSHeader *hdr = (SMBIOSHeader *)(uintptr_t)Header->TableAddress; + for (int i = 0; i <= 11; i++) + { + if (hdr < (void *)(uintptr_t)(Header->TableAddress + Header->TableLength)) + if (hdr->Type == Type) + { + debug("Found SMBIOS header for type %d at %#lx", Type, hdr); + return hdr; + } + hdr = (struct SMBIOSHeader *)((uintptr_t)hdr + SMBIOSTableLength(hdr)); + } + return nullptr; + } + + SMBIOSBIOSInformation *GetBIOSInformation() { return (SMBIOSBIOSInformation *)GetSMBIOSHeader(SMBIOSTypeBIOSInformation); } + + SMBIOSSystemInformation *GetSystemInformation() { return (SMBIOSSystemInformation *)GetSMBIOSHeader(SMBIOSTypeSystemInformation); } + + SMBIOSBaseBoardInformation *GetBaseBoardInformation() { return (SMBIOSBaseBoardInformation *)GetSMBIOSHeader(SMBIOSTypeBaseBoardInformation); } + + SMBIOSProcessorInformation *GetProcessorInformation() { return (SMBIOSProcessorInformation *)GetSMBIOSHeader(SMBIOSTypeProcessorInformation); } + + SMBIOSMemoryArray *GetMemoryArray() { return (SMBIOSMemoryArray *)GetSMBIOSHeader(SMBIOSTypePhysicalMemoryArray); } + + SMBIOSMemoryDevice *GetMemoryDevice() { return (SMBIOSMemoryDevice *)GetSMBIOSHeader(SMBIOSTypeMemoryDevice); } + + SMBIOSMemoryArrayMappedAddress *GetMemoryArrayMappedAddress() { return (SMBIOSMemoryArrayMappedAddress *)GetSMBIOSHeader(SMBIOSTypeMemoryArrayMappedAddress); } + + SMBIOSMemoryDeviceMappedAddress *GetMemoryDeviceMappedAddress() { return (SMBIOSMemoryDeviceMappedAddress *)GetSMBIOSHeader(SMBIOSTypeMemoryDeviceMappedAddress); } +} diff --git a/Kernel/Core/Time/HighPrecisionEventTimer.cpp b/Kernel/Core/Time/HighPrecisionEventTimer.cpp new file mode 100644 index 00000000..e8699ae9 --- /dev/null +++ b/Kernel/Core/Time/HighPrecisionEventTimer.cpp @@ -0,0 +1,89 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/acpi.hpp" +#elif defined(a32) +#include "../../Architecture/i386/acpi.hpp" +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +namespace Time +{ + bool HighPrecisionEventTimer::Sleep(uint64_t Duration, Units Unit) + { +#if defined(a86) + uint64_t Target = mminq(&((HPET *)hpet)->MainCounterValue) + (Duration * ConvertUnit(Unit)) / clk; + while (mminq(&((HPET *)hpet)->MainCounterValue) < Target) + CPU::Pause(); + return true; +#endif + } + + uint64_t HighPrecisionEventTimer::GetCounter() + { +#if defined(a86) + return mminq(&((HPET *)hpet)->MainCounterValue); +#endif + } + + uint64_t HighPrecisionEventTimer::CalculateTarget(uint64_t Target, Units Unit) + { +#if defined(a86) + return mminq(&((HPET *)hpet)->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk; +#endif + } + + uint64_t HighPrecisionEventTimer::GetNanosecondsSinceClassCreation() + { +#if defined(a86) + uint64_t Subtraction = this->GetCounter() - this->ClassCreationTime; + if (Subtraction <= 0 || this->clk <= 0) + return 0; + return Subtraction / (this->clk / ConvertUnit(Units::Nanoseconds)); +#endif + } + + HighPrecisionEventTimer::HighPrecisionEventTimer(void *hpet) + { +#if defined(a86) + ACPI::ACPI::HPETHeader *HPET_HDR = (ACPI::ACPI::HPETHeader *)hpet; + Memory::Virtual().Remap((void *)HPET_HDR->Address.Address, + (void *)HPET_HDR->Address.Address, + Memory::PTFlag::RW | Memory::PTFlag::PCD); + this->hpet = (HPET *)HPET_HDR->Address.Address; + trace("%s timer is at address %016p", HPET_HDR->Header.OEMID, (void *)HPET_HDR->Address.Address); + clk = s_cst(uint32_t, this->hpet->GeneralCapabilities >> 32); + mmoutq(&this->hpet->GeneralConfiguration, 0); + mmoutq(&this->hpet->MainCounterValue, 0); + mmoutq(&this->hpet->GeneralConfiguration, 1); + ClassCreationTime = this->GetCounter(); +#endif + } + + HighPrecisionEventTimer::~HighPrecisionEventTimer() + { + } +} diff --git a/Kernel/Core/Time/Time.cpp b/Kernel/Core/Time/Time.cpp new file mode 100644 index 00000000..acb66fe9 --- /dev/null +++ b/Kernel/Core/Time/Time.cpp @@ -0,0 +1,105 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include +#include + +namespace Time +{ + Clock ReadClock() + { + Clock tm; +#if defined(a86) + uint32_t t = 0; + outb(0x70, 0x00); + t = inb(0x71); + tm.Second = ((t & 0x0F) + ((t >> 4) * 10)); + outb(0x70, 0x02); + t = inb(0x71); + tm.Minute = ((t & 0x0F) + ((t >> 4) * 10)); + outb(0x70, 0x04); + t = inb(0x71); + tm.Hour = ((t & 0x0F) + ((t >> 4) * 10)); + outb(0x70, 0x07); + t = inb(0x71); + tm.Day = ((t & 0x0F) + ((t >> 4) * 10)); + outb(0x70, 0x08); + t = inb(0x71); + tm.Month = ((t & 0x0F) + ((t >> 4) * 10)); + outb(0x70, 0x09); + t = inb(0x71); + tm.Year = ((t & 0x0F) + ((t >> 4) * 10)); + tm.Counter = 0; +#elif defined(aa64) + tm.Year = 0; + tm.Month = 0; + tm.Day = 0; + tm.Hour = 0; + tm.Minute = 0; + tm.Second = 0; + tm.Counter = 0; +#endif + return tm; + } + + Clock ConvertFromUnix(int Timestamp) + { + Clock result; + + uint64_t Seconds = Timestamp; + uint64_t Minutes = Seconds / 60; + uint64_t Hours = Minutes / 60; + uint64_t Days = Hours / 24; + + result.Year = 1970; + while (Days >= 365) + { + if (result.Year % 4 == 0 && (result.Year % 100 != 0 || result.Year % 400 == 0)) + { + if (Days >= 366) + { + Days -= 366; + result.Year++; + } + else + break; + } + else + { + Days -= 365; + result.Year++; + } + } + + int DaysInMonth[] = {31, result.Year % 4 == 0 ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + for (result.Month = 0; result.Month < 12; result.Month++) + { + if (Days < static_cast(DaysInMonth[result.Month])) + break; + Days -= DaysInMonth[result.Month]; + } + result.Month++; + + result.Day = static_cast(Days) + 1; + result.Hour = static_cast(Hours % 24); + result.Minute = static_cast(Minutes % 60); + result.Second = static_cast(Seconds % 60); + result.Counter = static_cast(Timestamp); + return result; + } +} diff --git a/Kernel/Core/Time/TimeStampCounter.cpp b/Kernel/Core/Time/TimeStampCounter.cpp new file mode 100644 index 00000000..61ab3afa --- /dev/null +++ b/Kernel/Core/Time/TimeStampCounter.cpp @@ -0,0 +1,81 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/acpi.hpp" +#elif defined(a32) +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +namespace Time +{ + bool TimeStampCounter::Sleep(uint64_t Duration, Units Unit) + { +#if defined(a86) + uint64_t Target = this->GetCounter() + (Duration * ConvertUnit(Unit)) / this->clk; + while (this->GetCounter() < Target) + CPU::Pause(); + return true; +#endif + } + + uint64_t TimeStampCounter::GetCounter() + { +#if defined(a86) + return CPU::Counter(); +#endif + } + + uint64_t TimeStampCounter::CalculateTarget(uint64_t Target, Units Unit) + { +#if defined(a86) + return this->GetCounter() + (Target * ConvertUnit(Unit)) / this->clk; +#endif + } + + uint64_t TimeStampCounter::GetNanosecondsSinceClassCreation() + { +#if defined(a86) + return (this->GetCounter() - this->ClassCreationTime) / this->clk; +#endif + } + + TimeStampCounter::TimeStampCounter() + { +#if defined(a86) + fixme(""); // FIXME: This is not a good way to measure the clock speed + uint64_t Start = CPU::Counter(); + TimeManager->Sleep(1, Units::Milliseconds); + uint64_t End = CPU::Counter(); + + this->clk = End - Start; + this->ClassCreationTime = this->GetCounter(); +#endif + } + + TimeStampCounter::~TimeStampCounter() + { + } +} diff --git a/Kernel/Core/Time/Timer.cpp b/Kernel/Core/Time/Timer.cpp new file mode 100644 index 00000000..837bd123 --- /dev/null +++ b/Kernel/Core/Time/Timer.cpp @@ -0,0 +1,215 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include +#include + +#if defined(a64) +#include "../../Architecture/amd64/acpi.hpp" +#elif defined(a32) +#include "../../Architecture/i386/acpi.hpp" +#elif defined(aa64) +#endif + +#include "../../kernel.h" + +namespace Time +{ + bool time::Sleep(uint64_t Duration, Units Unit) + { + switch (ActiveTimer) + { + case NONE: + error("No timer is active"); + return false; + case RTC: + fixme("RTC sleep not implemented"); + return false; + case PIT: + fixme("PIT sleep not implemented"); + return false; + case HPET: + return this->hpet->Sleep(Duration, Unit); + case ACPI: + fixme("ACPI sleep not implemented"); + return false; + case APIC: + fixme("APIC sleep not implemented"); + return false; + case TSC: + return this->tsc->Sleep(Duration, Unit); + default: + error("Unknown timer"); + return false; + } + } + + uint64_t time::GetCounter() + { + switch (ActiveTimer) + { + case NONE: + error("No timer is active"); + return false; + case RTC: + fixme("RTC sleep not implemented"); + return false; + case PIT: + fixme("PIT sleep not implemented"); + return false; + case HPET: + return this->hpet->GetCounter(); + case ACPI: + fixme("ACPI sleep not implemented"); + return false; + case APIC: + fixme("APIC sleep not implemented"); + return false; + case TSC: + return this->tsc->GetCounter(); + default: + error("Unknown timer"); + return false; + } + } + + uint64_t time::CalculateTarget(uint64_t Target, Units Unit) + { + switch (ActiveTimer) + { + case NONE: + error("No timer is active"); + return false; + case RTC: + fixme("RTC sleep not implemented"); + return false; + case PIT: + fixme("PIT sleep not implemented"); + return false; + case HPET: + return this->hpet->CalculateTarget(Target, Unit); + case ACPI: + fixme("ACPI sleep not implemented"); + return false; + case APIC: + fixme("APIC sleep not implemented"); + return false; + case TSC: + return this->tsc->CalculateTarget(Target, Unit); + default: + error("Unknown timer"); + return false; + } + } + + uint64_t time::GetNanosecondsSinceClassCreation() + { + switch (ActiveTimer) + { + case NONE: + error("No timer is active"); + return false; + case RTC: + fixme("RTC sleep not implemented"); + return false; + case PIT: + fixme("PIT sleep not implemented"); + return false; + case HPET: + return this->hpet->GetNanosecondsSinceClassCreation(); + case ACPI: + fixme("ACPI sleep not implemented"); + return false; + case APIC: + fixme("APIC sleep not implemented"); + return false; + case TSC: + return this->tsc->GetNanosecondsSinceClassCreation(); + default: + error("Unknown timer"); + return false; + } + } + + void time::FindTimers(void *acpi) + { +#if defined(a86) + /* TODO: RTC check */ + /* TODO: PIT check */ + + if (acpi) + { + if (((ACPI::ACPI *)acpi)->HPET) + { + hpet = new HighPrecisionEventTimer(((ACPI::ACPI *)acpi)->HPET); + ActiveTimer = HPET; + SupportedTimers |= HPET; + KPrint("\e11FF11HPET found"); + } + else + { + KPrint("\eFF2200HPET not found"); + } + + /* TODO: ACPI check */ + /* TODO: APIC check */ + } + else + { + KPrint("\eFF2200ACPI not found"); + } + + bool TSCInvariant = false; + if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) + { + CPU::x86::AMD::CPUID0x80000007 cpuid80000007; + cpuid80000007.Get(); + if (cpuid80000007.EDX.TscInvariant) + TSCInvariant = true; + } + else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) + { + // TODO: Intel 0x80000007 + CPU::x86::AMD::CPUID0x80000007 cpuid80000007; + cpuid80000007.Get(); + if (cpuid80000007.EDX.TscInvariant) + TSCInvariant = true; + } + + if (TSCInvariant) + { + tsc = new TimeStampCounter; + // FIXME: ActiveTimer = TSC; + SupportedTimers |= TSC; + KPrint("\e11FF11Invariant TSC found"); + } + else + KPrint("\eFF2200TSC is not invariant"); +#endif + } + + time::time() + { + } + + time::~time() + { + } +} diff --git a/Kernel/Core/UndefinedBehaviorSanitization.c b/Kernel/Core/UndefinedBehaviorSanitization.c new file mode 100644 index 00000000..8d19125a --- /dev/null +++ b/Kernel/Core/UndefinedBehaviorSanitization.c @@ -0,0 +1,610 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include "ubsan.h" + +#include +#include + +// TODO: implement: +/* +__ubsan_handle_type_mismatch_v1_abort +__ubsan_handle_add_overflow_abort +__ubsan_handle_sub_overflow_abort +__ubsan_handle_mul_overflow_abort +__ubsan_handle_negate_overflow_abort +__ubsan_handle_divrem_overflow_abort +__ubsan_handle_shift_out_of_bounds_abort +__ubsan_handle_out_of_bounds_abort +__ubsan_handle_vla_bound_not_positive_abort +__ubsan_handle_float_cast_overflow +__ubsan_handle_float_cast_overflow_abort +__ubsan_handle_load_invalid_value_abort +__ubsan_handle_invalid_builtin_abort +__ubsan_handle_function_type_mismatch_abort +__ubsan_handle_nonnull_return_v1 +__ubsan_handle_nonnull_return_v1_abort +__ubsan_handle_nullability_return_v1 +__ubsan_handle_nullability_return_v1_abort +__ubsan_handle_nonnull_arg_abort +__ubsan_handle_nullability_arg +__ubsan_handle_nullability_arg_abort +__ubsan_handle_pointer_overflow_abort +__ubsan_handle_cfi_check_fail +*/ + +extern void __asan_report_load1(void *unknown) +{ + ubsan("load1"); + UNUSED(unknown); +} + +extern void __asan_report_load2(void *unknown) +{ + ubsan("load2"); + UNUSED(unknown); +} + +extern void __asan_report_load4(void *unknown) +{ + ubsan("load4"); + UNUSED(unknown); +} + +extern void __asan_report_load8(void *unknown) +{ + ubsan("load8"); + UNUSED(unknown); +} + +extern void __asan_report_load16(void *unknown) +{ + ubsan("load16"); + UNUSED(unknown); +} + +extern void __asan_report_load_n(void *unknown, uintptr_t size) +{ + ubsan("loadn"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_report_store1(void *unknown) +{ + ubsan("store1"); + UNUSED(unknown); +} + +extern void __asan_report_store2(void *unknown) +{ + ubsan("store2"); + UNUSED(unknown); +} + +extern void __asan_report_store4(void *unknown) +{ + ubsan("store4"); + UNUSED(unknown); +} + +extern void __asan_report_store8(void *unknown) +{ + ubsan("store8"); + UNUSED(unknown); +} + +extern void __asan_report_store16(void *unknown) +{ + ubsan("store16"); + UNUSED(unknown); +} + +extern void __asan_report_store_n(void *unknown, uintptr_t size) +{ + ubsan("storen"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_report_load1_noabort(void *unknown) +{ + ubsan("load1"); + UNUSED(unknown); +} + +extern void __asan_report_load2_noabort(void *unknown) +{ + ubsan("load2"); + UNUSED(unknown); +} + +extern void __asan_report_load4_noabort(void *unknown) +{ + ubsan("load4"); + UNUSED(unknown); +} + +extern void __asan_report_load8_noabort(void *unknown) +{ + ubsan("load8"); + UNUSED(unknown); +} + +extern void __asan_report_load16_noabort(void *unknown) +{ + ubsan("load16"); + UNUSED(unknown); +} + +extern void __asan_report_load_n_noabort(void *unknown, uintptr_t size) +{ + ubsan("loadn"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_report_store1_noabort(void *unknown) +{ + ubsan("store1"); + UNUSED(unknown); +} + +extern void __asan_report_store2_noabort(void *unknown) +{ + ubsan("store2"); + UNUSED(unknown); +} + +extern void __asan_report_store4_noabort(void *unknown) +{ + ubsan("store4"); + UNUSED(unknown); +} + +extern void __asan_report_store8_noabort(void *unknown) +{ + ubsan("store8"); + UNUSED(unknown); +} + +extern void __asan_report_store16_noabort(void *unknown) +{ + ubsan("store16"); + UNUSED(unknown); +} + +extern void __asan_report_store_n_noabort(void *unknown, uintptr_t size) +{ + ubsan("storen"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_stack_malloc_0(uintptr_t size) +{ + ubsan("stack malloc 0"); + UNUSED(size); +} + +extern void __asan_stack_malloc_1(uintptr_t size) +{ + ubsan("stack malloc 1"); + UNUSED(size); +} + +extern void __asan_stack_malloc_2(uintptr_t size) +{ + ubsan("stack malloc 2"); + UNUSED(size); +} + +extern void __asan_stack_malloc_3(uintptr_t size) +{ + ubsan("stack malloc 3"); + UNUSED(size); +} + +extern void __asan_stack_malloc_4(uintptr_t size) +{ + ubsan("stack malloc 4"); + UNUSED(size); +} + +extern void __asan_stack_malloc_5(uintptr_t size) +{ + ubsan("stack malloc 5"); + UNUSED(size); +} + +extern void __asan_stack_malloc_6(uintptr_t size) +{ + ubsan("stack malloc 6"); + UNUSED(size); +} + +extern void __asan_stack_malloc_7(uintptr_t size) +{ + ubsan("stack malloc 7"); + UNUSED(size); +} + +extern void __asan_stack_malloc_8(uintptr_t size) +{ + ubsan("stack malloc 8"); + UNUSED(size); +} + +extern void __asan_stack_malloc_9(uintptr_t size) +{ + ubsan("stack malloc 9"); + UNUSED(size); +} + +extern void __asan_stack_free_0(void *ptr, uintptr_t size) +{ + ubsan("stack free 0"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_1(void *ptr, uintptr_t size) +{ + ubsan("stack free 1"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_2(void *ptr, uintptr_t size) +{ + ubsan("stack free 2"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_3(void *ptr, uintptr_t size) +{ + ubsan("stack free 3"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_4(void *ptr, uintptr_t size) +{ + ubsan("stack free 4"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_5(void *ptr, uintptr_t size) +{ + ubsan("stack free 5"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_6(void *ptr, uintptr_t size) +{ + ubsan("stack free 6"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_7(void *ptr, uintptr_t size) +{ + ubsan("stack free 7"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_8(void *ptr, uintptr_t size) +{ + ubsan("stack free 8"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_9(void *ptr, uintptr_t size) +{ + ubsan("stack free 9"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_poison_stack_memory(void *addr, uintptr_t size) +{ + ubsan("poison stack memory"); + UNUSED(addr); + UNUSED(size); +} + +extern void __asan_unpoison_stack_memory(void *addr, uintptr_t size) +{ + ubsan("unpoison stack memory"); + UNUSED(addr); + UNUSED(size); +} + +extern void __asan_before_dynamic_init(const char *module_name) +{ + ubsan("before dynamic init"); + UNUSED(module_name); +} + +extern void __asan_after_dynamic_init(void) { ubsan("after dynamic init"); } + +extern void __asan_register_globals(void *unknown, size_t size) +{ + ubsan("register_globals"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_unregister_globals(void) { ubsan("unregister_globals"); } + +extern void __asan_init(void) { ubsan("init"); } +extern void __asan_version_mismatch_check_v8(void) { ubsan("version_mismatch_check_v8"); } +extern void __asan_option_detect_stack_use_after_return(void) { ubsan("stack use after return"); } + +extern __noreturn void __asan_handle_no_return(void) +{ + ubsan("no_return"); + while (1) + ; +} + +#define is_aligned(value, alignment) !(value & (alignment - 1)) + +const char *Type_Check_Kinds[] = { + "Load of", + "Store to", + "Reference binding to", + "Member access within", + "Member call on", + "Constructor call on", + "Downcast of", + "Downcast of", + "Upcast of", + "Cast to virtual base of", +}; + +bool UBSANMsg(const char *file, uint32_t line, uint32_t column) +{ + /* This can be ignored (unaligned memory access) */ + if (strstr(file, "AdvancedConfigurationAndPowerInterface.cpp") && + ((line == 34 && column == 47) || + (line == 36 && column == 47))) + return false; + + /* This can be ignored (unaligned memory access) */ + if (strstr(file, "SystemManagementBIOS.cpp") && + ((line == 47 && column == 21) || + (line == 44 && column == 49) || + (line == 62 && column == 26))) + return false; + + /* This can be ignored (unaligned memory access) */ + if (strstr(file, "DynamicHostConfigurationProtocol.cpp") && + (line == 63 && column == 30)) + return false; + + if (strstr(file, "liballoc_1_1.c")) + return false; + + /* This can be ignored (store address x with insufficient space for object of type 'y') */ + if (strstr(file, "Task.cpp") && line > 500) + return false; + + /* This can be ignored (store address x with insufficient space for object of type 'y') */ + if (strstr(file, "InternetProtocol.cpp") && + ((line == 66 && column == 13) || + (line == 66 && column == 93) || + (line == 68 && column == 51) || + (line == 68 && column == 165) || + (line == 73 && column == 36) || + (line == 78 && column == 54) || + (line == 79 && column == 64) || + + (line == 81 && column == 126) || + (line == 81 && column == 165) || + + (line == 81 && column == 15) || + (line == 156 && column == 38) || + (line == 157 && column == 47) || + (line == 158 && column == 45))) + return false; + + /* This can be ignored (store address x with insufficient space for object of type 'y') */ + if (strstr(file, "DynamicHostConfigurationProtocol.cpp") && + ((line == 156 && column == 38) || + (line == 157 && column == 47) || + (line == 158 && column == 45))) + return false; + + ubsan("\t\tIn File: %s:%i:%i", file, line, column); + return true; +} + +void __ubsan_handle_type_mismatch_v1(struct type_mismatch_v1_data *type_mismatch, uintptr_t pointer) +{ + struct source_location *location = &type_mismatch->location; + if (pointer == 0) + { + if (UBSANMsg(location->file, location->line, location->column)) + { + ubsan("Null pointer access."); + } + } + else if (type_mismatch->alignment != 0 && is_aligned(pointer, type_mismatch->alignment)) + { + if (UBSANMsg(location->file, location->line, location->column)) + { + ubsan("Unaligned memory access %#llx.", pointer); + } + } + else + { + if (UBSANMsg(location->file, location->line, location->column)) + { + ubsan("%s address %#llx with insufficient space for object of type %s", + Type_Check_Kinds[type_mismatch->type_check_kind], (void *)pointer, type_mismatch->type->name); + } + } +} + +void __ubsan_handle_add_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Addition overflow."); + } +} + +void __ubsan_handle_sub_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Subtraction overflow."); + } +} + +void __ubsan_handle_mul_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Multiplication overflow."); + } +} + +void __ubsan_handle_divrem_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Division overflow."); + } +} + +void __ubsan_handle_negate_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Negation overflow."); + } +} + +void __ubsan_handle_pointer_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Pointer overflow."); + } +} + +void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Shift out of bounds."); + } +} + +void __ubsan_handle_load_invalid_value(struct invalid_value_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Invalid load value."); + } +} + +void __ubsan_handle_out_of_bounds(struct array_out_of_bounds_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Array out of bounds."); + } +} + +void __ubsan_handle_vla_bound_not_positive(struct negative_vla_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Variable-length argument is negative."); + } +} + +void __ubsan_handle_nonnull_return(struct nonnull_return_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Non-null return is null."); + } +} + +void __ubsan_handle_nonnull_return_v1(struct nonnull_return_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Non-null return is null."); + } +} + +void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Non-null argument is null."); + } +} + +void __ubsan_handle_builtin_unreachable(struct unreachable_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Unreachable code reached."); + } +} + +void __ubsan_handle_invalid_builtin(struct invalid_builtin_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Invalid builtin."); + } +} + +void __ubsan_handle_missing_return(struct unreachable_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Missing return."); + } +} + +void __ubsan_vptr_type_cache(uintptr_t *cache, uintptr_t ptr) +{ + ubsan("Vptr type cache."); + *cache = ptr; +} + +void __ubsan_handle_dynamic_type_cache_miss(struct dynamic_type_cache_miss_data *data, uintptr_t ptr) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Dynamic type cache miss."); + } + UNUSED(ptr); +} \ No newline at end of file diff --git a/Kernel/Core/UniversalAsynchronousReceiverTransmitter.cpp b/Kernel/Core/UniversalAsynchronousReceiverTransmitter.cpp new file mode 100644 index 00000000..e9b79617 --- /dev/null +++ b/Kernel/Core/UniversalAsynchronousReceiverTransmitter.cpp @@ -0,0 +1,171 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include + +#include +#include + +volatile bool serialports[8] = {false, false, false, false, false, false, false, false}; +std::vector RegisteredEvents; + +#if defined(a86) +NIF __always_inline inline uint8_t NoProfiler_inportb(uint16_t Port) +{ + uint8_t Result; + asm("in %%dx, %%al" + : "=a"(Result) + : "d"(Port)); + return Result; +} + +NIF __always_inline inline void NoProfiler_outportb(uint16_t Port, uint8_t Data) +{ + asmv("out %%al, %%dx" + : + : "a"(Data), "d"(Port)); +} +#endif + +namespace UniversalAsynchronousReceiverTransmitter +{ +#define SERIAL_ENABLE_DLAB 0x80 +#define SERIAL_RATE_115200_LO 0x01 +#define SERIAL_RATE_115200_HI 0x00 +#define SERIAL_RATE_57600_LO 0x02 +#define SERIAL_RATE_57600_HI 0x00 +#define SERIAL_RATE_38400_LO 0x03 +#define SERIAL_RATE_38400_HI 0x00 +#define SERIAL_BUFFER_EMPTY 0x20 + + /* TODO: Serial Port implementation needs reword. https://wiki.osdev.org/Serial_Ports */ + + SafeFunction NIF UART::UART(SerialPorts Port) + { +#if defined(a86) + if (Port == COMNULL) + return; + + this->Port = Port; + int PortNumber = 0; + + switch (Port) + { + case COM1: + PortNumber = 0; + break; + case COM2: + PortNumber = 1; + break; + case COM3: + PortNumber = 2; + break; + case COM4: + PortNumber = 3; + break; + case COM5: + PortNumber = 4; + break; + case COM6: + PortNumber = 5; + break; + case COM7: + PortNumber = 6; + break; + case COM8: + PortNumber = 7; + break; + default: + return; + } + + if (serialports[PortNumber]) + return; + + // Initialize the serial port + NoProfiler_outportb(s_cst(uint16_t, Port + 1), 0x00); // Disable all interrupts + NoProfiler_outportb(s_cst(uint16_t, Port + 3), SERIAL_ENABLE_DLAB); // Enable DLAB (set baud rate divisor) + NoProfiler_outportb(s_cst(uint16_t, Port + 0), SERIAL_RATE_115200_LO); // Set divisor to 1 (lo byte) 115200 baud + NoProfiler_outportb(s_cst(uint16_t, Port + 1), SERIAL_RATE_115200_HI); // (hi byte) + NoProfiler_outportb(s_cst(uint16_t, Port + 3), 0x03); // 8 bits, no parity, one stop bit + NoProfiler_outportb(s_cst(uint16_t, Port + 2), 0xC7); // Enable FIFO, clear them, with 14-byte threshold + NoProfiler_outportb(s_cst(uint16_t, Port + 4), 0x0B); // IRQs enabled, RTS/DSR set + + // Check if the serial port is faulty. + if (NoProfiler_inportb(s_cst(uint16_t, Port + 0)) != 0xAE) + { + static int once = 0; + if (!once++) + warn("Serial port %#llx is faulty.", Port); + // serialports[Port] = false; // ignore for now + // return; + } + + // Set to normal operation mode. + NoProfiler_outportb(s_cst(uint16_t, Port + 4), 0x0F); + serialports[PortNumber] = true; +#endif + } + + SafeFunction NIF UART::~UART() {} + + SafeFunction NIF void UART::Write(uint8_t Char) + { +#if defined(a86) + while ((NoProfiler_inportb(s_cst(uint16_t, Port + 5)) & SERIAL_BUFFER_EMPTY) == 0) + ; + NoProfiler_outportb(Port, Char); +#endif + foreach (auto e in RegisteredEvents) + if (e->GetRegisteredPort() == Port || e->GetRegisteredPort() == COMNULL) + e->OnSent(Char); + } + + SafeFunction NIF uint8_t UART::Read() + { +#if defined(a86) + while ((NoProfiler_inportb(s_cst(uint16_t, Port + 5)) & 1) == 0) + ; + return NoProfiler_inportb(Port); +#endif + foreach (auto e in RegisteredEvents) + { + if (e->GetRegisteredPort() == Port || e->GetRegisteredPort() == COMNULL) + { +#if defined(a86) + e->OnReceived(NoProfiler_inportb(Port)); +#endif + } + } + } + + SafeFunction NIF Events::Events(SerialPorts Port) + { + this->Port = Port; + RegisteredEvents.push_back(this); + } + + SafeFunction NIF Events::~Events() + { + for (size_t i = 0; i < RegisteredEvents.size(); i++) + if (RegisteredEvents[i] == this) + { + RegisteredEvents.remove(i); + return; + } + } +} diff --git a/Kernel/Core/Video/Display.cpp b/Kernel/Core/Video/Display.cpp new file mode 100644 index 00000000..c3462929 --- /dev/null +++ b/Kernel/Core/Video/Display.cpp @@ -0,0 +1,442 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include +#include +#include + +extern uintptr_t _binary_Files_tamsyn_font_1_11_Tamsyn7x14r_psf_start; +extern uintptr_t _binary_Files_tamsyn_font_1_11_Tamsyn7x14r_psf_end; +extern uintptr_t _binary_Files_tamsyn_font_1_11_Tamsyn7x14r_psf_size; + +NewLock(PrintLock); + +namespace Video +{ + Font *Display::GetCurrentFont() { return CurrentFont; } + void Display::SetCurrentFont(Font *Font) { CurrentFont = Font; } + uint16_t Display::GetBitsPerPixel() { return this->framebuffer.BitsPerPixel; } + uint64_t Display::GetPitch() { return this->framebuffer.Pitch; } + + void Display::CreateBuffer(uint32_t Width, uint32_t Height, int Index) + { + if (Width == 0 || Height == 0) + { + Width = this->framebuffer.Width; + Height = this->framebuffer.Height; + debug("Buffer %d created with default size (%d, %d)", Index, Width, Height); + } + + if (this->Buffers[Index].Checksum == 0xBBFFE515A117E) + { + warn("Buffer %d already exists, skipping creation", Index); + return; + } + + size_t Size = this->framebuffer.Pitch * Height; + + this->Buffers[Index].Buffer = KernelAllocator.RequestPages(TO_PAGES(Size + 1)); + memset(this->Buffers[Index].Buffer, 0, Size); + + this->Buffers[Index].Width = Width; + this->Buffers[Index].Height = Height; + this->Buffers[Index].Size = Size; + this->Buffers[Index].Color = 0xFFFFFF; + this->Buffers[Index].CursorX = 0; + this->Buffers[Index].CursorY = 0; + this->Buffers[Index].Brightness = 100; + this->Buffers[Index].Checksum = 0xBBFFE515A117E; + debug("Buffer %d created", Index); + } + + void Display::SetBuffer(int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + if (this->Buffers[Index].Brightness != 100) + this->SetBrightness(this->Buffers[Index].Brightness, Index); + + if (this->Buffers[Index].Brightness == 0) /* Just clear the buffer */ + memset(this->Buffers[Index].Buffer, 0, this->Buffers[Index].Size); + + memcpy(this->framebuffer.BaseAddress, this->Buffers[Index].Buffer, this->Buffers[Index].Size); + } + + ScreenBuffer *Display::GetBuffer(int Index) { return &this->Buffers[Index]; } + + void Display::ClearBuffer(int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + memset(this->Buffers[Index].Buffer, 0, this->Buffers[Index].Size); + } + + void Display::DeleteBuffer(int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + KernelAllocator.FreePages(this->Buffers[Index].Buffer, TO_PAGES(this->Buffers[Index].Size + 1)); + this->Buffers[Index].Buffer = nullptr; + this->Buffers[Index].Checksum = 0; + debug("Buffer %d deleted", Index); + } + + void Display::SetBrightness(int Value, int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + if (Value > 100) + Value = 100; + else if (Value < 0) + Value = 0; + + uint32_t *pixel = (uint32_t *)this->Buffers[Index].Buffer; + + for (uint32_t y = 0; y < this->Buffers[Index].Height; y++) + { + for (uint32_t x = 0; x < this->Buffers[Index].Width; x++) + { + uint32_t color = pixel[y * this->Buffers[Index].Width + x]; + + uint8_t r = color & 0xff; + uint8_t g = (color >> 8) & 0xff; + uint8_t b = (color >> 16) & 0xff; + + r = s_cst(uint8_t, (r * Value) / 100); + g = s_cst(uint8_t, (g * Value) / 100); + b = s_cst(uint8_t, (b * Value) / 100); + + pixel[y * this->Buffers[Index].Width + x] = (b << 16) | (g << 8) | r; + } + } + this->Buffers[Index].Brightness = s_cst(char, Value); + } + + void Display::SetBufferCursor(int Index, uint32_t X, uint32_t Y) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + this->Buffers[Index].CursorX = X; + this->Buffers[Index].CursorY = Y; + } + + void Display::GetBufferCursor(int Index, uint32_t *X, uint32_t *Y) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + *X = this->Buffers[Index].CursorX; + *Y = this->Buffers[Index].CursorY; + } + + void Display::SetPixel(uint32_t X, uint32_t Y, uint32_t Color, int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + if (unlikely(X >= this->Buffers[Index].Width)) + X = this->Buffers[Index].Width - 1; + + if (unlikely(Y >= this->Buffers[Index].Height)) + Y = this->Buffers[Index].Height - 1; + + uint32_t *Pixel = (uint32_t *)((uintptr_t)this->Buffers[Index].Buffer + (Y * this->Buffers[Index].Width + X) * (this->framebuffer.BitsPerPixel / 8)); + *Pixel = Color; + } + + uint32_t Display::GetPixel(uint32_t X, uint32_t Y, int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + return 0; + + if (unlikely(X >= this->Buffers[Index].Width || Y >= this->Buffers[Index].Height)) + return 0; + + uint32_t *Pixel = (uint32_t *)((uintptr_t)this->Buffers[Index].Buffer + (Y * this->Buffers[Index].Width + X) * (this->framebuffer.BitsPerPixel / 8)); + return *Pixel; + } + + void Display::Scroll(int Index, int Lines) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + if (this->Buffers[Index].DoNotScroll) + return; + + if (Lines == 0) + return; + + if (Lines > 0) + { + uint32_t LineSize = this->Buffers[Index].Width * (this->framebuffer.BitsPerPixel / 8); + uint32_t BytesToMove = LineSize * Lines * this->CurrentFont->GetInfo().Height; + size_t BytesToClear = this->Buffers[Index].Size - BytesToMove; + memmove(this->Buffers[Index].Buffer, (uint8_t *)this->Buffers[Index].Buffer + BytesToMove, BytesToClear); + memset((uint8_t *)this->Buffers[Index].Buffer + BytesToClear, 0, BytesToMove); + } + } + + void Display::SetDoNotScroll(bool Value, int Index) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + this->Buffers[Index].DoNotScroll = Value; + } + + char Display::Print(char Char, int Index, bool WriteToUART) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + return 0; + + // SmartLock(PrintLock); + + if (this->ColorIteration) + { + // RRGGBB + if (Char >= '0' && Char <= '9') + this->Buffers[Index].Color = (this->Buffers[Index].Color << 4) | (Char - '0'); + else if (Char >= 'a' && Char <= 'f') + this->Buffers[Index].Color = (this->Buffers[Index].Color << 4) | (Char - 'a' + 10); + else if (Char >= 'A' && Char <= 'F') + this->Buffers[Index].Color = (this->Buffers[Index].Color << 4) | (Char - 'A' + 10); + else + this->Buffers[Index].Color = 0xFFFFFF; + if (WriteToUART) + UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write(Char); + this->ColorPickerIteration++; + if (this->ColorPickerIteration == 6) + { + this->ColorPickerIteration = 0; + if (WriteToUART) + UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write(']'); + this->ColorIteration = false; + } + return Char; + } + + if (WriteToUART) + UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write(Char); + + switch (Char) + { + case '\e': + { + if (WriteToUART) + UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write('['); + this->ColorIteration = true; + return Char; + } + case '\b': + { + switch (this->CurrentFont->GetInfo().Type) + { + case FontType::PCScreenFont1: + { + fixme("PCScreenFont1"); + break; + } + case FontType::PCScreenFont2: + { + uint32_t fonthdrWidth = this->CurrentFont->GetInfo().PSF2Font->Header->width; + uint32_t fonthdrHeight = this->CurrentFont->GetInfo().PSF2Font->Header->height; + + for (unsigned long Y = this->Buffers[Index].CursorY; Y < this->Buffers[Index].CursorY + fonthdrHeight; Y++) + for (unsigned long X = this->Buffers[Index].CursorX - fonthdrWidth; X < this->Buffers[Index].CursorX; X++) + *(uint32_t *)((uintptr_t)this->Buffers[Index].Buffer + + (Y * this->Buffers[Index].Width + X) * (this->framebuffer.BitsPerPixel / 8)) = 0; + break; + } + default: + warn("Unsupported font type"); + break; + } + + if (this->Buffers[Index].CursorX > 0) + this->Buffers[Index].CursorX -= this->GetCurrentFont()->GetInfo().Width; + + return Char; + } + case '\t': + { + this->Buffers[Index].CursorX = (this->Buffers[Index].CursorX + 8) & ~(8 - 1); + return Char; + } + case '\r': + { + this->Buffers[Index].CursorX = 0; + return Char; + } + case '\n': + { + this->Buffers[Index].CursorX = 0; + this->Buffers[Index].CursorY += this->GetCurrentFont()->GetInfo().Height; + return Char; + } + default: + break; + } + + uint32_t FontHeight = this->GetCurrentFont()->GetInfo().Height; + + if (this->Buffers[Index].CursorX + this->GetCurrentFont()->GetInfo().Width >= this->Buffers[Index].Width) + { + this->Buffers[Index].CursorX = 0; + this->Buffers[Index].CursorY += FontHeight; + } + + if (this->Buffers[Index].CursorY + FontHeight >= this->Buffers[Index].Height) + { + if (!this->Buffers[Index].DoNotScroll) + { + this->Buffers[Index].CursorY -= FontHeight; + this->Scroll(Index, 1); + } + } + + switch (this->CurrentFont->GetInfo().Type) + { + case FontType::PCScreenFont1: + { + uint32_t *PixelPtr = (uint32_t *)this->Buffers[Index].Buffer; + char *FontPtr = (char *)this->CurrentFont->GetInfo().PSF1Font->GlyphBuffer + (Char * this->CurrentFont->GetInfo().PSF1Font->Header->charsize); + for (uint64_t Y = this->Buffers[Index].CursorY; Y < this->Buffers[Index].CursorY + 16; Y++) + { + for (uint64_t X = this->Buffers[Index].CursorX; X < this->Buffers[Index].CursorX + 8; X++) + if ((*FontPtr & (0b10000000 >> (X - this->Buffers[Index].CursorX))) > 0) + *(unsigned int *)(PixelPtr + X + (Y * this->Buffers[Index].Width)) = this->Buffers[Index].Color; + FontPtr++; + } + this->Buffers[Index].CursorX += 8; + + break; + } + case FontType::PCScreenFont2: + { + // if (this->CurrentFont->PSF2Font->GlyphBuffer == (uint16_t *)0x01) // HAS UNICODE TABLE + // Char = this->CurrentFont->PSF2Font->GlyphBuffer[Char]; + int BytesPerLine = (this->CurrentFont->GetInfo().PSF2Font->Header->width + 7) / 8; + char *FontAddress = (char *)this->CurrentFont->GetInfo().StartAddress; + uint32_t FontHeaderSize = this->CurrentFont->GetInfo().PSF2Font->Header->headersize; + uint32_t FontCharSize = this->CurrentFont->GetInfo().PSF2Font->Header->charsize; + uint32_t FontLength = this->CurrentFont->GetInfo().PSF2Font->Header->length; + char *FontPtr = FontAddress + FontHeaderSize + (Char > 0 && (uint32_t)Char < FontLength ? Char : 0) * FontCharSize; + + uint32_t FontHdrWidth = this->CurrentFont->GetInfo().PSF2Font->Header->width; + uint32_t FontHdrHeight = this->CurrentFont->GetInfo().PSF2Font->Header->height; + + for (size_t Y = this->Buffers[Index].CursorY; Y < this->Buffers[Index].CursorY + FontHdrHeight; Y++) + { + for (size_t X = this->Buffers[Index].CursorX; X < this->Buffers[Index].CursorX + FontHdrWidth; X++) + { + if ((*FontPtr & (0b10000000 >> (X - this->Buffers[Index].CursorX))) > 0) + { + void *FramebufferAddress = (void *)((uintptr_t)this->Buffers[Index].Buffer + + (Y * this->Buffers[Index].Width + X) * + (this->framebuffer.BitsPerPixel / 8)); + *(uint32_t *)FramebufferAddress = this->Buffers[Index].Color; + } + } + FontPtr += BytesPerLine; + } + this->Buffers[Index].CursorX += FontHdrWidth; + break; + } + default: + warn("Unsupported font type"); + break; + } + return Char; + } + + void Display::DrawString(const char *String, uint32_t X, uint32_t Y, int Index, bool WriteToUART) + { + if (unlikely(this->Buffers[Index].Checksum != 0xBBFFE515A117E)) + { + debug("Invalid buffer %d", Index); + return; + } + + this->Buffers[Index].CursorX = X; + this->Buffers[Index].CursorY = Y; + + for (int i = 0; String[i] != '\0'; i++) + this->Print(String[i], Index, WriteToUART); + } + + Display::Display(BootInfo::FramebufferInfo Info, bool LoadDefaultFont) + { + this->framebuffer = Info; + if (LoadDefaultFont) + { + this->CurrentFont = new Font(&_binary_Files_tamsyn_font_1_11_Tamsyn7x14r_psf_start, &_binary_Files_tamsyn_font_1_11_Tamsyn7x14r_psf_end, FontType::PCScreenFont2); +#ifdef DEBUG + FontInfo Info = this->CurrentFont->GetInfo(); + debug("Font loaded: %dx%d %s", + Info.Width, Info.Height, Info.Type == FontType::PCScreenFont1 ? "PSF1" : "PSF2"); +#endif + } + this->CreateBuffer(Info.Width, Info.Height, 0); + } + + Display::~Display() + { + debug("Destructor called"); + this->ClearBuffer(0); + this->SetBuffer(0); + + for (int i = 0; i < s_cst(int, sizeof(this->Buffers) / sizeof(this->Buffers[0])); i++) + { + if (this->Buffers[i].Checksum == 0xBBFFE515A117E) + this->DeleteBuffer(i); + } + } +} diff --git a/Kernel/Core/Video/Font.cpp b/Kernel/Core/Video/Font.cpp new file mode 100644 index 00000000..20cd3053 --- /dev/null +++ b/Kernel/Core/Video/Font.cpp @@ -0,0 +1,76 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#include +#include +#include + +namespace Video +{ + Font::Font(uintptr_t *Start, uintptr_t *End, FontType Type) + { + trace("Initializing font with start %#llx and end %#llx Type: %d", Start, End, Type); + this->Info.StartAddress = Start; + this->Info.EndAddress = End; + this->Info.Type = Type; + size_t FontDataLength = End - Start; + + if (Type == FontType::PCScreenFont2) + { + this->Info.PSF2Font = new PSF2_FONT; + + PSF2_HEADER *font2 = (PSF2_HEADER *)KernelAllocator.RequestPages(TO_PAGES(FontDataLength + 1)); + memcpy((void *)font2, Start, FontDataLength); + + Memory::Virtual().Map((void *)font2, (void *)font2, FontDataLength, Memory::PTFlag::RW); + + if (font2->magic[0] != PSF2_MAGIC0 || font2->magic[1] != PSF2_MAGIC1 || font2->magic[2] != PSF2_MAGIC2 || font2->magic[3] != PSF2_MAGIC3) + { + error("Font2 magic mismatch."); + KernelAllocator.FreePages((void *)font2, TO_PAGES(FontDataLength + 1)); + return; + } + + this->Info.PSF2Font->Header = font2; + this->Info.PSF2Font->GlyphBuffer = reinterpret_cast(reinterpret_cast(Start) + sizeof(PSF2_HEADER)); + this->Info.Width = font2->width; + this->Info.Height = font2->height; + } + else if (Type == FontType::PCScreenFont1) + { + this->Info.PSF1Font = new PSF1_FONT; + PSF1_HEADER *font1 = (PSF1_HEADER *)Start; + if (font1->magic[0] != PSF1_MAGIC0 || font1->magic[1] != PSF1_MAGIC1) + error("Font1 magic mismatch."); + uint32_t glyphBufferSize = font1->charsize * 256; + if (font1->mode == 1) // 512 glyph mode + glyphBufferSize = font1->charsize * 512; + void *glyphBuffer = reinterpret_cast(reinterpret_cast(Start) + sizeof(PSF1_HEADER)); + this->Info.PSF1Font->Header = font1; + this->Info.PSF1Font->GlyphBuffer = glyphBuffer; + UNUSED(glyphBufferSize); // TODO: Use this in the future? + + // TODO: Get font size. + this->Info.Width = 16; + this->Info.Height = 8; + } + } + + Font::~Font() + { + } +} diff --git a/Kernel/Core/crashhandler.hpp b/Kernel/Core/crashhandler.hpp new file mode 100644 index 00000000..0a9fc11b --- /dev/null +++ b/Kernel/Core/crashhandler.hpp @@ -0,0 +1,35 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_CRASH_HANDLER_H__ +#define __FENNIX_KERNEL_CRASH_HANDLER_H__ + +#include + +#include +#include + +namespace CrashHandler +{ + extern uintptr_t PageFaultAddress; + extern void *EHIntFrames[INT_FRAMES_MAX]; + + void EHPrint(const char *Format, ...); + void Handle(void *Data); +} + +#endif // !__FENNIX_KERNEL_CRASH_HANDLER_H__ diff --git a/Kernel/Core/smbios.hpp b/Kernel/Core/smbios.hpp new file mode 100644 index 00000000..c2f31cb7 --- /dev/null +++ b/Kernel/Core/smbios.hpp @@ -0,0 +1,357 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_SMBIOS_H__ +#define __FENNIX_KERNEL_SMBIOS_H__ + +#include + +namespace SMBIOS +{ + enum SMBIOSType + { + SMBIOSTypeBIOSInformation = 0, + SMBIOSTypeSystemInformation = 1, + SMBIOSTypeBaseBoardInformation = 2, + SMBIOSTypeSystemEnclosure = 3, + SMBIOSTypeProcessorInformation = 4, + SMBIOSTypeMemoryControllerInformation = 5, + SMBIOSTypeMemoryModuleInformation = 6, + SMBIOSTypeCacheInformation = 7, + SMBIOSTypePortConnectorInformation = 8, + SMBIOSTypeSystemSlots = 9, + SMBIOSTypeOnBoardDevicesInformation = 10, + SMBIOSTypeOEMStrings = 11, + SMBIOSTypeSystemConfigurationOptions = 12, + SMBIOSTypeBIOSLanguageInformation = 13, + SMBIOSTypeGroupAssociations = 14, + SMBIOSTypeSystemEventLog = 15, + SMBIOSTypePhysicalMemoryArray = 16, + SMBIOSTypeMemoryDevice = 17, + SMBIOSType32BitMemoryErrorInformation = 18, + SMBIOSTypeMemoryArrayMappedAddress = 19, + SMBIOSTypeMemoryDeviceMappedAddress = 20, + SMBIOSTypeBuiltInPointingDevice = 21, + SMBIOSTypePortableBattery = 22, + SMBIOSTypeSystemReset = 23, + SMBIOSTypeHardwareSecurity = 24, + SMBIOSTypeSystemPowerControls = 25, + SMBIOSTypeVoltageProbe = 26, + SMBIOSTypeCoolingDevice = 27, + SMBIOSTypeTemperatureProbe = 28, + SMBIOSTypeElectricalCurrentProbe = 29, + SMBIOSTypeOutofBandRemoteAccess = 30, + SMBIOSTypeBootIntegrityServices = 31, + SMBIOSTypeSystemBoot = 32, + SMBIOSType64BitMemoryErrorInformation = 33, + SMBIOSTypeManagementDevice = 34, + SMBIOSTypeManagementDeviceComponent = 35, + SMBIOSTypeManagementDeviceThresholdData = 36, + SMBIOSTypeMemoryChannel = 37, + SMBIOSTypeIPMIDevice = 38, + SMBIOSTypePowerSupply = 39, + SMBIOSTypeAdditionalInformation = 40, + SMBIOSTypeOnboardDevicesExtendedInformation = 41, + SMBIOSTypeManagementControllerHostInterface = 42, + SMBIOSTypeTPMDevice = 43, + SMBIOSTypeProcessorAdditionalInformation = 44, + SMBIOSTypeInactive = 126, + SMBIOSTypeEndOfTable = 127 + }; + + struct SMBIOSHeader + { + unsigned char Type; + unsigned char Length; + unsigned short Handle; + }; + + struct SMBIOSEntryPoint + { + char EntryPointString[4]; + unsigned char Checksum; + unsigned char Length; + unsigned char MajorVersion; + unsigned char MinorVersion; + unsigned short MaxStructureSize; + unsigned char EntryPointRevision; + char FormattedArea[5]; + char EntryPointString2[5]; + unsigned char Checksum2; + unsigned short TableLength; + unsigned int TableAddress; + unsigned short NumberOfStructures; + unsigned char BCDRevision; + }; + + static inline char *SMBIOSNextString(char *Str) + { + while (*Str != '\0') + Str++; + return Str + 1; + } + + struct SMBIOSBIOSInformation + { + SMBIOSHeader Header; + unsigned char Vendor; + unsigned char Version; + unsigned short StartingAddressSegment; + unsigned char ReleaseDate; + unsigned char ROMSize; + unsigned long Characteristics; + unsigned char CharacteristicsExtensionBytes[2]; + unsigned char SystemBIOSMajorRelease; + unsigned char SystemBIOSMinorRelease; + unsigned char EmbeddedControllerFirmwareMajorRelease; + unsigned char EmbeddedControllerFirmwareMinorRelease; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSSystemInformation + { + SMBIOSHeader Header; + unsigned char Manufacturer; + unsigned char ProductName; + unsigned char Version; + unsigned char SerialNumber; + unsigned char UUID[16]; + unsigned char WakeUpType; + unsigned char SKU; + unsigned char Family; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSBaseBoardInformation + { + SMBIOSHeader Header; + unsigned char Manufacturer; + unsigned char Product; + unsigned char Version; + unsigned char SerialNumber; + unsigned char AssetTag; + unsigned char FeatureFlags; + unsigned char LocationInChassis; + unsigned short ChassisHandle; + unsigned char BoardType; + unsigned char NumberOfContainedObjectHandles; + unsigned short ContainedObjectHandles[0]; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSProcessorInformation + { + SMBIOSHeader Header; + unsigned char SocketDesignation; + unsigned char ProcessorType; + unsigned char ProcessorFamily; + unsigned char ProcessorManufacturer; + unsigned long ProcessorID; + unsigned char ProcessorVersion; + unsigned char Voltage; + unsigned short ExternalClock; + unsigned short MaxSpeed; + unsigned short CurrentSpeed; + unsigned char Status; + unsigned char ProcessorUpgrade; + unsigned short L1CacheHandle; + unsigned short L2CacheHandle; + unsigned short L3CacheHandle; + unsigned char SerialNumber; + unsigned char AssetTag; + unsigned char PartNumber; + unsigned char CoreCount; + unsigned char CoreEnabled; + unsigned char ThreadCount; + unsigned short ProcessorCharacteristics; + unsigned short ProcessorFamily2; + unsigned short CoreCount2; + unsigned short CoreEnabled2; + unsigned short ThreadCount2; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSMemoryDevice + { + SMBIOSHeader Header; + unsigned char PhysicalMemoryArrayHandle; + unsigned char MemoryErrorInformationHandle; + unsigned short TotalWidth; + unsigned short DataWidth; + unsigned short Size; + unsigned char FormFactor; + unsigned char DeviceSet; + unsigned char DeviceLocator; + unsigned char BankLocator; + unsigned char MemoryType; + unsigned short TypeDetail; + unsigned short Speed; + unsigned char Manufacturer; + unsigned char SerialNumber; + unsigned char AssetTag; + unsigned char PartNumber; + unsigned char Attributes; + unsigned short ExtendedSize; + unsigned short ConfiguredMemoryClockSpeed; + unsigned short MinimumVoltage; + unsigned short MaximumVoltage; + unsigned short ConfiguredVoltage; + unsigned char MemoryTechnology; + unsigned char OperatingModeCapability; + unsigned char FirmwareVersion; + unsigned char ModuleManufacturerID; + unsigned char ModuleProductID; + unsigned char MemorySubsystemControllerManufacturerID; + unsigned char MemorySubsystemControllerProductID; + unsigned short NonVolatileSize; + unsigned short VolatileSize; + unsigned short CacheSize; + unsigned short LogicalSize; + unsigned char ExtendedSpeed; + unsigned char ExtendedConfiguredMemorySpeed; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSMemoryArrayMappedAddress + { + SMBIOSHeader Header; + unsigned int StartingAddress; + unsigned int EndingAddress; + unsigned short MemoryArrayHandle; + unsigned char PartitionWidth; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSMemoryDeviceMappedAddress + { + SMBIOSHeader Header; + unsigned int StartingAddress; + unsigned int EndingAddress; + unsigned short MemoryDeviceHandle; + unsigned short MemoryArrayMappedAddressHandle; + unsigned char PartitionRowPosition; + unsigned char InterleavePosition; + unsigned char InterleavedDataDepth; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + struct SMBIOSMemoryArray + { + SMBIOSHeader Header; + unsigned char Location; + unsigned char Use; + unsigned char MemoryErrorCorrection; + unsigned int MaximumCapacity; + unsigned short MemoryErrorInformationHandle; + unsigned short NumberOfMemoryDevices; + + const char *GetString(unsigned char Index) + { + char *Str = (char *)((unsigned long)this + this->Header.Length); + Index--; + if (Index == 0 || Index > 10) + return Str; + for (unsigned char i = 0; i < Index; i++) + Str = SMBIOSNextString(Str); + return Str; + } + }; + + bool CheckSMBIOS(); + SMBIOSEntryPoint *GetSMBIOSEntryPoint(); + void *GetSMBIOSHeader(SMBIOSType Type); + SMBIOSBIOSInformation *GetBIOSInformation(); + SMBIOSSystemInformation *GetSystemInformation(); + SMBIOSBaseBoardInformation *GetBaseBoardInformation(); + SMBIOSProcessorInformation *GetProcessorInformation(); + SMBIOSMemoryArray *GetMemoryArray(); + SMBIOSMemoryDevice *GetMemoryDevice(); + SMBIOSMemoryArrayMappedAddress *GetMemoryArrayMappedAddress(); + SMBIOSMemoryDeviceMappedAddress *GetMemoryDeviceMappedAddress(); +} + +#endif // !__FENNIX_KERNEL_SMBIOS_H__ diff --git a/Kernel/Core/ubsan.h b/Kernel/Core/ubsan.h new file mode 100644 index 00000000..cb3e622a --- /dev/null +++ b/Kernel/Core/ubsan.h @@ -0,0 +1,111 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_KERNEL_UBSAN_H__ +#define __FENNIX_KERNEL_UBSAN_H__ + +#include + +struct source_location +{ + const char *file; + uint32_t line; + uint32_t column; +}; + +struct type_descriptor +{ + uint16_t kind; + uint16_t info; + char name[]; +}; + +struct type_mismatch_v1_data +{ + struct source_location location; + struct type_descriptor *type; + uint8_t alignment; + uint8_t type_check_kind; +}; + +struct out_of_bounds_info +{ + struct source_location location; + struct type_descriptor left_type; + struct type_descriptor right_type; +}; + +struct overflow_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct negative_vla_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct invalid_value_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct nonnull_return_data +{ + struct source_location location; +}; + +struct nonnull_arg_data +{ + struct source_location location; +}; + +struct unreachable_data +{ + struct source_location location; +}; + +struct invalid_builtin_data +{ + struct source_location location; + uint8_t kind; +}; + +struct array_out_of_bounds_data +{ + struct source_location location; + struct type_descriptor *array_type; + struct type_descriptor *index_type; +}; + +struct shift_out_of_bounds_data +{ + struct source_location location; + struct type_descriptor *left_type; + struct type_descriptor *right_type; +}; + +struct dynamic_type_cache_miss_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +#endif // !__FENNIX_KERNEL_UBSAN_H__ diff --git a/Kernel/DAPI.hpp b/Kernel/DAPI.hpp new file mode 100644 index 00000000..b218fc7c --- /dev/null +++ b/Kernel/DAPI.hpp @@ -0,0 +1,431 @@ +/* + BSD 3-Clause License + + Copyright (c) 2023, EnderIce2 + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __FENNIX_DRIVER_API_H__ +#define __FENNIX_DRIVER_API_H__ + +/** + * @brief The driver API is a set of functions that the kernel provides to the drivers. + * + * - The driver is responsible for the memory management. + * - The kernel will NOT free any memory allocated by the driver. On @see StopReason the driver must free all the memory it allocated and disable the hardware it uses. + * - The driver image will be freed after the driver is unloaded. + * - The kernel will unbind the interrupt handlers and the process handlers. + * - Kernel API will be freed after the driver is unloaded. + * + */ + +enum DriverReturnCode +{ + ERROR, + OK, + NOT_IMPLEMENTED, + NOT_FOUND, + NOT_READY, + NOT_AVAILABLE, + NOT_AUTHORIZED, + NOT_VALID, + NOT_ACCEPTED, + INVALID_PCI_BAR, + INVALID_KERNEL_API, + INVALID_MEMORY_ALLOCATION, + INVALID_DATA, + DEVICE_NOT_SUPPORTED, + SYSTEM_NOT_SUPPORTED, + KERNEL_API_VERSION_NOT_SUPPORTED, +}; + +enum DriverBindType +{ + BIND_NULL, + BIND_INTERRUPT, + BIND_PROCESS, + BIND_PCI, + BIND_INPUT +}; + +struct KernelAPI +{ + struct KAPIVersion + { + int Major; + int Minor; + int Patch; + } Version; + + struct KAPIInfo + { + unsigned long Offset; + unsigned long DriverUID; + char KernelDebug; + } Info; + + struct KAPIMemory + { + unsigned long PageSize; + void *(*RequestPage)(unsigned long Size); + void (*FreePage)(void *Page, unsigned long Size); + void (*Map)(void *VirtualAddress, void *PhysicalAddress, unsigned long Flags); + void (*Unmap)(void *VirtualAddress); + } Memory; + + struct KAPIPCI + { + char *(*GetDeviceName)(unsigned int VendorID, unsigned int DeviceID); + } PCI; + + struct KAPIUtilities + { + void (*DebugPrint)(char *String, unsigned long DriverUID); + void (*DisplayPrint)(char *Value); + void *(*memcpy)(void *Destination, void *Source, unsigned long Size); + void *(*memset)(void *Destination, int Value, unsigned long Size); + void (*Sleep)(unsigned long Milliseconds); + int (*sprintf)(char *Buffer, const char *Format, ...); + } Util; + + struct KAPIDriverTalk + { + /** @brief Connects to the network manager */ + struct + { + void (*SendPacket)(unsigned int DriverID, unsigned char *Data, unsigned short Size); + void (*ReceivePacket)(unsigned int DriverID, unsigned char *Data, unsigned short Size); + } Network; + + /** @brief Connects to the disk manager */ + struct + { + struct + { + void (*ReadSector)(unsigned int DriverID, unsigned long Sector, unsigned char *Data, unsigned int SectorCount, unsigned char Port); + void (*WriteSector)(unsigned int DriverID, unsigned long Sector, unsigned char *Data, unsigned int SectorCount, unsigned char Port); + } AHCI; + } Disk; + } Command; + + struct KAPIDisplay + { + unsigned int (*GetWidth)(void); + unsigned int (*GetHeight)(void); + /* TODO: Add more */ + } Display; +} __attribute__((packed)); + +enum CallbackReason +{ + /** + * @brief This is used to detect memory corruption, not used. + */ + UnknownReason, + + /** + * @brief This is called once the kernel is ready to use the driver and call @see ConfigurationReason . + */ + AcknowledgeReason, + + /** + * @brief This is used after the driver is loaded and the kernel is ready to use the driver. + * + * For PCI drivers, @see RawPtr will be the PCI device address. + */ + ConfigurationReason, + + /** + * @brief This is used when the kernel wants to stop the driver. + * + * The memory allocated by the driver will be freed automatically. + */ + StopReason, + + ProcessReason, + InputReason, + + /* Kernel reserved callbacks. */ + /* ------------------------------------------------------- */ + /* Driver callbacks for basic usage. */ + + /** + * @brief This is used when the kernel sends data. + * + * - Network + * - Packet + * - Audio + * - PCM Data + */ + SendReason, + + /** + * @brief This is used when the kernel wants to receive data. + * Currently not used. + */ + ReceiveReason, + + /** + * @brief This is used to adjust driver settings. + * + * - Audio + * - Volume + * - PCM Encoding + */ + AdjustReason, + + /** + * @brief This is used when the kernel wants to fetch information about the driver. + * + * - Input + * - Mouse + * - Position + * - Buttons + * - Keyboard + * - Key + */ + FetchReason, +}; + +union KernelCallback +{ + struct + { + CallbackReason Reason; + void *RawPtr; + unsigned long RawData; + + /** @brief When the kernel wants to send a packet. */ + struct + { + struct + { + unsigned char *Data; + unsigned long Length; + } Send; + + struct + { + char Name[128]; + unsigned long MAC; + } Fetch; + } NetworkCallback; + + /** @brief When the kernel wants to write to disk. */ + struct + { + struct + { + unsigned long Sector; + unsigned long SectorCount; + unsigned char Port; + unsigned char *Buffer; + bool Write; + } RW; + + struct + { + unsigned char Ports; + int BytesPerSector; + } Fetch; + } DiskCallback; + + /** @brief When the kernel wants to get mouse position / keyboard key */ + struct + { + struct + { + unsigned long X; + unsigned long Y; + unsigned long Z; + struct + { + bool Left; + bool Right; + bool Middle; + } Buttons; + } Mouse; + } InputCallback; + + struct + { + struct + { + bool _Volume; + bool _Encoding; + bool _SampleRate; + bool _Channels; + + /** + * @brief Adjust the volume. + * + * 0 - 100 + */ + unsigned char Volume; + + /** + * @brief Adjust the encoding. + * + * 0 - None, use default + * + * 1 - Signed PCM 8-bit + * 2 - Unsigned PCM 8-bit + * + * 3 - Signed PCM 16-bit Little Endian + * 4 - Signed PCM 20-bit Little Endian + * 5 - Signed PCM 24-bit Little Endian + * 6 - Signed PCM 32-bit Little Endian + * + * 7 - Unsigned PCM 16-bit Little Endian + * 8 - Unsigned PCM 20-bit Little Endian + * 9 - Unsigned PCM 24-bit Little Endian + * 10 - Unsigned PCM 32-bit Little Endian + * + * 11 - Signed PCM 16-bit Big Endian + * 12 - Signed PCM 20-bit Big Endian + * 13 - Signed PCM 24-bit Big Endian + * 14 - Signed PCM 32-bit Big Endian + * + * 15 - Unsigned PCM 16-bit Big Endian + * 16 - Unsigned PCM 20-bit Big Endian + * 17 - Unsigned PCM 24-bit Big Endian + * 18 - Unsigned PCM 32-bit Big Endian + * + * 19 - Float PCM 32-bit Little Endian + * 20 - Float PCM 64-bit Little Endian + * + * 21 - Float PCM 32-bit Big Endian + * 22 - Float PCM 64-bit Big Endian + * + * 23 - PCM A-law + * 24 - PCM Mu-law + * + * ... - More + */ + unsigned short Encoding; + + /** + * @brief Adjust the sample rate. + * + * 0 - 8000 Hz + * 1 - 11025 Hz + * 2 - 16000 Hz + * 3 - 22050 Hz + * 4 - 32000 Hz + * 5 - 44100 Hz + * 6 - 48000 Hz + * 7 - 88200 Hz + * 8 - 96000 Hz + */ + unsigned char SampleRate; + + /** + * @brief Adjust the channels. + * + * 0 - Mono + * 1 - Stereo + */ + unsigned char Channels; + } Adjust; + + struct + { + unsigned char *Data; + unsigned long Length; + } Send; + + struct + { + unsigned char Volume; + unsigned short Encoding; + unsigned char SampleRate; + unsigned char Channels; + } Fetch; + } AudioCallback; + + struct + { + unsigned char Vector; + } InterruptInfo; + }; + unsigned long raw; +} __attribute__((packed)); + +union CPURegisters +{ + struct + { +#if defined(__x86_64__) || defined(__amd64__) + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + + unsigned long rbp; + unsigned long rdi; + unsigned long rsi; + unsigned long rdx; + unsigned long rcx; + unsigned long rbx; + unsigned long rax; + + unsigned long InterruptNumber; + unsigned long ErrorCode; + unsigned long rip; + unsigned long cs; + unsigned long rflags; + unsigned long rsp; + unsigned long ss; +#elif defined(__i386__) + unsigned int ebp; + unsigned int edi; + unsigned int esi; + unsigned int edx; + unsigned int ecx; + unsigned int ebx; + unsigned int eax; + + unsigned int InterruptNumber; + unsigned int ErrorCode; + unsigned int eip; + unsigned int cs; + unsigned int eflags; + unsigned int esp; + unsigned int ss; +#else +#warning "Unsupported architecture" +#endif + }; + unsigned long raw; +} __attribute__((packed)); + +#endif // !__FENNIX_DRIVER_API_H__ diff --git a/Kernel/Doxyfile b/Kernel/Doxyfile new file mode 100644 index 00000000..a514614a --- /dev/null +++ b/Kernel/Doxyfile @@ -0,0 +1,2659 @@ +# Doxyfile 1.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Fennix Kernel" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 1.0.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Opeating System from scratch made in C and C++" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = YES + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = YES + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = YES + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = YES + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = Kernel tools/Doxygen_README.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice \ + Makefile \ + *.asm \ + *.s + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = .vscode .github + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = tools/Doxygen_README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = doxygen-doc/kernel + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = tools/doxygen-awesome.css tools/custom.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = YES + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /