From 83d870e58457a1d335a1d9b9966a6a1887cc297b Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Mon, 24 Nov 2025 03:02:43 +0100 Subject: feat! breaking changes on kernel sources. Signed-off-by: Amlal El Mahrouss --- src/kernel/ArchKit/ArchKit.h | 108 ++ src/kernel/CFKit/GUIDWizard.h | 21 + src/kernel/CFKit/GUIDWrapper.h | 48 + src/kernel/CFKit/Property.h | 51 + src/kernel/CFKit/Utils.h | 48 + src/kernel/CompilerKit/CompilerKit.h | 13 + src/kernel/CompilerKit/Detail.h | 27 + src/kernel/CompilerKit/Version.h | 9 + src/kernel/DmaKit/DmaPool.h | 101 ++ src/kernel/FSKit/Defines.h | 12 + src/kernel/FSKit/Ext2+IFS.h | 273 ++++ src/kernel/FSKit/Ext2.h | 148 ++ src/kernel/FSKit/IndexableProperty.h | 58 + src/kernel/FSKit/NeFS.h | 413 ++++++ src/kernel/FSKit/OpenHeFS.h | 434 ++++++ src/kernel/FirmwareKit/.gitkeep | 1 + src/kernel/FirmwareKit/EFI.h | 12 + src/kernel/FirmwareKit/EFI/API.h | 88 ++ src/kernel/FirmwareKit/EFI/EFI.h | 916 ++++++++++++ src/kernel/FirmwareKit/EFI/NS.h | 18 + src/kernel/FirmwareKit/EPM.h | 112 ++ src/kernel/FirmwareKit/GPT.h | 48 + src/kernel/FirmwareKit/Handover.h | 108 ++ src/kernel/FirmwareKit/NeBoot/BootNet.h | 41 + src/kernel/FirmwareKit/NeBoot/NS.h | 10 + src/kernel/FirmwareKit/NeBoot/NeBoot.h | 34 + src/kernel/FirmwareKit/VEPM.h | 47 + src/kernel/GfxKit/FB.h | 52 + src/kernel/HALKit/.gitkeep | 0 src/kernel/HALKit/AMD64/CPUID.h | 89 ++ src/kernel/HALKit/AMD64/CxxAbi.cc | 79 + src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc | 113 ++ src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc | 39 + src/kernel/HALKit/AMD64/HalApplicationProcessor.cc | 221 +++ .../HALKit/AMD64/HalApplicationProcessorStartup.s | 23 + src/kernel/HALKit/AMD64/HalCommonAPI.asm | 137 ++ src/kernel/HALKit/AMD64/HalControlRegisterAPI.s | 45 + src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc | 168 +++ src/kernel/HALKit/AMD64/HalCoreSystemCalls.cc | 9 + src/kernel/HALKit/AMD64/HalDebugOutput.cc | 227 +++ src/kernel/HALKit/AMD64/HalDebugProtocol.cc | 16 + src/kernel/HALKit/AMD64/HalDescriptorLoader.cc | 71 + src/kernel/HALKit/AMD64/HalHandoverStub.asm | 30 + src/kernel/HALKit/AMD64/HalInterruptAPI.asm | 370 +++++ src/kernel/HALKit/AMD64/HalKernelMain.cc | 160 ++ src/kernel/HALKit/AMD64/HalKernelPanic.cc | 58 + src/kernel/HALKit/AMD64/HalPagingMgr.cc | 167 +++ src/kernel/HALKit/AMD64/HalProcessor.cc | 89 ++ src/kernel/HALKit/AMD64/HalRoutineWait.s | 11 + .../HALKit/AMD64/HalSchedulerCorePrimitives.cc | 51 + src/kernel/HALKit/AMD64/HalTimer.cc | 101 ++ src/kernel/HALKit/AMD64/HalUtilsAPI.asm | 24 + src/kernel/HALKit/AMD64/Hypervisor.h | 24 + .../HALKit/AMD64/Network/Generic+Basic+RTL8139.cc | 129 ++ src/kernel/HALKit/AMD64/PCI/DMA.cc | 72 + src/kernel/HALKit/AMD64/PCI/Database.cc | 9 + src/kernel/HALKit/AMD64/PCI/Device.cc | 142 ++ src/kernel/HALKit/AMD64/PCI/Express.cc | 9 + src/kernel/HALKit/AMD64/PCI/IO.cc | 7 + src/kernel/HALKit/AMD64/PCI/Iterator.cc | 30 + src/kernel/HALKit/AMD64/PCI/PCI.cc | 7 + src/kernel/HALKit/AMD64/Paging.h | 91 ++ src/kernel/HALKit/AMD64/Processor.h | 283 ++++ src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc | 600 ++++++++ src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc | 199 +++ src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc | 9 + src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc | 278 ++++ src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc | 13 + src/kernel/HALKit/ARM64/APM/APM+IO.cc | 35 + src/kernel/HALKit/ARM64/ApplicationProcessor.h | 19 + src/kernel/HALKit/ARM64/CxxAbi.cc | 87 ++ src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc | 26 + src/kernel/HALKit/ARM64/HalApplicationProcessor.cc | 140 ++ .../HALKit/ARM64/HalApplicationProcessorStartup.s | 12 + src/kernel/HALKit/ARM64/HalCommonAPI.s | 9 + src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc | 159 ++ src/kernel/HALKit/ARM64/HalDebugOutput.cc | 71 + src/kernel/HALKit/ARM64/HalHandoverStub.s | 19 + src/kernel/HALKit/ARM64/HalInterruptAPI.s | 3 + src/kernel/HALKit/ARM64/HalKernelMain.cc | 63 + src/kernel/HALKit/ARM64/HalKernelPanic.cc | 54 + src/kernel/HALKit/ARM64/HalPagingMgr.cc | 86 ++ src/kernel/HALKit/ARM64/HalSchedulerCore.cc | 21 + .../HALKit/ARM64/HalSchedulerCorePrimitives.cc | 30 + src/kernel/HALKit/ARM64/HalTimer.cc | 15 + src/kernel/HALKit/ARM64/Paging.h | 107 ++ src/kernel/HALKit/ARM64/Processor.h | 78 + src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc | 13 + src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc | 8 + src/kernel/HALKit/POWER/.gitkeep | 0 src/kernel/HALKit/POWER/AP.h | 39 + src/kernel/HALKit/POWER/APM/.gitkeep | 0 src/kernel/HALKit/POWER/HalApplicationProcessor.cc | 43 + src/kernel/HALKit/POWER/HalDebugOutput.cc | 24 + src/kernel/HALKit/POWER/HalHardwareThread.cc | 8 + src/kernel/HALKit/POWER/HalStartSequence.s | 14 + src/kernel/HALKit/POWER/HalVirtualMemory.cc | 46 + src/kernel/HALKit/POWER/Processor.h | 60 + src/kernel/HALKit/RISCV/.keep | 0 src/kernel/HALKit/RISCV/AP.h | 35 + src/kernel/HALKit/RISCV/APM/.gitkeep | 0 src/kernel/HALKit/RISCV/HalApplicationProcessor.cc | 40 + src/kernel/HALKit/RISCV/Storage/.gitkeep | 0 src/kernel/HALKit/X86S/.gitkeep | 0 src/kernel/HALKit/X86S/ACPI/.gitkeep | 0 src/kernel/HALKit/X86S/Storage/.gitkeep | 0 src/kernel/KernelKit/BinaryMutex.h | 39 + src/kernel/KernelKit/CodeMgr.h | 49 + src/kernel/KernelKit/CoreProcessScheduler.h | 273 ++++ src/kernel/KernelKit/DebugOutput.h | 207 +++ src/kernel/KernelKit/Defines.h | 19 + src/kernel/KernelKit/DeviceMgr.h | 129 ++ src/kernel/KernelKit/DriveMgr.h | 175 +++ src/kernel/KernelKit/FileMgr.h | 445 ++++++ src/kernel/KernelKit/HardwareThreadScheduler.h | 135 ++ src/kernel/KernelKit/HeapMgr.h | 58 + src/kernel/KernelKit/HeapMgr.inl | 35 + src/kernel/KernelKit/IDylibObject.h | 45 + src/kernel/KernelKit/IFS.h | 25 + src/kernel/KernelKit/ILoader.h | 32 + src/kernel/KernelKit/IPEFDylibObject.h | 86 ++ src/kernel/KernelKit/KPC.h | 78 + src/kernel/KernelKit/KernelTaskScheduler.h | 47 + src/kernel/KernelKit/LockDelegate.h | 58 + src/kernel/KernelKit/MSDOS.h | 51 + src/kernel/KernelKit/PCI/DMA.h | 75 + src/kernel/KernelKit/PCI/DMA.inl | 17 + src/kernel/KernelKit/PCI/Database.h | 51 + src/kernel/KernelKit/PCI/Device.h | 73 + src/kernel/KernelKit/PCI/Express.h | 12 + src/kernel/KernelKit/PCI/IO.h | 63 + src/kernel/KernelKit/PCI/IOArray+AMD64.inl | 49 + src/kernel/KernelKit/PCI/Iterator.h | 41 + src/kernel/KernelKit/PCI/PCI.h | 54 + src/kernel/KernelKit/PE.h | 130 ++ src/kernel/KernelKit/PE32CodeMgr.h | 91 ++ src/kernel/KernelKit/PEF.h | 117 ++ src/kernel/KernelKit/PEFCodeMgr.h | 75 + src/kernel/KernelKit/ProcessScheduler.h | 18 + src/kernel/KernelKit/Semaphore.h | 110 ++ src/kernel/KernelKit/ThreadLocalStorage.h | 68 + src/kernel/KernelKit/ThreadLocalStorage.inl | 89 ++ src/kernel/KernelKit/Timer.h | 75 + src/kernel/KernelKit/TraceSrv.h | 22 + src/kernel/KernelKit/UserMgr.h | 95 ++ src/kernel/KernelKit/UserProcessScheduler.h | 243 +++ src/kernel/KernelKit/UserProcessScheduler.inl | 64 + src/kernel/KernelKit/XCOFF.h | 51 + src/kernel/KernelKit/ZXD.h | 53 + src/kernel/NeKit/Array.h | 46 + src/kernel/NeKit/ArrayList.h | 44 + src/kernel/NeKit/Atom.h | 33 + src/kernel/NeKit/Crc32.h | 20 + src/kernel/NeKit/CxxAbi.h | 26 + src/kernel/NeKit/Defines.h | 179 +++ src/kernel/NeKit/ErrorOr.h | 62 + src/kernel/NeKit/Function.h | 51 + src/kernel/NeKit/Json.h | 146 ++ src/kernel/NeKit/KString.h | 92 ++ src/kernel/NeKit/KString.inl | 174 +++ src/kernel/NeKit/KernelPanic.h | 69 + src/kernel/NeKit/Macros.h | 151 ++ src/kernel/NeKit/MutableArray.h | 203 +++ src/kernel/NeKit/NeKit.h | 20 + src/kernel/NeKit/New.h | 20 + src/kernel/NeKit/OwnPtr.h | 67 + src/kernel/NeKit/PageMgr.h | 76 + src/kernel/NeKit/Pair.h | 51 + src/kernel/NeKit/Pmm.h | 39 + src/kernel/NeKit/Ref.h | 77 + src/kernel/NeKit/Stream.h | 45 + src/kernel/NeKit/TOML.h | 15 + src/kernel/NeKit/Utils.h | 62 + src/kernel/NeKit/Variant.h | 71 + src/kernel/NetworkKit/IP.h | 76 + src/kernel/NetworkKit/IPC.h | 90 ++ src/kernel/NetworkKit/LTE.h | 16 + src/kernel/NetworkKit/MAC.h | 34 + src/kernel/NetworkKit/NetworkDevice.h | 83 ++ src/kernel/NetworkKit/NetworkDevice.inl | 32 + src/kernel/SignalKit/Signals.h | 51 + src/kernel/StorageKit/AHCI.h | 49 + src/kernel/StorageKit/ATA.h | 56 + src/kernel/StorageKit/NVME.h | 32 + src/kernel/StorageKit/PRDT.h | 33 + src/kernel/StorageKit/SCSI.h | 11 + src/kernel/StorageKit/StorageKit.h | 21 + src/kernel/SwapKit/DiskSwap.h | 72 + src/kernel/amd64-ci.make | 77 + src/kernel/amd64-desktop.make | 79 + src/kernel/arm64-desktop.make | 64 + src/kernel/kernel_rsrc.rsrc | 25 + src/kernel/move-all-aarch64.sh | 7 + src/kernel/move-all-x64.sh | 7 + src/kernel/obj/.gitkeep | 0 src/kernel/power64-cb.make | 4 + src/kernel/riscv64-cb.make | 0 src/kernel/src/ACPIFactoryInterface.cc | 88 ++ src/kernel/src/Array.cc | 7 + src/kernel/src/ArrayList.cc | 7 + src/kernel/src/AsciiUtils.cc | 161 ++ src/kernel/src/Atom.cc | 10 + src/kernel/src/BinaryMutex.cc | 70 + src/kernel/src/BitMapMgr.cc | 206 +++ src/kernel/src/CRuntimeOverrides.cc | 27 + src/kernel/src/CodeMgr.cc | 35 + src/kernel/src/Crc32.cc | 65 + src/kernel/src/Defines.cc | 7 + src/kernel/src/DeviceMgr.cc | 9 + src/kernel/src/DriveMgr.cc | 254 ++++ src/kernel/src/ErrorOr.cc | 12 + src/kernel/src/FS/Ext2+IFS.cc | 1555 ++++++++++++++++++++ src/kernel/src/FS/NeFS+FileMgr.cc | 276 ++++ src/kernel/src/FS/NeFS+FileSystemParser.cc | 870 +++++++++++ src/kernel/src/FS/OpenHeFS+FileMgr.cc | 191 +++ src/kernel/src/FS/OpenHeFS+FileSystemParser.cc | 1160 +++++++++++++++ src/kernel/src/FileMgr.cc | 49 + src/kernel/src/GUIDWizard.cc | 67 + src/kernel/src/GUIDWrapper.cc | 9 + src/kernel/src/Gfx/FBDeviceInterface.cc | 50 + src/kernel/src/HardwareThreadScheduler.cc | 184 +++ src/kernel/src/HeapMgr.cc | 260 ++++ src/kernel/src/IDylibObject.cc | 13 + src/kernel/src/IFS.cc | 89 ++ src/kernel/src/IPEFDylibObject.cc | 109 ++ src/kernel/src/IndexableProperty.cc | 45 + src/kernel/src/Json.cc | 9 + src/kernel/src/KPC.cc | 39 + src/kernel/src/KernelTaskScheduler.cc | 37 + src/kernel/src/LockDelegate.cc | 11 + src/kernel/src/MutableArray.cc | 7 + src/kernel/src/Network/IPAddress.cc | 113 ++ src/kernel/src/Network/IPCAddress.cc | 27 + src/kernel/src/Network/IPCMessage.cc | 129 ++ src/kernel/src/Network/MACAddressGetter.cc | 13 + src/kernel/src/Network/NetworkDevice.cc | 29 + src/kernel/src/New+Delete.cc | 48 + src/kernel/src/OwnPtr.cc | 7 + src/kernel/src/PE32CodeMgr.cc | 258 ++++ src/kernel/src/PEFCodeMgr.cc | 335 +++++ src/kernel/src/PRDT.cc | 22 + src/kernel/src/PageMgr.cc | 95 ++ src/kernel/src/Pmm.cc | 83 ++ src/kernel/src/Property.cc | 41 + src/kernel/src/Ref.cc | 7 + src/kernel/src/Semaphore.cc | 7 + src/kernel/src/SoftwareTimer.cc | 36 + src/kernel/src/Storage/AHCIDeviceInterface.cc | 87 ++ src/kernel/src/Storage/ATADeviceInterface.cc | 96 ++ src/kernel/src/Storage/NVMEDeviceInterface.cc | 22 + src/kernel/src/Storage/SCSIDeviceInterface.cc | 9 + src/kernel/src/Stream.cc | 12 + src/kernel/src/Swap/DiskSwap.cc | 64 + src/kernel/src/ThreadLocalStorage.cc | 52 + src/kernel/src/Timer.cc | 19 + src/kernel/src/UserMgr.cc | 131 ++ src/kernel/src/UserProcessScheduler.cc | 690 +++++++++ src/kernel/src/UserProcessTeam.cc | 57 + src/kernel/src/UtfUtils.cc | 62 + src/kernel/src/Variant.cc | 38 + src/kernel/src/ZXD.cc | 7 + 261 files changed, 23879 insertions(+) create mode 100644 src/kernel/ArchKit/ArchKit.h create mode 100644 src/kernel/CFKit/GUIDWizard.h create mode 100644 src/kernel/CFKit/GUIDWrapper.h create mode 100644 src/kernel/CFKit/Property.h create mode 100644 src/kernel/CFKit/Utils.h create mode 100644 src/kernel/CompilerKit/CompilerKit.h create mode 100644 src/kernel/CompilerKit/Detail.h create mode 100644 src/kernel/CompilerKit/Version.h create mode 100644 src/kernel/DmaKit/DmaPool.h create mode 100644 src/kernel/FSKit/Defines.h create mode 100644 src/kernel/FSKit/Ext2+IFS.h create mode 100644 src/kernel/FSKit/Ext2.h create mode 100644 src/kernel/FSKit/IndexableProperty.h create mode 100644 src/kernel/FSKit/NeFS.h create mode 100644 src/kernel/FSKit/OpenHeFS.h create mode 100644 src/kernel/FirmwareKit/.gitkeep create mode 100644 src/kernel/FirmwareKit/EFI.h create mode 100644 src/kernel/FirmwareKit/EFI/API.h create mode 100644 src/kernel/FirmwareKit/EFI/EFI.h create mode 100644 src/kernel/FirmwareKit/EFI/NS.h create mode 100644 src/kernel/FirmwareKit/EPM.h create mode 100644 src/kernel/FirmwareKit/GPT.h create mode 100644 src/kernel/FirmwareKit/Handover.h create mode 100644 src/kernel/FirmwareKit/NeBoot/BootNet.h create mode 100644 src/kernel/FirmwareKit/NeBoot/NS.h create mode 100644 src/kernel/FirmwareKit/NeBoot/NeBoot.h create mode 100644 src/kernel/FirmwareKit/VEPM.h create mode 100644 src/kernel/GfxKit/FB.h create mode 100644 src/kernel/HALKit/.gitkeep create mode 100644 src/kernel/HALKit/AMD64/CPUID.h create mode 100644 src/kernel/HALKit/AMD64/CxxAbi.cc create mode 100644 src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc create mode 100644 src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc create mode 100644 src/kernel/HALKit/AMD64/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/AMD64/HalApplicationProcessorStartup.s create mode 100644 src/kernel/HALKit/AMD64/HalCommonAPI.asm create mode 100644 src/kernel/HALKit/AMD64/HalControlRegisterAPI.s create mode 100644 src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc create mode 100644 src/kernel/HALKit/AMD64/HalCoreSystemCalls.cc create mode 100644 src/kernel/HALKit/AMD64/HalDebugOutput.cc create mode 100644 src/kernel/HALKit/AMD64/HalDebugProtocol.cc create mode 100644 src/kernel/HALKit/AMD64/HalDescriptorLoader.cc create mode 100644 src/kernel/HALKit/AMD64/HalHandoverStub.asm create mode 100644 src/kernel/HALKit/AMD64/HalInterruptAPI.asm create mode 100644 src/kernel/HALKit/AMD64/HalKernelMain.cc create mode 100644 src/kernel/HALKit/AMD64/HalKernelPanic.cc create mode 100644 src/kernel/HALKit/AMD64/HalPagingMgr.cc create mode 100644 src/kernel/HALKit/AMD64/HalProcessor.cc create mode 100644 src/kernel/HALKit/AMD64/HalRoutineWait.s create mode 100644 src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc create mode 100644 src/kernel/HALKit/AMD64/HalTimer.cc create mode 100644 src/kernel/HALKit/AMD64/HalUtilsAPI.asm create mode 100644 src/kernel/HALKit/AMD64/Hypervisor.h create mode 100644 src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/DMA.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Database.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Device.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Express.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/IO.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Iterator.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/PCI.cc create mode 100644 src/kernel/HALKit/AMD64/Paging.h create mode 100644 src/kernel/HALKit/AMD64/Processor.h create mode 100644 src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc create mode 100644 src/kernel/HALKit/ARM64/APM/APM+IO.cc create mode 100644 src/kernel/HALKit/ARM64/ApplicationProcessor.h create mode 100644 src/kernel/HALKit/ARM64/CxxAbi.cc create mode 100644 src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc create mode 100644 src/kernel/HALKit/ARM64/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/ARM64/HalApplicationProcessorStartup.s create mode 100644 src/kernel/HALKit/ARM64/HalCommonAPI.s create mode 100644 src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc create mode 100644 src/kernel/HALKit/ARM64/HalDebugOutput.cc create mode 100644 src/kernel/HALKit/ARM64/HalHandoverStub.s create mode 100644 src/kernel/HALKit/ARM64/HalInterruptAPI.s create mode 100644 src/kernel/HALKit/ARM64/HalKernelMain.cc create mode 100644 src/kernel/HALKit/ARM64/HalKernelPanic.cc create mode 100644 src/kernel/HALKit/ARM64/HalPagingMgr.cc create mode 100644 src/kernel/HALKit/ARM64/HalSchedulerCore.cc create mode 100644 src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cc create mode 100644 src/kernel/HALKit/ARM64/HalTimer.cc create mode 100644 src/kernel/HALKit/ARM64/Paging.h create mode 100644 src/kernel/HALKit/ARM64/Processor.h create mode 100644 src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc create mode 100644 src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc create mode 100644 src/kernel/HALKit/POWER/.gitkeep create mode 100644 src/kernel/HALKit/POWER/AP.h create mode 100644 src/kernel/HALKit/POWER/APM/.gitkeep create mode 100644 src/kernel/HALKit/POWER/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/POWER/HalDebugOutput.cc create mode 100644 src/kernel/HALKit/POWER/HalHardwareThread.cc create mode 100644 src/kernel/HALKit/POWER/HalStartSequence.s create mode 100644 src/kernel/HALKit/POWER/HalVirtualMemory.cc create mode 100644 src/kernel/HALKit/POWER/Processor.h create mode 100644 src/kernel/HALKit/RISCV/.keep create mode 100644 src/kernel/HALKit/RISCV/AP.h create mode 100644 src/kernel/HALKit/RISCV/APM/.gitkeep create mode 100644 src/kernel/HALKit/RISCV/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/RISCV/Storage/.gitkeep create mode 100644 src/kernel/HALKit/X86S/.gitkeep create mode 100644 src/kernel/HALKit/X86S/ACPI/.gitkeep create mode 100644 src/kernel/HALKit/X86S/Storage/.gitkeep create mode 100644 src/kernel/KernelKit/BinaryMutex.h create mode 100644 src/kernel/KernelKit/CodeMgr.h create mode 100644 src/kernel/KernelKit/CoreProcessScheduler.h create mode 100644 src/kernel/KernelKit/DebugOutput.h create mode 100644 src/kernel/KernelKit/Defines.h create mode 100644 src/kernel/KernelKit/DeviceMgr.h create mode 100644 src/kernel/KernelKit/DriveMgr.h create mode 100644 src/kernel/KernelKit/FileMgr.h create mode 100644 src/kernel/KernelKit/HardwareThreadScheduler.h create mode 100644 src/kernel/KernelKit/HeapMgr.h create mode 100644 src/kernel/KernelKit/HeapMgr.inl create mode 100644 src/kernel/KernelKit/IDylibObject.h create mode 100644 src/kernel/KernelKit/IFS.h create mode 100644 src/kernel/KernelKit/ILoader.h create mode 100644 src/kernel/KernelKit/IPEFDylibObject.h create mode 100644 src/kernel/KernelKit/KPC.h create mode 100644 src/kernel/KernelKit/KernelTaskScheduler.h create mode 100644 src/kernel/KernelKit/LockDelegate.h create mode 100644 src/kernel/KernelKit/MSDOS.h create mode 100644 src/kernel/KernelKit/PCI/DMA.h create mode 100644 src/kernel/KernelKit/PCI/DMA.inl create mode 100644 src/kernel/KernelKit/PCI/Database.h create mode 100644 src/kernel/KernelKit/PCI/Device.h create mode 100644 src/kernel/KernelKit/PCI/Express.h create mode 100644 src/kernel/KernelKit/PCI/IO.h create mode 100644 src/kernel/KernelKit/PCI/IOArray+AMD64.inl create mode 100644 src/kernel/KernelKit/PCI/Iterator.h create mode 100644 src/kernel/KernelKit/PCI/PCI.h create mode 100644 src/kernel/KernelKit/PE.h create mode 100644 src/kernel/KernelKit/PE32CodeMgr.h create mode 100644 src/kernel/KernelKit/PEF.h create mode 100644 src/kernel/KernelKit/PEFCodeMgr.h create mode 100644 src/kernel/KernelKit/ProcessScheduler.h create mode 100644 src/kernel/KernelKit/Semaphore.h create mode 100644 src/kernel/KernelKit/ThreadLocalStorage.h create mode 100644 src/kernel/KernelKit/ThreadLocalStorage.inl create mode 100644 src/kernel/KernelKit/Timer.h create mode 100644 src/kernel/KernelKit/TraceSrv.h create mode 100644 src/kernel/KernelKit/UserMgr.h create mode 100644 src/kernel/KernelKit/UserProcessScheduler.h create mode 100644 src/kernel/KernelKit/UserProcessScheduler.inl create mode 100644 src/kernel/KernelKit/XCOFF.h create mode 100644 src/kernel/KernelKit/ZXD.h create mode 100644 src/kernel/NeKit/Array.h create mode 100644 src/kernel/NeKit/ArrayList.h create mode 100644 src/kernel/NeKit/Atom.h create mode 100644 src/kernel/NeKit/Crc32.h create mode 100644 src/kernel/NeKit/CxxAbi.h create mode 100644 src/kernel/NeKit/Defines.h create mode 100644 src/kernel/NeKit/ErrorOr.h create mode 100644 src/kernel/NeKit/Function.h create mode 100644 src/kernel/NeKit/Json.h create mode 100644 src/kernel/NeKit/KString.h create mode 100644 src/kernel/NeKit/KString.inl create mode 100644 src/kernel/NeKit/KernelPanic.h create mode 100644 src/kernel/NeKit/Macros.h create mode 100644 src/kernel/NeKit/MutableArray.h create mode 100644 src/kernel/NeKit/NeKit.h create mode 100644 src/kernel/NeKit/New.h create mode 100644 src/kernel/NeKit/OwnPtr.h create mode 100644 src/kernel/NeKit/PageMgr.h create mode 100644 src/kernel/NeKit/Pair.h create mode 100644 src/kernel/NeKit/Pmm.h create mode 100644 src/kernel/NeKit/Ref.h create mode 100644 src/kernel/NeKit/Stream.h create mode 100644 src/kernel/NeKit/TOML.h create mode 100644 src/kernel/NeKit/Utils.h create mode 100644 src/kernel/NeKit/Variant.h create mode 100644 src/kernel/NetworkKit/IP.h create mode 100644 src/kernel/NetworkKit/IPC.h create mode 100644 src/kernel/NetworkKit/LTE.h create mode 100644 src/kernel/NetworkKit/MAC.h create mode 100644 src/kernel/NetworkKit/NetworkDevice.h create mode 100644 src/kernel/NetworkKit/NetworkDevice.inl create mode 100644 src/kernel/SignalKit/Signals.h create mode 100644 src/kernel/StorageKit/AHCI.h create mode 100644 src/kernel/StorageKit/ATA.h create mode 100644 src/kernel/StorageKit/NVME.h create mode 100644 src/kernel/StorageKit/PRDT.h create mode 100644 src/kernel/StorageKit/SCSI.h create mode 100644 src/kernel/StorageKit/StorageKit.h create mode 100644 src/kernel/SwapKit/DiskSwap.h create mode 100644 src/kernel/amd64-ci.make create mode 100644 src/kernel/amd64-desktop.make create mode 100644 src/kernel/arm64-desktop.make create mode 100644 src/kernel/kernel_rsrc.rsrc create mode 100755 src/kernel/move-all-aarch64.sh create mode 100755 src/kernel/move-all-x64.sh create mode 100644 src/kernel/obj/.gitkeep create mode 100644 src/kernel/power64-cb.make create mode 100644 src/kernel/riscv64-cb.make create mode 100644 src/kernel/src/ACPIFactoryInterface.cc create mode 100644 src/kernel/src/Array.cc create mode 100644 src/kernel/src/ArrayList.cc create mode 100644 src/kernel/src/AsciiUtils.cc create mode 100644 src/kernel/src/Atom.cc create mode 100644 src/kernel/src/BinaryMutex.cc create mode 100644 src/kernel/src/BitMapMgr.cc create mode 100644 src/kernel/src/CRuntimeOverrides.cc create mode 100644 src/kernel/src/CodeMgr.cc create mode 100644 src/kernel/src/Crc32.cc create mode 100644 src/kernel/src/Defines.cc create mode 100644 src/kernel/src/DeviceMgr.cc create mode 100644 src/kernel/src/DriveMgr.cc create mode 100644 src/kernel/src/ErrorOr.cc create mode 100644 src/kernel/src/FS/Ext2+IFS.cc create mode 100644 src/kernel/src/FS/NeFS+FileMgr.cc create mode 100644 src/kernel/src/FS/NeFS+FileSystemParser.cc create mode 100644 src/kernel/src/FS/OpenHeFS+FileMgr.cc create mode 100644 src/kernel/src/FS/OpenHeFS+FileSystemParser.cc create mode 100644 src/kernel/src/FileMgr.cc create mode 100644 src/kernel/src/GUIDWizard.cc create mode 100644 src/kernel/src/GUIDWrapper.cc create mode 100644 src/kernel/src/Gfx/FBDeviceInterface.cc create mode 100644 src/kernel/src/HardwareThreadScheduler.cc create mode 100644 src/kernel/src/HeapMgr.cc create mode 100644 src/kernel/src/IDylibObject.cc create mode 100644 src/kernel/src/IFS.cc create mode 100644 src/kernel/src/IPEFDylibObject.cc create mode 100644 src/kernel/src/IndexableProperty.cc create mode 100644 src/kernel/src/Json.cc create mode 100644 src/kernel/src/KPC.cc create mode 100644 src/kernel/src/KernelTaskScheduler.cc create mode 100644 src/kernel/src/LockDelegate.cc create mode 100644 src/kernel/src/MutableArray.cc create mode 100644 src/kernel/src/Network/IPAddress.cc create mode 100644 src/kernel/src/Network/IPCAddress.cc create mode 100644 src/kernel/src/Network/IPCMessage.cc create mode 100644 src/kernel/src/Network/MACAddressGetter.cc create mode 100644 src/kernel/src/Network/NetworkDevice.cc create mode 100644 src/kernel/src/New+Delete.cc create mode 100644 src/kernel/src/OwnPtr.cc create mode 100644 src/kernel/src/PE32CodeMgr.cc create mode 100644 src/kernel/src/PEFCodeMgr.cc create mode 100644 src/kernel/src/PRDT.cc create mode 100644 src/kernel/src/PageMgr.cc create mode 100644 src/kernel/src/Pmm.cc create mode 100644 src/kernel/src/Property.cc create mode 100644 src/kernel/src/Ref.cc create mode 100644 src/kernel/src/Semaphore.cc create mode 100644 src/kernel/src/SoftwareTimer.cc create mode 100644 src/kernel/src/Storage/AHCIDeviceInterface.cc create mode 100644 src/kernel/src/Storage/ATADeviceInterface.cc create mode 100644 src/kernel/src/Storage/NVMEDeviceInterface.cc create mode 100644 src/kernel/src/Storage/SCSIDeviceInterface.cc create mode 100644 src/kernel/src/Stream.cc create mode 100644 src/kernel/src/Swap/DiskSwap.cc create mode 100644 src/kernel/src/ThreadLocalStorage.cc create mode 100644 src/kernel/src/Timer.cc create mode 100644 src/kernel/src/UserMgr.cc create mode 100644 src/kernel/src/UserProcessScheduler.cc create mode 100644 src/kernel/src/UserProcessTeam.cc create mode 100644 src/kernel/src/UtfUtils.cc create mode 100644 src/kernel/src/Variant.cc create mode 100644 src/kernel/src/ZXD.cc (limited to 'src/kernel') diff --git a/src/kernel/ArchKit/ArchKit.h b/src/kernel/ArchKit/ArchKit.h new file mode 100644 index 00000000..626f1e0b --- /dev/null +++ b/src/kernel/ArchKit/ArchKit.h @@ -0,0 +1,108 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +#include + +#ifdef __NE_AMD64__ +#include +#include +#include +#elif defined(__NE_POWER64__) +#include +#elif defined(__NE_ARM64__) +#include +#elif defined(__NE_SDK__) +#include +#else +#error !!! unknown architecture !!! +#endif + +#define kMaxDispatchCallCount (512U) + +namespace Kernel { +inline SizeT rt_hash_seed(const Char* seed, UInt32 mul) { + SizeT hash = 0; + + for (SSizeT idx = 0; seed[idx] != 0; ++idx) { + hash += seed[idx]; + hash ^= mul; + } + + return hash; +} + +/// @brief write to mapped memory register +/// @param base the base address. +/// @param reg the register. +/// @param value the write to write on it. +template +inline Void ke_dma_write(UIntPtr base, DataKind reg, DataKind value) noexcept { + *(volatile DataKind*) (base + reg) = value; +} + +/// @brief read from mapped memory register. +/// @param base base address +/// @param reg the register. +/// @return the value inside the register. +template +inline UInt32 ke_dma_read(UIntPtr base, DataKind reg) noexcept { + return *(volatile DataKind*) (base + reg); +} + +/// @brief Hardware Abstraction Layer +namespace HAL { + /// @brief Check whether this pointer is a bitmap object. + /// @param ptr argument to verify. + /// @param whether successful or not. + auto mm_is_bitmap(VoidPtr ptr) -> Bool; +} // namespace HAL +} // namespace Kernel + +typedef Kernel::Void (*rt_syscall_proc)(Kernel::VoidPtr); + +/// @brief System Call Dispatch. +struct HAL_DISPATCH_ENTRY final { + Kernel::UInt64 fHash; + Kernel::Bool fHooked; + rt_syscall_proc fProc; + + BOOL IsKernCall() { return NO; } + BOOL IsSysCall() { return YES; } + + operator bool() { return fHooked; } +}; + +typedef Kernel::Void (*rt_kerncall_proc)(Kernel::SizeT, Kernel::VoidPtr, Kernel::SizeT); + +/// @brief Kernel Call Dispatch. +struct HAL_KERNEL_DISPATCH_ENTRY final { + Kernel::UInt64 fHash; + Kernel::Bool fHooked; + rt_kerncall_proc fProc; + + BOOL IsKernCall() { return YES; } + BOOL IsSysCall() { return NO; } + + operator bool() { return fHooked; } +}; + +inline Kernel::Array kSysCalls; + +inline Kernel::Array kKernCalls; + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + +inline Kernel::VoidPtr kKernelVM = nullptr; + +#endif // __NE_VIRTUAL_MEMORY_SUPPORT__ + +inline Kernel::SizeT kBitMapCursor = 0UL; diff --git a/src/kernel/CFKit/GUIDWizard.h b/src/kernel/CFKit/GUIDWizard.h new file mode 100644 index 00000000..8745a1f9 --- /dev/null +++ b/src/kernel/CFKit/GUIDWizard.h @@ -0,0 +1,21 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel::CF::XRN::Version1 { +Ref cf_make_sequence(const ArrayList& seq); +ErrorOr> cf_try_guid_to_string(Ref& guid); +} // namespace Kernel::CF::XRN::Version1 diff --git a/src/kernel/CFKit/GUIDWrapper.h b/src/kernel/CFKit/GUIDWrapper.h new file mode 100644 index 00000000..2bf7b64e --- /dev/null +++ b/src/kernel/CFKit/GUIDWrapper.h @@ -0,0 +1,48 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +/* GUID for C++ classes. */ + +#define kXRNNil "@{........-....-M...-N...-............}" + +/// @brief eXtended Resource Namespace +namespace Kernel::CF::XRN { +union GUIDSequence final { + alignas(8) UShort fU8[16]; + alignas(8) UShort fU16[8]; + alignas(8) UInt fU32[4]; + alignas(8) ULong fU64[2]; + + struct GUID { + alignas(8) UInt fMs1; + UShort fMs2; + UShort fMs3; + UChar fMs4[8]; + } fUuid; +}; + +class GUID final { + public: + explicit GUID() = default; + ~GUID() = default; + + public: + NE_COPY_DEFAULT(GUID) + + public: + GUIDSequence& operator->() noexcept { return fUUID; } + GUIDSequence& Leak() noexcept { return fUUID; } + + private: + GUIDSequence fUUID; +}; +} // namespace Kernel::CF::XRN diff --git a/src/kernel/CFKit/Property.h b/src/kernel/CFKit/Property.h new file mode 100644 index 00000000..cfb8a599 --- /dev/null +++ b/src/kernel/CFKit/Property.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef CFKIT_PROPS_H +#define CFKIT_PROPS_H + +#include +#include +#include +#include +#include + +#define kMaxPropLen (256U) + +namespace Kernel::CF { +/// @brief handle to anything (number, ptr, string...) +using PropertyId = UIntPtr; + +/// @brief Kernel property class. +/// @example /prop/smp_max or /prop/kern_ver +class Property { + public: + Property(); + virtual ~Property(); + + public: + Property& operator=(const Property&) = default; + Property(const Property&) = default; + + BOOL StringEquals(KBasicString<>& name); + PropertyId& GetValue(); + KBasicString<>& GetKey(); + + private: + KString fName{kMaxPropLen}; + PropertyId fValue{0UL}; + Ref fGUID{}; +}; + +template +using PropertyArray = Array; +} // namespace Kernel::CF + +namespace Kernel { +using namespace Kernel::CF; +} + +#endif // !CFKIT_PROPS_H diff --git a/src/kernel/CFKit/Utils.h b/src/kernel/CFKit/Utils.h new file mode 100644 index 00000000..247ad5fb --- /dev/null +++ b/src/kernel/CFKit/Utils.h @@ -0,0 +1,48 @@ +#ifndef CFKIT_UTILS_H +#define CFKIT_UTILS_H + +#include +#include + +/// @brief CFKit +namespace Kernel::CF { +/// @brief Finds the PE header inside the blob. +inline auto ldr_find_exec_header(DosHeaderPtr ptrDos) -> LDR_EXEC_HEADER_PTR { + if (!ptrDos) return nullptr; + + if (ptrDos->eMagic[0] != kMagMz0) return nullptr; + + if (ptrDos->eMagic[1] != kMagMz1) return nullptr; + +#ifdef __NE_AMD64__ + return (LDR_EXEC_HEADER_PTR) (VoidPtr) (&ptrDos->eLfanew + 1); +#else + return (LDR_EXEC_HEADER_PTR) (VoidPtr) (&ptrDos->eLfanew); +#endif +} + +/// @brief Finds the PE optional header inside the blob. +inline auto ldr_find_opt_exec_header(DosHeaderPtr ptrDos) -> LDR_OPTIONAL_HEADER_PTR { + if (!ptrDos) return nullptr; + + auto exec = ldr_find_exec_header(ptrDos); + + if (!exec) return nullptr; + + return (LDR_OPTIONAL_HEADER_PTR) (VoidPtr) (&exec->Characteristics + 1); +} + +/// @brief Finds the PE header inside the blob. +/// @note overloaded function. +inline auto ldr_find_exec_header(const Char* ptrDos) -> LDR_EXEC_HEADER_PTR { + return ldr_find_exec_header((DosHeaderPtr) ptrDos); +} + +/// @brief Finds the PE header inside the blob. +/// @note overloaded function. +inline auto ldr_find_opt_exec_header(const Char* ptrDos) -> LDR_OPTIONAL_HEADER_PTR { + return ldr_find_opt_exec_header((DosHeaderPtr) ptrDos); +} +} // namespace Kernel::CF + +#endif // ifndef CFKIT_UTILS_H diff --git a/src/kernel/CompilerKit/CompilerKit.h b/src/kernel/CompilerKit/CompilerKit.h new file mode 100644 index 00000000..523a4ecd --- /dev/null +++ b/src/kernel/CompilerKit/CompilerKit.h @@ -0,0 +1,13 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef _INC_CL_H +#define _INC_CL_H + +#include +#include + +#endif /* ifndef _INC_CL_H */ diff --git a/src/kernel/CompilerKit/Detail.h b/src/kernel/CompilerKit/Detail.h new file mode 100644 index 00000000..93e9b2dd --- /dev/null +++ b/src/kernel/CompilerKit/Detail.h @@ -0,0 +1,27 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#ifdef __NEOSKRNL__ +#include +#endif // ifdef __NEOSKRNL__ + +#define NE_COPY_DELETE(KLASS) \ + KLASS& operator=(const KLASS&) = delete; \ + KLASS(const KLASS&) = delete; + +#define NE_COPY_DEFAULT(KLASS) \ + KLASS& operator=(const KLASS&) = default; \ + KLASS(const KLASS&) = default; + +#define NE_MOVE_DELETE(KLASS) \ + KLASS& operator=(KLASS&&) = delete; \ + KLASS(KLASS&&) = delete; + +#define NE_MOVE_DEFAULT(KLASS) \ + KLASS& operator=(KLASS&&) = default; \ + KLASS(KLASS&&) = default; diff --git a/src/kernel/CompilerKit/Version.h b/src/kernel/CompilerKit/Version.h new file mode 100644 index 00000000..4250531a --- /dev/null +++ b/src/kernel/CompilerKit/Version.h @@ -0,0 +1,9 @@ +// (c) Amlal El Mahrouss + +#pragma once + +#define BOOTLOADER_VERSION "v0.0.2-bootz" +#define KERNEL_VERSION "v0.0.2-krnl" + +#define BOOTLOADER_VERSION_BCD (0x0002) +#define KERNEL_VERSION_BCD (0x0002) diff --git a/src/kernel/DmaKit/DmaPool.h b/src/kernel/DmaKit/DmaPool.h new file mode 100644 index 00000000..e20f8c69 --- /dev/null +++ b/src/kernel/DmaKit/DmaPool.h @@ -0,0 +1,101 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss , licensed under the Apache 2.0 license. + + File: DmaPool.h + Purpose: Dma Pool Manager. + +======================================== */ + +#pragma once + +#include + +#ifdef __NE_AMD64__ +#include +#define kNeDMAPoolStart (0x1000000) +#define kNeDMAPoolSize (0x1000000) +#elif defined(__NE_ARM64__) +#include + +/// @todo what reference offset shall we use? +#define kNeDMAPoolStart (0x1000000) +#define kNeDMAPoolSize (0x1000000) +#endif + +#define kNeDMABestAlign (8) + +namespace Kernel { +/// @brief DMA pool base pointer, here we're sure that AHCI or whatever tricky standard sees it. +inline UInt8* kDmaPoolPtr = (UInt8*) kNeDMAPoolStart; +inline const UInt8* kDmaPoolEnd = (UInt8*) (kNeDMAPoolStart + kNeDMAPoolSize); + +/***********************************************************************************/ +/// @brief allocate from the rtl_dma_alloc system. +/// @param size the size of the chunk to allocate. +/// @param align alignement of pointer. +/***********************************************************************************/ +inline VoidPtr rtl_dma_alloc(SizeT size, SizeT align) { + if (!size) { + return nullptr; + } + + /// Check alignement according to architecture. + if ((align % kNeDMABestAlign) != 0) { + return nullptr; + } + + UIntPtr addr = (UIntPtr) kDmaPoolPtr; + + /// here we just align the address according to a `align` variable, i'd rather be a power of two + /// really. + addr = (addr + (align - 1)) & ~(align - 1); + + if ((addr + size) > reinterpret_cast(kDmaPoolEnd)) { + err_global_get() = kErrorDmaExhausted; + return nullptr; + } + + kDmaPoolPtr = (UInt8*) (addr + size); + + HAL::mm_memory_fence((VoidPtr) addr); + + return (VoidPtr) addr; +} + +/***********************************************************************************/ +/// @brief Free DMA pointer. +/***********************************************************************************/ +inline Void rtl_dma_free(SizeT size) { + if (!size) return; + + auto ptr = (kDmaPoolPtr - size); + + if (!ptr || ptr < (UInt8*) kNeDMAPoolStart) { + err_global_get() = kErrorDmaExhausted; + return; + } + + kDmaPoolPtr = ptr; + + HAL::mm_memory_fence(ptr); +} + +/***********************************************************************************/ +/// @brief Flush DMA pointer. +/***********************************************************************************/ +inline Void rtl_dma_flush(VoidPtr ptr, SizeT size_buffer) { + if (ptr > kDmaPoolEnd) { + return; + } + + if (!ptr || ptr < (UInt8*) kNeDMAPoolStart) { + err_global_get() = kErrorDmaExhausted; + return; + } + + for (SizeT buf_idx = 0UL; buf_idx < size_buffer; ++buf_idx) { + HAL::mm_memory_fence((VoidPtr) ((UInt8*) ptr + buf_idx)); + } +} +} // namespace Kernel diff --git a/src/kernel/FSKit/Defines.h b/src/kernel/FSKit/Defines.h new file mode 100644 index 00000000..c5bf8f88 --- /dev/null +++ b/src/kernel/FSKit/Defines.h @@ -0,0 +1,12 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define FSKIT_VERSION "1.0.0" +#define FSKIT_VERSION_BCD 0x0100 diff --git a/src/kernel/FSKit/Ext2+IFS.h b/src/kernel/FSKit/Ext2+IFS.h new file mode 100644 index 00000000..01ca4c90 --- /dev/null +++ b/src/kernel/FSKit/Ext2+IFS.h @@ -0,0 +1,273 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { +/// @brief Context for an EXT2 filesystem on a given drive +class Ext2Context final { + public: + DriveTrait* drive{nullptr}; + EXT2_SUPER_BLOCK* superblock{nullptr}; + + /// @brief context with a drive + Ext2Context(Kernel::DriveTrait* drv) : drive(drv) {} + + /// @brief Clean up + ~Ext2Context() { + if (superblock) { + Kernel::mm_free_ptr(superblock); + superblock = nullptr; + } + } + + Ext2Context(const Ext2Context&) = delete; + Ext2Context& operator=(const Ext2Context&) = delete; + + Ext2Context(Ext2Context&& other) noexcept : drive(other.drive), superblock(other.superblock) { + other.drive = nullptr; + other.superblock = nullptr; + } + + Ext2Context& operator=(Ext2Context&& other) noexcept { + if (this != &other) { + if (superblock) { + Kernel::mm_free_ptr(superblock); + } + drive = other.drive; + superblock = other.superblock; + other.drive = nullptr; + other.superblock = nullptr; + } + return *this; + } + + SizeT BlockSize() const { + if (!superblock) return kExt2FSBlockSizeBase; + return kExt2FSBlockSizeBase << superblock->fLogBlockSize; + } + + operator BOOL() { return superblock != nullptr; } +}; + +/// ======================================================================= /// +/// IFS FUNCTIONS +/// ======================================================================= /// + +inline BOOL ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, VoidPtr buffer, + Kernel::UInt32 size) { + if (!drv || !buffer) return false; + + Kernel::DriveTrait::DrivePacket pkt{}; + pkt.fPacketContent = buffer; + pkt.fPacketSize = size; + pkt.fPacketLba = lba; + drv->fInput(pkt); + + return pkt.fPacketGood; +} + +inline BOOL ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const VoidPtr buffer, + Kernel::UInt32 size) { + if (!drv || !buffer) return false; + + Kernel::DriveTrait::DrivePacket pkt{}; + pkt.fPacketContent = const_cast(buffer); + pkt.fPacketSize = size; + pkt.fPacketLba = lba; + drv->fOutput(pkt); + return pkt.fPacketGood; +} + +inline Kernel::ErrorOr ext2_load_superblock(Ext2Context* ctx) { + if (!ctx || !ctx->drive) return Kernel::ErrorOr(Kernel::kErrorInvalidData); + + auto buf = Kernel::mm_alloc_ptr(sizeof(EXT2_SUPER_BLOCK), true, false); + if (!buf) return Kernel::ErrorOr(Kernel::kErrorHeapOutOfMemory); + + Kernel::UInt32 blockLba = kExt2FSSuperblockOffset / ctx->drive->fSectorSz; + + if (!ext2_read_block(ctx->drive, blockLba, buf, sizeof(EXT2_SUPER_BLOCK))) { + Kernel::mm_free_ptr(buf); + return Kernel::ErrorOr(Kernel::kErrorDisk); + } + + auto sb = reinterpret_cast(buf); + if (sb->fMagic != kExt2FSMagic) { + Kernel::mm_free_ptr(buf); + return Kernel::ErrorOr(Kernel::kErrorInvalidData); + } + + ctx->superblock = sb; + return Kernel::ErrorOr(sb); +} + +// Load inode +inline Kernel::ErrorOr ext2_load_inode(Ext2Context* ctx, Kernel::UInt32 inodeNumber) { + if (!ctx || !ctx->superblock) return Kernel::ErrorOr(Kernel::kErrorInvalidData); + + auto nodePtr = Kernel::mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!nodePtr) return Kernel::ErrorOr(Kernel::kErrorHeapOutOfMemory); + + auto ext2Node = reinterpret_cast(nodePtr); + ext2Node->inodeNumber = inodeNumber; + + // Compute block group and index within group + Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + Kernel::UInt32 group = (inodeNumber - 1) / inodesPerGroup; + + // dummy: just offset first inode + Kernel::UInt32 inodeTableBlock = ctx->superblock->fFirstInode + group; + + if (!ext2_read_block(ctx->drive, inodeTableBlock, &ext2Node->inode, sizeof(EXT2_INODE))) { + Kernel::mm_free_ptr(nodePtr); + return Kernel::ErrorOr(Kernel::kErrorDisk); + } + + ext2Node->cursor = 0; + return Kernel::ErrorOr(ext2Node); +} + +/* + * Ext2FileSystemParser Class + * + * Provides high-level interface for EXT2 filesystem operations + */ +class Ext2FileSystemParser final { + private: + Ext2Context fCtx; // Internal EXT2 context + + public: + /* + * Constructor + * Initializes the parser with a drive interface + * + * @param drive: Pointer to drive trait for disk I/O operations + */ + explicit Ext2FileSystemParser(DriveTrait* drive); + + NE_COPY_DELETE(Ext2FileSystemParser) + + /* + * Open a file or directory by path + * + * @param path: Full path to the file/directory (e.g., "/home/user/file.txt") + * @param restrict_type: Access mode restriction (e.g., "r", "w", "rw") + * @return: VoidPtr handle to the opened file/directory, or nullptr on failure + */ + VoidPtr Open(const char* path, const char* restrict_type); + + /* + * Read data from an open file node + * + * @param node: File node handle returned by Open() + * @param flags: Read operation flags + * @param size: Number of bytes to read + * @return: Pointer to allocated buffer containing read data, or nullptr on failure + * Caller is responsible for freeing the returned buffer + */ + VoidPtr Read(VoidPtr node, Int32 flags, SizeT size); + + /* + * Write data to an open file node + * + * @param node: File node handle returned by Open() + * @param data: Buffer containing data to write + * @param flags: Write operation flags + * @param size: Number of bytes to write + */ + Void Write(VoidPtr node, VoidPtr data, Int32 flags, SizeT size); + + /* + * Seek to a specific position in the file + * + * @param node: File node handle + * @param offset: Byte offset from beginning of file + * @return: true on success, false on failure + */ + BOOL Seek(VoidPtr node, SizeT offset); + + /* + * Get current position in the file + * + * @param node: File node handle + * @return: Current byte offset from beginning of file + */ + SizeT Tell(VoidPtr node); + + /* + * Reset file position to beginning + * + * @param node: File node handle + * @return: true on success, false on failure + */ + BOOL Rewind(VoidPtr node); + + /* + * Read data from a named file within a directory node + * + * @param name: Name of file within the directory + * @param node: Directory node handle + * @param flags: Read operation flags + * @param size: Number of bytes to read + * @return: Pointer to allocated buffer containing read data, or nullptr on failure + */ + VoidPtr Read(const char* name, VoidPtr node, Int32 flags, SizeT size); + + /* + * Write data to a named file within a directory node + * + * @param name: Name of file within the directory + * @param node: Directory node handle + * @param data: Buffer containing data to write + * @param flags: Write operation flags + * @param size: Number of bytes to write + */ + Void Write(const char* name, VoidPtr node, VoidPtr data, Int32 flags, SizeT size); + + /* + * Create a new regular file + * + * @param path: Full path for the new file + * @return: VoidPtr handle to the created file, or nullptr on failure + */ + VoidPtr Create(const char* path); + + /* + * Create a new directory + * + * @param path: Full path for the new directory + * @return: VoidPtr handle to the created directory, or nullptr on failure + */ + VoidPtr CreateDirectory(const char* path); + + /* + * Close and free a file/directory node + * Note: This method is not shown in the implementation but would typically be needed + * + * @param node: Node handle to close and free + */ + Void Close(VoidPtr node); + + /* + * Get file/directory information + * Note: This method is not shown in the implementation but would typically be needed + * + * @param node: Node handle + * @param info: Structure to fill with file information (size, type, permissions, etc.) + * @return: true on success, false on failure + */ + BOOL GetInfo(VoidPtr node, VoidPtr info); +}; +} // namespace Kernel diff --git a/src/kernel/FSKit/Ext2.h b/src/kernel/FSKit/Ext2.h new file mode 100644 index 00000000..e979e556 --- /dev/null +++ b/src/kernel/FSKit/Ext2.h @@ -0,0 +1,148 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include + +/// @file Ext2.h +/// @brief EXT2 filesystem structures, constants, and base wrappers. + +/// EXT2 Constants +#define kExt2FSMagic (0xEF53) +#define kExt2FSMaxFileNameLen (255U) +#define kExt2FSSuperblockOffset (1024) +#define kExt2FSRootInodeNumber (2) +#define kExt2FSInodeSize (128U) +#define kExt2FSBlockSizeBase (1024U) + +#define kExt2FSRev0 (0) +#define kExt2FSRev1 (1) + +/// EXT2 file types +enum { + kExt2FileTypeUnknown = 0, + kExt2FileTypeRegular = 1, + kExt2FileTypeDirectory = 2, + kExt2FileTypeCharDevice = 3, + kExt2FileTypeBlockDevice = 4, + kExt2FileTypeFIFO = 5, + kExt2FileTypeSocket = 6, + kExt2FileTypeSymbolicLink = 7 +}; + +typedef struct EXT2_GROUP_DESCRIPTOR final { + UInt32 fBlockBitmap; + UInt32 fInodeBitmap; + UInt32 fInodeTable; + UInt16 fFreeBlocksCount; + UInt16 fFreeInodesCount; + UInt16 fBgUsedDirsCount; + UInt16 fBgPad; + UInt32 fBgReserved[3]; +} EXT2_GROUP_DESCRIPTOR; + +struct PACKED EXT2_SUPER_BLOCK final { + Kernel::UInt32 fInodeCount; + Kernel::UInt32 fBlockCount; + Kernel::UInt32 fReservedBlockCount; + Kernel::UInt32 fFreeBlockCount; + Kernel::UInt32 fFreeInodeCount; + Kernel::UInt32 fFirstDataBlock; + Kernel::UInt32 fLogBlockSize; + Kernel::UInt32 fLogFragmentSize; + Kernel::UInt32 fBlocksPerGroup; + Kernel::UInt32 fFragmentsPerGroup; + Kernel::UInt32 fInodesPerGroup; + Kernel::UInt32 fMountTime; + Kernel::UInt32 fWriteTime; + Kernel::UInt16 fMountCount; + Kernel::UInt16 fMaxMountCount; + Kernel::UInt16 fMagic; + Kernel::UInt16 fState; + Kernel::UInt16 fErrors; + Kernel::UInt16 fMinorRevision; + Kernel::UInt32 fLastCheck; + Kernel::UInt32 fCheckInterval; + Kernel::UInt32 fCreatorOS; + Kernel::UInt32 fRevisionLevel; + Kernel::UInt16 fDefaultUID; + Kernel::UInt16 fDefaultGID; + + // EXT2_DYNAMIC_REV fields + Kernel::UInt32 fFirstInode; + Kernel::UInt16 fInodeSize; + Kernel::UInt16 fBlockGroupNumber; + Kernel::UInt32 fFeatureCompat; + Kernel::UInt32 fFeatureIncompat; + Kernel::UInt32 fFeatureROCompat; + Kernel::UInt8 fUUID[16]; + Kernel::Char fVolumeName[16]; + Kernel::Char fLastMounted[64]; + Kernel::UInt32 fAlgoBitmap; + + Kernel::UInt8 fPreallocBlocks; + Kernel::UInt8 fPreallocDirBlocks; + Kernel::UInt16 fReservedGDTBlocks; + + Kernel::UInt8 fJournalUUID[16]; + Kernel::UInt32 fJournalInode; + Kernel::UInt32 fJournalDevice; + Kernel::UInt32 fLastOrphan; + + Kernel::UInt32 fHashSeed[4]; + Kernel::UInt8 fDefHashVersion; + Kernel::UInt8 fReservedCharPad; + Kernel::UInt16 fReservedWordPad; + Kernel::UInt32 fDefaultMountOpts; + Kernel::UInt32 fFirstMetaBlockGroup; + + Kernel::UInt8 fReserved[760]; // Padding to make 1024 bytes +}; + +struct PACKED EXT2_INODE final { + Kernel::UInt16 fMode; + Kernel::UInt16 fUID; + Kernel::UInt32 fSize; + Kernel::UInt32 fAccessTime; + Kernel::UInt32 fCreateTime; + Kernel::UInt32 fModifyTime; + Kernel::UInt32 fDeleteTime; + Kernel::UInt16 fGID; + Kernel::UInt16 fLinksCount; + Kernel::UInt32 fBlocks; + Kernel::UInt32 fFlags; + Kernel::UInt32 fOSD1; + + Kernel::UInt32 fBlock[15]; // direct 0-11, indirect 12, double 13, triple 14 + + Kernel::UInt32 fGeneration; + Kernel::UInt32 fFileACL; + Kernel::UInt32 fDirACL; + Kernel::UInt32 fFragmentAddr; + + Kernel::UInt8 fOSD2[12]; +}; + +/// Directory entry +struct PACKED EXT2_DIR_ENTRY final { + Kernel::UInt32 fInode; + Kernel::UInt16 fRecordLength; + Kernel::UInt8 fNameLength; + Kernel::UInt8 fFileType; + Kernel::Char fName[kExt2FSMaxFileNameLen]; +}; + +/// VFS usage +struct Ext2Node { + Kernel::UInt32 inodeNumber; + EXT2_INODE inode; + Kernel::UInt32 cursor{0}; +}; diff --git a/src/kernel/FSKit/IndexableProperty.h b/src/kernel/FSKit/IndexableProperty.h new file mode 100644 index 00000000..a89e78b8 --- /dev/null +++ b/src/kernel/FSKit/IndexableProperty.h @@ -0,0 +1,58 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +#define kIndexerCatalogNameLength (256U) +#define kIndexerClaimed (0xCF) +#define kIndexerUnclaimed (0xCA) + +namespace Kernel { +namespace Indexer { + struct Index final { + public: + Char Drive[kDriveNameLen]{}; + Char Path[kIndexerCatalogNameLength]{}; + }; + + class IndexableProperty final : public Property { + public: + explicit IndexableProperty() : Property() { + KBasicString<> strProp(kMaxPropLen); + strProp += "/prop/indexable"; + + this->GetKey() = strProp; + } + + ~IndexableProperty() override = default; + + NE_COPY_DEFAULT(IndexableProperty) + + public: + Index& Leak() noexcept; + + public: + Void AddFlag(UInt16 flag); + Void RemoveFlag(UInt16 flag); + UInt16 HasFlag(UInt16 flag); + + private: + Index fIndex; + UInt32 fFlags{}; + }; + + /// @brief Index a file into the indexer instance. + /// @param filename path + /// @param filenameLen used bytes in path. + /// @param indexer the filesystem indexer. + /// @return none. + Void fs_index_file(const Char* filename, SizeT filenameLen, IndexableProperty& indexer); +} // namespace Indexer +} // namespace Kernel diff --git a/src/kernel/FSKit/NeFS.h b/src/kernel/FSKit/NeFS.h new file mode 100644 index 00000000..54861dec --- /dev/null +++ b/src/kernel/FSKit/NeFS.h @@ -0,0 +1,413 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + FILE: NeFS.h + PURPOSE: NeFS (New extended File System) support. + + Revision History: + + ?/?/?: Added file (amlel) + 12/02/24: Add UUID macro for EPM and GPT partition schemes. + 3/16/24: Add mandatory sector size, kNeFSSectorSz is set to 2048 by +default. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include + +/** + @brief New extended File System specification. + @author Amlal El Mahrouss (Amlal El Mahrouss, amlalelmahrouss at icloud dot com) +*/ + +#define kNeFSInvalidFork (-1) +#define kNeFSInvalidCatalog (-1) +#define kNeFSCatalogNameLen (256) + +#define kNeFSVolumeName "NeFS Volume" + +#define kNeFSMinimumDiskSize (mib_cast(8)) + +#define kNeFSSectorSz (512) + +#define kNeFSIdentLen (8) +#define kNeFSIdent " NeFS" +#define kNeFSPadLen (392) + +#define kNeFSMetaFilePrefix '$' + +#define kNeFSVersionInteger (0x0129) +#define kNeFSVerionString "1.2.9" + +/// @brief Standard fork types. +#define kNeFSDataFork "main_data" +#define kNeFSResourceFork "main_rsrc" + +#define kNeFSForkSize (sizeof(NEFS_FORK_STRUCT)) + +#define kNeFSPartitionTypeStandard (7) +#define kNeFSPartitionTypePage (8) +#define kNeFSPartitionTypeBoot (9) + +#define kNeFSCatalogKindFile (1) +#define kNeFSCatalogKindDir (2) +#define kNeFSCatalogKindAlias (3) + +//! Shared between network and/or partitions. Export forks as .zip when copying. +#define kNeFSCatalogKindShared (4) + +#define kNeFSCatalogKindResource (5) +#define kNeFSCatalogKindExecutable (6) + +#define kNeFSCatalogKindPage (8) + +#define kNeFSCatalogKindDevice (9) +#define kNeFSCatalogKindLock (10) + +#define kNeFSCatalogKindRLE (11) +#define kNeFSCatalogKindMetaFile (12) + +#define kNeFSCatalogKindTTF (13) +#define kNeFSCatalogKindRIFF (14) +#define kNeFSCatalogKindMPEG (15) +#define kNeFSCatalogKindMOFF (16) + +#define kNeFSSeparator '/' +#define kNeFSSeparatorAlt '/' + +#define kNeFSUpDir ".." +#define kNeFSRoot "/" +#define kNeFSRootAlt "/" + +#define kNeFSLF '\r' +#define kNeFSEOF (-1) + +#define kNeFSBitWidth (sizeof(Kernel::Char)) +#define kNeFSLbaType (Kernel::Lba) + +/// @note Start after the partition map header. (Virtual addressing) +#define kNeFSRootCatalogStartAddress (1024) +#define kNeFSCatalogStartAddress (kNeFSRootCatalogStartAddress + sizeof(NEFS_ROOT_PARTITION_BLOCK)) + +#define kResourceTypeDialog (10) +#define kResourceTypeString (11) +#define kResourceTypeMenu (12) +#define kResourceTypeSound (13) +#define kResourceTypeFont (14) +#define kNeFSPartLen (32) + +#define kNeFSFlagDeleted (70) +#define kNeFSFlagUnallocated (0) +#define kNeFSFlagCreated (71) + +#define kNeFSMimeNameLen (200) +#define kNeFSForkNameLen (199) + +#define kNeFSFrameworkExt ".fwrk/" +#define kNeFSStepsExt ".step/" +#define kNeFSApplicationExt ".app/" +#define kNeFSJournalExt ".jrnl" + +struct NEFS_CATALOG_STRUCT; +struct NEFS_FORK_STRUCT; +struct NEFS_ROOT_PARTITION_BLOCK; + +enum { + kNeFSHardDrive = 0xC0, // Hard Drive + kNeFSSolidStateDrive = 0xC1, // Solid State Drive + kNeFSOpticalDrive = 0x0C, // Blu-Ray/DVD + kNeFSMassStorageDevice = 0xCC, // USB + kNeFSScsiDrive = 0xC4, // SCSI Hard Drive + kNeFSFlashDrive = 0xC6, + kNeFSUnknown = 0xFF, // Unknown device. + kNeFSDriveCount = 7, +}; + +enum { + kNeFSStatusUnlocked = 0x18, + kNeFSStatusLocked, + kNeFSStatusError, + kNeFSStatusInvalid, + kNeFSStatusCount, +}; + +/// @brief Catalog record type. +struct PACKED NEFS_CATALOG_STRUCT final { + BOOL ForkOrCatalog : 1 {0}; + + Kernel::Char Name[kNeFSCatalogNameLen] = {0}; + Kernel::Char Mime[kNeFSMimeNameLen] = {0}; + + /// Catalog flags. + Kernel::UInt16 Flags; + + /// Catalog allocation status. + Kernel::UInt16 Status; + + /// Custom catalog flags. + Kernel::UInt16 CatalogFlags; + + /// Catalog kind. + Kernel::Int32 Kind; + + /// Size of the data fork. + Kernel::Lba DataForkSize; + + /// Size of all resource forks. + Kernel::Lba ResourceForkSize; + + /// Forks LBA. + Kernel::Lba DataFork; + Kernel::Lba ResourceFork; + + /// Buddy allocation tracker. + Kernel::Lba NextSibling; + Kernel::Lba PrevSibling; + + /// Best-buddy tracker. + Kernel::Lba NextBestSibling; + Kernel::Lba NextPrevSibling; + + Kernel::UInt32 Checksum; +}; + +/// @brief Fork type, contains a data page. +/// @note The way we store is way different than how other filesystems do, specific chunk of code +/// are written into either the data fork or resource fork, the resource fork is reserved for file +/// metadata. whereas the data fork is reserved for file data. +struct PACKED NEFS_FORK_STRUCT final { + BOOL ForkOrCatalog : 1 {1}; + + Kernel::Char ForkName[kNeFSForkNameLen] = {0}; + Kernel::Char CatalogName[kNeFSCatalogNameLen] = {0}; + + Kernel::Int32 Flags; + Kernel::Int32 Kind; + + Kernel::Int64 ResourceId; + Kernel::Int32 ResourceKind; + Kernel::Int32 ResourceFlags; + + Kernel::Lba DataOffset; // 8 Where to look for this data? + Kernel::SizeT DataSize; /// Data size according using sector count. + + Kernel::Lba NextSibling; + Kernel::Lba PreviousSibling; +}; + +/// @brief Partition block type +struct PACKED NEFS_ROOT_PARTITION_BLOCK final { + Kernel::Char Ident[kNeFSIdentLen] = {0}; + Kernel::Char PartitionName[kNeFSPartLen] = {0}; + + Kernel::Int32 Flags; + Kernel::Int32 Kind; + + Kernel::Lba StartCatalog; + Kernel::SizeT CatalogCount; + + Kernel::SizeT DiskSize; + + Kernel::SizeT FreeCatalog; + Kernel::SizeT FreeSectors; + + Kernel::SizeT SectorCount; + Kernel::SizeT SectorSize; + + Kernel::UInt64 Version; + + Kernel::Lba EpmBlock; + + Kernel::Char Pad[kNeFSPadLen]; +}; + +namespace Kernel { +class NeFileSystemParser; +class NeFileSystemJournal; +class NeFileSystemHelper; + +enum { + kNeFSSubDriveA, + kNeFSSubDriveB, + kNeFSSubDriveC, + kNeFSSubDriveD, + kNeFSSubDriveInvalid, + kNeFSSubDriveCount, +}; + +/// \brief Resource fork kind. +enum { kNeFSRsrcForkKind = 0, kNeFSDataForkKind = 1 }; + +/// +/// \name NeFileSystemParser +/// \brief NeFS parser class. (catalog creation, remove removal, root, +/// forks...) Designed like the DOM, detects the filesystem automatically. +/// +class NeFileSystemParser final { + public: + explicit NeFileSystemParser() = default; + ~NeFileSystemParser() = default; + + public: + NE_COPY_DELETE(NeFileSystemParser) + + NE_MOVE_DELETE(NeFileSystemParser) + + public: + /// @brief Creates a new fork inside the NeFS partition. + /// @param catalog it's catalog + /// @param theFork the fork itself. + /// @return the fork + _Output BOOL CreateFork(_Input NEFS_FORK_STRUCT& in); + + /// @brief Find fork inside New filesystem. + /// @param catalog the catalog. + /// @param name the fork name. + /// @return the fork. + _Output NEFS_FORK_STRUCT* FindFork(_Input NEFS_CATALOG_STRUCT* catalog, _Input const Char* name, + Boolean data); + + _Output Void RemoveFork(_Input NEFS_FORK_STRUCT* fork); + + _Output Void CloseFork(_Input NEFS_FORK_STRUCT* fork); + + _Output NEFS_CATALOG_STRUCT* FindCatalog(_Input const Char* catalog_name, Lba& ou_lba, + Bool search_hidden = YES, Bool local_search = NO); + + _Output NEFS_CATALOG_STRUCT* GetCatalog(_Input const Char* name); + + _Output NEFS_CATALOG_STRUCT* CreateCatalog(_Input const Char* name, _Input const Int32& flags, + _Input const Int32& kind); + + _Output NEFS_CATALOG_STRUCT* CreateCatalog(_Input const Char* name); + + _Output Bool WriteCatalog(_Input const Char* catalog, _Input Bool rsrc, _Input VoidPtr data, + _Input SizeT sz, _Input const Char* name); + + _Output VoidPtr ReadCatalog(_Input _Output NEFS_CATALOG_STRUCT* catalog, _Input Bool isRsrcFork, + _Input SizeT dataSz, _Input const Char* forkName); + + _Output Bool Seek(_Input _Output NEFS_CATALOG_STRUCT* catalog, SizeT off); + + _Output SizeT Tell(_Input _Output NEFS_CATALOG_STRUCT* catalog); + + _Output Bool RemoveCatalog(_Input const Char* catalog); + + _Output Bool CloseCatalog(_InOut NEFS_CATALOG_STRUCT* catalog); + + /// @brief Make a EPM+NeFS drive out of the disk. + /// @param drive The drive to write on. + /// @return If it was sucessful, see err_local_get(). + _Output Bool Format(_Input _Output DriveTrait* drive, _Input const Int32 flags, + const Char* part_name); + + public: + UInt32 mDriveIndex{kNeFSSubDriveA}; +}; + +/// +/// \name NeFileSystemHelper +/// \brief Filesystem helper class. +/// + +class NeFileSystemHelper final { + public: + STATIC const Char* Root(); + STATIC const Char* UpDir(); + STATIC Char Separator(); + STATIC Char MetaFile(); +}; + +/// @brief Journal class for NeFS. +class NeFileSystemJournal final { + private: + NEFS_CATALOG_STRUCT* mNode{nullptr}; + + public: + explicit NeFileSystemJournal(const char* stamp = nullptr) { + if (!stamp) { + kout << "Invalid: Journal stamp, using default name.\r"; + return; + } + + (Void)(kout << "Info: Journal stamp: " << stamp << kendl); + rt_copy_memory((VoidPtr) stamp, this->mStamp, rt_string_len(stamp)); + } + + ~NeFileSystemJournal() = default; + + NE_COPY_DEFAULT(NeFileSystemJournal) + + Bool CreateJournal(NeFileSystemParser* parser) { + if (!parser) return NO; + + delete parser->CreateCatalog("/etc/xml/", 0, kNeFSCatalogKindDir); + mNode = parser->CreateCatalog(mStamp); + + if (!mNode) return NO; + + return YES; + } + + Bool GetJournal(NeFileSystemParser* parser) { + if (!parser) return NO; + + auto node = parser->GetCatalog(mStamp); + + if (node) { + mNode = node; + return YES; + } + + return NO; + } + + Bool ReleaseJournal() { + if (mNode) { + delete mNode; + mNode = nullptr; + return YES; + } + + return NO; + } + + Bool CommitJournal(NeFileSystemParser* parser, Char* xml_data, Char* journal_name) { + if (!parser || !mNode) return NO; + + NEFS_FORK_STRUCT new_fork{}; + + rt_copy_memory(mStamp, new_fork.CatalogName, rt_string_len(mStamp)); + rt_copy_memory(journal_name, new_fork.ForkName, rt_string_len(journal_name)); + + new_fork.ResourceKind = 0; + new_fork.ResourceId = 0; + new_fork.ResourceFlags = 0; + new_fork.DataSize = rt_string_len(xml_data); + new_fork.Kind = kNeFSRsrcForkKind; + + if (!parser->CreateFork(new_fork)) return NO; + + (Void)(kout << "XML commit: " << xml_data << " to fork: " << journal_name << kendl); + + auto ret = parser->WriteCatalog(new_fork.CatalogName, YES, xml_data, rt_string_len(xml_data), + new_fork.ForkName); + + return ret; + } + + private: + Char mStamp[kNeFSCatalogNameLen] = {"/etc/xml/journal" kNeFSJournalExt}; +}; + +namespace NeFS { + Boolean fs_init_nefs(Void) noexcept; +} // namespace NeFS +} // namespace Kernel diff --git a/src/kernel/FSKit/OpenHeFS.h b/src/kernel/FSKit/OpenHeFS.h new file mode 100644 index 00000000..bd392fc3 --- /dev/null +++ b/src/kernel/FSKit/OpenHeFS.h @@ -0,0 +1,434 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +/// @file OpenHeFS.h +/// @brief OpenHeFS filesystem support. + +#define kOpenHeFSVersion (0x0104) +#define kOpenHeFSMagic "OpenHeFS" +#define kOpenHeFSMagicLen (9U) + +#define kOpenHeFSBlockLen (512U) +#define kOpenHeFSFileNameLen (256U) +#define kOpenHeFSPartNameLen (128U) + +#define kOpenHeFSMinimumDiskSize (gib_cast(128)) + +#define kOpenHeFSDefaultVolumeName u8"OpenHeFS Volume" + +#define kOpenHeFSINDStartOffset (sizeof(HEFS_BOOT_NODE)) +#define kOpenHeFSINStartOffset (sizeof(HEFS_INDEX_NODE_DIRECTORY)) + +#define kOpenHeFSRootDirectory "/" +#define kOpenHeFSRootDirectoryU8 u8"/" + +#define kOpenHeFSSeparator '/' +#define kOpenHeFSUpDir ".." + +#define kOpenHeFSRootDirectoryLen (2U) + +#define kOpenHeFSSearchAllStr u8"*" + +struct HEFS_BOOT_NODE; +struct HEFS_INDEX_NODE; +struct HEFS_INDEX_NODE_DIRECTORY; +struct HEFS_JOURNAL_NODE; + +enum : UInt8 { + kOpenHeFSHardDrive = 0xC0, // Hard Drive + kOpenHeFSSolidStateDrive = 0xC1, // Solid State Drive + kOpenHeFSOpticalDrive = 0x0C, // Blu-Ray/DVD + kOpenHeFSMassStorageDevice = 0xCC, // USB + kOpenHeFSScsiDrive = 0xC4, // SCSI Hard Drive + kOpenHeFSFlashDrive = 0xC6, + kOpenHeFSUnknown = 0xFF, // Unknown device. + kOpenHeFSDriveCount = 8, +}; + +enum : UInt8 { + kOpenHeFSStatusUnlocked = 0x18, + kOpenHeFSStatusLocked, + kOpenHeFSStatusError, + kOpenHeFSStatusInvalid, + kOpenHeFSStatusCount, +}; + +enum : UInt16 { + kOpenHeFSEncodingFlagsUTF8 = 0x50, + kOpenHeFSEncodingFlagsUTF16, + kOpenHeFSEncodingFlagsUTF32, + kOpenHeFSEncodingFlagsUTF16BE, + kOpenHeFSEncodingFlagsUTF16LE, + kOpenHeFSEncodingFlagsUTF32BE, + kOpenHeFSEncodingFlagsUTF32LE, + kOpenHeFSEncodingFlagsUTF8BE, + kOpenHeFSEncodingFlagsUTF8LE, + kOpenHeFSEncodingFlagsBinary, + kOpenHeFSEncodingFlagsCount = 11, + kOpenHeFSFlagsNone = 0, + kOpenHeFSFlagsReadOnly = 0x100, + kOpenHeFSFlagsHidden, + kOpenHeFSFlagsSystem, + kOpenHeFSFlagsArchive, + kOpenHeFSFlagsDevice, + kOpenHeFSFlagsCount = 7 +}; + +inline constexpr UInt16 kOpenHeFSFileKindRegular = 0x00; +inline constexpr UInt16 kOpenHeFSFileKindDirectory = 0x01; +inline constexpr UInt16 kOpenHeFSFileKindBlock = 0x02; +inline constexpr UInt16 kOpenHeFSFileKindCharacter = 0x03; +inline constexpr UInt16 kOpenHeFSFileKindFIFO = 0x04; +inline constexpr UInt16 kOpenHeFSFileKindSocket = 0x05; +inline constexpr UInt16 kOpenHeFSFileKindSymbolicLink = 0x06; +inline constexpr UInt16 kOpenHeFSFileKindUnknown = 0x07; +inline constexpr UInt16 kOpenHeFSFileKindCount = 0x08; + +/// @brief OpenHeFS blocks are array containing sparse blocks of data. +/// @details The blocks are used to store the data of a file. Each block is a pointer to a block of +/// data on the disk. +inline constexpr UInt16 kOpenHeFSSliceCount = 0x10; + +inline constexpr UInt16 kOpenHeFSInvalidVID = 0xFFFF; + +namespace Kernel { +/// @brief Access time type. +/// @details Used to keep track of the INode, INodeDir allocation status. +typedef UInt64 ATime; + +/// @brief OpenHeFS Boot node. +/// @details Acts like a superblock, it contains the information about the filesystem. +/// @note The boot node is the first block of the filesystem. +struct PACKED HEFS_BOOT_NODE final { + Char fMagic[kOpenHeFSMagicLen]; /// @brief Magic number of the filesystem. + Utf8Char fVolName[kOpenHeFSPartNameLen]; /// @brief Volume name. + UInt32 fVersion; /// @brief Version of the filesystem. + UInt64 fBadSectors; /// @brief Number of bad sectors in the filesystem. + UInt64 fSectorCount; /// @brief Number of sectors in the filesystem. + UInt64 fSectorSize; /// @brief Size of the sector. + UInt32 fChecksum; /// @brief Checksum of the boot node. + UInt8 fDiskKind; /// @brief Kind of the drive. (Hard Drive, Solid State Drive, Optical + /// Drive, etc). + UInt8 fEncoding; /// @brief Encoding of the filesystem. (UTF-8, UTF-16, etc). + UInt64 fStartIND; /// @brief Start of the INode directory tree. + UInt64 fEndIND; /// @brief End of the INode directory tree. + UInt64 fINDCount; /// @brief Number of leafs in the INode tree. + UInt64 fDiskSize; /// @brief Size of the disk. (Could be a virtual size, that is not the + /// real size of the disk.) + UInt16 fDiskStatus; /// @brief Status of the disk. (locked, unlocked, error, invalid). + UInt16 fDiskFlags; /// @brief Flags of the disk. (read-only, read-write, etc). + UInt16 fVID; /// @brief Virtual Identification Number within an EPM disk. (0xFFFF if not used). + UInt64 fStartIN; /// @brief Start INodes range + UInt64 fEndIN; /// @brief End INodes range + UInt64 fStartBlock; /// @brief Start Blocks range + UInt64 fEndBlock; /// @brief End Blocks range + UInt64 fJournalLBA; /// @brief Boot Node's COW journal LBA. + Char fPad[264]; +}; + +inline constexpr ATime kOpenHeFSTimeInvalid = 0x0000000000000000; +inline constexpr ATime kOpenHeFSTimeMax = 0xFFFFFFFFFFFFFFFF - 1; + +/// @brief Journal Node structure +/// @param fHashPath target hash path +/// @param fStatus target status +/// @param fCopyElem copy of element +/// @param fCopyKind kind of element +struct PACKED HEFS_JOURNAL_NODE { + UInt64 fHashPath; + UInt64 fStatus; + UInt64 fCopyElem; + UInt8 fCopyKind; + UInt8 fPad[487]; +}; + +/// @brief This enum defines the opcode of the journal, here mentioned as 'kinds' +enum HeFSJournalKind : UInt8 { + kJournalKindInvalid = 0x00, + kJournalKindWrite = 0x01, + kJournalKindRename = 0x02, + kJournalKindDelete = 0x03, + kJournalKindFlagEdit = 0x04, + kJournalKindCreate = 0x05, + kJournalKindCount, +}; + +/// @brief OpenHeFS index node. +/// @details This structure is used to store the file information of a file. +/// @note The index node is a special type of INode that contains the file information. +/// @note The index node is used to store the file information of a file. +struct PACKED HEFS_INDEX_NODE final { + UInt64 fHashPath; /// @brief File name. + UInt32 fFlags; /// @brief File flags. + UInt16 fKind; /// @brief File kind. (Regular, Directory, Block, Character, FIFO, Socket, + /// Symbolic Link, Unknown). + UInt32 fSize; /// @brief File size. + UInt32 fChecksum; /// @brief Checksum. + + Boolean fSymLink; /// @brief Is this a symbolic link? (if yes, the fName is the path to + /// the file and blocklinkstart and end contains it's inodes.) + + ATime fCreated, fAccessed, fModified, fDeleted; /// @brief File timestamps. + UInt32 fUID, fGID; /// @brief User ID and Group ID of the file. + UInt32 fMode; /// @brief File mode. (read, write, execute, etc). + + /// @brief Extents system by using blocks + /// @details Using an offset to ask fBase, and fLength to compute each slice's length. + UInt32 fOffsetSliceLow; + UInt32 fOffsetSliceHigh; + + Char fPad[437]; +}; + +enum { + kOpenHeFSInvalidColor = 0, + kOpenHeFSRed = 100, + kOpenHeFSBlack, + kOpenHeFSColorCount, +}; + +/// @brief OpenHeFS directory node. +/// @details This structure is used to store the directory information of a file. +/// @note The directory node is a special type of INode that contains the directory entries. +struct PACKED HEFS_INDEX_NODE_DIRECTORY final { + UInt64 fHashPath; /// @brief Directory path as FNV hash. + + UInt32 fFlags; /// @brief File flags. + UInt16 fReserved; /// @note Reserved for future use. + UInt32 fEntryCount; /// @brief Entry Count of this directory inode. + UInt32 fChecksum; /// @brief Checksum of the file, index node checksum. + + ATime fCreated, fAccessed, fModified, + fDeleted; /// @brief File timestamps and allocation status. + UInt32 fUID, fGID; /// @brief User ID and Group ID of the file. + UInt32 fMode; /// @brief File mode. (read, write, execute, etc). + + /// @note These slices are organized as: + /// [0] = OFFSET + /// [1] = SIZE + /// @note Thus the += 2 when iterating over them. + UInt64 fINSlices[kOpenHeFSSliceCount]; /// @brief Start of the index node. + + UInt8 fColor; /// @brief Color of the node. (Red or Black). + Lba fNext, fPrev, fChild, fParent; /// @brief Red-black tree pointers. + + Char fPad[285]; +}; +} // namespace Kernel + +namespace Kernel::Detail { +/// @brief OpenHeFS get year from ATime. +/// @param raw_atime the raw ATime value. +/// @return the year value. +/// @note The year is stored in the upper 32 bits of the ATime value. +inline UInt32 hefs_year_get(ATime raw_atime) noexcept { + return (raw_atime) >> 32; +} + +/// @brief OpenHeFS get month from ATime. +/// @param raw_atime the raw ATime value. +/// @return the month value. +/// @note The month is stored in the upper 24 bits of the ATime value. +inline UInt32 hefs_month_get(ATime raw_atime) noexcept { + return (raw_atime) >> 24; +} + +/// @brief OpenHeFS get day from ATime. +/// @param raw_atime the raw ATime value. +/// @return the day value. +/// @note The day is stored in the upper 16 bits of the ATime value. +inline UInt32 hefs_day_get(ATime raw_atime) noexcept { + return (raw_atime) >> 16; +} + +/// @brief OpenHeFS get hour from ATime. +/// @param raw_atime the raw ATime value. +/// @return the hour value. +/// @note The hour is stored in the upper 8 bits of the ATime value. +inline UInt32 hefs_hour_get(ATime raw_atime) noexcept { + return (raw_atime) >> 8; +} + +/// @brief OpenHeFS get minute from ATime. +/// @param raw_atime the raw ATime value. +/// @return the minute value. +/// @note The minute is stored in the lower 8 bits of the ATime value. +inline UInt32 hefs_minute_get(ATime raw_atime) noexcept { + return (raw_atime) &0xFF; +} + +inline constexpr UInt32 kOpenHeFSBaseYear = 1970; +inline constexpr UInt32 kOpenHeFSBaseMonth = 1; +inline constexpr UInt32 kOpenHeFSBaseDay = 1; +inline constexpr UInt32 kOpenHeFSBaseHour = 0; +inline constexpr UInt32 kOpenHeFSBaseMinute = 0; + +inline const Char* hefs_status_to_string(UInt16 status) noexcept { + switch (status) { + case kOpenHeFSStatusUnlocked: + return "Unlocked"; + case kOpenHeFSStatusLocked: + return "Locked"; + case kOpenHeFSStatusError: + return "Error"; + case kOpenHeFSStatusInvalid: + return "Invalid"; + default: + return "Unknown"; + } +} + +inline const Char* hefs_drive_kind_to_string(UInt8 kind) noexcept { + switch (kind) { + case kOpenHeFSHardDrive: + return "Hard Drive"; + case kOpenHeFSSolidStateDrive: + return "Solid State Drive"; + case kOpenHeFSOpticalDrive: + return "Optical Drive"; + case kOpenHeFSMassStorageDevice: + return "Mass Storage Device"; + case kOpenHeFSScsiDrive: + return "SCSI/SAS Drive"; + case kOpenHeFSFlashDrive: + return "Flash Drive"; + case kOpenHeFSUnknown: + default: + return "Unknown"; + } +} + +inline const Char* hefs_encoding_to_string(UInt8 encoding) noexcept { + switch (encoding) { + case kOpenHeFSEncodingFlagsUTF8: + return "UTF-8"; + case kOpenHeFSEncodingFlagsUTF16: + return "UTF-16"; + case kOpenHeFSEncodingFlagsUTF32: + return "UTF-32"; + case kOpenHeFSEncodingFlagsUTF16BE: + return "UTF-16BE"; + case kOpenHeFSEncodingFlagsUTF16LE: + return "UTF-16LE"; + case kOpenHeFSEncodingFlagsUTF32BE: + return "UTF-32BE"; + case kOpenHeFSEncodingFlagsUTF32LE: + return "UTF-32LE"; + case kOpenHeFSEncodingFlagsUTF8BE: + return "UTF-8BE"; + case kOpenHeFSEncodingFlagsUTF8LE: + return "UTF-8LE"; + default: + return "Unknown"; + } +} + +inline const Char* hefs_file_kind_to_string(UInt16 kind) noexcept { + switch (kind) { + case kOpenHeFSFileKindRegular: + return "Regular File"; + case kOpenHeFSFileKindDirectory: + return "Directory"; + case kOpenHeFSFileKindBlock: + return "Block Device"; + case kOpenHeFSFileKindCharacter: + return "Character Device"; + case kOpenHeFSFileKindFIFO: + return "FIFO"; + case kOpenHeFSFileKindSocket: + return "Socket"; + case kOpenHeFSFileKindSymbolicLink: + return "Symbolic Link"; + case kOpenHeFSFileKindUnknown: + default: + return "Unknown"; + } +} + +inline const Char* hefs_file_flags_to_string(UInt32 flags) noexcept { + switch (flags) { + case kOpenHeFSFlagsNone: + return "No Flags"; + case kOpenHeFSFlagsReadOnly: + return "Read Only"; + case kOpenHeFSFlagsHidden: + return "Hidden"; + case kOpenHeFSFlagsSystem: + return "System"; + case kOpenHeFSFlagsArchive: + return "Archive"; + case kOpenHeFSFlagsDevice: + return "Device"; + default: + return "Unknown"; + } +} +} // namespace Kernel::Detail + +namespace Kernel { +/// @brief OpenHeFS filesystem parser class. +/// @details This class is used to parse the OpenHeFS filesystem. +class HeFileSystemParser final { + public: + HeFileSystemParser() = default; + ~HeFileSystemParser() = default; + + public: + HeFileSystemParser(const HeFileSystemParser&) = delete; + HeFileSystemParser& operator=(const HeFileSystemParser&) = delete; + + HeFileSystemParser(HeFileSystemParser&&) = delete; + HeFileSystemParser& operator=(HeFileSystemParser&&) = delete; + + public: + /// @brief Make a EPM+OpenHeFS drive out of the disk. + /// @param drive The drive to write on. + /// @return If it was sucessful, see err_local_get(). + _Output Bool Format(_Input _Output DriveTrait* drive, _Input const Int32 flags, + const Utf8Char* part_name); + + _Output Bool CreateINodeDirectory(_Input DriveTrait* drive, _Input const Int32 flags, + const Utf8Char* dir); + + _Output Bool RemoveINodeDirectory(_Input DriveTrait* drive, _Input const Int32 flags, + const Utf8Char* dir); + + _Output Bool CreateINode(_Input DriveTrait* drive, _Input const Int32 flags, const Utf8Char* dir, + const Utf8Char* name, const UInt8 kind); + + _Output Bool DeleteINode(_Input DriveTrait* drive, _Input const Int32 flags, const Utf8Char* dir, + const Utf8Char* name, const UInt8 kind); + + _Output Bool INodeManip(_Input DriveTrait* drive, VoidPtr block, SizeT block_sz, + const Utf8Char* dir, const Utf8Char* name, const UInt8 kind, + const BOOL input); + + private: + _Output Bool INodeCtlManip(_Input DriveTrait* drive, _Input const Int32 flags, + const Utf8Char* dir, const Utf8Char* name, const BOOL delete_or_create, + const UInt8 kind); + + _Output Bool INodeDirectoryCtlManip(_Input DriveTrait* drive, _Input const Int32 flags, + const Utf8Char* dir, const BOOL delete_or_create); +}; + +namespace OpenHeFS { + + /// @brief Initialize OpenHeFS inside the main disk. + /// @return Whether it successfuly formated it or not. + Boolean fs_init_openhefs(Void) noexcept; +} // namespace OpenHeFS +} // namespace Kernel diff --git a/src/kernel/FirmwareKit/.gitkeep b/src/kernel/FirmwareKit/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/kernel/FirmwareKit/.gitkeep @@ -0,0 +1 @@ + diff --git a/src/kernel/FirmwareKit/EFI.h b/src/kernel/FirmwareKit/EFI.h new file mode 100644 index 00000000..f75f0fbe --- /dev/null +++ b/src/kernel/FirmwareKit/EFI.h @@ -0,0 +1,12 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +/// @note this header is used to reference the EFI/EFI.h diff --git a/src/kernel/FirmwareKit/EFI/API.h b/src/kernel/FirmwareKit/EFI/API.h new file mode 100644 index 00000000..24fceb2b --- /dev/null +++ b/src/kernel/FirmwareKit/EFI/API.h @@ -0,0 +1,88 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __EFI_API__ +#define __EFI_API__ + +#include +#include +#include +#include + +#ifdef __BOOTZ__ +// forward decl. +class BootTextWriter; + +#define __BOOTKIT_NO_INCLUDE__ 1 + +#include +#include +#endif // ifdef __BOOTZ__ + +inline EfiSystemTable* ST = nullptr; +inline EfiBootServices* BS = nullptr; + +EXTERN_C void rt_cli(); +EXTERN_C void rt_halt(); + +namespace Boot { +/// @brief Halt and clear interrut flag on x86. +/// @return +inline Void Stop() noexcept { + while (YES) { + rt_cli(); + rt_halt(); + } +} + +/** +@brief Exit EFI API to let the OS load correctly. +Bascially frees everything we have in the EFI side. +*/ +inline Void ExitBootServices(UInt64 MapKey, EfiHandlePtr ImageHandle) noexcept { + if (!ST) return; + + ST->BootServices->ExitBootServices(ImageHandle, MapKey); +} + +inline UInt32 Platform() noexcept { + return kPeMachineAMD64; +} + +/*** + * @brief Throw an error, stop execution as well. + * @param ErrorCode error code to be print. + * @param Reason reason to be print. + */ +inline void ThrowError(const EfiCharType* ErrorCode, const EfiCharType* Reason) noexcept { + ST->ConOut->OutputString(ST->ConOut, L"\r*** STOP ***\r"); + + ST->ConOut->OutputString(ST->ConOut, L"*** ERROR: "); + ST->ConOut->OutputString(ST->ConOut, ErrorCode); + + ST->ConOut->OutputString(ST->ConOut, L" ***\r *** REASON: "); + ST->ConOut->OutputString(ST->ConOut, Reason); + + ST->ConOut->OutputString(ST->ConOut, L" ***\r"); + + Boot::Stop(); +} +} // namespace Boot + +inline void fw_init_efi(EfiSystemTable* SystemTable) noexcept { + if (!SystemTable) return; + + ST = SystemTable; + BS = ST->BootServices; +} + +#ifdef __BOOTZ__ + +#include + +#endif // ifdef __BOOTZ__ + +#endif /* ifndef __EFI_API__ */ diff --git a/src/kernel/FirmwareKit/EFI/EFI.h b/src/kernel/FirmwareKit/EFI/EFI.h new file mode 100644 index 00000000..240fb9ea --- /dev/null +++ b/src/kernel/FirmwareKit/EFI/EFI.h @@ -0,0 +1,916 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef FIRMWARE_KIT_EFI_H +#define FIRMWARE_KIT_EFI_H + +/** +@brief Implementation of the main EFI protocols. +*/ + +#include + +using namespace Kernel; + +/* we always use stdcall in EFI, the pascal way of calling functions. */ + +#ifndef EPI_API +#define EFI_API __attribute__((ms_abi)) +#endif // ifndef EPI_API + +#ifndef EPIAPI +#define EFIAPI __attribute__((ms_abi)) +#endif // ifndef EPIAPI + +#define IN +#define OUT +#define OPTIONAL + +#define EFI_STATUS EfiStatusType + +#define EFI_FINAL final + +// Forward decls + +struct EfiTableHeader; +struct EfiLoadFileProtocol; +struct EfiSimpleTextOutputProtocol; +struct EfiDevicePathProtocol; +struct EfiBootServices; +struct EfiMemoryDescriptor; +struct EfiSystemTable; +struct EFI_GUID; +struct EfiFileDevicePathProtocol; +struct EfiHandle; +struct EfiGraphicsOutputProtocol; +struct EfiBitmask; +struct EfiFileProtocol; +struct EfiSimpleTextInputProtocol; + +typedef UInt64 EfiStatusType; + +typedef Char16 EfiChar16Type; + +/// @brief Core Handle Kind +/// Self is like NT's Win32 HANDLE type. +typedef struct EfiHandle { +} * EfiHandlePtr; + +/* UEFI uses wide characters by default. */ +typedef WideChar EfiCharType; + +typedef UInt64 EfiPhysicalAddress; +typedef UIntPtr EfiVirtualAddress; + +/// What's BootBolicy? +/// If TRUE, indicates that the request originates from the boot manager, and +/// that the boot manager is attempting to load FilePath as a boot selection. If +/// FALSE, then FilePath must match an exact file to be loaded. + +typedef UInt64(EFI_API* EfiTextString)(struct EfiSimpleTextOutputProtocol* Self, + const WideChar* OutputString); + +typedef UInt64(EFI_API* EfiTextAttrib)(struct EfiSimpleTextOutputProtocol* Self, + const WideChar Attribute); + +typedef UInt64(EFI_API* EfiTextClear)(struct EfiSimpleTextOutputProtocol* Self); + +typedef UInt64(EFI_API* EfiLoadFile)(EfiLoadFileProtocol* Self, EfiFileDevicePathProtocol* FilePath, + Boolean BootPolicy, UInt32* BufferSize, VoidPtr Buffer); + +typedef UInt64(EFI_API* EfiCopyMem)(VoidPtr DstBuf, VoidPtr SrcBuf, SizeT Length); + +typedef UInt64(EFI_API* EfiSetMem)(VoidPtr DstBuf, Char Byte, SizeT Length); + +typedef UInt64(EFI_API* EfiHandleProtocol)(EfiHandlePtr Handle, EFI_GUID* Guid, VoidPtr* Device); + +typedef UInt64(EFI_API* EfiLocateDevicePath)(EFI_GUID* Protocol, EfiDevicePathProtocol** DevicePath, + EfiHandlePtr Device); + +typedef UInt64(EFI_API* EfiStartImage)(EfiHandlePtr Handle, VoidPtr ArgsSize, VoidPtr ArgsPtr); + +typedef UInt64(EFI_API* EfiLoadImage)(Boolean BootPolicy, EfiHandlePtr ParentHandle, + EfiFileDevicePathProtocol* DeviceFile, VoidPtr buffer, + SizeT size, EfiHandlePtr* ppHandle); + +/// EFI pool helpers, taken from iPXE. + +typedef enum EfiMemoryType { + /// + /// Not used. + /// + EfiReservedMemoryType, + /// + /// The code portions of a loaded application. + /// (Note that UEFI OS loaders are UEFI applications.) + /// + EfiLoaderCode, + /// + /// The data portions of a loaded application and the default data allocation + /// type used by an application to allocate pool memory. + /// + EfiLoaderData, + /// + /// The code portions of a loaded Boot Services Driver. + /// + EfiBootServicesCode, + /// + /// The data portions of a loaded Boot Serves Driver, and the default data + /// allocation type used by a Boot Services Driver to allocate pool memory. + /// + EfiBootServicesData, + /// + /// The code portions of a loaded Runtime Services Driver. + /// + EfiRuntimeServicesCode, + /// + /// The data portions of a loaded Runtime Services Driver and the default + /// data allocation type used by a Runtime Services Driver to allocate pool + /// memory. + /// + EfiRuntimeServicesData, + /// + /// Free (unallocated) memory. + /// + EfiConventionalMemory, + /// + /// Memory in which errors have been detected. + /// + EfiUnusableMemory, + /// + /// Memory that holds the ACPI tables. + /// + EfiACPIReclaimMemory, + /// + /// Address space reserved for use by the firmware. + /// + EfiACPIMemoryNVS, + /// + /// Used by system firmware to request that a memory-mapped IO region + /// be mapped by the OS to a virtual address so it can be accessed by EFI + /// runtime services. + /// + EfiMemoryMappedIO, + /// + /// System memory-mapped IO region that is used to translate memory + /// cycles to IO cycles by the processor. + /// + EfiMemoryMappedIOPortSpace, + /// + /// Address space reserved by the firmware for code that is part of the + /// processor. + /// + EfiPalCode, + /// + /// A memory region that operates as EfiConventionalMemory, + /// however it happens to also support byte-addressable non-volatility. + /// + EfiPersistentMemory, + /// + /// A memory region that describes system memory that has not been accepted + /// by a corresponding call to the underlying isolation architecture. + /// + EfiUnacceptedMemoryType, + /// + /// The last type of memory. + /// Not a real type. + /// + EfiMaxMemoryType, +} EfiMemoryType; + +typedef enum EfiAllocateType { + /// Anything that satisfy the request. + AllocateAnyPages, + AllocateMaxAddress, + /// + /// Allocate pages at a specified address. + /// + AllocateAddress, + /// + /// Maximum enumeration value that may be used for bounds checking. + /// + MaxAllocateType +} EfiAllocateType; + +typedef struct EfiMemoryDescriptor { + /// @brief Kind of the memory region. + UInt32 Kind; + + /// @brief Physical address of the first byte in the memory region. PhysicalStart + EfiPhysicalAddress PhysicalStart; + /// + /// Virtual address of the first byte in the memory region. + /// VirtualStart must be aligned on a 4 KiB boundary, + /// and must not be above 0xfffffffffffff000. + /// + EfiVirtualAddress VirtualStart; + /// + /// NumberOfPages Number of 4 KiB pages in the memory region. + /// NumberOfPages must not be 0, and must not be any value + /// that would represent a memory page with a start address, + /// either physical or virtual, above 0xfffffffffffff000. + /// + UInt64 NumberOfPages; + /// + /// Attributes of the memory region that describe the bit mask of capabilities + /// for that memory region, and not necessarily the current settings for that + /// memory region. + /// + UInt64 Attribute; +} EfiMemoryDescriptor; + +typedef UInt64(EFI_API* EfiAllocatePool)(EfiMemoryType PoolType, UInt32 Size, VoidPtr* Buffer); + +typedef UInt64(EFI_API* EfiFreePool)(VoidPtr Buffer); + +typedef UInt64(EFI_API* EfiCalculateCrc32)(VoidPtr Data, UInt32 DataSize, UInt32* CrcOut); + +/** +@brief Present in every header, used to identify a UEFI structure. +*/ +typedef struct EfiTableHeader { + UInt64 Signature; + UInt32 Revision; + UInt32 HeaderSize; + UInt32 Crc32; + UInt32 Reserved; +} EfiTableHeader; + +#define EFI_ACPI_TABLE_PROTOCOL_GUID \ + { \ + 0xffe06bdd, 0x6107, 0x46a6, { 0x7b, 0xb2, 0x5a, 0x9c, 0x7e, 0xc5, 0x27, 0x5c } \ + } + +#define EFI_LOAD_FILE_PROTOCOL_GUID \ + { \ + 0x56EC3091, 0x954C, 0x11d2, { 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +#define EFI_LOAD_FILE2_PROTOCOL_GUID \ + { \ + 0x4006c0c1, 0xfcb3, 0x403e, { 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d } \ + } + +#define EFI_LOADED_IMAGE_PROTOCOL_GUID \ + { \ + 0x5B1B31A1, 0x9562, 0x11d2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } \ + } + +#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \ + { \ + 0x9042a9de, 0x23dc, 0x4a38, { 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \ + } + +#define EFI_SIMPLE_NETWORK_PROTOCOL_GUID \ + { \ + 0xA19832B9, 0xAC25, 0x11D3, { 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000 + +#define EFI_IP4_PROTOCOL_GUID \ + { \ + 0x41d94cd2, 0x35b6, 0x455a, { 0x82, 0x58, 0xd4, 0xe5, 0x13, 0x34, 0xaa, 0xdd } \ + } + +#define EFI_LOADED_IMAGE_PROTOCOL_REVISION 0x1000 + +#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ + { \ + 0x0964e5b22, 0x6459, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +#define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID \ + { \ + 0xbc62157e, 0x3e33, 0x4fec, { 0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf } \ + } + +#define EFI_DEVICE_PATH_PROTOCOL_GUID \ + { \ + 0x9576e91, 0x6d3f, 0x11d2, { 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ + { \ + 0x0964e5b22, 0x6459, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +typedef UInt64(EfiImageUnload)(EfiHandlePtr ImageHandle); + +enum { + kPixelRedGreenBlueReserved8BitPerColor, + kPixelBlueGreenRedReserved8BitPerColor, + kPixelBitMask, + kPixelBltOnly, + kPixelFormatMax +}; + +struct EFI_SIMPLE_NETWORK_PROTOCOL; + +typedef EFI_STATUS(EFI_API* EFI_SIMPLE_NETWORK_START)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This); + +typedef EFI_STATUS(EFI_API* EFI_SIMPLE_NETWORK_STOP)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This); + +typedef EFI_STATUS(EFI_API* EFI_SIMPLE_NETWORK_INITIALIZE)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This, + IN UInt32 ExtraRxBufferSize OPTIONAL, + IN UInt32 ExtraTxBufferSize OPTIONAL); + +typedef EFI_STATUS(EFIAPI* EFI_SIMPLE_NETWORK_RESET)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This, + IN Boolean ExtendedVerification); + +typedef EFI_STATUS(EFIAPI* EFI_SIMPLE_NETWORK_SHUTDOWN)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This); + +typedef UInt8 EfiMacAddress[32]; + +#define MAX_MCAST_FILTER_CNT 16 + +typedef struct { + UInt32 State; + UInt32 HwAddressSize; + UInt32 MediaHeaderSize; + UInt32 MaxPacketSize; + UInt32 NvRamSize; + UInt32 NvRamAccessSize; + UInt32 ReceiveFilterMask; + UInt32 ReceiveFilterSetting; + UInt32 MaxMCastFilterCount; + UInt32 MCastFilterCount; + EfiMacAddress MCastFilter[MAX_MCAST_FILTER_CNT]; + EfiMacAddress CurrentAddress; + EfiMacAddress BroadcastAddress; + EfiMacAddress PermanentAddress; + UInt8 IfType; + BOOL MacAddressChangeable; + BOOL MultipleTxSupported; + BOOL MediaPresentSupported; + BOOL MediaPresent; +} EFI_SIMPLE_NETWORK_MODE; + +typedef EFI_STATUS(EFIAPI* EFI_SIMPLE_NETWORK_TRANSMIT)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This, + IN UInt32 HeaderSize, IN UInt32 BufferSize, + IN Void* Buffer, + IN EfiMacAddress* SrcAddr OPTIONAL, + IN EfiMacAddress* DestAddr OPTIONAL, + IN UInt16* Protocol OPTIONAL); + +typedef EFI_STATUS(EFIAPI* EFI_SIMPLE_NETWORK_RECEIVE)(IN EFI_SIMPLE_NETWORK_PROTOCOL* This, + OUT UInt32* HeaderSize OPTIONAL, + IN OUT UInt32* BufferSize, OUT Void* Buffer, + OUT EfiMacAddress* SrcAddr OPTIONAL, + OUT EfiMacAddress* DestAddr OPTIONAL, + OUT UInt16* Protocol OPTIONAL); + +typedef struct EFI_SIMPLE_NETWORK_PROTOCOL { + UInt64 Revision; + EFI_SIMPLE_NETWORK_START Start; + EFI_SIMPLE_NETWORK_STOP Stop; + EFI_SIMPLE_NETWORK_INITIALIZE Initialize; + EFI_SIMPLE_NETWORK_RESET Reset; + EFI_SIMPLE_NETWORK_SHUTDOWN Shutdown; + VoidPtr ReceiveFilters; + VoidPtr StationAddress; + VoidPtr Statistics; + VoidPtr MCastIpToMac; + VoidPtr NvData; + VoidPtr GetStatus; + EFI_SIMPLE_NETWORK_TRANSMIT Transmit; + EFI_SIMPLE_NETWORK_RECEIVE Receive; + VoidPtr WaitForPacket; + EFI_SIMPLE_NETWORK_MODE* Mode; +} EFI_SIMPLE_NETWORK_PROTOCOL; + +typedef struct EfiBitmask { + UInt32 RedMask; + UInt32 GreenMask; + UInt32 BlueMask; + UInt32 ReservedMask; +} EfiBitmask; + +typedef struct { + UInt8 Blue; + UInt8 Green; + UInt8 Red; + UInt8 Reserved; +} EfiGraphicsOutputBltPixel; + +typedef enum EfiGraphicsOutputProtocolBltOperation { + EfiBltVideoFill, + EfiBltVideoToBltBuffer, + EfiBltBufferToVideo, + EfiBltVideoToVideo, + EfiGraphicsOutputBltOperationMax +} EfiGraphicsOutputProtocolBltOperation; + +typedef struct EfiGraphicsOutputProtocolModeInformation { + UInt32 Version; + UInt32 HorizontalResolution; + UInt32 VerticalResolution; + UInt32 PixelFormat; + EfiBitmask PixelInformation; + UInt32 PixelsPerScanLine; +} EfiGraphicsOutputProtocolModeInformation; + +typedef UInt64(EFI_API* EfiGraphicsOutputProtocolQueryMode)( + EfiGraphicsOutputProtocol* Self, UInt32 ModeNumber, UInt32* SizeOfInfo, + EfiGraphicsOutputProtocolModeInformation** Info); + +typedef UInt64(EFI_API* EfiGraphicsOutputProtocolSetMode)(EfiGraphicsOutputProtocol* Self, + UInt32 ModeNumber); + +typedef UInt64(EFI_API* EfiGraphicsOutputProtocolBlt)( + EfiGraphicsOutputProtocol* Self, EfiGraphicsOutputBltPixel* BltBuffer, + EfiGraphicsOutputProtocolBltOperation BltOperation, UInt32 SourceX, UInt32 SourceY, + UInt32 DestinationX, UInt32 DestinationY, UInt32 Width, UInt32 Height, UInt32 Delta); + +typedef struct { + UInt32 MaxMode; + UInt32 Mode; + EfiGraphicsOutputProtocolModeInformation* Info; + UInt32 SizeOfInfo; + UIntPtr FrameBufferBase; + UInt32 FrameBufferSize; +} EfiGraphicsOutputProtocolMode; + +typedef struct EfiGraphicsOutputProtocol { + EfiGraphicsOutputProtocolQueryMode QueryMode; + EfiGraphicsOutputProtocolSetMode SetMode; + EfiGraphicsOutputProtocolBlt Blt; + EfiGraphicsOutputProtocolMode* Mode; +} EfiGraphicsOutputProtocol; + +typedef struct EfiLoadImageProtocol { + UInt32 Revision; + EfiHandlePtr ParentHandle; + EfiSystemTable* SystemTable; + + // Source location of the image + EfiHandlePtr DeviceHandle; + EfiDevicePathProtocol* FilePath; + Void* Reserved; + + // Image’s load options + UInt32 LoadOptionsSize; + Void* LoadOptions; + + // Location where image was loaded + Void* ImageBase; + UInt64 ImageSize; + EfiMemoryType ImageCodeType; + EfiMemoryType ImageDataType; + EfiImageUnload Unload; +} EfiLoadImageProtocol; + +typedef struct EfiLoadFileProtocol { + EfiLoadFile LoadFile; +} EfiLoadFileProtocol; + +typedef struct EfiDevicePathProtocol { + UInt8 Kind; + UInt8 SubType; + UInt8 Length[2]; +} EfiDevicePathProtocol; + +typedef struct EfiFileDevicePathProtocol { + EfiDevicePathProtocol Proto; + + /// + /// File Path of this struct + /// + WideChar Path[kPathLen]; +} EfiFileDevicePathProtocol; + +typedef UInt64(EFI_API* EfiExitBootServices)(VoidPtr ImageHandle, UInt32 MapKey); + +typedef UInt64(EFI_API* EfiAllocatePages)(EfiAllocateType AllocType, EfiMemoryType MemType, + UInt32 Count, EfiPhysicalAddress* Memory); + +typedef UInt64(EFI_API* EfiFreePages)(EfiPhysicalAddress* Memory, UInt32 Pages); + +typedef UInt64(EFI_API* EfiGetMemoryMap)(UInt32* MapSize, EfiMemoryDescriptor* DescPtr, + UInt32* MapKey, UInt32* DescSize, UInt32* DescVersion); + +/** + * @brief GUID type, something you can also find in CFKit. + */ +typedef struct EFI_GUID EFI_FINAL { + UInt32 Data1; + UInt16 Data2; + UInt16 Data3; + UInt8 Data4[8]; +} EFI_GUID; + +/*** + * Protocol stuff... + */ + +#define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \ + { \ + 0x387477c1, 0x69c7, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +/** some helpers */ +#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001 +#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002 +#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004 +#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008 +#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010 +#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020 + +typedef UInt64(EFI_API* EfiLocateProtocol)(EFI_GUID* Protocol, VoidPtr Registration, + VoidPtr* Interface); + +typedef UInt64(EFI_API* EfiOpenProtocol)(EfiHandlePtr Handle, EFI_GUID* Guid, VoidPtr* Interface, + EfiHandlePtr AgentHandle, EfiHandlePtr ControllerHandle, + UInt32 Attributes); + +typedef UInt64(EFI_API* EfiEnableCursor)(EfiSimpleTextOutputProtocol* Self, Boolean Visible); + +/** +@name EfiBootServices +@brief UEFI Boot Services record, it contains functions necessary to a +firmware level application. +*/ +typedef struct EfiBootServices { + EfiTableHeader SystemTable; + VoidPtr RaiseTPL; + VoidPtr RestoreTPL; + EfiAllocatePages AllocatePages; + EfiFreePages FreePages; + EfiGetMemoryMap GetMemoryMap; + EfiAllocatePool AllocatePool; + EfiFreePool FreePool; + VoidPtr CreateEvent; + VoidPtr SetTimer; + VoidPtr WaitForEvent; + VoidPtr SignalEvent; + VoidPtr CloseEvent; + VoidPtr CheckEvent; + VoidPtr InstallProtocolInterface; + VoidPtr ReinstallProtocolInterface; + VoidPtr UninstallProtocolInterface; + EfiHandleProtocol HandleProtocol; + VoidPtr Reserved; + VoidPtr RegisterProtocolNotify; + VoidPtr LocateHandle; + EfiLocateDevicePath LocateDevicePath; + VoidPtr InstallConfigurationTable; + EfiLoadImage LoadImage; + EfiStartImage StartImage; + VoidPtr Exit; + VoidPtr UnloadImage; + EfiExitBootServices ExitBootServices; + VoidPtr GetNextMonotonicCount; + VoidPtr Stall; + EfiStatusType(EFI_API* SetWatchdogTimer)(UInt32 Timeout, UInt64 WatchdogCode, UInt32 DataSize, + EfiCharType* Data); + VoidPtr ConnectController; + VoidPtr DriveonnectController; + EfiOpenProtocol OpenProtocol; + VoidPtr CloseProtocol; + VoidPtr OpenProtocolInformation; + VoidPtr ProtocolsPerHandle; + VoidPtr LocateHandleBuffer; + EfiLocateProtocol LocateProtocol; + VoidPtr InstallMultipleProtocolInterfaces; + VoidPtr UninstallMultipleProtocolInterfaces; + EfiCalculateCrc32 CalculateCrc32; + EfiCopyMem CopyMem; + EfiSetMem SetMem; + VoidPtr CreateEventEx; +} EfiBootServices; + +#define kEntireDevPath 0xFF +#define kThisInstancePath 0x01 + +/** +@brief PrintF like protocol. +*/ +typedef struct EfiSimpleTextOutputProtocol { + VoidPtr Reset; + EfiTextString OutputString; + VoidPtr TestString; + VoidPtr QueryMode; + VoidPtr SetMode; + EfiTextAttrib SetAttribute; + EfiTextClear ClearScreen; + VoidPtr SetCursorPosition; + EfiEnableCursor EnableCursor; + VoidPtr Mode; +} EfiSimpleTextOutputProtocol; + +typedef struct { + UInt16 ScanCode; + EfiChar16Type UnicodeChar; +} EfiInputKey; + +typedef EfiStatusType(EFI_API* EfiInputReadKey)(IN EfiSimpleTextInputProtocol* This, + OUT EfiInputKey* Key); + +typedef EfiStatusType(EFI_API* EfiInputReset)(IN EfiSimpleTextInputProtocol* This, + IN Boolean ExtendedChk); + +typedef EfiStatusType(EFI_API* EfiWaitForEvent)(IN UInt32 NumberOfEvents, IN VoidPtr Event, + OUT UInt32* Index); + +typedef struct EfiSimpleTextInputProtocol { + EfiInputReset Reset; + EfiInputReadKey ReadKeyStroke; + EfiWaitForEvent WaitForKey; +} EfiSimpleTextInputProtocol; + +/// @biref Open Volume procedure ptr. +typedef UInt64(EFI_API* EfiOpenVolume)(struct EfiSimpleFilesystemProtocol*, + struct EfiFileProtocol**); + +struct EfiSimpleFilesystemProtocol { + UInt64 Revision; + EfiOpenVolume OpenVolume; +}; + +typedef struct EfiRuntimeServices { + EfiTableHeader SystemTable; + VoidPtr GetTime, SetTime, GetWakeupTime, SetWakeupTime, SetVirtualAddressMap, ConvertPointer; + UInt64(EFI_API* GetVariable)(const WideChar* Name, EFI_GUID VendorGUID, UInt32* Attributes, + UInt32* DataSize, VoidPtr Data); + VoidPtr GetNextVariable; + UInt64(EFI_API* SetVariable)(const WideChar* Name, EFI_GUID VendorGUID, UInt32* Attributes, + UInt32* DataSize, VoidPtr Data); + VoidPtr GetNextHighMonotonicCount; + VoidPtr ResetSystem; + VoidPtr UpdateCapsule; + VoidPtr QueryCapsuleCapabilites; + VoidPtr QueryVariableInfo; +} EfiRuntimeServices; + +/** +@brief The Structure that they give you when booting. +*/ +typedef struct EfiSystemTable { + EfiTableHeader SystemHeader; + WideChar* FirmwareVendor; + UInt32 FirmwareRevision; + EfiHandlePtr ConsoleInHandle; + EfiSimpleTextInputProtocol* ConIn; + EfiHandlePtr ConsoleOutHandle; + EfiSimpleTextOutputProtocol* ConOut; + EfiHandlePtr StandardErrorHandle; + VoidPtr StdErr; + EfiRuntimeServices* RuntimeServices; + EfiBootServices* BootServices; + UInt64 NumberOfTableEntries; + /// The configuration table (contains the RSD PTR entry.) + struct { + EFI_GUID VendorGUID; + VoidPtr VendorTable; + } * ConfigurationTable; +} EfiSystemTable; + +#define kEfiOk 0 +#define kEfiFail -1 +#define kBufferTooSmall 5 + +#define EFI_EXTERN_C extern "C" + +typedef struct EfiIPV4 { + UInt8 Addr[4]; +} EfiIPV4; + +/// +/// 16-byte buffer. An IPv6 internet protocol address. +/// +typedef struct EfiIPV6 { + UInt8 Addr[16]; +} EfiIPV6; + +#define kEFIYellow (0x01 | 0x02 | 0x04 | 0x08) + +#ifdef __x86_64 +#define __EFI_x86_64__ 1 +#endif // __x86_64 + +enum { + kEFIHwDevicePath = 0x01, + kEFIAcpiDevicePath = 0x02, + kEFIMessaingDevicePath = 0x03, + kEFIMediaDevicePath = 0x04, + kEFIBiosBootPath = 0x05, + kEFIEndOfPath = 0x06, + kEFICount = 6, +}; + +#define END_DEVICE_PATH_TYPE 0x7f +#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xFF +#define END_INSTANCE_DEVICE_PATH_SUBTYPE 0x01 + +#define kEfiOffsetOf(T, F) __builtin_offsetof(T, F) + +/// File I/O macros + +#define kEFIFileRead 0x0000000000000001 +#define kEFIFileWrite 0x0000000000000002 +#define kEFIFileCreate 0x0000000000000000 + +#define kEFIReadOnly 0x01 +#define kEFIHidden 0x02 +#define kEFISystem 0x04 +#define kEFIReserved 0x08 +#define kEFIDirectory 0x10 +#define kEFIArchive 0x20 + +#define EFI_FILE_PROTOCOL_REVISION 0x00010000 +#define EFI_FILE_PROTOCOL_REVISION2 0x00020000 +#define EFI_FILE_PROTOCOL_LATEST_REVISION EFI_FILE_PROTOCOL_REVISION2 + +#define EFI_EXTRA_DESCRIPTOR_SIZE 8 + +#define EFI_MP_SERVICES_PROTOCOL_GUID \ + { \ + 0x3fdda605, 0xa76e, 0x4f46, { 0xad, 0x29, 0x12, 0xf4, 0x53, 0x1b, 0x3d, 0x08 } \ + } + +#define PROCESSOR_AS_BSP_BIT 0x00000001 +#define PROCESSOR_ENABLED_BIT 0x00000002 +#define PROCESSOR_HEALTH_STATUS_BIT 0x00000004 + +#define END_OF_CPU_LIST 0xffffffff + +typedef struct EfiIOToken { + // + // If Event is NULL, then blocking I/O is performed. + // If Event is not NULL and non-blocking I/O is supported, then non-blocking + // I/O is performed, and Event will be signaled when the read request is + // completed. The caller must be prepared to handle the case where the + // callback associated with Event occurs before the original asynchronous I/O + // request call returns. + // + UInt64 Event; + + // + // Defines whether or not the signaled event encountered an error. + // + UInt64 Status; + + // + // For OpenEx(): Not Used, ignored. + // For ReadEx(): On input, the size of the Buffer. On output, the amount of + // data returned in Buffer. + // In both cases, the size is measured in bytes. + // For WriteEx(): On input, the size of the Buffer. On output, the amount of + // data actually written. + // In both cases, the size is measured in bytes. + // For FlushEx(): Not used, ignored. + // + UInt32 BufferSize; + + // + // For OpenEx(): Not Used, ignored. + // For ReadEx(): The buffer into which the data is read. + // For WriteEx(): The buffer of data to write. + // For FlushEx(): Not Used, ignored. + // + Void* Buffer; +} EfiIOToken; + +typedef struct EfiFileProtocol { + UInt64 Revision; + + EfiStatusType(EFI_API* Open)(struct EfiFileProtocol* Self, struct EfiFileProtocol** Out, + EfiCharType* CharType, UInt64 OpenMode, UInt64 Attrib); + + EfiStatusType(EFI_API* Close)(struct EfiFileProtocol* Self); + + EfiStatusType(EFI_API* Delete)(struct EfiFileProtocol* Self); + + EfiStatusType(EFI_API* Read)(struct EfiFileProtocol* Self, UInt64* BufSize, VoidPtr BufOut); + + EfiStatusType(EFI_API* Write)(struct EfiFileProtocol* Self, UInt64* BufSize, VoidPtr BufOut); + + EfiStatusType(EFI_API* GetPosition)(EfiFileProtocol* Self, UInt64* Position); + + EfiStatusType(EFI_API* SetPosition)(EfiFileProtocol* Self, UInt64* Position); + + EfiStatusType(EFI_API* GetInfo)(struct EfiFileProtocol*, struct EFI_GUID*, UInt32*, void*); + + EfiStatusType(EFI_API* SetInfo)(struct EfiFileProtocol*, struct EFI_GUID*, UInt32*, void*); + + EfiStatusType(EFI_API* Flush)(EfiFileProtocol*); + + EfiStatusType(EFI_API* OpenEx)(EfiFileProtocol* Self, EfiFileProtocol** OutHandle, + EfiCharType* Path, UInt64 Mode, UInt64 Attrib, + struct EfiIOToken* Token); + + EfiStatusType(EFI_API* ReadEx)(EfiFileProtocol* Self, struct EfiIOToken* Token); + + EfiStatusType(EFI_API* WriteEx)(EfiFileProtocol* Self, struct EfiIOToken* Token); + + EfiStatusType(EFI_API* FlushEx)(EfiFileProtocol* Self, struct EfiIOToken* Token); +} EfiFileProtocol, *EfiFileProtocolPtr; + +typedef UInt64 EfiCursorType; + +typedef struct EfiTime { + UInt16 Year; + UInt8 Month; + UInt8 Day; + UInt8 Hour; + UInt8 Minute; + UInt8 Second; + UInt8 Pad1; + UInt32 Nanosecond; + Int16 TimeZone; + UInt8 Daylight; + UInt8 Pad2; +} EfiTime; + +#define EFI_FILE_INFO_GUID \ + { \ + 0x09576e92, 0x6d3f, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +struct EfiFileInfo EFI_FINAL { + /// @brief Structure size. + UInt64 Size; + /// @brief File size. + UInt64 FileSize; + /// @brief Physical size on disk. + UInt64 PhysicalSize; + /// @brief Create time. + EfiTime CreateTime; + /// @brief Last access time. + EfiTime LastAccessTime; + /// @brief Edit time. + EfiTime EditTime; + /// @brief Attributes. + UInt64 Attribute; + /// @brief VLA file name. + WideChar FileName[1]; +}; + +//******************************************************* +// EFI_CPU_PHYSICAL_LOCATION +// @note As in the EFI specs. +//******************************************************* +typedef struct _EfiCPUPhyiscalLocation { + UInt32 Package; + UInt32 Core; + UInt32 Thread; +} EfiCPUPhyiscalLocation; + +typedef union _EfiExtendedProcessorInformation { + EfiCPUPhyiscalLocation Location2; +} EfiExtendedProcessorInformation; + +typedef struct _EfiProcessorInformation { + UInt64 ProcessorId; + UInt32 StatusFlag; + EfiCPUPhyiscalLocation Location; + EfiExtendedProcessorInformation ExtendedInformation; +} EfiProcessorInformation; + +typedef EfiStatusType EFI_API (*EfiMpServicesGetNumberOfProcessors)( + IN struct _EfiMpServicesProtocol* Self, OUT UInt32* NumberOfProcessors, + OUT UInt32* NumberOfEnabledProcessors); + +typedef EfiStatusType EFI_API (*EfiMpServicesGetProcessorInfo)( + IN struct _EfiMpServicesProtocol* Self, IN UInt32* ProcessorNumber, + OUT struct _EfiProcessorInformation* NumberOfEnabledProcessors); + +typedef void EFI_API (*EFI_AP_PROCEDURE)(IN VoidPtr ProcedureArgument); + +typedef EfiStatusType EFI_API (*EfiMpServicesStartupAllAPS)( + IN struct _EfiMpServicesProtocol* Self, IN EFI_AP_PROCEDURE Procedure, IN Boolean SingleThread, + IN VoidPtr WaitEvent OPTIONAL, // EFI_EVENT first, but unused here. + IN UInt32 TimeoutInMicroSeconds, IN Void* ProcedureArgument OPTIONAL, + OUT UInt32** FailedCpuList OPTIONAL); + +typedef EfiStatusType EFI_API (*EfiMpServicesSwitchBSP)(IN struct _EfiMpServicesProtocol* Self, + IN UInt32 ProcessorNumber, + IN Boolean EnableOldBSP); + +typedef EfiStatusType EFI_API (*EfiMpServicesStartupThisAP)( + IN struct _EfiMpServicesProtocol* Self, IN EFI_AP_PROCEDURE Procedure, + IN UInt32 ProcessorNumber, IN VoidPtr WaitEvent OPTIONAL, IN UInt32 TimeoutInMicroseconds, + IN Void* ProcedureArgument OPTIONAL, OUT Boolean* Finished OPTIONAL); + +typedef EfiStatusType EFI_API (*EfiMpServicesDisableThisAP)(IN struct _EfiMpServicesProtocol* Self, + IN UInt32 ProcessorNumber, + IN Boolean EnableAP, + IN UInt32* HealthFlag OPTIONAL); + +typedef EfiStatusType EFI_API (*EfiMpServicesWhoAmI)(IN struct _EfiMpServicesProtocol* Self, + OUT UInt32* ProcessorNumber); + +typedef struct _EfiMpServicesProtocol { + EfiMpServicesGetNumberOfProcessors GetNumberOfProcessors; + EfiMpServicesGetProcessorInfo GetProcessorInfo; + EfiMpServicesStartupAllAPS StartupAllAPs; + EfiMpServicesStartupThisAP StartupThisAP; + EfiMpServicesSwitchBSP SwitchBSP; + EfiMpServicesDisableThisAP EnableDisableAP; + EfiMpServicesWhoAmI WhoAmI; +} EfiMpServicesProtocol; + +#endif // ifndef FIRMWARE_KIT_EFI_H diff --git a/src/kernel/FirmwareKit/EFI/NS.h b/src/kernel/FirmwareKit/EFI/NS.h new file mode 100644 index 00000000..64e59870 --- /dev/null +++ b/src/kernel/FirmwareKit/EFI/NS.h @@ -0,0 +1,18 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Firmware::Detail::EFI { +using namespace Kernel; + +EXTERN_C { +#include +} + +} // namespace Firmware::Detail::EFI diff --git a/src/kernel/FirmwareKit/EPM.h b/src/kernel/FirmwareKit/EPM.h new file mode 100644 index 00000000..8df4d345 --- /dev/null +++ b/src/kernel/FirmwareKit/EPM.h @@ -0,0 +1,112 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + @brief The Explicit Partition Map scheme. +*/ + +#ifndef FIRMWAREKIT_EPM_H +#define FIRMWAREKIT_EPM_H + +#include + +#define kEPMNameLength (32) +#define kEPMFilesystemLength (16) +#define kEPMMagicLength (5) + +/* @brief AMD64 magic for EPM */ +#define kEPMMagic86 "EPMAM" + +/* @brief RISC-V magic for EPM */ +#define kEPMMagicRISCV "EPMRV" + +/* @brief ARM magic for EPM */ +#define kEPMMagicARM "EPMAR" + +/* @brief 64x0 magic for EPM */ +#define kEPMMagic64k "EPM64" + +/* @brief 32x0 magic for EPM */ +#define kEPMMagic32k "EPM32" + +/* @brief POWER magic for EPM */ +#define kEPMMagicPPC "EPMPC" + +/* @brief Invalid magic for EPM */ +#define kEPMMagicError "EPM??" + +#ifdef __NE_AMD64__ +#define kEPMMagic kEPMMagic86 +#else +#ifdef __NE_ARM64__ +#define kEPMMagic kEPMMagicARM +#else +#define kEPMMagic kEPMMagicError +#endif +#endif + +///! @brief partition must start at this address. +///! Anything below is reserved for Data backup by the Main OS. +#define kEPMPartBlockLba (sizeof(EPM_PART_BLOCK)) + +///! @brief Current EPM revision. +#define kEPMRevisionBcd (0x0100) + +/// !@brief EPM boot block address. +#define kEPMBootBlockLba (0U) + +#define kEPMReserveLen (399) + +struct EPM_GUID; +struct EPM_PART_BLOCK; + +/* The first 0 > 128 addresses of a disk contains these headers. */ + +/// @brief EPM GUID structure. +typedef struct EPM_GUID { + Kernel::UInt32 Data1; + Kernel::UInt16 Data2; + Kernel::UInt16 Data3; + Kernel::UInt8 Data4[8]; +} PACKED EPM_GUID; + +/** + * @brief The EPM boot block. + * @note NumBlock and LbaStart are ignored on some platforms. + */ +struct PACKED EPM_PART_BLOCK { + Kernel::Char Magic[kEPMMagicLength]; + Kernel::Char Name[kEPMNameLength]; + EPM_GUID Guid; + Kernel::Int32 Version; + Kernel::Int64 NumBlocks; + Kernel::Int64 SectorSz; + Kernel::Int64 LbaStart; // base offset + Kernel::Int64 LbaEnd; // end offset + Kernel::Int16 Kind; + Kernel::Int16 Flags; + Kernel::Int32 FsVersion; + Kernel::Char Fs[kEPMFilesystemLength]; /* NeFS, OpenHeFS... */ + Kernel::Char Reserved[kEPMReserveLen]; // to fill a full sector. +}; + +///! @brief Version kind enum. +///! @brief Use in boot block version field. + +enum { + kEPMInvalid = 0x00, + kEPMGeneric = 0xcf, /// @brief Generic OS + kEPMLinux = 0x8f, /// @brief Linux on EPM. + kEPMBSD = 0x9f, /// @brief BSD on EPM. + kEPMNeKernel = 0x1f, /// @brief NeKernel. + kEPMLegacy = 0x2f, /// @brief Legacy VMKernel. + /// @note ... the rest is reserved for future OSes. + kEPMInvalidOS = 0xff, +}; + +inline EPM_GUID kEPMNilGuid = {0x0U, 0x0U, 0x0U, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + +#endif // ifndef FIRMWAREKIT_EPM_H diff --git a/src/kernel/FirmwareKit/GPT.h b/src/kernel/FirmwareKit/GPT.h new file mode 100644 index 00000000..1772cc79 --- /dev/null +++ b/src/kernel/FirmwareKit/GPT.h @@ -0,0 +1,48 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +#define kSectorAlignGPT_PartTbl (420U) +#define kSectorAlignGPT_PartEntry (72U) +#define kMagicLenGPT (8U) +#define kMagicGPT ("EFI PART") // "EFI PART" +#define kGPTPartitionTableLBA (512U + sizeof(GPT_PARTITION_TABLE)) + +namespace Kernel { +struct GPT_PARTITION_TABLE; +struct GPT_PARTITION_ENTRY; + +struct PACKED GPT_PARTITION_TABLE final { + Char Signature[kMagicLenGPT]; + UInt32 Revision; + UInt32 HeaderSize; + UInt32 CRC32; + UInt32 Reserved1; + UInt64 LBAHeader; + UInt64 LBAAltHeader; + UInt64 FirstGPTEntry; + UInt64 LastGPTEntry; + EFI_GUID Guid; + UInt64 StartingLBA; + UInt32 NumPartitionEntries; + UInt32 SizeOfEntries; + UInt32 CRC32PartEntry; + UInt8 Reserved2[kSectorAlignGPT_PartTbl]; +}; + +struct PACKED GPT_PARTITION_ENTRY { + EFI_GUID PartitionTypeGUID; + EFI_GUID UniquePartitionGUID; + UInt64 StartLBA; + UInt64 EndLBA; + UInt64 Attributes; + UInt8 Name[kSectorAlignGPT_PartEntry]; +}; +} // namespace Kernel diff --git a/src/kernel/FirmwareKit/Handover.h b/src/kernel/FirmwareKit/Handover.h new file mode 100644 index 00000000..354b6e57 --- /dev/null +++ b/src/kernel/FirmwareKit/Handover.h @@ -0,0 +1,108 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file Handover.h + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief The handover boot protocol. + * @version 1.15 + * @date 2024-02-23 + * + * @copyright Copyright (c) 2024, Amlal El Mahrouss + * + */ + +#pragma once + +#include +#include + +#define kHandoverMagic (0xBADCC) +#define kHandoverVersion (0x0117) + +/* Initial bitmap pointer location and size. */ +#define kHandoverBitMapSz (gib_cast(4)) +#define kHandoverStructSz sizeof(HEL::BootInfoHeader) + +namespace Kernel::HEL { +/** +@brief The executable type enum. +*/ +enum { + kTypeKernel = 100, + kTypeKernelDriver = 101, + kTypeRsrc = 102, + kTypeInvalid = 103, + kTypeCount = 4, +}; + +/** +@brief The executable architecture enum. +*/ + +enum { + kArchAMD64 = 122, + kArchARM64 = 123, + kArchRISCV = 124, + kArchCount = 3, +}; + +struct BootInfoHeader final { + UInt64 f_Magic; + UInt64 f_Version; + + VoidPtr f_BitMapStart; + SizeT f_BitMapSize; + + VoidPtr f_PageStart; + + VoidPtr f_KernelImage; + SizeT f_KernelSz; + + VoidPtr f_LibSystemImage; + SizeT f_LibSystemSz; + + VoidPtr f_StackTop; + SizeT f_StackSz; + + WideChar f_FirmwareVendorName[32]; + SizeT f_FirmwareVendorLen; + + VoidPtr f_FirmwareCustomTables[2]; // On EFI 0: BS 1: ST + + struct { + VoidPtr f_SmBios; + VoidPtr f_VendorPtr; + VoidPtr f_MpPtr; + Bool f_MultiProcessingEnabled; + UInt32 f_ImageKey; + EfiHandlePtr f_ImageHandle; + } f_HardwareTables; + + struct { + UIntPtr f_The; + SizeT f_Size; + UInt32 f_Width; + UInt32 f_Height; + UInt32 f_PixelFormat; + UInt32 f_PixelPerLine; + } f_GOP; + + UInt64 f_FirmwareSpecific[8]; +}; + +enum { + kHandoverTableBS, + kHandoverTableST, + kHandoverTableCount, +}; + +/// @brief Alias of bootloader main type. +typedef Int32 (*HandoverProc)(BootInfoHeader* boot_info); +} // namespace Kernel::HEL + +/// @brief Bootloader information header global variable. +inline Kernel::HEL::BootInfoHeader* kHandoverHeader = nullptr; diff --git a/src/kernel/FirmwareKit/NeBoot/BootNet.h b/src/kernel/FirmwareKit/NeBoot/BootNet.h new file mode 100644 index 00000000..3637bab0 --- /dev/null +++ b/src/kernel/FirmwareKit/NeBoot/BootNet.h @@ -0,0 +1,41 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define kBootNetINetMagic "ONET" +#define kBootNetINetMagicLength (4) +#define kBootNetVersion (0x001) +#define kBootNetNameLen (256U) + +struct _BOOTNET_INTERNET_HEADER; + +/// @brief Netboot Internet Header +/// Consists of 4 magic characters, and a set of fields describing the current patch that's being +/// sent (if m_preflight = 0) +/// @note Can be used to patch ROMs too (if ImpliesProgram = 1) +typedef struct _BOOTNET_INTERNET_HEADER { + Kernel::Char NB1; /// magic char 1 'O' + Kernel::Char NB2; /// magic char 2 'N' + Kernel::Char NB3; /// magic char 3 'E' + Kernel::Char NB4; /// magic char 4 'T' + + Kernel::UInt16 Version; + + Kernel::Char Name[kBootNetNameLen]; /// example: Modjo + + Kernel::Int32 Length; /// the patch length. + Kernel::Char Target[kBootNetNameLen]; /// the target file. + + Kernel::Boolean ImpliesProgram : 1; /// does it imply reprogramming? + + Kernel::Boolean Preflight : 1; /// is it a preflight packet. + Kernel::Char Data[1]; /// non preflight packet has a patch blob for a **PatchTarget** +} PACKED BOOTNET_INTERNET_HEADER; + +using BOOTNET_INTERNET_HEADER_PTR = BOOTNET_INTERNET_HEADER*; diff --git a/src/kernel/FirmwareKit/NeBoot/NS.h b/src/kernel/FirmwareKit/NeBoot/NS.h new file mode 100644 index 00000000..3a6bed6d --- /dev/null +++ b/src/kernel/FirmwareKit/NeBoot/NS.h @@ -0,0 +1,10 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include \ No newline at end of file diff --git a/src/kernel/FirmwareKit/NeBoot/NeBoot.h b/src/kernel/FirmwareKit/NeBoot/NeBoot.h new file mode 100644 index 00000000..8e8fd87f --- /dev/null +++ b/src/kernel/FirmwareKit/NeBoot/NeBoot.h @@ -0,0 +1,34 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Firmware::Detail::NeBoot { +using namespace Kernel; + +struct NEBOOT_LINEAR_EXEC; + +/// @brief Linear Executable Header +/// @author Amlal El Mahrouss +struct ATTRIBUTE(aligned(4)) NEBOOT_LINEAR_EXEC { + const Char fMagic[2]; // magic number + const Char fName[10]; // operating system name + const UInt32 fRevision; // firmware revision + const UInt32 fStartAddress; // start address (master/slave(s) thread) + +#ifdef NE_IS_EXTENDED_COREBOOT + UIntPtr fMasterStructure; // master structure for MP/PM and device tree and such. (ARM) + UIntPtr fMasterStructureVersion; // master structure version. +#endif + +#ifdef NE_IS_MBCI_COREBOOT + UIntPtr fMBCIStructure; // MBCI structure for MBCI (ARM) + UIntPtr fMBCIStructureVersion; // MBCI structure version. +#endif +}; +} // namespace Firmware::Detail::NeBoot diff --git a/src/kernel/FirmwareKit/VEPM.h b/src/kernel/FirmwareKit/VEPM.h new file mode 100644 index 00000000..b2bee0d3 --- /dev/null +++ b/src/kernel/FirmwareKit/VEPM.h @@ -0,0 +1,47 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef FIRMWAREKIT_VEPM_H +#define FIRMWAREKIT_VEPM_H + +#include +#include + +/// =========================================================== /// +/// @brief The Virtual Explicit Partition Map scheme extension. +/// =========================================================== /// + +#if defined(__NE_VEPM__) +#ifdef kEPMMagic +#undef kEPMMagic +#endif // kEPMMagic + +/// =========================================================== /// +/// @brief VEPM Ident String. +/// =========================================================== /// +#define kEPMMagic "EPMVM" + +/// =========================================================== /// +/// @brief VEPM GUID. +/// @note This is the GUID used to identify a VEPM partition. +/// =========================================================== /// +inline EPM_GUID kVEPMGuidEPM = { + 0x9a1b3f2e, 0x4c3f, 0x4d52, {0xa7, 0x83, 0x9c, 0x21, 0x7b, 0x5e, 0x4d, 0xac}}; + +/// =========================================================== /// +/// @brief VEPM GUID. +/// @note This is the GUID used to identify a VEPM partition (EFI version) +/// =========================================================== /// +inline EFI_GUID kVEPMGuidEFI = { + 0x9a1b3f2e, 0x4c3f, 0x4d52, {0xa7, 0x83, 0x9c, 0x21, 0x7b, 0x5e, 0x4d, 0xac}}; + +/// =========================================================== /// +/// @brief VEPM GUID String. +/// =========================================================== /// +#define kVEPMGuidStr "9a1b3f2e-4c3f-4d52-a783-9c217b5e4dac" +#endif // __NE_VEPM__ + +#endif // FIRMWAREKIT_VEPM_H \ No newline at end of file diff --git a/src/kernel/GfxKit/FB.h b/src/kernel/GfxKit/FB.h new file mode 100644 index 00000000..c8a2dc4b --- /dev/null +++ b/src/kernel/GfxKit/FB.h @@ -0,0 +1,52 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +class FBDeviceInterface; +struct FBDevicePacket; + +typedef UInt32 FBCoord2x2; +typedef UInt32 FBDim2x2; +typedef UInt32 FBColorProfile; +typedef UInt32 FBFlags; + +/// @brief Framebuffer device interface packet. +/// @details This structure is used to send and receive data from the framebuffer device. +/// @note The structure is packed to ensure that the data is aligned correctly for the device. +struct PACKED FBDevicePacket final { + FBCoord2x2 fX; + FBCoord2x2 fY; + FBDim2x2 fWidth; + FBDim2x2 fHeight; + FBColorProfile fColor; + FBFlags fFlags; +}; + +/// @brief Framebuffer device interface. +/// @details This class is used to send and receive data from the framebuffer device. +/// @note The class is derived from the DeviceInterface class. +class FBDeviceInterface NE_DEVICE { + public: + explicit FBDeviceInterface(void (*out)(DeviceInterface* self, FBDevicePacket* out), + void (*in)(DeviceInterface* self, FBDevicePacket* in)); + + virtual ~FBDeviceInterface() override; + + public: + FBDeviceInterface& operator=(const FBDeviceInterface&) = default; + FBDeviceInterface(const FBDeviceInterface&) = default; + + const Char* Name() const override; + + public: + FBDeviceInterface& operator<<(FBDevicePacket* dat) override; + FBDeviceInterface& operator>>(FBDevicePacket* dat) override; +}; +} // namespace Kernel diff --git a/src/kernel/HALKit/.gitkeep b/src/kernel/HALKit/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/AMD64/CPUID.h b/src/kernel/HALKit/AMD64/CPUID.h new file mode 100644 index 00000000..0ab95c07 --- /dev/null +++ b/src/kernel/HALKit/AMD64/CPUID.h @@ -0,0 +1,89 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: CPUID.h + Purpose: CPUID flags. + + Revision History: + + 30/01/24: Added file (amlel) + +======================================== */ + +#pragma once + +#include + +EXTERN_C { +#include +} + +namespace Kernel { + +enum { + kCPUFeatureSSE3 = 1 << 0, + kCPUFeaturePCLMUL = 1 << 1, + kCPUFeatureDTES64 = 1 << 2, + kCPUFeatureMONITOR = 1 << 3, + kCPUFeatureDS_CPL = 1 << 4, + kCPUFeatureVMX = 1 << 5, + kCPUFeatureSMX = 1 << 6, + kCPUFeatureEST = 1 << 7, + kCPUFeatureTM2 = 1 << 8, + kCPUFeatureSSSE3 = 1 << 9, + kCPUFeatureCID = 1 << 10, + kCPUFeatureSDBG = 1 << 11, + kCPUFeatureFMA = 1 << 12, + kCPUFeatureCX16 = 1 << 13, + kCPUFeatureXTPR = 1 << 14, + kCPUFeaturePDCM = 1 << 15, + kCPUFeaturePCID = 1 << 17, + kCPUFeatureDCA = 1 << 18, + kCPUFeatureSSE4_1 = 1 << 19, + kCPUFeatureSSE4_2 = 1 << 20, + kCPUFeatureX2APIC = 1 << 21, + kCPUFeatureMOVBE = 1 << 22, + kCPUFeaturePOP3C = 1 << 23, + kCPUFeatureECXTSC = 1 << 24, + kCPUFeatureAES = 1 << 25, + kCPUFeatureXSAVE = 1 << 26, + kCPUFeatureOSXSAVE = 1 << 27, + kCPUFeatureAVX = 1 << 28, + kCPUFeatureF16C = 1 << 29, + kCPUFeatureRDRAND = 1 << 30, + kCPUFeatureHYPERVISOR = 1 << 31, + kCPUFeatureFPU = 1 << 0, + kCPUFeatureVME = 1 << 1, + kCPUFeatureDE = 1 << 2, + kCPUFeaturePSE = 1 << 3, + kCPUFeatureEDXTSC = 1 << 4, + kCPUFeatureMSR = 1 << 5, + kCPUFeaturePAE = 1 << 6, + kCPUFeatureMCE = 1 << 7, + kCPUFeatureCX8 = 1 << 8, + kCPUFeatureAPIC = 1 << 9, + kCPUFeatureSEP = 1 << 11, + kCPUFeatureMTRR = 1 << 12, + kCPUFeaturePGE = 1 << 13, + kCPUFeatureMCA = 1 << 14, + kCPUFeatureCMOV = 1 << 15, + kCPUFeaturePAT = 1 << 16, + kCPUFeaturePSE36 = 1 << 17, + kCPUFeaturePSN = 1 << 18, + kCPUFeatureCLFLUSH = 1 << 19, + kCPUFeatureDS = 1 << 21, + kCPUFeatureACPI = 1 << 22, + kCPUFeatureMMX = 1 << 23, + kCPUFeatureFXSR = 1 << 24, + kCPUFeatureSSE = 1 << 25, + kCPUFeatureSSE2 = 1 << 26, + kCPUFeatureSS = 1 << 27, + kCPUFeatureHTT = 1 << 28, + kCPUFeatureTM = 1 << 29, + kCPUFeatureIA64 = 1 << 30, + kCPUFeaturePBE = 1 << 31 +}; + +typedef Int64 CPUID; +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/CxxAbi.cc b/src/kernel/HALKit/AMD64/CxxAbi.cc new file mode 100644 index 00000000..9049457b --- /dev/null +++ b/src/kernel/HALKit/AMD64/CxxAbi.cc @@ -0,0 +1,79 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include + +atexit_func_entry_t __atexit_funcs[kAtExitMacDestructors]; + +uarch_t __atexit_func_count; + +/// @brief dynamic shared object Handle. +Kernel::UIntPtr __dso_handle; + +EXTERN_C Kernel::Void __cxa_pure_virtual(void* self) { + (Kernel::Void)(Kernel::kout << "object: " + << Kernel::number(reinterpret_cast(self))); + (Kernel::Void)(Kernel::kout << ", has unimplemented virtual functions.\r"); +} + +EXTERN_C void ___chkstk_ms(PtrDiff frame_size) { + char* sp; + asm volatile("mov %%rsp, %0" : "=r"(sp)); + + for (PtrDiff offset = kPageSize; offset < frame_size; offset += kPageSize) { + sp[-offset] = 0; + } +} + +EXTERN_C int atexit(void (*f)()) { + if (__atexit_func_count >= kAtExitMacDestructors) return 1; + + __atexit_funcs[__atexit_func_count].destructor_func = f; + + __atexit_func_count++; + + return 0; +} + +EXTERN_C void __cxa_finalize(void* f) { + uarch_t i = __atexit_func_count; + if (!f) { + while (i--) { + if (__atexit_funcs[i].destructor_func) { + (*__atexit_funcs[i].destructor_func)(); + }; + } + + return; + } + + while (i--) { + if (__atexit_funcs[i].destructor_func) { + (*__atexit_funcs[i].destructor_func)(); + __atexit_funcs[i].destructor_func = 0; + }; + } +} + +namespace cxxabiv1 { +EXTERN_C int __cxa_guard_acquire(__guard g) { + if ((*g & 1) || (*g & 2)) return 1; + *g |= 2; + return 0; +} + +EXTERN_C void __cxa_guard_release(__guard g) { + *g |= 1; + *g &= 2; +} + +EXTERN_C void __cxa_guard_abort(__guard g) { + *g &= ~2; +} +} // namespace cxxabiv1 diff --git a/src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc b/src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc new file mode 100644 index 00000000..83545df8 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc @@ -0,0 +1,113 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include + +namespace Kernel { +namespace Detail { + struct FADT final : public SDT { + UInt32 FirmwareCtrl; + UInt32 Dsdt; + + // field used in ACPI 1.0; no longer in use, for compatibility only + UInt8 Reserved; + + UInt8 PreferredPowerManagementProfile; + UInt16 SCI_Interrupt; + UInt32 SMI_CommandPort; + UInt8 AcpiEnable; + UInt8 AcpiDisable; + UInt8 S4BIOS_REQ; + UInt8 PSTATE_Control; + UInt32 PM1aEventBlock; + UInt32 PM1bEventBlock; + UInt32 PM1aControlBlock; + UInt32 PM1bControlBlock; + UInt32 PM2ControlBlock; + UInt32 PMTimerBlock; + UInt32 GPE0Block; + UInt32 GPE1Block; + UInt8 PM1EventLength; + UInt8 PM1ControlLength; + UInt8 PM2ControlLength; + UInt8 PMTimerLength; + UInt8 GPE0Length; + UInt8 GPE1Length; + UInt8 GPE1Base; + UInt8 CStateControl; + UInt16 WorstC2Latency; + UInt16 WorstC3Latency; + UInt16 FlushSize; + UInt16 FlushStride; + UInt8 DutyOffset; + UInt8 DutyWidth; + UInt8 DayAlarm; + UInt8 MonthAlarm; + UInt8 Century; + + // reserved in ACPI 1.0; used since ACPI 2.0+ + UInt16 BootArchitecturkMMFlags; + + UInt8 Reserved2; + UInt32 Flags; + + // 12 byte structure; see below for details + ACPI_ADDRESS ResetReg; + + UInt8 ResetValue; + UInt8 Reserved3[3]; + + // 64bit pointers - Available on ACPI 2.0+ + UInt64 X_FirmwareControl; + UInt64 X_Dsdt; + + ACPI_ADDRESS X_PM1aEventBlock; + ACPI_ADDRESS X_PM1bEventBlock; + ACPI_ADDRESS X_PM1aControlBlock; + ACPI_ADDRESS X_PM1bControlBlock; + ACPI_ADDRESS X_PM2ControlBlock; + ACPI_ADDRESS X_PMTimerBlock; + ACPI_ADDRESS X_GPE0Block; + ACPI_ADDRESS X_GPE1Block; + }; +} // namespace Detail + +ACPIFactoryInterface::ACPIFactoryInterface(VoidPtr rsp_ptr) : fRsdp(rsp_ptr), fEntries(0) {} + +Bool ACPIFactoryInterface::Shutdown() { + return NO; +} + +/// @brief Reboot machine in either ACPI or by triple faulting. +/// @return nothing it's a reboot. +Void ACPIFactoryInterface::Reboot() { + asm volatile( + ".intel_syntax noprefix; " + "rt_reset_hardware:; " + "cli; " + "wait_gate1: ; " + "in al,0x64 ; " + "and al,2 ; " + "jnz wait_gate1 ; " + "mov al,0x0D1 ; " + "out 0x64,al ; " + "wait_gate2: ; " + "in al,0x64 ; " + "and al,2 ; " + "jnz wait_gate2 ; " + "mov al,0x0FE ; " + "out 0x60,al ; " + "xor rax,rax ; " + "lidt [rax] ; " + "reset_wait: ; " + "jmp reset_wait ; " + ".att_syntax; "); +} +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc b/src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc new file mode 100644 index 00000000..36a027a2 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc @@ -0,0 +1,39 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel::HAL { +/***********************************************************************************/ +/// Constructors. +/***********************************************************************************/ +LAPICDmaWrapper::LAPICDmaWrapper(VoidPtr base) : fApic(base) {} +LAPICDmaWrapper::~LAPICDmaWrapper() = default; + +/***********************************************************************************/ +/// @brief Read from APIC controller. +/// @param reg register. +/***********************************************************************************/ +UInt32 LAPICDmaWrapper::Read(UInt16 reg) noexcept { + MUST_PASS(this->fApic); + + UInt32 volatile* io_apic = (UInt32 volatile*) this->fApic; + return io_apic[reg]; +} + +/***********************************************************************************/ +/// @brief Write to APIC controller. +/// @param reg register. +/// @param value value. +/***********************************************************************************/ +Void LAPICDmaWrapper::Write(UInt16 reg, UInt32 value) noexcept { + MUST_PASS(this->fApic); + + UInt32 volatile* io_apic = (UInt32 volatile*) this->fApic; + io_apic[reg] = value; +} +} // namespace Kernel::HAL diff --git a/src/kernel/HALKit/AMD64/HalApplicationProcessor.cc b/src/kernel/HALKit/AMD64/HalApplicationProcessor.cc new file mode 100644 index 00000000..5a530457 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalApplicationProcessor.cc @@ -0,0 +1,221 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/// Different than the MADT, might be confusing to some. +#define APIC_MAG "APIC" + +#define APIC_ICR_LOW 0x300 +#define APIC_ICR_HIGH 0x310 +#define APIC_SIPI_VEC 0x00500 +#define APIC_EIPI_VEC 0x00400 + +#define LAPIC_REG_TIMER_LVT 0x320 +#define LAPIC_REG_TIMER_INITCNT 0x380 +#define LAPIC_REG_TIMER_CURRCNT 0x390 +#define LAPIC_REG_TIMER_DIV 0x3E0 +#define LAPIC_REG_ENABLE 0x80 +#define LAPIC_REG_SPURIOUS 0xF0 + +#define APIC_BASE_MSR 0x1B +#define APIC_BASE_MSR_BSP 0x100 +#define APIC_BASE_MSR_ENABLE 0x800 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////// + +/// @note: _hal_switch_context is internal. +/// @brief The **HAL** namespace. + +/////////////////////////////////////////////////////////////////////////////////////// + +namespace Kernel::HAL { +struct HAL_APIC_MADT; +struct HAL_HARDWARE_THREAD; + +struct HAL_HARDWARE_THREAD final { + StackFramePtr mFramePtr; + ProcessID mThreadID{0}; +}; + +EXTERN_C Void sched_jump_to_task(StackFramePtr stack_frame); + +STATIC HAL_APIC_MADT* kSMPBlock = nullptr; +STATIC Bool kSMPAware = false; +STATIC Int64 kSMPCount = 0; + +EXTERN_C UIntPtr kApicBaseAddress; + +STATIC Int32 kSMPInterrupt = 0; +STATIC UInt64 kAPICLocales[kMaxAPInsideSched] = {0}; +STATIC VoidPtr kRawMADT = nullptr; + +STATIC HAL_HARDWARE_THREAD kHWThread[kSchedProcessLimitPerTeam] = {{}}; + +/// @brief Multiple APIC Descriptor Table. +struct HAL_APIC_MADT final SDT_OBJECT { + UInt32 Address; // Madt address + UInt32 Flags; // Madt flags + UInt8 List[1]; // Records List +}; + +/// @brief Local APIC Descriptor Table. +struct LAPIC final { + UInt8 Type; + UInt8 Length; + UInt8 ProcessorID; + UInt8 APICID; + UInt32 Flags; +}; + +/////////////////////////////////////////////////////////////////////////////////////// + +/***********************************************************************************/ +/// @brief Send end IPI for CPU. +/// @param apic_id +/// @param vector +/// @param target +/// @return +/***********************************************************************************/ +EXTERN_C Void hal_send_ipi_msg(UInt32 target, UInt32 apic_id, UInt8 vector) { + Kernel::ke_dma_write(target, APIC_ICR_HIGH, apic_id << 24); + Kernel::ke_dma_write(target, APIC_ICR_LOW, 0x00000600 | 0x00004000 | 0x00000000 | vector); + + while (Kernel::ke_dma_read(target, APIC_ICR_LOW) & 0x1000) { + NE_UNUSED(0); + } +} + +/***********************************************************************************/ +/// @brief Get current stack frame for a thread. +/// @param thrdid The thread ID. +/***********************************************************************************/ + +EXTERN_C HAL::StackFramePtr mp_get_current_task(ThreadID thrdid) { + if (thrdid > kSMPCount) return nullptr; + return kHWThread[thrdid].mFramePtr; +} + +/***********************************************************************************/ +/// @brief Register current stack frame for a thread. +/// @param stack_frame The current stack frame. +/// @param thrdid The thread ID. +/***********************************************************************************/ + +EXTERN_C BOOL mp_register_task(HAL::StackFramePtr stack_frame, ThreadID thrdid) { + if (!stack_frame) return NO; + + if (!kSMPAware) { + sched_jump_to_task(kHWThread[thrdid].mFramePtr); + return YES; + } + + if (thrdid > kSMPCount) return NO; + + HardwareThreadScheduler::The()[thrdid].Leak()->Busy(NO); + kHWThread[thrdid].mFramePtr = stack_frame; + + return YES; +} + +/***********************************************************************************/ +/// @brief Is the current config SMP aware? +/// @return True if YES, False if not. +/***********************************************************************************/ + +Bool mp_is_smp(Void) noexcept { + return kSMPAware; +} + +/***********************************************************************************/ +/// @brief Fetch and enable SMP scheduler. +/// @param vendor_ptr SMP containing structure. +/***********************************************************************************/ + +Void mp_init_cores(VoidPtr vendor_ptr) noexcept { + if (!vendor_ptr) return; + + PowerFactoryInterface hw_and_pow_int{vendor_ptr}; + + auto pwr = hw_and_pow_int.Find(APIC_MAG); + + if (pwr.HasError()) { + kSMPAware = NO; + return; + } + + kRawMADT = pwr.Leak().Leak(); + kSMPBlock = reinterpret_cast(kRawMADT); + kSMPAware = NO; + + if (kSMPBlock) { + kSMPInterrupt = 0; + kSMPCount = 0; + + UInt32 lo = 0U, hi = 0U; + + hal_get_msr(APIC_BASE_MSR, &lo, &hi); + + UInt64 apic_base = ((UInt64) hi << 32) | lo; + + apic_base |= APIC_BASE_MSR_ENABLE; // Enable APIC. + + lo = apic_base & 0xFFFFFFFF; + hi = apic_base >> 32; + + hal_set_msr(APIC_BASE_MSR, lo, hi); + + kApicBaseAddress = apic_base & 0xFFFFF000; + + LAPICDmaWrapper controller{(VoidPtr) kApicBaseAddress}; + + controller.Write(LAPIC_REG_ENABLE, 0); + controller.Write(LAPIC_REG_SPURIOUS, 0x1FF); // Enable bit, spurious interrupt vector register. + controller.Write(LAPIC_REG_TIMER_DIV, 0b0011); + controller.Write(LAPIC_REG_TIMER_LVT, 0x20 | (1 << 17)); + controller.Write(LAPIC_REG_TIMER_INITCNT, 1000000); + + volatile UInt8* entry_ptr = reinterpret_cast(kSMPBlock->List); + volatile UInt8* end_ptr = ((UInt8*) kSMPBlock) + kSMPBlock->Length; + + while (entry_ptr < end_ptr) { + UInt8 type = *entry_ptr; + UInt8 length = *(entry_ptr + 1); + + if (type == 0 && length == sizeof(struct LAPIC)) { + volatile LAPIC* entry_struct = (volatile LAPIC*) entry_ptr; + + if (entry_struct->Flags & 0x1) { + kAPICLocales[kSMPCount] = entry_struct->ProcessorID; + kHWThread[kSMPCount].mThreadID = kAPICLocales[kSMPCount]; + + ++kSMPCount; + + kout << "AP: kind: LAPIC: ON.\r"; + } else { + kout << "AP: kind: LAPIC: OFF.\r"; + } + } else { + kout << "AP: kind: UNKNOWN: OFF.\r"; + } + + entry_ptr += length; + } + + kSMPAware = kSMPCount > 1; + } +} +} // namespace Kernel::HAL + +/////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/kernel/HALKit/AMD64/HalApplicationProcessorStartup.s b/src/kernel/HALKit/AMD64/HalApplicationProcessorStartup.s new file mode 100644 index 00000000..7b383404 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalApplicationProcessorStartup.s @@ -0,0 +1,23 @@ + /* + * ======================================================== + * + * NeKernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +.text + +.global hal_ap_blob_start +.global hal_ap_blob_length + +hal_ap_blob_start: + cli + hlt + jmp hal_ap_blob_start + +.data + +hal_ap_blob_length: + .long 4 diff --git a/src/kernel/HALKit/AMD64/HalCommonAPI.asm b/src/kernel/HALKit/AMD64/HalCommonAPI.asm new file mode 100644 index 00000000..9f3652bc --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalCommonAPI.asm @@ -0,0 +1,137 @@ +;; /* +;; * ======================================================== +;; * +;; * NeKernel +;; * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. +;; * +;; * ======================================================== +;; */ + +section .text + +extern rt_wait_400ns + +global rt_out8 +global rt_out16 +global rt_out32 + +global rt_in8 +global rt_in16 +global rt_in32 + +rt_out8: + mov al, dl + mov dx, cx + out dx, al + ret + +rt_out16: + mov ax, dx + mov dx, cx + out dx, ax + ret + +rt_out32: + mov eax, edx + mov edx, ecx + out dx, eax + ret + +rt_in8: + mov dx, cx + in al, dx + ret + +rt_in16: + mov edx, ecx + in ax, dx + ret + +rt_in32: + mov rdx, rcx + in eax, dx + ret + +extern hal_system_call_enter +global mp_system_call_handler + +mp_system_call_handler: + push rbp + mov rbp, rsp + + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + jmp hal_system_call_enter + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + + pop rbp + + o64 iret + + +section .text + +global sched_jump_to_task + +sched_jump_to_task: + push rbp + mov rbp, rsp + + mov ax, 0x30 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ax, 0x18 + ltr ax + + push 0x30 + mov rdx, [rcx + 0x08] + push rdx + o64 pushf + push 0x28 + mov rdx, [rcx + 0x00] + push rdx + + call sched_recover_registers + + o64 iret + +global sched_idle_task + +sched_idle_task: + jmp $ + ret + +sched_recover_registers: + push rbp + mov rbp, rsp + + mov r8, [rcx + 0x10] + mov r9, [rcx + 0x18] + mov r10, [rcx + 0x20] + mov r11, [rcx + 0x28] + mov r12, [rcx + 0x30] + mov r13, [rcx + 0x38] + mov r14, [rcx + 0x40] + mov r15, [rcx + 0x48] + + pop rbp + + ret \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/HalControlRegisterAPI.s b/src/kernel/HALKit/AMD64/HalControlRegisterAPI.s new file mode 100644 index 00000000..90fdeb81 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalControlRegisterAPI.s @@ -0,0 +1,45 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +.globl hal_write_cr3 +.globl hal_write_cr0 +.globl hal_read_cr2 +.globl hal_read_cr3 +.globl hal_read_cr0 +.globl hal_flush_tlb +.globl hal_invl_tlb + +.text + +hal_invl_tlb: + invlpg (%rcx) + retq + +hal_flush_tlb: + call hal_read_cr3 + mov %rax, %rcx + call hal_write_cr3 + retq + +hal_read_cr3: + movq %cr3, %rax + retq + +hal_read_cr0: + movq %cr0, %rax + retq + +hal_read_cr2: + movq %cr2, %rax + retq + +hal_write_cr3: + movq %rcx, %cr3 + retq + +hal_write_cr0: + movq %rcx, %cr0 + retq diff --git a/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc b/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc new file mode 100644 index 00000000..f19e49b5 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc @@ -0,0 +1,168 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include + +EXTERN_C Kernel::Void idt_handle_breakpoint(Kernel::UIntPtr rip); + +EXTERN_C Kernel::UIntPtr kApicBaseAddress; + +STATIC BOOL kIsRunning = NO; + +/// @brief Notify APIC and PIC that we're done with the interrupt. +/// @note +STATIC void hal_idt_send_eoi(UInt8 vector) { + ((volatile UInt32*) kApicBaseAddress)[0xB0 / 4] = 0; + + if (vector >= kPICCommand && vector <= 0x2F) { + if (vector >= 0x28) { + Kernel::HAL::rt_out8(kPIC2Command, kPICCommand); + } + Kernel::HAL::rt_out8(kPICCommand, kPICCommand); + } +} + +/// @brief Handle GPF fault. +/// @param rsp +EXTERN_C Kernel::Void idt_handle_gpf(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_idt_send_eoi(13); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Handle page fault. +/// @param rsp +EXTERN_C void idt_handle_pf(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_idt_send_eoi(14); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Handle scheduler interrupt. +EXTERN_C void idt_handle_scheduler(Kernel::UIntPtr rsp) { + NE_UNUSED(rsp); + + hal_idt_send_eoi(32); + + while (kIsRunning) + ; + + kIsRunning = YES; + + Kernel::UserProcessHelper::StartScheduling(); + + kIsRunning = NO; +} + +/// @brief Handle math fault. +/// @param rsp +EXTERN_C void idt_handle_math(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_idt_send_eoi(8); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = sig_generate_unique(); + ; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Handle any generic fault. +/// @param rsp +EXTERN_C void idt_handle_generic(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_idt_send_eoi(30); + + Kernel::kout << "Kernel: Generic Process Fault.\r"; + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = sig_generate_unique(); + ; + process.Leak().Signal.Status = process.Leak().Status; + + Kernel::kout << "Kernel: SIGKILL status.\r"; +} + +EXTERN_C Kernel::Void idt_handle_breakpoint(Kernel::UIntPtr rip) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + + hal_idt_send_eoi(3); + + process.Leak().Signal.SignalArg = rip; + process.Leak().Signal.SignalID = sig_generate_unique(); + + process.Leak().Signal.Status = process.Leak().Status; + + process.Leak().Status = Kernel::ProcessStatusKind::kFrozen; +} + +/// @brief Handle #UD fault. +/// @param rsp +EXTERN_C void idt_handle_ud(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_idt_send_eoi(6); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = sig_generate_unique(); + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Enter syscall from assembly (libSystem only) +/// @param stack the stack pushed from assembly routine. +/// @return nothing. +EXTERN_C Kernel::Void hal_system_call_enter(Kernel::UIntPtr rcx_hash, + Kernel::UIntPtr rdx_syscall_arg) { + hal_idt_send_eoi(50); + + if (!Kernel::kCurrentUser) return; + + for (SizeT i = 0UL; i < kMaxDispatchCallCount; ++i) { + if (kSysCalls[i].fHooked && rcx_hash == kSysCalls[i].fHash) { + if (kSysCalls[i].fProc) { + (kSysCalls[i].fProc)((Kernel::VoidPtr) rdx_syscall_arg); + } + } + } +} + +/// @brief Enter Kernel call from assembly (libDDK only). +/// @param stack the stack pushed from assembly routine. +/// @return nothing. +EXTERN_C Kernel::Void hal_kernel_call_enter(Kernel::UIntPtr rcx_hash, Kernel::SizeT cnt, + Kernel::UIntPtr arg, Kernel::SizeT sz) { + hal_idt_send_eoi(51); + + if (!Kernel::kRootUser) return; + if (Kernel::kCurrentUser != Kernel::kRootUser) return; + if (!Kernel::kCurrentUser->IsSuperUser()) return; + + for (SizeT i = 0UL; i < kMaxDispatchCallCount; ++i) { + if (kKernCalls[i].fHooked && rcx_hash == kKernCalls[rcx_hash].fHash) { + if (kKernCalls[i].fProc) { + (kKernCalls[i].fProc)(cnt, (Kernel::VoidPtr) arg, sz); + } + } + } +} diff --git a/src/kernel/HALKit/AMD64/HalCoreSystemCalls.cc b/src/kernel/HALKit/AMD64/HalCoreSystemCalls.cc new file mode 100644 index 00000000..f50e4abd --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalCoreSystemCalls.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + ======================================== */ + +#include + +using namespace Kernel; diff --git a/src/kernel/HALKit/AMD64/HalDebugOutput.cc b/src/kernel/HALKit/AMD64/HalDebugOutput.cc new file mode 100644 index 00000000..8e5f4cbd --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalDebugOutput.cc @@ -0,0 +1,227 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include + +namespace Kernel { +enum CommStatus : UInt16 { + kStateInvalid = 0x64, + kStateReady = 0xCF, + kStateTransmit = 0xFC, + kStateCnt = 3 +}; + +namespace Detail { + constexpr ATTRIBUTE(unused) const UInt16 kPort = 0x3F8; + STATIC ATTRIBUTE(unused) UInt16 kState = kStateInvalid; + + /// @brief Init COM1. + /// @return + template + bool hal_serial_init() noexcept { + if (kState == kStateReady || kState == kStateTransmit) return true; + + HAL::rt_out8(PORT + 1, 0x00); // Disable all interrupts + HAL::rt_out8(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + HAL::rt_out8(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud + HAL::rt_out8(PORT + 1, 0x00); // (hi byte) + HAL::rt_out8(PORT + 3, 0x03); // 8 bits, no parity, one stop bit + HAL::rt_out8(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + HAL::rt_out8(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + HAL::rt_out8(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip + HAL::rt_out8(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if + // serial returns same byte) + + // Check if serial is faulty (i.e: not same byte as sent) + if (HAL::rt_in8(PORT) != 0xAE) { + return false; + } + + kState = kStateReady; + + // If serial is not faulty set it in normal operation mode + // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) + HAL::rt_out8(PORT + 4, 0x0F); + + return true; + } +} // namespace Detail + +TerminalDevice::~TerminalDevice() = default; + +#ifdef __DEBUG__ +STATIC SizeT kX = kFontSizeX, kY = kFontSizeY; +#endif // __DEBUG__ + +EXTERN_C void ke_utf_io_write(DeviceInterface* obj, const Utf8Char* bytes) { + NE_UNUSED(bytes); + NE_UNUSED(obj); + +#ifdef __DEBUG__ + Detail::hal_serial_init(); + + if (!bytes || Detail::kState != kStateReady) return; + + if (*bytes == 0) return; + + Detail::kState = kStateTransmit; + + SizeT index = 0; + SizeT len = 0; + + index = 0; + len = urt_string_len(bytes); + + Char tmp_str[2]; + + while (index < len) { + if (bytes[index] == '\r') HAL::rt_out8(Detail::kPort, '\r'); + + HAL::rt_out8(Detail::kPort, bytes[index] == '\r' ? '\n' : bytes[index]); + + tmp_str[0] = (bytes[index] > 127) ? '?' : bytes[index]; + tmp_str[1] = 0; + + cg_render_string(tmp_str, kY, kX, RGB(0xff, 0xff, 0xff)); + + if (bytes[index] == '\r') { + kY += kFontSizeY; + kX = kFontSizeX; + } + + kX += kFontSizeX; + + if (kX > kHandoverHeader->f_GOP.f_Width) { + kX = kFontSizeX; + } + + if (kY > kHandoverHeader->f_GOP.f_Height) { + kY = kFontSizeY; + + FBDrawInRegion(cg_get_clear_clr(), FB::CGAccessibilty::Height(), FB::CGAccessibilty::Width(), + 0, 0); + } + + ++index; + } + + Detail::kState = kStateReady; +#endif // __DEBUG__ +} + +EXTERN_C void ke_io_write(DeviceInterface* obj, const Char* bytes) { + NE_UNUSED(bytes); + NE_UNUSED(obj); + +#ifdef __DEBUG__ + Detail::hal_serial_init(); + + if (!bytes || Detail::kState != kStateReady) return; + + if (*bytes == 0) return; + + Detail::kState = kStateTransmit; + + SizeT index = 0; + SizeT len = 0; + + index = 0; + len = rt_string_len(bytes); + + Char tmp_str[2]; + + while (index < len) { + if (bytes[index] == '\r') HAL::rt_out8(Detail::kPort, '\r'); + + HAL::rt_out8(Detail::kPort, bytes[index] == '\r' ? '\n' : bytes[index]); + + tmp_str[0] = bytes[index]; + tmp_str[1] = 0; + + cg_render_string(tmp_str, kY, kX, RGB(0xff, 0xff, 0xff)); + + if (bytes[index] == '\r') { + kY += kFontSizeY; + kX = kFontSizeX; + } + + kX += kFontSizeX; + + if (kX > kHandoverHeader->f_GOP.f_Width) { + kX = kFontSizeX; + } + + if (kY > kHandoverHeader->f_GOP.f_Height) { + kY = kFontSizeY; + + FBDrawInRegion(cg_get_clear_clr(), FB::CGAccessibilty::Height(), FB::CGAccessibilty::Width(), + 0, 0); + } + + ++index; + } + + Detail::kState = kStateReady; +#endif // __DEBUG__ +} + +EXTERN_C void ke_io_read(DeviceInterface*, const Char* bytes) { + NE_UNUSED(bytes); + +#ifdef __DEBUG__ + Detail::hal_serial_init(); + + if (!bytes || Detail::kState != kStateReady) return; + + Detail::kState = kStateTransmit; + + SizeT index = 0; + + ///! TODO: Look on how to wait for the UART to complete. + while (true) { + auto in = HAL::rt_in8(Detail::kPort); + + ///! If enter pressed then break. + if (in == 0xD) { + break; + } + + if (in < '0' || in < 'A' || in < 'a') { + if (in != '@' || in != '!' || in != '?' || in != '.' || in != '/' || in != ':') { + continue; + } + } + + ((char*) bytes)[index] = in; + + ++index; + } + + ((char*) bytes)[index] = 0; + + Detail::kState = kStateReady; +#endif // __DEBUG__ +} + +TerminalDevice TerminalDevice::The() noexcept { + TerminalDevice out(Kernel::ke_io_write, Kernel::ke_io_read); + return out; +} + +Utf8TerminalDevice::~Utf8TerminalDevice() = default; + +Utf8TerminalDevice Utf8TerminalDevice::The() noexcept { + Utf8TerminalDevice out(Kernel::ke_utf_io_write, + [](DeviceInterface*, const Utf8Char*) -> Void {}); + return out; +} + +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalDebugProtocol.cc b/src/kernel/HALKit/AMD64/HalDebugProtocol.cc new file mode 100644 index 00000000..1adfff3e --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalDebugProtocol.cc @@ -0,0 +1,16 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +//! @file DebuggerPort.cc +//! @brief UART debug via packets. + +#include +#include +#include + +// after that we have start of additional data. + +namespace Kernel {} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalDescriptorLoader.cc b/src/kernel/HALKit/AMD64/HalDescriptorLoader.cc new file mode 100644 index 00000000..65bf0b1e --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalDescriptorLoader.cc @@ -0,0 +1,71 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +namespace Kernel::HAL { +namespace Detail { + STATIC ::Kernel::Detail::AMD64::InterruptDescriptorAMD64 kInterruptVectorTable[kKernelIdtSize] = + {}; +} // namespace Detail + +/// @brief Loads the provided Global Descriptor Table. +/// @param gdt +/// @return +Void GDTLoader::Load(Register64& gdt) { +#ifndef __NE_MODULAR_KERNEL_COMPONENTS__ + hal_load_gdt(gdt); +#endif // __NE_MODULAR_KERNEL_COMPONENTS__ +} + +Void IDTLoader::Load(Register64& idt) { +#ifndef __NE_MODULAR_KERNEL_COMPONENTS__ + rt_cli(); + + volatile UIntPtr** ptr_ivt = (volatile UIntPtr**) idt.Base; + + for (SizeT idt_indx = 0; idt_indx < kKernelIdtSize; ++idt_indx) { + Detail::kInterruptVectorTable[idt_indx].Selector = kIDTSelector; + Detail::kInterruptVectorTable[idt_indx].Ist = 0; + Detail::kInterruptVectorTable[idt_indx].TypeAttributes = + kKernelInterruptId ? kUserInterruptGate : kInterruptGate; + Detail::kInterruptVectorTable[idt_indx].OffsetLow = ((UIntPtr) ptr_ivt[idt_indx] & 0xFFFF); + Detail::kInterruptVectorTable[idt_indx].OffsetMid = + (((UIntPtr) ptr_ivt[idt_indx] >> 16) & 0xFFFF); + Detail::kInterruptVectorTable[idt_indx].OffsetHigh = + (((UIntPtr) ptr_ivt[idt_indx] >> 32) & 0xFFFFFFFF); + + Detail::kInterruptVectorTable[idt_indx].Zero = 0; + } + + idt.Base = (UIntPtr) &Detail::kInterruptVectorTable[0]; + idt.Limit = sizeof(::Kernel::Detail::AMD64::InterruptDescriptorAMD64) * (kKernelIdtSize); + + hal_load_idt(idt); + rt_sti(); +#endif // __NE_MODULAR_KERNEL_COMPONENTS__ + + return; +} + +/// @brief Loads the Global Descriptor Table into the CPU. +/// @param gdt GDT register wrapped in a ref. +void GDTLoader::Load(Ref& gdt) { + if (!gdt) return; + + GDTLoader::Load(gdt.Leak()); +} + +/// @brief Loads the IDT, for interupts. +/// @param idt IDT register wrapped in a ref. +void IDTLoader::Load(Ref& idt) { + if (!idt) return; + + IDTLoader::Load(idt.Leak()); +} +} // namespace Kernel::HAL diff --git a/src/kernel/HALKit/AMD64/HalHandoverStub.asm b/src/kernel/HALKit/AMD64/HalHandoverStub.asm new file mode 100644 index 00000000..c9cabd1c --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalHandoverStub.asm @@ -0,0 +1,30 @@ +;; /* +;; * ======================================================== +;; * +;; * NeKernel +;; * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. +;; * +;; * ======================================================== +;; */ + +[bits 64] + +%define kTypeKernel 100 +%define kArchAmd64 122 +%define kHandoverMagic 0xBADCC + +global _HandoverMagic +global _HandoverType +global _HandoverPad +global _HandoverArch + +section .ldr + +_HandoverMagic: + dq kHandoverMagic +_HandoverType: + dw kTypeKernel +_HandoverPad: + dw 0 +_HandoverArch: + dw kArchAmd64 diff --git a/src/kernel/HALKit/AMD64/HalInterruptAPI.asm b/src/kernel/HALKit/AMD64/HalInterruptAPI.asm new file mode 100644 index 00000000..c761684e --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalInterruptAPI.asm @@ -0,0 +1,370 @@ +;; /* +;; * --------------------------------------------------- +;; * +;; * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. +;; * +;; * File: HalInterruptAPI.asm +;; * Purpose: Interrupt API, redirect raw interrupts into their handlers. +;; * +;; * --------------------------------------------------- +;; */ + +[bits 64] + +%define kInterruptId 50 + +%macro IntExp 1 +global __NE_INT_%1 +__NE_INT_%1: + cli + + std + + o64 iret +%endmacro + +%macro IntNormal 1 +global __NE_INT_%1 +__NE_INT_%1: + cli + + std + + add rsp, 8 + + o64 iret +%endmacro + +; This file handles the core interrupt table +; Last edited 31/01/24 + +global ke_handle_irq +global kInterruptVectorTable + +extern idt_handle_gpf +extern idt_handle_pf +extern ke_io_write +extern idt_handle_ud +extern idt_handle_generic +extern idt_handle_breakpoint +extern idt_handle_math + +section .text + +__NE_INT_0: + cli + push rcx + call idt_handle_generic + pop rcx + + std + + o64 iret + +__NE_INT_1: + cli + push rcx + call idt_handle_generic + pop rcx + + std + + o64 iret + +__NE_INT_2: + cli + push rcx + call idt_handle_generic + pop rcx + + std + + o64 iret + +;; @brief Triggers a breakpoint and freeze the process. RIP is also fetched. +__NE_INT_3: + cli + push rcx + call idt_handle_breakpoint + pop rcx + + std + + o64 iret + +__NE_INT_4: + cli + + push rcx + call idt_handle_generic + pop rcx + + std + + o64 iret + +__NE_INT_5: + cli + std + + o64 iret + +;; Invalid opcode interrupt +__NE_INT_6: + cli + push rcx + call idt_handle_ud + pop rcx + + std + + o64 iret + +__NE_INT_7: + cli + push rcx + call idt_handle_generic + pop rcx + + std + + o64 iret + +;; Invalid opcode interrupt +__NE_INT_8: + cli + + push rcx + call idt_handle_math + pop rcx + + std + + o64 iret + +IntNormal 9 +IntExp 10 +IntExp 11 + +IntExp 12 + +__NE_INT_13: + cli + + push rcx + call idt_handle_gpf + pop rcx + + std + + add rsp, 8 + + o64 iret + +__NE_INT_14: + cli + push rcx + call idt_handle_pf + pop rcx + + std + + o64 iret + +IntNormal 15 +IntNormal 16 +IntExp 17 +IntNormal 18 +IntNormal 19 +IntNormal 20 +IntNormal 21 + +IntNormal 22 + +IntNormal 23 +IntNormal 24 +IntNormal 25 +IntNormal 26 +IntNormal 27 +IntNormal 28 +IntNormal 29 +IntExp 30 +IntNormal 31 + +[extern idt_handle_scheduler] +[extern kApicBaseAddress] + +__NE_INT_32: + cli + + push rax + mov rcx, rsp + call idt_handle_scheduler + pop rax + + std + + o64 iret + +IntNormal 33 + +IntNormal 34 +IntNormal 35 +IntNormal 36 +IntNormal 37 +IntNormal 38 +IntNormal 39 + +[extern rtl_rtl8139_interrupt_handler] + +__NE_INT_40: + cli + + push rax + mov rcx, rsp + call rtl_rtl8139_interrupt_handler + pop rax + + std + + o64 iret + +IntNormal 41 + +IntNormal 42 +IntNormal 43 +IntNormal 44 +IntNormal 45 +IntNormal 46 +IntNormal 47 +IntNormal 48 +IntNormal 49 + +[extern hal_system_call_enter] +[extern hal_kernel_call_enter] + +__NE_INT_50: + cli + + push rax + mov rax, hal_system_call_enter + + mov rcx, r8 + mov rdx, r9 + mov r8, r10 + mov r9, r11 + + call rax + pop rax + + std + + o64 iret + +__NE_INT_51: + cli + + push rax + mov rax, hal_kernel_call_enter + + mov rcx, r8 + mov rdx, r9 + mov r8, r10 + mov r9, r11 + + call rax + pop rax + + std + + o64 iret + +IntNormal 52 + +IntNormal 53 +IntNormal 54 +IntNormal 55 +IntNormal 56 +IntNormal 57 +IntNormal 58 +IntNormal 59 +IntNormal 60 + +%assign i 61 +%rep 195 + IntNormal i +%assign i i+1 +%endrep + +section .text + +[global hal_load_gdt] + +hal_load_gdt: + cli + + lgdt [rcx] + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + mov rax, 0x08 + push rax + push hal_reload_segments + + o64 retf + +extern hal_real_init + +hal_reload_segments: + std + jmp hal_real_init + ret + +global hal_load_idt + +hal_load_idt: + lidt [rcx] + + ; Master PIC initialization + mov al, 0x11 ; Start initialization in cascade mode + out 0x20, al ; Send initialization command to Master PIC + out 0xA0, al ; Send initialization command to Slave PIC + + ; Remap the PIC to use vectors 32-39 for Master and 40-47 for Slave + mov al, 0x20 ; Set Master PIC offset to 32 + out 0x21, al ; Send offset to Master PIC + + mov al, 0x28 ; Set Slave PIC offset to 40 + out 0xA1, al ; Send offset to Slave PIC + + ; Configure Master PIC to inform Slave PIC at IRQ2 + mov al, 0x04 ; Tell Master PIC there is a Slave PIC at IRQ2 + out 0x21, al + + ; Configure Slave PIC identity + mov al, 0x02 ; Tell Slave PIC its cascade identity + out 0xA1, al + + ; Set both PICs to 8086 mode + mov al, 0x01 ; 8086 mode + out 0x21, al + out 0xA1, al + + ret + +section .data + +kInterruptVectorTable: + %assign i 0 + %rep 256 + dq __NE_INT_%+i + %assign i i+1 + %endrep + +kApicBaseAddress: + dq 0 \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/HalKernelMain.cc b/src/kernel/HALKit/AMD64/HalKernelMain.cc new file mode 100644 index 00000000..e23c5bc1 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalKernelMain.cc @@ -0,0 +1,160 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __NE_MODULAR_KERNEL_COMPONENTS__ +EXTERN_C Kernel::VoidPtr kInterruptVectorTable[]; + +/// @brief Kernel init function. +/// @param handover_hdr Handover boot header. +EXTERN_C Int32 hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { + using namespace Kernel; + + if (handover_hdr->f_Magic != kHandoverMagic && handover_hdr->f_Version != kHandoverVersion) { + return kEfiFail; + } + + HAL::rt_sti(); + + fw_init_efi((EfiSystemTable*) handover_hdr->f_FirmwareCustomTables[1]); + + Boot::ExitBootServices(handover_hdr->f_HardwareTables.f_ImageKey, + handover_hdr->f_HardwareTables.f_ImageHandle); + + kHandoverHeader = handover_hdr; + + kKernelVM = kHandoverHeader->f_PageStart; + + if (!kKernelVM) { + MUST_PASS(kKernelVM); + return kEfiFail; + } + + hal_write_cr3(kKernelVM); + + /************************************** */ + /* INITIALIZE BIT MAP. */ + /************************************** */ + + kBitMapCursor = 0UL; + kKernelBitMpSize = kHandoverHeader->f_BitMapSize; + kKernelBitMpStart = + reinterpret_cast(reinterpret_cast(kHandoverHeader->f_BitMapStart)); + + /************************************** */ + /* INITIALIZE GDT AND SEGMENTS. */ + /************************************** */ + + STATIC CONST auto kGDTEntriesCount = 8; + + STATIC HAL::Detail::NE_TSS kKernelTSS{}; + + kKernelTSS.fRsp0 = (UInt64) kHandoverHeader->f_StackTop; + kKernelTSS.fIopb = sizeof(HAL::Detail::NE_TSS); + + /* The GDT, mostly descriptors for user and kernel segments. */ + STATIC HAL::Detail::NE_GDT_ENTRY ALIGN(0x08) kGDTArray[kGDTEntriesCount] = { + {.fLimitLow = 0, + .fBaseLow = 0, + .fBaseMid = 0, + .fAccessByte = 0x00, + .fFlags = 0x00, + .fBaseHigh = 0}, // Null entry + {.fLimitLow = 0x0, + .fBaseLow = 0, + .fBaseMid = 0, + .fAccessByte = 0x9A, + .fFlags = 0xAF, + .fBaseHigh = 0}, // Kernel code + {.fLimitLow = 0x0, + .fBaseLow = 0, + .fBaseMid = 0, + .fAccessByte = 0x92, + .fFlags = 0xCF, + .fBaseHigh = 0}, // Kernel data + {}, // TSS data low + {}, // TSS data high + {.fLimitLow = 0x0, + .fBaseLow = 0, + .fBaseMid = 0, + .fAccessByte = 0xFA, + .fFlags = 0xAF, + .fBaseHigh = 0}, // User code + {.fLimitLow = 0x0, + .fBaseLow = 0, + .fBaseMid = 0, + .fAccessByte = 0xF2, + .fFlags = 0xCF, + .fBaseHigh = 0}, // User data + }; + + kGDTArray[3].fLimitLow = sizeof(HAL::Detail::NE_TSS) - 1; + kGDTArray[3].fBaseLow = ((UIntPtr) &kKernelTSS) & 0xFFFF; + kGDTArray[3].fBaseMid = (((UIntPtr) &kKernelTSS) >> 16) & 0xFF; + kGDTArray[3].fAccessByte = 0x89; // Present, type 9 = 64-bit available TSS + kGDTArray[3].fFlags = 0x20 | ((((UIntPtr) &kKernelTSS) >> 24) & 0x0F); + kGDTArray[3].fBaseHigh = (((UIntPtr) &kKernelTSS) >> 24) & 0xFF; + + kGDTArray[4].fLimitLow = ((UIntPtr) &kKernelTSS >> 32) & 0xFFFF; + kGDTArray[4].fBaseLow = 0; + kGDTArray[4].fBaseMid = 0; + kGDTArray[4].fAccessByte = 0; + kGDTArray[4].fFlags = 0; + kGDTArray[4].fBaseHigh = 0; + + FB::cg_clear_video(); + + // Load memory descriptors. + HAL::Register64 gdt_reg; + + gdt_reg.Base = reinterpret_cast(kGDTArray); + gdt_reg.Limit = (sizeof(HAL::Detail::NE_GDT_ENTRY) * kGDTEntriesCount) - 1; + + //! GDT will load hal_read_init after it successfully loads the segments. + HAL::GDTLoader gdt_loader; + gdt_loader.Load(gdt_reg); + + return kEfiFail; +} + +EXTERN_C Kernel::Void hal_real_init(Kernel::Void) { +#ifdef __FSKIT_INCLUDES_OPENHEFS__ + OpenHeFS::fs_init_openhefs(); +#endif + +#ifdef __FSKIT_INCLUDES_NEFS__ + NeFS::fs_init_nefs(); +#endif + + UserProcessScheduler::The().SwitchTeam(kHighUserTeam); + + rtl_create_user_process([]() -> void { while (YES); }, "NeKernel"); + + HAL::mp_init_cores(kHandoverHeader->f_HardwareTables.f_VendorPtr); + + HAL::Register64 idt_reg; + idt_reg.Base = reinterpret_cast(kInterruptVectorTable); + + HAL::IDTLoader idt_loader; + idt_loader.Load(idt_reg); + + while (YES) + ; +} +#endif // ifndef __NE_MODULAR_KERNEL_COMPONENTS__ diff --git a/src/kernel/HALKit/AMD64/HalKernelPanic.cc b/src/kernel/HALKit/AMD64/HalKernelPanic.cc new file mode 100644 index 00000000..12538667 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalKernelPanic.cc @@ -0,0 +1,58 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Each error code is attributed with an ID, which will prompt a string onto the + * screen. Wait for debugger... */ + +namespace Kernel { +/// @brief Dumping factory class. +class RecoveryFactory final { + public: + STATIC Void Recover() noexcept; +}; + +/***********************************************************************************/ +/// @brief Stops execution of the kernel. +/// @param id kernel stop ID. +/***********************************************************************************/ +Void ke_panic(const Kernel::Int32& id, const Char* message) { + (Void)(kout << "*** STOP ***\r"); + + (Void)(kout << "Kernel_Panic_MSG: " << message << kendl); + (Void)(kout << "Kernel_Panic_ID: " << hex_number(id) << kendl); + (Void)(kout << "Kernel_Panic_CR2: " << hex_number((UIntPtr) hal_read_cr2()) << kendl); + + RecoveryFactory::Recover(); +} + +Void RecoveryFactory::Recover() noexcept { + while (YES) { + HAL::rt_halt(); + } +} + +void ke_runtime_check(bool expr, const Char* file, const Char* line) { + if (!expr) { + (Void)(kout << "*** CHECK ***\r"); + + (Void)(kout << "Kernel_Panic_FILE: " << file << kendl); + (Void)(kout << "Kernel_Panic_LINE: " << line << kendl); + + ke_panic(RUNTIME_CHECK_FAILED, file); // Runtime Check failed + } +} +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalPagingMgr.cc b/src/kernel/HALKit/AMD64/HalPagingMgr.cc new file mode 100644 index 00000000..4043da96 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalPagingMgr.cc @@ -0,0 +1,167 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: HalPagingMgr.cc + Purpose: Platform Paging Manager. + +======================================== */ + +#include +#include + +namespace Kernel::HAL { +namespace Detail { + /// @brief Page Table Entry for AMD64. + struct PTE { + UInt64 Present : 1; + UInt64 Wr : 1; + UInt64 User : 1; + UInt64 Pwt : 1; // Page-level Write-Through + UInt64 Pcd : 1; // Page-level Cache Disable + UInt64 Accessed : 1; + UInt64 Dirty : 1; + UInt64 Pat : 1; // Page Attribute Table (or PS for PDE) + UInt64 Global : 1; + UInt64 Ignored1 : 3; // Available to software + UInt64 PhysicalAddress : 40; // Physical page frame address (bits 12–51) + UInt64 Ignored2 : 7; // More software bits / reserved + UInt64 ProtectionKey : 4; // Optional (if PKU enabled) + UInt64 Reserved : 1; // Usually reserved + UInt64 Nx : 1; // No Execute + }; +} // namespace Detail + +/***********************************************************************************/ +/// \brief Retrieve the page status of a PTE. +/// \param pte Page Table Entry pointer. +/***********************************************************************************/ +STATIC Void mmi_page_status(Detail::PTE* pte) { + NE_UNUSED(pte); + +#ifdef __NE_VERBOSE_BITMAP__ + (Void)(kout << "Flag: " << (pte->Present ? "Present" : "Not Present") << kendl); + (Void)(kout << "Flag: " << (pte->Wr ? "W/R" : "Not W/R") << kendl); + (Void)(kout << "Flag: " << (pte->Nx ? "NX" : "Not NX") << kendl); + (Void)(kout << "Flag: " << pte->User ? "User" : "Not User") << kendl); + (Void)(kout << "Flag: " << (pte->Pcd ? "Not Cached" : "Cached") << kendl); + (Void)(kout << "Flag: " << (pte->Accessed ? "Accessed" : "Not Accessed") << kendl); + (Void)(kout << "Flag: " << (pte->ProtectionKey ? "Protected" : "Not Protected/PKU Disabled") + << kendl); + (Void)(kout << "Physical Address: " << hex_number(pte->PhysicalAddress) << kendl); +#endif +} + +/***********************************************************************************/ +/// @brief Gets a physical address from a virtual address. +/// @param virt a valid virtual address. +/// @return Physical address. +/***********************************************************************************/ +EXTERN_C UIntPtr mm_get_page_addr(VoidPtr virt) { + const UInt64 kVMAddr = (UInt64) virt; + const UInt64 kMask9Bits = 0x1FFULL; + const UInt64 kPageOffsetMask = 0xFFFULL; + + UInt64 cr3 = (UInt64) hal_read_cr3() & ~kPageOffsetMask; + + // Level 4 + auto pml4 = reinterpret_cast(cr3); + UInt64 pml4e = pml4[(kVMAddr >> 39) & kMask9Bits]; + + if (!(pml4e & 1)) return 0; + + // Level 3 + auto pdpt = reinterpret_cast(pml4e & ~kPageOffsetMask); + UInt64 pdpte = pdpt[(kVMAddr >> 30) & kMask9Bits]; + + if (!(pdpte & 1)) return 0; + + // Level 2 + auto pd = reinterpret_cast(pdpte & ~kPageOffsetMask); + UInt64 pde = pd[(kVMAddr >> 21) & kMask9Bits]; + + if (!(pde & 1)) return 0; + + // 1 GiB page support + if (pde & (1 << 7)) { + return (pde & ~((1ULL << 30) - 1)) | (kVMAddr & ((1ULL << 30) - 1)); + } + + // Level 1 + auto pt = reinterpret_cast(pde & ~kPageOffsetMask); + Detail::PTE* pte = (Detail::PTE*) pt[(kVMAddr >> 12) & kMask9Bits]; + + if (!pte->Present) return 0; + + mmi_page_status((Detail::PTE*) pte); + + return (pte->PhysicalAddress << 12) | (kVMAddr & 0xFFF); +} + +/***********************************************************************************/ +/// @brief clflush+mfence helper function. +/***********************************************************************************/ +EXTERN_C Int32 mm_memory_fence(VoidPtr virtual_address) { + if (!virtual_address || !mm_get_page_addr(virtual_address)) return kErrorInvalidData; + + asm volatile("clflush (%0)" : : "r"(virtual_address) : "memory"); + asm volatile("mfence" ::: "memory"); + + return kErrorSuccess; +} + +/***********************************************************************************/ +/// @brief Maps or allocates a page from virtual_address. +/// @param virtual_address a valid virtual address. +/// @param phys_addr point to physical address. +/// @param flags the flags to put on the page. +/// @return Status code of page manipulation process. +/***********************************************************************************/ +EXTERN_C Int32 mm_map_page(VoidPtr virtual_address, VoidPtr physical_address, UInt32 flags, + UInt32 level) { + if (physical_address == 0) return kErrorInvalidData; + + NE_UNUSED(level); /// @todo support PML4, and PDPT levels. + + const UInt64 kVMAddr = (UInt64) virtual_address; + constexpr UInt64 kMask9 = 0x1FF; + constexpr UInt64 kPageMask = 0xFFF; + + UInt64 cr3 = (UIntPtr) hal_read_cr3() & ~kPageMask; + + auto pml4 = reinterpret_cast(cr3); + UInt64 pml4e = pml4[(kVMAddr >> 39) & kMask9]; + + if (!(pml4e & 1)) return kErrorInvalidData; + + UInt64* pdpt = reinterpret_cast(pml4e & ~kPageMask); + UInt64 pdpte = pdpt[(kVMAddr >> 30) & kMask9]; + + if (!(pdpte & 1)) return kErrorInvalidData; + + UInt64* pd = reinterpret_cast(pdpte & ~kPageMask); + UInt64 pde = pd[(kVMAddr >> 21) & kMask9]; + + if (!(pde & 1)) return kErrorInvalidData; + + UInt64* pt = reinterpret_cast(pde & ~kPageMask); + Detail::PTE* pte = (Detail::PTE*) pt[(kVMAddr >> 12) & kMask9]; + + pte->Present = !!(flags & kMMFlagsPresent); + pte->Wr = !!(flags & kMMFlagsWr); + pte->User = !!(flags & kMMFlagsUser); + pte->Nx = !!(flags & kMMFlagsNX); + pte->Pcd = !!(flags & kMMFlagsPCD); + pte->Pwt = !!(flags & kMMFlagsPwt); + + pte->PhysicalAddress = ((UIntPtr) (physical_address)) >> 12; + + hal_invl_tlb(virtual_address); + + mm_memory_fence(virtual_address); + + mmi_page_status(pte); + + return kErrorSuccess; +} +} // namespace Kernel::HAL diff --git a/src/kernel/HALKit/AMD64/HalProcessor.cc b/src/kernel/HALKit/AMD64/HalProcessor.cc new file mode 100644 index 00000000..d202a758 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalProcessor.cc @@ -0,0 +1,89 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: HalCPU.cc + Purpose: Platform processor routines. + +======================================== */ + +#include +#include + +/** + * @file HalCPU.cc + * @brief Common CPU API. + */ + +namespace Kernel::HAL { +inline Bool hal_has_msr() noexcept { + static UInt32 eax, unused, edx; // eax, edx + + __get_cpuid(1, &eax, &unused, &unused, &edx); + + // edx returns the flag for MSR (which is 1 shifted to 5.) + return edx & (1 << 5); +} + +Void hal_get_msr(UInt32 msr, UInt32* lo, UInt32* hi) noexcept { + if (!lo || !hi) return; + asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); +} + +Void hal_set_msr(UInt32 msr, UInt32 lo, UInt32 hi) noexcept { + asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); +} + +Void lrt_hal_out8(UInt16 port, UInt8 value) { + asm volatile("outb %%al, %1" : : "a"(value), "Nd"(port) : "memory"); +} + +Void lrt_hal_out16(UInt16 port, UInt16 value) { + asm volatile("outw %%ax, %1" : : "a"(value), "Nd"(port) : "memory"); +} + +Void lrt_hal_out32(UInt16 port, UInt32 value) { + asm volatile("outl %%eax, %1" : : "a"(value), "Nd"(port) : "memory"); +} + +UInt8 lrt_hal_in8(UInt16 port) { + UInt8 value = 0UL; + asm volatile("inb %1, %%al" : "=a"(value) : "Nd"(port) : "memory"); + + return value; +} + +UInt16 lrt_hal_in16(UInt16 port) { + UInt16 value = 0UL; + asm volatile("inw %1, %%ax" : "=a"(value) : "Nd"(port) : "memory"); + + return value; +} + +UInt32 lrt_hal_in32(UInt16 port) { + UInt32 value = 0UL; + asm volatile("inl %1, %%eax" : "=a"(value) : "Nd"(port) : "memory"); + + return value; +} + +Void rt_halt() { + asm volatile("hlt"); +} + +Void rt_cli() { + asm volatile("cli"); +} + +Void rt_sti() { + asm volatile("sti"); +} + +Void rt_cld() { + asm volatile("cld"); +} + +Void rt_std() { + asm volatile("std"); +} +} // namespace Kernel::HAL diff --git a/src/kernel/HALKit/AMD64/HalRoutineWait.s b/src/kernel/HALKit/AMD64/HalRoutineWait.s new file mode 100644 index 00000000..89051ba4 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalRoutineWait.s @@ -0,0 +1,11 @@ +.globl rt_wait_400ns + +.section .text +rt_wait_400ns: + jmp .loop + pause + .loop: + jmp .loop2 + pause + .loop2: + ret diff --git a/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc b/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc new file mode 100644 index 00000000..44ec2a37 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief Unimplemented function (crashes by default) +/// @param +/***********************************************************************************/ + +EXTERN_C Void __ne_pure_call(USER_PROCESS* process) { + if (process) process->Crash(); +} + +/***********************************************************************************/ +/// @brief Validate user stack. +/// @param stack_ptr the frame pointer. +/***********************************************************************************/ + +EXTERN_C Bool hal_check_task(HAL::StackFramePtr stack_ptr) { + if (!stack_ptr) return No; + + return stack_ptr->SP > 0 && stack_ptr->IP > 0; +} + +/// @brief Wakes up thread. +/// Wakes up thread from the hang state. +Void mp_wakeup_thread(HAL::StackFrame* stack) { + if (!hal_check_task(stack)) return; + + // RIP is always in R15. R15 is reserved for the RIP. + stack->IP = stack->R15; + + Kernel::UserProcessHelper::StartScheduling(); +} + +/// @brief makes the thread sleep on a loop. +/// hooks and hangs thread to prevent code from executing. +Void mp_hang_thread(HAL::StackFrame* stack) { + if (!hal_check_task(stack)) return; + + // Store IP in R15 + stack->R15 = stack->IP; + stack->IP = 0UL; +} +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalTimer.cc b/src/kernel/HALKit/AMD64/HalTimer.cc new file mode 100644 index 00000000..f6488b05 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalTimer.cc @@ -0,0 +1,101 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: HalTimer.cc + Purpose: HAL timer + + Revision History: + + 07/07/24: Added file (amlel) + +======================================== */ + +#include +#include +#include + +/// ================================================================================ +/// @note timer slot 0 +/// ================================================================================ + +#define kHPETSignature ("HPET") + +#define kHPETCounterRegValue (0x00) +#define kHPETConfigRegValue (0x20) +#define kHPETCompRegValue (0x24) +#define kHPETInterruptRegValue (0x2C) + +/// ================================================================================ +///! BUGS: 0 +///! @file HalTimer.cc +///! @brief Hardware Timer (HPET) +/// ================================================================================ + +namespace Kernel::Detail { +struct HPET_BLOCK : public Kernel::SDT { + Kernel::UInt8 hardware_rev_id; + Kernel::UInt8 comparator_count : 5; + Kernel::UInt8 counter_size : 1; + Kernel::UInt8 reserved : 1; + Kernel::UInt8 legacy_replacement : 1; + Kernel::UInt16 pci_vendor_id; + ACPI_ADDRESS address; + Kernel::UInt8 hpet_number; + Kernel::UInt16 minimum_tick; + Kernel::UInt8 page_protection; +} PACKED; +} // namespace Kernel::Detail + +using namespace Kernel; + +HardwareTimer::HardwareTimer(UInt64 ms) : fWaitFor(ms) { + auto power = PowerFactoryInterface(kHandoverHeader->f_HardwareTables.f_VendorPtr); + + auto hpet = (Detail::HPET_BLOCK*) power.Find(kHPETSignature).Leak().Leak(); + MUST_PASS(hpet); + + fDigitalTimer = (UInt8*) hpet->address.Address; + + if (hpet->page_protection) { + HAL::mm_map_page((VoidPtr) fDigitalTimer, (VoidPtr) fDigitalTimer, + HAL::kMMFlagsWr | HAL::kMMFlagsPCD | HAL::kMMFlagsPwt); + } + + // if not enabled yet. + if (!(*((volatile UInt64*) ((UInt8*) fDigitalTimer + kHPETConfigRegValue)) & (1 << 0))) { + *((volatile UInt64*) ((UInt8*) fDigitalTimer + kHPETConfigRegValue)) = + *((volatile UInt64*) ((UInt8*) fDigitalTimer + kHPETConfigRegValue)) | (1 << 0) | + (1 << 3); // enable timer & one shot conf + } +} + +HardwareTimer::~HardwareTimer() { + fDigitalTimer = nullptr; + fWaitFor = 0; +} + +/***********************************************************************************/ +/// @brief Wait for the timer to stop spinning. +/***********************************************************************************/ + +BOOL HardwareTimer::Wait() noexcept { + if (fWaitFor < 1) return NO; + if (fWaitFor > 1'000'000) return NO; // max 1000s = 16 minutes + + UInt64 hpet_cap = *((volatile UInt64*) (fDigitalTimer)); + UInt64 femtoseconds_per_tick = (hpet_cap >> 32); + + if (femtoseconds_per_tick == 0) return NO; + + volatile UInt64* timer = (volatile UInt64*) (fDigitalTimer + kHPETCounterRegValue); + + UInt64 now = *timer; + + UInt64 fs_wait = fWaitFor * 1'000'000'000'000ULL; + UInt64 stop_at = now + (fs_wait / femtoseconds_per_tick); + + while (*timer < (stop_at)) asm volatile("pause"); + + return YES; +} diff --git a/src/kernel/HALKit/AMD64/HalUtilsAPI.asm b/src/kernel/HALKit/AMD64/HalUtilsAPI.asm new file mode 100644 index 00000000..2a0a5eff --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalUtilsAPI.asm @@ -0,0 +1,24 @@ +;; /* +;; * ======================================================== +;; * +;; * NeKernel +;; * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. +;; * +;; * ======================================================== +;; */ + +[bits 64] + +[global rt_install_tib] + +section .text + +;; changed: rs, fs +;; expected: rcx, rdx + +rt_install_tib: + mov rcx, gs ;; TIB -> Thread Information Block + mov rdx, fs ;; PIB -> Process Information Block + ret + +;; //////////////////////////////////////////////////// ;; diff --git a/src/kernel/HALKit/AMD64/Hypervisor.h b/src/kernel/HALKit/AMD64/Hypervisor.h new file mode 100644 index 00000000..4cacb003 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Hypervisor.h @@ -0,0 +1,24 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +MAKE_STRING_ENUM(HYPERVISOR) +ENUM_STRING(Qemu, "TCGTCGTCGTCG"); +ENUM_STRING(KVM, " KVMKVMKVM "); +ENUM_STRING(VMWare, "VMwareVMware"); +ENUM_STRING(VirtualBox, "VBoxVBoxVBox"); +ENUM_STRING(Xen, "XenVMMXenVMM"); +ENUM_STRING(Microsoft, "Microsoft Hv"); +ENUM_STRING(Parallels, " prl hyperv "); +ENUM_STRING(ParallelsAlt, " lrpepyh vr "); +ENUM_STRING(Bhyve, "bhyve bhyve "); +ENUM_STRING(Qnx, " QNXQVMBSQG "); +END_STRING_ENUM() +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc b/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc new file mode 100644 index 00000000..f9865139 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc @@ -0,0 +1,129 @@ +/* ======================================== + +Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +using namespace Kernel; +using namespace Kernel::HAL; + +STATIC UInt16 kRTLIOBase = 0xFFFF; + +STATIC BOOL kTXRXEnabled = NO; + +STATIC UInt32 kRXOffset = 0UL; +STATIC constexpr CONST UInt32 kRXBufferSize = 8192 + 16 + 1500; + +STATIC UInt8* kRXUpperLayer = nullptr; +STATIC UInt8* kRXBuffer = nullptr; + +/***********************************************************************************/ +///@brief RTL8139 Init routine. +/***********************************************************************************/ + +EXTERN_C BOOL rtl_init_nic_rtl8139(UInt16 io_base) noexcept { + if (kTXRXEnabled) return NO; + + kRTLIOBase = io_base; + + MUST_PASS(io_base != 0xFFFF); + + kRXBuffer = reinterpret_cast(rtl_dma_alloc(sizeof(UInt8) * kRXBufferSize, 0)); + + MUST_PASS(kRXBuffer); + + /// Reset first. + + rt_out8(io_base + 0x37, 0x10); + + UInt16 timeout = 0U; + + while (rt_in8(io_base + 0x37) & 0x10) { + ++timeout; + if (timeout > 0x1000) break; + } + + if (timeout <= 0x1000) { + return NO; + } + + rt_out32(io_base + 0x30, (UInt32) (UIntPtr) kRXBuffer); + + rt_out8(io_base + 0x37, 0x0C); + + rt_out32(io_base + 0x44, 0xF | (1 << 7)); + + rt_out16(io_base + 0x3C, 0x0005); + + kTXRXEnabled = YES; + + return YES; +} + +/***********************************************************************************/ +/// @brief RTL8139 I/O interrupt handler. +/// @param rsp stack pointer. +/// @note This function is called when the device interrupts to retrieve network data. +/***********************************************************************************/ + +EXTERN_C Void rtl_rtl8139_interrupt_handler(UIntPtr rsp) { + if (kRTLIOBase == 0xFFFF || kRTLIOBase == 0) return; + + NE_UNUSED(rsp); + + UInt16 status = rt_in16(kRTLIOBase + 0x3E); + rt_out16(kRTLIOBase + 0x3E, status); + + if (status & 0x01) { + // While we receive data. + while ((rt_in8(kRTLIOBase + 0x37) & 0x01) == 0) { + // We grab an offset from the RX buffer. + UInt32 offset = kRXOffset % kRXBufferSize; + + // If the offset is too high, we reset it. + if (offset >= (kRXBufferSize - 16)) { + kRXOffset = 0UL; + offset = 0UL; + } + + volatile UInt8* packet = kRXBuffer + offset + 4; + UInt16 len = *(UInt16*) (kRXBuffer + offset + 2); + + kRXUpperLayer[(offset + 4)] = *packet; + kRXOffset += (len + 4); + + rt_out16(kRTLIOBase + 0x38, (UInt16) (kRXOffset - 16)); + } + } + + if (!(status & 0x04)) { + err_global_get() = kErrorNoNetwork; + } +} + +/***********************************************************************************/ +/// @brief RTL8139 get upper layer function +/// @return the upper layer. +/// @retval nullptr if no upper layer is set. +/// @retval pointer to the upper layer if set. +/***********************************************************************************/ + +EXTERN_C UInt8* rtl_rtl8139_get_upper_layer() { + return kRXUpperLayer; +} + +/***********************************************************************************/ +/// @brief RTL8139 set upper layer function +/// @param layer the upper layer. +/***********************************************************************************/ + +EXTERN_C BOOL rtl_rtl8139_set_upper_layer(UInt8* layer) { + if (!layer) return NO; + kRXUpperLayer = layer; + + return YES; +} diff --git a/src/kernel/HALKit/AMD64/PCI/DMA.cc b/src/kernel/HALKit/AMD64/PCI/DMA.cc new file mode 100644 index 00000000..809494b6 --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/DMA.cc @@ -0,0 +1,72 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +DMAWrapper::operator bool() { + return this->fAddress; +} + +bool DMAWrapper::operator!() { + return !this->fAddress; +} + +Boolean DMAWrapper::Check(UIntPtr offset) const { + if (!this->fAddress) return false; + + if (offset == 0) return false; + + kout << "[DMAWrapper::IsIn] Checking offset...\r"; + return reinterpret_cast(this->fAddress) >= offset; +} + +bool DMAWrapper::Write(UIntPtr& bit, const UInt32& offset) { + kout << "[DMAWrapper::Read] Checking this->fAddress...\r"; + + if (!this->fAddress) return false; + + (Void)(kout << "[DMAWrapper::Write] Writing at address: " + << hex_number(reinterpret_cast(this->fAddress) + offset) << kendl); + + ke_dma_write(reinterpret_cast(this->fAddress), offset, bit); + + return true; +} + +UIntPtr DMAWrapper::Read(const UInt32& offset) { + kout << "[DMAWrapper::Read] Checking this->fAddress...\r"; + + if (!this->fAddress) return ~0; + + (Void)(kout << "[DMAWrapper::Write] Writing at address: " + << hex_number(reinterpret_cast(this->fAddress) + offset) << kendl); + + return (UIntPtr) ke_dma_read(reinterpret_cast(this->fAddress), offset); +} + +UIntPtr DMAWrapper::operator[](UIntPtr& offset) { + return this->Read(offset); +} + +OwnPtr> DMAFactory::Construct(OwnPtr& dma) { + if (!dma) return {}; + + OwnPtr> dmaOwnPtr = + mm_make_own_ptr, char*>(reinterpret_cast(dma->fAddress)); + + if (!dmaOwnPtr) return {}; + + kout << "Returning the new OwnPtr>!\r"; + return dmaOwnPtr; +} + +DMAWrapper& DMAWrapper::operator=(voidPtr Ptr) { + this->fAddress = Ptr; + return *this; +} +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/PCI/Database.cc b/src/kernel/HALKit/AMD64/PCI/Database.cc new file mode 100644 index 00000000..ba3e946c --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Database.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/PCI/Device.cc b/src/kernel/HALKit/AMD64/PCI/Device.cc new file mode 100644 index 00000000..65af2f25 --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Device.cc @@ -0,0 +1,142 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +#define PCI_BAR_IO (0x01) +#define PCI_BAR_LOWMEM (0x02) +#define PCI_BAR_64 (0x04) +#define PCI_BAR_PREFETCH (0x08) +#define PCI_ENABLE_BIT (0x80000000) + +static Kernel::UInt NE_PCIReadRaw(Kernel::UInt bar, Kernel::UShort bus, Kernel::UShort dev, + Kernel::UShort fun) { + Kernel::UInt target = PCI_ENABLE_BIT | ((Kernel::UInt) bus << 16) | ((Kernel::UInt) dev << 11) | + ((Kernel::UInt) fun << 8) | (bar & 0xFC); + + Kernel::HAL::rt_out32((Kernel::UShort) Kernel::PCI::PciConfigKind::ConfigAddress, target); + + Kernel::HAL::rt_wait_400ns(); + + return Kernel::HAL::rt_in32((Kernel::UShort) Kernel::PCI::PciConfigKind::ConfigData); +} + +static Kernel::Void NE_PCISetCfgTarget(Kernel::UInt bar, Kernel::UShort bus, Kernel::UShort dev, + Kernel::UShort fun) { + Kernel::UInt target = 0x80000000 | ((Kernel::UInt) bus << 16) | ((Kernel::UInt) dev << 11) | + ((Kernel::UInt) fun << 8) | (bar & 0xFC); + + Kernel::HAL::rt_out32((Kernel::UShort) Kernel::PCI::PciConfigKind::ConfigAddress, target); + + Kernel::HAL::rt_wait_400ns(); +} + +namespace Kernel::PCI { +Device::Device(UShort bus, UShort device, UShort func, UInt32 bar) + : fBus(bus), fDevice(device), fFunction(func), fBar(bar) {} + +Device::~Device() = default; + +UInt Device::Read(UInt bar, Size sz) { + // Ensure aligned access by masking to 4-byte boundary + NE_PCISetCfgTarget(bar & 0xFC, fBus, fDevice, fFunction); + + // Read 4 bytes and shift out the correct value + UInt data = HAL::rt_in32((UShort) PciConfigKind::ConfigData); + + if (sz == 4) return data; + if (sz == 2) return (data >> ((bar & 2) * 8)) & 0xFFFF; + if (sz == 1) return (data >> ((bar & 3) * 8)) & 0xFF; + + return (UShort) PciConfigKind::Invalid; +} + +void Device::Write(UInt bar, UIntPtr data, Size sz) { + NE_PCISetCfgTarget(bar & 0xFC, fBus, fDevice, fFunction); + + if (sz == 4) { + HAL::rt_out32((UShort) PciConfigKind::ConfigAddress, (UInt) data); + } else if (sz == 2) { + UInt temp = HAL::rt_in32((UShort) PciConfigKind::ConfigData); + + temp &= ~(0xFFFF << ((bar & 2) * 8)); + temp |= (data & 0xFFFF) << ((bar & 2) * 8); + + HAL::rt_out32((UShort) PciConfigKind::ConfigAddress, temp); + } else if (sz == 1) { + UInt temp = HAL::rt_in32((UShort) PciConfigKind::ConfigData); + + temp &= ~(0xFF << ((bar & 3) * 8)); + temp |= (data & 0xFF) << ((bar & 3) * 8); + + HAL::rt_out32((UShort) PciConfigKind::ConfigAddress, temp); + } +} + +UShort Device::DeviceId() { + return (UShort) (NE_PCIReadRaw(0x0, fBus, fDevice, fFunction) >> 16); +} + +UShort Device::VendorId() { + return (UShort) (NE_PCIReadRaw(0x0, fBus, fDevice, fFunction) & 0xFFFF); +} + +UShort Device::InterfaceId() { + return (UShort) (NE_PCIReadRaw(0x09, fBus, fDevice, fFunction) >> 16); +} + +UChar Device::Class() { + return (UChar) (NE_PCIReadRaw(0x08, fBus, fDevice, fFunction) >> 24); +} + +UChar Device::Subclass() { + return (UChar) (NE_PCIReadRaw(0x08, fBus, fDevice, fFunction) >> 16); +} + +UChar Device::ProgIf() { + return (UChar) (NE_PCIReadRaw(0x08, fBus, fDevice, fFunction) >> 8); +} + +UChar Device::HeaderType() { + return (UChar) (NE_PCIReadRaw(0xC, fBus, fDevice, fFunction) >> 16); +} + +void Device::EnableMmio() { + UInt32 command = Read(0x04, sizeof(UInt32)); + command |= (1 << 1); // Memory Space Enable (bit 1) + + Write(0x04, command, sizeof(UInt32)); +} + +void Device::BecomeBusMaster() { + UInt32 command = Read(0x04, sizeof(UInt32)); + command |= (1 << 2); // Bus Master Enable (bit 2) + Write(0x04, command, sizeof(UInt32)); +} + +UIntPtr Device::Bar(UInt32 bar_in) { + UInt32 bar = NE_PCIReadRaw(bar_in, fBus, fDevice, fFunction); + + if (bar & PCI_BAR_IO) return static_cast(bar & ~0x03); + + if (bar & PCI_BAR_64) { + UInt32 high = NE_PCIReadRaw((bar_in + 4) & ~0x03, fBus, fDevice, fFunction); + return (static_cast(high) << 32) | (bar & ~0x0F); + } + + return static_cast(bar & ~0x0F); +} + +UShort Device::Vendor() { + UShort vendor = this->VendorId(); + return vendor; +} + +Device::operator bool() { + return this->VendorId() != (UShort) PciConfigKind::Invalid; +} +} // namespace Kernel::PCI diff --git a/src/kernel/HALKit/AMD64/PCI/Express.cc b/src/kernel/HALKit/AMD64/PCI/Express.cc new file mode 100644 index 00000000..2b6ba8d3 --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Express.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/PCI/IO.cc b/src/kernel/HALKit/AMD64/PCI/IO.cc new file mode 100644 index 00000000..233c8ef2 --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/IO.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/HALKit/AMD64/PCI/Iterator.cc b/src/kernel/HALKit/AMD64/PCI/Iterator.cc new file mode 100644 index 00000000..103ddb2c --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Iterator.cc @@ -0,0 +1,30 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel::PCI { +Iterator::Iterator(const Types::PciDeviceKind type, UInt32 bar) { + // probe devices. + for (Int32 bus = 0; bus < NE_BUS_COUNT; ++bus) { + for (Int32 device = 0; device < NE_DEVICE_COUNT; ++device) { + for (Int32 function = 0; function < NE_FUNCTION_COUNT; ++function) { + Device dev(bus, device, function, bar); + + if (dev.Class() == type) { + fDevices[bus] = dev; + } + } + } + } +} + +Iterator::~Iterator() {} + +Ref Iterator::operator[](const Size& at) { + return fDevices[at]; +} +} // namespace Kernel::PCI diff --git a/src/kernel/HALKit/AMD64/PCI/PCI.cc b/src/kernel/HALKit/AMD64/PCI/PCI.cc new file mode 100644 index 00000000..a8c48fb9 --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/PCI.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/HALKit/AMD64/Paging.h b/src/kernel/HALKit/AMD64/Paging.h new file mode 100644 index 00000000..a938700e --- /dev/null +++ b/src/kernel/HALKit/AMD64/Paging.h @@ -0,0 +1,91 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#ifdef __NE_AMD64__ + +/** --------------------------------------------------- + + * THIS FILE CONTAINS CODE FOR X86_64 PAGING. + +------------======================================== */ + +#include + +#ifndef kPageMax +#define kPageMax (0x200) +#endif //! kPageMax + +#ifndef kPageAlign +#define kPageAlign (0x08) +#endif //! kPageAlign + +#ifndef kPageSize +#define kPageSize (0x1000) +#endif // !kPageSize + +#ifndef kAlign +#define kAlign __BIGGEST_ALIGNMENT__ +#endif // !kAlign + +EXTERN_C void hal_flush_tlb(); +EXTERN_C void hal_invl_tlb(Kernel::VoidPtr addr); +EXTERN_C void hal_write_cr3(Kernel::VoidPtr cr3); +EXTERN_C void hal_write_cr0(Kernel::VoidPtr bit); + +EXTERN_C Kernel::VoidPtr hal_read_cr0(); // @brief CPU control register. +EXTERN_C Kernel::VoidPtr hal_read_cr2(); // @brief Fault address. +EXTERN_C Kernel::VoidPtr hal_read_cr3(); // @brief Page directory inside cr3 register. + +namespace Kernel::HAL { +namespace Detail { + enum class ControlRegisterBits { + kProtectedModeEnable = 0, + kMonitorCoProcessor = 1, + kEmulation = 2, + kTaskSwitched = 3, + kExtensionType = 4, + kNumericError = 5, + kWriteProtect = 16, + kAlignementMask = 18, + kNotWriteThrough = 29, + kCacheDisable = 30, + kPageEnable = 31, + }; + + inline UInt8 control_register_cast(ControlRegisterBits reg) { return static_cast(reg); } +} // namespace Detail + +auto mm_alloc_bitmap(Boolean wr, Boolean user, SizeT size, Bool is_page, SizeT pad = 0) -> VoidPtr; +auto mm_free_bitmap(VoidPtr page_ptr) -> Bool; +} // namespace Kernel::HAL + +namespace Kernel { +struct PTE { + UInt64 Present : 1; + UInt64 Wr : 1; + UInt64 User : 1; + UInt64 Pwt : 1; // Page-level Write-Through + UInt64 Pcd : 1; // Page-level Cache Disable + UInt64 Accessed : 1; + UInt64 Dirty : 1; + UInt64 Pat : 1; // Page Attribute Table (or PS for PDE) + UInt64 Global : 1; + UInt64 Ignored1 : 3; // Available to software + UInt64 PhysicalAddress : 40; // Physical page frame address (bits 12–51) + UInt64 Ignored2 : 7; // More software bits / reserved + UInt64 ProtectionKey : 4; // Optional (if PKU enabled) + UInt64 Reserved : 1; // Usually reserved + UInt64 Nx : 1; // No Execute +}; + +struct PDE { + ATTRIBUTE(aligned(kib_cast(4))) PTE fPTE[512]; +}; +} // namespace Kernel + +#endif // __NE_AMD64__ \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/Processor.h b/src/kernel/HALKit/AMD64/Processor.h new file mode 100644 index 00000000..db1ed573 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Processor.h @@ -0,0 +1,283 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: Prcoessor.h + Purpose: AMD64 processor abstraction. + + Revision History: + + 30/01/24: Added file (amlel) + +======================================== */ + +#pragma once + +#ifdef __NE_AMD64__ + +#include +#include +#include +#include +#include + +#include + +#define kPITControlPort (0x43) +#define kPITChannel0Port (0x40) +#define kPITFrequency (1193180) + +#define kPICCommand (0x20) +#define kPICData (0x21) +#define kPIC2Command (0xA0) +#define kPIC2Data (0xA1) + +#define kIOAPICRegVal (4) +#define kIOAPICRegReg (0) + +#define rtl_nop_op() asm volatile("nop") + +/// @brief Maximum entries of the interrupt descriptor table. +#define kKernelIdtSize (0x100) + +/// @brief interrupt for system call. +#define kKernelInterruptId (0x32) + +#define IsActiveLow(FLG) (FLG & 2) +#define IsLevelTriggered(FLG) (FLG & 8) + +#define kInterruptGate (0x8E) +#define kUserInterruptGate (0xEE) +#define kTrapGate (0xEF) +#define kTaskGate (0b10001100) +#define kIDTSelector (0x08) + +namespace Kernel { +namespace Detail::AMD64 { + struct PACKED InterruptDescriptorAMD64 final { + UInt16 OffsetLow; // offset bits 0..15 + UInt16 Selector; // a code segment selector in GDT or LDT + UInt8 Ist; + UInt8 TypeAttributes; + UInt16 OffsetMid; + UInt32 OffsetHigh; + UInt32 Zero; // reserved + }; +} // namespace Detail::AMD64 +} // namespace Kernel + +namespace Kernel::HAL { +/// @brief Memory Manager mapping flags. +enum { + kMMFlagsInvalid = 1 << 0, + kMMFlagsPresent = 1 << 1, + kMMFlagsWr = 1 << 2, + kMMFlagsUser = 1 << 3, + kMMFlagsNX = 1 << 4, + kMMFlagsPCD = 1 << 5, + kMMFlagsPwt = 1 << 6, + kMMFlagsCount = 6, +}; + +struct PACKED Register64 final { + UShort Limit; + UIntPtr Base; +}; + +using RawRegister = UInt64; +using Reg = RawRegister; +using InterruptId = UInt16; /* For each element in the IVT */ + +/// @brief Stack frame (as retrieved from assembly.) +struct PACKED StackFrame { + Reg IP; + Reg SP; + Reg R8; + Reg R9; + Reg R10; + Reg R11; + Reg R12; + Reg R13; + Reg R14; + Reg R15; +}; + +typedef StackFrame* StackFramePtr; + +class InterruptDescriptor final { + public: + UShort Offset; + UShort Selector; + UChar Ist; + UChar Atrributes; + + UShort SecondOffset; + UInt ThirdOffset; + UInt Zero; + + operator bool() { return Offset != 0xFFFF; } +}; + +using InterruptDescriptorArray = Array; + +class SegmentDescriptor final { + public: + UInt16 Base; + UInt8 BaseMiddle; + UInt8 BaseHigh; + + UShort Limit; + UChar Gran; + UChar AccessByte; +}; + +/*** + * @brief Segment Boolean operations + */ +class SegmentDescriptorComparator final { + public: + Bool IsValid(SegmentDescriptor& seg) { return seg.Base > seg.Limit; } + + Bool Equals(SegmentDescriptor& seg, SegmentDescriptor& segRight) { + return seg.Base == segRight.Base && seg.Limit == segRight.Limit; + } +}; + +using SegmentArray = Array; + +class GDTLoader final { + public: + static Void Load(Register64& gdt); + static Void Load(Ref& gdt); +}; + +class IDTLoader final { + public: + static Void Load(Register64& idt); + static Void Load(Ref& idt); +}; + +/***********************************************************************************/ +/// @brief Is the current config SMP aware? +/// @return True if YES, False if not. +/***********************************************************************************/ + +Bool mp_is_smp(Void) noexcept; + +/***********************************************************************************/ +/// @brief Fetch and enable SMP scheduler. +/// @param vendor_ptr SMP containing structure. +/***********************************************************************************/ +Void mp_init_cores(VoidPtr vendor_ptr) noexcept; + +/***********************************************************************************/ +/// @brief Do a cpuid to check if MSR exists on CPU. +/// @retval true it does exists. +/// @retval false it doesn't. +/***********************************************************************************/ +Bool hal_has_msr() noexcept; + +/***********************************************************************************/ +/// @brief Get Model specific register inside core. +/// @param msr MSR +/// @param lo low byte +/// @param hi high byte +/***********************************************************************************/ +Void hal_get_msr(UInt32 msr, UInt32* lo, UInt32* hi) noexcept; + +/// @brief Set Model-specific register. +/// @param msr MSR +/// @param lo low byte +/// @param hi high byte +Void hal_set_msr(UInt32 msr, UInt32 lo, UInt32 hi) noexcept; + +/// @brief Processor specific namespace. +namespace Detail { + /* @brief TSS struct. */ + struct NE_TSS final { + UInt32 fReserved1; + UInt64 fRsp0; + UInt64 fRsp1; + UInt64 fRsp2; + UInt64 fReserved2; + UInt64 fIst1; + UInt64 fIst2; + UInt64 fIst3; + UInt64 fIst4; + UInt64 fIst5; + UInt64 fIst6; + UInt64 fIst7; + UInt64 fReserved3; + UInt16 fReserved4; + UInt16 fIopb; + }; + + /** + @brief Global descriptor table entry, either null, code or data. + */ + + struct PACKED NE_GDT_ENTRY final { + UInt16 fLimitLow; + UInt16 fBaseLow; + UInt8 fBaseMid; + UInt8 fAccessByte; + UInt8 fFlags; + UInt8 fBaseHigh; + }; +} // namespace Detail + +class LAPICDmaWrapper final { + public: + explicit LAPICDmaWrapper(VoidPtr base); + ~LAPICDmaWrapper(); + + NE_COPY_DEFAULT(LAPICDmaWrapper) + + public: + UInt32 Read(UInt16 reg) noexcept; + Void Write(UInt16 reg, UInt32 value) noexcept; + + private: + VoidPtr fApic{nullptr}; +}; + +/// @brief Set a PTE from pd_base. +/// @param virt_addr a valid virtual address. +/// @param phys_addr point to physical address. +/// @param flags the flags to put on the page. +/// @return Status code of page manip. +EXTERN_C Int32 mm_map_page(VoidPtr virtual_address, VoidPtr physical_address, UInt32 flags, + UInt32 level = 2); + +EXTERN_C UInt8 rt_in8(UInt16 port); +EXTERN_C UInt16 rt_in16(UInt16 port); +EXTERN_C UInt32 rt_in32(UInt16 port); + +EXTERN_C Void rt_out16(UShort port, UShort byte); +EXTERN_C Void rt_out8(UShort port, UChar byte); +EXTERN_C Void rt_out32(UShort port, UInt byte); + +EXTERN_C Void rt_wait_400ns(); +EXTERN_C Void rt_halt(); +EXTERN_C Void rt_cli(); +EXTERN_C Void rt_sti(); +EXTERN_C Void rt_cld(); +EXTERN_C Void rt_std(); + +EXTERN_C UIntPtr mm_get_page_addr(VoidPtr virtual_address); + +EXTERN_C Int32 mm_memory_fence(VoidPtr virtual_address); +} // namespace Kernel::HAL + +EXTERN_C Kernel::Void idt_handle_generic(Kernel::UIntPtr rsp); +EXTERN_C Kernel::Void idt_handle_gpf(Kernel::UIntPtr rsp); +EXTERN_C Kernel::Void idt_handle_math(Kernel::UIntPtr rsp); +EXTERN_C Kernel::Void idt_handle_pf(Kernel::UIntPtr rsp); + +EXTERN_C ATTRIBUTE(naked) Kernel::Void hal_load_idt(Kernel::HAL::Register64 ptr); +EXTERN_C ATTRIBUTE(naked) Kernel::Void hal_load_gdt(Kernel::HAL::Register64 ptr); + +inline Kernel::VoidPtr kKernelBitMpStart = nullptr; +inline Kernel::UIntPtr kKernelBitMpSize = 0UL; + +#endif // __NE_AMD64__ */ \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc new file mode 100644 index 00000000..14b40b98 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc @@ -0,0 +1,600 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file AHCI+Generic.cc + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief AHCI Generic driver. + * @version 0.1 + * @date 2024-02-02 + * + * @copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define kSATAErrTaskFile (1 << 30) +#define kSATAPxCmdST (0x0001) +#define kSATAPxCmdFre (0x0010) +#define kSATAPxCmdFR (0x4000) +#define kSATAPxCmdCR (0x8000) + +#define kSATALBAMode (1 << 6) + +#define kSATASRBsy (0x80) +#define kSATASRDrq (0x08) + +#define kSATABohcBiosOwned (1 << 0) +#define kSATABohcOSOwned (1 << 1) + +#define kSATAPortCnt (0x20) + +#define kSATASig (0x00000101) +#define kSATAPISig (0xEB140101) + +#define kSATAProgIfAHCI (0x01) +#define kSATASubClass (0x06) +#define kSATABar5 (0x24) + +using namespace Kernel; + +STATIC PCI::Device kSATADev; +STATIC HbaMemRef kSATAHba; +STATIC Lba kSATASectorCount = 0UL; +STATIC UInt16 kSATAIndex = 0U; +STATIC Char kCurrentDiskModel[50] = {"GENERIC SATA"}; +STATIC UInt16 kSATAPortsImplemented = 0U; +STATIC ALIGN(kib_cast(4)) UInt8 kIdentifyData[kAHCISectorSize] = {0}; + +template +STATIC Void drv_std_input_output_ahci(UInt64 lba, UInt8* buffer, SizeT sector_sz, + SizeT size_buffer) noexcept; + +STATIC Int32 drv_find_cmd_slot_ahci(HbaPort* port) noexcept; + +STATIC Void drv_compute_disk_ahci() noexcept; + +STATIC SizeT drv_get_size_ahci(); + +STATIC SizeT drv_get_sector_count_ahci(); + +/***********************************************************************************/ +/// @brief Identify device and read LBA info, Disk OEM vendor. +/***********************************************************************************/ +STATIC Void drv_compute_disk_ahci() noexcept { + kSATASectorCount = 0UL; + + rt_set_memory(kIdentifyData, 0, kAHCISectorSize); + + drv_std_input_output_ahci(0, kIdentifyData, kAHCISectorSize, kAHCISectorSize); + + // --> Reinterpret the 512-byte buffer as an array of 256 UInt16 words + UInt16* identify_words = reinterpret_cast(kIdentifyData); + + /// Extract 48-bit LBA. + UInt64 lba48_sectors = 0UL; + lba48_sectors |= (UInt64) identify_words[100]; + lba48_sectors |= (UInt64) identify_words[101] << 16; + lba48_sectors |= (UInt64) identify_words[102] << 32; + + if (lba48_sectors == 0) + kSATASectorCount = (identify_words[61] << 16) | identify_words[60]; + else + kSATASectorCount = lba48_sectors; + + for (Int32 i = 0; i < 20; i++) { + kCurrentDiskModel[i * 2] = (identify_words[27 + i] >> 8) & 0xFF; + kCurrentDiskModel[i * 2 + 1] = identify_words[27 + i] & 0xFF; + } + + kCurrentDiskModel[40] = '\0'; + + (Void)(kout << "SATA Sector Count: " << hex_number(kSATASectorCount) << kendl); + (Void)(kout << "SATA Disk Model: " << kCurrentDiskModel << kendl); +} + +/***********************************************************************************/ +/// @brief Finds a command slot for a HBA port. +/// @param port The port to search on. +/// @return The slot, or -1. +/***********************************************************************************/ +STATIC Int32 drv_find_cmd_slot_ahci(HbaPort* port) noexcept { + UInt32 slots = port->Sact | port->Ci; + + for (Int32 i = 0; i < kSATAPortCnt; ++i) // AHCI supports up to 32 slots + { + if ((slots & (1U << i)) == 0) return i; + } + + return -1; // no free slot found +} + +/***********************************************************************************/ +/// @brief Send an AHCI command, according to the template parameters. +/// @param lba Logical Block Address to look for. +/// @param buffer The data buffer to transfer. +/// @param sector_sz The disk's sector size (unused) +/// @param size_buffer The size of the **buffer** parameter. +/***********************************************************************************/ +template +STATIC Void drv_std_input_output_ahci(UInt64 lba, UInt8* buffer, SizeT sector_sz, + SizeT size_buffer) noexcept { + if (sector_sz == 0) { + kout << "ahci: Invalid sector size.\r"; + err_global_get() = kErrorDisk; + return; + } + + lba /= sector_sz; + + if (!buffer || size_buffer == 0) { + kout << "ahci: Invalid buffer for AHCI I/O.\r"; + err_global_get() = kErrorDisk; + return; + } + + UIntPtr slot = drv_find_cmd_slot_ahci(&kSATAHba->Ports[kSATAIndex]); + + UInt16 timeout = 0; + + constexpr static UInt16 kTimeout = 0x8000; + + while (slot == ~0UL) { + if (timeout > kTimeout) { + kout << "ahci: No free command slot found, AHCI disk is busy!\r"; + + err_global_get() = kErrorDisk; + return; + } + + slot = drv_find_cmd_slot_ahci(&kSATAHba->Ports[kSATAIndex]); + ++timeout; + } + + volatile HbaCmdHeader* command_header = + (volatile HbaCmdHeader*) ((UInt64) kSATAHba->Ports[kSATAIndex].Clb); + + command_header += slot; + + MUST_PASS(command_header); + + // Clear old command table memory + volatile HbaCmdTbl* command_table = + (volatile HbaCmdTbl*) (((UInt64) command_header->Ctbau << 32) | command_header->Ctba); + + MUST_PASS(command_table); + + rt_set_memory((VoidPtr) command_table, 0, sizeof(HbaCmdTbl)); + + VoidPtr ptr = rtl_dma_alloc(size_buffer, kib_cast(4)); + + rtl_dma_flush(ptr, size_buffer); + + if (Write) { + rt_copy_memory(buffer, ptr, size_buffer); + } + + rtl_dma_flush(ptr, size_buffer); + + // Build the PRD table. + SizeT bytes_remaining = size_buffer; + SizeT prdt_index = 0; + UIntPtr buffer_phys = (UIntPtr) ptr; + + while (bytes_remaining > 0) { + SizeT chunk_size = bytes_remaining; + + if (chunk_size > kib_cast(32)) chunk_size = kib_cast(32); + + command_table->Prdt[prdt_index].Dba = (UInt32) (buffer_phys & 0xFFFFFFFF); + command_table->Prdt[prdt_index].Dbau = (UInt32) (buffer_phys >> 32); + command_table->Prdt[prdt_index].Dbc = (UInt32) (chunk_size - 1); + command_table->Prdt[prdt_index].Ie = NO; + + buffer_phys += chunk_size; + bytes_remaining -= chunk_size; + + ++prdt_index; + } + + // Mark the last PRD entry, for the FIS to process the table. + command_table->Prdt[prdt_index - 1].Ie = YES; + + if (bytes_remaining > 0) { + kout << "ahci: AHCI PRDT overflow, cannot map full buffer.\r"; + err_global_get() = kErrorDisk; + rtl_dma_free(size_buffer); + + return; + } + + command_header->Prdtl = prdt_index; + command_header->HbaFlags.Struct.Cfl = sizeof(FisRegH2D) / sizeof(UInt32); + command_header->HbaFlags.Struct.Write = Write; + + volatile FisRegH2D* h2d_fis = (volatile FisRegH2D*) (&command_table->Cfis[0]); + + h2d_fis->FisType = kFISTypeRegH2D; + h2d_fis->CmdOrCtrl = CommandOrCTRL; + h2d_fis->Command = + (Identify ? kAHCICmdIdentify : (Write ? kAHCICmdWriteDmaEx : kAHCICmdReadDmaEx)); + + h2d_fis->Lba0 = (lba >> 0) & 0xFF; + h2d_fis->Lba1 = (lba >> 8) & 0xFF; + h2d_fis->Lba2 = (lba >> 16) & 0xFF; + h2d_fis->Lba3 = (lba >> 24) & 0xFF; + h2d_fis->Lba4 = (lba >> 32) & 0xFF; + h2d_fis->Lba5 = (lba >> 40) & 0xFF; + + h2d_fis->Device = 0; + + if (Identify) { + h2d_fis->CountLow = 1; + h2d_fis->CountHigh = 0; + } else { + h2d_fis->Device = kSATALBAMode; + h2d_fis->CountLow = (size_buffer / kAHCISectorSize) & 0xFF; + h2d_fis->CountHigh = ((size_buffer / kAHCISectorSize) >> 8) & 0xFF; + } + + rtl_dma_flush(ptr, size_buffer); + + // Issue command + kSATAHba->Ports[kSATAIndex].Ci = (1 << slot); + + timeout = 0UL; + + while (YES) { + if (timeout > kTimeout) { + kout << "ahci: disk-hangup, corrupted-disk.\r"; + err_global_get() = kErrorDiskIsCorrupted; + rtl_dma_free(size_buffer); + + return; + } + + ++timeout; + + if (!(kSATAHba->Ports[kSATAIndex].Ci & (1 << slot))) break; + } + + rtl_dma_flush(ptr, size_buffer); + + if (kSATAHba->Is & kSATAErrTaskFile) { + kout << "ahci: Task File Error during I/O.\r"; + + rtl_dma_free(size_buffer); + err_global_get() = kErrorDiskIsCorrupted; + + return; + } else { + if (!Write) { + rtl_dma_flush(ptr, size_buffer); + rt_copy_memory(ptr, buffer, size_buffer); + rtl_dma_flush(ptr, size_buffer); + } + + if ((kSATAHba->Ports[kSATAIndex].Tfd & (kSATASRBsy | kSATASRDrq)) == 0) { + goto ahci_io_end; + } else { + kout << "ahci: Disk still busy after command completion!\r"; + while (kSATAHba->Ports[kSATAIndex].Tfd & (kSATASRBsy | kSATASRDrq)) + ; + } + + ahci_io_end: + rtl_dma_free(size_buffer); + err_global_get() = kErrorSuccess; + } +} + +/*** + @brief Gets the number of sectors inside the drive. + @return Sector size in bytes. + */ +STATIC ATTRIBUTE(unused) SizeT drv_get_sector_count_ahci() { + return kSATASectorCount; +} + +/// @brief Get the drive size. +/// @return Disk size in bytes. +STATIC ATTRIBUTE(unused) SizeT drv_get_size_ahci() { + return drv_std_get_sector_count() * kAHCISectorSize; +} + +/// @brief Enable Host and probe using the IDENTIFY command. +STATIC BOOL ahci_enable_and_probe() { + if (kSATAHba->Cap == 0x0) return NO; + + kSATAHba->Ports[kSATAIndex].Cmd &= ~kSATAPxCmdFre; + kSATAHba->Ports[kSATAIndex].Cmd &= ~kSATAPxCmdST; + + while (YES) { + if (kSATAHba->Ports[kSATAIndex].Cmd & kSATAPxCmdCR) continue; + + if (kSATAHba->Ports[kSATAIndex].Cmd & kSATAPxCmdFR) continue; + + break; + } + + // Now we are ready. + + kSATAHba->Ports[kSATAIndex].Cmd |= kSATAPxCmdFre; + kSATAHba->Ports[kSATAIndex].Cmd |= kSATAPxCmdST; + + if (kSATAHba->Bohc & kSATABohcBiosOwned) { + kSATAHba->Bohc |= kSATABohcOSOwned; + + while (kSATAHba->Bohc & kSATABohcBiosOwned) { + ; + } + } + + drv_compute_disk_ahci(); + + return YES; +} + +STATIC Bool drv_init_command_structures_ahci() { + // Allocate 4KiB for Command List (32 headers) + VoidPtr clb_mem = rtl_dma_alloc(4096, 1024); + if (!clb_mem) { + kout << "Failed to allocate CLB memory!\r"; + return NO; + } + + UIntPtr clb_phys = HAL::mm_get_page_addr(clb_mem); + + kSATAHba->Ports[kSATAIndex].Clb = (UInt32) (clb_phys & 0xFFFFFFFF); + kSATAHba->Ports[kSATAIndex].Clbu = (UInt32) (clb_phys >> 32); + + // Clear it + rt_set_memory(clb_mem, 0, kib_cast(4)); + + // For each command slot (up to 32) + volatile HbaCmdHeader* header = (volatile HbaCmdHeader*) clb_mem; + + for (Int32 i = 0; i < 32; ++i) { + // Allocate 4KiB for Command Table + VoidPtr ct_mem = rtl_dma_alloc(4096, 128); + if (!ct_mem) { + (Void)(kout << "Failed to allocate CTB memory for slot " << hex_number(i)); + kout << "!\r"; + return NO; + } + + UIntPtr ct_phys = HAL::mm_get_page_addr(ct_mem); + + header[i].Ctba = (UInt32) (ct_phys & 0xFFFFFFFF); + header[i].Ctbau = (UInt32) (ct_phys >> 32); + + // Clear the command table + rt_set_memory((VoidPtr) ct_mem, 0, 4096); + } + + return YES; +} + +/// @brief Initializes an AHCI disk. +/// @param pi the amount of ports that have been detected. +/// @param atapi reference value, tells whether we should detect ATAPI instead of SATA. +/// @return if the disk was successfully initialized or not. +STATIC Bool drv_std_init_ahci(UInt16& pi, BOOL& atapi) { + /// AMLALE: TODO: Iterator is good enough, but we need to expand it. + PCI::Iterator iterator(Types::PciDeviceKind::MassStorageController, 0x00); + + for (SizeT device_index = 0; device_index < NE_BUS_COUNT; ++device_index) { + kSATADev = iterator[device_index].Leak(); // Leak device. + + if (kSATADev.Subclass() == kSATASubClass && kSATADev.ProgIf() == kSATAProgIfAHCI) { + kSATADev.EnableMmio(); + kSATADev.BecomeBusMaster(); + + HbaMem* mem_ahci = (HbaMem*) kSATADev.Bar(kSATABar5); + + HAL::mm_map_page( + (VoidPtr) mem_ahci, (VoidPtr) mem_ahci, + HAL::kMMFlagsPresent | HAL::kMMFlagsWr | HAL::kMMFlagsPCD | HAL::kMMFlagsPwt); + + UInt32 ports_implemented = mem_ahci->Pi; + UInt16 ahci_index = 0; + + pi = ports_implemented; + + const UInt16 kSATAMaxPortsImplemented = ports_implemented; + const UInt32 kSATASignature = kSATASig; + const UInt32 kSATAPISignature = kSATAPISig; + const UInt8 kSATAPresent = 0x03; + const UInt8 kSATAIPMActive = 0x01; + + if (kSATAMaxPortsImplemented < 1) continue; + + while (ports_implemented) { + UInt8 ipm = (mem_ahci->Ports[ahci_index].Ssts >> 8) & 0x0F; + UInt8 det = (mem_ahci->Ports[ahci_index].Ssts & 0x0F); + + if (det != kSATAPresent || ipm != kSATAIPMActive) continue; + + if ((mem_ahci->Ports[ahci_index].Sig == kSATASignature) || + (atapi && kSATAPISignature == mem_ahci->Ports[ahci_index].Sig)) { + kSATAIndex = ahci_index; + kSATAHba = mem_ahci; + + if (!drv_init_command_structures_ahci()) { + err_global_get() = kErrorDisk; + } + + goto success_hba_fetch; + } + + ports_implemented >>= 1; + ++ahci_index; + } + } + } + + err_global_get() = kErrorDisk; + + return NO; + +success_hba_fetch: + if (ahci_enable_and_probe()) { + err_global_get() = kErrorSuccess; + } + + return err_global_get() == kErrorSuccess; +} + +/// @brief Checks if an AHCI device is detected. +/// @return Either if detected, or not found. +Bool drv_std_detected_ahci() { + return kSATADev.DeviceId() != (UShort) PCI::PciConfigKind::Invalid && + kSATADev.Bar(kSATABar5) != 0; +} + +// ================================================================================================ + +// +/// @note This applies only if we compile with AHCI as a default disk driver. +// + +// ================================================================================================ + +#ifdef __AHCI__ + +//////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////// +Void drv_std_write(UInt64 lba, Char* buffer, SizeT sector_sz, SizeT size_buffer) { + drv_std_input_output_ahci(lba, reinterpret_cast(buffer), sector_sz, + size_buffer); +} + +//////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////// +Void drv_std_read(UInt64 lba, Char* buffer, SizeT sector_sz, SizeT size_buffer) { + drv_std_input_output_ahci(lba, reinterpret_cast(buffer), sector_sz, + size_buffer); +} + +//////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////// +Bool drv_std_init(UInt16& pi) { + BOOL atapi = NO; + return drv_std_init_ahci(pi, atapi); +} + +//////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////// +Bool drv_std_detected(Void) { + return drv_std_detected_ahci(); +} + +//////////////////////////////////////////////////// +/** + @brief Gets the number of sectors inside the drive. + @return Sector size in bytes. + */ +//////////////////////////////////////////////////// +SizeT drv_std_get_sector_count() { + return drv_get_sector_count_ahci(); +} + +//////////////////////////////////////////////////// +/// @brief Get the drive size. +/// @return Disk size in bytes. +//////////////////////////////////////////////////// +SizeT drv_std_get_size() { + return drv_get_size_ahci(); +} + +#endif // ifdef __AHCI__ + +namespace Kernel { +/// @brief Initialize an AHCI device (StorageKit) +UInt16 sk_init_ahci_device(BOOL atapi) { + UInt16 pi = 0; + + if (drv_std_init_ahci(pi, atapi)) kSATAPortsImplemented = pi; + + return pi; +} + +/// @brief Implementation details namespace. +namespace Detail { + /// @brief Read AHCI device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_read_ahci(DeviceInterface* self, IMountpoint* mnt) { + AHCIDeviceInterface* dev = (AHCIDeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_std_input_output_ahci(disk->fPacket.fPacketLba / kAHCISectorSize, + (UInt8*) disk->fPacket.fPacketContent, kAHCISectorSize, + disk->fPacket.fPacketSize); + } + + /// @brief Write AHCI device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_write_ahci(DeviceInterface* self, IMountpoint* mnt) { + AHCIDeviceInterface* dev = (AHCIDeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_std_input_output_ahci(disk->fPacket.fPacketLba / kAHCISectorSize, + (UInt8*) disk->fPacket.fPacketContent, kAHCISectorSize, + disk->fPacket.fPacketSize); + } +} // namespace Detail + +/// @brief Acquires a new AHCI device with drv_index in mind. +/// @param drv_index The drive index to assign. +/// @return A wrapped device interface if successful, or error code. +ErrorOr sk_acquire_ahci_device(UInt32 drv_index) { + if (!drv_std_detected_ahci()) return ErrorOr(kErrorDisk); + + AHCIDeviceInterface device(Detail::sk_io_read_ahci, Detail::sk_io_write_ahci); + + device.SetPortsImplemented(kSATAPortsImplemented); + device.SetIndex(drv_index); + + return ErrorOr(device); +} +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc b/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc new file mode 100644 index 00000000..cf6147d9 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc @@ -0,0 +1,199 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file DMA+Generic.cc + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief ATA driver (DMA mode). + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) Amlal El Mahrouss + * + */ + +#include +#include +#include + +#if defined(__ATA_DMA__) + +#define kATADataLen (256) + +using namespace Kernel; +using namespace Kernel::HAL; + +/// BUGS: 0 + +STATIC Boolean kATADetected = false; +STATIC Int32 kATADeviceType ATTRIBUTE(unused) = kATADeviceCount; +STATIC UInt16 kATAIdentifyData[kATADataLen] = {0}; +STATIC Kernel::PCI::Device kATADevice; +STATIC Char kATADiskModel[50] ATTRIBUTE(unused) = {"GENERIC DMA"}; + +Boolean drv_std_wait_io(UInt16 IO) { + for (int i = 0; i < 400; i++) rt_in8(IO + ATA_REG_STATUS); + +ATAWaitForIO_Retry: + auto status_rdy = rt_in8(IO + ATA_REG_STATUS); + + if ((status_rdy & ATA_SR_BSY)) goto ATAWaitForIO_Retry; + +ATAWaitForIO_Retry2: + status_rdy = rt_in8(IO + ATA_REG_STATUS); + + if (status_rdy & ATA_SR_ERR) return false; + + if (!(status_rdy & ATA_SR_DRDY)) goto ATAWaitForIO_Retry2; + + return true; +} + +Void drv_std_select(UInt16 Bus) { + if (Bus == ATA_PRIMARY_IO) + rt_out8(Bus + ATA_REG_HDDEVSEL, ATA_PRIMARY_SEL); + else + rt_out8(Bus + ATA_REG_HDDEVSEL, ATA_SECONDARY_SEL); +} + +Boolean drv_std_init(UInt16 Bus, UInt8 Drive, UInt16& OutBus, UInt8& OutMaster) { + NE_UNUSED(Bus); + NE_UNUSED(Drive); + NE_UNUSED(OutBus); + NE_UNUSED(OutMaster); + + PCI::Iterator iterator(Types::PciDeviceKind::MassStorageController); + + for (SizeT device_index = 0; device_index < NE_BUS_COUNT; ++device_index) { + kATADevice = iterator[device_index].Leak(); // And then leak the reference. + + /// IDE interface + if (kATADevice.Subclass() == 0x01) { + break; + } + } + + return NO; +} + +namespace Kernel::Detail { +struct PRDEntry final { + UInt32 mAddress; + UInt16 mByteCount; + UInt16 mFlags; /// @param PRD flags, set to 0x8000 to indicate end of prd. +}; +} // namespace Kernel::Detail + +static UIntPtr kReadAddr = mib_cast(2); +static UIntPtr kWriteAddr = mib_cast(2) + kib_cast(64); + +Void drv_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + if (Size > kib_cast(64)) return; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + rt_copy_memory((VoidPtr) Buf, (VoidPtr) kReadAddr, Size); + + drv_std_select(IO); + + rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F)); + + rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + SectorSz - 1) / SectorSz)); + + rt_out8(IO + ATA_REG_LBA0, (Lba) &0xFF); + rt_out8(IO + ATA_REG_LBA1, (Lba) >> 8); + rt_out8(IO + ATA_REG_LBA2, (Lba) >> 16); + rt_out8(IO + ATA_REG_LBA3, (Lba) >> 24); + + Kernel::Detail::PRDEntry* prd = + (Kernel::Detail::PRDEntry*) (kATADevice.Bar(0x20) + 4); // The PRDEntry is not correct. + + prd->mAddress = (UInt32) (UIntPtr) kReadAddr; + prd->mByteCount = Size - 1; + prd->mFlags = 0x8000; // indicate the end of prd. + + rt_out32(kATADevice.Bar(0x20) + 0x04, (UInt32) (UIntPtr) prd); + + rt_out8(kATADevice.Bar(0x20) + ATA_REG_COMMAND, ATA_CMD_READ_DMA); + + rt_out8(kATADevice.Bar(0x20) + 0x00, 0x09); // Start DMA engine + + while (rt_in8(kATADevice.Bar(0x20) + ATA_REG_STATUS) & 0x01) + ; + + rt_out8(kATADevice.Bar(0x20) + 0x00, 0x00); // Stop DMA engine + + rt_copy_memory((VoidPtr) kReadAddr, (VoidPtr) Buf, Size); + + delete prd; + prd = nullptr; +} + +Void drv_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + if (Size > kib_cast(64)) return; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + rt_copy_memory((VoidPtr) Buf, (VoidPtr) kWriteAddr, Size); + + rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F)); + + rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + (SectorSz - 1)) / SectorSz)); + + rt_out8(IO + ATA_REG_LBA0, (Lba) &0xFF); + rt_out8(IO + ATA_REG_LBA1, (Lba) >> 8); + rt_out8(IO + ATA_REG_LBA2, (Lba) >> 16); + rt_out8(IO + ATA_REG_LBA3, (Lba) >> 24); + + Kernel::Detail::PRDEntry* prd = (Kernel::Detail::PRDEntry*) (kATADevice.Bar(0x20) + 4); + + prd->mAddress = (UInt32) (UIntPtr) kWriteAddr; + prd->mByteCount = Size - 1; + prd->mFlags = 0x8000; + + rt_out32(kATADevice.Bar(0x20) + 0x04, (UInt32) (UIntPtr) prd); + rt_out8(kATADevice.Bar(0x20) + ATA_REG_COMMAND, ATA_CMD_WRITE_DMA); + + rt_out8(IO + 0x00, 0x09); // Start DMA engine + + while (rt_in8(kATADevice.Bar(0x20) + ATA_REG_STATUS) & 0x01) + ; + + rt_out8(kATADevice.Bar(0x20) + 0x00, 0x00); // Stop DMA engine + + delete prd; + prd = nullptr; +} + +/***********************************************************************************/ +/// @brief Is ATA detected? +/***********************************************************************************/ +Boolean drv_std_detected(Void) { + return kATADetected; +} + +/***********************************************************************************/ +/*** + @brief Gets the number of sectors inside the drive. + @return Number of sectors, or zero. +*/ +/***********************************************************************************/ +Kernel::SizeT drv_std_get_sector_count() { + return (kATAIdentifyData[61] << 16) | kATAIdentifyData[60]; +} + +/***********************************************************************************/ +/// @brief Get the size of the current drive. +/***********************************************************************************/ +Kernel::SizeT drv_std_get_size() { + return (drv_std_get_sector_count()) * kATASectorSize; +} + +#endif /* ifdef __ATA_DMA__ */ diff --git a/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc b/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc new file mode 100644 index 00000000..88f95a86 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; diff --git a/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc new file mode 100644 index 00000000..0516be39 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc @@ -0,0 +1,278 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file PIO+Generic.cc + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief ATA driver (PIO mode). + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) Amlal El Mahrouss + * + */ + +#include +#include +#include +#include + +using namespace Kernel; +using namespace Kernel::HAL; + +/// BUGS: 0 + +#define kATADataLen 256 + +STATIC Boolean kATADetected = false; +STATIC UInt16 kATAIdentifyData[kATADataLen] = {0}; +STATIC Char kATADiskModel[50] = {"GENERIC PIO"}; + +static Boolean drv_pio_std_wait_io(UInt16 IO) { + for (int i = 0; i < 400; i++) rt_in8(IO + ATA_REG_STATUS); + +ATAWaitForIO_Retry: + auto stat_rdy = rt_in8(IO + ATA_REG_STATUS); + + if ((stat_rdy & ATA_SR_BSY)) goto ATAWaitForIO_Retry; + +ATAWaitForIO_Retry2: + stat_rdy = rt_in8(IO + ATA_REG_STATUS); + + if (stat_rdy & ATA_SR_ERR) return false; + + if (!(stat_rdy & ATA_SR_DRDY)) goto ATAWaitForIO_Retry2; + + return true; +} + +STATIC Void drv_pio_std_select(UInt16 Bus) { + if (Bus == ATA_PRIMARY_IO) + rt_out8(Bus + ATA_REG_HDDEVSEL, ATA_PRIMARY_SEL); + else + rt_out8(Bus + ATA_REG_HDDEVSEL, ATA_SECONDARY_SEL); +} + +Boolean drv_pio_std_init(UInt16 Bus, UInt8 Drive, UInt16& OutBus, UInt8& OutMaster) { + UInt16 IO = Bus; + + NE_UNUSED(Drive); + + drv_pio_std_select(IO); + + // Bus init, NEIN bit. + rt_out8(IO + ATA_REG_NEIN, 1); + + // identify until it's good. +ATAInit_Retry: + auto stat_rdy = rt_in8(IO + ATA_REG_STATUS); + + if (stat_rdy & ATA_SR_ERR) { + return false; + } + + if ((stat_rdy & ATA_SR_BSY)) goto ATAInit_Retry; + + OutBus = (Bus == ATA_PRIMARY_IO) ? ATA_PRIMARY_IO : ATA_SECONDARY_IO; + OutMaster = (Bus == ATA_PRIMARY_IO) ? ATA_MASTER : ATA_SLAVE; + + drv_pio_std_select(IO); + + rt_out8(OutBus + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + + while (!(rt_in8(IO + ATA_REG_STATUS) & ATA_SR_DRQ)) + ; + + /// fetch serial info + /// model, speed, number of sectors... + + for (SizeT i = 0ul; i < kATADataLen; ++i) { + kATAIdentifyData[i] = rt_in16(OutBus + ATA_REG_DATA); + } + + for (Int32 i = 0; i < 20; i++) { + kATADiskModel[i * 2] = (kATAIdentifyData[27 + i] >> 8) & 0xFF; + kATADiskModel[i * 2 + 1] = kATAIdentifyData[27 + i] & 0xFF; + } + + kATADiskModel[40] = '\0'; + + (Void)(kout << "Drive Model: " << kATADiskModel << kendl); + + return true; +} + +Void drv_pio_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + drv_pio_std_wait_io(IO); + drv_pio_std_select(IO); + + rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F)); + + rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + SectorSz) / SectorSz)); + + rt_out8(IO + ATA_REG_LBA0, (Lba) &0xFF); + rt_out8(IO + ATA_REG_LBA1, (Lba) >> 8); + rt_out8(IO + ATA_REG_LBA2, (Lba) >> 16); + rt_out8(IO + ATA_REG_LBA3, (Lba) >> 24); + + rt_out8(IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO); + + while (!(rt_in8(IO + ATA_REG_STATUS) & ATA_SR_DRQ)) + ; + + for (SizeT IndexOff = 0; IndexOff < Size; IndexOff += 2) { + drv_pio_std_wait_io(IO); + + auto in = rt_in16(IO + ATA_REG_DATA); + + Buf[IndexOff] = in & 0xFF; + Buf[IndexOff + 1] = (in >> 8) & 0xFF; + } +} + +Void drv_pio_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + drv_pio_std_wait_io(IO); + drv_pio_std_select(IO); + + rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F)); + + rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + SectorSz) / SectorSz)); + + rt_out8(IO + ATA_REG_LBA0, (Lba) &0xFF); + rt_out8(IO + ATA_REG_LBA1, (Lba) >> 8); + rt_out8(IO + ATA_REG_LBA2, (Lba) >> 16); + rt_out8(IO + ATA_REG_LBA3, (Lba) >> 24); + + rt_out8(IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); + + while (!(rt_in8(IO + ATA_REG_STATUS) & ATA_SR_DRQ)) + ; + + for (SizeT IndexOff = 0; IndexOff < Size; IndexOff += 2) { + drv_pio_std_wait_io(IO); + + UInt8 low = (UInt8) Buf[IndexOff]; + UInt8 high = (IndexOff + 1 < Size) ? (UInt8) Buf[IndexOff + 1] : 0; + UInt16 packed = (high << 8) | low; + + rt_out16(IO + ATA_REG_DATA, packed); + } +} + +/// @brief is ATA detected? +Boolean drv_pio_std_detected(Void) { + return kATADetected; +} + +/*** + @brief Getter, gets the number of sectors inside the drive. + */ +SizeT drv_pio_get_sector_count() { + return (kATAIdentifyData[61] << 16) | kATAIdentifyData[60]; +} + +/// @brief Get the drive size. +SizeT drv_pio_get_size() { + return (drv_pio_get_sector_count()) * kATASectorSize; +} + +namespace Kernel { +/// @brief Initialize an PIO device (StorageKit function) +/// @param is_master is the current PIO master? +/// @return [io:master] for PIO device. +BOOL sk_init_ata_device(BOOL is_master, UInt16& io, UInt8& master) { + return drv_pio_std_init(ATA_SECONDARY_IO, is_master, io, master); +} + +/// @brief Implementation details namespace. +namespace Detail { + /// @brief Read PIO device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_read_pio(DeviceInterface* self, IMountpoint* mnt) { + ATADeviceInterface* dev = (ATADeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_pio_std_read(disk->fPacket.fPacketLba, dev->GetIO(), dev->GetMaster(), + (Char*) disk->fPacket.fPacketContent, kATASectorSize, + disk->fPacket.fPacketSize); + } + + /// @brief Write PIO device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_write_pio(DeviceInterface* self, IMountpoint* mnt) { + ATADeviceInterface* dev = (ATADeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_pio_std_write(disk->fPacket.fPacketLba, dev->GetIO(), dev->GetMaster(), + (Char*) disk->fPacket.fPacketContent, kATASectorSize, + disk->fPacket.fPacketSize); + } +} // namespace Detail + +/// @brief Acquires a new PIO device with drv_index in mind. +/// @param drv_index The drive index to assign. +/// @return A wrapped device interface if successful, or error code. +ErrorOr sk_acquire_ata_device(Int32 drv_index) { + /// here we don't check if we probed ATA, since we'd need to grab IO after that. + ATADeviceInterface device(Detail::sk_io_read_pio, Detail::sk_io_write_pio); + + device.SetIndex(drv_index); + + return ErrorOr(device); +} +} // namespace Kernel + +#ifdef __ATA_PIO__ + +Void drv_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + drv_pio_std_read(Lba, IO, Master, Buf, SectorSz, Size); +} + +Void drv_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + drv_pio_std_write(Lba, IO, Master, Buf, SectorSz, Size); +} + +SizeT drv_std_get_size() { + return drv_pio_get_size(); +} + +SizeT drv_std_get_sector_count() { + return drv_pio_get_sector_count(); +} + +Boolean drv_std_init(UInt16 Bus, UInt8 Drive, UInt16& OutBus, UInt8& OutMaster) { + return drv_pio_std_init(Bus, Drive, OutBus, OutMaster); +} + +#endif \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc b/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc new file mode 100644 index 00000000..0200ec5a --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc @@ -0,0 +1,13 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; + +///! @brief ATAPI SCSI packet. +const ATTRIBUTE(unused) scsi_packet_type_12 kCDRomPacketTemplate = {0x43, 0, 1, 0, 0, 0, + 0, 12, 0x40, 0, 0}; diff --git a/src/kernel/HALKit/ARM64/APM/APM+IO.cc b/src/kernel/HALKit/ARM64/APM/APM+IO.cc new file mode 100644 index 00000000..c4d0154b --- /dev/null +++ b/src/kernel/HALKit/ARM64/APM/APM+IO.cc @@ -0,0 +1,35 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +using namespace Kernel; + +/// @brief Send APM command to its IO space. +/// @param base_dma the IO base port. +/// @param cmd the command. +/// @return status code. +EXTERN_C Int32 apm_send_io_command(UInt16 cmd) { + switch (cmd) { + case kAPMPowerCommandReboot: { + asm volatile( + "ldr x0, =0x84000004\n" + "svc #0\n"); + + return kErrorSuccess; + } + case kAPMPowerCommandShutdown: { + asm volatile( + "ldr x0, =0x84000008\n" + "svc #0\n"); + + return kErrorSuccess; + } + default: + return kErrorInvalidData; + } +} diff --git a/src/kernel/HALKit/ARM64/ApplicationProcessor.h b/src/kernel/HALKit/ARM64/ApplicationProcessor.h new file mode 100644 index 00000000..208bf82e --- /dev/null +++ b/src/kernel/HALKit/ARM64/ApplicationProcessor.h @@ -0,0 +1,19 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +/************************************************** */ +/* INITIALIZE THE GIC ON THE CURRENT CORE. */ +/* WITH AN EXECUTION LEVEL IN MIND. */ +/************************************************** */ + +namespace Kernel { +Void mp_init_cores(Void) noexcept; +} \ No newline at end of file diff --git a/src/kernel/HALKit/ARM64/CxxAbi.cc b/src/kernel/HALKit/ARM64/CxxAbi.cc new file mode 100644 index 00000000..7b4eca20 --- /dev/null +++ b/src/kernel/HALKit/ARM64/CxxAbi.cc @@ -0,0 +1,87 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +atexit_func_entry_t __atexit_funcs[kAtExitMacDestructors]; + +uarch_t __atexit_func_count; + +/// @brief dynamic shared object Handle. +Kernel::UIntPtr __dso_handle; + +EXTERN_C void __chkstk(void) {} + +EXTERN_C int atexit(void (*f)(), void* arg, void* dso) { + if (__atexit_func_count >= kAtExitMacDestructors) return 1; + + __atexit_funcs[__atexit_func_count].destructor_func = f; + __atexit_funcs[__atexit_func_count].obj_ptr = arg; + __atexit_funcs[__atexit_func_count].dso_handle = dso; + + __atexit_func_count++; + + return 0; +} + +EXTERN_C void __cxa_finalize(void* f) { + uarch_t i = __atexit_func_count; + if (!f) { + while (i--) { + if (__atexit_funcs[i].destructor_func) { + (*__atexit_funcs[i].destructor_func)(); + __atexit_funcs[i].destructor_func = 0; + }; + } + + return; + } + + while (i--) { + if (__atexit_funcs[i].destructor_func) { + (*__atexit_funcs[i].destructor_func)(); + __atexit_funcs[i].destructor_func = 0; + }; + } +} + +namespace cxxabiv1 { +EXTERN_C int __cxa_guard_acquire(__guard* g) { + (void) g; + return 0; +} + +EXTERN_C int __cxa_guard_release(__guard* g) { + *(char*) g = 1; + return 0; +} + +EXTERN_C void __cxa_guard_abort(__guard* g) { + (void) g; +} +} // namespace cxxabiv1 + +EXTERN_C Kernel::Void _purecall(void* self) { + (Kernel::Void)(Kernel::kout << "object: " + << Kernel::number(reinterpret_cast(self))); + (Kernel::Void)(Kernel::kout << ", has unimplemented virtual functions.\r"); +} + +EXTERN_C Kernel::Void _Init_thread_footer(Kernel::Int* thread_obj) { + NE_UNUSED(thread_obj); +} + +EXTERN_C Kernel::Void _Init_thread_epoch(Kernel::Void) { + NE_UNUSED(0); +} + +EXTERN_C Kernel::Void _Init_thread_header(Kernel::Int* thread_obj) { + NE_UNUSED(0); +} + +EXTERN_C Kernel::Int _tls_index = 0UL; diff --git a/src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc b/src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc new file mode 100644 index 00000000..9a8661cd --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc @@ -0,0 +1,26 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include + +namespace Kernel { +ACPIFactoryInterface::ACPIFactoryInterface(VoidPtr rsp_ptr) : fRsdp(rsp_ptr), fEntries(0) {} + +BOOL ACPIFactoryInterface::Shutdown() { + apm_send_io_command(kAPMPowerCommandShutdown); + return NO; +} + +/// @brief Reboot machine in either ACPI or by triple faulting. +/// @return nothing it's a reboot. +Void ACPIFactoryInterface::Reboot() { + apm_send_io_command(kAPMPowerCommandReboot); +} +} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalApplicationProcessor.cc b/src/kernel/HALKit/ARM64/HalApplicationProcessor.cc new file mode 100644 index 00000000..2a3c73e5 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalApplicationProcessor.cc @@ -0,0 +1,140 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#define GICD_BASE 0x08000000 +#define GICC_BASE 0x08010000 + +#define GICD_CTLR 0x000 +#define GICD_ISENABLER 0x100 +#define GICD_ICENABLER 0x180 +#define GICD_ISPENDR 0x200 +#define GICD_ICPENDR 0x280 +#define GICD_IPRIORITYR 0x400 +#define GICD_ITARGETSR 0x800 +#define GICD_ICFGR 0xC00 + +#define GICC_CTLR 0x000 +#define GICC_PMR 0x004 +#define GICC_IAR 0x00C +#define GICC_EOIR 0x010 + +#include +#include +#include +#include +#include +#include + +// ================================================================= // + +namespace Kernel { +struct HAL_HARDWARE_THREAD final { + HAL::StackFramePtr mFramePtr; + ProcessID mThreadID{0}; +}; + +STATIC HAL_HARDWARE_THREAD kHWThread[kMaxAPInsideSched] = {{nullptr}}; + +namespace Detail { + STATIC BOOL kGICEnabled = NO; + + /***********************************************************************************/ + /// @brief Enables the GIC with EL0 configuration. + /// @internal + /***********************************************************************************/ + STATIC Void mp_setup_gic_el0(Void) { + ke_dma_write(GICD_BASE, GICD_CTLR, YES); + + UInt32 gicc_ctlr = ke_dma_read(GICC_BASE, GICC_CTLR); + + const UInt8 kEnableSignalInt = 0x1; + + gicc_ctlr |= kEnableSignalInt; + gicc_ctlr |= (kEnableSignalInt << 0x1); + + ke_dma_write(GICC_BASE, GICC_CTLR, gicc_ctlr); + + ke_dma_write(GICC_BASE, GICC_PMR, 0xFF); + + UInt32 icfgr = ke_dma_read(GICD_BASE, GICD_ICFGR + (0x20 / 0x10) * 4); + + icfgr |= (0x2 << ((32 % 16) * 2)); + + ke_dma_write(GICD_BASE, GICD_ICFGR + (0x20 / 0x10) * 4, icfgr); + ke_dma_write(GICD_BASE, GICD_ITARGETSR + (0x20 / 0x04) * 4, 0x2 << ((32 % 4) * 8)); + ke_dma_write(GICD_BASE, GICD_IPRIORITYR + (0x20 / 0x04) * 4, 0xFF << ((32 % 4) * 8)); + ke_dma_write(GICD_BASE, GICD_ISENABLER + 4, 0x01); + } + + EXTERN_C BOOL mp_handle_gic_interrupt_el0(Void) { + UInt32 interrupt_id = ke_dma_read(GICC_BASE, GICC_IAR); + + if ((interrupt_id & 0x3FF) < 1020) { + auto interrupt = interrupt_id & 0x3FF; + + const UInt16 kInterruptScheduler = 0x20; + + (Void)(kout << "SMP: AP: " << hex_number(interrupt) << kendl); + + switch (interrupt) { + case kInterruptScheduler: { + ke_dma_write(GICC_BASE, GICC_EOIR, interrupt_id); + UserProcessHelper::StartScheduling(); + break; + } + default: { + ke_dma_write(GICC_BASE, GICC_EOIR, interrupt_id); + break; + } + } + + return YES; + } + + return NO; + } +} // namespace Detail + +/***********************************************************************************/ +/// @brief Get current stack frame for a thread. +/// @param thrdid The thread ID. +/***********************************************************************************/ + +EXTERN_C HAL::StackFramePtr mp_get_current_task(ProcessID thrdid) { + return kHWThread[thrdid].mFramePtr; +} + +/***********************************************************************************/ +/// @brief Register current stack frame for a thread. +/// @param stack_frame The current stack frame. +/// @param thrdid The thread ID. +/***********************************************************************************/ + +EXTERN_C Bool mp_register_task(HAL::StackFramePtr stack_frame, ProcessID thrdid) { + MUST_PASS(Detail::kGICEnabled); + + if (!stack_frame) return NO; + if (thrdid > kMaxAPInsideSched) return NO; + + const auto process_index = thrdid; + + kHWThread[process_index].mFramePtr = stack_frame; + kHWThread[process_index].mThreadID = thrdid; + + return YES; +} + +/***********************************************************************************/ +/// @brief Initialize the Global Interrupt Controller. +/// @internal +/***********************************************************************************/ +Void mp_init_cores(Void) noexcept { + if (!Detail::kGICEnabled) { + Detail::kGICEnabled = YES; + Detail::mp_setup_gic_el0(); + } +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/HALKit/ARM64/HalApplicationProcessorStartup.s b/src/kernel/HALKit/ARM64/HalApplicationProcessorStartup.s new file mode 100644 index 00000000..dca52571 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalApplicationProcessorStartup.s @@ -0,0 +1,12 @@ +.text + +.global hal_ap_blob_start +.global hal_ap_blob_length + +hal_ap_blob_start: + ret + +.data + +hal_ap_blob_length: + .long 4 diff --git a/src/kernel/HALKit/ARM64/HalCommonAPI.s b/src/kernel/HALKit/ARM64/HalCommonAPI.s new file mode 100644 index 00000000..f0c69368 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalCommonAPI.s @@ -0,0 +1,9 @@ +/* (c) 2024-2025 Amlal El Mahrouss */ + +.text + +.global hal_flush_tlb + +hal_flush_tlb: + tlbi vmalle1 + ret diff --git a/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc b/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc new file mode 100644 index 00000000..b89f68bd --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc @@ -0,0 +1,159 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include + +EXTERN_C Kernel::Void int_handle_breakpoint(Kernel::UIntPtr rip); +EXTERN_C BOOL mp_handle_gic_interrupt_el0(Void); + +EXTERN_C BOOL kEndOfInterrupt; +EXTERN_C UInt8 kEndOfInterruptVector; + +STATIC BOOL kIsRunning = NO; + +/// @note This is managed by the system software. +STATIC void hal_int_send_eoi(UInt8 vector) { + kEndOfInterrupt = YES; + kEndOfInterruptVector = vector; +} + +/// @brief Handle GPF fault. +/// @param rsp +EXTERN_C Kernel::Void int_handle_gpf(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_int_send_eoi(13); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Handle page fault. +/// @param rsp +EXTERN_C void int_handle_pf(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_int_send_eoi(14); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Handle scheduler interrupt. +EXTERN_C void int_handle_scheduler(Kernel::UIntPtr rsp) { + NE_UNUSED(rsp); + + hal_int_send_eoi(32); + + while (kIsRunning) + ; + + kIsRunning = YES; + + mp_handle_gic_interrupt_el0(); + + kIsRunning = NO; +} + +/// @brief Handle math fault. +/// @param rsp +EXTERN_C void int_handle_math(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_int_send_eoi(8); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Handle any generic fault. +/// @param rsp +EXTERN_C void int_handle_generic(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_int_send_eoi(30); + + Kernel::kout << "Kernel: Generic Process Fault.\r"; + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; + + Kernel::kout << "Kernel: SIGKILL status.\r"; +} + +EXTERN_C Kernel::Void int_handle_breakpoint(Kernel::UIntPtr rip) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + + hal_int_send_eoi(3); + + process.Leak().Signal.SignalArg = rip; + process.Leak().Signal.SignalID = SIGTRAP; + + process.Leak().Signal.Status = process.Leak().Status; + + process.Leak().Status = Kernel::ProcessStatusKind::kFrozen; +} + +/// @brief Handle #UD fault. +/// @param rsp +EXTERN_C void int_handle_ud(Kernel::UIntPtr rsp) { + auto& process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Leak().Crash(); + + hal_int_send_eoi(6); + + process.Leak().Signal.SignalArg = rsp; + process.Leak().Signal.SignalID = SIGKILL; + process.Leak().Signal.Status = process.Leak().Status; +} + +/// @brief Enter syscall from assembly (libSystem only) +/// @param stack the stack pushed from assembly routine. +/// @return nothing. +EXTERN_C Kernel::Void hal_system_call_enter(Kernel::UIntPtr rcx_hash, + Kernel::UIntPtr rdx_syscall_arg) { + hal_int_send_eoi(50); + + if (!Kernel::kCurrentUser) return; + + for (SizeT i = 0UL; i < kMaxDispatchCallCount; ++i) { + if (kSysCalls[i].fHooked && rcx_hash == kSysCalls[i].fHash) { + if (kSysCalls[i].fProc) { + (kSysCalls[i].fProc)((Kernel::VoidPtr) rdx_syscall_arg); + } + } + } +} + +/// @brief Enter Kernel call from assembly (libDDK only). +/// @param stack the stack pushed from assembly routine. +/// @return nothing. +EXTERN_C Kernel::Void hal_kernel_call_enter(Kernel::UIntPtr rcx_hash, Kernel::SizeT cnt, + Kernel::UIntPtr arg, Kernel::SizeT sz) { + if (!Kernel::kRootUser) return; + if (Kernel::kCurrentUser != Kernel::kRootUser) return; + if (!Kernel::kCurrentUser->IsSuperUser()) return; + + for (SizeT i = 0UL; i < kMaxDispatchCallCount; ++i) { + if (kKernCalls[i].fHooked && rcx_hash == kKernCalls[rcx_hash].fHash) { + if (kKernCalls[i].fProc) { + (kKernCalls[i].fProc)(cnt, (Kernel::VoidPtr) arg, sz); + } + } + } +} diff --git a/src/kernel/HALKit/ARM64/HalDebugOutput.cc b/src/kernel/HALKit/ARM64/HalDebugOutput.cc new file mode 100644 index 00000000..c0da9c3a --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalDebugOutput.cc @@ -0,0 +1,71 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include + +namespace Kernel { +EXTERN_C void ke_io_write(DeviceInterface* self, const Char* bytes) { +#ifdef __DEBUG__ + if (*bytes == 0) return; + + SizeT index = 0; + SizeT len = 0; + + index = 0; + len = rt_string_len(bytes, 256U); + + volatile UInt8* uart_ptr = (UInt8*) 0x09000000; + + while (index < len) { + if (bytes[index] == '\r') *uart_ptr = '\r'; + + *uart_ptr = bytes[index] == '\r' ? '\n' : bytes[index]; + ++index; + } +#endif // __DEBUG__ +} + +TerminalDevice::~TerminalDevice() = default; + +EXTERN_C void ke_io_read(DeviceInterface* self, const Char* bytes) { +#ifdef __DEBUG__ + SizeT index = 0; + + volatile UInt8* uart_ptr = (UInt8*) 0x09000000; + + ///! TODO: Look on how to wait for the UART to complete. + while (Yes) { + auto in = *uart_ptr; + + ///! If enter pressed then break. + if (in == 0xD) { + break; + } + + if (in < '0' || in < 'A' || in < 'a') { + if (in != '@' || in != '!' || in != '?' || in != '.' || in != '/' || in != ':') { + continue; + } + } + + ((char*) bytes)[index] = in; + + ++index; + } + + ((char*) bytes)[index] = 0; +#endif // __DEBUG__ +} + +TerminalDevice TerminalDevice::The() noexcept { + TerminalDevice out(Kernel::ke_io_write, Kernel::ke_io_read); + return out; +} + +} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalHandoverStub.s b/src/kernel/HALKit/ARM64/HalHandoverStub.s new file mode 100644 index 00000000..5d5647c4 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalHandoverStub.s @@ -0,0 +1,19 @@ +;; /* +;; * ======================================================== +;; * +;; * NeKernel +;; * Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. +;; * +;; * ======================================================== +;; */ + +.section .ldr + + ;; // MAGIC +.quad 0xDAB4 + ;; // VERSION (1.0.0) +.word 100 + ;; // CPU (ARM64) +.word 0 + ;; // TYPE (KERNEL) +.word 122 diff --git a/src/kernel/HALKit/ARM64/HalInterruptAPI.s b/src/kernel/HALKit/ARM64/HalInterruptAPI.s new file mode 100644 index 00000000..cafebb7d --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalInterruptAPI.s @@ -0,0 +1,3 @@ +/* (c) 2024-2025 Amlal El Mahrouss */ + +.text diff --git a/src/kernel/HALKit/ARM64/HalKernelMain.cc b/src/kernel/HALKit/ARM64/HalKernelMain.cc new file mode 100644 index 00000000..e36535c3 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalKernelMain.cc @@ -0,0 +1,63 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __NE_MODULAR_KERNEL_COMPONENTS__ +EXTERN_C void hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { + using namespace Kernel; + + /************************************************** */ + /* INITIALIZE AND VALIDATE HEADER. */ + /************************************************** */ + + kHandoverHeader = handover_hdr; + + if (kHandoverHeader->f_Magic != kHandoverMagic && + kHandoverHeader->f_Version != kHandoverVersion) { + return; + } + +#ifdef __NE_ARM64_EFI__ + fw_init_efi((EfiSystemTable*) handover_hdr->f_FirmwareCustomTables[1]); + + Boot::ExitBootServices(handover_hdr->f_HardwareTables.f_ImageKey, + handover_hdr->f_HardwareTables.f_ImageHandle); +#endif + + FB::cg_clear_video(); + + /************************************** */ + /* INITIALIZE BIT MAP. */ + /************************************** */ + + kBitMapCursor = 0UL; + kKernelBitMpSize = kHandoverHeader->f_BitMapSize; + kKernelBitMpStart = reinterpret_cast( + reinterpret_cast(kHandoverHeader->f_BitMapStart)); + + /// @note do initialize the interrupts after it. + + Kernel::mp_init_cores(); + + while (YES) + ; +} +#endif diff --git a/src/kernel/HALKit/ARM64/HalKernelPanic.cc b/src/kernel/HALKit/ARM64/HalKernelPanic.cc new file mode 100644 index 00000000..6837ba1c --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalKernelPanic.cc @@ -0,0 +1,54 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Each error code is attributed with an ID, which will prompt a string onto the + * screen. Wait for debugger... */ + +namespace Kernel { +/// @brief Dumping factory class. +class RecoveryFactory final { + public: + STATIC Void Recover() noexcept; +}; + +/***********************************************************************************/ +/// @brief Stops execution of the kernel. +/// @param id kernel stop ID. +/***********************************************************************************/ +Void ke_panic(const Kernel::Int32& id, const Char* message) { + (Void)(kout << "*** STOP ***\r"); + (Void)(kout << "Kernel_Panic_MSG: " << message << kendl); + (Void)(kout << "Kernel_Panic_ID: " << hex_number(id) << kendl); + + RecoveryFactory::Recover(); +} + +Void RecoveryFactory::Recover() noexcept { + while (YES) { + HAL::rt_halt(); + } +} + +void ke_runtime_check(bool expr, const Char* file, const Char* line) { + if (!expr) { + (Void)(kout << "Kernel_Panic_FILE: " << file << kendl); + (Void)(kout << "Kernel_Panic_LINE: " << line << kendl); + + ke_panic(RUNTIME_CHECK_FAILED, file); // Runtime Check failed + } +} +} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalPagingMgr.cc b/src/kernel/HALKit/ARM64/HalPagingMgr.cc new file mode 100644 index 00000000..d597ccce --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalPagingMgr.cc @@ -0,0 +1,86 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: HalPagingMgr.cc + Purpose: Platform Paging Manager. + +======================================== */ + +#include +#include + +namespace Kernel::HAL { +typedef UInt32 PageTableIndex; + +EXTERN_C UIntPtr mm_get_page_addr(VoidPtr virtual_address) { + if (!virtual_address) return 0; + + UInt64 ttbr0_val = 0; + + asm volatile("mrs %0, ttbr0_el1" : "=r"(ttbr0_val)); + volatile UInt64* l1_table = reinterpret_cast(ttbr0_val); + + UInt64 l1_idx = (reinterpret_cast(virtual_address) >> 39) & 0x1FF; + UInt64 l2_idx = (reinterpret_cast(virtual_address) >> 30) & 0x1FF; + UInt64 l3_idx = (reinterpret_cast(virtual_address) >> 21) & 0x1FF; + + if (!l1_table[l1_idx]) return 0; + + volatile UInt64* l2_table = reinterpret_cast(l1_table[l1_idx] & ~0xFFFUL); + + if (!l2_table[l2_idx]) return 0; + + volatile UInt64* l3_table = reinterpret_cast(l2_table[l2_idx] & ~0xFFFUL); + + if (!l3_table[l3_idx]) return 0; + + return (l3_table[l3_idx] & ~0xFFFUL); +} + +/// @brief Maps or allocates a page from virtual_address. +/// @param virtual_address a valid virtual address. +/// @param phys_addr point to physical address. +/// @param flags the flags to put on the page. +/// @return Status code of page manipulation process. +EXTERN_C Int32 mm_map_page(VoidPtr virtual_address, VoidPtr physical_address, UInt32 flags, + UInt32 level) { + if (!virtual_address || !flags || !physical_address) return kErrorInvalidData; + + UInt64 ttbr0_val = 0; + + asm volatile("mrs %0, ttbr0_el1" : "=r"(ttbr0_val)); + volatile UInt64* l1_table = reinterpret_cast(ttbr0_val); + + UInt64 l1_idx = (reinterpret_cast(virtual_address) >> 39) & 0x1FF; + UInt64 l2_idx = (reinterpret_cast(virtual_address) >> 30) & 0x1FF; + UInt64 l3_idx = (reinterpret_cast(virtual_address) >> 21) & 0x1FF; + + if (!l1_table[l1_idx]) return kErrorInvalidData; + + volatile UInt64* l2_table = reinterpret_cast(l1_table[l1_idx] & ~0xFFFUL); + + if (!l2_table[l2_idx]) return kErrorInvalidData; + + volatile UInt64* l3_table = reinterpret_cast(l2_table[l2_idx] & ~0xFFFUL); + + l3_table[l3_idx] = (reinterpret_cast(physical_address) & ~0xFFFUL) | flags; + + switch (level) { + case 2: { + l3_table[l3_idx] = (reinterpret_cast(physical_address) & ~0xFFFUL) | flags; + return kErrorSuccess; + } + case 1: { + l1_table[l1_idx] = (reinterpret_cast(physical_address) & ~0xFFFUL) | flags; + return kErrorSuccess; + } + case 0: { + l1_table[l1_idx] = (reinterpret_cast(physical_address) & ~0xFFFUL) | flags; + return kErrorSuccess; + } + } + + return kErrorInvalidData; +} +} // namespace Kernel::HAL diff --git a/src/kernel/HALKit/ARM64/HalSchedulerCore.cc b/src/kernel/HALKit/ARM64/HalSchedulerCore.cc new file mode 100644 index 00000000..467547b0 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalSchedulerCore.cc @@ -0,0 +1,21 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel { +/// @brief Wakes up thread. +/// Wakes up thread from the hang state. +Void mp_wakeup_thread(HAL::StackFrame* stack) { + NE_UNUSED(stack); +} + +/// @brief makes the thread sleep on a loop. +/// hooks and hangs thread to prevent code from executing. +Void mp_hang_thread(HAL::StackFrame* stack) { + NE_UNUSED(stack); +} +} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cc b/src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cc new file mode 100644 index 00000000..b1728bac --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cc @@ -0,0 +1,30 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief Unimplemented function (crashes by default) +/// @param process The process handle. +/***********************************************************************************/ + +EXTERN_C Void __ne_pure_call(USER_PROCESS* process) { + if (process) process->Crash(); +} + +/***********************************************************************************/ +/// @brief Validate user stack. +/// @param stack_ptr the frame pointer. +/***********************************************************************************/ + +EXTERN_C Bool hal_check_task(HAL::StackFramePtr stack_ptr) { + if (!stack_ptr) return No; + + return stack_ptr->SP != 0 && stack_ptr->IP != 0; +} +} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalTimer.cc b/src/kernel/HALKit/ARM64/HalTimer.cc new file mode 100644 index 00000000..2f524a1b --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalTimer.cc @@ -0,0 +1,15 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: HalTimer.cc + Purpose: HAL timer + + Revision History: + + 07/07/24: Added file (amlel) + +======================================== */ + +#include +#include \ No newline at end of file diff --git a/src/kernel/HALKit/ARM64/Paging.h b/src/kernel/HALKit/ARM64/Paging.h new file mode 100644 index 00000000..766210b3 --- /dev/null +++ b/src/kernel/HALKit/ARM64/Paging.h @@ -0,0 +1,107 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +/** --------------------------------------------------- + + * THIS FILE CONTAINS CODE FOR ARMV8 PAGING. + +------------======================================== */ + +#ifdef __NE_ARM64__ + +#include + +#ifndef kPageMax +#define kPageMax (0x200) +#endif //! kPageMax + +#ifndef kPageAlign +#define kPageAlign (0x1000) +#endif //! kPageAlign + +#ifndef kPageSize +#define kPageSize (0x1000) +#endif // !kPageSize + +//! short format address range + +#define c16KBPage 0b000 +#define c8KBPage 0b001 +#define c4KBPage 0b010 +#define c2KBPage 0b011 +#define c1KBPage 0b100 +#define c512BPage 0b101 +#define c256BPage 0b110 +#define c128BPage 0b111 + +/// Long format address range + +#define cPageMAll \ + { 0b000, 0b000 } +#define cPageMToMax(M) \ + { M, 0b000 } +#define cPageMaxToM(M) \ + { 0b000, M } +#define cPageMToN(M, N) \ + { M, N } + +namespace Kernel::HAL { +struct PACKED PTE_4KB final { + UInt64 Valid : 1; + UInt64 Table : 1; + UInt64 AttrIndex : 3; + UInt64 NS : 1; + UInt64 AP : 2; + UInt64 SH : 2; + UInt64 AF : 1; + UInt64 NG : 1; + UInt64 Reserved1 : 1; + UInt64 Contiguous : 1; + UInt64 Dirty : 1; + UInt64 Reserved : 2; + UInt64 PhysicalAddress : 36; + UInt64 Reserved3 : 4; + UInt64 PXN : 1; + UInt64 XN : 1; + UInt64 Reserved4 : 9; +}; + +namespace Detail { + enum class ControlRegisterBits { + ProtectedModeEnable = 0, + MonitorCoProcessor = 1, + Emulation = 2, + TaskSwitched = 3, + ExtensionType = 4, + NumericError = 5, + WriteProtect = 16, + AlignementMask = 18, + NotWriteThrough = 29, + CacheDisable = 30, + PageEnable = 31, + }; + + inline UInt8 control_register_cast(ControlRegisterBits reg) { return static_cast(reg); } +} // namespace Detail + +struct PDE_4KB final { + PTE_4KB ALIGN(kPageAlign) fEntries[kPageMax]; +}; + +auto mm_alloc_bitmap(Boolean wr, Boolean user, SizeT size, Bool is_page, SizeT pad = 0) -> VoidPtr; +auto mm_free_bitmap(VoidPtr page_ptr) -> Bool; +} // namespace Kernel::HAL + +namespace Kernel { +typedef HAL::PTE_4KB PTE; +typedef HAL::PDE_4KB PDE; +} // namespace Kernel + +EXTERN_C void hal_flush_tlb(); + +#endif // __NE_ARM64__ \ No newline at end of file diff --git a/src/kernel/HALKit/ARM64/Processor.h b/src/kernel/HALKit/ARM64/Processor.h new file mode 100644 index 00000000..716d317b --- /dev/null +++ b/src/kernel/HALKit/ARM64/Processor.h @@ -0,0 +1,78 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#ifdef __NE_ARM64__ + +#include +#include +#include +#include + +#define kCPUBackendName "aarch64" + +namespace Kernel::HAL { +struct PACKED Register64 final { + UShort Limit; + UIntPtr Base; +}; + +/// @brief Memory Manager mapping flags. +enum { + kMMFlagsInvalid = 1 << 0, + kMMFlagsPresent = 1 << 1, + kMMFlagsWr = 1 << 2, + kMMFlagsUser = 1 << 3, + kMMFlagsNX = 1 << 4, + kMMFlagsCount = 4, +}; + +/// @brief Set a PTE from pd_base. +/// @param virt_addr a valid virtual address. +/// @param phys_addr point to physical address. +/// @param flags the flags to put on the page. +/// @return Status code of page manip. +EXTERN_C Int32 mm_map_page(VoidPtr virtual_address, VoidPtr physical_address, UInt32 flags, + UInt32 level = 2); + +EXTERN_C UIntPtr mm_get_page_addr(VoidPtr virtual_address); + +typedef UIntPtr Reg; +typedef Register64 Register; + +/// @note let's keep the same name as AMD64 HAL. +struct PACKED StackFrame { + Reg IP; + Reg SP; + Reg R8; + Reg R9; + Reg R10; + Reg R11; + Reg R12; + Reg R13; + Reg R14; + Reg R15; +}; + +typedef StackFrame* StackFramePtr; + +inline Void rt_halt() noexcept { + while (Yes) { + } +} + +inline Void hal_wfi(Void) { + asm volatile("wfi"); +} +} // namespace Kernel::HAL + +inline Kernel::VoidPtr kKernelBitMpStart = nullptr; +inline Kernel::UIntPtr kKernelBitMpSize = 0UL; + +#include + +#endif // __NE_ARM64__ \ No newline at end of file diff --git a/src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc b/src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc new file mode 100644 index 00000000..0200ec5a --- /dev/null +++ b/src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc @@ -0,0 +1,13 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; + +///! @brief ATAPI SCSI packet. +const ATTRIBUTE(unused) scsi_packet_type_12 kCDRomPacketTemplate = {0x43, 0, 1, 0, 0, 0, + 0, 12, 0x40, 0, 0}; diff --git a/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc b/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc new file mode 100644 index 00000000..9971b2a8 --- /dev/null +++ b/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc @@ -0,0 +1,8 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/// @file UFS.cc +/// @brief UFS Flash Memory support. diff --git a/src/kernel/HALKit/POWER/.gitkeep b/src/kernel/HALKit/POWER/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/POWER/AP.h b/src/kernel/HALKit/POWER/AP.h new file mode 100644 index 00000000..efe4ceff --- /dev/null +++ b/src/kernel/HALKit/POWER/AP.h @@ -0,0 +1,39 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: AP.h + Purpose: POWER hardware threads. + + Revision History: + + 14/04/24: Added file (amlel) + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +struct HAL_HARDWARE_THREAD; + +/// @brief hardware thread indentification type. +typedef Kernel::Int32 hal_ap_kind; + +/// @brief Hardware thread information structure. +typedef struct HAL_HARDWARE_THREAD { + Kernel::UIntPtr fStartAddress; + Kernel::UIntPtr fStackPtr; + Kernel::UIntPtr fFramePtr; + Kernel::UInt8 fPrivileged : 1; + Kernel::UInt32 fPageMemoryFlags; + hal_ap_kind fIdentNumber; +} HAL_HARDWARE_THREAD; + +/// @brief Set PC to specific hart. +/// @param hart the hart +/// @param epc the pc. +/// @return +EXTERN_C Kernel::Void hal_set_pc_to_hart(HAL_HARDWARE_THREAD* hart, Kernel::VoidPtr epc); +} // namespace Kernel diff --git a/src/kernel/HALKit/POWER/APM/.gitkeep b/src/kernel/HALKit/POWER/APM/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/POWER/HalApplicationProcessor.cc b/src/kernel/HALKit/POWER/HalApplicationProcessor.cc new file mode 100644 index 00000000..84d9b1c1 --- /dev/null +++ b/src/kernel/HALKit/POWER/HalApplicationProcessor.cc @@ -0,0 +1,43 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +/// @note This part of the HAL needs an owner. + +namespace Kernel::Detail { +STATIC void mp_hang_fn(void) { + while (YES) + ; +} +} // namespace Kernel::Detail + +namespace Kernel { +/// @brief wakes up thread. +/// wakes up thread from hang. +void mp_wakeup_thread(HAL::StackFramePtr stack) { + if (!stack) return; + + MUST_PASS(stack->R15 > 0); + MUST_PASS(stack->IP > 0); + + hal_set_pc_to_hart(reinterpret_cast(stack->R15), + reinterpret_cast(stack->IP)); +} + +/// @brief makes thread sleep. +/// hooks and hangs thread to prevent code from executing. +void mp_hang_thread(HAL::StackFramePtr stack) { + if (!stack) return; + + MUST_PASS(stack->R15 > 0); + + hal_set_pc_to_hart(reinterpret_cast(stack->R15), + reinterpret_cast(Kernel::Detail::mp_hang_fn)); +} +} // namespace Kernel diff --git a/src/kernel/HALKit/POWER/HalDebugOutput.cc b/src/kernel/HALKit/POWER/HalDebugOutput.cc new file mode 100644 index 00000000..0c4be809 --- /dev/null +++ b/src/kernel/HALKit/POWER/HalDebugOutput.cc @@ -0,0 +1,24 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +using namespace Kernel; + +/// @brief Writes to COM1. +/// @param bytes +void ke_io_write(const Char* bytes) { + if (!bytes) return; + + SizeT index = 0; + SizeT len = rt_string_len(bytes, 256U); + + while (index < len) { + // TODO + ++index; + } +} diff --git a/src/kernel/HALKit/POWER/HalHardwareThread.cc b/src/kernel/HALKit/POWER/HalHardwareThread.cc new file mode 100644 index 00000000..c77040f7 --- /dev/null +++ b/src/kernel/HALKit/POWER/HalHardwareThread.cc @@ -0,0 +1,8 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include diff --git a/src/kernel/HALKit/POWER/HalStartSequence.s b/src/kernel/HALKit/POWER/HalStartSequence.s new file mode 100644 index 00000000..194e220e --- /dev/null +++ b/src/kernel/HALKit/POWER/HalStartSequence.s @@ -0,0 +1,14 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +.globl __ImageStart +.extern hal_init_platform +.align 4 +.text + +__ImageStart: + bl hal_init_platform + blr diff --git a/src/kernel/HALKit/POWER/HalVirtualMemory.cc b/src/kernel/HALKit/POWER/HalVirtualMemory.cc new file mode 100644 index 00000000..cd9511c9 --- /dev/null +++ b/src/kernel/HALKit/POWER/HalVirtualMemory.cc @@ -0,0 +1,46 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +/// @note Refer to SoC documentation. + +using namespace Kernel; + +EXTERN_C Void hal_write_tlb(UInt32 mas0, UInt32 mas1, UInt32 mas2, UInt32 mas3, UInt32 mas7) { + hal_mtspr(MAS0, mas0); + hal_mtspr(MAS1, mas1); + hal_mtspr(MAS2, mas2); + hal_mtspr(MAS3, mas3); + hal_mtspr(MAS7, mas7); + + hal_flush_tlb(); +} + +EXTERN_C Bool hal_set_tlb(UInt8 tlb, UInt32 epn, UInt64 rpn, UInt8 perms, UInt8 wimge, UInt8 ts, + UInt8 esel, UInt8 tsize, UInt8 iprot) { + if ((hal_mfspr(SPRN_MMUCFG) & MMUCFG_MAVN) == MMUCFG_MAVN_V1 && (tsize & 1)) { + // this MMU does not allow odd tsize values + return false; + } + + UInt32 mas0 = FSL_BOOKE_MAS0(tlb, esel, 0); + UInt32 mas1 = FSL_BOOKE_MAS1(1, iprot, 0, ts, tsize); + UInt32 mas2 = FSL_BOOKE_MAS2(epn, wimge); + UInt32 mas3 = FSL_BOOKE_MAS3(rpn, 0, perms); + UInt32 mas7 = FSL_BOOKE_MAS7(rpn); + + hal_write_tlb(mas0, mas1, mas2, mas3, mas7); + + return true; +} + +/// @brief Flush TLB +EXTERN_C void hal_flush_tlb() { + asm volatile("isync;tlbwe;msync;isync"); +} diff --git a/src/kernel/HALKit/POWER/Processor.h b/src/kernel/HALKit/POWER/Processor.h new file mode 100644 index 00000000..46cda33e --- /dev/null +++ b/src/kernel/HALKit/POWER/Processor.h @@ -0,0 +1,60 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + Purpose: POWER processor header. + +======================================== */ + +#pragma once + +#include +#include + +#define rtl_nop_op() asm volatile("mr 0, 0") +#define kHalPPCAlignment __attribute__((aligned(4))) + +namespace Kernel::HAL { +typedef UIntPtr Reg; + +/// @brief Stack frame (as retrieved from assembly.) +struct PACKED StackFrame final { + Reg R8{0}; + Reg R9{0}; + Reg R10{0}; + Reg R11{0}; + Reg R12{0}; + Reg R13{0}; + Reg R14{0}; + Reg R15{0}; + Reg SP{0}; + Reg IP{0}; +}; + +typedef StackFrame* StackFramePtr; + +inline void rt_halt() { + while (true) { + NoOp(); // no oop. + } +} + +inline void rt_cli() { + NoOp(); // no oop +} +} // namespace Kernel::HAL + +EXTERN_C Kernel::Void int_handle_math(Kernel::UIntPtr sp); +EXTERN_C Kernel::Void int_handle_pf(Kernel::UIntPtr sp); + +/// @brief Set TLB. +Kernel::Bool hal_set_tlb(Kernel::UInt8 tlb, Kernel::UInt32 epn, Kernel::UInt64 rpn, + Kernel::UInt8 perms, Kernel::UInt8 wimge, Kernel::UInt8 ts, + Kernel::UInt8 esel, Kernel::UInt8 tsize, Kernel::UInt8 iprot); + +/// @brief Write TLB. +Kernel::Void hal_write_tlb(Kernel::UInt32 mas0, Kernel::UInt32 mas1, Kernel::UInt32 mas2, + Kernel::UInt32 mas3, Kernel::UInt32 mas7); + +/// @brief Flush TLB. +EXTERN_C Kernel::Void hal_flush_tlb(); diff --git a/src/kernel/HALKit/RISCV/.keep b/src/kernel/HALKit/RISCV/.keep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/RISCV/AP.h b/src/kernel/HALKit/RISCV/AP.h new file mode 100644 index 00000000..1061b637 --- /dev/null +++ b/src/kernel/HALKit/RISCV/AP.h @@ -0,0 +1,35 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: AP.h + Purpose: RISC-V hardware threads. + + Revision History: + + 30/01/24: Added file (amlel) + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +typedef Int64 hal_ap_kind; + +typedef struct HAL_HARDWARE_THREAD { + Kernel::UIntPtr fStartAddress; + Kernel::UIntPtr fStackPtr; + Kernel::UIntPtr fFramePtr; + Kernel::UInt8 fPrivileged : 1; + Kernel::UInt32 fPageMemoryFlags; + hal_ap_kind fIdentNumber; +} HAL_HARDWARE_THREAD; + +/// @brief Set PC to specific hart. +/// @param hart the hart +/// @param epc the pc. +/// @return +EXTERN_C Kernel::Void hal_set_pc_to_hart(HAL_HARDWARE_THREAD* hart, Kernel::VoidPtr epc); +} // namespace Kernel diff --git a/src/kernel/HALKit/RISCV/APM/.gitkeep b/src/kernel/HALKit/RISCV/APM/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/RISCV/HalApplicationProcessor.cc b/src/kernel/HALKit/RISCV/HalApplicationProcessor.cc new file mode 100644 index 00000000..520481d1 --- /dev/null +++ b/src/kernel/HALKit/RISCV/HalApplicationProcessor.cc @@ -0,0 +1,40 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +using namespace Kernel; + +namespace Kernel { +namespace Detail { + STATIC void mp_hang_fn(void) { + while (YES) + ; + } + +} // namespace Detail + +/// @brief wakes up thread. +/// wakes up thread from hang. +void mp_wakeup_thread(HAL::StackFramePtr stack) { + if (!stack) return; + + hal_set_pc_to_hart(reinterpret_cast(stack->R15), + reinterpret_cast(stack->IP)); +} + +/// @brief makes thread sleep. +/// hooks and hangs thread to prevent code from executing. +void mp_hang_thread(HAL::StackFramePtr stack) { + if (!stack) return; + + hal_set_pc_to_hart(reinterpret_cast(stack->R15), + reinterpret_cast(Kernel::Detail::mp_hang_fn)); +} + +} // namespace Kernel diff --git a/src/kernel/HALKit/RISCV/Storage/.gitkeep b/src/kernel/HALKit/RISCV/Storage/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/X86S/.gitkeep b/src/kernel/HALKit/X86S/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/X86S/ACPI/.gitkeep b/src/kernel/HALKit/X86S/ACPI/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/HALKit/X86S/Storage/.gitkeep b/src/kernel/HALKit/X86S/Storage/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/KernelKit/BinaryMutex.h b/src/kernel/KernelKit/BinaryMutex.h new file mode 100644 index 00000000..5431ab72 --- /dev/null +++ b/src/kernel/KernelKit/BinaryMutex.h @@ -0,0 +1,39 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +namespace Kernel { +class USER_PROCESS; + +/// @brief Access control class, which locks a task until one is done. +class BinaryMutex final { + public: + explicit BinaryMutex() = default; + ~BinaryMutex() = default; + + public: + bool IsLocked() const; + bool Unlock() noexcept; + + public: + BOOL WaitForProcess(const UInt32& sec) noexcept; + + public: + bool Lock(USER_PROCESS* process); + bool LockAndWait(USER_PROCESS* process, TimerInterface* timer); + + public: + NE_COPY_DEFAULT(BinaryMutex) + + private: + USER_PROCESS* fLockingProcess{nullptr}; +}; +} // namespace Kernel diff --git a/src/kernel/KernelKit/CodeMgr.h b/src/kernel/KernelKit/CodeMgr.h new file mode 100644 index 00000000..e537b26d --- /dev/null +++ b/src/kernel/KernelKit/CodeMgr.h @@ -0,0 +1,49 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: CodeMgr.h + Purpose: Code Mgr. + + Revision History: + + 30/01/24: Added file (amlel) + 3/8/24: Add UPP struct. + +======================================== */ + +#pragma once + +#include +#include +#include +#include + +/// @file CodeMgr.h +/// @brief Code Manager header file. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +namespace Kernel { +/// @brief Main process entrypoint. +typedef void (*rtl_main_kind)(void); + +/// @brief C++ Constructor entrypoint. +typedef void (*rtl_cxx_ctor_kind)(void); + +/// @brief C++ Destructor entrypoint. +typedef void (*rtl_cxx_dtor_kind)(void); + +/// @brief Executes a new process from a function. Kernel code only. +/// @note This sets up a new stack, anything on the main function that calls the Kernel will not be +/// accessible. +/// @param main the start of the process. +/// @return The team's process id. +BOOL rtl_create_kernel_task(HAL::StackFramePtr main, const KID kid) noexcept; + +/// @brief Executes a new process from a function. User code only. +/// @note This sets up a new stack, anything on the main function that calls the Kernel will not be +/// accessible. +/// @param main the start of the process. +/// @return The team's process id. +ProcessID rtl_create_user_process(rtl_main_kind main, const Char* process_name) noexcept; +} // namespace Kernel diff --git a/src/kernel/KernelKit/CoreProcessScheduler.h b/src/kernel/KernelKit/CoreProcessScheduler.h new file mode 100644 index 00000000..54a0614a --- /dev/null +++ b/src/kernel/KernelKit/CoreProcessScheduler.h @@ -0,0 +1,273 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +/// @file CoreProcessScheduler.h +/// @brief Core Process Scheduler header file. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +#define kSchedMinMicroTime (AffinityKind::kStandard) +#define kSchedInvalidPID (-1) +#define kSchedProcessLimitPerTeam (32U) +#define kSchedTeamCount (256U) + +#define kSchedMaxMemoryLimit (gib_cast(128)) /* max physical memory limit */ +#define kSchedMaxStackSz (kib_cast(8)) /* maximum stack size */ + +#define kSchedNameLen (128U) + +EXTERN_C void sched_idle_task(void); + +namespace Kernel { +class USER_PROCESS; +class KERNEL_TASK; +class KernelTaskScheduler; +class UserProcessScheduler; +class UserProcessTeam; + +template +struct PROCESS_HEAP_TREE; + +template +struct PROCESS_SPECIAL_TREE; + +template +struct PROCESS_FILE_TREE; + +enum { + kInvalidTreeKind = 0U, + kRedTreeKind = 100U, + kBlackTreeKind = 101U, + kTreeKindCount = 3U, +}; + +template +struct PROCESS_HEAP_TREE { + static constexpr auto kHeap = true; + static constexpr auto kFile = false; + static constexpr auto kSpecial = false; + + T Entry{nullptr}; + SizeT EntrySize{0UL}; + SizeT EntryPad{0UL}; + + UInt32 Color{kBlackTreeKind}; + + struct PROCESS_HEAP_TREE* Parent { + nullptr + }; + struct PROCESS_HEAP_TREE* Child { + nullptr + }; + + struct PROCESS_HEAP_TREE* Prev { + nullptr + }; + struct PROCESS_HEAP_TREE* Next { + nullptr + }; +}; + +template +struct PROCESS_FILE_TREE { + static constexpr auto kHeap = false; + static constexpr auto kFile = true; + static constexpr auto kSpecial = false; + + T Entry{nullptr}; + SizeT EntrySize{0UL}; + SizeT EntryPad{0UL}; + + UInt32 Color{kBlackTreeKind}; + + struct PROCESS_FILE_TREE* Parent { + nullptr + }; + + struct PROCESS_FILE_TREE* Child { + nullptr + }; + + struct PROCESS_FILE_TREE* Prev { + nullptr + }; + + struct PROCESS_FILE_TREE* Next { + nullptr + }; +}; + +using ProcessCtx = UInt32; + +template +struct PROCESS_SPECIAL_TREE { + static constexpr auto kHeap = false; + static constexpr auto kFile = false; + static constexpr auto kSpecial = true; + + T Entry{nullptr}; + SizeT EntrySize{0UL}; + SizeT EntryPad{0UL}; + + /// @brief a context is where the resource comes from. + ProcessCtx EntryContext{0UL}; // could be a socket, printer, device... + + UInt32 Color{kBlackTreeKind}; + + struct PROCESS_SPECIAL_TREE* Parent { + nullptr + }; + + struct PROCESS_SPECIAL_TREE* Child { + nullptr + }; + + struct PROCESS_SPECIAL_TREE* Prev { + nullptr + }; + + struct PROCESS_SPECIAL_TREE* Next { + nullptr + }; +}; + +/***********************************************************************************/ +/// @brief Subsystem enum type. +/***********************************************************************************/ + +enum class ProcessSubsystem : Int32 { + kProcessSubsystemSecurity = 100, + kProcessSubsystemUser, + kProcessSubsystemService, + kProcessSubsystemDriver, + kProcessSubsystemKernel, + kProcessSubsystemCount = kProcessSubsystemKernel - kProcessSubsystemSecurity + 1, + kProcessSubsystemInvalid = 0xFFFFFFF, +}; + +/***********************************************************************************/ +//! @brief Local Process status enum. +/***********************************************************************************/ +enum class ProcessStatusKind : Int32 { + kInvalid = 0, + kStarting = 100, + kRunning, + kKilled, + kFrozen, + kFinished, + kCount = kFinished - kStarting + 1, +}; + +/***********************************************************************************/ +//! @brief Affinity is the amount of nano-seconds this process is going to run. +/***********************************************************************************/ +enum class AffinityKind : Int32 { + kInvalid = 0, + kRealTime = 100, + kVeryHigh = 150, + kHigh = 200, + kStandard = 1000, + kLowUsage = 1500, + kVeryLowUsage = 2000, +}; + +/***********************************************************************************/ +//! Operators for AffinityKind +/***********************************************************************************/ + +inline bool operator<(AffinityKind lhs, AffinityKind rhs) { + Int32 lhs_int = static_cast(lhs); + Int32 rhs_int = static_cast(rhs); + + return lhs_int < rhs_int; +} + +inline bool operator>(AffinityKind lhs, AffinityKind rhs) { + Int32 lhs_int = static_cast(lhs); + Int32 rhs_int = static_cast(rhs); + + return lhs_int > rhs_int; +} + +inline bool operator<=(AffinityKind lhs, AffinityKind rhs) { + Int32 lhs_int = static_cast(lhs); + Int32 rhs_int = static_cast(rhs); + + return lhs_int <= rhs_int; +} + +inline bool operator>=(AffinityKind lhs, AffinityKind rhs) { + Int32 lhs_int = static_cast(lhs); + Int32 rhs_int = static_cast(rhs); + + return lhs_int >= rhs_int; +} + +using PTime = UInt64; +using ProcessTime = PTime; + +/***********************************************************************************/ +//! @brief Local Process Identifier type. +/***********************************************************************************/ +using ProcessID = Int64; + +/***********************************************************************************/ +/// @note For User manager, tells where we run the code. +/***********************************************************************************/ +enum class ProcessLevelRing : Int32 { + kRingStdUser = 1, + kRingSuperUser = 2, + kRingGuestUser = 5, + kRingCount = 3, +}; + +/***********************************************************************************/ +/// @brief Helper type to describe a code image. +/***********************************************************************************/ +using ImagePtr = VoidPtr; + +/***********************************************************************************/ +/// @brief Helper class to contain a process's image and blob. +/***********************************************************************************/ +struct ProcessImage final { + explicit ProcessImage() = default; + + private: + friend USER_PROCESS; + friend KERNEL_TASK; + + friend UserProcessScheduler; + friend KernelTaskScheduler; + + ImagePtr fCode{}; + ImagePtr fBlob{}; + + public: + Bool HasCode() const { return this->fCode != nullptr; } + + Bool HasImage() const { return this->fBlob != nullptr; } + + ErrorOr LeakImage() { + if (this->fCode) { + return ErrorOr{this->fCode}; + } + + return ErrorOr{kErrorInvalidData}; + } + + ErrorOr LeakBlob() { + if (this->fBlob) { + return ErrorOr{this->fBlob}; + } + + return ErrorOr{kErrorInvalidData}; + } +}; +} // namespace Kernel diff --git a/src/kernel/KernelKit/DebugOutput.h b/src/kernel/KernelKit/DebugOutput.h new file mode 100644 index 00000000..3f9b5125 --- /dev/null +++ b/src/kernel/KernelKit/DebugOutput.h @@ -0,0 +1,207 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { +class TerminalDevice; +class DTraceDevice; +class NeTraceDevice; +class Utf8TerminalDevice; + +inline TerminalDevice end_line(); +inline TerminalDevice number(const Long& x); +inline TerminalDevice hex_number(const Long& x); + +// @brief Emulates a VT100 terminal. +class TerminalDevice final NE_DEVICE { + public: + TerminalDevice(void (*print)(DeviceInterface*, const Char*), + void (*gets)(DeviceInterface*, const Char*)) + : DeviceInterface(print, gets) {} + + ~TerminalDevice() override; + + /// @brief returns device name (terminal name) + /// @return string type (const Char*) + const Char* Name() const override { return ("TerminalDevice"); } + + NE_COPY_DEFAULT(TerminalDevice) + + STATIC TerminalDevice The() noexcept; +}; + +class Utf8TerminalDevice final NE_DEVICE { + public: + Utf8TerminalDevice(void (*print)(DeviceInterface*, const Utf8Char*), + void (*gets)(DeviceInterface*, const Utf8Char*)) + : DeviceInterface(print, gets) {} + + ~Utf8TerminalDevice() override; + + /// @brief returns device name (terminal name) + /// @return string type (const Char*) + const Char* Name() const override { return ("Utf8TerminalDevice"); } + + NE_COPY_DEFAULT(Utf8TerminalDevice) + + STATIC Utf8TerminalDevice The() noexcept; +}; + +inline TerminalDevice end_line() { + TerminalDevice self = TerminalDevice::The(); + + self.operator<<("\r"); + return self; +} + +inline Utf8TerminalDevice utf_end_line() { + Utf8TerminalDevice self = Utf8TerminalDevice::The(); + + self.operator<<(u8"\r"); + return self; +} + +inline TerminalDevice carriage_return() { + TerminalDevice self = TerminalDevice::The(); + + self.operator<<("\r"); + return self; +} + +inline TerminalDevice tabulate() { + TerminalDevice self = TerminalDevice::The(); + + self.operator<<("\t"); + return self; +} + +/// @brief emulate a terminal bell, like the VT100 does. +inline TerminalDevice bell() { + TerminalDevice self = TerminalDevice::The(); + + self.operator<<("\a"); + return self; +} + +namespace Detail { + inline TerminalDevice _write_number(const Long& x, TerminalDevice& term) { + UInt64 y = (x > 0 ? x : -x) / 10; + UInt64 h = (x > 0 ? x : -x) % 10; + + if (y) _write_number(y, term); + + /* fail if the number is not base-10 */ + if (h > 10) { + _write_number('?', term); + return term; + } + + if (y == ~0UL) y = -y; + + const Char kNumbers[11] = "0123456789"; + + Char buf[2]; + buf[0] = kNumbers[h]; + buf[1] = 0; + + term.operator<<(buf); + return term; + } + + inline TerminalDevice _write_number_hex(const Long& x, TerminalDevice& term) { + UInt64 y = (x > 0 ? x : -x) / 16; + UInt64 h = (x > 0 ? x : -x) % 16; + + if (y) _write_number_hex(y, term); + + /* fail if the hex number is not base-16 */ + if (h > 16) { + _write_number_hex('?', term); + return term; + } + + if (y == ~0UL) y = -y; + + const Char kNumbers[17] = "0123456789ABCDEF"; + + Char buf[2]; + buf[0] = kNumbers[h]; + buf[1] = 0; + + term.operator<<(buf); + return term; + } +} // namespace Detail + +inline TerminalDevice hex_number(const Long& x) { + TerminalDevice self = TerminalDevice::The(); + + self << "0x"; + Detail::_write_number_hex(x, self); + + return self; +} + +inline TerminalDevice number(const Long& x) { + TerminalDevice self = TerminalDevice::The(); + + Detail::_write_number(x, self); + + return self; +} + +inline TerminalDevice get_console_in(Char* buf) { + TerminalDevice self = TerminalDevice::The(); + + self >> buf; + + return self; +} + +inline constexpr auto kDebugPort = 51820; +inline constexpr auto kDebugMagic = "VMK1.0.0;"; +inline constexpr auto kDebugVersion = 0x0100; + +inline constexpr SizeT kDebugCmdLen = 256U; + +typedef Char rt_debug_cmd[kDebugCmdLen]; + +inline TerminalDevice& operator<<(TerminalDevice& src, const Long& num) { + src = number(num); + return src; +} +} // namespace Kernel + +#ifdef kout +#undef kout +#endif // ifdef kout + +#define kout TerminalDevice::The() + +#ifdef kendl +#undef kendl +#endif // ifdef kendl + +#define kendl end_line() + +#ifdef kout8 +#undef kout8 +#endif // ifdef kout8 + +#define kout8 Utf8TerminalDevice::The() + +#ifdef kendl8 +#undef kendl8 +#endif // ifdef kendl8 + +#define kendl8 utf_end_line() diff --git a/src/kernel/KernelKit/Defines.h b/src/kernel/KernelKit/Defines.h new file mode 100644 index 00000000..e88441d2 --- /dev/null +++ b/src/kernel/KernelKit/Defines.h @@ -0,0 +1,19 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define KERNELKIT_VERSION "0.0.1-kernelkit" +#define KERNELKIT_VERSION_BCD 0x0001 + +namespace Kernel { +class UserProcessScheduler; +class IDylibObject; +class USER_PROCESS; +class KERNEL_TASK; +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/DeviceMgr.h b/src/kernel/KernelKit/DeviceMgr.h new file mode 100644 index 00000000..1dbad161 --- /dev/null +++ b/src/kernel/KernelKit/DeviceMgr.h @@ -0,0 +1,129 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/* ======================================== + + Revision History: + + 31/01/24: Add kDeviceCnt (amlel) + 15/11/24: Add NE_DEVICE macro, to inherit from device object. + + ======================================== */ + +#pragma once + +/* @note Device Mgr. */ +/* @file KernelKit/DeviceMgr.h */ +/* @brief Device abstraction and I/O buffer. */ + +#include +#include + +#define kDeviceMgrRootDirPath "/devices/" + +#define NE_DEVICE : public ::Kernel::DeviceInterface + +// Last Rev: Wed, May 27, 2025 6:22 PM + +namespace Kernel { +template +class DeviceInterface; + +template +class IOBuf; + +/***********************************************************************************/ +/// @brief Device contract interface, represents an HW device. +/***********************************************************************************/ +template +class DeviceInterface { + public: + DeviceInterface() = default; + + explicit DeviceInterface(void (*Out)(DeviceInterface*, T), void (*In)(DeviceInterface*, T)) + : fOut(Out), fIn(In) {} + + virtual ~DeviceInterface() = default; + + public: + DeviceInterface& operator=(const DeviceInterface&) = default; + DeviceInterface(const DeviceInterface&) = default; + + public: + virtual DeviceInterface& operator<<(T Data) { + fOut(this, Data); + return *this; + } + + virtual DeviceInterface& operator>>(T Data) { + fIn(this, Data); + return *this; + } + + virtual const char* Name() const { return "/devices/null"; } + + operator bool() { return fOut && fIn; } + + Bool operator!() { return !fOut || !fIn; } + + protected: + Void (*fOut)(DeviceInterface*, T Data) = {nullptr}; + Void (*fIn)(DeviceInterface*, T Data) = {nullptr}; +}; + +/// +/// @brief Input Output abstract class. +/// Used mainly to communicate between OS to hardware. +/// +template +class IOBuf final { + public: + explicit IOBuf(T dma_addr) : fData(dma_addr) { + // At least pass something valid when instancating this struct. + MUST_PASS(fData); + } + + IOBuf& operator=(const IOBuf&) = default; + IOBuf(const IOBuf&) = default; + + ~IOBuf() = default; + + public: + template + R operator->() const { + return fData; + } + + template + R& operator[](Size index) const { + return fData[index]; + } + + private: + T fData; +}; + +///! @brief Device enum types. +enum { + kDeviceTypeInvalid = 0, + kDeviceTypeIDE = 100, + kDeviceTypeEthernet, + kDeviceTypeWiFi, + kDeviceTypeFW, + kDeviceTypeBT, + kDeviceTypeRS232, + kDeviceTypeSCSI, + kDeviceTypeAHCI, + kDeviceTypeMBCI, + kDeviceTypeATA, + kDeviceTypeUSB, + kDeviceTypeAPM, // Adv. Pwr. Mgmt. + kDeviceTypePCI, + kDeviceTypeVGA, + kDeviceTypeGPU, + kDeviceTypeCount, +}; +} // namespace Kernel diff --git a/src/kernel/KernelKit/DriveMgr.h b/src/kernel/KernelKit/DriveMgr.h new file mode 100644 index 00000000..daf93b89 --- /dev/null +++ b/src/kernel/KernelKit/DriveMgr.h @@ -0,0 +1,175 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef INC_DRIVE_MANAGER_H +#define INC_DRIVE_MANAGER_H + +/// @file DriveMgr.h +/// @brief Drive Manager. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +#include +#include +#include +#include +#include +#include +#include +#include + +#define kDriveMaxCount (4U) +#define kDriveSectorSz (512U) +#define kDriveInvalidID (-1) +#define kDriveNameLen (32) + +#define drv_sector_cnt(SIZE, SECTOR_SZ) (((SIZE) + (SECTOR_SZ)) / (SECTOR_SZ)) + +#define kDriveHiddenPrefix '~' + +namespace Kernel { +enum { + kInvalidDrive = -1, + + /// Storage types, combine with flags. + kBlockDevice = 0xAD, + kMassStorageDrive = 0xDA, + kFloppyDrive = 0xCD, + kOpticalDrive = 0xDC, // CD-ROM/DVD-ROM/Blu-Ray + kTapeDrive = 0xD7, + + /// Storage flags, combine with types. + kReadOnlyDrive = 0x10, // Read only drive + kEPMDrive = 0x11, // Explicit Partition Map. + kVEPMDrive = 0x12, // ESP w/ EPM partition. + kMBRDrive = 0x13, // PC classic partition scheme + kGPTDrive = 0x14, // PC new partition scheme + kUnformattedDrive = 0x15, + kStorageCount = 9, +}; + +/// @brief Media drive trait type. +struct DriveTrait final { + Char fName[kDriveNameLen] = "/media/null"; // /System, /boot, //./Devices/USB... + UInt32 fKind{}; // fMassStorage, fFloppy, fOpticalDrive. + UInt32 fFlags{}; // fReadOnly, fEPMDrive... + + /// @brief Packet drive (StorageKit compilant.) + struct DrivePacket final { + VoidPtr fPacketContent{nullptr}; //! packet body. + Char fPacketMime[kDriveNameLen] = "*/*"; //! identify what we're sending. + SizeT fPacketSize{0UL}; //! packet size + UInt32 fPacketCRC32{0UL}; //! sanity crc, in case if good is set to false + Boolean fPacketGood{YES}; + Lba fPacketLba{0UL}; + Boolean fPacketReadOnly{NO}; + } fPacket; + + Lba fLbaStart{0}, fLbaEnd{0}; + SizeT fSectorSz{kDriveSectorSz}; + + Void (*fInput)(DrivePacket& packet){nullptr}; + Void (*fOutput)(DrivePacket& packet){nullptr}; + Void (*fVerify)(DrivePacket& packet){nullptr}; + Void (*fInit)(DrivePacket& packet){nullptr}; + const Char* (*fProtocol)(Void){nullptr}; +}; + +namespace Probe { + Void io_detect_drive(DriveTrait& trait); +} + +///! drive as a device. +typedef DriveTrait* DriveTraitPtr; + +/** + * @brief Mounted drives interface. + * @note This class has all of it's drive set to nullptr, allocate them using + * GetAddressOf(index). + */ +class IMountpoint final { + public: + explicit IMountpoint() = default; + ~IMountpoint() = default; + + NE_COPY_DEFAULT(IMountpoint) + + public: + DriveTrait& A() { return mA; } + + DriveTrait& B() { return mB; } + + DriveTrait& C() { return mC; } + + DriveTrait& D() { return mD; } + + enum { + kDriveIndexA = 0, + kDriveIndexB, + kDriveIndexC, + kDriveIndexD, + kDriveIndexInvalid, + }; + + DriveTraitPtr GetAddressOf(const Int32& index) { + err_local_get() = kErrorSuccess; + + switch (index) { + case kDriveIndexA: + return &mA; + case kDriveIndexB: + return &mB; + case kDriveIndexC: + return &mC; + case kDriveIndexD: + return &mD; + default: { + err_local_get() = kErrorNoSuchDisk; + kout << "No such disc letter.\n"; + + break; + } + } + + return nullptr; + } + + private: + DriveTrait mA, mB, mC, mD; +}; + +/// @brief Unimplemented drive. +/// @param pckt the packet to read. +/// @return +Void io_drv_unimplemented(DriveTrait::DrivePacket* pckt) noexcept; + +/// @brief Gets the drive kind (ATA, SCSI, AHCI...) +/// @param void none. +/// @return the drive kind (ATA, Flash, NVM) +const Char* io_drv_kind(Void); + +/// @brief Makes a new drive. +/// @return the new drive as a trait. +DriveTrait io_construct_blank_drive(Void) noexcept; + +/// @brief Fetches the main drive. +/// @param trait the new drive as a trait. +Void io_construct_main_drive(DriveTrait& trait) noexcept; + +/// @brief Fetches the main drive. +/// @return the new drive as a trait. +/// @deprecated use io_construct_main_drive(DriveTrait& trait) instead. +DriveTrait io_construct_main_drive(Void) noexcept; + +namespace Detect { + Void io_detect_drive(DriveTrait& trait); +} + +Void io_drv_input(DriveTrait::DrivePacket pckt); + +Void io_drv_output(DriveTrait::DrivePacket pckt); +} // namespace Kernel + +#endif /* ifndef INC_DRIVE_MANAGER_H */ diff --git a/src/kernel/KernelKit/FileMgr.h b/src/kernel/KernelKit/FileMgr.h new file mode 100644 index 00000000..93d5f580 --- /dev/null +++ b/src/kernel/KernelKit/FileMgr.h @@ -0,0 +1,445 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss , licensed under the Apache 2.0 license. + + File: FileMgr.h + Purpose: Kernel file manager. + Author: Amlal El Mahrouss (amlal@nekernel.org) + +======================================== */ + +/* ======================================== + + Revision History: + + 31/01/24: Update documentation (amlel) + 05/07/24: NeFS support, and fork support, updated constants and specs + as well. + 18/01/25: Patches to FileStream class. + + ======================================== */ + +#ifndef INC_FILEMGR_H +#define INC_FILEMGR_H + +/// @file FileMgr.h +/// @brief File Manager System. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +//! Include filesystems that NeKernel supports. +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/// @brief Filesystem manager, abstraction over mounted filesystem. +/// Works like an VFS (Virtual File System) or IFS subsystem on NT/OS 2. + +#define kRestrictR "r" +#define kRestrictRB "rb" +#define kRestrictW "w" +#define kRestrictWR "rw" +#define kRestrictWRB "rwb" + +#define kRestrictMax (5U) + +#define rtl_node_cast(PTR) reinterpret_cast(PTR) + +#define kFileMimeGeneric "ne-application-kind/all" + +/** @brief invalid position. (n-pos) */ +#define kFileMgrNPos (~0UL) + +namespace Kernel { +enum { + kFileIOInvalid = 0, + kFileWriteAll = 100, + kFileReadAll = 101, + kFileReadChunk = 102, + kFileWriteChunk = 103, + // File flags (HFS+, NeFS specific) + kFileFlagRsrc = 104, + kFileFlagData = 105, + kFileIOCnt = (kFileFlagData - kFileWriteAll) + 1, +}; + +using NodePtr = VoidPtr; + +/** +@brief Filesystem Mgr Interface class +@brief Used to provide common I/O for a specific filesystem. +*/ +class IFilesystemMgr { + public: + explicit IFilesystemMgr() = default; + virtual ~IFilesystemMgr() = default; + + public: + NE_COPY_DEFAULT(IFilesystemMgr) + + public: + /// @brief Mounts a new filesystem into an active state. + /// @param interface the filesystem interface + /// @return + static bool Mount(IFilesystemMgr* interface); + + /// @brief Unmounts the active filesystem + /// @return + static IFilesystemMgr* Unmount(); + + /// @brief Getter, gets the active filesystem. + /// @return + static IFilesystemMgr* GetMounted(); + + public: + virtual NodePtr Create(_Input const Char* path) = 0; + virtual NodePtr CreateAlias(_Input const Char* path) = 0; + virtual NodePtr CreateDirectory(_Input const Char* path) = 0; + virtual NodePtr CreateSwapFile(const Char* path) = 0; + + public: + virtual bool Remove(_Input const Char* path) = 0; + + public: + virtual NodePtr Open(_Input const Char* path, _Input const Char* r) = 0; + + public: + virtual Void Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) = 0; + + virtual _Output VoidPtr Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT sz) = 0; + + virtual Void Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, + _Input Int32 flags, _Input SizeT size) = 0; + + virtual _Output VoidPtr Read(_Input const Char* name, _Input NodePtr node, _Input Int32 flags, + _Input SizeT sz) = 0; + + public: + virtual bool Seek(_Input NodePtr node, _Input SizeT off) = 0; + + public: + virtual SizeT Tell(_Input NodePtr node) = 0; + virtual bool Rewind(_Input NodePtr node) = 0; +}; + +#ifdef __FSKIT_INCLUDES_NEFS__ +/** + * @brief Based of IFilesystemMgr, takes care of managing NeFS + * disks. + */ +class NeFileSystemMgr final : public IFilesystemMgr { + public: + explicit NeFileSystemMgr(); + ~NeFileSystemMgr() override; + + public: + NE_COPY_DEFAULT(NeFileSystemMgr) + + public: + NodePtr Create(const Char* path) override; + NodePtr CreateAlias(const Char* path) override; + NodePtr CreateDirectory(const Char* path) override; + NodePtr CreateSwapFile(const Char* path) override; + + public: + bool Remove(_Input const Char* path) override; + NodePtr Open(_Input const Char* path, _Input const Char* r) override; + Void Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT sz) override; + VoidPtr Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT sz) override; + bool Seek(_Input NodePtr node, _Input SizeT off) override; + SizeT Tell(_Input NodePtr node) override; + bool Rewind(_Input NodePtr node) override; + + Void Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) override; + + _Output VoidPtr Read(_Input const Char* name, _Input NodePtr node, _Input Int32 flags, + _Input SizeT sz) override; + + public: + /// @brief Get NeFS parser class. + /// @return The filesystem parser class. + NeFileSystemParser* GetParser() noexcept; + + private: + NeFileSystemParser* mParser{nullptr}; +}; + +#endif // ifdef __FSKIT_INCLUDES_NEFS__ + +#ifdef __FSKIT_INCLUDES_EXT2__ +/** + * @brief Based of IFilesystemMgr, takes care of managing NeFS + * disks. + */ +class Ext2FileSystemMgr final : public IFilesystemMgr { + public: + explicit Ext2FileSystemMgr(); + ~Ext2FileSystemMgr() override; + + public: + NE_COPY_DEFAULT(Ext2FileSystemMgr) + + public: + NodePtr Create(const Char* path) override; + NodePtr CreateAlias(const Char* path) override; + NodePtr CreateDirectory(const Char* path) override; + NodePtr CreateSwapFile(const Char* path) override; + + public: + bool Remove(_Input const Char* path) override; + NodePtr Open(_Input const Char* path, _Input const Char* r) override; + Void Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT sz) override; + VoidPtr Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT sz) override; + bool Seek(_Input NodePtr node, _Input SizeT off) override; + SizeT Tell(_Input NodePtr node) override; + bool Rewind(_Input NodePtr node) override; + + Void Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) override; + + _Output VoidPtr Read(_Input const Char* name, _Input NodePtr node, _Input Int32 flags, + _Input SizeT sz) override; + + public: + /// @brief Get NeFS parser class. + /// @return The filesystem parser class. + Ext2FileSystemParser* GetParser() noexcept; + + private: + Ext2FileSystemParser* mParser{nullptr}; +}; + +#endif // ifdef __FSKIT_INCLUDES_EXT2__ + +#ifdef __FSKIT_INCLUDES_OPENHEFS__ +/** + * @brief Based of IFilesystemMgr, takes care of managing NeFS + * disks. + */ +class HeFileSystemMgr final : public IFilesystemMgr { + public: + explicit HeFileSystemMgr(); + ~HeFileSystemMgr() override; + + public: + NE_COPY_DEFAULT(HeFileSystemMgr) + + public: + NodePtr Create(const Char* path) override; + NodePtr CreateAlias(const Char* path) override; + NodePtr CreateDirectory(const Char* path) override; + NodePtr CreateSwapFile(const Char* path) override; + + public: + bool Remove(_Input const Char* path) override; + NodePtr Open(_Input const Char* path, _Input const Char* r) override; + Void Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT sz) override; + VoidPtr Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT sz) override; + bool Seek(_Input NodePtr node, _Input SizeT off) override; + SizeT Tell(_Input NodePtr node) override; + bool Rewind(_Input NodePtr node) override; + + Void Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) override; + + _Output VoidPtr Read(_Input const Char* name, _Input NodePtr node, _Input Int32 flags, + _Input SizeT sz) override; + + public: + /// @brief Get NeFS parser class. + /// @return The filesystem parser class. + HeFileSystemParser* GetParser() noexcept; + + private: + HeFileSystemParser* mParser{nullptr}; +}; + +#endif // ifdef __FSKIT_INCLUDES_OPENHEFS__ + +/** + * FileStream class. + * @tparam Encoding file encoding (char, wchar_t...) + * @tparam FSClass Filesystem contract who takes care of it. + */ +template +class FileStream final { + public: + explicit FileStream(const Encoding* path, const Encoding* restrict_type); + ~FileStream(); + + public: + FileStream& operator=(const FileStream&); + FileStream(const FileStream&); + + public: + ErrorOr Write(SizeT offset, const VoidPtr data, SizeT len) noexcept { + if (this->fFileRestrict != kFileMgrRestrictReadWrite && + this->fFileRestrict != kFileMgrRestrictReadWriteBinary && + this->fFileRestrict != kFileMgrRestrictWrite && + this->fFileRestrict != kFileMgrRestrictWriteBinary) + return ErrorOr(kErrorInvalidData); + + if (data == nullptr) return ErrorOr(kErrorInvalidData); + + auto man = FSClass::GetMounted(); + + if (man) { + man->Write(offset, fFile, data, len); + return ErrorOr(kErrorSuccess); + } + + return ErrorOr(kErrorInvalidData); + } + + ErrorOr Write(const Char* name, const VoidPtr data, SizeT len) noexcept { + if (this->fFileRestrict != kFileMgrRestrictReadWrite && + this->fFileRestrict != kFileMgrRestrictReadWriteBinary && + this->fFileRestrict != kFileMgrRestrictWrite && + this->fFileRestrict != kFileMgrRestrictWriteBinary) + return ErrorOr(kErrorInvalidData); + + if (data == nullptr) return ErrorOr(kErrorInvalidData); + + auto man = FSClass::GetMounted(); + + if (man) { + man->Write(name, fFile, data, 0, len); + return ErrorOr(kErrorSuccess); + } + + return ErrorOr(kErrorInvalidData); + } + + VoidPtr Read(const Char* name, SizeT sz) noexcept { + if (this->fFileRestrict != kFileMgrRestrictReadWrite && + this->fFileRestrict != kFileMgrRestrictReadWriteBinary && + this->fFileRestrict != kFileMgrRestrictRead && + this->fFileRestrict != kFileMgrRestrictReadBinary) + return nullptr; + + auto man = FSClass::GetMounted(); + + if (man) { + VoidPtr ret = man->Read(name, fFile, kFileReadAll, sz); + return ret; + } + + return nullptr; + } + + VoidPtr Read(SizeT offset, SizeT sz) { + if (this->fFileRestrict != kFileMgrRestrictReadWrite && + this->fFileRestrict != kFileMgrRestrictReadWriteBinary && + this->fFileRestrict != kFileMgrRestrictRead && + this->fFileRestrict != kFileMgrRestrictReadBinary) + return nullptr; + + auto man = FSClass::GetMounted(); + + if (man) { + man->Seek(fFile, offset); + auto ret = man->Read(fFile, kFileReadChunk, sz); + + return ret; + } + + return nullptr; + } + + public: + /// @brief Leak node pointer. + /// @return The node pointer. + NodePtr Leak() { return fFile; } + + /// @brief Leak MIME. + /// @return The MIME. + Char* MIME() noexcept { return const_cast(fMime); } + + enum { + kFileMgrRestrictRead = 100, + kFileMgrRestrictReadBinary, + kFileMgrRestrictWrite, + kFileMgrRestrictWriteBinary, + kFileMgrRestrictReadWrite, + kFileMgrRestrictReadWriteBinary, + }; + + private: + NodePtr fFile{nullptr}; + Int32 fFileRestrict{kFileMgrRestrictReadBinary}; + const Char* fMime{kFileMimeGeneric}; +}; + +using FileStreamASCII = FileStream; +using FileStreamUTF8 = FileStream; +using FileStreamUTF16 = FileStream; + +typedef UInt64 CursorType; + +inline STATIC const auto kRestrictStrLen = 8U; + +/// @brief restrict information about the file descriptor. +struct FILEMGR_RESTRICT final { + Char fRestrict[kRestrictStrLen]; + Int32 fMappedTo; +}; + +/// @brief constructor +template +inline FileStream::FileStream(const Encoding* path, const Encoding* restrict_type) + : fFile(Class::GetMounted()->Open(path, restrict_type)) { + SizeT kRestrictCount = kRestrictMax; + const FILEMGR_RESTRICT kRestrictList[] = {{ + .fRestrict = kRestrictR, + .fMappedTo = kFileMgrRestrictRead, + }, + { + .fRestrict = kRestrictRB, + .fMappedTo = kFileMgrRestrictReadBinary, + }, + { + .fRestrict = kRestrictWRB, + .fMappedTo = kFileMgrRestrictReadWriteBinary, + }, + { + .fRestrict = kRestrictW, + .fMappedTo = kFileMgrRestrictWrite, + }, + { + .fRestrict = kRestrictWR, + .fMappedTo = kFileMgrRestrictReadWrite, + }}; + + for (SizeT index = 0; index < kRestrictCount; ++index) { + if (rt_string_cmp(restrict_type, kRestrictList[index].fRestrict, + rt_string_len(kRestrictList[index].fRestrict)) == 0) { + fFileRestrict = kRestrictList[index].fMappedTo; + break; + } + } + + kout << "FileMgr: Open file at: " << path << ".\r"; +} + +/// @brief destructor of the file stream. +template +inline FileStream::~FileStream() { + mm_free_ptr(fFile); + fFile = nullptr; +} +} // namespace Kernel + +#endif // ifndef INC_FILEMGR_H diff --git a/src/kernel/KernelKit/HardwareThreadScheduler.h b/src/kernel/KernelKit/HardwareThreadScheduler.h new file mode 100644 index 00000000..36a870ba --- /dev/null +++ b/src/kernel/KernelKit/HardwareThreadScheduler.h @@ -0,0 +1,135 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __INC_MP_MANAGER_H__ +#define __INC_MP_MANAGER_H__ + +#include +#include +#include + +/// @note Last Rev Sun 28 Jul CET 2024 +/// @note Last Rev Thu, Aug 1, 2024 9:07:38 AM + +#define kMaxAPInsideSched (4U) + +namespace Kernel { +class HardwareThread; +class HardwareThreadScheduler; + +using ThreadID = UInt32; + +enum ThreadKind { + kAPInvalid = 0, + kAPSystemReserved = 100, // System reserved thread, well user can't use it + kAPStandard, // user thread, cannot be used by Kernel + kAPRealTime, // fallback thread, cannot be used by user if not clear or + // used by Kernel. + kAPBoot, // The core we booted from, the mama. + kAPCount, +}; + +typedef enum ThreadKind ThreadKind; +typedef ThreadID ThreadID; + +/***********************************************************************************/ +/// +/// \name HardwareThread +/// \brief Abstraction over the CPU's core, used to run processes or threads. +/// +/***********************************************************************************/ + +class HardwareThread final { + public: + explicit HardwareThread(); + ~HardwareThread(); + + public: + NE_COPY_DEFAULT(HardwareThread) + + public: + operator bool(); + + public: + void Wake(const BOOL wakeup = false) noexcept; + void Busy(const BOOL busy = false) noexcept; + + public: + BOOL Switch(HAL::StackFramePtr frame); + BOOL IsWakeup() noexcept; + + public: + HAL::StackFramePtr StackFrame() noexcept; + ThreadKind& Kind() noexcept; + BOOL IsBusy() noexcept; + ThreadID& ID() noexcept; + + private: + HAL::StackFramePtr fStack{nullptr}; + ThreadKind fKind{ThreadKind::kAPStandard}; + ThreadID fID{0}; + Bool fWakeup{NO}; + Bool fBusy{NO}; + UInt64 fPTime{0}; + + private: + friend class HardwareThreadScheduler; + friend class UserProcessHelper; +}; + +/// +/// \name HardwareThreadScheduler +/// \brief Class to manage the thread scheduling. +/// + +class HardwareThreadScheduler final : public ISchedulable { + private: + friend class UserProcessHelper; + + public: + explicit HardwareThreadScheduler(); + ~HardwareThreadScheduler(); + NE_COPY_DEFAULT(HardwareThreadScheduler) + + public: + HAL::StackFramePtr Leak() noexcept; + + public: + Ref operator[](SizeT idx); + bool operator!() noexcept; + operator bool() noexcept; + + Bool IsUser() override { return Yes; } + + Bool IsKernel() override { return No; } + + Bool HasMP() override { return kHandoverHeader->f_HardwareTables.f_MultiProcessingEnabled; } + + public: + /// @brief Shared instance of the MP Mgr. + /// @return the reference to the mp manager class. + STATIC HardwareThreadScheduler& The(); + + public: + /// @brief Returns the amount of threads present in the system. + /// @returns SizeT the amount of cores present. + SizeT Capacity() noexcept; + + private: + Array fThreadList; + ThreadID fCurrentThreadIdx{0}; +}; + +/// @brief wakes up thread. +/// wakes up thread from hang. +Void mp_wakeup_thread(HAL::StackFramePtr stack); + +/// @brief makes thread sleep. +/// hooks and hangs thread to prevent code from executing. +Void mp_hang_thread(HAL::StackFramePtr stack); +} // namespace Kernel + +#endif // !__INC_MP_MANAGER_H__ diff --git a/src/kernel/KernelKit/HeapMgr.h b/src/kernel/KernelKit/HeapMgr.h new file mode 100644 index 00000000..58bac7a5 --- /dev/null +++ b/src/kernel/KernelKit/HeapMgr.h @@ -0,0 +1,58 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef INC_KERNEL_HEAP_H +#define INC_KERNEL_HEAP_H + +/// @date 30/01/24 +/// @file: HeapMgr.h +/// @brief: Memory allocation support for the NeKernel. + +#include +#include +#include + +namespace Kernel { +/// @brief Declare pointer as free. +/// @param heap_ptr the pointer. +/// @return a status code regarding the deallocation. +Int32 mm_free_ptr(VoidPtr heap_ptr); + +/// @brief Check if pointer is a valid Kernel pointer. +/// @param heap_ptr the pointer +/// @return if it exists it returns true. +Boolean mm_is_valid_ptr(VoidPtr heap_ptr); + +/// @brief Allocate chunk of memory. +/// @param sz Size of pointer +/// @param wr Read Write bit. +/// @param user User enable bit. +/// @return The newly allocated pointer, or nullptr. +VoidPtr mm_alloc_ptr(SizeT sz, Bool wr, Bool user, SizeT pad_amount = 0); + +/// @brief Protect the heap with a CRC value. +/// @param heap_ptr pointer. +/// @return if it valid: point has crc now., otherwise fail. +Boolean mm_protect_ptr(VoidPtr heap_ptr); + +/// @brief Makes a Kernel page. +/// @param heap_ptr the page pointer. +/// @return status code +Int32 mm_make_page(VoidPtr heap_ptr); + +/// @brief Overwrites and set the flags of a heap header. +/// @param heap_ptr the pointer to update. +/// @param flags the flags to set. +Int32 mm_set_ptr_flags(VoidPtr heap_ptr, UInt64 flags); + +/// @brief Gets the flags of a heap header. +/// @param heap_ptr the pointer to get. +UInt64 mm_get_ptr_flags(VoidPtr heap_ptr); +} // namespace Kernel + +#include + +#endif // !INC_KERNEL_HEAP_H diff --git a/src/kernel/KernelKit/HeapMgr.inl b/src/kernel/KernelKit/HeapMgr.inl new file mode 100644 index 00000000..3231d33c --- /dev/null +++ b/src/kernel/KernelKit/HeapMgr.inl @@ -0,0 +1,35 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#ifndef INC_KERNEL_HEAP_H +#include +#endif // !INC_KERNEL_HEAP_H + +namespace Kernel { +/// @brief Allocate C++ class. +/// @param cls The class to allocate. +/// @param args The args to pass. +template +inline BOOL mm_new_class(_Input _Output T** cls, _Input Args&&... args) { + if (*cls) { + err_global_get() = Kernel::kErrorInvalidData; + return NO; + } + + *cls = new T(move(args)...); + return *cls; +} + +/// @brief Delete and nullify C++ class. +/// @param cls The class to delete. +template +inline Void mm_delete_class(_Input _Output T** cls) { + delete *cls; + *cls = nullptr; +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/IDylibObject.h b/src/kernel/KernelKit/IDylibObject.h new file mode 100644 index 00000000..ef6b96db --- /dev/null +++ b/src/kernel/KernelKit/IDylibObject.h @@ -0,0 +1,45 @@ +/* + * ======================================================== + * + * Kernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#pragma once + +#include +#include + +#define NE_DYLIB_OBJECT : public IDylibObject + +namespace Kernel { +class IDylibObject; + +/// @brief Dylib class object. A handle to a shared library. +class IDylibObject { + public: + explicit IDylibObject() = default; + virtual ~IDylibObject() = default; + + struct DylibTraits final { + VoidPtr ImageObject{nullptr}; + VoidPtr ImageEntrypointOffset{nullptr}; + + VoidPtr Image() const { return ImageObject; } + Bool IsValid() const { return ImageObject && ImageEntrypointOffset; } + }; + + NE_COPY_DEFAULT(IDylibObject) + + virtual DylibTraits** GetAddressOf() = 0; + virtual DylibTraits* Get() = 0; + + virtual Void Mount(DylibTraits* to_mount) = 0; + virtual Void Unmount() = 0; +}; + +/// @brief Pure implementation, missing method/function handler. +EXTERN_C void __ne_pure_call(void); +} // namespace Kernel diff --git a/src/kernel/KernelKit/IFS.h b/src/kernel/KernelKit/IFS.h new file mode 100644 index 00000000..7118a935 --- /dev/null +++ b/src/kernel/KernelKit/IFS.h @@ -0,0 +1,25 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +/// @brief Read from IFS disk. +/// @param Mnt mounted interface. +/// @param DrvTrait drive info +/// @param DrvIndex drive index. +/// @return +Int32 fs_ifs_read(IMountpoint* Mnt, DriveTrait& DrvTrait, Int32 DrvIndex); + +/// @brief Write to IFS disk. +/// @param Mnt mounted interface. +/// @param DrvTrait drive info +/// @param DrvIndex drive index. +/// @return +Int32 fs_ifs_write(IMountpoint* Mnt, DriveTrait& DrvTrait, Int32 DrvIndex); +} // namespace Kernel diff --git a/src/kernel/KernelKit/ILoader.h b/src/kernel/KernelKit/ILoader.h new file mode 100644 index 00000000..28dd1ed9 --- /dev/null +++ b/src/kernel/KernelKit/ILoader.h @@ -0,0 +1,32 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { +/// @brief This interface is used to make loader contracts (MSCOFF, PEF). +/// @author @Amlal-El-Mahrouss +class ILoader { + public: + explicit ILoader() = default; + virtual ~ILoader() = default; + + NE_COPY_DEFAULT(ILoader) + + public: + virtual _Output ErrorOr GetBlob() = 0; + virtual _Output const Char* AsString() = 0; + virtual _Output const Char* MIME() = 0; + virtual _Output const Char* Path() = 0; + virtual _Output ErrorOr FindStart() = 0; + virtual _Output ErrorOr FindSymbol(_Input const Char* name, _Input Int32 kind) = 0; +}; +} // namespace Kernel diff --git a/src/kernel/KernelKit/IPEFDylibObject.h b/src/kernel/KernelKit/IPEFDylibObject.h new file mode 100644 index 00000000..17ef02d5 --- /dev/null +++ b/src/kernel/KernelKit/IPEFDylibObject.h @@ -0,0 +1,86 @@ +/* + * ======================================================== + * + * Kernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#ifndef __KERNELKIT_PEF_SHARED_OBJECT_H__ +#define __KERNELKIT_PEF_SHARED_OBJECT_H__ + +#include +#include +#include +#include +#include + +namespace Kernel { +/** + * @brief Shared Library class + * Load library from this class + */ +class IPEFDylibObject final NE_DYLIB_OBJECT { + public: + explicit IPEFDylibObject() = default; + ~IPEFDylibObject() = default; + + public: + NE_COPY_DEFAULT(IPEFDylibObject) + + private: + DylibTraits* fMounted{nullptr}; + + public: + DylibTraits** GetAddressOf() { return &fMounted; } + + DylibTraits* Get() { return fMounted; } + + public: + void Mount(DylibTraits* to_mount) noexcept { + if (!to_mount || !to_mount->ImageObject) return; + + fMounted = to_mount; + + if (fLoader && to_mount) { + delete fLoader; + fLoader = nullptr; + } + + if (!fLoader) { + fLoader = new PEFLoader(fMounted->ImageObject); + } + } + + void Unmount() noexcept { + if (fMounted) fMounted = nullptr; + }; + + template + SymbolType Load(const Char* symbol_name, const SizeT& len, const UInt32& kind) { + if (symbol_name == nullptr || *symbol_name == 0) return nullptr; + if (len > kPathLen || len < 1) return nullptr; + + auto ret = reinterpret_cast(fLoader->FindSymbol(symbol_name, kind).Leak().Leak()); + + if (!ret) { + if (kind == kPefCode) return (VoidPtr) &__ne_pure_call; + + return nullptr; + } + + return ret; + } + + private: + PEFLoader* fLoader{nullptr}; +}; + +typedef IPEFDylibObject* IDylibRef; + +EXTERN_C IDylibRef rtl_init_dylib_pef(USER_PROCESS& header); +EXTERN_C Void rtl_fini_dylib_pef(USER_PROCESS& header, IDylibRef lib, Bool* successful); +} // namespace Kernel + +#endif /* ifndef __KERNELKIT_PEF_SHARED_OBJECT_H__ */ diff --git a/src/kernel/KernelKit/KPC.h b/src/kernel/KernelKit/KPC.h new file mode 100644 index 00000000..dbdc2a93 --- /dev/null +++ b/src/kernel/KernelKit/KPC.h @@ -0,0 +1,78 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +/// @file KPC.h +/// @brief Kernel Procedure Code. + +#define err_local_ok() \ + (Kernel::UserProcessScheduler::The().TheCurrentProcess().Leak().GetLocalCode() == \ + Kernel::kErrorSuccess) +#define err_local_fail() \ + (Kernel::UserProcessScheduler::The().TheCurrentProcess().Leak().GetLocalCode() != \ + Kernel::kErrorSuccess) +#define err_local_get() \ + (Kernel::UserProcessScheduler::The().TheCurrentProcess().Leak().GetLocalCode()) + +#define err_global_ok() (Kernel::kErrorLocalNumber == Kernel::kErrorSuccess) +#define err_global_fail() (Kernel::kErrorLocalNumber != Kernel::kErrorSuccess) +#define err_global_get() (Kernel::kErrorLocalNumber) + +namespace Kernel { +typedef Int32 KPCError; + +inline KPCError kErrorLocalNumber = 0UL; + +inline constexpr KPCError kErrorSuccess = 0; +inline constexpr KPCError kErrorExecutable = 33; +inline constexpr KPCError kErrorExecutableLib = 34; +inline constexpr KPCError kErrorFileNotFound = 35; +inline constexpr KPCError kErrorDirectoryNotFound = 36; +inline constexpr KPCError kErrorDiskReadOnly = 37; +inline constexpr KPCError kErrorDiskIsFull = 38; +inline constexpr KPCError kErrorProcessFault = 39; +inline constexpr KPCError kErrorSocketHangUp = 40; +inline constexpr KPCError kErrorThreadLocalStorage = 41; +inline constexpr KPCError kErrorMath = 42; +inline constexpr KPCError kErrorNoNetwork = 43; +inline constexpr KPCError kErrorHeapOutOfMemory = 44; +inline constexpr KPCError kErrorNoSuchDisk = 45; +inline constexpr KPCError kErrorFileExists = 46; +inline constexpr KPCError kErrorFormatFailed = 47; +inline constexpr KPCError kErrorNetworkTimeout = 48; +inline constexpr KPCError kErrorInternal = 49; +inline constexpr KPCError kErrorForkAlreadyExists = 50; +inline constexpr KPCError kErrorOutOfTeamSlot = 51; +inline constexpr KPCError kErrorHeapNotPresent = 52; +inline constexpr KPCError kErrorNoEntrypoint = 53; +inline constexpr KPCError kErrorDiskIsCorrupted = 54; +inline constexpr KPCError kErrorDisk = 55; +inline constexpr KPCError kErrorInvalidData = 56; +inline constexpr KPCError kErrorAsync = 57; +inline constexpr KPCError kErrorNonBlocking = 58; +inline constexpr KPCError kErrorIPC = 59; +inline constexpr KPCError kErrorSign = 60; +inline constexpr KPCError kErrorInvalidCreds = 61; +inline constexpr KPCError kErrorCDTrayBroken = 62; +inline constexpr KPCError kErrorUnrecoverableDisk = 63; +inline constexpr KPCError kErrorFileLocked = 64; +inline constexpr KPCError kErrorDiskIsTooTiny = 65; +inline constexpr KPCError kErrorDmaExhausted = 66; +inline constexpr KPCError kErrorOutOfBitMapMemory = 67; +inline constexpr KPCError kErrorTimeout = 68; +inline constexpr KPCError kErrorAccessDenied = 69; +inline constexpr KPCError kErrorUnavailable = 70; +/// Generic errors. +inline constexpr KPCError kErrorUnimplemented = -1; + +/// @brief Does a system wide bug check. +/// @param void no params are needed. +/// @return if error-free: false, otherwise true. +Boolean err_bug_check_raise(Void) noexcept; +} // namespace Kernel diff --git a/src/kernel/KernelKit/KernelTaskScheduler.h b/src/kernel/KernelKit/KernelTaskScheduler.h new file mode 100644 index 00000000..9bfce1d6 --- /dev/null +++ b/src/kernel/KernelKit/KernelTaskScheduler.h @@ -0,0 +1,47 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +/// @file KernelTaskScheduler.h +/// @brief Kernel Task Scheduler header file. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +#include +#include +#include + +namespace Kernel { +class KernelTaskHelper; + +typedef ProcessID KID; + +/// @brief Equivalent of USER_PROCESS, but for kernel tasks. +/// @author Amlal +class KERNEL_TASK final { + public: + Char Name[kSchedNameLen] = {"KERNEL_TASK"}; + ProcessSubsystem SubSystem{ProcessSubsystem::kProcessSubsystemKernel}; + HAL::StackFramePtr StackFrame{nullptr}; + UInt8* StackReserve{nullptr}; + SizeT StackSize{kSchedMaxStackSz}; + ProcessImage Image{}; + /// @brief a KID is a Kernel ID, it is used to find a task running within + /// the kernel. + KID Kid{0}; +}; + +/// @brief Equivalent of UserProcessHelper, but for kernel tasks. +/// @author Amlal +class KernelTaskHelper final { + public: + STATIC Bool Add(HAL::StackFramePtr frame_ptr, ProcessID new_kid); + STATIC Bool Remove(const KID kid); + STATIC Bool CanBeScheduled(const KERNEL_TASK& process); + STATIC ErrorOr TheCurrentKID(); + STATIC SizeT StartScheduling(); +}; +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/LockDelegate.h b/src/kernel/KernelKit/LockDelegate.h new file mode 100644 index 00000000..8ff67f19 --- /dev/null +++ b/src/kernel/KernelKit/LockDelegate.h @@ -0,0 +1,58 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +enum { + kLockInvalid = 0, + kLockDone = 200, + kLockTimedOut = 300, + kLockCount = 3, +}; + +/// @brief Lock condition pointer. +typedef Boolean* LockPtr; + +/// @brief Locking delegate class, hangs until limit. +/// @tparam N the amount of cycles to wait. +template +class LockDelegate final { + public: + LockDelegate() = delete; + + public: + explicit LockDelegate(LockPtr expr) { + auto spin = 0U; + + while (spin < N) { + if (*expr) { + fLockStatus | kLockDone; + break; + } + + ++spin; + } + + if (spin > N) fLockStatus | kLockTimedOut; + } + + ~LockDelegate() = default; + + LockDelegate& operator=(const LockDelegate&) = delete; + LockDelegate(const LockDelegate&) = delete; + + bool Done() { return fLockStatus[kLockDone] == kLockDone; } + + bool HasTimedOut() { return fLockStatus[kLockTimedOut] != kLockTimedOut; } + + private: + Atom fLockStatus; +}; +} // namespace Kernel diff --git a/src/kernel/KernelKit/MSDOS.h b/src/kernel/KernelKit/MSDOS.h new file mode 100644 index 00000000..8c58b65b --- /dev/null +++ b/src/kernel/KernelKit/MSDOS.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: MSDOS.h + Purpose: MS-DOS header for Kernel. + + Revision History: + + 30/01/24: Added file (amlel) + +======================================== */ + +#ifndef __MSDOS_EXEC__ +#define __MSDOS_EXEC__ + +#include +#include + +// Last Rev +// Sat Feb 24 CET 2024 + +#define kMagMz0 'M' +#define kMagMz1 'Z' + +typedef Kernel::UInt32 DosWord; +typedef Kernel::Long DosLong; + +typedef struct _DosHeader { + Kernel::UInt8 eMagic[2]; + DosWord eMagLen; + DosWord ePagesCount; + DosWord eCrlc; + DosWord eCParHdr; + DosWord eMinAlloc; + DosWord eMaxAlloc; + DosWord eStackSeg; + DosWord eStackPtr; + DosWord eChksum; + DosWord eIp; + DosWord eCs; + DosWord eLfarlc; + DosWord eOvno; + DosWord eRes[4]; + DosWord eOemid; + DosWord eOeminfo; + DosWord eRes2[10]; + DosLong eLfanew; +} DosHeader, *DosHeaderPtr; + +#endif /* ifndef __MSDOS_EXEC__ */ diff --git a/src/kernel/KernelKit/PCI/DMA.h b/src/kernel/KernelKit/PCI/DMA.h new file mode 100644 index 00000000..80103dab --- /dev/null +++ b/src/kernel/KernelKit/PCI/DMA.h @@ -0,0 +1,75 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { +enum class DmaKind { + PCI = 10, // Bus mastering is required to be turned on. Basiaclly a request + // control system. 64-Bit access depends on the PAE bit and the device + // (if Double Address Cycle is available) + ISA, // Four DMA channels 0-3; 8 bit transfers and only a megabyte of RAM. + Count = 2, + Invalid = 0, +}; + +class DMAWrapper final { + public: + explicit DMAWrapper() = delete; + + public: + explicit DMAWrapper(nullPtr) = delete; + explicit DMAWrapper(voidPtr Ptr, DmaKind Kind = DmaKind::PCI) : fAddress(Ptr), fKind(Kind) {} + + public: + DMAWrapper& operator=(voidPtr Ptr); + + public: + DMAWrapper& operator=(const DMAWrapper&) = default; + DMAWrapper(const DMAWrapper&) = default; + + public: + ~DMAWrapper() = default; + + template + T* operator->(); + + template + T* Get(UIntPtr off = 0); + + public: + operator bool(); + bool operator!(); + + public: + bool Write(UIntPtr& bit, const UInt32& offset); + UIntPtr Read(const UInt32& offset); + Boolean Check(UIntPtr offset) const; + + public: + UIntPtr operator[](UIntPtr& offset); + + private: + voidPtr fAddress{nullptr}; + DmaKind fKind{DmaKind::Invalid}; + + private: + friend class DMAFactory; +}; + +class DMAFactory final { + public: + static OwnPtr> Construct(OwnPtr& dma); +}; +} // namespace Kernel + +#include diff --git a/src/kernel/KernelKit/PCI/DMA.inl b/src/kernel/KernelKit/PCI/DMA.inl new file mode 100644 index 00000000..d81a632e --- /dev/null +++ b/src/kernel/KernelKit/PCI/DMA.inl @@ -0,0 +1,17 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +namespace Kernel { +template +T* DMAWrapper::operator->() { + return this->fAddress; +} + +template +T* DMAWrapper::Get(UIntPtr offset) { + return reinterpret_cast((UIntPtr) this->fAddress + offset); +} +} // namespace Kernel diff --git a/src/kernel/KernelKit/PCI/Database.h b/src/kernel/KernelKit/PCI/Database.h new file mode 100644 index 00000000..463fde38 --- /dev/null +++ b/src/kernel/KernelKit/PCI/Database.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ +#pragma once + +#include +#include + +namespace Kernel { +namespace Types { + // https://wiki.osdev.org/PCI + enum class PciDeviceKind : UChar { + MassStorageController = 0x1, + NetworkController = 0x2, + DisplayController = 0x3, + MultimediaController = 0x4, + MemoryController = 0x5, + Bridge = 0x6, + CommunicationController = 0x7, + GenericSystemPeripheral = 0x8, + InputDeviceController = 0x9, + DockingStation = 0xa, + Processor = 0xb, + SerialBusController = 0xc, + WirelessController = 0xd, + IntelligentController = 0xe, + SatelliteCommunicationsController = 0xf, + CoProcessor = 0x40, + Unassgined = 0xf, + Invalid = Unassgined, + }; +} // namespace Types +} // namespace Kernel + +inline BOOL operator!=(const Kernel::Types::PciDeviceKind& lhs, Kernel::UChar rhs) { + return rhs != (Kernel::UChar) lhs; +} + +inline BOOL operator==(const Kernel::Types::PciDeviceKind& lhs, Kernel::UChar rhs) { + return rhs == (Kernel::UChar) lhs; +} + +inline BOOL operator!=(Kernel::UChar lhs, const Kernel::Types::PciDeviceKind& rhs) { + return lhs != (Kernel::UChar) rhs; +} + +inline BOOL operator==(Kernel::UChar lhs, const Kernel::Types::PciDeviceKind& rhs) { + return lhs == (Kernel::UChar) rhs; +} \ No newline at end of file diff --git a/src/kernel/KernelKit/PCI/Device.h b/src/kernel/KernelKit/PCI/Device.h new file mode 100644 index 00000000..f2111e40 --- /dev/null +++ b/src/kernel/KernelKit/PCI/Device.h @@ -0,0 +1,73 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ +#pragma once + +#include + +namespace Kernel::PCI { +enum class PciConfigKind : UShort { + ConfigAddress = 0xCF8, + ConfigData = 0xCFC, + CommandReg = 0x0004, + Invalid = 0xFFFF, +}; + +/// @brief Device interface class +class Device final { + public: + Device() = default; + + public: + Device(UShort bus, UShort device, UShort function, UInt32 bar); + + Device& operator=(const Device&) = default; + Device(const Device&) = default; + + ~Device(); + + public: + UInt Read(UInt bar, Size szData); + void Write(UInt bar, UIntPtr data, Size szData); + + public: + operator bool(); + + public: + template + UInt Read(UInt bar) { + static_assert(sizeof(T) <= sizeof(UInt32), "64-bit PCI addressing is unsupported"); + return Read(bar, sizeof(T)); + } + + template + void Write(UInt bar, UIntPtr data) { + static_assert(sizeof(T) <= sizeof(UInt32), "64-bit PCI addressing is unsupported"); + Write(bar, data, sizeof(T)); + } + + public: + UShort DeviceId(); + UShort VendorId(); + UShort InterfaceId(); + UChar Class(); + UChar Subclass(); + UChar ProgIf(); + UChar HeaderType(); + UIntPtr Bar(UInt32 bar_in); + + public: + void EnableMmio(); + void BecomeBusMaster(); // for PCI-DMA, PC-DMA does not need that. + + UShort Vendor(); + + private: + UShort fBus; + UShort fDevice; + UShort fFunction; + UInt32 fBar; +}; +} // namespace Kernel::PCI diff --git a/src/kernel/KernelKit/PCI/Express.h b/src/kernel/KernelKit/PCI/Express.h new file mode 100644 index 00000000..484739ec --- /dev/null +++ b/src/kernel/KernelKit/PCI/Express.h @@ -0,0 +1,12 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +#define PCI_EXPRESS_BUS_COUNT (4096) diff --git a/src/kernel/KernelKit/PCI/IO.h b/src/kernel/KernelKit/PCI/IO.h new file mode 100644 index 00000000..2ab72269 --- /dev/null +++ b/src/kernel/KernelKit/PCI/IO.h @@ -0,0 +1,63 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { +template +class IOArray final { + public: + IOArray() = delete; + + IOArray(nullPtr) = delete; + + explicit IOArray(Array& ports) : fPorts(ports) {} + + ~IOArray() {} + + IOArray& operator=(const IOArray&) = default; + + IOArray(const IOArray&) = default; + + operator bool() { return !fPorts.Empty(); } + + public: + template + T In(SizeT index); + + template + void Out(SizeT index, T value); + + private: + Array fPorts; +}; + +inline constexpr UInt16 kMaxPorts = 16; + +using IOArray16 = IOArray; + +template +inline Array make_ports(UShort base) { + Array ports; + + for (UShort i = 0; i < Sz; ++i) { + ports[i] = base + i; + } + + return ports; +} +} // namespace Kernel + +#ifdef __NE_AMD64__ +#include +#else +#error Please provide platform specific code for the I/O +#endif // ifdef __NE_AMD64__ diff --git a/src/kernel/KernelKit/PCI/IOArray+AMD64.inl b/src/kernel/KernelKit/PCI/IOArray+AMD64.inl new file mode 100644 index 00000000..2b9125e0 --- /dev/null +++ b/src/kernel/KernelKit/PCI/IOArray+AMD64.inl @@ -0,0 +1,49 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: IO-Impl-AMD64.h + Purpose: I/O for AMD64. + + Revision History: + + 30/01/24: Add file. (amlel) + 02/02/24: Update I/O routines. (amlel) + +======================================== */ + +namespace Kernel { +template +template +T IOArray::In(SizeT index) { + switch (sizeof(T)) { +#ifdef __NE_AMD64__ + case 4: + return HAL::rt_in32(fPorts[index].Leak()); + case 2: + return HAL::rt_in16(fPorts[index].Leak()); + case 1: + return HAL::rt_in8(fPorts[index].Leak()); +#endif + default: + return 0xFFFF; + } +} + +template +template +void IOArray::Out(SizeT index, T value) { + switch (sizeof(T)) { +#ifdef __NE_AMD64__ + case 4: + HAL::rt_out32(fPorts[index].Leak(), value); + case 2: + HAL::rt_out16(fPorts[index].Leak(), value); + case 1: + HAL::rt_out8(fPorts[index].Leak(), value); +#endif + default: + break; + } +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/PCI/Iterator.h b/src/kernel/KernelKit/PCI/Iterator.h new file mode 100644 index 00000000..5926049b --- /dev/null +++ b/src/kernel/KernelKit/PCI/Iterator.h @@ -0,0 +1,41 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __PCI_ITERATOR_H__ +#define __PCI_ITERATOR_H__ + +#include +#include +#include +#include +#include + +#define NE_BUS_COUNT (256) +#define NE_DEVICE_COUNT (33) +#define NE_FUNCTION_COUNT (8) + +namespace Kernel::PCI { +class Iterator final { + public: + Iterator() = delete; + + public: + explicit Iterator(const Types::PciDeviceKind deviceType, UInt32 bar); + + Iterator& operator=(const Iterator&) = default; + Iterator(const Iterator&) = default; + + ~Iterator(); + + public: + Ref operator[](const Size& sz); + + private: + Array fDevices; +}; +} // namespace Kernel::PCI + +#endif // __PCI_ITERATOR_H__ diff --git a/src/kernel/KernelKit/PCI/PCI.h b/src/kernel/KernelKit/PCI/PCI.h new file mode 100644 index 00000000..f76270da --- /dev/null +++ b/src/kernel/KernelKit/PCI/PCI.h @@ -0,0 +1,54 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define kPCIConfigAddressPort (0xCF8) +#define kPCIConfigDataPort (0xCFC) + +#define kPCIDeviceCount (32) +#define kPCIFuncCount (8) +#define kPCIBusCount (256U) + +namespace Kernel::PCI { +// model +struct DeviceHeader { + UInt16 VendorId; + UInt16 DeviceId; + UInt8 Command; + UInt8 Status; + UInt8 RevisionId; + UInt8 ProgIf; + UInt8 SubClass; + UInt8 Class; + UInt8 CacheLineSz; + UInt8 LatencyTimer; + UInt8 HeaderType; + UInt8 Bist; + UInt8 Bus; + UInt8 Device; + UInt8 Function; +}; + +namespace Detail { + class BAR { + public: + UIntPtr BAR; + SizeT Size; + }; +} // namespace Detail + +class BAR { + public: + Detail::BAR BAR1; + Detail::BAR BAR2; + Detail::BAR BAR3; + Detail::BAR BAR4; + Detail::BAR BAR5; +}; +} // namespace Kernel::PCI diff --git a/src/kernel/KernelKit/PE.h b/src/kernel/KernelKit/PE.h new file mode 100644 index 00000000..b4b4576e --- /dev/null +++ b/src/kernel/KernelKit/PE.h @@ -0,0 +1,130 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: PE.h + Purpose: Portable Executable for Kernel. + + Revision History: + + 30/01/24: Added file (amlel) + +======================================== */ + +#ifndef __KERNELKIT_INC_PE_H__ +#define __KERNELKIT_INC_PE_H__ + +#include + +#define kPeSignature (0x00004550) + +#define kPeMagic32 (0x010b) +#define kPeMagic64 (0x020b) + +#define kPeMachineAMD64 (0x8664) +#define kPeMachineARM64 (0xaa64) + +typedef struct LDR_EXEC_HEADER final { + Kernel::UInt32 Signature; + Kernel::UInt16 Machine; + Kernel::UInt16 NumberOfSections; + Kernel::UInt32 TimeDateStamp; + Kernel::UInt32 PointerToSymbolTable; + Kernel::UInt32 NumberOfSymbols; + Kernel::UInt16 SizeOfOptionalHeader; + Kernel::UInt16 Characteristics; +} LDR_EXEC_HEADER, *LDR_EXEC_HEADER_PTR; + +typedef struct LDR_OPTIONAL_HEADER final { + Kernel::UInt16 Magic; // 0x010b - PE32, 0x020b - PE32+ (64 bit) + Kernel::UInt8 MajorLinkerVersion; + Kernel::UInt8 MinorLinkerVersion; + Kernel::UInt32 SizeOfCode; + Kernel::UInt32 SizeOfInitializedData; + Kernel::UInt32 SizeOfUninitializedData; + Kernel::UInt32 AddressOfEntryPoint; + Kernel::UInt32 BaseOfCode; + Kernel::UInt64 ImageBase; + Kernel::UInt32 SectionAlignment; + Kernel::UInt32 FileAlignment; + Kernel::UInt16 MajorOperatingSystemVersion; + Kernel::UInt16 MinorOperatingSystemVersion; + Kernel::UInt16 MajorImageVersion; + Kernel::UInt16 MinorImageVersion; + Kernel::UInt16 MajorSubsystemVersion; + Kernel::UInt16 MinorSubsystemVersion; + Kernel::UInt32 Win32VersionValue; + Kernel::UInt32 SizeOfImage; + Kernel::UInt32 SizeOfHeaders; + Kernel::UInt32 CheckSum; + Kernel::UInt16 Subsystem; + Kernel::UInt16 DllCharacteristics; + Kernel::UInt32 SizeOfStackReserve; + Kernel::UInt32 SizeOfStackCommit; + Kernel::UInt32 SizeOfHeapReserve; + Kernel::UInt32 SizeOfHeapCommit; + Kernel::UInt32 LoaderFlags; + Kernel::UInt32 NumberOfRvaAndSizes; +} LDR_OPTIONAL_HEADER, *LDR_OPTIONAL_HEADER_PTR; + +typedef struct LDR_SECTION_HEADER final { + Kernel::Char Name[8]; + Kernel::UInt32 VirtualSize; + Kernel::UInt32 VirtualAddress; + Kernel::UInt32 SizeOfRawData; + Kernel::UInt32 PointerToRawData; + Kernel::UInt32 PointerToRelocations; + Kernel::UInt32 PointerToLineNumbers; + Kernel::UInt16 NumberOfRelocations; + Kernel::UInt16 NumberOfLinenumbers; + Kernel::UInt32 Characteristics; +} LDR_SECTION_HEADER, *LDR_SECTION_HEADER_PTR; + +enum kExecDataDirParams { + kExecExport, + kExecImport, + kExecInvalid, + kExecCount, +}; + +typedef struct LDR_EXPORT_DIRECTORY { + Kernel::UInt32 Characteristics; + Kernel::UInt32 TimeDateStamp; + Kernel::UInt16 MajorVersion; + Kernel::UInt16 MinorVersion; + Kernel::UInt32 Name; + Kernel::UInt32 Base; + Kernel::UInt32 NumberOfFunctions; + Kernel::UInt32 NumberOfNames; + Kernel::UInt32 AddressOfFunctions; // export table rva + Kernel::UInt32 AddressOfNames; + Kernel::UInt32 AddressOfNameOrdinal; // ordinal table rva +} LDR_EXPORT_DIRECTORY, *LDR_EXPORT_DIRECTORY_PTR; + +typedef struct LDR_IMPORT_DIRECTORY { + union { + Kernel::UInt32 Characteristics; + Kernel::UInt32 OriginalFirstThunk; + }; + Kernel::UInt32 TimeDateStamp; + Kernel::UInt32 ForwarderChain; + Kernel::UInt32 NameRva; + Kernel::UInt32 ThunkTableRva; +} LDR_IMPORT_DIRECTORY, *LDR_IMPORT_DIRECTORY_PTR; + +typedef struct LDR_DATA_DIRECTORY { + Kernel::UInt32 VirtualAddress; + Kernel::UInt32 Size; +} LDR_DATA_DIRECTORY, *LDR_DATA_DIRECTORY_PTR; + +typedef struct LDR_IMAGE_HEADER { + LDR_EXEC_HEADER Header; + LDR_OPTIONAL_HEADER OptHdr; +} LDR_IMAGE_HEADER, *LDR_IMAGE_HEADER_PTR; + +enum { + kUserSection = 0x00000020, + kPEResourceId = 0xFFaadd00, +}; + +#endif /* ifndef __KERNELKIT_INC_PE_H__ */ diff --git a/src/kernel/KernelKit/PE32CodeMgr.h b/src/kernel/KernelKit/PE32CodeMgr.h new file mode 100644 index 00000000..52bc22b4 --- /dev/null +++ b/src/kernel/KernelKit/PE32CodeMgr.h @@ -0,0 +1,91 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: PE32CodeMgr.h + Purpose: PE32+ Code Mgr and Dylib mgr. + + Revision History: + + 12/02/24: Added file (amlel) + +======================================== */ + +#pragma once + +//////////////////////////////////////////////////// + +// LAST REV: Mon Feb 12 13:52:01 CET 2024 + +//////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#ifndef INC_PROCESS_SCHEDULER_H +#include +#endif + +#define kPeStackSizeSymbol "__NESizeOfReserveStack" +#define kPeHeapSizeSymbol "__NESizeOfReserveHeap" +#define kPeNameSymbol "__NEProgramName" + +#define kPeApplicationMime "application/vnd-portable-executable" + +#define kPeImageStart "__ImageStart" + +namespace Kernel { +/// +/// \name PE32Loader +/// \brief PE32+ loader class. +/// +class PE32Loader : public ILoader { + private: + explicit PE32Loader() = delete; + + public: + explicit PE32Loader(const VoidPtr blob); + explicit PE32Loader(const Char* path); + ~PE32Loader() override; + + public: + NE_COPY_DEFAULT(PE32Loader) + + public: + const Char* Path() override; + const Char* AsString() override; + const Char* MIME() override; + + public: + ErrorOr FindStart() override; + ErrorOr FindSectionByName(const Char* name); + ErrorOr FindSymbol(const Char* name, Int32 kind) override; + ErrorOr GetBlob() override; + + public: + bool IsLoaded() noexcept; + + private: +#ifdef __FSKIT_INCLUDES_NEFS__ + OwnPtr> fFile; +#elif defined(__FSKIT_INCLUDES_OPENHEFS__) + OwnPtr> fFile; +#else + OwnPtr> fFile; +#endif // __FSKIT_INCLUDES_NEFS__ + + Ref fPath; + VoidPtr fCachedBlob; + BOOL fBad; +}; + +enum { kPEPlatformInvalid, kPEPlatformAMD64 = 100, kPEPlatformARM64 }; +enum { kPETypeInvalid, kPETypeText = 100, kPETypeData, kPETypeBSS }; + +typedef LDR_SECTION_HEADER PE_SECTION_INFO; + +ProcessID rtl_create_user_process(PE32Loader& exec, const Int32& process_kind) noexcept; +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/PEF.h b/src/kernel/KernelKit/PEF.h new file mode 100644 index 00000000..94284c98 --- /dev/null +++ b/src/kernel/KernelKit/PEF.h @@ -0,0 +1,117 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: PEF.h + Purpose: Preferred Executable Format for Kernel. + + Revision History: + + ?/?/23: Added file (amlel) + +======================================== */ + +#ifndef __KERNELKIT_PEF_H__ +#define __KERNELKIT_PEF_H__ + +#include +#include +#include + +#define kPefMagic "Open" +#define kPefMagicFat "nepO" + +#define kPefMagicLen (5) + +#define kPefVersion (0x0500) +#define kPefNameLen (256U) + +/* not mandatory, only for non fork based filesystems. */ +#define kPefExt ".exec" +#define kPefDylibExt ".dylib" +#define kPefLibExt ".lib" +#define kPefObjectExt ".obj" +#define kPefDebugExt ".dbg" +#define kPefDriverExt ".sys" + +// Kernel System Binary Interface. +#define kPefAbi (0x5046) + +#define kPefBaseOrigin (0x40000000) + +#define kPefStart "__ImageStart" +#define kPefMainSymbol "_NeMain" + +#define kPefForkKind kPefMagic +#define kPefForkKindFAT kPefMagicFat + +namespace Kernel { +enum { + kPefArchIntel86S, + kPefArchAMD64, + kPefArchRISCV, + kPefArch64x0, /* 64x0. ISA */ + kPefArch32x0, /* 32x0. ISA */ + kPefArchPowerPC, + kPefArchARM64, + kPefArchCount = (kPefArchARM64 - kPefArchIntel86S) + 1, + kPefArchInvalid = 0xFF, +}; + +enum { + kPefSubArchGeneric = 0, + kPefSubArchAMD = 200, + kPefSubArchIntel, + kPefSubArchARM, + kPefSubArchIBM, +}; + +enum { + kPefKindInvalid = 0, + kPefKindExec = 1, /* .exec */ + kPefKindDylib = 2, /* .dylib */ + kPefKindObject = 4, /* .obj */ + kPefKindDebug = 5, /* .dbg */ + kPefKindDriver = 6, + kPefKindCount, +}; + +typedef struct PEFContainer final { + Char Magic[kPefMagicLen]; + UInt32 Linker; + UInt32 Version; + UInt32 Kind; + UInt32 Abi; + UInt32 Cpu; + UInt32 SubCpu; /* Cpu specific information */ + UIntPtr Start; + SizeT HdrSz; /* Size of header */ + SizeT Count; /* container header count */ + UInt32 Checksum; +} PACKED PEFContainer; + +/* First PEFCommandHeader starts after PEFContainer */ + +typedef struct PEFCommandHeader final { + Char Name[kPefNameLen]; /* container name */ + UInt32 Cpu; /* container cpu */ + UInt32 SubCpu; /* container sub-cpu */ + UInt32 Flags; /* container flags */ + UInt16 Kind; /* container kind */ + UIntPtr Offset; /* content offset */ + SizeT OffsetSize; /* offset size (physical size inside the file) */ + UIntPtr VMAddress; /* Virtual Address */ + SizeT VMSize; /* Virtual Size */ +} PACKED PEFCommandHeader; + +enum { + kPefInvalid = 0x0, + kPefCode = 0xC, + kPefData = 0xD, + kPefZero = 0xE, + kPefLinkerID = 0x1, + kPefCount = 4, +}; +} // namespace Kernel + +#endif /* ifndef __KERNELKIT_PEF_H__ */ diff --git a/src/kernel/KernelKit/PEFCodeMgr.h b/src/kernel/KernelKit/PEFCodeMgr.h new file mode 100644 index 00000000..41b135e0 --- /dev/null +++ b/src/kernel/KernelKit/PEFCodeMgr.h @@ -0,0 +1,75 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef _INC_CODE_MANAGER_PEF_H_ +#define _INC_CODE_MANAGER_PEF_H_ + +/// @file PEFCodeMgr.h +/// @brief PEF Code Manager header file. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +#include +#include +#include +#include + +#ifndef INC_PROCESS_SCHEDULER_H +#include +#endif + +#define kPefApplicationMime "application/vnd-nekernel-executable" + +namespace Kernel { +/// +/// \name PEFLoader +/// \brief PEF loader class. +/// +class PEFLoader : public ILoader { + private: + explicit PEFLoader() = delete; + + public: + explicit PEFLoader(const VoidPtr blob); + explicit PEFLoader(const Char* path); + ~PEFLoader() override; + + public: + NE_COPY_DEFAULT(PEFLoader) + + public: + const Char* Path() override; + const Char* AsString() override; + const Char* MIME() override; + + public: + ErrorOr FindStart() override; + ErrorOr FindSymbol(const Char* name, Int32 kind) override; + ErrorOr GetBlob() override; + + public: + bool IsLoaded() noexcept; + + private: +#ifdef __FSKIT_INCLUDES_NEFS__ + OwnPtr> fFile; +#elif defined(__FSKIT_INCLUDES_OPENHEFS__) + OwnPtr> fFile; +#else + OwnPtr> fFile; +#endif // __FSKIT_INCLUDES_NEFS__ + + Ref fPath; + VoidPtr fCachedBlob; + BOOL fFatBinary{}; + BOOL fBad{}; +}; + +namespace Utils { + ProcessID rtl_create_user_process(PEFLoader& exec, const Int32& procKind) noexcept; +} // namespace Utils +} // namespace Kernel + +#endif // ifndef _INC_CODE_MANAGER_PEF_H_ diff --git a/src/kernel/KernelKit/ProcessScheduler.h b/src/kernel/KernelKit/ProcessScheduler.h new file mode 100644 index 00000000..7414e4fe --- /dev/null +++ b/src/kernel/KernelKit/ProcessScheduler.h @@ -0,0 +1,18 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +#ifdef __NEOSKRNL__ +namespace Kernel { +inline UserProcessTeam kLowUserTeam; +inline UserProcessTeam kHighUserTeam; +inline UserProcessTeam kMidUserTeam; +} // namespace Kernel +#endif \ No newline at end of file diff --git a/src/kernel/KernelKit/Semaphore.h b/src/kernel/KernelKit/Semaphore.h new file mode 100644 index 00000000..831774a5 --- /dev/null +++ b/src/kernel/KernelKit/Semaphore.h @@ -0,0 +1,110 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +/// @author Amlal El Mahrouss +/// @file Semaphore.h +/// @brief Semaphore structure and functions for synchronization in the kernel. + +#include +#include +#include + +#define kSemaphoreOwnerIndex (0U) +#define kSemaphoreCountIndex (1U) + +#define kSemaphoreCount (2U) + +#define kSemaphoreIncrementOwner(sem) (sem[kSemaphoreOwnerIndex]++) +#define kSemaphoreDecrementOwner(sem) (sem[kSemaphoreOwnerIndex]--) + +namespace Kernel { +/// @brief Semaphore structure used for synchronization. +typedef UInt64 SemaphoreArr[kSemaphoreCount]; + +/// @brief Checks if the semaphore is valid. +inline BOOL rtl_sem_is_valid(const SemaphoreArr& sem, UInt64 owner = 0) { + return sem[kSemaphoreOwnerIndex] == owner && sem[kSemaphoreCountIndex] > 0; +} + +/// @brief Releases the semaphore, resetting its owner and count. +/// @param sem +/// @return +inline BOOL rtl_sem_release(SemaphoreArr& sem) { + sem[kSemaphoreOwnerIndex] = 0; + sem[kSemaphoreCountIndex] = 0; + + return TRUE; +} + +/// @brief Initializes the semaphore with an owner and a count of zero. +/// @param sem the semaphore array to use. +/// @param owner the owner to set, could be anything identifitable. +/// @return +inline BOOL rtl_sem_acquire(SemaphoreArr& sem, UInt64 owner) { + if (!owner) { + err_global_get() = kErrorInvalidData; + return FALSE; // Invalid owner + } + + sem[kSemaphoreOwnerIndex] = owner; + sem[kSemaphoreCountIndex] = 0; + + return TRUE; +} + +/// @brief Waits for the semaphore to be available, blocking until it is. +/// @param sem +/// @param timeout +/// @param condition condition pointer. +/// @return +inline BOOL rtl_sem_wait(SemaphoreArr& sem, UInt64 owner, UInt64 timeout, + BOOL* condition = nullptr) { + if (!rtl_sem_is_valid(sem, owner)) { + return FALSE; + } + + if (timeout <= 0) { + err_global_get() = kErrorTimeout; + + return FALSE; + } + + if (!condition || *condition) { + if (sem[kSemaphoreCountIndex] == 0) { + err_global_get() = kErrorUnavailable; + return FALSE; + } + + err_global_get() = kErrorSuccess; + sem[kSemaphoreCountIndex]--; + + return TRUE; + } + + HardwareTimer timer(timeout); + BOOL ret = timer.Wait(); + + if (ret) { + if (!condition || *condition) { + if (sem[kSemaphoreCountIndex] == 0) { + err_global_get() = kErrorUnavailable; + return FALSE; + } + + err_global_get() = kErrorSuccess; + sem[kSemaphoreCountIndex]--; + + return TRUE; + } + } + + err_global_get() = kErrorTimeout; + + return FALSE; // Failed to acquire semaphore +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/ThreadLocalStorage.h b/src/kernel/KernelKit/ThreadLocalStorage.h new file mode 100644 index 00000000..205d6df9 --- /dev/null +++ b/src/kernel/KernelKit/ThreadLocalStorage.h @@ -0,0 +1,68 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef KERNELKIT_TLS_H +#define KERNELKIT_TLS_H + +#include +#include + +///! @brief Thread Local Storage for NeKernel. + +#define kCookieMag0Idx (0U) +#define kCookieMag1Idx (1U) +#define kCookieMag2Idx (2U) + +#define kCookieMag0 'N' +#define kCookieMag1 'K' +#define kCookieMag2 'O' + +#define kCookieMagLen (3U) + +struct THREAD_INFORMATION_BLOCK; + +/// @brief Thread Information Block. +/// Located in GS on AMD64, other architectures have their own stuff. (64x0, 32x0, ARM64) +struct PACKED THREAD_INFORMATION_BLOCK final { + Kernel::Char Cookie[kCookieMagLen]{0}; //! Thread Magic Number. + Kernel::VoidPtr UserData{nullptr}; //! Thread Information Record (User defined canary structure) +}; + +///! @brief Cookie Sanity check. +Kernel::Boolean tls_check_tib(THREAD_INFORMATION_BLOCK* the_tib); + +///! @brief new ptr syscall. +template +T* tls_new_ptr(void) noexcept; + +///! @brief delete ptr syscall. +template +Kernel::Boolean tls_delete_ptr(T* ptr) noexcept; + +//! @brief Delete process pointer. +//! @param obj The pointer to delete. +template +inline Kernel::Bool tls_delete_ptr(Kernel::ErrorOr obj) noexcept; + +//! @brief Delete process pointer. +//! @param obj The pointer to delete. +template +inline Kernel::Bool tls_delete_ptr(Kernel::ErrorOr obj) noexcept; + +template +T* tls_new_class(Args&&... args); + +/// @brief TLS install TIB and PIB. (syscall) +EXTERN_C Kernel::Void rt_install_tib(THREAD_INFORMATION_BLOCK* TIB, THREAD_INFORMATION_BLOCK* PIB); + +/// @brief TLS check (syscall) +EXTERN_C Kernel::Bool tls_check_syscall_impl(Kernel::VoidPtr TIB) noexcept; + +#include + +// last rev 7/7/24 + +#endif /* ifndef KERNELKIT_TLS_H */ diff --git a/src/kernel/KernelKit/ThreadLocalStorage.inl b/src/kernel/KernelKit/ThreadLocalStorage.inl new file mode 100644 index 00000000..66a3d282 --- /dev/null +++ b/src/kernel/KernelKit/ThreadLocalStorage.inl @@ -0,0 +1,89 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +//! @file ThreadLocalStorage.inl +//! @brief Allocate resources from the process's heap storage. + +#ifndef INC_PROCESS_SCHEDULER_H +#include +#endif + +template +inline T* tls_new_ptr(void) noexcept { + using namespace Kernel; + + auto ref_process = UserProcessScheduler::The().TheCurrentProcess(); + MUST_PASS(ref_process); + + auto pointer = ref_process.Leak().New(sizeof(T)); + + if (pointer.Error()) return nullptr; + + return reinterpret_cast(pointer.Leak().Leak()); +} + +//! @brief Delete process pointer. +//! @param obj The pointer to delete. +template +inline Kernel::Bool tls_delete_ptr(T* obj) noexcept { + using namespace Kernel; + + if (!obj) return No; + + auto ref_process = UserProcessScheduler::The().TheCurrentProcess(); + MUST_PASS(ref_process); + + ErrorOr obj_wrapped{obj}; + + return ref_process.Leak().Delete(obj_wrapped); +} + +//! @brief Delete process pointer. +//! @param obj The pointer to delete. +template +inline Kernel::Bool tls_delete_ptr(Kernel::ErrorOr obj) noexcept { + return tls_delete_ptr(obj.Leak()); +} + +//! @brief Delete process pointer. +//! @param obj The pointer to delete. +template +inline Kernel::Bool tls_delete_ptr(Kernel::ErrorOr obj) noexcept { + return tls_delete_ptr(obj->Leak()); +} + +/// @brief Allocate a C++ class, and then call the constructor of it. +/// @tparam T class type. +/// @tparam ...Args varg class type. +/// @param args arguments list. +/// @return Class instance. +template +T* tls_new_class(Args&&... args) { + using namespace Kernel; + + T* obj = tls_new_ptr(); + + if (obj) { + *obj = T(forward(args)...); + return obj; + } + + return nullptr; +} + +/// @brief Delete a C++ class (call constructor first.) +/// @tparam T +/// @param obj +/// @return +template +inline Kernel::Bool tls_delete_class(T* obj) { + using namespace Kernel; + + if (!obj) return No; + + obj->~T(); + return tls_delete_ptr(obj); +} diff --git a/src/kernel/KernelKit/Timer.h b/src/kernel/KernelKit/Timer.h new file mode 100644 index 00000000..46db5671 --- /dev/null +++ b/src/kernel/KernelKit/Timer.h @@ -0,0 +1,75 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +class SoftwareTimer; +class TimerInterface; + +inline constexpr Int16 kTimeUnit = 1000; + +class TimerInterface { + public: + /// @brief Default constructor + explicit TimerInterface() = default; + virtual ~TimerInterface() = default; + + public: + NE_COPY_DEFAULT(TimerInterface) + + public: + virtual BOOL Wait() noexcept; +}; + +class SoftwareTimer final : public TimerInterface { + public: + explicit SoftwareTimer(Int64 seconds); + ~SoftwareTimer() override; + + public: + NE_COPY_DEFAULT(SoftwareTimer) + + public: + BOOL Wait() noexcept override; + + private: + UIntPtr* fDigitalTimer{nullptr}; + Int64 fWaitFor{0}; +}; + +class HardwareTimer final : public TimerInterface { + public: + explicit HardwareTimer(UInt64 seconds); + ~HardwareTimer() override; + + public: + NE_COPY_DEFAULT(HardwareTimer) + + public: + BOOL Wait() noexcept override; + + private: + volatile UInt8* fDigitalTimer{nullptr}; + Int64 fWaitFor{0}; +}; + +inline UInt64 rtl_microseconds(UInt64 time) { + if (time < 1) return 0; + + // TODO: nanoseconds maybe? + return time / kTimeUnit; +} + +inline UInt64 rtl_milliseconds(UInt64 time) { + if (time < 1) return 0; + + return time; +} +} // namespace Kernel diff --git a/src/kernel/KernelKit/TraceSrv.h b/src/kernel/KernelKit/TraceSrv.h new file mode 100644 index 00000000..df188ea2 --- /dev/null +++ b/src/kernel/KernelKit/TraceSrv.h @@ -0,0 +1,22 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + NeKernel is licensed under the Apache License 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +namespace Detail { + inline constexpr auto kDebugCmdLen = 256U; + inline constexpr auto kDebugPort = 51820; + inline constexpr auto kDebugMagic = "NE1.0.0;"; + inline constexpr auto kDebugVersion = 0x0100; + inline constexpr auto kDebugDelim = ';'; + inline constexpr auto kDebugEnd = '\r'; + typedef UInt64 dk_socket_type; +} // namespace Detail +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/KernelKit/UserMgr.h b/src/kernel/KernelKit/UserMgr.h new file mode 100644 index 00000000..3ce6254d --- /dev/null +++ b/src/kernel/KernelKit/UserMgr.h @@ -0,0 +1,95 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef INC_USER_H +#define INC_USER_H + +/* ======================================== + + Revision History: + + 04/03/25: Set users directory as /libSystem/ instead of /usr/ + + ======================================== */ + +#include +#include +#include +#include + +///! We got the MGMT, STD (%s format) and GUEST users, +///! all are used to make authorized operations. +#define kMgmtUser "NEKERNEL/MGMT/%s" +#define kGuestUser "NEKERNEL/GUEST/%s" +#define kStdUser "NEKERNEL/STD/%s" + +#define kUsersDir "/users/" + +#define kMaxUserNameLen (256U) +#define kMaxUserTokenLen (256U) + +namespace Kernel { +class User; + +enum class UserRingKind : Int32 { + kRingInvalid = 0, + kRingStdUser = 444, + kRingSuperUser = 666, + kRingGuestUser = 777, + kRingCount = 3, +}; + +typedef Char* UserPublicKey; +typedef Char UserPublicKeyType; + +/// @brief System User class. +class User final { + public: + User() = delete; + + User(const Int32& sel, const Char* username); + User(const UserRingKind& kind, const Char* username); + + ~User(); + + public: + NE_COPY_DEFAULT(User) + + public: + bool operator==(const User& lhs); + bool operator!=(const User& lhs); + + public: + /// @brief Get software ring + const UserRingKind& Ring() noexcept; + + /// @brief Get user name + Char* Name() noexcept; + + /// @brief Is he a standard user? + Bool IsStdUser() noexcept; + + /// @brief Is she a super user? + Bool IsSuperUser() noexcept; + + /// @brief Saves a password from the public key. + Bool Save(const UserPublicKey password) noexcept; + + /// @brief Checks if a password matches the **password**. + /// @param password the password to check. + Bool Login(const UserPublicKey password) noexcept; + + private: + UserRingKind mUserRing{UserRingKind::kRingStdUser}; + Char mUserName[kMaxUserNameLen] = {0}; + UInt64 mUserFNV{0UL}; +}; + +inline User* kCurrentUser = nullptr; +inline User* kRootUser = nullptr; +} // namespace Kernel + +#endif /* ifndef INC_USER_H */ diff --git a/src/kernel/KernelKit/UserProcessScheduler.h b/src/kernel/KernelKit/UserProcessScheduler.h new file mode 100644 index 00000000..b2ab7dc2 --- /dev/null +++ b/src/kernel/KernelKit/UserProcessScheduler.h @@ -0,0 +1,243 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef INC_PROCESS_SCHEDULER_H +#define INC_PROCESS_SCHEDULER_H + +/// @file UserProcessScheduler.h +/// @brief User Process Scheduler header file. +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////// +// Last revision date is: Fri Mar 28 2025 // +//////////////////////////////////////////////////// + +namespace Kernel { +//! @brief Forward declarations. + +class IDylibObject; +class UserProcessScheduler; +class UserProcessHelper; + +/***********************************************************************************/ +/// @name USER_PROCESS +/// @brief USER_PROCESS class, holds information about the running process/thread. +/***********************************************************************************/ +class USER_PROCESS final { + public: + explicit USER_PROCESS(); + ~USER_PROCESS(); + + public: + NE_COPY_DEFAULT(USER_PROCESS) + + public: + Char Name[kSchedNameLen] = {"USER_PROCESS"}; + ProcessSubsystem SubSystem{ProcessSubsystem::kProcessSubsystemUser}; + User* Owner{nullptr}; + HAL::StackFramePtr StackFrame{nullptr}; + AffinityKind Affinity{AffinityKind::kStandard}; + ProcessStatusKind Status{ProcessStatusKind::kKilled}; + UInt8 StackReserve[kSchedMaxStackSz]; + ProcessImage Image{}; + SizeT StackSize{kSchedMaxStackSz}; + IDylibObject* DylibDelegate{nullptr}; + SizeT MemoryCursor{0UL}; + SizeT MemoryLimit{kSchedMaxMemoryLimit}; + SizeT UsedMemory{0UL}; + + struct USER_PROCESS_SIGNAL final { + UIntPtr SignalArg{0}; + ProcessStatusKind Status{ProcessStatusKind::kKilled}; + UIntPtr SignalID{0}; + }; + + USER_PROCESS_SIGNAL Signal; + PROCESS_FILE_TREE* FileTree{nullptr}; + PROCESS_HEAP_TREE* HeapTree{nullptr}; + UserProcessTeam* ParentTeam; + + VoidPtr VMRegister{0UL}; + + enum { + kInvalidExecutableKind, + kExecutableKind, + kExecutableDylibKind, + kExecutableKindCount, + }; + + ProcessTime PTime{0}; //! @brief Process allocated tine. + ProcessTime RTime{0}; //! @brief Process run time. + ProcessTime UTime{0}; //! #brief Process used time. + + ProcessID ProcessId{kSchedInvalidPID}; + Int32 Kind{kExecutableKind}; + + public: + /***********************************************************************************/ + //! @brief boolean operator, check status. + /***********************************************************************************/ + explicit operator bool(); + + /***********************************************************************************/ + ///! @brief Crashes the app, exits with code ~0. + /***********************************************************************************/ + Void Crash(); + + /***********************************************************************************/ + ///! @brief Spawns a dynamic library handle if dylib. + /***********************************************************************************/ + Bool InitDylib(); + + /***********************************************************************************/ + ///! @brief Exits the app. + /***********************************************************************************/ + Void Exit(const Int32& exit_code = 0); + + /***********************************************************************************/ + ///! @brief TLS allocate. + ///! @param sz size of data structure. + ///! @param pad_amount amount to add after pointer. + ///! @return A wrapped pointer, or error code. + /***********************************************************************************/ + ErrorOr New(SizeT sz, SizeT pad_amount = 0); + + /***********************************************************************************/ + ///! @brief TLS free. + ///! @param ptr the pointer to free. + ///! @param sz the size of it. + /***********************************************************************************/ + template + Boolean Delete(ErrorOr ptr); + + /***********************************************************************************/ + ///! @brief Wakes up thread. + /***********************************************************************************/ + Void Wake(Bool wakeup = false); + + public: + /***********************************************************************************/ + //! @brief Gets the local exit code. + /***********************************************************************************/ + KPCError& GetExitCode() noexcept; + + /***********************************************************************************/ + ///! @brief Get the process's name + ///! @example 'C Runtime Library' + /***********************************************************************************/ + const Char* GetName() noexcept; + + /***********************************************************************************/ + //! @brief return local error code of process. + //! @return Int32 local error code. + /***********************************************************************************/ + KPCError& GetLocalCode() noexcept; + + const User* GetOwner() noexcept; + const ProcessStatusKind& GetStatus() noexcept; + const AffinityKind& GetAffinity() noexcept; + + private: + KPCError LastExitCode{0}; + KPCError LocalCode{0}; + + friend UserProcessScheduler; + friend UserProcessHelper; +}; + +typedef Array USER_PROCESS_ARRAY; +typedef Ref USER_PROCESS_REF; + +/// \brief Processs Team (contains multiple processes inside it.) +/// Equivalent to a process batch +class UserProcessTeam final { + public: + explicit UserProcessTeam(); + ~UserProcessTeam() = default; + + NE_COPY_DEFAULT(UserProcessTeam) + + Array& AsArray(); + Ref& AsRef(); + ProcessID& Id() noexcept; + + public: + USER_PROCESS_ARRAY mProcessList; + USER_PROCESS_REF mCurrentProcess; + ProcessID mTeamId{0}; + ProcessID mProcessCur{0}; +}; + +/***********************************************************************************/ +/// @brief USER_PROCESS scheduler class. +/// The main class which you call to schedule user processes. +/***********************************************************************************/ +class UserProcessScheduler final : public ISchedulable { + friend class UserProcessHelper; + + public: + explicit UserProcessScheduler() = default; + ~UserProcessScheduler() override = default; + + NE_COPY_DELETE(UserProcessScheduler) + NE_MOVE_DELETE(UserProcessScheduler) + + public: + explicit operator bool(); + bool operator!(); + + public: + UserProcessTeam& TheCurrentTeam(); + BOOL SwitchTeam(UserProcessTeam& team); + + public: + ProcessID Spawn(const Char* name, VoidPtr code, VoidPtr image); + Void Remove(ProcessID process_id); + + Bool IsUser() override; + Bool IsKernel() override; + Bool HasMP() override; + + public: + USER_PROCESS_REF& TheCurrentProcess(); + SizeT Run() noexcept; + + public: + STATIC UserProcessScheduler& The(); + + private: + UserProcessTeam mTeam{}; +}; + +/***********************************************************************************/ +/** + * \brief USER_PROCESS helper class, which contains needed utilities for the scheduler. + */ +/***********************************************************************************/ + +class UserProcessHelper final { + public: + STATIC Bool Switch(HAL::StackFramePtr frame_ptr, ProcessID new_pid); + STATIC Bool CanBeScheduled(const USER_PROCESS& process); + STATIC ErrorOr TheCurrentPID(); + STATIC SizeT StartScheduling(); +}; +} // namespace Kernel + +#include +#include + +//////////////////////////////////////////////////// +// END +//////////////////////////////////////////////////// + +#endif /* ifndef INC_PROCESS_SCHEDULER_H */ diff --git a/src/kernel/KernelKit/UserProcessScheduler.inl b/src/kernel/KernelKit/UserProcessScheduler.inl new file mode 100644 index 00000000..3d3659d6 --- /dev/null +++ b/src/kernel/KernelKit/UserProcessScheduler.inl @@ -0,0 +1,64 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + FILE: UserProcessScheduler.inl + PURPOSE: Low level/Ring-3 process scheduler. + +======================================== */ + +/// @brief USER_PROCESS inline definitions. +/// @author Amlal El Mahrouss (amlal@nekernel.org) +/// @date Tue Apr 22 22:01:07 CEST 2025 + +#ifndef INC_PROCESS_SCHEDULER_H +#include +#endif // INC_PROCESS_SCHEDULER_H + +namespace Kernel { +/***********************************************************************************/ +/** @brief Free pointer/file from usage. */ +/***********************************************************************************/ + +template +BOOL USER_PROCESS::Delete(ErrorOr ptr) { + if (!ptr) return No; + + if (!this->HeapTree) { + kout << "USER_PROCESS's heap is empty.\r"; + return No; + } + + PROCESS_HEAP_TREE* entry = this->HeapTree; + + while (entry != nullptr) { + if (entry->Entry == ptr.Leak().Leak()) { + this->UsedMemory -= entry->EntrySize; + +#ifdef __NE_AMD64__ + auto pd = hal_read_cr3(); + + hal_write_cr3(this->VMRegister); + + auto ret = mm_free_ptr(entry->Entry); + + hal_write_cr3(pd); + + return ret == kErrorSuccess; +#else + Bool ret = mm_free_ptr(ptr.Leak().Leak()); + + return ret == kErrorSuccess; +#endif + } + + entry = entry->Next; + } + + kout << "USER_PROCESS: Trying to free a pointer which doesn't exist.\r"; + + this->Crash(); + + return No; +} +} // namespace Kernel diff --git a/src/kernel/KernelKit/XCOFF.h b/src/kernel/KernelKit/XCOFF.h new file mode 100644 index 00000000..cbee6100 --- /dev/null +++ b/src/kernel/KernelKit/XCOFF.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: XCOFF.h + Purpose: XCOFF for Kernel. + + Revision History: + + 04/07/24: Added file (amlel) + +======================================== */ + +#ifndef INC_XOCFF_H +#define INC_XOCFF_H + +#include + +#define kXCOFF64Magic (0x01F7) +#define kXCOFF64ForkNameLen (256U) + +#define kXCOFFRelFlg (0x0001) +#define kXCOFFExecutable (0x0002) +#define kXCOFFLnno (0x0004) +#define kXCOFFLSyms (0x0008) + +struct XCOFF_FILE_HEADER; +struct XCOFF_FORK_HEADER; + +/// @brief XCoff file header, meant for POWER apps. +typedef struct XCOFF_FILE_HEADER { + Kernel::UInt16 fMagic; + Kernel::UInt16 fTarget; + Kernel::UInt16 fNumSecs; + Kernel::UInt32 fTimeDat; + Kernel::UIntPtr fSymPtr; + Kernel::UInt32 fNumSyms; + Kernel::UInt16 fOptHdr; // ?: Number of bytes in optional header +} XCOFF_FILE_HEADER, XCOFF_FILE_HEADER32, XCOFF_FILE_HEADER64; + +/// @brief This the executable's manifest fork, designed for NeFS. +/// @param fPropertiesXMLFork The XML fork of the executable. +/// @param fDynamicLoaderFork The DYLD fork metadata. +/// @param fCodeSignFork Executable's certificate contained in a fork. +typedef struct XCOFF_FORK_HEADER { + Kernel::Char fPropertiesXMLFork[kXCOFF64ForkNameLen]; + Kernel::Char fDynamicLoaderFork[kXCOFF64ForkNameLen]; + Kernel::Char fCodeSignFork[kXCOFF64ForkNameLen]; +} XCOFF_FORK_HEADER; + +#endif // ifndef INC_XOCFF_H diff --git a/src/kernel/KernelKit/ZXD.h b/src/kernel/KernelKit/ZXD.h new file mode 100644 index 00000000..a4b07bfa --- /dev/null +++ b/src/kernel/KernelKit/ZXD.h @@ -0,0 +1,53 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define kZXDMagicNumber (0x2010AF) +#define kZXDVersion (0x0001) + +namespace Kernel { +struct ZXD_EXEC_HEADER; +struct ZXD_STUB_HEADER; + +enum ZXD_FLAGS { + kZXDFlagsInvalid, + kZXDFlagsDriver, + kZXDFlagsCount, +}; + +/// @brief ZXD executable header +/// @details This header is used to identify ZXD executable files. +struct PACKED ZXD_EXEC_HEADER final { + UInt32 fMagic; + UInt32 fVersion; + UInt32 fFlags; + UInt32 fHdrSize; + UInt32 fCRC32; + UInt32 fAssigneeSignature; + UInt32 fIssuerSingature; + UIntPtr fExecOffset; + SizeT fExecSize; + UIntPtr fStubOffset; + SizeT fStubSize; + SizeT fStubAlign; + SizeT fStubCount; +}; + +/// @brief ZXD stub header +/// @details This header is used to identify ZXD stub files. It contains the size of the stub, the +/// offset of the stub, and the CRC32 checksum of the stub. +struct PACKED ZXD_STUB_HEADER final { + UInt32 fStubSize; + UInt32 fStubOffset; + UInt32 fStubCRC32; +}; + +using ZXD_EXEC_HEADER_PTR = ZXD_EXEC_HEADER*; +using ZXD_STUB_HEADER_PTR = ZXD_STUB_HEADER*; +} // namespace Kernel diff --git a/src/kernel/NeKit/Array.h b/src/kernel/NeKit/Array.h new file mode 100644 index 00000000..5b8371db --- /dev/null +++ b/src/kernel/NeKit/Array.h @@ -0,0 +1,46 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +namespace Kernel { +template +class Array final { + public: + explicit Array() = default; + ~Array() = default; + + Array& operator=(const Array&) = default; + Array(const Array&) = default; + + T& operator[](SizeT at) { return fArray[at]; } + + Boolean Empty() { return this->Count() > 0; } + + SizeT Capacity() { return N; } + + SizeT Count() { + const static SizeT kArrCnt = N; + return kArrCnt; // avoid constexpr error. + } + + const T* CData() { return fArray; } + + operator bool() { return !Empty(); } + + private: + T fArray[N]; +}; + +template +auto make_list(ValueType val) { + return Array{val}; +} +} // namespace Kernel diff --git a/src/kernel/NeKit/ArrayList.h b/src/kernel/NeKit/ArrayList.h new file mode 100644 index 00000000..54613b67 --- /dev/null +++ b/src/kernel/NeKit/ArrayList.h @@ -0,0 +1,44 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +template +class ArrayList final { + public: + explicit ArrayList(T* list, SizeT length) : fList(reinterpret_cast(list)), fLen(length) {} + + ~ArrayList() = default; + + ArrayList& operator=(const ArrayList&) = default; + ArrayList(const ArrayList&) = default; + + T* Data() { return fList; } + + const T* CData() { return fList; } + + T& operator[](SizeT index) const { + MUST_PASS(index < this->Count()); + return fList[index]; + } + + operator bool() { return fList; } + + SizeT Count() const { return fLen; } + + private: + T* fList{nullptr}; + SizeT fLen{0}; +}; + +template +ArrayList make_list(ValueType val) { + return ArrayList{val}; +} +} // namespace Kernel diff --git a/src/kernel/NeKit/Atom.h b/src/kernel/NeKit/Atom.h new file mode 100644 index 00000000..0f8eefbc --- /dev/null +++ b/src/kernel/NeKit/Atom.h @@ -0,0 +1,33 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ +#pragma once + +#include + +namespace Kernel { +template +class Atom final { + public: + explicit Atom() = default; + ~Atom() = default; + + public: + Atom& operator=(const Atom&) = delete; + Atom(const Atom&) = delete; + + public: + T operator[](Size bit) { return (fArrayOfAtoms & (1 << bit)); } + + void operator|(Size bit) { fArrayOfAtoms |= (1 << bit); } + + friend Boolean operator==(Atom& atomic, const T& idx) { return atomic[idx] == idx; } + + friend Boolean operator!=(Atom& atomic, const T& idx) { return atomic[idx] == idx; } + + private: + T fArrayOfAtoms; +}; +} // namespace Kernel diff --git a/src/kernel/NeKit/Crc32.h b/src/kernel/NeKit/Crc32.h new file mode 100644 index 00000000..3c6a904f --- /dev/null +++ b/src/kernel/NeKit/Crc32.h @@ -0,0 +1,20 @@ +/* + * ======================================================== + * + * NeKernel + * Date Added: 13/02/2023 + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#ifndef NEKIT_CRC32_H +#define NEKIT_CRC32_H + +#include + +namespace Kernel { +UInt32 ke_calculate_crc32(const VoidPtr crc, Int32 len) noexcept; +} // namespace Kernel + +#endif // !NEKIT_CRC32_H diff --git a/src/kernel/NeKit/CxxAbi.h b/src/kernel/NeKit/CxxAbi.h new file mode 100644 index 00000000..7b13d6b3 --- /dev/null +++ b/src/kernel/NeKit/CxxAbi.h @@ -0,0 +1,26 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ +#pragma once + +#include + +#ifndef __NECTI__ + +#define kAtExitMacDestructors (128) + +struct atexit_func_entry_t { + void (*destructor_func)(); + void* obj_ptr; + void* dso_handle; +}; + +typedef Kernel::UInt32 uarch_t; + +namespace cxxabiv1 { +typedef Kernel::SizeT* __guard; +} + +#endif // !__NECTI__ diff --git a/src/kernel/NeKit/Defines.h b/src/kernel/NeKit/Defines.h new file mode 100644 index 00000000..1a6a2cf6 --- /dev/null +++ b/src/kernel/NeKit/Defines.h @@ -0,0 +1,179 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define NEKIT_VERSION_STR "0.0.1" +#define NEKIT_VERSION_BCD 0x0001 + +#ifndef __cplusplus +#error !!! Kernel compiles only with a C++ compiler. !!! +#endif + +#if __cplusplus <= 201703L +#define char8_t char +#endif + +#ifdef __has_feature +#if !__has_feature(cxx_nullptr) +#if !__has_nullptr +#error !!! You must at least have nullptr featured on your C++ compiler. !!! +#endif +#endif +#endif + +/// @brief The **Kernel** namespace. +namespace Kernel { +using voidPtr = void*; +using VoidPtr = void*; +using nullPtr = decltype(nullptr); +using NullPtr = decltype(nullptr); + +using Int = int; +using Int32 = __INT32_TYPE__; +using UShort = __UINT16_TYPE__; +using UInt16 = __UINT16_TYPE__; +using Short = short; +using Int16 = __INT16_TYPE__; +using UInt = __UINT32_TYPE__; +using UInt32 = __UINT32_TYPE__; +using Long = __INT64_TYPE__; +using Int64 = __INT64_TYPE__; +using ULong = __UINT64_TYPE__; +using UInt64 = __UINT64_TYPE__; +using Boolean = bool; +using Bool = bool; +using Char = char; +using Int8 = __INT8_TYPE__; +using Char8 = char8_t; +using UChar = __UINT8_TYPE__; +using UInt8 = __UINT8_TYPE__; + +using SSize = long; +using SSizeT = long; +using Size = __SIZE_TYPE__; +using SizeT = __SIZE_TYPE__; +using IntPtr = __INTPTR_TYPE__; +using UIntPtr = __UINTPTR_TYPE__; +using IntFast = __INT_FAST32_TYPE__; +using IntFast64 = __INT_FAST64_TYPE__; +using PtrDiff = __PTRDIFF_TYPE__; + +using SInt16 = Int16; +using SInt32 = Int32; +using SInt64 = Int64; + +typedef UIntPtr* Ptr64; +typedef UInt32* Ptr32; +typedef UInt8* Ptr8; + +using Utf8Char = char8_t; +using Utf16Char = char16_t; +using WideChar = wchar_t; +using Utf32Char = char32_t; + +using LongDouble = long double; +using Double = double; +using Float = float; + +typedef UInt32 PhysicalAddressKind; +typedef UIntPtr VirtualAddressKind; + +using Void = void; +using Any = void*; + +using Lba = UInt64; + +using Char16 = char16_t; + +enum class Endian : UInt8 { kEndianInvalid, kEndianBig, kEndianLittle, kEndianMixed, kEndianCount }; + +/// @brief Forward object. +/// @tparam Args the object type. +/// @param arg the object. +/// @return object's rvalue +template +inline Args&& forward(Args& arg) { + return static_cast(arg); +} + +/// @brief Move object. +/// @tparam Args the object type. +/// @param arg the object. +/// @return object's rvalue +template +inline Args&& move(Args&& arg) { + return static_cast(arg); +} + +/// @brief Encoding interface, used as a proxy to convert T to Char* +/// Used to cast A to B or B to A. +class ICodec { + public: + explicit ICodec() = default; + virtual ~ICodec() = default; + + ICodec& operator=(const ICodec&) = default; + ICodec(const ICodec&) = default; + + public: + /// @brief Convert type to bytes. + /// @tparam T the type. + /// @param type (a1) the data. + /// @return a1 as Char* + template + const Char* AsBytes(T type) noexcept { + NE_UNUSED(type); + return nullptr; + } + + /// @brief Construct from type to class. + /// @tparam T the type to convert. + /// @param type (a1) the data. + /// @return a1 as Char* + template + OutputClass* Construct(Char* type) noexcept { + FactoryClass class_fac; + return class_fac.template From(type); + } + + /// @brief Convert T class to Y class. + /// @tparam T the class type of type. + /// @tparam Y the result class. + /// @param type the class to cast. + /// @return the class as Y. + template + Y As(T type) noexcept { + if (type.template IsSerializable()) { + return reinterpret_cast(type); + } + + return type.template As(); + } +}; + +/// \brief Scheduler interface, represents a scheduler object. +/// @note This is used to schedule tasks, such as threads, drivers, user threads, etc. +class ISchedulable { + public: + explicit ISchedulable() = default; + virtual ~ISchedulable() = default; + + ISchedulable& operator=(const ISchedulable&) = default; + ISchedulable(const ISchedulable&) = default; + + /// @brief Is this object only accepting user tasks? + virtual Bool IsUser() { return NO; } + + /// @brief Is this object only accepting kernel tasks? + virtual Bool IsKernel() { return NO; } + + /// @brief Is this object offloading to another CPU? + virtual Bool HasMP() { return NO; } +}; +} // namespace Kernel diff --git a/src/kernel/NeKit/ErrorOr.h b/src/kernel/NeKit/ErrorOr.h new file mode 100644 index 00000000..d930fe17 --- /dev/null +++ b/src/kernel/NeKit/ErrorOr.h @@ -0,0 +1,62 @@ +/* + * ======================================================== + * + * NeKernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#pragma once + +#include +#include + +namespace Kernel { +using ErrorT = Int32; + +/// ================================================================================ +/// @brief ErrorOr class for error handling. +/// ================================================================================ +template +class ErrorOr final { + public: + explicit ErrorOr() = default; + ~ErrorOr() = default; + + public: + explicit ErrorOr(ErrorT err) : mRef((T*) RTL_ALLOCA(sizeof(T))), mId(err) {} + + explicit ErrorOr(nullPtr) {} + + explicit ErrorOr(T* Class) : mRef(Class) {} + + explicit ErrorOr(T Class) : mRef(Class) {} + + ErrorOr& operator=(const ErrorOr&) = default; + ErrorOr(const ErrorOr&) = default; + + ErrorOr& operator=(const Ref& refErr) { + mRef = refErr; + return *this; + } + + const T& Value() { return mRef.TryLeak(); } + + Ref& Leak() { return mRef; } + + ErrorT Error() { return mId; } + + /// @note DO NOT MAKE THIS EXPLICIT! IT WILL BREAK THE COMPILATION. + operator bool() { return mRef; } + + BOOL HasError() { return this->mId < 0; } + + private: + Ref mRef; + ErrorT mId{0}; +}; + +using ErrorOrAny = ErrorOr; + +} // namespace Kernel diff --git a/src/kernel/NeKit/Function.h b/src/kernel/NeKit/Function.h new file mode 100644 index 00000000..70242bc3 --- /dev/null +++ b/src/kernel/NeKit/Function.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +/// ================================================================================ +/// @brief Function wrapper class. +/// ================================================================================ +template +class Function final { + public: + Function() = default; + + public: + explicit Function(T (*Fn)(Args... args)) : fFn(Fn) {} + + ~Function() = default; + + Function& operator=(const Function&) = default; + Function(const Function&) = default; + + template + T operator()(Args&&... args) { + return fFn(args...); + } + + template + T Call(Args&&... args) { + return fFn(args...); + } + + operator bool() { return fFn; } + + bool operator!() { return !fFn; } + + private: + T(*fFn) + (Args... args){nullptr}; +}; + +template +using FunctionOr = ErrorOr>; +} // namespace Kernel + diff --git a/src/kernel/NeKit/Json.h b/src/kernel/NeKit/Json.h new file mode 100644 index 00000000..1e804354 --- /dev/null +++ b/src/kernel/NeKit/Json.h @@ -0,0 +1,146 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +/// @brief Kernel JSON API. + +#include +#include +#include +#include +#include + +#define kNeJsonMaxLen (8196) +#define kNeJsonLen (256) +#define kNeJsonNullArr "[]" +#define kNeJsonNullObj "{}" +#define kNeJsonNullKey "null" +#define kNeJsonNullValue kNeJsonNullKey + +namespace Kernel { +/// ================================================================================ +/// @brief JSON object representation. +/// ================================================================================ +template +class JsonObject final { + public: + explicit JsonObject() { + KBasicString key = KString(kNeJsonMaxLen); + key += kNeJsonNullValue; + + this->AsKey() = key; + this->AsValue() = key; + } + + explicit JsonObject(SizeT lhsLen, SizeT rhsLen) : fKey(lhsLen), fValue(rhsLen) { + + KBasicString key = KString(lhsLen); + this->AsKey() = key; + + KBasicString value = KString(rhsLen); + this->AsValue() = value; + } + + ~JsonObject() = default; + + NE_COPY_DEFAULT(JsonObject) + NE_MOVE_DEFAULT(JsonObject) + + Bool& IsUndefined() { return fUndefined; } + + private: + Bool fUndefined; // is this instance undefined? + KBasicString fKey; + KBasicString fValue; + + public: + /// @brief returns the key of the json + /// @return the key as string view. + KBasicString& AsKey() { return fKey; } + + /// @brief returns the value of the json. + /// @return the key as string view. + KBasicString& AsValue() { return fValue; } + + STATIC JsonObject kNull; +}; + +/// ================================================================================ +/// @brief JsonObject stream reader helper for ASCII. +/// ================================================================================ +struct AsciiJsonStreamReader final { + STATIC JsonObject In(const Char* full_array) { + auto start_val = '{'; + auto end_val = '}'; + Boolean probe_value = false; + + if (full_array[0] != start_val) { + if (full_array[0] != '[') return JsonObject{0, 0}; + + start_val = '['; + end_val = ']'; + + probe_value = true; + } + + SizeT len = rt_string_len(full_array); + + SizeT key_len = 0; + SizeT value_len = 0; + + JsonObject type(kNeJsonMaxLen, kNeJsonMaxLen); + + for (SizeT i = 1; i < len; ++i) { + if (full_array[i] == '\r' || full_array[i] == '\n') continue; + + if (probe_value) { + if (full_array[i] == end_val || full_array[i] == ',') { + probe_value = false; + + ++value_len; + } else { + if (full_array[i] == '\'') { + type.AsValue().Data()[value_len] = 0; + break; + } + + type.AsValue().Data()[value_len] = full_array[i]; + + ++value_len; + } + } else { + if (start_val == '[') continue; + + if (full_array[i] == ':') { + type.AsKey().Data()[key_len] = 0; + ++key_len; + + ++i; + + while (full_array[i] == ' ' || full_array[i] == '\t') ++i; + + probe_value = true; + } else { + type.AsKey().Data()[key_len] = full_array[i]; + + ++key_len; + } + } + } + + type.AsValue().Data()[value_len] = 0; + + return type; + } +}; + +/// ================================================================================ +/// @brief AsciiJsonStream type definition. +/// ================================================================================ +using AsciiJsonStream = Stream>; +} // namespace Kernel diff --git a/src/kernel/NeKit/KString.h b/src/kernel/NeKit/KString.h new file mode 100644 index 00000000..fa83fed4 --- /dev/null +++ b/src/kernel/NeKit/KString.h @@ -0,0 +1,92 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { +inline auto kMinimumStringSize = 8196; + +/// @brief Kernel string class, not dynamic. +template +class KBasicString final { + public: + explicit KBasicString() { + fDataSz = kMinimumStringSize; + + fData = new CharKind[fDataSz]; + MUST_PASS(fData); + + rt_set_memory(fData, 0, fDataSz); + } + + explicit KBasicString(SizeT Sz) : fDataSz(Sz) { + MUST_PASS(Sz > 1); + + fData = new CharKind[Sz]; + MUST_PASS(fData); + + rt_set_memory(fData, 0, Sz); + } + + ~KBasicString() { + if (fData) { + delete[] fData; + fData = nullptr; + } + } + + NE_COPY_DEFAULT(KBasicString) + + CharKind* Data(); + const CharKind* CData() const; + Size Length() const; + + bool operator==(const CharKind* rhs) const; + bool operator!=(const CharKind* rhs) const; + + bool operator==(const KBasicString& rhs) const; + bool operator!=(const KBasicString& rhs) const; + + KBasicString& operator+=(const CharKind* rhs); + KBasicString& operator+=(const KBasicString& rhs); + + operator const char*() { return fData; } + + operator bool() { return fData; } + + bool operator!() { return fData; } + + private: + CharKind* fData{nullptr}; + Size fDataSz{0}; + Size fCur{0}; + + friend class KStringBuilder; +}; + +using KString = KBasicString<>; +using KStringOr = ErrorOr; + +class KStringBuilder final { + public: + template + static ErrorOr> Construct(const CharKind* data); + template + static const CharKind* FromBool(const CharKind* fmt, bool n); + template + static const CharKind* Format(const CharKind* fmt, const CharKind* from); + template + static bool Equals(const CharKind* lhs, const CharKind* rhs); +}; +} // namespace Kernel + +#include diff --git a/src/kernel/NeKit/KString.inl b/src/kernel/NeKit/KString.inl new file mode 100644 index 00000000..3a73e90f --- /dev/null +++ b/src/kernel/NeKit/KString.inl @@ -0,0 +1,174 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/// @file KBasicString.inl +/// @brief Kernel String manipulation file. + +namespace Kernel { +template +inline void ort_string_append(CharKind* lhs, const CharKind* rhs, Int32 cur) { + SizeT sz_rhs = oe_string_len(rhs); + SizeT rhs_i = 0; + + for (; rhs_i < sz_rhs; ++rhs_i) { + lhs[rhs_i + cur] = rhs[rhs_i]; + } +} + +template +inline CharKind* KBasicString::Data() { + return this->fData; +} + +template +inline const CharKind* KBasicString::CData() const { + return const_cast(this->fData); +} + +template +inline SizeT KBasicString::Length() const { + return this->fDataSz; +} + +template +inline bool KBasicString::operator==(const KBasicString& rhs) const { + if (rhs.Length() != this->Length()) return false; + + for (Size index = 0; index < this->Length(); ++index) { + if (rhs.fData[index] != this->fData[index]) return false; + } + + return true; +} + +template +inline bool KBasicString::operator==(const CharKind* rhs) const { + if (oe_string_len(rhs) != this->Length()) return false; + + for (Size index = 0; index < oe_string_len(rhs); ++index) { + if (rhs[index] != this->fData[index]) return false; + } + + return true; +} + +template +inline bool KBasicString::operator!=(const KBasicString& rhs) const { + if (rhs.Length() != this->Length()) return false; + + for (Size index = 0; index < rhs.Length(); ++index) { + if (rhs.fData[index] == this->fData[index]) return false; + } + + return true; +} + +template +inline bool KBasicString::operator!=(const CharKind* rhs) const { + if (oe_string_len(rhs) != this->Length()) return false; + + for (Size index = 0; index < oe_string_len(rhs); ++index) { + if (rhs[index] == this->fData[index]) return false; + } + + return true; +} + +template +inline KBasicString& KBasicString::operator+=( + const KBasicString& rhs) { + if (oe_string_len(rhs.fData) > this->Length()) return *this; + + ort_string_append(this->fData, const_cast(rhs.fData), this->fCur); + this->fCur += oe_string_len(const_cast(rhs.fData)); + + return *this; +} + +template +inline KBasicString& KBasicString::operator+=(const CharKind* rhs) { + ort_string_append(this->fData, const_cast(rhs), this->fCur); + this->fCur += oe_string_len(const_cast(rhs)); + + return *this; +} + +template +inline ErrorOr> KStringBuilder::Construct(const CharKind* data) { + if (!data || *data == 0) return ErrorOr>(nullptr); + + KBasicString* view = new KBasicString(oe_string_len(data)); + (*view) += data; + + return ErrorOr>(*view); +} +template +inline const CharKind* KStringBuilder::FromBool(const CharKind* fmt, bool i) { + if (!fmt) return ("?"); + + const CharKind* boolean_expr = i ? "YES" : "NO"; + CharKind* ret = + (CharKind*) RTL_ALLOCA(oe_string_len(boolean_expr) + oe_string_len(fmt)); + + if (!ret) return ("?"); + + const auto fmt_len = oe_string_len(fmt); + const auto res_len = oe_string_len(boolean_expr); + + for (Size idx = 0; idx < fmt_len; ++idx) { + if (fmt[idx] == '%') { + SizeT result_cnt = idx; + + for (auto y_idx = idx; y_idx < res_len; ++y_idx) { + ret[result_cnt] = boolean_expr[y_idx]; + ++result_cnt; + } + + break; + } + + ret[idx] = fmt[idx]; + } + + return ret; +} +template +inline bool KStringBuilder::Equals(const CharKind* lhs, const CharKind* rhs) { + if (oe_string_len(rhs) != oe_string_len(lhs)) return false; + + for (Size index = 0; index < oe_string_len(rhs); ++index) { + if (rhs[index] != lhs[index]) return false; + } + + return true; +} +template +inline const CharKind* KStringBuilder::Format(const CharKind* fmt, const CharKind* fmt2) { + if (!fmt || !fmt2) return ("?"); + + CharKind* ret = (CharKind*) RTL_ALLOCA( + sizeof(char) * (oe_string_len(fmt2) + oe_string_len(fmt))); + + if (!ret) return ("?"); + + const auto len = oe_string_len(fmt); + + for (Size idx = 0; idx < len; ++idx) { + if (fmt[idx] == '%' && idx < oe_string_len(fmt) && fmt[idx] == 's') { + Size result_cnt = idx; + + for (Size y_idx = 0; y_idx < oe_string_len(fmt2); ++y_idx) { + ret[result_cnt] = fmt2[y_idx]; + ++result_cnt; + } + } + + ret[idx] = fmt[idx]; + } + + return ret; +} +} // namespace Kernel diff --git a/src/kernel/NeKit/KernelPanic.h b/src/kernel/NeKit/KernelPanic.h new file mode 100644 index 00000000..f716e6de --- /dev/null +++ b/src/kernel/NeKit/KernelPanic.h @@ -0,0 +1,69 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +void ke_runtime_check(bool expr, const Char* file, const Char* line); +} + +#define MUST_PASS_COMPILER(EXPR, MSG) static_assert(EXPR, MSG) + +#ifdef TRY +#undef TRY +#endif + +#define TRY(X) \ + { \ + auto fn = X; \ + if ((fn()) == NO) { \ + MUST_PASS(NO); \ + } \ + } + +#ifdef __MUST_PASS +#undef __MUST_PASS +#endif + +#define __MUST_PASS(EXPR, FILE, LINE) Kernel::ke_runtime_check(EXPR, FILE, STRINGIFY(LINE)) + +#ifdef __DEBUG__ +#define MUST_PASS(EXPR) __MUST_PASS((EXPR), __FILE__, __LINE__) +#define assert(EXPR) MUST_PASS(EXPR) +#else +#define MUST_PASS(EXPR) (Kernel::Void)(EXPR) +#define assert(EXPR) (Kernel::Void)(EXPR) +#endif + +enum RUNTIME_CHECK { + RUNTIME_CHECK_FAILED = 1111, + RUNTIME_CHECK_POINTER, + RUNTIME_CHECK_EXPRESSION, + RUNTIME_CHECK_FILE, + RUNTIME_CHECK_IPC, + RUNTIME_CHECK_TLS, + RUNTIME_CHECK_HANDSHAKE, + RUNTIME_CHECK_ACPI, + RUNTIME_CHECK_INVALID_PRIVILEGE, + RUNTIME_CHECK_PROCESS, + RUNTIME_CHECK_BAD_BEHAVIOR, + RUNTIME_CHECK_BOOTSTRAP, + RUNTIME_CHECK_UNEXCPECTED, + RUNTIME_CHECK_FILESYSTEM, + RUNTIME_CHECK_VIRTUAL_OUT_OF_MEM, + RUNTIME_CHECK_PAGE, + RUNTIME_CHECK_INVALID, + RUNTIME_CHECK_COUNT, +}; + +typedef enum RUNTIME_CHECK RTL_RUNTIME_CHECK; + +namespace Kernel { +void ke_panic(const Int32& id, const Char* message = nullptr); +} // namespace Kernel diff --git a/src/kernel/NeKit/Macros.h b/src/kernel/NeKit/Macros.h new file mode 100644 index 00000000..5949b414 --- /dev/null +++ b/src/kernel/NeKit/Macros.h @@ -0,0 +1,151 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +/***********************************************************************************/ +/// @file NeKit/Macros.h +/// @brief Core Types and Macros. +/***********************************************************************************/ + +#ifndef KIB +#define KIB(X) (Kernel::UInt64)((X) / 1024) +#endif + +#ifndef kib_cast +#define kib_cast(X) (Kernel::UInt64)((X) *1024) +#endif + +#ifndef MIB +#define MIB(X) (Kernel::UInt64)((Kernel::UInt64) KIB(X) / 1024) +#endif + +#ifndef mib_cast +#define mib_cast(X) (Kernel::UInt64)((Kernel::UInt64) kib_cast(X) * 1024) +#endif + +#ifndef GIB +#define GIB(X) (Kernel::UInt64)((Kernel::UInt64) MIB(X) / 1024) +#endif + +#ifndef gib_cast +#define gib_cast(X) (Kernel::UInt64)((Kernel::UInt64) mib_cast(X) * 1024) +#endif + +#ifndef TIB +#define TIB(X) (Kernel::UInt64)((Kernel::UInt64) GIB(X) / 1024) +#endif + +#ifndef tib_cast +#define tib_cast(X) ((Kernel::UInt64) gib_cast(X) * 1024) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) \ + (((sizeof(a) / sizeof(*(a))) / (static_cast(!(sizeof(a) % sizeof(*(a))))))) +#endif + +#define DEPRECATED ATTRIBUTE(deprecated) + +#ifndef ALIGN +#define ALIGN(X) __attribute__((aligned(X))) +#endif // #ifndef ALIGN + +#ifndef ATTRIBUTE +#define ATTRIBUTE(...) __attribute__((__VA_ARGS__)) +#endif // #ifndef ATTRIBUTE + +#ifndef __NE_VER__ +#define __NE_VER__ (2024) +#endif // !__NE_VER__ + +#ifndef EXTERN +#define EXTERN extern +#endif + +#ifndef EXTERN_C +#define EXTERN_C extern "C" +#endif + +#ifndef MAKE_ENUM +#define MAKE_ENUM(NAME) enum NAME { +#endif + +#ifndef END_ENUM +#define END_ENUM() \ + } \ + ; +#endif + +#ifndef MAKE_STRING_ENUM +#define MAKE_STRING_ENUM(NAME) namespace NAME { +#endif + +#ifndef ENUM_STRING +#define ENUM_STRING(NAME, VAL) inline constexpr const char* e##NAME = VAL +#endif + +#ifndef END_STRING_ENUM +#define END_STRING_ENUM() } +#endif + +#ifndef RTL_ALLOCA +#define RTL_ALLOCA(sz) __builtin_alloca(sz) +#endif // #ifndef RTL_ALLOCA + +#ifndef CANT_REACH +#define CANT_REACH() __builtin_unreachable() +#endif + +#define kInvalidAddress 0xFBFBFBFBFBFBFBFB +#define kBadAddress 0x0000000000000000 +#define kMaxAddr 0xFFFFFFFFFFFFFFFF +#define kPathLen 0x100 + +#define PACKED ATTRIBUTE(packed) +#define NO_EXEC ATTRIBUTE(noexec) + +#define EXTERN extern +#define STATIC static + +#define CONST const + +#define STRINGIFY(X) #X +#define NE_UNUSED(X) ((Kernel::Void) X) + +#ifndef RGB +#define RGB(R, G, B) ((Kernel::UInt32)((0xFF << 24) | ((R) << 16) | ((G) << 8) | (B))) +#endif // !RGB + +#ifdef __NE_AMD64__ +#define DBG_TRAP() asm volatile("int $3") +#else +#define DBG_TRAP() ((Kernel::Void) 0) +#endif + +#define LIKELY(ARG) ((ARG) ? MUST_PASS(NO) : ((Kernel::Void) 0)) +#define UNLIKELY(ARG) LIKELY(!(ARG)) + +#define RTL_ENDIAN(address, value) \ + (((reinterpret_cast(address)[0]) == (value)) ? (Kernel::Endian::kEndianBig) \ + : (Kernel::Endian::kEndianLittle)) + +#define Yes true +#define No false + +#define YES true +#define NO false + +#define TRUE true +#define FALSE false + +#define BOOL Kernel::Boolean + +#ifdef RTL_INIT_OBJECT +#undef RTL_INIT_OBJECT +#endif // ifdef RTL_INIT_OBJECT + +#define RTL_INIT_OBJECT(OBJ, TYPE, ...) TYPE OBJ = TYPE(__VA_ARGS__) diff --git a/src/kernel/NeKit/MutableArray.h b/src/kernel/NeKit/MutableArray.h new file mode 100644 index 00000000..e1138b3b --- /dev/null +++ b/src/kernel/NeKit/MutableArray.h @@ -0,0 +1,203 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ +#pragma once + +#include +#include +#include + +#define RTL_TRY_FIND_NODE(NAME, NODE) \ + auto* NAME = NODE; \ + while (NAME) { \ + if (NAME->fIndex == Index) return NAME->fVal; \ + NAME = NAME->fNext; \ + } + +#define RTL_TRY_FIND_NODE2(NAME, NODE) \ + auto* NAME = NODE; \ + while (NAME) { \ + if (NAME->fIndex == Index) return Ref{NAME->fVal}; \ + NAME = NAME->fNext; \ + } + +#define RTL_TRY_REMOVE_NODE(NODE) \ + if (NODE && NODE->fIndex == Index) { \ + NODE->fUsed = false; \ + NODE->fIndex = 0; \ + \ + return true; \ + } + +// FIXME: this is a shitty algorithm, because it is memory heavy. +// Remove and occurences of that, and remove that class. +namespace Kernel { +template +class MutableArray; + +template +class NullableMutableArray; + +template +class MutableLinkedList { + public: + T fVal; + SizeT fIndex{0}; + Boolean fUsed{false}; + + MutableLinkedList* fPrev{nullptr}; + MutableLinkedList* fNext{nullptr}; +}; + +template +class NullableMutableArray { + public: + // explicit this. + explicit NullableMutableArray() : fFirstNode(new MutableLinkedList()) {} + + /* + * We free all the nodes allocated by the array + * and store the next one inside "NextIt" + */ + + virtual ~NullableMutableArray() { + auto* It = fFirstNode; + MutableLinkedList* NextIt = nullptr; + + while (It) { + NextIt = It->fNext; + delete It; + + It = NextIt; + } + } + + NullableMutableArray& operator=(const NullableMutableArray&) = default; + NullableMutableArray(const NullableMutableArray&) = default; + + operator bool() { return Count() > 1; } + + public: + T operator[](SizeT Index) const { + RTL_TRY_FIND_NODE(first, fFirstNode); + RTL_TRY_FIND_NODE(last, fLastNode); + + return _PlaceHolderValue; + } + + SizeT Count() const { return fNodeCount; } + + public: + Boolean Remove(SizeT Index) { + RTL_TRY_REMOVE_NODE(fFirstNode); + RTL_TRY_REMOVE_NODE(fLastNode); + + return false; + } + + Boolean Add(const T val) { + auto* iterationNode = fFirstNode; + MUST_PASS(iterationNode); + + while (iterationNode) { + if (!iterationNode->fUsed) { + iterationNode->fVal = val; + iterationNode->fIndex = 0; + + iterationNode->fUsed = true; + + ++fNodeCount; + + return true; + } + + iterationNode = iterationNode->fNext; + } + + return false; + } + + private: + /* Avoid useless lookups */ + MutableLinkedList* fLastNode{nullptr}; + MutableLinkedList* fFirstNode{nullptr}; + + /* Number of nodes inside of this dynamic array. */ + Kernel::SizeT fNodeCount{0}; + + private: + // don't remove that + friend MutableArray; +}; + +template +class MutableArray : public NullableMutableArray { + public: + // explicit this. + explicit MutableArray() = default; + virtual ~MutableArray() = default; + + NE_COPY_DEFAULT(MutableArray) + + public: + Boolean Add(const T val) { + auto* iterationNode = fFirstNode; + + if (!iterationNode) { + fFirstNode = new MutableLinkedList(); + iterationNode = fFirstNode; + } + + MUST_PASS(iterationNode); + + while (iterationNode) { + if (!iterationNode->fUsed) { + iterationNode->fVal = val; + iterationNode->fIndex = 0; + + iterationNode->fUsed = true; + + ++fNodeCount; + + return true; + } + + iterationNode = iterationNode->fNext; + } + + return false; + } + + public: + Ref operator[](SizeT Index) const { + RTL_TRY_FIND_NODE2(first, fFirstNode); + RTL_TRY_FIND_NODE2(last, fLastNode); + + return {}; + } + + SizeT Count() const { return fNodeCount; } + + bool Contains(T& value) noexcept { + MutableLinkedList* first = fFirstNode; + + while (first) { + if (first->fVal == value && first->fUsed) return true; + + first = first->fNext; + } + + return false; + } + + private: + /* Avoid useless lookups */ + MutableLinkedList* fLastNode{nullptr}; + MutableLinkedList* fFirstNode{nullptr}; + + /* Number of nodes inside of this dynamic array. */ + Kernel::SizeT fNodeCount{0}; +}; +} // namespace Kernel diff --git a/src/kernel/NeKit/NeKit.h b/src/kernel/NeKit/NeKit.h new file mode 100644 index 00000000..4b1e64ca --- /dev/null +++ b/src/kernel/NeKit/NeKit.h @@ -0,0 +1,20 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/src/kernel/NeKit/New.h b/src/kernel/NeKit/New.h new file mode 100644 index 00000000..36830129 --- /dev/null +++ b/src/kernel/NeKit/New.h @@ -0,0 +1,20 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +/// @note compatible with tk too. +typedef __SIZE_TYPE__ size_t; + +void* operator new(size_t); +void* operator new[](size_t); + +void operator delete(void*) noexcept; +void operator delete(void*, unsigned long); +void operator delete[](void*) noexcept; diff --git a/src/kernel/NeKit/OwnPtr.h b/src/kernel/NeKit/OwnPtr.h new file mode 100644 index 00000000..c8ceb1a2 --- /dev/null +++ b/src/kernel/NeKit/OwnPtr.h @@ -0,0 +1,67 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +namespace Kernel { +template +class OwnPtr; + +template +class NonNullRefPtr; + +template +class OwnPtr final { + public: + OwnPtr() {} + ~OwnPtr() { this->Delete(); } + + OwnPtr& operator=(const OwnPtr&) = default; + OwnPtr(const OwnPtr&) = default; + + public: + template + bool New(Args&&... arg) { + if (fCls) { + return false; + } + + fCls = new T(arg...); + return fCls; + } + + void Delete() { + if (fCls) delete fCls; + + fCls = nullptr; + } + + T* operator->() const { return fCls; } + + T* Raw() { return fCls; } + + Ref AsRef() { return Ref(fCls); } + + operator bool() { return fCls; } + bool operator!() { return !fCls; } + + private: + T* fCls; +}; + +template +inline OwnPtr mm_make_own_ptr(Args... args) { + OwnPtr ret; + ret.template New(forward(args)...); + + return ret; +} +} // namespace Kernel diff --git a/src/kernel/NeKit/PageMgr.h b/src/kernel/NeKit/PageMgr.h new file mode 100644 index 00000000..6cdd5a5c --- /dev/null +++ b/src/kernel/NeKit/PageMgr.h @@ -0,0 +1,76 @@ +// a way to create and find our pages. +// I'm thinking about a separate way of getting a paged area. + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +class PageMgr; + +class PTEWrapper final { + public: + explicit PTEWrapper(Boolean Rw = false, Boolean User = false, Boolean ExecDisable = false, + UIntPtr Address = 0); + + ~PTEWrapper(); + + PTEWrapper& operator=(const PTEWrapper&) = default; + PTEWrapper(const PTEWrapper&) = default; + + public: + UIntPtr VirtualAddress(); + + Void NoExecute(const bool enable = false); + Bool NoExecute(); + + operator bool() { return fVirtAddr; } + + bool Reclaim(); + bool Shareable(); + bool Present(); + bool Access(); + + private: + Boolean fRw; + Boolean fUser; + Boolean fExecDisable; + UIntPtr fVirtAddr; + Boolean fCache; + Boolean fShareable; + Boolean fWt; + Boolean fPresent; + Boolean fAccessed; + + private: + friend class PageMgr; + friend class Pmm; +}; + +struct PageMgr final { + public: + PageMgr() = default; + ~PageMgr() = default; + + PageMgr& operator=(const PageMgr&) = default; + PageMgr(const PageMgr&) = default; + + public: + PTEWrapper Request(Boolean Rw, Boolean User, Boolean ExecDisable, SizeT Sz, SizeT Pad); + bool Free(Ref& wrapper); + + private: + void FlushTLB(); + + private: + friend PTEWrapper; + friend class Pmm; +}; +} // namespace Kernel diff --git a/src/kernel/NeKit/Pair.h b/src/kernel/NeKit/Pair.h new file mode 100644 index 00000000..c8914ec6 --- /dev/null +++ b/src/kernel/NeKit/Pair.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +namespace Kernel { +template +class Pair; + +class PairBuilder; + +template +class Pair final { + T1 fFirst{nullptr}; + T2 fSecond{nullptr}; + + friend PairBuilder; + + public: + explicit Pair() = default; + ~Pair() = default; + + NE_COPY_DEFAULT(Pair) + + T1& First() { return fFirst; } + T2& Second() { return fSecond; } + + const T1& First() const { return *fFirst; } + const T2& Second() const { return *fSecond; } + + private: + Pair(T1 first, T2 second) : fFirst(first), fSecond(second) {} +}; + +class PairBuilder final { + template + STATIC Pair Construct(T1 first, T2 second) { + return Pair(first, second); + } +}; + +template +using PairOr = ErrorOr>; +} // namespace Kernel diff --git a/src/kernel/NeKit/Pmm.h b/src/kernel/NeKit/Pmm.h new file mode 100644 index 00000000..8c117a67 --- /dev/null +++ b/src/kernel/NeKit/Pmm.h @@ -0,0 +1,39 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +class Pmm; +class PTEWrapper; + +class Pmm final { + public: + explicit Pmm(); + ~Pmm(); + + Pmm& operator=(const Pmm&) = delete; + Pmm(const Pmm&) = default; + + Ref RequestPage(Boolean user = false, Boolean readWrite = false); + Boolean FreePage(Ref refPage); + + Boolean ToggleRw(Ref refPage, Boolean enable = true); + Boolean TogglePresent(Ref refPage, Boolean enable = true); + Boolean ToggleUser(Ref refPage, Boolean enable = true); + Boolean ToggleShare(Ref refPage, Boolean enable = true); + + /// @brief Get the page manager of this. + Ref& Leak() { return fPageMgr; } + + private: + Ref fPageMgr; +}; +} // namespace Kernel diff --git a/src/kernel/NeKit/Ref.h b/src/kernel/NeKit/Ref.h new file mode 100644 index 00000000..dac701e0 --- /dev/null +++ b/src/kernel/NeKit/Ref.h @@ -0,0 +1,77 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef _NEKIT_REF_H_ +#define _NEKIT_REF_H_ + +#include +#include +#include +#include + +namespace Kernel { +/// =========================================================== /// +/// @brief Reference wrapper class. /// +/// =========================================================== /// +template +class Ref final { + public: + explicit Ref() = default; + ~Ref() = default; + + public: + Ref(T* cls) : fClass(*cls) {} + Ref(T cls) : fClass(cls) {} + + Ref& operator=(T ref) { + fClass = ref; + return *this; + } + + NE_COPY_DEFAULT(Ref) + + public: + T operator->() const { return fClass; } + + T& Leak() noexcept { return fClass; } + + T& TryLeak() noexcept { return fClass; } + + T operator*() { return fClass; } + + operator bool() noexcept { return true; } + + private: + T fClass; +}; + +template +class NonNullRef final { + public: + NonNullRef() = delete; + NonNullRef(nullPtr) = delete; + + NonNullRef(T* ref) : fRef(ref) { MUST_PASS(ref); } + NonNullRef(Ref ref) : fRef(ref) { MUST_PASS(ref); } + + Ref& operator->() { + MUST_PASS(fRef); + return fRef; + } + + NonNullRef& operator=(const NonNullRef& ref) = delete; + NonNullRef(const NonNullRef& ref) = default; + + private: + Ref fRef{}; +}; + +using RefAny = Ref; +using NonNullRefAny = NonNullRef; +} // namespace Kernel + +#endif // ifndef _NEKIT_REF_H_ diff --git a/src/kernel/NeKit/Stream.h b/src/kernel/NeKit/Stream.h new file mode 100644 index 00000000..1a53e7f0 --- /dev/null +++ b/src/kernel/NeKit/Stream.h @@ -0,0 +1,45 @@ + +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +template +class Stream final { + public: + explicit Stream(Ref ref) : fStream(ref) {} + + ~Stream() = default; + + Stream& operator=(const Stream&) = default; + Stream(const Stream&) = default; + + template + friend Stream& operator>>(Stream& Ks, Ref& Buf) { + Ks.fKind = Ks.fStream->In(Buf); + return *Ks; + } + + template + friend Stream& operator<<(Stream& Ks, Ref& Buf) { + Ks.fKind = Buf; + Ks.fStream->Out(Buf.Leak()); + return *Ks; + } + + Ref& AsStreamTrait() { return fStream; } + + Ref& AsType() { return fKind; } + + private: + Ref fStream; + Ref fKind; +}; +} // namespace Kernel diff --git a/src/kernel/NeKit/TOML.h b/src/kernel/NeKit/TOML.h new file mode 100644 index 00000000..dee273ad --- /dev/null +++ b/src/kernel/NeKit/TOML.h @@ -0,0 +1,15 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +namespace Kernel { +class TOMLObject final { + public: + explicit TOMLObject() = delete; + ~TOMLObject() = default; +}; +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/NeKit/Utils.h b/src/kernel/NeKit/Utils.h new file mode 100644 index 00000000..caabd2af --- /dev/null +++ b/src/kernel/NeKit/Utils.h @@ -0,0 +1,62 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel { +/// =========================================================== /// +/// @brief ASCII API +/// =========================================================== /// + +Int rt_copy_memory(const voidPtr src, voidPtr dst, Size len); +Int rt_move_memory(const voidPtr src, voidPtr dst, Size len); +voidPtr rt_set_memory(voidPtr dst, UInt32 val, Size len); +void rt_zero_memory(voidPtr pointer, Size len); +Int rt_string_cmp(const Char* src, const Char* cmp, Size len); +const Char* rt_alloc_string(const Char* text); +Size rt_string_len(const Char* str); +Size rt_string_len(const Char* str, SizeT _len); +Boolean rt_to_string(Char* str_out, UInt64 base, Int32 limit); +Boolean rt_is_newln(Int chr); +Boolean rt_is_space(Int chr); +Int32 rt_is_alnum(Int character); +Int rt_to_uppercase(Int c); +Int rt_to_lower(Int c); +voidPtr rt_string_in_string(const Char* in, const Char* needle); +char* rt_string_has_char(Char* str, Char chr); + +/// =========================================================== /// +/// @brief Safe memory functions API +/// =========================================================== /// + +Int rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size); +voidPtr rt_set_memory_safe(voidPtr dst, UInt32 value, Size len, Size dst_size); + +/// =========================================================== /// +/// @brief UNICODE API +/// =========================================================== /// + +Int urt_string_cmp(const Utf8Char* src, const Utf8Char* cmp, Size len); +Void urt_set_memory(const voidPtr src, UInt32 dst, Size len); +Int urt_copy_memory(const voidPtr src, voidPtr dst, Size len); +Size urt_string_len(const Utf8Char* str); + +/// =========================================================== /// +/// @brief OpenEncoding API +/// =========================================================== /// + +template +inline SizeT oe_string_len(const CharType* str) { + if (!str) return 0; + + SizeT len{0}; + + while (str[len] != 0) ++len; + return len; +} +} // namespace Kernel diff --git a/src/kernel/NeKit/Variant.h b/src/kernel/NeKit/Variant.h new file mode 100644 index 00000000..7bcd0dff --- /dev/null +++ b/src/kernel/NeKit/Variant.h @@ -0,0 +1,71 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { +class Variant final { + public: + enum class VariantKind { + kInvalid = 0, + kString = 200, + kBlob, + kNull, + kJson, + kTOML, + kSwap, + }; + + public: + explicit Variant() = delete; + + public: + NE_COPY_DEFAULT(Variant) + + ~Variant() = default; + + public: + template + explicit Variant(KBasicString* stringView) + : fPtr((VoidPtr) stringView), fKind(VariantKind::kString) {} + + explicit Variant(JsonObject<>* json) : fPtr((VoidPtr) json), fKind(VariantKind::kJson) {} + + explicit Variant(TOMLObject* toml) : fPtr((VoidPtr) toml), fKind(VariantKind::kTOML) {} + + explicit Variant(nullPtr ptr) : fPtr(ptr), fKind(VariantKind::kNull) {} + + explicit Variant(SWAP_DISK_HEADER* ptr) : fPtr(ptr), fKind(VariantKind::kSwap) {} + + explicit Variant(VoidPtr ptr) : fPtr(ptr), fKind(VariantKind::kBlob) {} + + public: + const Char* ToString(); + + /// ======================================================================== + /// @brief Returns the underlying pointer. + /// @return the underlying pointer. + /// ======================================================================== + VoidPtr Leak(); + + template + T* As() noexcept { + return reinterpret_cast(fPtr); + } + + VariantKind& Kind(); + + private: + VoidPtr fPtr{nullptr}; + VariantKind fKind{VariantKind::kNull}; +}; +} // namespace Kernel diff --git a/src/kernel/NetworkKit/IP.h b/src/kernel/NetworkKit/IP.h new file mode 100644 index 00000000..b19d132f --- /dev/null +++ b/src/kernel/NetworkKit/IP.h @@ -0,0 +1,76 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { +class RawIPAddress6; +class RawIPAddress; +class IPFactory; + +class RawIPAddress final { + private: + explicit RawIPAddress(UInt8 bytes[4]); + ~RawIPAddress() = default; + + RawIPAddress& operator=(const RawIPAddress&) = delete; + RawIPAddress(const RawIPAddress&) = default; + + public: + UInt8* Address(); + + UInt8& operator[](const Size& index); + + BOOL operator==(const RawIPAddress& ipv6); + BOOL operator!=(const RawIPAddress& ipv6); + + private: + UInt8 fAddr[4]; + + friend IPFactory; // it is the one creating these addresses, thus this + // is why the constructors are private. +}; + +/** + * @brief IPv6 address. + */ +class RawIPAddress6 final { + private: + explicit RawIPAddress6(UInt8 Bytes[16]); + ~RawIPAddress6() = default; + + RawIPAddress6& operator=(const RawIPAddress6&) = delete; + RawIPAddress6(const RawIPAddress6&) = default; + + public: + UInt8* Address() { return fAddr; } + + UInt8& operator[](const Size& index); + + bool operator==(const RawIPAddress6& ipv6); + bool operator!=(const RawIPAddress6& ipv6); + + private: + UInt8 fAddr[16]; + + friend IPFactory; +}; + +/** + * @brief IP Creation helpers + */ +class IPFactory final { + public: + static ErrorOr ToKString(Ref& ipv6); + static ErrorOr ToKString(Ref& ipv4); + static bool IpCheckVersion4(const Char* ip); +}; +} // namespace Kernel diff --git a/src/kernel/NetworkKit/IPC.h b/src/kernel/NetworkKit/IPC.h new file mode 100644 index 00000000..c08a9457 --- /dev/null +++ b/src/kernel/NetworkKit/IPC.h @@ -0,0 +1,90 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license.. + + File: IPC.h. + Purpose: IPC protocol. + +======================================== */ + +#ifndef INC_IPC_H +#define INC_IPC_H + +#include +#include +#include + +/// @file IPC.h +/// @brief IPC comm. protocol. + +/// IA separator. +#define kIPCRemoteSeparator ":" + +/// Interchange address, consists of ProcessID:TEAM. +#define kIPCRemoteInvalid "00:00" + +#define kIPCHeaderMagic (0x4950434) + +namespace Kernel { +struct IPC_ADDR; +struct IPC_MSG; + +/// @brief 128-bit IPC address. +struct PACKED IPC_ADDR final { + UInt64 UserProcessID; + UInt64 UserProcessTeam; + + //////////////////////////////////// + // some operators. + //////////////////////////////////// + + BOOL operator==(const IPC_ADDR& addr) noexcept; + BOOL operator==(IPC_ADDR& addr) noexcept; + BOOL operator!=(const IPC_ADDR& addr) noexcept; + BOOL operator!=(IPC_ADDR& addr) noexcept; +}; + +typedef struct IPC_ADDR IPC_ADDR; + +enum { + kIPCLittleEndian = 0, + kIPCBigEndian = 1, + kIPCMixedEndian = 2, +}; + +constexpr inline auto kIPCMsgSize = 6094U; + +enum { + kIPCLockInvalid = 0, + kIPCLockFree = 1, + kIPCLockUsed = 2, +}; + +/// @brief IPC connection header, message cannot be greater than 6K. +typedef struct IPC_MSG final { + UInt32 IpcHeaderMagic; // cRemoteHeaderMagic + UInt8 IpcEndianess; // 0 : LE, 1 : BE + SizeT IpcPacketSize; + IPC_ADDR IpcFrom; + IPC_ADDR IpcTo; + UInt32 IpcCRC32; + UInt32 IpcMsg; + UInt32 IpcMsgSz; + UInt8 IpcData[kIPCMsgSize]; + UInt32 IpcLock; + /// @brief Passes the message to target, could be anything, HTTP packet, JSON or whatever. + static Bool Pass(IPC_MSG* self, IPC_MSG* target) noexcept; +} PACKED ALIGN(8) IPC_MSG; + +/// @brief Sanitize packet function +/// @retval true packet is correct. +/// @retval false packet is incorrect and process has crashed. +BOOL ipc_sanitize_packet(_Input IPC_MSG* pckt_in); + +/// @brief Construct packet function +/// @retval true packet is correct. +/// @retval false packet is incorrect and process has crashed. +BOOL ipc_construct_packet(_Output _Input IPC_MSG** pckt_in); +} // namespace Kernel + +#endif // INC_IPC_H diff --git a/src/kernel/NetworkKit/LTE.h b/src/kernel/NetworkKit/LTE.h new file mode 100644 index 00000000..c29f1687 --- /dev/null +++ b/src/kernel/NetworkKit/LTE.h @@ -0,0 +1,16 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license.. + + File: LTE.h. + Purpose: LTE protocol classes. + +======================================== */ + +#ifndef _INC_NETWORK_LTE_H_ +#define _INC_NETWORK_LTE_H_ + +#include +#include + +#endif // ifndef _INC_NETWORK_LTE_H_ diff --git a/src/kernel/NetworkKit/MAC.h b/src/kernel/NetworkKit/MAC.h new file mode 100644 index 00000000..382eca77 --- /dev/null +++ b/src/kernel/NetworkKit/MAC.h @@ -0,0 +1,34 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +#define kMACAddrLen (32U) + +namespace Kernel { +class MacAddressGetter; + +/// \brief This retrieves the MAC address of the device. +/// \note Listens for the current NIC. +class MacAddressGetter final { + public: + MacAddressGetter() = default; + ~MacAddressGetter() = default; + + NE_COPY_DEFAULT(MacAddressGetter) + + public: + Array& AsBytes(); + + private: + Array fMacAddress; +}; + +} // namespace Kernel diff --git a/src/kernel/NetworkKit/NetworkDevice.h b/src/kernel/NetworkKit/NetworkDevice.h new file mode 100644 index 00000000..3afa8484 --- /dev/null +++ b/src/kernel/NetworkKit/NetworkDevice.h @@ -0,0 +1,83 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __INC_NETWORK_DEVICE_H__ +#define __INC_NETWORK_DEVICE_H__ + +#include +#include + +/// @note Can either work with: Ethernet, GPRS, WiFi + +namespace Kernel { +struct NetworkDeviceCommand; +class NetworkDevice; + +/** + * \brief Network device interface, establishes a connection to the NIC. + */ +class NetworkDevice final NE_DEVICE { + public: + NetworkDevice(void (*out)(DeviceInterface*, NetworkDeviceCommand), + void (*in)(DeviceInterface*, NetworkDeviceCommand), + void (*cleanup)(void) = nullptr); + + ~NetworkDevice() override; + + public: + NetworkDevice& operator=(const NetworkDevice&) = default; + NetworkDevice(const NetworkDevice&) = default; + + public: + const Char* Name() const override; + Boolean Name(const Char* newStr); + + private: + Void (*fCleanup)(void); +}; + +struct NetworkDeviceCommand final { + UInt32 CommandName; + UInt32 CommandType; + UInt32 CommandFlags; + VoidPtr CommandBuffer; + SizeT CommandSizeBuffer; +}; + +/// @brief TCP device. +using TCPNetworkDevice = NetworkDevice; + +/// @brief UDP device. +using UDPNetworkDevice = NetworkDevice; + +/// @brief PPP device. +using PPPNetworkDevice = NetworkDevice; + +/// @brief IPC device. +using IPCNetworkDevice = NetworkDevice; + +/// @brief GRPS device. +using GPRSNetworkDevice = NetworkDevice; + +/// @brief GSM device. +using GSMNetworkDevice = NetworkDevice; + +/// @brief Bluetooth device. +using BTNetworkDevice = NetworkDevice; + +/// @brief Printer device. +using PrinterNetworkDevice = NetworkDevice; + +/// @brief Debug device. +using DBGNetworkDevice = NetworkDevice; + +/// @brief LTE device. +using LTENetworkDevice = NetworkDevice; +} // namespace Kernel + +#include + +#endif // !__INC_NETWORK_DEVICE_H__ diff --git a/src/kernel/NetworkKit/NetworkDevice.inl b/src/kernel/NetworkKit/NetworkDevice.inl new file mode 100644 index 00000000..a86d7e56 --- /dev/null +++ b/src/kernel/NetworkKit/NetworkDevice.inl @@ -0,0 +1,32 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/*** + Dtor and ctors. +*/ + +#ifndef __INC_NETWORK_DEVICE_H__ +#include +#endif // __INC_NETWORK_DEVICE_H__ + +namespace Kernel { +inline NetworkDevice::NetworkDevice(void (*out)(DeviceInterface*, + NetworkDeviceCommand), + void (*in)(DeviceInterface*, + NetworkDeviceCommand), + void (*on_cleanup)(void)) + : DeviceInterface(out, in), fCleanup(on_cleanup) { + kout << "NetworkDevice initialized.\r"; + + MUST_PASS(out && in && on_cleanup); +} + +inline NetworkDevice::~NetworkDevice() { + kout << "NetworkDevice cleanup.\r"; + + if (fCleanup) fCleanup(); +} +} // namespace Kernel diff --git a/src/kernel/SignalKit/Signals.h b/src/kernel/SignalKit/Signals.h new file mode 100644 index 00000000..b4e72fc4 --- /dev/null +++ b/src/kernel/SignalKit/Signals.h @@ -0,0 +1,51 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +#define SIGKILL 1 /* kill */ +#define SIGPAUS 2 /* pause */ +#define SIGEXEC 3 /* execute */ +#define SIGTRAP 4 /* trap */ +#define SIGABRT 5 /* abort */ +#define SIGCONT 6 /* continue */ +#define SIGSEG 7 /* process fault */ +#define SIGBREK 8 +#define SIGATCH 9 +#define SIGDTCH 10 + +/// @author Amlal El Mahrouss +/// @brief Signal Generation API. + +namespace Kernel { +typedef SizeT rt_signal_kind; + +/// @brief Standard signal seed for general purpose usage. +inline constexpr auto kUserSignalSeed = 0x0895034fUL; + +/// @brief Special signal seed for kernel usage. +inline constexpr auto kKernelSignalSeed = 0x0895034f9fUL; + +/// @brief Generate signal from **Sig** +template +inline rt_signal_kind sig_generate_unique() { + static_assert(Sig > 0, "Signal is zero (invalid)"); + return Sig ^ Seed; +} + +/// @brief Checks if the signal matches the seed (user_seed or kernel_seed) +template +inline BOOL sig_matches_seed(rt_signal_kind sig) { + return (sig & 0xFF000000) == (Seed & 0xFF000000); +} + +/// @brief Validate signal from **sig** +inline BOOL sig_validate_unique(rt_signal_kind sig) { + return sig > 0; +} +} // namespace Kernel diff --git a/src/kernel/StorageKit/AHCI.h b/src/kernel/StorageKit/AHCI.h new file mode 100644 index 00000000..82bd9747 --- /dev/null +++ b/src/kernel/StorageKit/AHCI.h @@ -0,0 +1,49 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +namespace Kernel { +/// @brief AHCIDeviceInterface class +/// @details This class is used to send and receive data from the AHCI device. +/// @note The class is derived from the DeviceInterface class. +class AHCIDeviceInterface NE_DEVICE { + public: + explicit AHCIDeviceInterface(void (*out)(DeviceInterface* self, IMountpoint* out), + void (*in)(DeviceInterface* self, IMountpoint* in)); + + virtual ~AHCIDeviceInterface() override; + + public: + AHCIDeviceInterface& operator=(const AHCIDeviceInterface&) = default; + AHCIDeviceInterface(const AHCIDeviceInterface&) = default; + + const Char* Name() const override; + + const UInt16& GetPortsImplemented(); + + Void SetPortsImplemented(const UInt16& pi); + + const UInt32& GetIndex(); + + Void SetIndex(const UInt32& drv); + + public: + AHCIDeviceInterface& operator<<(IMountpoint* Data) override; + AHCIDeviceInterface& operator>>(IMountpoint* Data) override; + + private: + UInt16 fPortsImplemented{0U}; + UInt32 fDriveIndex{0U}; +}; + +UInt16 sk_init_ahci_device(BOOL atapi); +ErrorOr sk_acquire_ahci_device(UInt32 drv_index); +} // namespace Kernel diff --git a/src/kernel/StorageKit/ATA.h b/src/kernel/StorageKit/ATA.h new file mode 100644 index 00000000..f92e09d3 --- /dev/null +++ b/src/kernel/StorageKit/ATA.h @@ -0,0 +1,56 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { +/// @brief ATA device interface class. +class ATADeviceInterface : public DeviceInterface { + public: + explicit ATADeviceInterface(void (*Out)(DeviceInterface*, IMountpoint* outpacket), + void (*In)(DeviceInterface*, IMountpoint* inpacket)); + + virtual ~ATADeviceInterface(); + + public: + ATADeviceInterface& operator<<(IMountpoint* Data) override; + ATADeviceInterface& operator>>(IMountpoint* Data) override; + + public: + ATADeviceInterface& operator=(const ATADeviceInterface&) = default; + ATADeviceInterface(const ATADeviceInterface&) = default; + + const Char* Name() const override; + + const UInt16& GetIO(); + Void SetIO(const UInt16& io); + + const UInt16& GetMaster(); + Void SetMaster(const UInt16& master); + + const UInt32& GetIndex(); + Void SetIndex(const UInt32& drv); + + private: + UInt32 fDriveIndex{0U}; + UInt16 fIO, fMaster{0U}; +}; + +/// @brief Initialize an PIO device (StorageKit function) +/// @param is_master is the current PIO master? +/// @return [io:master] for PIO device. +BOOL sk_init_ata_device(BOOL is_master, UInt16& io, UInt8& master); + +/// @brief Acquires a new PIO device with drv_index in mind. +/// @param drv_index The drive index to assign. +/// @return A wrapped device interface if successful, or error code. +ErrorOr sk_acquire_ata_device(Int32 drv_index); +} // namespace Kernel diff --git a/src/kernel/StorageKit/NVME.h b/src/kernel/StorageKit/NVME.h new file mode 100644 index 00000000..d1c036ab --- /dev/null +++ b/src/kernel/StorageKit/NVME.h @@ -0,0 +1,32 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include + +namespace Kernel { +class NVMEDeviceInterface final NE_DEVICE { + public: + explicit NVMEDeviceInterface(Void (*out)(DeviceInterface*, IMountpoint* out_packet), + Void (*in)(DeviceInterface*, IMountpoint* in_packet), + Void (*cleanup)(Void)); + + ~NVMEDeviceInterface() override; + + public: + NE_COPY_DEFAULT(NVMEDeviceInterface) + + const Char* Name() const override; + + public: + OwnPtr operator()(UInt32 dma_low, UInt32 dma_high, SizeT dma_sz); + + private: + Void (*fCleanup)(Void) = {nullptr}; +}; +} // namespace Kernel diff --git a/src/kernel/StorageKit/PRDT.h b/src/kernel/StorageKit/PRDT.h new file mode 100644 index 00000000..44eb11be --- /dev/null +++ b/src/kernel/StorageKit/PRDT.h @@ -0,0 +1,33 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +#define kPrdtTransferSize (sizeof(Kernel::UShort)) + +namespace Kernel { +/// @brief Tranfer information about PRD. +enum { + kPRDTTransferInProgress, + kPRDTTransferIsDone, + kPRDTTransferCount, +}; + +/// @brief Physical Region Descriptor Table. +struct PRDT final { + UInt32 fPhysAddress; + UInt32 fSectorCount; + UInt8 fEndBit; +}; + +void construct_prdt(Ref& prd); + +EXTERN_C Int32 kPRDTTransferStatus; +} // namespace Kernel diff --git a/src/kernel/StorageKit/SCSI.h b/src/kernel/StorageKit/SCSI.h new file mode 100644 index 00000000..4dad00ad --- /dev/null +++ b/src/kernel/StorageKit/SCSI.h @@ -0,0 +1,11 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include + +namespace Kernel {} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/StorageKit/StorageKit.h b/src/kernel/StorageKit/StorageKit.h new file mode 100644 index 00000000..c9633392 --- /dev/null +++ b/src/kernel/StorageKit/StorageKit.h @@ -0,0 +1,21 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#define kDriveSectorSizeHDD (512U) +#define kDriveSectorSizeSSD (512U) +#define kDriveSectorSizeOptical (2048U) + +namespace Kernel { +template +class DeviceInterface; + +class NVMEDeviceInterface; +class AHCIDeviceInterface; +class ATADeviceInterface; +class SCSIDeviceInterface; +} // namespace Kernel diff --git a/src/kernel/SwapKit/DiskSwap.h b/src/kernel/SwapKit/DiskSwap.h new file mode 100644 index 00000000..cd81b13a --- /dev/null +++ b/src/kernel/SwapKit/DiskSwap.h @@ -0,0 +1,72 @@ + +/* ======================================== + + Copyright (C) 2024-2025 Amlal El Mahrouss , licensed under the Apache 2.0 license. + +======================================== */ + +#pragma once + +#include +#include +#include + +#define kSwapBlockMaxSize (mib_cast(16)) +#define kSwapPageFilePath "/boot/pagefile.sys" + +/// @file SwapDisk.h +/// @brief Virtual memory swap disk. + +namespace Kernel { +struct SWAP_DISK_HEADER; +class DiskSwapInterface; + +/// @brief Virtual memory interface to swap memory chunks onto disk. +/// @note The class only supports the NeFS as of right now, as it is using forks to write data into +/// disk. +class DiskSwapInterface final { + public: + explicit DiskSwapInterface() = default; + ~DiskSwapInterface() = default; + + NE_COPY_DELETE(DiskSwapInterface) + NE_MOVE_DELETE(DiskSwapInterface) + + public: + /***********************************************************************************/ + /// @brief Write memory chunk onto disk. + /// @param name The swap name to recognize this memory region. + /// @param name_len length of fork name. + /// @param data the data packet. + /// @return Whether the swap was written to disk, or not. + /***********************************************************************************/ + BOOL Write(const Char* name, SizeT name_len, SWAP_DISK_HEADER* data); + + /***********************************************************************************/ + /// @brief Read memory chunk from disk. + /// @param name The swap name to recognize this memory region. + /// @param name_len length of fork name. + /// @param data the data packet length. + /// @return Whether the swap was fetched to disk, or not. + /***********************************************************************************/ + _Output SWAP_DISK_HEADER* Read(const Char* name, SizeT name_len, SizeT data_len); +}; + +/// @brief Swap disk header, containing information about the held virtual memory. +/// @param fMagic Ident number. +/// @param fHeaderSz This header size. +/// @param fTeamID Process Team ID. +/// @param fProcessID Process ID. +/// @param fVirtualAddress Virtual address pointed by data. +/// @param fBlobSz Blob's size. +/// @param fBlob Data blob. +typedef struct SWAP_DISK_HEADER { + UInt32 fMagic; + SizeT fHeaderSz; + UInt64 fTeamID; + UInt64 fProcessID; + UInt64 fVirtualAddress; + SizeT fBlobSz; + UInt8 fBlob[1]; +} PACKED ALIGN(8) SWAP_DISK_HEADER; +} // namespace Kernel diff --git a/src/kernel/amd64-ci.make b/src/kernel/amd64-ci.make new file mode 100644 index 00000000..203d0dc9 --- /dev/null +++ b/src/kernel/amd64-ci.make @@ -0,0 +1,77 @@ +################################################## +# (c) Amlal El Mahrouss, licensed under the Apache 2.0 license. +# This is the NeKernel's makefile. +################################################## + +CXX = x86_64-w64-mingw32-g++ +LD = x86_64-w64-mingw32-ld +CCFLAGS = -fshort-wchar -c -D__NE_AMD64__ -D__NEOSKRNL__ -D__NE_VEPM__ -Werror -Wall -Wpedantic -Wextra -mno-red-zone -fno-rtti -fno-exceptions -std=c++20 -D__NE_SUPPORT_NX__ -O0 -I../vendor -D__FSKIT_INCLUDES_NEFS__ -D__NEKERNEL__ -D__HAVE_NE_APIS__ -D__FREESTANDING__ -D__NE_VIRTUAL_MEMORY_SUPPORT__ -D__NE_AUTO_FORMAT__ -D__NE__ -I./ -I../ -I../boot + +ASM = nasm + +DISK_DRV = + +ifneq ($(ATA_PIO_SUPPORT), ) +DISK_DRV = -D__ATA_PIO__ +endif + +ifneq ($(ATA_DMA_SUPPORT), ) +DISK_DRV = -D__ATA_DMA__ +endif + +ifneq ($(AHCI_SUPPORT), ) +DISK_DRV = -D__AHCI__ +endif + +DEBUG_MACRO = -D__DEBUG__ + +COPY = cp + +# Add assembler, linker, and object files variables. +ASMFLAGS = -f win64 + +# Kernel subsystem is 17 and entrypoint is hal_init_platform +LDFLAGS = -e hal_init_platform --subsystem=17 --image-base 0x4000000 +LDOBJ = obj/*.obj + +# This file is the Kernel, responsible of task, memory, driver, sci, disk and device management. +KERNEL_IMG = ne_kernel + +.PHONY: error +error: + @echo "=== ERROR ===" + @echo "=> Use a specific target." + +MOVEALL=./move-all-x64.sh +WINDRES=x86_64-w64-mingw32-windres + +.PHONY: nekernel-amd64-epm +nekernel-amd64-epm: clean + $(WINDRES) kernel_rsrc.rsrc -O coff -o kernel_rsrc.obj + $(CXX) $(CCFLAGS) $(DISK_DRV) $(DEBUG_MACRO) $(wildcard src/*.cc) $(wildcard src/Gfx/*.cc) $(wildcard HALKit/AMD64/PCI/*.cc) $(wildcard src/Network/*.cc) $(wildcard src/Storage/*.cc) $(wildcard src/FS/*.cc) $(wildcard HALKit/AMD64/Storage/*.cc) $(wildcard HALKit/AMD64/*.cc) $(wildcard src/Swap/*.cc) $(wildcard HALKit/AMD64/*.s) + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalInterruptAPI.asm + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalCommonAPI.asm + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalHandoverStub.asm + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalUtilsAPI.asm + $(MOVEALL) + +OBJCOPY=x86_64-w64-mingw32-objcopy + +.PHONY: link-amd64-epm +link-amd64-epm: + $(LD) $(LDFLAGS) $(LDOBJ) -o $(KERNEL_IMG) + +.PHONY: all +all: nekernel-amd64-epm link-amd64-epm + @echo "Kernel => OK." + +.PHONY: help +help: + @echo "=== HELP ===" + @echo "all: Build Kernel and link it." + @echo "link-amd64-epm: Link Kernel for EPM based disks." + @echo "nekernel-amd64-epm: Build Kernel for EPM based disks." + +.PHONY: clean +clean: + rm -f $(LDOBJ) $(wildcard *.o) $(KERNEL_IMG) diff --git a/src/kernel/amd64-desktop.make b/src/kernel/amd64-desktop.make new file mode 100644 index 00000000..081bf75b --- /dev/null +++ b/src/kernel/amd64-desktop.make @@ -0,0 +1,79 @@ +################################################## +# (c) Amlal El Mahrouss, licensed under the Apache 2.0 license. +# This is the NeKernel's makefile. +################################################## + +CXX = x86_64-w64-mingw32-g++ +LD = x86_64-w64-mingw32-ld +CCFLAGS = -fshort-wchar -c -D__NE_AMD64__ -D__NEOSKRNL__ -D__NE_VEPM__ -Wall -Wpedantic -Wextra -mno-red-zone -fno-rtti -fno-exceptions -std=c++20 -D__FSKIT_INCLUDES_OPENHEFS__ -D__FSKIT_INCLUDES_EXT2__ -D__NE_SUPPORT_NX__ -O0 -I../vendor -D__NEKERNEL__ -D__HAVE_NE_APIS__ -D__FREESTANDING__ -D__NE_VIRTUAL_MEMORY_SUPPORT__ -D__NE_AUTO_FORMAT__ -D__NE__ -I./ -I../ -I../boot + +ASM = nasm + +DISK_DRV = + +ifneq ($(ATA_PIO_SUPPORT), ) +DISK_DRV = -D__ATA_PIO__ +endif + +ifneq ($(ATA_DMA_SUPPORT), ) +DISK_DRV = -D__ATA_DMA__ +endif + +ifneq ($(AHCI_SUPPORT), ) +DISK_DRV = -D__AHCI__ +endif + +ifneq ($(DEBUG_SUPPORT), ) +DEBUG_MACRO = -D__DEBUG__ +endif + +COPY = cp + +# Add assembler, linker, and object files variables. +ASMFLAGS = -f win64 + +# Kernel subsystem is 17 and entrypoint is hal_init_platform +LDFLAGS = -e hal_init_platform --subsystem=17 --image-base 0x10000000 +LDOBJ = obj/*.obj + +# This file is the Kernel, responsible of task, memory, driver, sci, disk and device management. +KERNEL_IMG = ne_kernel + +.PHONY: error +error: + @echo "=== ERROR ===" + @echo "=> Use a specific target." + +MOVEALL=./move-all-x64.sh +WINDRES=x86_64-w64-mingw32-windres + +.PHONY: nekernel-amd64-epm +nekernel-amd64-epm: clean + $(WINDRES) kernel_rsrc.rsrc -O coff -o kernel_rsrc.obj + $(CXX) $(CCFLAGS) $(DISK_DRV) $(DEBUG_MACRO) $(wildcard src/*.cc) $(wildcard src/Gfx/*.cc) $(wildcard HALKit/AMD64/Network/*.cc) $(wildcard HALKit/AMD64/PCI/*.cc) $(wildcard src/Network/*.cc) $(wildcard src/Storage/*.cc) $(wildcard src/FS/*.cc) $(wildcard HALKit/AMD64/Storage/*.cc) $(wildcard HALKit/AMD64/*.cc) $(wildcard src/Swap/*.cc) $(wildcard HALKit/AMD64/*.s) + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalInterruptAPI.asm + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalCommonAPI.asm + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalHandoverStub.asm + $(ASM) $(ASMFLAGS) HALKit/AMD64/HalUtilsAPI.asm + $(MOVEALL) + +OBJCOPY=x86_64-w64-mingw32-objcopy + +.PHONY: link-amd64-epm +link-amd64-epm: + $(LD) $(LDFLAGS) $(LDOBJ) -o $(KERNEL_IMG) + +.PHONY: all +all: nekernel-amd64-epm link-amd64-epm + @echo "Kernel => OK." + +.PHONY: help +help: + @echo "=== HELP ===" + @echo "all: Build Kernel and link it." + @echo "link-amd64-epm: Link Kernel for EPM based disks." + @echo "nekernel-amd64-epm: Build Kernel for EPM based disks." + +.PHONY: clean +clean: + rm -f $(LDOBJ) $(wildcard *.o) $(KERNEL_IMG) diff --git a/src/kernel/arm64-desktop.make b/src/kernel/arm64-desktop.make new file mode 100644 index 00000000..f5228d6b --- /dev/null +++ b/src/kernel/arm64-desktop.make @@ -0,0 +1,64 @@ +################################################## +# (c) Amlal El Mahrouss, licensed under the Apache 2.0 license. +# This is the microKernel makefile. +################################################## + +CC = clang++ +LD = lld-link +CCFLAGS = -fshort-wchar -c -ffreestanding -MMD -mno-red-zone -D__NE_ARM64__ -fno-rtti -fno-exceptions -I./ \ + -target aarch64-unknown-windows \ + -std=c++20 -O3 -D__NEKERNEL__ -D__NEOSKRNL__ -D__NE_VEPM__ -D__NE_MINIMAL_OS__ -D__NE_NO_BUILTIN__ -D__HAVE_NE_APIS__ -D__NE__ -I../ + +ASM = clang++ + +DISKDRIVER = -D__USE_FLASH_MEM__ + +ifneq ($(DEBUG_SUPPORT), ) +DEBUG = -D__DEBUG__ +endif + +COPY = cp + +LDFLAGS = -subsystem:efi_application -entry:hal_init_platform /nodefaultlib +LDOBJ = obj/*.obj + +# This file is the Kernel, responsible of task management and memory. +KERNEL = ne_kernel + +.PHONY: error +error: + @echo "=== ERROR ===" + @echo "=> Use a specific target." + +MOVEALL=./move-all-aarch64.sh + +.PHONY: nekernel-arm64-epm +nekernel-arm64-epm: clean + $(CC) $(CCFLAGS) $(DISKDRIVER) $(DEBUG) $(wildcard src/*.cc) \ + $(wildcard src/FS/*.cc) $(wildcard HALKit/ARM64/Storage/*.cc) \ + $(wildcard HALKit/ARM64/PCI/*.cc) $(wildcard src/Network/*.cc) $(wildcard src/Storage/*.cc) \ + $(wildcard HALKit/ARM64/*.cc) $(wildcard HALKit/ARM64/*.cc) \ + $(wildcard HALKit/ARM64/*.s) $(wildcard HALKit/ARM64/APM/*.cc) + + $(MOVEALL) + +OBJCOPY=x86_64-w64-mingw32-objcopy + +.PHONY: link-arm64-epm +link-arm64-epm: + $(LD) $(LDFLAGS) $(LDOBJ) /out:$(KERNEL) + +.PHONY: all +all: nekernel-arm64-epm link-arm64-epm + @echo "Kernel => OK." + +.PHONY: help +help: + @echo "=== HELP ===" + @echo "all: Build Kernel and link it." + @echo "link-arm64-epm: Link Kernel for EPM based disks." + @echo "nekernel-arm64-epm: Build Kernel for EPM based disks." + +.PHONY: clean +clean: + rm -f $(LDOBJ) $(wildcard *.o) $(KERNEL) diff --git a/src/kernel/kernel_rsrc.rsrc b/src/kernel/kernel_rsrc.rsrc new file mode 100644 index 00000000..0a43086e --- /dev/null +++ b/src/kernel/kernel_rsrc.rsrc @@ -0,0 +1,25 @@ +#include "CompilerKit/Version.h" + +1 VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904E4" + BEGIN + VALUE "CompanyName", "Amlal El Mahrouss" + VALUE "FileDescription", "NeKernel" + VALUE "FileVersion", KERNEL_VERSION + VALUE "InternalName", "ne_kernel" + VALUE "LegalCopyright", "(c) 2024-2025 Amlal El Mahrouss, licensed under the Apache 2.0 license." + VALUE "OriginalFilename", "ne_kernel" + VALUE "ProductName", "NeKernel" + VALUE "ProductVersion", KERNEL_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1252 + END +END diff --git a/src/kernel/move-all-aarch64.sh b/src/kernel/move-all-aarch64.sh new file mode 100755 index 00000000..35e0909e --- /dev/null +++ b/src/kernel/move-all-aarch64.sh @@ -0,0 +1,7 @@ +#! /bin/sh + +for file in *.o; do + mv -- "$file" "${file%.o}.obj" +done + +mv *.obj obj/ diff --git a/src/kernel/move-all-x64.sh b/src/kernel/move-all-x64.sh new file mode 100755 index 00000000..1c135d06 --- /dev/null +++ b/src/kernel/move-all-x64.sh @@ -0,0 +1,7 @@ +#! /bin/sh + +for file in *.o; do + mv -- "$file" "${file%.o}.obj" +done + +mv *.obj HALKit/AMD64/*.obj obj/ diff --git a/src/kernel/obj/.gitkeep b/src/kernel/obj/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/power64-cb.make b/src/kernel/power64-cb.make new file mode 100644 index 00000000..c46a00d0 --- /dev/null +++ b/src/kernel/power64-cb.make @@ -0,0 +1,4 @@ +################################################## +# (c) Amlal El Mahrouss, licensed under the Apache 2.0 license. +# This is the microKernel makefile. +################################################## diff --git a/src/kernel/riscv64-cb.make b/src/kernel/riscv64-cb.make new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/src/ACPIFactoryInterface.cc b/src/kernel/src/ACPIFactoryInterface.cc new file mode 100644 index 00000000..42819b7e --- /dev/null +++ b/src/kernel/src/ACPIFactoryInterface.cc @@ -0,0 +1,88 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include + +namespace Kernel { +constexpr STATIC const auto kMinACPIVer = 1U; + +/// @brief Finds a descriptor table inside ACPI XSDT. +ErrorOr ACPIFactoryInterface::Find(const Char* signature) { + if (this->fRsdp) return ErrorOr{-kErrorInvalidData}; + if (!signature) return ErrorOr{-kErrorInvalidData}; + if (*signature == 0) return ErrorOr{-kErrorInvalidData}; + + RSDP* rsp_ptr = reinterpret_cast(this->fRsdp); + + if (rsp_ptr->Revision < kMinACPIVer) return ErrorOr{-kErrorInvalidData}; + + RSDT* xsdt = reinterpret_cast(rsp_ptr->RsdtAddress); + + Int64 num = (xsdt->Length - sizeof(SDT)) / sizeof(Int64); + + /*** + crucial to avoid underflows. + */ + if (num < 1) { + /// stop here, we should have entries... + ke_panic(RUNTIME_CHECK_ACPI); + return ErrorOr{-kErrorInvalidData}; + } + + this->fEntries = num; + + (Void)(kout << "ACPI: Number of entries: " << number(this->fEntries) << kendl); + (Void)(kout << "ACPI: Revision: " << number(xsdt->Revision) << kendl); + (Void)(kout << "ACPI: Signature: " << xsdt->Signature << kendl); + (Void)(kout << "ACPI: Address of XSDT: " << hex_number((UIntPtr) xsdt) << kendl); + + static constexpr const UInt16 cAcpiSignatureLength = 4U; + + for (Size index = 0; index < this->fEntries; ++index) { + SDT* sdt = reinterpret_cast(xsdt->AddressArr[index]); + + (Void)(kout << "ACPI: Checksum: " << number(sdt->Checksum) << kendl); + (Void)(kout << "ACPI: Revision: " << number(sdt->Revision) << kendl); + + for (UInt16 signature_index = 0; signature_index < cAcpiSignatureLength; ++signature_index) { + if (sdt->Signature[signature_index] != signature[signature_index]) break; + + if (signature_index == (cAcpiSignatureLength - 1)) { + (Void)(kout << "ACPI: SDT Signature: " << sdt->Signature << kendl); + (Void)(kout << "ACPI: SDT OEM ID: " << sdt->OemId << kendl); + return ErrorOr(reinterpret_cast(xsdt->AddressArr[index])); + } + } + } + + return ErrorOr{-kErrorInvalidData}; +} + +/*** +@brief Checksum on SDT header. +@param checksum the header to checksum +@param len the length of it. +*/ +bool ACPIFactoryInterface::Checksum(const Char* checksum, SSizeT len) { + if (len == 0 || !checksum) return NO; + + Char chr = 0; + + for (SSizeT index = 0L; index < len; ++index) { + chr += checksum[index]; + } + + return chr == 0; +} + +ErrorOr ACPIFactoryInterface::operator[](const Char* signature) { + if (!signature) return ErrorOr{-kErrorInvalidData}; + return this->Find(signature); +} +} // namespace Kernel diff --git a/src/kernel/src/Array.cc b/src/kernel/src/Array.cc new file mode 100644 index 00000000..c792ef93 --- /dev/null +++ b/src/kernel/src/Array.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/src/ArrayList.cc b/src/kernel/src/ArrayList.cc new file mode 100644 index 00000000..37042320 --- /dev/null +++ b/src/kernel/src/ArrayList.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/src/AsciiUtils.cc b/src/kernel/src/AsciiUtils.cc new file mode 100644 index 00000000..1f1ab0f6 --- /dev/null +++ b/src/kernel/src/AsciiUtils.cc @@ -0,0 +1,161 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel { +Int32 rt_string_cmp(const Char* src, const Char* cmp, Size size) { + for (Size i = 0; i < size; ++i) { + if (src[i] != cmp[i]) return static_cast(src[i]) - static_cast(cmp[i]); + } + return 0; +} + +SizeT rt_string_len(const Char* str, SizeT max_len) { + SizeT len = 0; + while (len < max_len && str[len] != '\0') ++len; + return len; +} + +Size rt_string_len(const Char* ptr) { + Size cnt = 0; + while (ptr[cnt] != '\0') ++cnt; + return cnt; +} + +const Char* rt_alloc_string(const Char* src) { + SizeT slen = rt_string_len(src); + Char* buffer = new Char[slen + 1]; + if (!buffer) return nullptr; + + if (rt_copy_memory_safe(reinterpret_cast(const_cast(src)), + reinterpret_cast(buffer), slen, slen + 1) < 0) { + delete[] buffer; + return nullptr; + } + + buffer[slen] = '\0'; + return buffer; +} + +Int32 rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size) { + if (!src || !dst || len > dst_size) { + if (dst && dst_size) { + rt_set_memory_safe(dst, 0, dst_size, dst_size); + } + return -1; + } + + auto s = reinterpret_cast(src); + auto d = reinterpret_cast(dst); + + for (Size i = 0; i < len; ++i) d[i] = s[i]; + + return static_cast(len); +} + +voidPtr rt_set_memory_safe(voidPtr dst, UInt32 value, Size len, Size dst_size) { + if (!dst || len > dst_size) return nullptr; + auto p = reinterpret_cast(dst); + UInt8 v = static_cast(value & 0xFF); + for (Size i = 0; i < len; ++i) p[i] = v; + return dst; +} + +Void rt_zero_memory(voidPtr pointer, Size len) { + rt_set_memory_safe(pointer, 0, len, len); +} + +#ifdef __NE_ENFORCE_DEPRECATED_WARNINGS +[[deprecated("Use rt_set_memory_safe instead")]] +#endif +voidPtr +rt_set_memory(voidPtr src, UInt32 value, Size len) { + if (!src) return nullptr; + auto p = reinterpret_cast(src); + UInt8 v = static_cast(value & 0xFF); + for (Size i = 0; i < len; ++i) p[i] = v; + return src; +} + +#ifdef __NE_ENFORCE_DEPRECATED_WARNINGS +[[deprecated("Use rt_copy_memory_safe instead")]] +#endif +Int32 rt_copy_memory(const voidPtr src, voidPtr dst, Size len) { + if (!src || !dst) return -1; + auto s = reinterpret_cast(src); + auto d = reinterpret_cast(dst); + + for (Size i = 0; i < len; ++i) d[i] = s[i]; + + return static_cast(len); +} + +Int32 rt_to_uppercase(Int ch) { + return (ch >= 'a' && ch <= 'z') ? ch - 0x20 : ch; +} + +Int32 rt_to_lower(Int ch) { + return (ch >= 'A' && ch <= 'Z') ? ch + 0x20 : ch; +} + +Int32 rt_is_alnum(Int ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'); +} + +Boolean rt_is_space(Int ch) { + return ch == ' '; +} + +Boolean rt_is_newln(Int ch) { + return ch == '\n'; +} + +Char rt_to_char(UInt64 value, Int32 base) { + static constexpr Char kDigits[] = "0123456789ABCDEF"; + return kDigits[value % base]; +} + +Bool rt_to_string(Char* str, UInt64 value, Int32 base) { + if (!str || base < 2 || base > 16) return NO; + + Int i = 0; + do { + str[i++] = rt_to_char(value, base); + value /= base; + } while (value); + str[i] = '\0'; + // in-place + for (Int j = 0; j < i / 2; ++j) { + Char tmp = str[j]; + str[j] = str[i - j - 1]; + str[i - j - 1] = tmp; + } + + return YES; +} + +VoidPtr rt_string_in_string(const Char* haystack, const Char* needle) { + if (!haystack || !needle) return nullptr; + + SizeT needle_len = rt_string_len(needle); + SizeT hay_len = rt_string_len(haystack); + + if (needle_len > hay_len) return nullptr; + for (SizeT i = 0; i <= hay_len - needle_len; ++i) { + if (rt_string_cmp(haystack + i, needle, needle_len) == 0) { + return reinterpret_cast(const_cast(haystack + i)); + } + } + return nullptr; +} + +Char* rt_string_has_char(Char* str, Char ch) { + if (!str) return nullptr; + while (*str && *str != ch) ++str; + return (*str == ch) ? str : nullptr; +} +} // namespace Kernel diff --git a/src/kernel/src/Atom.cc b/src/kernel/src/Atom.cc new file mode 100644 index 00000000..c6c1dd40 --- /dev/null +++ b/src/kernel/src/Atom.cc @@ -0,0 +1,10 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +// @file Atom.cc +// @brief Atomic primitives diff --git a/src/kernel/src/BinaryMutex.cc b/src/kernel/src/BinaryMutex.cc new file mode 100644 index 00000000..3c332b33 --- /dev/null +++ b/src/kernel/src/BinaryMutex.cc @@ -0,0 +1,70 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief Unlocks the binary mutex. +/***********************************************************************************/ + +Bool BinaryMutex::Unlock() noexcept { + if (fLockingProcess->Status == ProcessStatusKind::kRunning) { + fLockingProcess = nullptr; + + return Yes; + } + + return No; +} + +/***********************************************************************************/ +/// @brief Locks process in the binary mutex. +/***********************************************************************************/ + +Bool BinaryMutex::Lock(USER_PROCESS* process) { + if (!process || this->IsLocked()) return No; + + this->fLockingProcess = process; + + return Yes; +} + +/***********************************************************************************/ +/// @brief Checks if process is locked. +/***********************************************************************************/ + +Bool BinaryMutex::IsLocked() const { + return this->fLockingProcess->Status == ProcessStatusKind::kRunning; +} + +/***********************************************************************************/ +/// @brief Try lock or wait. +/***********************************************************************************/ + +Bool BinaryMutex::LockAndWait(USER_PROCESS* process, TimerInterface* timer) { + if (timer == nullptr) return No; + + this->Lock(process); + + timer->Wait(); + + return this->Lock(process); +} + +/***********************************************************************************/ +/// @brief Wait for process **sec** until we check if it's free. +/// @param sec seconds. +/***********************************************************************************/ + +BOOL BinaryMutex::WaitForProcess(const UInt32& sec) noexcept { + HardwareTimer hw_timer(rtl_milliseconds(sec)); + hw_timer.Wait(); + + return !this->IsLocked(); +} +} // namespace Kernel diff --git a/src/kernel/src/BitMapMgr.cc b/src/kernel/src/BitMapMgr.cc new file mode 100644 index 00000000..d7ecb810 --- /dev/null +++ b/src/kernel/src/BitMapMgr.cc @@ -0,0 +1,206 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifdef __NE_AMD64__ +#include +#elif defined(__NE_ARM64__) +#include +#endif + +#include +#include + +#define kBitMapMagic (0x10210U) + +#define kBitMapMagIdx (0U) +#define kBitMapSizeIdx (1U) +#define kBitMapUsedIdx (2U) + +///! @author Amlal El Mahrouss (amlal@nekernel.org) + +namespace Kernel { +namespace HAL { + namespace Detail { + /***********************************************************************************/ + /// \brief Proxy Interface to manage a bitmap allocator. + /***********************************************************************************/ + class IBitMapProxy final { + public: + explicit IBitMapProxy() = default; + ~IBitMapProxy() = default; + + NE_COPY_DELETE(IBitMapProxy) + + auto IsBitMap(VoidPtr page_ptr) -> Bool { + if (!page_ptr) return No; + + UIntPtr* ptr_bit_set = reinterpret_cast(page_ptr); + + if (!ptr_bit_set[kBitMapMagIdx] || ptr_bit_set[kBitMapMagIdx] != kBitMapMagic) return No; + + return Yes; + } + + auto FreeBitMap(VoidPtr page_ptr) -> Bool { + if (this->IsBitMap(page_ptr) == No) return No; + + UIntPtr* ptr_bit_set = reinterpret_cast(page_ptr); + + kBitMapCursor += ptr_bit_set[kBitMapSizeIdx]; + + ptr_bit_set[kBitMapMagIdx] = kBitMapMagic; + ptr_bit_set[kBitMapUsedIdx] = No; + + this->GetBitMapStatus(ptr_bit_set); + + return Yes; + } + + UInt32 MakeMMFlags(Bool wr, Bool user) { + UInt32 flags = kMMFlagsPresent; + + if (wr) flags |= kMMFlagsWr; + if (user) flags |= kMMFlagsUser; + + return flags; + } + + /***********************************************************************************/ + /// @brief Iterate over availables bitmap, until we find a free entry. + /// @param base_ptr base pointer to look on. + /// @param size the size of the requested data structure. + /// @param wr is writable flag? + /// @param user is user flag? + /// @param pad additional padding added to **size** + /// @return The new free address, or nullptr. + /***********************************************************************************/ + auto FindBitMap(VoidPtr base_ptr, SizeT size, Bool wr, Bool user, SizeT pad) -> VoidPtr { + if (!size) return nullptr; + + if (kBitMapCursor > kKernelBitMpSize) { + err_global_get() = kErrorOutOfBitMapMemory; + + (Void)(kout << "Bitmap limit reached, can't allocate more bitmaps." << kendl); + return nullptr; + } + + VoidPtr base = reinterpret_cast((UIntPtr) base_ptr); + MUST_PASS(base); + + if (!base) return nullptr; + + STATIC SizeT biggest = 0UL; + + while (YES) { + UIntPtr* ptr_bit_set = reinterpret_cast(base); + + if (ptr_bit_set[kBitMapMagIdx] == kBitMapMagic && + ptr_bit_set[kBitMapSizeIdx] == (size + pad)) { + if (ptr_bit_set[kBitMapUsedIdx] == No) { + ptr_bit_set[kBitMapSizeIdx] = size + pad; + ptr_bit_set[kBitMapUsedIdx] = Yes; + + this->GetBitMapStatus(ptr_bit_set); + + UInt32 flags = this->MakeMMFlags(wr, user); + mm_map_page(ptr_bit_set, (VoidPtr)mm_get_page_addr(ptr_bit_set), flags); + + if (biggest < (size + pad)) biggest = size + pad; + + kBitMapCursor += size + pad; + + return (VoidPtr) ptr_bit_set; + } + } else if (ptr_bit_set[kBitMapMagIdx] != kBitMapMagic) { + ptr_bit_set[kBitMapMagIdx] = kBitMapMagic; + ptr_bit_set[kBitMapSizeIdx] = (size + pad); + ptr_bit_set[kBitMapUsedIdx] = Yes; + + this->GetBitMapStatus(ptr_bit_set); + + UInt32 flags = this->MakeMMFlags(wr, user); + mm_map_page(ptr_bit_set, (VoidPtr)mm_get_page_addr(ptr_bit_set), flags); + + if (biggest < (size + pad)) biggest = (size + pad); + + kBitMapCursor += size + pad; + + return (VoidPtr) ptr_bit_set; + } + + UIntPtr raw_base = reinterpret_cast(base); + UIntPtr offset = (ptr_bit_set[kBitMapMagIdx] != kBitMapMagic) + ? (size + pad) + : ptr_bit_set[kBitMapSizeIdx]; + + base = reinterpret_cast(raw_base + offset); + if (base == nullptr) return nullptr; + } + + return nullptr; + } + + /// @brief Print Bitmap status + auto GetBitMapStatus(UIntPtr* ptr_bit_set) -> Void { + if (!this->IsBitMap(ptr_bit_set)) { + (Void)(kout << "Not a BitMap: " << hex_number((UIntPtr) ptr_bit_set) << kendl); + return; + } + + (Void)(kout << "Magic: " << hex_number(ptr_bit_set[kBitMapMagIdx]) << kendl); + (Void)(kout << "Is Allocated? " << (ptr_bit_set[kBitMapUsedIdx] ? "YES" : "NO") << kendl); + (Void)(kout << "Size of BitMap (B): " << number(ptr_bit_set[kBitMapSizeIdx]) << kendl); + (Void)(kout << "Size of BitMap (KIB): " << number(KIB(ptr_bit_set[kBitMapSizeIdx])) + << kendl); + (Void)(kout << "Size of BitMap (MIB): " << number(MIB(ptr_bit_set[kBitMapSizeIdx])) + << kendl); + (Void)(kout << "Size of BitMap (GIB): " << number(GIB(ptr_bit_set[kBitMapSizeIdx])) + << kendl); + (Void)(kout << "Size of BitMap (TIB): " << number(TIB(ptr_bit_set[kBitMapSizeIdx])) + << kendl); + (Void)(kout << "BitMap Address: " << hex_number((UIntPtr) ptr_bit_set) << kendl); + } + }; + } // namespace Detail + + STATIC Detail::IBitMapProxy kBitMapMgr; + + auto mm_is_bitmap(VoidPtr ptr) -> BOOL { + return kBitMapMgr.IsBitMap(ptr); + } + + /***********************************************************************************/ + /// @brief Allocate a new page to be used by the OS. + /// @param wr read/write bit. + /// @param user user bit. + /// @return a new bitmap allocated pointer. + /***********************************************************************************/ + auto mm_alloc_bitmap(Boolean wr, Boolean user, SizeT size, Bool is_page, SizeT pad) -> VoidPtr { + VoidPtr ptr_new = nullptr; + if (is_page) return nullptr; + + ptr_new = kBitMapMgr.FindBitMap(kKernelBitMpStart, size, wr, user, pad); + + if (!ptr_new) { + ke_panic(RUNTIME_CHECK_VIRTUAL_OUT_OF_MEM, "Out of memory bitmap"); + return nullptr; + } + + return ptr_new; + } + + /***********************************************************************************/ + /// @brief Free Bitmap, and mark it as absent. + /// @param ptr the pointer to free. + /***********************************************************************************/ + auto mm_free_bitmap(VoidPtr ptr) -> Bool { + if (!ptr) return No; + + Bool ret = kBitMapMgr.FreeBitMap(ptr); + return ret; + } +} // namespace HAL +} // namespace Kernel diff --git a/src/kernel/src/CRuntimeOverrides.cc b/src/kernel/src/CRuntimeOverrides.cc new file mode 100644 index 00000000..a773d4f9 --- /dev/null +++ b/src/kernel/src/CRuntimeOverrides.cc @@ -0,0 +1,27 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; + +/// =========================================================== /// +/// @brief C Standard Library overrides. /// +/// =========================================================== /// + +EXTERN_C void* memset(void* dst, int c, long long unsigned int len) { + return Kernel::rt_set_memory_safe(dst, c, static_cast(len), static_cast(len)); +} + +EXTERN_C void* memcpy(void* dst, const void* src, long long unsigned int len) { + Kernel::rt_copy_memory_safe(const_cast(src), dst, static_cast(len), + static_cast(len)); + return dst; +} + +EXTERN_C Int32 strcmp(const char* a, const char* b) { + return Kernel::rt_string_cmp(a, b, rt_string_len(a)); +} diff --git a/src/kernel/src/CodeMgr.cc b/src/kernel/src/CodeMgr.cc new file mode 100644 index 00000000..7b7add6d --- /dev/null +++ b/src/kernel/src/CodeMgr.cc @@ -0,0 +1,35 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +namespace Kernel { + +/// @brief Executes a new process from a function. Kernel code only. +/// @note This sets up a new stack, anything on the main function that calls the Kernel will not be +/// accessible. +/// @param main the start of the process. +/// @return The team's process id. +BOOL rtl_create_kernel_task(HAL::StackFramePtr task, const KID kid) noexcept { + if (!kid || task == nullptr) return FALSE; + return KernelTaskHelper::Add(task, kid); +} + +/***********************************************************************************/ +/// @brief Executes a new process from a function. Kernel code only. +/// @note This sets up a new stack, anything on the main function that calls the Kernel will not be +/// accessible. +/// @param main the start of the process. +/// @return if the process was started or not. +/***********************************************************************************/ + +ProcessID rtl_create_user_process(rtl_main_kind main, const Char* process_name) noexcept { + if (!process_name || *process_name == 0) return kSchedInvalidPID; + return UserProcessScheduler::The().Spawn(process_name, reinterpret_cast(main), nullptr); +} +} // namespace Kernel diff --git a/src/kernel/src/Crc32.cc b/src/kernel/src/Crc32.cc new file mode 100644 index 00000000..88a9b73a --- /dev/null +++ b/src/kernel/src/Crc32.cc @@ -0,0 +1,65 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +#define kCrcCnt (256) + +// @file CRC32.cc +// @brief Check sequence implementation. + +namespace Kernel { +/// @brief The CRC32 seed table. +UInt32 kChecksumPolys[kCrcCnt] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +/// @brief Calculate CRC32 of p +/// @param in the data to compute. +/// @param len the length of the data. +/// @return CRC32 of **in**. +UInt32 ke_calculate_crc32(const VoidPtr inp, Int32 len) noexcept { + if (!inp) return ~0; + + UInt32 crc = 0xffffffff; + + Char* in = static_cast(inp); + + while ((len--) > 0) crc = (crc >> 8) ^ kChecksumPolys[(crc ^ *(in++)) & 0xFF]; + + return ~crc; +} +} // namespace Kernel diff --git a/src/kernel/src/Defines.cc b/src/kernel/src/Defines.cc new file mode 100644 index 00000000..1ae6f12f --- /dev/null +++ b/src/kernel/src/Defines.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/src/DeviceMgr.cc b/src/kernel/src/DeviceMgr.cc new file mode 100644 index 00000000..b392a4ee --- /dev/null +++ b/src/kernel/src/DeviceMgr.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel {} // namespace Kernel diff --git a/src/kernel/src/DriveMgr.cc b/src/kernel/src/DriveMgr.cc new file mode 100644 index 00000000..8f2611a4 --- /dev/null +++ b/src/kernel/src/DriveMgr.cc @@ -0,0 +1,254 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/***********************************************************************************/ +/// @file DriveMgr.cc +/// @brief Drive Manager of NeKernel. +///! @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +namespace Kernel { +#if defined(__ATA_PIO__) || defined(__ATA_DMA__) +STATIC UInt16 kATAIO = 0U; +STATIC UInt8 kATAMaster = 0U; +#endif + +#if defined(__AHCI__) +STATIC UInt16 kAHCIPortsImplemented [[maybe_unused]] = 0UL; +#endif + +/// @brief reads from an ATA drive. +/// @param pckt Packet structure (fPacketContent must be non null) +/// @return +Void io_drv_input(DriveTrait::DrivePacket& pckt) { +#ifdef __AHCI__ + drv_std_read(pckt.fPacketLba, (Char*) pckt.fPacketContent, kAHCISectorSize, pckt.fPacketSize); + + if (err_global_get() != kErrorSuccess) pckt.fPacketGood = NO; +#elif defined(__ATA_PIO__) || defined(__ATA_DMA__) + drv_std_read(pckt.fPacketLba, kATAIO, kATAMaster, (Char*) pckt.fPacketContent, kATASectorSize, + pckt.fPacketSize); +#endif +} + +/// @brief Writes to an ATA drive. +/// @param pckt the packet to write. +/// @return +Void io_drv_output(DriveTrait::DrivePacket& pckt) { + if (pckt.fPacketReadOnly) { + pckt.fPacketGood = NO; + return; + } + + // nothing starts before 512 anyways, even an EPM partition. + if (!pckt.fPacketReadOnly && pckt.fPacketLba == 0) { + pckt.fPacketGood = NO; + return; + } + +#ifdef __AHCI__ + drv_std_write(pckt.fPacketLba, (Char*) pckt.fPacketContent, kAHCISectorSize, pckt.fPacketSize); + + if (err_global_get() != kErrorSuccess) pckt.fPacketGood = NO; +#elif defined(__ATA_PIO__) || defined(__ATA_DMA__) + drv_std_write(pckt.fPacketLba, kATAIO, kATAMaster, (Char*) pckt.fPacketContent, kATASectorSize, + pckt.fPacketSize); +#endif +} + +/// @brief Executes a disk check on the ATA drive. +/// @param pckt the packet to read. +/// @return +Void io_drv_init(DriveTrait::DrivePacket& pckt) { +#if defined(__ATA_PIO__) || defined(__ATA_DMA__) + kATAMaster = 0; + kATAIO = 0; + + kATAMaster = YES; + kATAIO = ATA_PRIMARY_IO; + + if (drv_std_init(kATAIO, kATAMaster, kATAIO, kATAMaster)) { + pckt.fPacketGood = YES; + return; + } + + kATAMaster = NO; + kATAIO = ATA_SECONDARY_IO; + + if (!drv_std_init(kATAIO, kATAMaster, kATAIO, kATAMaster)) { + pckt.fPacketGood = YES; + return; + } + + pckt.fPacketGood = YES; +#elif defined(__AHCI__) + kAHCIPortsImplemented = 0; + drv_std_init(kAHCIPortsImplemented); + + pckt.fPacketGood = kAHCIPortsImplemented > 0; +#endif // if defined(__ATA_PIO__) || defined (__ATA_DMA__) +} + +/// @brief Gets the drive kind (ATA, SCSI, AHCI...) +/// @param void no arguments. +/// @return no arguments. +#ifdef __ATA_PIO__ +const Char* io_drv_kind(Void) { + return "ATA-PIO"; +} +#elif defined(__ATA_DMA__) +const Char* io_drv_kind(Void) { + return "ATA-DMA"; +} +#elif defined(__AHCI__) +const Char* io_drv_kind(Void) { + return "AHCI"; +} +#else +const Char* io_drv_kind(Void) { + return "null"; +} +#endif + +/// @brief Unimplemented drive function. +/// @param pckt the packet to read. +Void io_drv_unimplemented(DriveTrait::DrivePacket& pckt) noexcept { + NE_UNUSED(pckt); +} + +/// @brief Makes a new drive. +/// @return the new blank drive. +DriveTrait io_construct_blank_drive() noexcept { + DriveTrait trait; + + constexpr auto kBlankDrive = "/media/blank/"; + + rt_copy_memory((VoidPtr) kBlankDrive, trait.fName, rt_string_len(kBlankDrive)); + trait.fKind = kInvalidDrive; + + trait.fInput = io_drv_unimplemented; + trait.fOutput = io_drv_unimplemented; + trait.fVerify = io_drv_unimplemented; + trait.fInit = io_drv_unimplemented; + trait.fProtocol = io_drv_kind; + + kout << "DriveMgr: Construct: " << trait.fName << "\r"; + + return trait; +} + +namespace Probe { + Void io_detect_drive(DriveTrait& trait) { + trait.fInit(trait.fPacket); + + EPM_PART_BLOCK block_struct; + + trait.fPacket.fPacketLba = kEPMBootBlockLba; + trait.fPacket.fPacketSize = sizeof(EPM_PART_BLOCK); + trait.fPacket.fPacketContent = &block_struct; + + rt_copy_memory((VoidPtr) "fs/detect-packet", trait.fPacket.fPacketMime, + rt_string_len("fs/detect-packet")); + + trait.fInput(trait.fPacket); + + if (rt_string_cmp(block_struct.Magic, kEPMMagic, kEPMMagicLength) == 0) { + trait.fPacket.fPacketReadOnly = NO; + trait.fKind = kMassStorageDrive | kEPMDrive; + + kout << "DriveMgr: Disk is EPM formatted.\r"; + + trait.fSectorSz = block_struct.SectorSz; + trait.fLbaEnd = block_struct.LbaEnd; + trait.fLbaStart = block_struct.LbaStart; + } else { + GPT_PARTITION_TABLE gpt_struct; + + trait.fPacket.fPacketLba = kEPMBootBlockLba; + trait.fPacket.fPacketSize = sizeof(GPT_PARTITION_TABLE); + trait.fPacket.fPacketContent = &gpt_struct; + + rt_copy_memory((VoidPtr) "fs/detect-packet", trait.fPacket.fPacketMime, + rt_string_len("fs/detect-packet")); + + trait.fInput(trait.fPacket); + + if (rt_string_cmp(gpt_struct.Signature, kMagicGPT, kMagicLenGPT) == 0) { + trait.fPacket.fPacketReadOnly = NO; + trait.fKind = kMassStorageDrive | kGPTDrive; + + kout << "DriveMgr: Disk is GPT formatted.\r"; + + trait.fSectorSz = gpt_struct.SizeOfEntries; + trait.fLbaEnd = gpt_struct.LastGPTEntry; + trait.fLbaStart = gpt_struct.FirstGPTEntry; + } else { + kout << "DriveMgr: Disk is unformatted.\r"; + + trait.fPacket.fPacketReadOnly = YES; + trait.fKind = kMassStorageDrive | kUnformattedDrive | kReadOnlyDrive; + + trait.fSectorSz = 512; + trait.fLbaEnd = drv_std_get_sector_count() - 1; + trait.fLbaStart = 0x400; + } + } + + rt_copy_memory((VoidPtr) "*/*", trait.fPacket.fPacketMime, rt_string_len("*/*")); + + trait.fPacket.fPacketLba = 0; + trait.fPacket.fPacketSize = 0UL; + trait.fPacket.fPacketContent = nullptr; + } +} // namespace Probe + +/// @brief Fetches the main drive. +/// @return the new drive. (returns kEPMDrive if EPM formatted) +DriveTrait io_construct_main_drive() noexcept { + constexpr auto kMainDrive = "/media/main/"; + + DriveTrait trait; + + rt_copy_memory((VoidPtr) kMainDrive, trait.fName, rt_string_len(kMainDrive)); + MUST_PASS(trait.fName[0] != 0); + + trait.fVerify = io_drv_unimplemented; + trait.fOutput = io_drv_output; + trait.fInput = io_drv_input; + trait.fInit = io_drv_init; + trait.fProtocol = io_drv_kind; + + Probe::io_detect_drive(trait); + + return trait; +} + +/// @brief Replacement for io_construct_main_drive that works with IMountpoint. +/// @return the new drive. (returns kEPMDrive if EPM formatted) +Void io_construct_main_drive(DriveTrait& trait) noexcept { + constexpr auto kMainDrive = "/media/main/"; + + rt_copy_memory((VoidPtr) kMainDrive, trait.fName, rt_string_len(kMainDrive)); + MUST_PASS(trait.fName[0] != 0); + + trait.fVerify = io_drv_unimplemented; + trait.fOutput = io_drv_output; + trait.fInput = io_drv_input; + trait.fInit = io_drv_init; + trait.fProtocol = io_drv_kind; + + Probe::io_detect_drive(trait); +} +} // namespace Kernel diff --git a/src/kernel/src/ErrorOr.cc b/src/kernel/src/ErrorOr.cc new file mode 100644 index 00000000..9f1b550b --- /dev/null +++ b/src/kernel/src/ErrorOr.cc @@ -0,0 +1,12 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +/***********************************************************************************/ +/// @file ErrorOr.cc /// +/// @brief ErrorOr container class. /// +/***********************************************************************************/ diff --git a/src/kernel/src/FS/Ext2+IFS.cc b/src/kernel/src/FS/Ext2+IFS.cc new file mode 100644 index 00000000..106229f7 --- /dev/null +++ b/src/kernel/src/FS/Ext2+IFS.cc @@ -0,0 +1,1555 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __NE_MINIMAL_OS__ +#ifdef __FSKIT_INCLUDES_EXT2__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr static UInt32 EXT2_DIRECT_BLOCKS = 12; +constexpr static UInt32 EXT2_SINGLE_INDIRECT_INDEX = 12; +constexpr static UInt32 EXT2_DOUBLE_INDIRECT_INDEX = 13; +constexpr ATTRIBUTE(unused) static UInt32 EXT2_TRIPLE_INDIRECT_INDEX = 14; +constexpr static UInt32 EXT2_ROOT_INODE = 2; +constexpr ATTRIBUTE(unused) static UInt32 EXT2_SUPERBLOCK_BLOCK = 1; +constexpr static UInt32 EXT2_GROUP_DESC_BLOCK_SMALL = 2; +constexpr static UInt32 EXT2_GROUP_DESC_BLOCK_LARGE = 1; + +static inline SizeT ext2_min(SizeT a, SizeT b) { + return a < b ? a : b; +} + +struct Ext2GroupInfo { + EXT2_GROUP_DESCRIPTOR* groupDesc; + UInt32 groupDescriptorBlock; + UInt32 offsetInGroupDescBlock; + UInt8* blockBuffer; +}; + +// Convert EXT2 block number -> LBA (sector index) for Drive I/O. +static inline UInt32 ext2_block_to_lba(Ext2Context* ctx, UInt32 blockNumber) { + if (!ctx || !ctx->drive) return 0; + UInt32 blockSize = ctx->BlockSize(); + UInt32 sectorSize = ctx->drive->fSectorSz; + UInt32 sectorsPerBlock = blockSize / sectorSize; + return blockNumber * sectorsPerBlock; +} + +// Read a block and return a pointer to its content +static ErrorOr ext2_read_block_ptr(Ext2Context* ctx, UInt32 blockNumber) { + if (!ctx || !ctx->drive || !ctx->superblock) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + auto buf = (UInt32*) mm_alloc_ptr(blockSize, true, false); + if (!buf) return ErrorOr(kErrorHeapOutOfMemory); + + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { + mm_free_ptr(buf); + return ErrorOr(kErrorDisk); + } + return ErrorOr(buf); +} + +// Get the block address for a given logical block index +static ErrorOr ext2_get_block_address(Ext2Context* ctx, Ext2Node* node, + UInt32 logicalIndex) { + if (!ctx || !node || !ctx->drive) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + UInt32 pointersPerBlock = blockSize / sizeof(UInt32); + + // Direct blocks + if (logicalIndex < EXT2_DIRECT_BLOCKS) { + UInt32 bn = node->inode.fBlock[logicalIndex]; + if (bn == 0) return ErrorOr(kErrorInvalidData); + return ErrorOr(bn); + } + + // Single indirect blocks + if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { + UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; + if (iblock == 0) return ErrorOr(kErrorInvalidData); + + auto res = ext2_read_block_ptr(ctx, iblock); + if (!res) return ErrorOr(res.Error()); + + // Using dereference operator + UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) + + UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; + mm_free_ptr(ptr); + + if (val == 0) return ErrorOr(kErrorInvalidData); + return ErrorOr(val); + } + + // Double indirect blocks + UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; + UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; + if (logicalIndex < (doubleStart + doubleSpan)) { + UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; + if (db == 0) return ErrorOr(kErrorInvalidData); + + auto dblRes = ext2_read_block_ptr(ctx, db); + if (!dblRes) return ErrorOr(dblRes.Error()); + + UInt32* dblPtr = *dblRes.Leak(); + + UInt32 idxWithin = logicalIndex - doubleStart; + UInt32 firstIdx = idxWithin / pointersPerBlock; + UInt32 secondIdx = idxWithin % pointersPerBlock; + UInt32 singleBlockNum = dblPtr[firstIdx]; + + mm_free_ptr(dblPtr); + if (singleBlockNum == 0) return ErrorOr(kErrorInvalidData); + + auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); + if (!singleRes) return ErrorOr(singleRes.Error()); + + UInt32* singlePtr = *singleRes.Leak(); + UInt32 val = singlePtr[secondIdx]; + mm_free_ptr(singlePtr); + + if (val == 0) return ErrorOr(kErrorInvalidData); + return ErrorOr(val); + } + + return ErrorOr(kErrorUnimplemented); +} + +static ErrorOr ext2_read_inode_data(Ext2Context* ctx, Ext2Node* node, SizeT size) { + if (!ctx || !ctx->drive || !node || size == 0) return ErrorOr(1); + + auto blockSize = ctx->BlockSize(); + SizeT available = (node->inode.fSize > node->cursor) ? (node->inode.fSize - node->cursor) : 0; + SizeT bytesToRead = (size < available) ? size : available; + if (bytesToRead == 0) return ErrorOr(2); // nothing to read + + auto buffer = mm_alloc_ptr(bytesToRead, true, false); + if (!buffer) return ErrorOr(3); // allocation failed + + UInt32 currentOffset = node->cursor; + SizeT remaining = bytesToRead; + UInt8* dest = reinterpret_cast(buffer); + + while (remaining > 0) { + UInt32 logicalIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; + + auto phys = ext2_get_block_address(ctx, node, logicalIndex); + if (phys.HasError()) { + mm_free_ptr(buffer); + return ErrorOr(phys.Error()); + } + + auto blockNumber = phys.Value(); + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(buffer); + return ErrorOr(4); // block buffer allocation failed + } + + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(buffer); + return ErrorOr(5); // block read failed + } + + SizeT chunk = ext2_min(remaining, blockSize - offsetInBlock); + rt_copy_memory_safe(static_cast(static_cast(blockBuf) + offsetInBlock), + static_cast(dest), chunk, chunk); + + mm_free_ptr(blockBuf); + + currentOffset += static_cast(chunk); + dest += chunk; + remaining -= chunk; + } + + node->cursor += static_cast(bytesToRead); + return ErrorOr(buffer); +} + +// Get group descriptor information for a given block/inode number +static ErrorOr ext2_get_group_descriptor_info(Ext2Context* ctx, + UInt32 targetBlockOrInode) { + if (!ctx || !ctx->superblock || !ctx->drive) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + UInt32 blocksPerGroup = ctx->superblock->fBlocksPerGroup; + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + UInt32 totalBlocks = ctx->superblock->fBlockCount; + UInt32 totalInodes = ctx->superblock->fInodeCount; + + if (blocksPerGroup == 0 || inodesPerGroup == 0) return ErrorOr(kErrorInvalidData); + + // block group index + UInt32 groupIndex = 0; + if (targetBlockOrInode == 0) { + groupIndex = 0; + } else if (targetBlockOrInode <= totalInodes) { + // 1-based + groupIndex = (targetBlockOrInode - 1) / inodesPerGroup; + } else { + // EXT2 block number + if (targetBlockOrInode < ctx->superblock->fFirstDataBlock) { + groupIndex = 0; + } else { + groupIndex = (targetBlockOrInode - ctx->superblock->fFirstDataBlock) / blocksPerGroup; + } + } + + // Calculate number of block groups + UInt32 groupsCount = static_cast((totalBlocks + blocksPerGroup - 1) / blocksPerGroup); + if (groupIndex >= groupsCount) return ErrorOr(kErrorInvalidData); + + // Determine GDT start block + UInt32 gdtStartBlock = + (blockSize == 1024) ? EXT2_GROUP_DESC_BLOCK_SMALL : EXT2_GROUP_DESC_BLOCK_LARGE; + + // Compute byte offset of descriptor within the GDT + const UInt32 descSize = sizeof(EXT2_GROUP_DESCRIPTOR); + UInt64 descByteOffset = static_cast(groupIndex) * descSize; + + // Which EXT2 block contains that descriptor? + UInt32 blockOffsetWithinGdt = static_cast(descByteOffset / blockSize); + UInt32 offsetInGroupDescBlock = static_cast(descByteOffset % blockSize); + UInt32 groupDescriptorBlock = gdtStartBlock + blockOffsetWithinGdt; + + // Allocate buffer and read the block containing the descriptor + auto blockBuffer = mm_alloc_ptr(blockSize, true, false); + if (!blockBuffer) return ErrorOr(kErrorHeapOutOfMemory); + + UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); + if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { + mm_free_ptr(blockBuffer); + return ErrorOr(kErrorDisk); + } + + auto groupInfo = (Ext2GroupInfo*) mm_alloc_ptr(sizeof(Ext2GroupInfo), true, false); + if (!groupInfo) { + mm_free_ptr(blockBuffer); + return ErrorOr(kErrorHeapOutOfMemory); + } + + groupInfo->groupDesc = reinterpret_cast( + reinterpret_cast(blockBuffer) + offsetInGroupDescBlock); + groupInfo->groupDescriptorBlock = groupDescriptorBlock; + groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; + groupInfo->blockBuffer = reinterpret_cast(blockBuffer); + + return ErrorOr(groupInfo); +} + +// Allocate a new block +inline ErrorOr ext2_alloc_block(Ext2Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + + // for the bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); + + // Read block bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // bit = 0 + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); + + // Compute block number + UInt32 blockNumber = byteIdx * 8 + bit; + + // Write bitmap back + if (!ext2_write_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeBlocksCount--; + mm_free_ptr(bitmap); + return ErrorOr(blockNumber); + } + } + } + } + + mm_free_ptr(bitmap); + return ErrorOr(kErrorDiskIsFull); +} + +// Indirect blocks +static ErrorOr ext2_set_block_address(Ext2Context* ctx, Ext2Node* node, + UInt32 logicalBlockIndex, UInt32 physicalBlockNumber) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !node) return ErrorOr(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 blocksPerPointerBlock = blockSize / sizeof(UInt32); + + // Direct blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS) { + node->inode.fBlock[logicalBlockIndex] = physicalBlockNumber; + return ErrorOr(nullptr); + } + + // Single indirect blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS + blocksPerPointerBlock) { + if (node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); // Ref + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] = newBlockRes.Leak(); + + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero out new indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); + + rt_zero_memory(zeroBuf, blockSize); + UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, indirectLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); + } + + // Read, modify, and write single indirect block + auto indirectRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (indirectRes.HasError()) return ErrorOr(indirectRes.Error()); + + UInt32* indirectPtr = indirectRes.Leak().Leak(); // Ref + indirectPtr[logicalBlockIndex - EXT2_DIRECT_BLOCKS] = physicalBlockNumber; + + UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, indirectLba, indirectPtr, blockSize)) { + mm_free_ptr(indirectPtr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(indirectPtr); + return ErrorOr(nullptr); + } + + // Double + UInt32 doubleStart = EXT2_DIRECT_BLOCKS + blocksPerPointerBlock; + UInt32 doubleSpan = blocksPerPointerBlock * blocksPerPointerBlock; + if (logicalBlockIndex < doubleStart + doubleSpan) { + if (node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] = newBlockRes.Leak(); + + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero new double-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); + + rt_zero_memory(zeroBuf, blockSize); + UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, dblLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); + } + + // Compute indices + UInt32 idxWithin = logicalBlockIndex - doubleStart; + UInt32 firstIdx = idxWithin / blocksPerPointerBlock; + UInt32 secondIdx = idxWithin % blocksPerPointerBlock; + + auto doubleRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (doubleRes.HasError()) return ErrorOr(doubleRes.Error()); + + UInt32* doublePtr = doubleRes.Leak().Leak(); + UInt32 singleIndirectBlock = doublePtr[firstIdx]; + + // Allocate single-indirect if missing + if (singleIndirectBlock == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) { + mm_free_ptr(doublePtr); + return ErrorOr(groupInfoRes.Error()); + } + + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + singleIndirectBlock = newBlockRes.Leak(); + doublePtr[firstIdx] = singleIndirectBlock; + + // Write back GDT + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero single-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) { + mm_free_ptr(doublePtr); + return ErrorOr(kErrorHeapOutOfMemory); + } + + rt_zero_memory(zeroBuf, blockSize); + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + mm_free_ptr(doublePtr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); + + // Write double-indirect back to disk + UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, dblLba, doublePtr, blockSize)) { + mm_free_ptr(doublePtr); + return ErrorOr(kErrorDisk); + } + } + + mm_free_ptr(doublePtr); + + // Write to single-indirect block + auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); + if (singleRes.HasError()) return ErrorOr(singleRes.Error()); + + UInt32* singlePtr = singleRes.Leak().Leak(); + singlePtr[secondIdx] = physicalBlockNumber; + + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, singlePtr, blockSize)) { + mm_free_ptr(singlePtr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(singlePtr); + return ErrorOr(nullptr); + } + + // Triple indirect blocks not implemented + return ErrorOr(kErrorUnimplemented); +} + +// Find a directory entry by name within a directory inode +static ErrorOr ext2_find_dir_entry(Ext2Context* ctx, Ext2Node* dirNode, + const char* name) { + if (!ctx || !ctx->drive || !dirNode || !name) return ErrorOr(kErrorInvalidData); + + // Check directory type + auto type = (dirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); + + SizeT nameLen = rt_string_len(name); + for (UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + UInt32 blockNum = dirNode->inode.fBlock[i]; + if (blockNum == 0) continue; + + UInt32 lba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + UInt32 offset = 0; + while (offset + sizeof(UInt32) + sizeof(UInt16) <= blockSize) { + auto onDiskEntry = reinterpret_cast((UInt8*) blockBuf + offset); + if (onDiskEntry->fRecordLength == 0) break; // corrupted + + if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { + // Compare names + if (rt_string_cmp(name, onDiskEntry->fName, nameLen) == 0) { + // Allocate a result sized to hold the name + metadata + SizeT recSize = sizeof(EXT2_DIR_ENTRY); + auto found = (EXT2_DIR_ENTRY*) mm_alloc_ptr(recSize, true, false); + if (!found) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorHeapOutOfMemory); + } + + // Copy only record-length bytes + rt_copy_memory_safe(onDiskEntry, found, onDiskEntry->fRecordLength, recSize); + mm_free_ptr(blockBuf); + return ErrorOr(found); + } + } + offset += onDiskEntry->fRecordLength; + } + } + + mm_free_ptr(blockBuf); + return ErrorOr(kErrorFileNotFound); +} + +// Compute ideal record length for a directory name +static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { + UInt16 raw = + static_cast(8 + nameLen); // 8 = inode(4)+rec_len(2)+name_len(1)+file_type(1) + return static_cast((raw + 3) & ~3u); // align up to 4 +} + +static ErrorOr ext2_add_dir_entry(Ext2Context* ctx, Ext2Node* parentDirNode, + const char* name, UInt32 inodeNumber, UInt8 fileType) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !parentDirNode || !name) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + SizeT nameLen = rt_string_len(name); + if (nameLen == 0 || nameLen > 255) return ErrorOr(kErrorInvalidData); + + UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast(nameLen)); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); + + for (UInt32 bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { + UInt32 blockNum = parentDirNode->inode.fBlock[bi]; + + if (blockNum == 0) { + // Allocate new block + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoRes) { + mm_free_ptr(blockBuf); + return ErrorOr(groupInfoRes.Error()); + } + + auto groupInfo = *groupInfoRes.Leak(); // Dereference to get Ext2GroupInfo* + auto allocBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!allocBlockRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(allocBlockRes.Error()); + } + + UInt32 newBlock = *allocBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero block & insert entry + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast(blockSize); + rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + + UInt32 blockLba = ext2_block_to_lba(ctx, newBlock); + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, bi, newBlock); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); + } + + // read it + UInt32 blockLba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + UInt32 offset = 0; + EXT2_DIR_ENTRY* lastEntry = nullptr; + UInt32 lastOffset = 0; + + while (offset < blockSize) { + if (offset + 8 > blockSize) break; + auto e = reinterpret_cast((UInt8*) blockBuf + offset); + if (e->fRecordLength == 0) break; + lastEntry = e; + lastOffset = offset; + offset += e->fRecordLength; + } + + if (!lastEntry) continue; + + UInt16 lastIdeal = ext2_dir_entry_ideal_len(lastEntry->fNameLength); + + if (lastEntry->fRecordLength >= (UInt16) (lastIdeal + newRecIdeal)) { + UInt16 origRec = lastEntry->fRecordLength; + lastEntry->fRecordLength = lastIdeal; + + UInt32 newOffset = lastOffset + lastIdeal; + auto newEntry = reinterpret_cast((UInt8*) blockBuf + newOffset); + newEntry->fInode = inodeNumber; + newEntry->fNameLength = static_cast(nameLen); + newEntry->fFileType = fileType; + newEntry->fRecordLength = static_cast(origRec - lastIdeal); + rt_copy_memory_safe(const_cast(name), newEntry->fName, nameLen, + newEntry->fRecordLength); + + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); + } + } + + // No space in direct blocks -> allocate new block + int targetIndex = -1; + for (UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + if (parentDirNode->inode.fBlock[i] == 0) { + targetIndex = i; + break; + } + } + if (targetIndex == -1) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorUnimplemented); + } + + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(blockBuf); + return ErrorOr(groupInfoResult.Error()); + } + + auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!newBlockRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(newBlockRes.Error()); + } + + UInt32 newBlockNum = *newBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast(blockSize); + rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + + UInt32 newBlockLba = ext2_block_to_lba(ctx, newBlockNum); + if (!ext2_write_block(ctx->drive, newBlockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, targetIndex, newBlockNum); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); +} + +// Soon +static ErrorOr ext2_alloc_inode(Ext2Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + + // buffer for the inode bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); + + // Read inode bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Find first free inode (bit = 0) + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); + + // Compute inode number + UInt32 inodeNumber = byteIdx * 8 + bit + 1; // Inodes are 1-based + + // Write bitmap back + if (!ext2_write_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeInodesCount--; + mm_free_ptr(bitmap); + return ErrorOr(inodeNumber); + } + } + } + } + + mm_free_ptr(bitmap); + return ErrorOr(kErrorDiskIsFull); +} + +// to write an inode to its correct location on disk +static ErrorOr ext2_write_inode(Ext2Context* ctx, Ext2Node* node) { + using namespace Kernel; + + if (!ctx || !ctx->superblock || !ctx->drive || !node) return ErrorOr(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + + if (inodesPerGroup == 0) return ErrorOr(kErrorInvalidData); + + // Calculate which group this inode belongs to + UInt32 groupIndex = (node->inodeNumber - 1) / inodesPerGroup; + NE_UNUSED(groupIndex); + UInt32 inodeIndexInGroup = (node->inodeNumber - 1) % inodesPerGroup; + + // Get group descriptor + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (!groupInfoResult) return ErrorOr(groupInfoResult.Error()); + + auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* + + // Calculate inode table position + UInt32 inodeTableBlock = groupInfo->groupDesc->fInodeTable; + UInt32 inodeSize = ctx->superblock->fInodeSize; + UInt32 inodesPerBlock = blockSize / inodeSize; + + UInt32 blockOffset = inodeIndexInGroup / inodesPerBlock; + UInt32 offsetInBlock = (inodeIndexInGroup % inodesPerBlock) * inodeSize; + + UInt32 inodeBlock = inodeTableBlock + blockOffset; + UInt32 inodeLba = ext2_block_to_lba(ctx, inodeBlock); + + // Read the block containing the inode + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorHeapOutOfMemory); + } + + if (!ext2_read_block(ctx->drive, inodeLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + // Copy the updated inode into the block buffer + rt_copy_memory_safe(&node->inode, static_cast((UInt8*) blockBuf + offsetInBlock), + sizeof(EXT2_INODE), blockSize - offsetInBlock); + + // Write the block back + if (!ext2_write_block(ctx->drive, inodeLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + return ErrorOr(nullptr); +} + +namespace { +// new +struct PathComponents { + const char** components; + int count; + Char* buffer; + + PathComponents(const char* path) : components(nullptr), count(0), buffer(nullptr) { + if (!path || *path == '\0') return; + + SizeT pathLen = rt_string_len(path); + buffer = (Char*) mm_alloc_ptr(pathLen + 1, true, false); + if (!buffer) return; + + rt_copy_memory_safe((void*) path, buffer, pathLen, pathLen + 1); + buffer[pathLen] = '\0'; + + // temp array + const char** temp = (const char**) mm_alloc_ptr(sizeof(char*) * (pathLen + 1), true, false); + if (!temp) { + mm_free_ptr(buffer); + buffer = nullptr; + return; + } + + UInt32 compCount = 0; + Char* p = buffer; + + while (*p != '\0') { + // skip slashes + while (*p == '/') p++; + if (*p == '\0') break; + + Char* start = p; + while (*p != '/' && *p != '\0') p++; + Char saved = *p; + *p = '\0'; + + // handle ".", "..", or normal + if (rt_string_cmp(start, ".", 1) == 0) { + // ignore + } else if (rt_string_cmp(start, "..", 2) == 0) { + if (compCount > 0) compCount--; // go up one level + } else { + temp[compCount++] = start; + } + + *p = saved; + } + + if (compCount == 0) { + mm_free_ptr(temp); + return; + } + + components = (const char**) mm_alloc_ptr(sizeof(char*) * compCount, true, false); + if (!components) { + mm_free_ptr(temp); + return; + } + + for (UInt32 i = 0; i < compCount; i++) components[i] = temp[i]; + count = compCount; + + mm_free_ptr(temp); + } + + ~PathComponents() { + if (components) mm_free_ptr(components); + if (buffer) mm_free_ptr(buffer); + } +}; +} // anonymous namespace + +// The Ext2FileSystemParser (not manager!) +Ext2FileSystemParser::Ext2FileSystemParser(DriveTrait* drive) : fCtx(drive) { + MUST_PASS(fCtx); +} + +NodePtr Ext2FileSystemParser::Open(const char* path, const char* restrict_type) { + NE_UNUSED(restrict_type); + if (!path || *path == '\0' || !this->fCtx.drive) { + return nullptr; + } + + // Root ("/") + if (rt_string_len(path) == 1 && rt_string_cmp(path, "/", 1) == 0) { + auto inodeResult = ext2_load_inode(&this->fCtx, EXT2_ROOT_INODE); + if (!inodeResult) { + return nullptr; + } + + auto heapNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!heapNode) return nullptr; + + *heapNode = *inodeResult.Leak().Leak(); + heapNode->cursor = 0; + return reinterpret_cast(heapNode); + } + + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + return nullptr; + } + + UInt32 currentInodeNumber = EXT2_ROOT_INODE; + Ext2Node* currentDirNode = nullptr; + + for (UInt32 i = 0; i < (UInt32) pathComponents.count; ++i) { + auto inodeResult = ext2_load_inode(&this->fCtx, currentInodeNumber); + if (!inodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; + } + + if (currentDirNode) { + mm_free_ptr(currentDirNode); + currentDirNode = nullptr; + } + + currentDirNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!currentDirNode) { + return nullptr; + } + + *currentDirNode = *inodeResult.Leak().Leak(); + currentDirNode->cursor = 0; + + if (i < pathComponents.count - 1U) { + UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(currentDirNode); + return nullptr; + } + } + + auto dirEntryResult = + ext2_find_dir_entry(&this->fCtx, currentDirNode, pathComponents.components[i]); + if (!dirEntryResult) { + mm_free_ptr(currentDirNode); + return nullptr; + } + + EXT2_DIR_ENTRY* entryPtr = *dirEntryResult.Leak(); + currentInodeNumber = entryPtr->fInode; + mm_free_ptr(entryPtr); + } + + auto finalInodeResult = ext2_load_inode(&this->fCtx, currentInodeNumber); + if (!finalInodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; + } + + if (currentDirNode) { + mm_free_ptr(currentDirNode); + } + + auto resultNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!resultNode) { + return nullptr; + } + + *resultNode = *finalInodeResult.Leak().Leak(); + resultNode->cursor = 0; + return reinterpret_cast(resultNode); +} + +void* Ext2FileSystemParser::Read(NodePtr node, Int32 flags, SizeT size) { + if (!node) return nullptr; + + NE_UNUSED(flags); + + auto extNode = reinterpret_cast(node); + auto dataResult = ext2_read_inode_data(&this->fCtx, extNode, size); + + if (!dataResult) { + return nullptr; // error, nothing to return + } + + void* data = *dataResult.Leak(); + if (data) { + extNode->cursor += static_cast(size); + } + + return data; +} + +void Ext2FileSystemParser::Write(NodePtr node, void* data, Int32 flags, SizeT size) { + if (!node || !data || size == 0) return; + + NE_UNUSED(flags); + + auto extNode = reinterpret_cast(node); + auto blockSize = this->fCtx.BlockSize(); + SizeT bytesWritten = 0; + + UInt32 currentOffset = extNode->cursor; + UInt8* src = reinterpret_cast(data); + + while (bytesWritten < size) { + UInt32 logicalBlockIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; + + auto physBlockResult = ext2_get_block_address(&this->fCtx, extNode, logicalBlockIndex); + UInt32 physicalBlock = 0; + + if (!physBlockResult) { + auto err = physBlockResult.Error(); + if (err == kErrorInvalidData || err == kErrorUnimplemented) { + auto groupInfoResult = ext2_get_group_descriptor_info(&this->fCtx, extNode->inodeNumber); + if (!groupInfoResult) { + return; + } + + auto groupInfo = *groupInfoResult.Leak(); + auto allocResult = ext2_alloc_block(&this->fCtx, groupInfo->groupDesc); + if (!allocResult) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + physicalBlock = *allocResult.Leak(); + + auto setRes = + ext2_set_block_address(&this->fCtx, extNode, logicalBlockIndex, physicalBlock); + if (!setRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + UInt32 gdtLba = ext2_block_to_lba(&this->fCtx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->fCtx.drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + } else { + return; + } + } else { + physicalBlock = physBlockResult.Value(); + } + + UInt32 physicalLba = ext2_block_to_lba(&this->fCtx, physicalBlock); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return; + + if (offsetInBlock > 0 || (size - bytesWritten) < blockSize) { + if (!ext2_read_block(this->fCtx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; + } + } else { + rt_zero_memory(blockBuf, blockSize); + } + + UInt32 bytesInCurrentBlock = + static_cast(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); + rt_copy_memory_safe(src, static_cast((UInt8*) blockBuf + offsetInBlock), + bytesInCurrentBlock, blockSize - offsetInBlock); + + if (!ext2_write_block(this->fCtx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; + } + + mm_free_ptr(blockBuf); + + currentOffset += bytesInCurrentBlock; + src += bytesInCurrentBlock; + bytesWritten += bytesInCurrentBlock; + } + + if (currentOffset > extNode->inode.fSize) { + extNode->inode.fSize = currentOffset; + } + + extNode->inode.fBlocks = (extNode->inode.fSize + blockSize - 1) / blockSize; + extNode->inode.fModifyTime = 0; + + auto writeInodeRes = ext2_write_inode(&this->fCtx, extNode); + if (!writeInodeRes) { + // Failed to persist inode + } + + extNode->cursor = currentOffset; +} + +bool Ext2FileSystemParser::Seek(NodePtr node, SizeT offset) { + if (!node) return false; + auto extNode = reinterpret_cast(node); + extNode->cursor = static_cast(offset); + return true; +} + +SizeT Ext2FileSystemParser::Tell(NodePtr node) { + if (!node) return 0; + auto extNode = reinterpret_cast(node); + return extNode->cursor; +} + +bool Ext2FileSystemParser::Rewind(NodePtr node) { + if (!node) return false; + auto extNode = reinterpret_cast(node); + extNode->cursor = 0; + return true; +} + +void* Ext2FileSystemParser::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { + NE_UNUSED(name); + return Read(node, flags, size); +} + +void Ext2FileSystemParser::Write(const char* name, NodePtr node, void* data, Int32 flags, + SizeT size) { + NE_UNUSED(name); + Write(node, data, flags, size); +} + +NodePtr Ext2FileSystemParser::Create(const char* path) { + if (!path || *path == '\0') return nullptr; + + PathComponents pathComponents(path); + if (pathComponents.count == 0) return nullptr; + + const char* filename = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(filename) > kExt2FSMaxFileNameLen) return nullptr; + + // Build parent path + Char parentPathBuf[256] = {0}; + SizeT currentPathLen = 0; + for (UInt32 i = 0; (i < pathComponents.count - 1U); ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + rt_copy_memory_safe(const_cast(pathComponents.components[i]), + parentPathBuf + currentPathLen, componentLen, + sizeof(parentPathBuf) - currentPathLen); + currentPathLen += componentLen; + } + parentPathBuf[currentPathLen] = '\0'; + + // Open parent directory + NodePtr parentDirNodePtr = nullptr; + if (currentPathLen == 0) { + // root + auto inodeRes = ext2_load_inode(&this->fCtx, EXT2_ROOT_INODE); + if (!inodeRes) return nullptr; + parentDirNodePtr = mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!parentDirNodePtr) return nullptr; + *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak().Leak(); + reinterpret_cast(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } + + if (!parentDirNodePtr) return nullptr; + + auto parentDirNode = reinterpret_cast(parentDirNodePtr); + + // Ensure parent is a directory + UInt32 type = (parentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(parentDirNode); + return nullptr; + } + + // Get group info for allocation + auto groupInfoResult = ext2_get_group_descriptor_info(&this->fCtx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(parentDirNode); + return nullptr; + } + auto groupInfo = *groupInfoResult.Leak(); + + // Allocate new inode + auto newInodeRes = ext2_alloc_inode(&this->fCtx, groupInfo->groupDesc); + if (!newInodeRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); // so this works + mm_free_ptr(parentDirNode); + return nullptr; + } + UInt32 newInodeNumber = newInodeRes.Value(); + + UInt32 gdtLba = ext2_block_to_lba(&this->fCtx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->fCtx.drive, gdtLba, groupInfo->blockBuffer, this->fCtx.BlockSize())) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node + Ext2Node* newFileNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!newFileNode) { + mm_free_ptr(parentDirNode); + return nullptr; + } + + newFileNode->inodeNumber = newInodeNumber; + rt_zero_memory(&newFileNode->inode, sizeof(EXT2_INODE)); + + newFileNode->inode.fMode = (kExt2FileTypeRegular << 12); + newFileNode->inode.fUID = 0; + newFileNode->inode.fGID = 0; + newFileNode->inode.fLinksCount = 1; + newFileNode->inode.fSize = 0; + newFileNode->inode.fBlocks = 0; + newFileNode->inode.fCreateTime = 0; + newFileNode->inode.fModifyTime = 0; + + // Persist new inode + auto writeInodeRes = ext2_write_inode(&this->fCtx, newFileNode); + if (!writeInodeRes) { + mm_free_ptr(parentDirNode); + mm_free_ptr(newFileNode); + return nullptr; + } + + // Add directory entry + auto addRes = ext2_add_dir_entry(&this->fCtx, parentDirNode, filename, newInodeNumber, + kExt2FileTypeRegular); + if (!addRes) { + mm_free_ptr(parentDirNode); + mm_free_ptr(newFileNode); + return nullptr; + } + + // Update parent inode + auto parentWriteRes = ext2_write_inode(&this->fCtx, parentDirNode); + // ignore failure + + NE_UNUSED(parentWriteRes); + + mm_free_ptr(parentDirNode); + return reinterpret_cast(newFileNode); +} + +NodePtr Ext2FileSystemParser::CreateDirectory(const char* path) { + if (!path || *path == '\0') return nullptr; + + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + kout << "EXT2: Failed to parse path for CreateDirectory.\n"; + return nullptr; + } + + const char* dirname = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(dirname) > kExt2FSMaxFileNameLen) { + kout << "EXT2: Directory name too long: " << dirname << ".\n"; + return nullptr; + } + + // Build parent path + Char parentPathBuf[256]; + SizeT currentPathLen = 0; + for (UInt32 i = 0; (i < pathComponents.count - 1U); ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { + kout << "EXT2: Parent path too long for CreateDirectory.\n"; + return nullptr; + } + + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + + rt_copy_memory_safe(static_cast(const_cast(pathComponents.components[i])), + static_cast(parentPathBuf + currentPathLen), componentLen, + sizeof(parentPathBuf) - currentPathLen); + currentPathLen += componentLen; + } + + parentPathBuf[currentPathLen] = '\0'; + + // Open parent directory node + NodePtr parentDirNodePtr = nullptr; + if (currentPathLen == 0) { + auto inodeRes = ext2_load_inode(&this->fCtx, EXT2_ROOT_INODE); + if (!inodeRes) { + return nullptr; + } + + parentDirNodePtr = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!parentDirNodePtr) return nullptr; + + *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak().Leak(); + reinterpret_cast(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } + + if (!parentDirNodePtr) { + kout << "EXT2: Failed to open parent directory for CreateDirectory: " << parentPathBuf << ".\n"; + return nullptr; + } + + auto parentDirNode = reinterpret_cast(parentDirNodePtr); + + // Check parent is a directory + UInt32 parentType = (parentDirNode->inode.fMode >> 12) & 0xF; + if (parentType != kExt2FileTypeDirectory) { + kout << "EXT2: Parent is not a directory: " << parentPathBuf << ".\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + // Allocate inode + auto groupInfoResult = ext2_get_group_descriptor_info(&this->fCtx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + kout << "EXT2: Failed to get group descriptor info for new dir inode.\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + auto groupInfo = *groupInfoResult.Leak(); + auto newInodeRes = ext2_alloc_inode(&this->fCtx, groupInfo->groupDesc); + if (!newInodeRes) { + kout << "EXT2: Failed to allocate inode for new directory.\n"; + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + UInt32 newInodeNumber = *newInodeRes.Leak(); + + // Write back group descriptor block + UInt32 gdtLba = ext2_block_to_lba(&this->fCtx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->fCtx.drive, gdtLba, groupInfo->blockBuffer, this->fCtx.BlockSize())) { + kout << "EXT2: Failed to write group descriptor after inode allocation.\n"; + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node and initialize inode fields + Ext2Node* newDirNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!newDirNode) { + kout << "EXT2: Out of memory for new directory node.\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + newDirNode->inodeNumber = newInodeNumber; + rt_zero_memory(&newDirNode->inode, sizeof(EXT2_INODE)); + newDirNode->inode.fMode = (kExt2FileTypeDirectory << 12); + newDirNode->inode.fUID = 0; + newDirNode->inode.fGID = 0; + newDirNode->inode.fLinksCount = 2; // . and .. + newDirNode->inode.fSize = this->fCtx.BlockSize(); + newDirNode->inode.fBlocks = 1; + newDirNode->inode.fCreateTime = 0; + newDirNode->inode.fModifyTime = 0; + + // Allocate a data block for the new directory + auto groupForBlockRes = ext2_get_group_descriptor_info(&this->fCtx, newDirNode->inodeNumber); + if (!groupForBlockRes) { + kout << "EXT2: Failed to get group info for directory block allocation.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + auto groupForBlock = *groupForBlockRes.Leak(); + auto newBlockRes = ext2_alloc_block(&this->fCtx, groupForBlock->groupDesc); + if (!newBlockRes) { + kout << "EXT2: Failed to allocate block for new directory contents.\n"; + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + UInt32 newDirBlockNum = *newBlockRes.Leak(); + + // Write back GDT + UInt32 gdtLba2 = ext2_block_to_lba(&this->fCtx, groupForBlock->groupDescriptorBlock); + if (!ext2_write_block(this->fCtx.drive, gdtLba2, groupForBlock->blockBuffer, + this->fCtx.BlockSize())) { + kout << "EXT2: Failed to write GDT after directory block allocation.\n"; + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + + // Set the block in newDirNode + auto setBlkRes = ext2_set_block_address(&this->fCtx, newDirNode, 0, newDirBlockNum); + if (!setBlkRes) { + kout << "EXT2: Failed to set data block for new directory.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Prepare block with '.' and '..' + auto dirBlockBuf = mm_alloc_ptr(this->fCtx.BlockSize(), true, false); + if (!dirBlockBuf) { + kout << "EXT2: Out of memory preparing directory block.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + rt_zero_memory(dirBlockBuf, this->fCtx.BlockSize()); + + // '.' entry + auto dot = reinterpret_cast(dirBlockBuf); + dot->fInode = newInodeNumber; + dot->fNameLength = 1; + dot->fFileType = kExt2FileTypeDirectory; + dot->fRecordLength = ext2_dir_entry_ideal_len(dot->fNameLength); + dot->fName[0] = '.'; + + // '..' entry occupies rest of block + auto dotdot = reinterpret_cast((UInt8*) dirBlockBuf + dot->fRecordLength); + dotdot->fInode = parentDirNode->inodeNumber; + dotdot->fNameLength = 2; + dotdot->fFileType = kExt2FileTypeDirectory; + dotdot->fRecordLength = static_cast(this->fCtx.BlockSize() - dot->fRecordLength); + dotdot->fName[0] = '.'; + dotdot->fName[1] = '.'; + + // Write dir block to disk + UInt32 newDirBlockLba = ext2_block_to_lba(&this->fCtx, newDirBlockNum); + if (!ext2_write_block(this->fCtx.drive, newDirBlockLba, dirBlockBuf, this->fCtx.BlockSize())) { + kout << "EXT2: Failed to write directory block to disk.\n"; + mm_free_ptr(dirBlockBuf); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + mm_free_ptr(dirBlockBuf); + + // Persist new directory inode + auto writeInodeRes = ext2_write_inode(&this->fCtx, newDirNode); + if (!writeInodeRes) { + kout << "EXT2: Failed to write new directory inode to disk.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Add directory entry into parent + auto addRes = ext2_add_dir_entry(&this->fCtx, parentDirNode, dirname, newInodeNumber, + kExt2FileTypeDirectory); + if (!addRes) { + kout << "EXT2: Failed to add directory entry for '" << dirname << "' to parent.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Increment parent link count and persist parent inode + parentDirNode->inode.fLinksCount += 1; + auto parentWriteRes = ext2_write_inode(&this->fCtx, parentDirNode); + if (!parentWriteRes) { + kout << "EXT2: Warning: failed to update parent inode after directory creation.\n"; + } + + mm_free_ptr(parentDirNode); + return reinterpret_cast(newDirNode); +} + +#endif +#endif diff --git a/src/kernel/src/FS/NeFS+FileMgr.cc b/src/kernel/src/FS/NeFS+FileMgr.cc new file mode 100644 index 00000000..0fdabd6f --- /dev/null +++ b/src/kernel/src/FS/NeFS+FileMgr.cc @@ -0,0 +1,276 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __NE_MINIMAL_OS__ +#ifdef __FSKIT_INCLUDES_NEFS__ + +#include +#include + +/// @brief NeFS File System Manager. +/// BUGS: 0 + +namespace Kernel { +static inline bool is_valid_nefs_catalog(NodePtr node); + +/// @brief C++ constructor +NeFileSystemMgr::NeFileSystemMgr() { + mParser = new NeFileSystemParser(); + MUST_PASS(mParser); + + kout << "We are done allocating NeFileSystemParser...\n"; +} + +NeFileSystemMgr::~NeFileSystemMgr() { + if (mParser) { + kout << "Destroying NeFileSystemParser...\n"; + delete mParser; + mParser = nullptr; + } +} + +/// @brief Removes a node from the filesystem. +/// @param path The filename +/// @return If it was deleted or not. +bool NeFileSystemMgr::Remove(_Input const Char* path) { + if (path == nullptr || *path == 0) { + kout << "NeFS: Remove called with null or empty path\n"; + return false; + } + return mParser->RemoveCatalog(path); +} + +/// @brief Creates a node with the specified. +/// @param path The filename path. +/// @return The Node pointer. +NodePtr NeFileSystemMgr::Create(_Input const Char* path) { + if (!path || *path == 0) { + kout << "NeFS: Create called with null or empty path\n"; + return nullptr; + } + return rtl_node_cast(mParser->CreateCatalog(path)); +} + +/// @brief Creates a node which is a directory. +/// @param path The filename path. +/// @return The Node pointer. +NodePtr NeFileSystemMgr::CreateDirectory(const Char* path) { + if (!path || *path == 0) { + kout << "NeFS: CreateDirectory called with null or empty path\n"; + return nullptr; + } + return rtl_node_cast(mParser->CreateCatalog(path, 0, kNeFSCatalogKindDir)); +} + +/// @brief Creates a node which is an alias. +/// @param path The filename path. +/// @return The Node pointer. +NodePtr NeFileSystemMgr::CreateAlias(const Char* path) { + if (!path || *path == 0) { + kout << "NeFS: CreateAlias called with null or empty path\n"; + return nullptr; + } + return rtl_node_cast(mParser->CreateCatalog(path, 0, kNeFSCatalogKindAlias)); +} + +NodePtr NeFileSystemMgr::CreateSwapFile(const Char* path) { + if (!path || *path == 0) { + kout << "NeFS: CreateSwapFile called with null or empty path\n"; + return nullptr; + } + return rtl_node_cast(mParser->CreateCatalog(path, 0, kNeFSCatalogKindPage)); +} + +/// @brief Gets the root directory. +/// @return +const Char* NeFileSystemHelper::Root() { + return kNeFSRoot; +} + +/// @brief Gets the up-dir directory. +/// @return +const Char* NeFileSystemHelper::UpDir() { + return kNeFSUpDir; +} + +/// @brief Gets the separator character. +/// @return +Char NeFileSystemHelper::Separator() { + return kNeFSSeparator; +} + +/// @brief Gets the metafile character. +/// @return +Char NeFileSystemHelper::MetaFile() { + return kNeFSMetaFilePrefix; +} + +/// @brief Opens a new file. +/// @param path +/// @param r +/// @return +_Output NodePtr NeFileSystemMgr::Open(_Input const Char* path, _Input const Char* r) { + if (!path || *path == 0) { + kout << "NeFS: Open called with null or empty path\n"; + return nullptr; + } + if (!r || *r == 0) { + kout << "NeFS: Open called with null or empty mode string\n"; + return nullptr; + } + auto catalog = mParser->GetCatalog(path); + if (!catalog) { + kout << "NeFS: Open could not find catalog for path\n"; + return nullptr; + } + return rtl_node_cast(catalog); +} + +Void NeFileSystemMgr::Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Write called with invalid node pointer\n"; + return; + } + if (!data) { + kout << "NeFS: Write called with null data pointer\n"; + return; + } + if (!size || size > kNeFSForkSize) { + (Void)(kout << "NeFS: Write called with invalid size: " << hex_number(size)); + kout << "\n"; + return; + } + constexpr auto kDataForkName = kNeFSDataFork; + this->Write(kDataForkName, node, data, flags, size); +} + +_Output VoidPtr NeFileSystemMgr::Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT size) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Read called with invalid node pointer\n"; + return nullptr; + } + if (!size || size > kNeFSForkSize) { + (Void)(kout << "NeFS: Write called with invalid size: " << hex_number(size)); + kout << "\n"; + return nullptr; + } + constexpr auto kDataForkName = kNeFSDataFork; + return this->Read(kDataForkName, node, flags, size); +} + +Void NeFileSystemMgr::Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, + _Input Int32 flags, _Input SizeT size) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Write(fork) called with invalid node pointer\n"; + return; + } + if (!name || *name == 0) { + kout << "NeFS: Write(fork) called with null or empty fork name\n"; + return; + } + if (!data) { + kout << "NeFS: Write(fork) called with null data pointer\n"; + return; + } + if (!size || size > kNeFSForkSize) { + (Void)(kout << "NeFS: Write called with invalid size: " << hex_number(size)); + kout << "\n"; + return; + } + NE_UNUSED(flags); + auto cat = reinterpret_cast(node); + if (cat->Kind == kNeFSCatalogKindFile) { + mParser->WriteCatalog(cat->Name, (flags & kFileFlagRsrc ? true : false), data, size, name); + } +} + +_Output VoidPtr NeFileSystemMgr::Read(_Input const Char* name, _Input NodePtr node, + _Input Int32 flags, _Input SizeT sz) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Read(fork) called with invalid node pointer\n"; + return nullptr; + } + if (!name || *name == 0) { + kout << "NeFS: Read(fork) called with null or empty fork name\n"; + return nullptr; + } + if (!sz || sz > kNeFSForkSize) { + (Void)(kout << "NeFS: Write called with invalid size: " << hex_number(sz)); + kout << "\n"; + return nullptr; + } + NE_UNUSED(flags); + auto cat = reinterpret_cast(node); + if (cat->Kind == kNeFSCatalogKindFile) { + return mParser->ReadCatalog(cat, (flags & kFileFlagRsrc ? true : false), sz, name); + } + return nullptr; +} + +_Output Bool NeFileSystemMgr::Seek(NodePtr node, SizeT off) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Seek called with invalid node pointer\n"; + return false; + } + // Allow off == 0 + return mParser->Seek(reinterpret_cast(node), off); +} + +/// @brief Tell current offset within catalog. +/// @param node +/// @return kFileMgrNPos if invalid, else current offset. +_Output SizeT NeFileSystemMgr::Tell(NodePtr node) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Tell called with invalid node pointer\n"; + return kFileMgrNPos; + } + return mParser->Tell(reinterpret_cast(node)); +} + +/// @brief Rewinds the catalog +/// @param node +/// @return False if invalid, nah? calls Seek(node, 0). +_Output Bool NeFileSystemMgr::Rewind(NodePtr node) { + if (!is_valid_nefs_catalog(node)) { + kout << "NeFS: Rewind called with invalid node pointer\n"; + return false; + } + return this->Seek(node, 0); +} + +/// @brief Returns the parser of NeFS. +_Output NeFileSystemParser* NeFileSystemMgr::GetParser() noexcept { + return mParser; +} + +static inline bool is_valid_nefs_catalog(NodePtr node) { + if (!node) return false; + auto cat = reinterpret_cast(node); + switch (cat->Kind) { + case kNeFSCatalogKindFile: + case kNeFSCatalogKindDir: + case kNeFSCatalogKindAlias: + case kNeFSCatalogKindPage: + break; + default: + return false; + } + bool null_found = false; + for (int i = 0; i < kNeFSCatalogNameLen; ++i) { + if (cat->Name[i] == 0) { + null_found = true; + break; + } + } + if (!null_found) return false; + return true; +} + +} // namespace Kernel + +#endif // ifdef __FSKIT_INCLUDES_NEFS__ +#endif // ifndef __NE_MINIMAL_OS__ diff --git a/src/kernel/src/FS/NeFS+FileSystemParser.cc b/src/kernel/src/FS/NeFS+FileSystemParser.cc new file mode 100644 index 00000000..bfb8d63a --- /dev/null +++ b/src/kernel/src/FS/NeFS+FileSystemParser.cc @@ -0,0 +1,870 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifdef __FSKIT_INCLUDES_NEFS__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Kernel; + +#ifdef __NE_NO_BUILTIN__ +/***********************************************************************************/ +/** + Define those external symbols, to make the editor shutup +*/ +/***********************************************************************************/ + +/***********************************************************************************/ +/// @brief get sector count. +/***********************************************************************************/ +Kernel::SizeT drv_std_get_sector_count(); + +/***********************************************************************************/ +/// @brief get device size. +/***********************************************************************************/ +Kernel::SizeT drv_std_get_size(); + +#endif + +///! BUGS: 0 + +/***********************************************************************************/ +/// This file implements the New extended File System. +/// New extended File System implements a flat linked-list based algorithm. +/// / +/// /Path1/ /Path2/ +/// /readme.rtf /ListContents.pef /readme.lnk <-- symlink. +/// /Path1/readme.rtf +/***********************************************************************************/ +static inline bool is_valid_size(SizeT size, SizeT max_size) { + return size > 0 && size <= max_size; +} + +static inline bool is_valid_lba(Lba lba, DriveTrait& drive) { + NEFS_ROOT_PARTITION_BLOCK part_block; + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketContent = reinterpret_cast(&part_block); + drive.fInput(drive.fPacket); + + if (!drive.fPacket.fPacketGood) { + return false; + } + + // Compute the maximum LBA (DiskSize / sector size) + SizeT sectorSize = drive.fSectorSz; + Lba maxLba = part_block.DiskSize / sectorSize; + + return (lba >= part_block.StartCatalog) && (lba < maxLba); +} + +STATIC IMountpoint kMountpoint; +/***********************************************************************************/ +/// @brief Creates a new fork inside the New filesystem partition. +/// @param catalog it's catalog +/// @param the_fork the fork itself. +/// @return the fork +/***********************************************************************************/ +_Output BOOL NeFileSystemParser::CreateFork(_Input NEFS_FORK_STRUCT& the_fork) { + if (the_fork.ForkName[0] == 0 || the_fork.CatalogName[0] == 0 || the_fork.DataSize == 0) { + return NO; + } + + auto catalog = this->GetCatalog(the_fork.CatalogName); + if (!catalog) return NO; + + Lba lba = catalog->DataFork; + if (lba < kNeFSCatalogStartAddress) { + delete catalog; + return NO; + } + + auto& drv = kMountpoint.A(); + Lba lba_prev = lba; + NEFS_FORK_STRUCT prev_fork{}; + NEFS_FORK_STRUCT cur_fork{}; + + while (true) { + drv.fPacket.fPacketLba = lba; + drv.fPacket.fPacketSize = sizeof(NEFS_FORK_STRUCT); + drv.fPacket.fPacketContent = reinterpret_cast(&cur_fork); + drv.fInput(drv.fPacket); + if (!drv.fPacket.fPacketGood) { + delete catalog; + return NO; + } + + if (cur_fork.Flags & kNeFSFlagCreated) { + if (KStringBuilder::Equals(cur_fork.ForkName, the_fork.ForkName) && + KStringBuilder::Equals(cur_fork.CatalogName, the_fork.CatalogName)) { + break; + } + lba_prev = lba; + lba = cur_fork.NextSibling; + prev_fork = cur_fork; + } else { + if (lba >= kNeFSCatalogStartAddress) { + prev_fork.NextSibling = lba; + drv.fPacket.fPacketLba = lba_prev; + drv.fPacket.fPacketSize = sizeof(NEFS_FORK_STRUCT); + drv.fPacket.fPacketContent = reinterpret_cast(&prev_fork); + drv.fOutput(drv.fPacket); + } + break; + } + } + + SizeT sectorCount = (the_fork.DataSize + kNeFSSectorSz - 1) / kNeFSSectorSz; + the_fork.DataOffset = lba + 1; + the_fork.PreviousSibling = lba_prev; + the_fork.NextSibling = lba + 1 + sectorCount; + the_fork.Flags |= kNeFSFlagCreated; + + drv.fPacket.fPacketLba = lba; + drv.fPacket.fPacketSize = sizeof(NEFS_FORK_STRUCT); + drv.fPacket.fPacketContent = reinterpret_cast(&the_fork); + drv.fOutput(drv.fPacket); + + fs_ifs_write(&kMountpoint, drv, IMountpoint::kDriveIndexA); + + delete catalog; + return YES; +} + +/***********************************************************************************/ +/// @brief Find fork inside filesystem. +/// @param catalog the catalog. +/// @param name the fork name. +/// @return the newly found fork. +/***********************************************************************************/ +_Output NEFS_FORK_STRUCT* NeFileSystemParser::FindFork(_Input NEFS_CATALOG_STRUCT* catalog, + _Input const Char* name, + _Input Boolean is_data) { + if (!catalog || !name) return nullptr; + + auto& drive = kMountpoint.A(); + Lba lba = is_data ? catalog->DataFork : catalog->ResourceFork; + NEFS_FORK_STRUCT local_buf{}; + + while (lba >= kNeFSCatalogStartAddress) { + drive.fPacket.fPacketLba = lba; + drive.fPacket.fPacketSize = sizeof(NEFS_FORK_STRUCT); + drive.fPacket.fPacketContent = reinterpret_cast(&local_buf); + + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive.fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive.fPacket.fPacketMime)); + + if (auto res = fs_ifs_read(&kMountpoint, drive, this->mDriveIndex); res) { + switch (res) { + case 1: + err_global_get() = kErrorDiskReadOnly; + break; + case 2: + err_global_get() = kErrorDiskIsFull; + break; + case 3: + err_global_get() = kErrorNoSuchDisk; + break; + default: + break; + } + return nullptr; + } + + if (KStringBuilder::Equals(local_buf.ForkName, name) && + KStringBuilder::Equals(local_buf.CatalogName, catalog->Name)) { + auto result = new NEFS_FORK_STRUCT(); + rt_copy_memory_safe(&local_buf, result, sizeof(NEFS_FORK_STRUCT), sizeof(NEFS_FORK_STRUCT)); + return result; + } + + lba = local_buf.NextSibling; + } + + return nullptr; +} + +/***********************************************************************************/ +/// @brief Simpler factory to create a catalog (assumes you want to create a +/// file.) +/// @param name +/// @return catalog pointer. +/***********************************************************************************/ +_Output NEFS_CATALOG_STRUCT* NeFileSystemParser::CreateCatalog(_Input const Char* name) { + return this->CreateCatalog(name, 0, kNeFSCatalogKindFile); +} + +/***********************************************************************************/ +/// @brief Creates a new catalog into the disk. +/// @param name the catalog name. +/// @param flags the flags of the catalog. +/// @param kind the catalog kind. +/// @return catalog pointer. +/***********************************************************************************/ +_Output NEFS_CATALOG_STRUCT* NeFileSystemParser::CreateCatalog(_Input const Char* name, + _Input const Int32& flags, + _Input const Int32& kind) { + kout << "CreateCatalog(*...*)\r"; + + if (!name) return nullptr; + SizeT nameLen = rt_string_len(name); + if (nameLen == 0) return nullptr; + + Lba out_lba = 0UL; + + kout << "Checking for path separator...\r"; + + /// a directory should have a slash in the end. + if (kind == kNeFSCatalogKindDir && name[nameLen - 1] != NeFileSystemHelper::Separator()) + return nullptr; + + /// a file shouldn't have a slash in the end. + if (kind != kNeFSCatalogKindDir && name[nameLen - 1] == NeFileSystemHelper::Separator()) + return nullptr; + + NEFS_CATALOG_STRUCT* catalog_copy = this->FindCatalog(name, out_lba); + + if (catalog_copy) { + kout << "Catalog already exists: " << name << ".\r"; + err_global_get() = kErrorFileExists; + delete catalog_copy; + catalog_copy = nullptr; + return nullptr; + } + + Char* parent_name = (Char*) mm_alloc_ptr(nameLen + 1, Yes, No); + rt_copy_memory_safe(const_cast(name), parent_name, nameLen + 1, nameLen + 1); + + if (nameLen < 2) { + mm_free_ptr(parent_name); + err_global_get() = kErrorFileNotFound; + return nullptr; + } + + SizeT index_reverse_copy = nameLen - 1; + if (parent_name[index_reverse_copy] == NeFileSystemHelper::Separator()) { + parent_name[index_reverse_copy] = 0; + --index_reverse_copy; + } + while (index_reverse_copy > 0 && + parent_name[index_reverse_copy] != NeFileSystemHelper::Separator()) { + parent_name[index_reverse_copy] = 0; + --index_reverse_copy; + } + if (index_reverse_copy == 0 && parent_name[0] != NeFileSystemHelper::Separator()) { + mm_free_ptr(parent_name); + err_global_get() = kErrorFileNotFound; + return nullptr; + } + + NEFS_CATALOG_STRUCT* catalog = this->FindCatalog(parent_name, out_lba); + mm_free_ptr(parent_name); + + auto& drive = kMountpoint.A(); + if (catalog && catalog->Kind == kNeFSCatalogKindFile) { + kout << "Parent is a file.\r"; + delete catalog; + return nullptr; + } else if (!catalog) { + Char part_block[sizeof(NEFS_ROOT_PARTITION_BLOCK)] = {0}; + drive.fPacket.fPacketContent = reinterpret_cast(part_block); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fInput(drive.fPacket); + + NEFS_ROOT_PARTITION_BLOCK* blk_nefs = (NEFS_ROOT_PARTITION_BLOCK*) part_block; + out_lba = blk_nefs->StartCatalog; + } + + if (drive.fPacket.fPacketReadOnly) { + delete catalog; + return nullptr; + } + + NEFS_CATALOG_STRUCT* child_catalog = new NEFS_CATALOG_STRUCT(); + child_catalog->Checksum = 0; + child_catalog->ResourceForkSize = 0UL; + child_catalog->DataForkSize = 0UL; + child_catalog->CatalogFlags = kNeFSStatusUnlocked; + child_catalog->NextSibling = out_lba; + child_catalog->PrevSibling = out_lba; + child_catalog->Kind = kind; + child_catalog->Flags |= kNeFSFlagCreated; + child_catalog->CatalogFlags = flags; + + SizeT i = nameLen; + --i; + if (kind == kNeFSCatalogKindDir) --i; + while (name[i] != '/') --i; + rt_copy_memory_safe((VoidPtr) (name + i), (VoidPtr) child_catalog->Name, rt_string_len(name) - i, + kNeFSCatalogNameLen); + + NEFS_CATALOG_STRUCT temporary_catalog{}; + Lba start_free = out_lba; + + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive.fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive.fPacket.fPacketMime)); + + Char buf_part_block[sizeof(NEFS_ROOT_PARTITION_BLOCK)] = {0}; + drive.fPacket.fPacketContent = reinterpret_cast(buf_part_block); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fInput(drive.fPacket); + + NEFS_ROOT_PARTITION_BLOCK* part_block = (NEFS_ROOT_PARTITION_BLOCK*) buf_part_block; + drive.fPacket.fPacketContent = reinterpret_cast(&temporary_catalog); + drive.fPacket.fPacketSize = sizeof(NEFS_CATALOG_STRUCT); + drive.fPacket.fPacketLba = start_free; + drive.fInput(drive.fPacket); + + if (part_block->FreeCatalog < 1) { + delete child_catalog; + delete catalog; + return nullptr; + } + + kout << "Start finding catalog to allocate or empty space...\r"; + + SizeT catalogSectors = (sizeof(NEFS_CATALOG_STRUCT) + drive.fSectorSz - 1) / drive.fSectorSz; + while (start_free < part_block->StartCatalog + (part_block->CatalogCount * catalogSectors)) { + drive.fPacket.fPacketContent = reinterpret_cast(&temporary_catalog); + drive.fPacket.fPacketSize = sizeof(NEFS_CATALOG_STRUCT); + drive.fPacket.fPacketLba = start_free; + drive.fInput(drive.fPacket); + + if ((temporary_catalog.Flags & kNeFSFlagCreated) == 0) { + child_catalog->NextSibling = start_free + catalogSectors; + + NEFS_CATALOG_STRUCT placeholder{}; + drive.fPacket.fPacketContent = reinterpret_cast(&placeholder); + drive.fPacket.fPacketSize = sizeof(NEFS_CATALOG_STRUCT); + drive.fPacket.fPacketLba = start_free; + drive.fOutput(drive.fPacket); + + child_catalog->DataFork = part_block->DiskSize - start_free; + child_catalog->ResourceFork = child_catalog->DataFork; + + drive.fPacket.fPacketContent = reinterpret_cast(child_catalog); + drive.fPacket.fPacketSize = sizeof(NEFS_CATALOG_STRUCT); + drive.fPacket.fPacketLba = start_free; + drive.fOutput(drive.fPacket); + + drive.fPacket.fPacketContent = reinterpret_cast(buf_part_block); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fInput(drive.fPacket); + + part_block->FreeSectors -= catalogSectors; + part_block->CatalogCount += 1; + part_block->FreeCatalog -= 1; + + drive.fPacket.fPacketContent = reinterpret_cast(part_block); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fOutput(drive.fPacket); + + delete catalog; + NEFS_CATALOG_STRUCT* found_catalog = new NEFS_CATALOG_STRUCT(); + rt_copy_memory_safe(&temporary_catalog, found_catalog, sizeof(NEFS_CATALOG_STRUCT), + sizeof(NEFS_CATALOG_STRUCT)); + + delete child_catalog; + return found_catalog; + } else if ((temporary_catalog.Flags & kNeFSFlagCreated) && + KStringBuilder::Equals(temporary_catalog.Name, name)) { + rt_copy_memory_safe(&temporary_catalog, child_catalog, sizeof(NEFS_CATALOG_STRUCT), + sizeof(NEFS_CATALOG_STRUCT)); + delete catalog; + return child_catalog; + } + + start_free += catalogSectors; + } + + delete child_catalog; + delete catalog; + return nullptr; +} + +/***********************************************************************************/ +/// @brief Make a EPM+NeFS drive out of the disk. +/// @param drive The drive to write on. +/// @return If it was sucessful, see err_global_get(). +/***********************************************************************************/ +bool NeFileSystemParser::Format(_Input _Output DriveTrait* drive, _Input const Int32 flags, + const Char* part_name) { + if (!part_name || *part_name == 0) return false; + NE_UNUSED(flags); + + // verify disk. + drive->fVerify(drive->fPacket); + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive->fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive->fPacket.fPacketMime)); + if (!drive->fPacket.fPacketGood) { + err_global_get() = kErrorDiskIsCorrupted; + return false; + } + + Char fs_buf[sizeof(NEFS_ROOT_PARTITION_BLOCK)] = {0}; + Lba start = drive->fLbaStart; + + drive->fPacket.fPacketContent = reinterpret_cast(fs_buf); + drive->fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive->fPacket.fPacketLba = start; + drive->fInput(drive->fPacket); + + NEFS_ROOT_PARTITION_BLOCK* part_block = (NEFS_ROOT_PARTITION_BLOCK*) fs_buf; + if (rt_string_cmp(kNeFSIdent, part_block->Ident, kNeFSIdentLen) == 0) return true; + + const auto kNeFSUntitledHD = part_name; + rt_copy_memory_safe((VoidPtr) kNeFSIdent, (VoidPtr) part_block->Ident, kNeFSIdentLen, + sizeof(part_block->Ident)); + rt_copy_memory_safe((VoidPtr) kNeFSUntitledHD, (VoidPtr) part_block->PartitionName, + rt_string_len(kNeFSUntitledHD), sizeof(part_block->PartitionName)); + + SizeT sectorCount = drv_std_get_sector_count(); + SizeT sectorSize = drive->fSectorSz; + SizeT totalBytes = sectorCount * sectorSize; + SizeT catalogEntries = totalBytes / sizeof(NEFS_CATALOG_STRUCT); + SizeT catalogSectors = (sizeof(NEFS_CATALOG_STRUCT) + sectorSize - 1) / sectorSize; + + part_block->Version = kNeFSVersionInteger; + part_block->Kind = kNeFSPartitionTypeStandard; + part_block->StartCatalog = start + catalogSectors; + part_block->Flags = 0UL; + part_block->CatalogCount = catalogEntries; + part_block->FreeCatalog = catalogEntries - 1; + part_block->SectorCount = sectorCount; + part_block->DiskSize = totalBytes; + part_block->SectorSize = sectorSize; + part_block->FreeSectors = sectorCount - catalogSectors; + + drive->fPacket.fPacketContent = reinterpret_cast(fs_buf); + drive->fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive->fPacket.fPacketLba = start; + drive->fOutput(drive->fPacket); + + (Void)(kout << "Drive kind: " << drive->fProtocol() << kendl); + (Void)(kout << "Partition name: " << part_block->PartitionName << kendl); + (Void)(kout << "Start catalog: " << hex_number(part_block->StartCatalog) << kendl); + (Void)(kout << "Number of catalogs: " << hex_number(part_block->CatalogCount) << kendl); + (Void)(kout << "Free catalog: " << hex_number(part_block->FreeCatalog) << kendl); + (Void)(kout << "Free sectors: " << hex_number(part_block->FreeSectors) << kendl); + (Void)(kout << "Sector size: " << hex_number(part_block->SectorSize) << kendl); + + return true; +} + +/***********************************************************************************/ +/// @brief Writes the data fork into a specific catalog. +/// @param catalog the catalog itself +/// @param data the data. +/// @return if the catalog wrote the contents successfully. +/***********************************************************************************/ +bool NeFileSystemParser::WriteCatalog(_Input const Char* catalog_name, Bool is_rsrc_fork, + _Input VoidPtr data, _Input SizeT size_of_data, + _Input const Char* fork_name) { + if (size_of_data < 1) return NO; + + auto catalog = this->GetCatalog(catalog_name); + if (!catalog) { + kout << "NeFS: WriteCatalog failed to find catalog: " << catalog_name << "\n"; + return false; + } + + SizeT maxSize = is_rsrc_fork ? catalog->ResourceForkSize : catalog->DataForkSize; + + if (!is_valid_size(size_of_data, maxSize)) { + (Void)(kout << "NeFS: WriteCatalog called with invalid size: " << hex_number(size_of_data)); + kout << "\n"; + + delete catalog; + return false; + } + + Lba startFork = is_rsrc_fork ? catalog->ResourceFork : catalog->DataFork; + auto& drive = kMountpoint.A(); + + if (!is_valid_lba(startFork, drive)) { + (Void)(kout << "NeFS: WriteCatalog called with invalid LBA: " << hex_number(startFork)); + kout << "\n"; + + delete catalog; + return false; + } + + NEFS_ROOT_PARTITION_BLOCK part_block; + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fPacket.fPacketSize = sizeof(part_block); + drive.fPacket.fPacketContent = reinterpret_cast(&part_block); + drive.fInput(drive.fPacket); + + auto buf = new UInt8[size_of_data]; + rt_set_memory(buf, 0, size_of_data); + rt_copy_memory_safe(data, buf, size_of_data, size_of_data); + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive.fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive.fPacket.fPacketMime)); + + NEFS_FORK_STRUCT* fork_data_input = new NEFS_FORK_STRUCT(); + NEFS_FORK_STRUCT prev_fork{}; + + while (startFork >= part_block.StartCatalog && drive.fPacket.fPacketGood) { + drive.fPacket.fPacketContent = reinterpret_cast(fork_data_input); + drive.fPacket.fPacketSize = sizeof(NEFS_FORK_STRUCT); + drive.fPacket.fPacketLba = startFork; + drive.fInput(drive.fPacket); + + if (!drive.fPacket.fPacketGood) { + break; + } + + if ((fork_data_input->Flags & kNeFSFlagCreated) && + KStringBuilder::Equals(fork_data_input->ForkName, fork_name) && + KStringBuilder::Equals(fork_data_input->CatalogName, catalog_name) && + fork_data_input->DataSize == size_of_data) { + SizeT bytes_left = size_of_data; + SizeT offset = 0; + Lba base_lba = fork_data_input->DataOffset; + + while (bytes_left > 0) { + SizeT chunk = (bytes_left > kNeFSSectorSz) ? kNeFSSectorSz : bytes_left; + drive.fPacket.fPacketContent = reinterpret_cast(buf + offset); + drive.fPacket.fPacketSize = chunk; + drive.fPacket.fPacketLba = base_lba + (offset / kNeFSSectorSz); + drive.fOutput(drive.fPacket); + offset += chunk; + bytes_left -= chunk; + } + + delete fork_data_input; + delete[] buf; + delete catalog; + return true; + } + + prev_fork = *fork_data_input; + startFork = fork_data_input->NextSibling; + } + + delete fork_data_input; + delete[] buf; + delete catalog; + return false; +} + +/***********************************************************************************/ +/// @brief +/// @param catalog_name the catalog name. +/// @return the newly found catalog. +/***********************************************************************************/ +_Output NEFS_CATALOG_STRUCT* NeFileSystemParser::FindCatalog(_Input const Char* catalog_name, + Lba& out_lba, Bool search_hidden, + Bool local_search) { + if (!catalog_name || *catalog_name == 0) return nullptr; + + NEFS_ROOT_PARTITION_BLOCK part{}; + auto& drive = kMountpoint.A(); + + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive.fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive.fPacket.fPacketMime)); + drive.fPacket.fPacketContent = reinterpret_cast(&part); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fInput(drive.fPacket); + + auto start_catalog_lba = kNeFSCatalogStartAddress; + + // Helper lambda to scan from a given LBA + auto scan_from = [&](Lba lba_start, Bool allow_hidden) -> NEFS_CATALOG_STRUCT* { + Lba cursor = lba_start; + NEFS_CATALOG_STRUCT tmp{}; + while (cursor >= part.StartCatalog && drive.fPacket.fPacketGood) { + drive.fPacket.fPacketLba = cursor; + drive.fPacket.fPacketContent = reinterpret_cast(&tmp); + drive.fPacket.fPacketSize = sizeof(NEFS_CATALOG_STRUCT); + drive.fInput(drive.fPacket); + + if (KStringBuilder::Equals( + tmp.Name, catalog_name + (rt_string_len(catalog_name) - rt_string_len(tmp.Name)))) { + if (tmp.Status == kNeFSStatusLocked && !allow_hidden) { + err_global_get() = kErrorFileLocked; + return nullptr; + } + if (!(tmp.Flags & kNeFSFlagCreated)) { + err_global_get() = kErrorFileNotFound; + return nullptr; + } + NEFS_CATALOG_STRUCT* catalog_ptr = new NEFS_CATALOG_STRUCT(); + rt_copy_memory_safe(&tmp, catalog_ptr, sizeof(NEFS_CATALOG_STRUCT), + sizeof(NEFS_CATALOG_STRUCT)); + out_lba = cursor; + return catalog_ptr; + } + cursor = tmp.NextSibling; + } + return nullptr; + }; + + if (!KStringBuilder::Equals(catalog_name, NeFileSystemHelper::Root()) && local_search) { + Char parent_name[kNeFSCatalogNameLen] = {0}; + SizeT nameLen = rt_string_len(catalog_name); + rt_copy_memory_safe(const_cast(catalog_name), parent_name, nameLen + 1, + kNeFSCatalogNameLen); + + SizeT indexReverseCopy = nameLen - 1; + if (parent_name[indexReverseCopy] == NeFileSystemHelper::Separator()) { + parent_name[indexReverseCopy] = 0; + --indexReverseCopy; + } + while (indexReverseCopy > 0 && + parent_name[indexReverseCopy] != NeFileSystemHelper::Separator()) { + parent_name[indexReverseCopy] = 0; + --indexReverseCopy; + } + if (indexReverseCopy == 0 && parent_name[0] != NeFileSystemHelper::Separator()) { + return nullptr; + } + + NEFS_CATALOG_STRUCT* parent_catalog = + this->FindCatalog(parent_name, out_lba, search_hidden, NO); + if (parent_catalog) { + start_catalog_lba = parent_catalog->NextSibling; + delete parent_catalog; + NEFS_CATALOG_STRUCT* found = scan_from(start_catalog_lba, search_hidden); + if (found) return found; + } + } + + return scan_from(part.StartCatalog, search_hidden); +} + +/***********************************************************************************/ +/// @brief Get catalog from filesystem. +/// @param name the catalog's name/ +/// @return +/***********************************************************************************/ +_Output NEFS_CATALOG_STRUCT* NeFileSystemParser::GetCatalog(_Input const Char* name) { + Lba unused = 0; + return this->FindCatalog(name, unused, YES, YES); +} + +/***********************************************************************************/ +/// @brief Closes a catalog, (frees it). +/// @param catalog the catalog to close. +/// @return +/***********************************************************************************/ +_Output Boolean NeFileSystemParser::CloseCatalog(_Input _Output NEFS_CATALOG_STRUCT* catalog) { + if (!catalog) return false; + delete catalog; + catalog = nullptr; + return true; +} + +/***********************************************************************************/ +/// @brief Mark catalog as removed. +/// @param catalog The catalog structure. +/// @return if the catalog was removed or not. +/***********************************************************************************/ +_Output Boolean NeFileSystemParser::RemoveCatalog(_Input const Char* catalog_name) { + if (!catalog_name || KStringBuilder::Equals(catalog_name, NeFileSystemHelper::Root())) { + err_global_get() = kErrorInternal; + return false; + } + + Lba out_lba = 0; + auto catalog = this->FindCatalog(catalog_name, out_lba, YES, YES); + if (!catalog) return false; + + auto& drive = kMountpoint.A(); + NEFS_FORK_STRUCT fork_buf{}; + Lba fork_lba = catalog->DataFork; + while (fork_lba >= kNeFSCatalogStartAddress) { + drive.fPacket.fPacketLba = fork_lba; + drive.fPacket.fPacketSize = sizeof(fork_buf); + drive.fPacket.fPacketContent = reinterpret_cast(&fork_buf); + drive.fInput(drive.fPacket); + + fork_buf.Flags &= (~kNeFSFlagCreated); + fork_buf.Flags |= kNeFSFlagDeleted; + + drive.fPacket.fPacketContent = reinterpret_cast(&fork_buf); + drive.fPacket.fPacketSize = sizeof(fork_buf); + drive.fPacket.fPacketLba = fork_lba; + drive.fOutput(drive.fPacket); + + fork_lba = fork_buf.NextSibling; + } + + fork_lba = catalog->ResourceFork; + while (fork_lba >= kNeFSCatalogStartAddress) { + drive.fPacket.fPacketLba = fork_lba; + drive.fPacket.fPacketSize = sizeof(fork_buf); + drive.fPacket.fPacketContent = reinterpret_cast(&fork_buf); + drive.fInput(drive.fPacket); + + fork_buf.Flags &= (~kNeFSFlagCreated); + fork_buf.Flags |= kNeFSFlagDeleted; + + drive.fPacket.fPacketContent = reinterpret_cast(&fork_buf); + drive.fPacket.fPacketSize = sizeof(fork_buf); + drive.fPacket.fPacketLba = fork_lba; + drive.fOutput(drive.fPacket); + + fork_lba = fork_buf.NextSibling; + } + + if (out_lba >= kNeFSCatalogStartAddress || (catalog->Flags & kNeFSFlagCreated)) { + catalog->Flags &= (~kNeFSFlagCreated); + catalog->Flags |= kNeFSFlagDeleted; + + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive.fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive.fPacket.fPacketMime)); + drive.fPacket.fPacketLba = out_lba; + drive.fPacket.fPacketSize = sizeof(NEFS_CATALOG_STRUCT); + drive.fPacket.fPacketContent = reinterpret_cast(catalog); + drive.fOutput(drive.fPacket); + + Char partitionBlockBuf[sizeof(NEFS_ROOT_PARTITION_BLOCK)] = {0}; + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fPacket.fPacketContent = reinterpret_cast(partitionBlockBuf); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fInput(drive.fPacket); + + NEFS_ROOT_PARTITION_BLOCK* part_block = (NEFS_ROOT_PARTITION_BLOCK*) partitionBlockBuf; + if (part_block->CatalogCount > 0) --part_block->CatalogCount; + ++part_block->FreeSectors; + + drive.fPacket.fPacketContent = reinterpret_cast(part_block); + drive.fPacket.fPacketSize = sizeof(NEFS_ROOT_PARTITION_BLOCK); + drive.fPacket.fPacketLba = kNeFSRootCatalogStartAddress; + drive.fOutput(drive.fPacket); + + delete catalog; + catalog = nullptr; + return true; + } + + delete catalog; + catalog = nullptr; + return false; +} + +/// ***************************************************************** /// +/// Reading,Seek,Tell are unimplemented on catalogs, refer to forks I/O instead. +/// ***************************************************************** /// + +/***********************************************************************************/ +/// @brief Read the catalog data fork. +/// @param catalog +/// @param dataSz +/// @return +/***********************************************************************************/ +VoidPtr NeFileSystemParser::ReadCatalog(_Input _Output NEFS_CATALOG_STRUCT* catalog, + _Input Bool is_rsrc_fork, _Input SizeT dataSz, + _Input const Char* forkName) { + if (!catalog) { + err_global_get() = kErrorInvalidData; + return nullptr; + } + // Validate size against fork size + SizeT maxSize = is_rsrc_fork ? catalog->ResourceForkSize : catalog->DataForkSize; + if (!is_valid_size(dataSz, maxSize)) { + kout << "NeFS: ReadCatalog called with invalid size: "; + hex_number(dataSz); + kout << "\n"; + return nullptr; + } + + Lba dataForkLba = is_rsrc_fork ? catalog->ResourceFork : catalog->DataFork; + auto& drive = kMountpoint.A(); + if (!is_valid_lba(dataForkLba, drive)) { + kout << "NeFS: ReadCatalog called with invalid LBA: "; + hex_number(dataForkLba); + kout << "\n"; + return nullptr; + } + + auto* fs_buf = new NEFS_FORK_STRUCT(); + rt_copy_memory_safe((VoidPtr) "fs/nefs-packet", drive.fPacket.fPacketMime, + rt_string_len("fs/nefs-packet"), sizeof(drive.fPacket.fPacketMime)); + + NEFS_FORK_STRUCT* fs_fork_data = nullptr; + while (dataForkLba >= kNeFSCatalogStartAddress) { + drive.fPacket.fPacketLba = dataForkLba; + drive.fPacket.fPacketSize = sizeof(NEFS_FORK_STRUCT); + drive.fPacket.fPacketContent = reinterpret_cast(fs_buf); + drive.fInput(drive.fPacket); + + fs_fork_data = fs_buf; + (Void)(kout << "ForkName: " << fs_fork_data->ForkName << kendl); + (Void)(kout << "CatalogName: " << fs_fork_data->CatalogName << kendl); + + if (KStringBuilder::Equals(forkName, fs_fork_data->ForkName) && + KStringBuilder::Equals(catalog->Name, fs_fork_data->CatalogName)) { + break; + } + dataForkLba = fs_fork_data->NextSibling; + } + + if (dataForkLba < kNeFSCatalogStartAddress) { + delete fs_buf; + return nullptr; + } + return fs_fork_data; +} + +/***********************************************************************************/ +/// @brief Seek in the data fork. +/// @param catalog the catalog offset. +/// @param off where to seek. +/// @return if the seeking was successful. +/***********************************************************************************/ +bool NeFileSystemParser::Seek(_Input _Output NEFS_CATALOG_STRUCT* catalog, SizeT off) { + NE_UNUSED(catalog); + NE_UNUSED(off); + err_global_get() = kErrorUnimplemented; + return false; +} + +/***********************************************************************************/ +/// @brief Tell where we are inside the data fork. +/// @param catalog +/// @return The position on the file. +/***********************************************************************************/ +SizeT NeFileSystemParser::Tell(_Input _Output NEFS_CATALOG_STRUCT* catalog) { + NE_UNUSED(catalog); + err_global_get() = kErrorUnimplemented; + return 0; +} + +namespace Kernel::NeFS { +/***********************************************************************************/ +/// @brief Construct NeFS drives. +/***********************************************************************************/ +Boolean fs_init_nefs(Void) noexcept { + kout << "Creating OpenHeFS disk...\r"; + kMountpoint.A() = io_construct_main_drive(); + if (kMountpoint.A().fPacket.fPacketReadOnly == YES) + ke_panic(RUNTIME_CHECK_FILESYSTEM, "Main disk cannot be mounted."); + NeFileSystemParser parser; + return parser.Format(&kMountpoint.A(), 0, kNeFSVolumeName); +} +} // namespace Kernel::NeFS + +#endif // ifdef __FSKIT_INCLUDES_NEFS__ diff --git a/src/kernel/src/FS/OpenHeFS+FileMgr.cc b/src/kernel/src/FS/OpenHeFS+FileMgr.cc new file mode 100644 index 00000000..7340514a --- /dev/null +++ b/src/kernel/src/FS/OpenHeFS+FileMgr.cc @@ -0,0 +1,191 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifndef __NE_MINIMAL_OS__ +#ifdef __FSKIT_INCLUDES_OPENHEFS__ + +#include +#include + +/// @brief OpenHeFS File System Manager. +/// BUGS: 0 + +namespace Kernel { +/// @brief C++ constructor +HeFileSystemMgr::HeFileSystemMgr() { + mParser = new HeFileSystemParser(); + MUST_PASS(mParser); + + kout << "We are done allocating HeFileSystemParser...\n"; +} + +HeFileSystemMgr::~HeFileSystemMgr() { + if (mParser) { + kout << "Destroying HeFileSystemParser...\n"; + delete mParser; + mParser = nullptr; + } +} + +/// @brief Removes a node from the filesystem. +/// @param path The filename +/// @return If it was deleted or not. +bool HeFileSystemMgr::Remove(_Input const Char* path) { + if (path == nullptr || *path == 0) { + kout << "OpenHeFS: Remove called with null or empty path\n"; + return false; + } + + return NO; +} + +/// @brief Creates a node with the specified. +/// @param path The filename path. +/// @return The Node pointer. +NodePtr HeFileSystemMgr::Create(_Input const Char* path) { + if (!path || *path == 0) { + kout << "OpenHeFS: Create called with null or empty path\n"; + return nullptr; + } + return nullptr; +} + +/// @brief Creates a node which is a directory. +/// @param path The filename path. +/// @return The Node pointer. +NodePtr HeFileSystemMgr::CreateDirectory(const Char* path) { + if (!path || *path == 0) { + kout << "OpenHeFS: CreateDirectory called with null or empty path\n"; + return nullptr; + } + return nullptr; +} + +/// @brief Creates a node which is an alias. +/// @param path The filename path. +/// @return The Node pointer. +NodePtr HeFileSystemMgr::CreateAlias(const Char* path) { + if (!path || *path == 0) { + kout << "OpenHeFS: CreateAlias called with null or empty path\n"; + return nullptr; + } + return nullptr; +} + +NodePtr HeFileSystemMgr::CreateSwapFile(const Char* path) { + if (!path || *path == 0) { + kout << "OpenHeFS: CreateSwapFile called with null or empty path\n"; + return nullptr; + } + return nullptr; +} + +/// @brief Gets the root directory. +/// @return +const Char* NeFileSystemHelper::Root() { + return kOpenHeFSRootDirectory; +} + +/// @brief Gets the up-dir directory. +/// @return +const Char* NeFileSystemHelper::UpDir() { + return kOpenHeFSUpDir; +} + +/// @brief Gets the separator character. +/// @return +Char NeFileSystemHelper::Separator() { + return kOpenHeFSSeparator; +} + +/// @brief Gets the metafile character. +/// @return +Char NeFileSystemHelper::MetaFile() { + return '\0'; +} + +/// @brief Opens a new file. +/// @param path +/// @param r +/// @return +_Output NodePtr HeFileSystemMgr::Open(_Input const Char* path, _Input const Char* r) { + if (!path || *path == 0) { + kout << "OpenHeFS: Open called with null or empty path\n"; + return nullptr; + } + if (!r || *r == 0) { + kout << "OpenHeFS: Open called with null or empty mode string\n"; + return nullptr; + } + return nullptr; +} + +Void HeFileSystemMgr::Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) { + NE_UNUSED(node); + NE_UNUSED(flags); + NE_UNUSED(size); + NE_UNUSED(data); +} + +_Output VoidPtr HeFileSystemMgr::Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT size) { + NE_UNUSED(node); + NE_UNUSED(flags); + NE_UNUSED(size); + + return nullptr; +} + +Void HeFileSystemMgr::Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, + _Input Int32 flags, _Input SizeT size) { + NE_UNUSED(node); + NE_UNUSED(flags); + NE_UNUSED(size); + NE_UNUSED(name); + NE_UNUSED(data); +} + +_Output VoidPtr HeFileSystemMgr::Read(_Input const Char* name, _Input NodePtr node, + _Input Int32 flags, _Input SizeT sz) { + NE_UNUSED(node); + NE_UNUSED(flags); + NE_UNUSED(sz); + NE_UNUSED(name); + + return nullptr; +} + +_Output Bool HeFileSystemMgr::Seek(NodePtr node, SizeT off) { + NE_UNUSED(node); + NE_UNUSED(off); + + return false; +} + +/// @brief Tell current offset within catalog. +/// @param node +/// @return kFileMgrNPos if invalid, else current offset. +_Output SizeT HeFileSystemMgr::Tell(NodePtr node) { + NE_UNUSED(node); + return kFileMgrNPos; +} + +/// @brief Rewinds the catalog +/// @param node +/// @return False if invalid, nah? calls Seek(node, 0). +_Output Bool HeFileSystemMgr::Rewind(NodePtr node) { + NE_UNUSED(node); + return kFileMgrNPos; +} + +/// @brief Returns the parser of OpenHeFS. +_Output HeFileSystemParser* HeFileSystemMgr::GetParser() noexcept { + return mParser; +} +} // namespace Kernel + +#endif // ifdef __FSKIT_INCLUDES_OPENHEFS__ +#endif // ifndef __NE_MINIMAL_OS__ diff --git a/src/kernel/src/FS/OpenHeFS+FileSystemParser.cc b/src/kernel/src/FS/OpenHeFS+FileSystemParser.cc new file mode 100644 index 00000000..d6aeb992 --- /dev/null +++ b/src/kernel/src/FS/OpenHeFS+FileSystemParser.cc @@ -0,0 +1,1160 @@ +/* ======================================== + + Copyright (C) 2024-2025 Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#ifdef __FSKIT_INCLUDES_OPENHEFS__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { +namespace Detail { + /// @brief Forward declarations of internal functions. + + /***********************************************************************************/ + /// @brief Traverse the RB-Tree of the filesystem. + /// @param dir The directory to traverse. + /// @param start The starting point of the traversal. + /// @note This function is used to traverse the RB-Tree of the filesystem. + /// @internal Internal filesystem use only. + /***********************************************************************************/ + STATIC ATTRIBUTE(unused) _Output Void + hefsi_traverse_tree(HEFS_INDEX_NODE_DIRECTORY* dir, DriveTrait* mnt, const Lba& start_ind, + Lba& start); + + /***********************************************************************************/ + /// @brief Get the index node of a file or directory. + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read from. + /// @param dir_name The name of the directory. + /// @param file_name The name of the file. + /// @param kind The kind of the file (regular, directory, block, character, FIFO, socket, symbolic + /// link, unknown). + /***********************************************************************************/ + STATIC ATTRIBUTE(unused) _Output HEFS_INDEX_NODE* hefsi_fetch_in(HEFS_BOOT_NODE* boot, + DriveTrait* mnt, + const Utf8Char* dir_name, + const Utf8Char* file_name, + UInt8 kind); + + /***********************************************************************************/ + /// @brief Allocate a new index node-> + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read/write from. + /// @param dir_name The name of the parent directory. + /// @return Status, see err_global_get(). + /***********************************************************************************/ + STATIC ATTRIBUTE(unused) _Output BOOL + hefsi_update_in_status(HEFS_BOOT_NODE* boot, DriveTrait* mnt, const Utf8Char* dir_name, + HEFS_INDEX_NODE* node, const BOOL create_or_delete); + + /***********************************************************************************/ + /// @brief Balance RB-Tree of the filesystem. + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read/write from. + /// @return Status, see err_global_get(). + /***********************************************************************************/ + STATIC ATTRIBUTE(unused) _Output BOOL hefsi_balance_ind(HEFS_BOOT_NODE* boot, DriveTrait* mnt); + + /// @brief Alllocate IND from boot node. + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read from. + /// @param dir_name The name of the directory. + /// @param dir_name The parent of the directory. + /// @param flags Directory flags. + /// @param delete_or_create Delete or create directory. + STATIC _Output BOOL hefsi_update_ind_status(HEFS_BOOT_NODE* boot, DriveTrait* mnt, + const Utf8Char* dir_name, UInt16 flags, + const BOOL delete_or_create); + + /// @brief This helper makes it easier for other machines to understand OpenHeFS encoded hashes. + STATIC UInt64 hefsi_to_big_endian_64(UInt64 val) { + return ((val >> 56) & 0x00000000000000FFULL) | ((val >> 40) & 0x000000000000FF00ULL) | + ((val >> 24) & 0x0000000000FF0000ULL) | ((val >> 8) & 0x00000000FF000000ULL) | + ((val << 8) & 0x000000FF00000000ULL) | ((val << 24) & 0x0000FF0000000000ULL) | + ((val << 40) & 0x00FF000000000000ULL) | ((val << 56) & 0xFF00000000000000ULL); + } + + /// @brief Simple algorithm to hash directory entries for INDs. + /// @param path the directory path. + /// @return The hashed path. + template + STATIC UInt64 hefsi_hash_64(const CharT* path) { + if (!path || *path == 0) return 0; + + const UInt64 kFnvBaseOffset = 0xcbf29ce484222325ULL; + const UInt64 kFnvPrimeNumber = 0x100000001b3ULL; + + UInt64 hash = kFnvBaseOffset; + + while (*path) { + hash ^= (CharT) (*path++); + hash *= kFnvPrimeNumber; + } + + return hefsi_to_big_endian_64(hash); + } + + /// @brief Traverse the RB-Tree of the filesystem. + /// @param dir The directory to traverse. + /// @param start The starting point of the traversal. + /// @note This function is used to traverse the RB-Tree of the filesystem. + /// @internal Internal filesystem use only. + STATIC ATTRIBUTE(unused) Void hefsi_traverse_tree(HEFS_INDEX_NODE_DIRECTORY* dir, DriveTrait* mnt, + const Lba& ind_start, Lba& start) { + if (!mnt || !dir) return; + + BOOL check_is_good = NO; + HEFS_INDEX_NODE_DIRECTORY* dir_tmp = new HEFS_INDEX_NODE_DIRECTORY(); + + while (YES) { + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir_tmp; + mnt->fInput(mnt->fPacket); + + if (!mnt->fPacket.fPacketGood) break; + + if (dir_tmp->fNext != 0) { + if (check_is_good) break; + + start = dir_tmp->fNext; + + check_is_good = YES; + continue; + } else if (dir_tmp->fPrev != 0) { + if (check_is_good) break; + + start = dir_tmp->fPrev; + check_is_good = YES; + continue; + } else { + if (dir_tmp->fParent != 0) { + if (check_is_good) break; + + start = dir_tmp->fParent; + check_is_good = YES; + continue; + } else if (dir_tmp->fPrev != 0) { + if (check_is_good) break; + + start = dir_tmp->fPrev; + check_is_good = YES; + continue; + } else { + if (start == 0) { + start = ind_start; + continue; + } + + start += sizeof(HEFS_INDEX_NODE_DIRECTORY); + break; + } + } + } + + delete dir_tmp; + + start += sizeof(HEFS_INDEX_NODE_DIRECTORY); + if (start == 0) start = ind_start; + } + + /***********************************************************************************/ + /// @brief Rotate the RB-Tree to the left or right. + /// @internal + /***********************************************************************************/ + STATIC ATTRIBUTE(unused) _Output Void hefsi_rotate_tree(Lba& start, DriveTrait* mnt) { + if (!start || !mnt) return; + + HEFS_INDEX_NODE_DIRECTORY* cur = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = cur; + + mnt->fInput(mnt->fPacket); + + if (cur->fHashPath == 0) return; + + HEFS_INDEX_NODE_DIRECTORY* sibling = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = cur->fPrev; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = sibling; + + mnt->fInput(mnt->fPacket); + + if (sibling->fHashPath == 0) return; + + auto child_sibling = sibling->fChild; + auto child_cur = cur->fChild; + + cur->fChild = child_sibling; + sibling->fChild = child_cur; + + sibling->fChecksum = ke_calculate_crc32((Char*) sibling, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + cur->fChecksum = ke_calculate_crc32((Char*) cur, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = cur->fParent; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = sibling; + + mnt->fOutput(mnt->fPacket); + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = cur; + + mnt->fOutput(mnt->fPacket); + + HEFS_INDEX_NODE_DIRECTORY* sibling_child = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = child_sibling; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = sibling_child; + + mnt->fInput(mnt->fPacket); + + sibling_child->fParent = cur->fParent; + + sibling_child->fChecksum = + ke_calculate_crc32((Char*) sibling, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fOutput(mnt->fPacket); + + HEFS_INDEX_NODE_DIRECTORY* cur_child = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = child_cur; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = cur_child; + + mnt->fInput(mnt->fPacket); + + cur_child->fParent = start; + + cur_child->fChecksum = ke_calculate_crc32((Char*) sibling, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fOutput(mnt->fPacket); + + kout << "RB-Tree has been rotated.\r"; + } + + /// @brief Alllocate IND from boot node. + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read from. + /// @param dir_name The name of the directory. + /// @param dir_name The parent of the directory. + /// @param flags Directory flags. + /// @param delete_or_create Delete or create directory. + STATIC _Output BOOL hefsi_update_ind_status(HEFS_BOOT_NODE* boot, DriveTrait* mnt, + const Utf8Char* dir_name, UInt16 flags, + const BOOL delete_or_create) { + if (mnt) { + HEFS_INDEX_NODE_DIRECTORY* tmpdir = + (HEFS_INDEX_NODE_DIRECTORY*) mm_alloc_ptr(sizeof(HEFS_INDEX_NODE_DIRECTORY), Yes, No); + + auto start = boot->fStartIND; + auto prev_location = start; + auto parent_location = 0UL; + + MUST_PASS(boot->fStartIND > mnt->fLbaStart); + + while (YES) { + auto prev_start = start; + + if (start) + mnt->fPacket.fPacketLba = start; + else + mnt->fPacket.fPacketLba = prev_location + sizeof(HEFS_INDEX_NODE_DIRECTORY); + + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = tmpdir; + + mnt->fInput(mnt->fPacket); + + BOOL expr = NO; + + if (!delete_or_create) { + expr = (!tmpdir->fCreated && tmpdir->fDeleted) || tmpdir->fHashPath == 0; + } else { + expr = + tmpdir->fCreated && !tmpdir->fDeleted && hefsi_hash_64(dir_name) == tmpdir->fHashPath; + } + + if (expr) { + HEFS_INDEX_NODE_DIRECTORY* dirent = + (HEFS_INDEX_NODE_DIRECTORY*) mm_alloc_ptr(sizeof(HEFS_INDEX_NODE_DIRECTORY), Yes, No); + + rt_set_memory(dirent, 0, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + dirent->fHashPath = delete_or_create ? 0UL : hefsi_hash_64(dir_name); + dirent->fAccessed = 0UL; + dirent->fCreated = delete_or_create ? 0UL : 1UL; + dirent->fDeleted = delete_or_create ? 1UL : 0UL; + dirent->fModified = 0UL; + dirent->fEntryCount = 0UL; + + dirent->fReserved = 0; + dirent->fFlags = flags; + dirent->fChecksum = 0; + + dirent->fEntryCount = 0; + + if (parent_location) { + HEFS_INDEX_NODE_DIRECTORY* tmpend = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = parent_location; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = tmpend; + + mnt->fInput(mnt->fPacket); + + if (tmpend->fChecksum != + ke_calculate_crc32((Char*) tmpend, sizeof(HEFS_INDEX_NODE_DIRECTORY))) + ke_panic(RUNTIME_CHECK_FILESYSTEM, "Bad CRC32 value, aborting."); + + if (delete_or_create) + --tmpend->fEntryCount; + else + ++tmpend->fEntryCount; + + tmpend->fChecksum = + ke_calculate_crc32((Char*) tmpend, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fOutput(mnt->fPacket); + + auto child_first = tmpend->fChild; + + while (YES) { + mnt->fPacket.fPacketLba = child_first; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = tmpend; + + mnt->fInput(mnt->fPacket); + + if ((!tmpend->fCreated && tmpend->fDeleted) || tmpend->fHashPath == 0) { + start = child_first; + break; + } + + hefsi_traverse_tree(tmpend, mnt, boot->fStartIND, child_first); + } + } + + dirent->fNext = tmpdir->fNext; + dirent->fPrev = tmpdir->fPrev; + dirent->fParent = tmpdir->fParent; + dirent->fChild = tmpdir->fChild; + dirent->fColor = tmpdir->fColor; + + if (dirent->fColor == 0) { + dirent->fColor = dirent->fNext ? kOpenHeFSRed : kOpenHeFSBlack; + } + + if (dirent->fPrev == 0) { + dirent->fPrev = boot->fStartIND; + } + + if (dirent->fParent == 0) { + dirent->fParent = boot->fStartIND; + } + + if (tmpdir->fChild == 0) { + auto child = dirent->fNext + sizeof(HEFS_INDEX_NODE_DIRECTORY); + + HEFS_INDEX_NODE_DIRECTORY* tmpend = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + while (YES) { + mnt->fPacket.fPacketLba = child; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = tmpend; + + mnt->fInput(mnt->fPacket); + + if ((!tmpend->fCreated && tmpend->fDeleted) || tmpdir->fHashPath == 0) { + break; + } + + child += sizeof(HEFS_INDEX_NODE_DIRECTORY); + if (child > boot->fEndIND) break; + } + + dirent->fColor = kOpenHeFSRed; + dirent->fChild = child; + + if (child > boot->fEndIND) dirent->fChild = boot->fStartIND; + } + + for (SizeT index = 0UL; index < kOpenHeFSSliceCount; ++index) { + dirent->fINSlices[index] = 0UL; + } + + dirent->fChecksum = ke_calculate_crc32((Char*) dirent, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = prev_start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dirent; + + mnt->fOutput(mnt->fPacket); + + err_global_get() = kErrorSuccess; + + mm_free_ptr(dirent); + mm_free_ptr(tmpdir); + + if (!delete_or_create) + ++boot->fINDCount; + else + --boot->fINDCount; + + boot->fChecksum = ke_calculate_crc32((Char*) boot, sizeof(HEFS_BOOT_NODE)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fOutput(mnt->fPacket); + + return YES; + } + + prev_location = start; + + hefsi_traverse_tree(tmpdir, mnt, boot->fStartIND, start); + if (start > boot->fEndIND || start == 0) break; + } + + err_global_get() = kErrorDisk; + mm_free_ptr(tmpdir); + + return NO; + } + + err_global_get() = kErrorDiskIsFull; + return NO; + } + + /// @brief Get the index node of a file or directory. + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read from. + /// @param dir_name The name of the directory. + /// @param file_name The name of the file. + /// @param kind The kind of the file (regular, directory, block, character, FIFO, socket, symbolic + /// link, unknown). + STATIC ATTRIBUTE(unused) _Output HEFS_INDEX_NODE* hefsi_fetch_in(HEFS_BOOT_NODE* boot, + DriveTrait* mnt, + const Utf8Char* dir_name, + const Utf8Char* file_name, + UInt8 kind) { + if (mnt) { + if (boot->fStartIND > boot->fEndIND) return nullptr; + if (boot->fStartIN > boot->fEndIN) return nullptr; + + auto start = boot->fStartIND; + HEFS_INDEX_NODE* node = new HEFS_INDEX_NODE(); + + HEFS_INDEX_NODE_DIRECTORY* dir = + (HEFS_INDEX_NODE_DIRECTORY*) mm_alloc_ptr(sizeof(HEFS_INDEX_NODE_DIRECTORY), Yes, No); + + while (YES) { + if (err_global_get() == kErrorDiskIsCorrupted) { + delete dir; + dir = nullptr; + + delete node; + node = nullptr; + + return nullptr; + } + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fInput(mnt->fPacket); + + (Void)(kout << hex_number(hefsi_hash_64(dir_name)) << kendl); + (Void)(kout << hex_number(dir->fHashPath) << kendl); + + if (dir->fHashPath == 0) break; + + if (hefsi_hash_64(dir_name) == dir->fHashPath) { + for (SizeT inode_index = 0UL; inode_index < kOpenHeFSSliceCount; ++inode_index) { + mnt->fPacket.fPacketLba = dir->fINSlices[inode_index]; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE); + mnt->fPacket.fPacketContent = node; + + mnt->fInput(mnt->fPacket); + + (Void)(kout << hex_number(hefsi_hash_64(file_name)) << kendl); + (Void)(kout << hex_number(node->fHashPath) << kendl); + + if (hefsi_hash_64(file_name) == node->fHashPath && node->fKind == kind) { + delete dir; + dir = nullptr; + + return node; + } + } + } + + hefsi_traverse_tree(dir, mnt, boot->fStartIND, start); + if (start == boot->fStartIND || start == boot->fStartIND) break; + } + + delete node; + node = nullptr; + + delete dir; + dir = nullptr; + } + + kout << "Error: Failed to find IN.\r"; + + err_global_get() = kErrorFileNotFound; + + return nullptr; + } + + STATIC ATTRIBUTE(unused) _Output BOOL + hefsi_update_in_status(HEFS_BOOT_NODE* boot, DriveTrait* mnt, const Utf8Char* dir_name, + HEFS_INDEX_NODE* node, BOOL delete_or_create) { + if (!boot || !mnt) return NO; + + auto start = boot->fStartIND; + + if (start > boot->fEndIND) return NO; + if (boot->fStartIN > boot->fEndIN) return NO; + ; + if (boot->fStartBlock > boot->fEndBlock) return NO; + + if (mnt) { + HEFS_INDEX_NODE_DIRECTORY* dir = + (HEFS_INDEX_NODE_DIRECTORY*) mm_alloc_ptr(sizeof(HEFS_INDEX_NODE_DIRECTORY), Yes, No); + + auto hash_file = node->fHashPath; + + while (YES) { + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fInput(mnt->fPacket); + + (Void)(kout << hex_number(hefsi_hash_64(dir_name)) << kendl); + (Void)(kout << hex_number(dir->fHashPath) << kendl); + + if (hefsi_hash_64(dir_name) == dir->fHashPath) { + for (SizeT inode_index = 0UL; inode_index < kOpenHeFSSliceCount; ++inode_index) { + if (dir->fINSlices[inode_index] == 0 && !delete_or_create) { + dir->fINSlices[inode_index] = boot->fStartIN; + + ++dir->fEntryCount; + + dir->fChecksum = ke_calculate_crc32((Char*) dir, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fOutput(mnt->fPacket); + + auto lba = dir->fINSlices[inode_index]; + + node->fOffsetSliceLow = (UInt32) (boot->fStartBlock); + node->fOffsetSliceHigh = (UInt32) (boot->fStartBlock >> 32); + + node->fChecksum = ke_calculate_crc32((Char*) node, sizeof(HEFS_INDEX_NODE)); + + mnt->fPacket.fPacketLba = lba; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE); + mnt->fPacket.fPacketContent = node; + + mnt->fOutput(mnt->fPacket); + + boot->fStartIN += sizeof(HEFS_INDEX_NODE); + boot->fStartBlock += kOpenHeFSBlockLen; + + boot->fChecksum = ke_calculate_crc32((Char*) boot, sizeof(HEFS_BOOT_NODE)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fOutput(mnt->fPacket); + + mm_free_ptr(dir); + + return YES; + } else if (dir->fINSlices[inode_index] != 0 && delete_or_create) { + auto lba = dir->fINSlices[inode_index]; + + HEFS_INDEX_NODE tmp_node{}; + + mnt->fPacket.fPacketLba = lba; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE); + mnt->fPacket.fPacketContent = &tmp_node; + + mnt->fInput(mnt->fPacket); + + if (tmp_node.fHashPath != hash_file) { + continue; + } + + node->fOffsetSliceLow = 0; + node->fOffsetSliceHigh = 0; + + boot->fStartIN -= sizeof(HEFS_INDEX_NODE); + boot->fStartBlock -= kOpenHeFSBlockLen; + + boot->fChecksum = ke_calculate_crc32((Char*) boot, sizeof(HEFS_BOOT_NODE)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fOutput(mnt->fPacket); + + mnt->fPacket.fPacketLba = lba; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE); + mnt->fPacket.fPacketContent = node; + + mnt->fOutput(mnt->fPacket); + + dir->fINSlices[inode_index] = 0; + + if (dir->fEntryCount) --dir->fEntryCount; + + dir->fChecksum = ke_calculate_crc32((Char*) dir, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fOutput(mnt->fPacket); + + mm_free_ptr(dir); + + return YES; + } + } + } + + hefsi_traverse_tree(dir, mnt, boot->fStartIND, start); + if (start > boot->fEndIND || start == 0) break; + } + + mm_free_ptr(dir); + err_global_get() = kErrorFileNotFound; + return NO; + } + + err_global_get() = kErrorDiskIsFull; + return NO; + } + + /// @brief Balance RB-Tree of the filesystem. + /// @param boot The boot node of the filesystem. + /// @param mnt The mnt to read/write from. + /// @return Status, see err_global_get(). + STATIC ATTRIBUTE(unused) _Output BOOL hefsi_balance_ind(HEFS_BOOT_NODE* boot, DriveTrait* mnt) { + if (mnt) { + HEFS_INDEX_NODE_DIRECTORY* dir = + (HEFS_INDEX_NODE_DIRECTORY*) RTL_ALLOCA(sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + auto start = boot->fStartIND; + + while (YES) { + if (start == 0UL || start > boot->fEndIND) break; + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fInput(mnt->fPacket); + + if (!mnt->fPacket.fPacketGood) { + err_global_get() = kErrorDiskIsCorrupted; + + return NO; + } + + if (start == boot->fStartIND) { + dir->fColor = kOpenHeFSBlack; + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fOutput(mnt->fPacket); + } + + if (dir->fColor == kOpenHeFSBlack && dir->fChild != 0UL) { + dir->fColor = kOpenHeFSRed; + hefsi_rotate_tree(start, mnt); + } else if (dir->fColor == kOpenHeFSBlack && dir->fChild == 0UL) { + dir->fColor = kOpenHeFSBlack; + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fOutput(mnt->fPacket); + } + + if (ke_calculate_crc32((Char*) dir, sizeof(HEFS_INDEX_NODE_DIRECTORY)) != dir->fChecksum) { + dir->fChecksum = ke_calculate_crc32((Char*) dir, sizeof(HEFS_INDEX_NODE_DIRECTORY)); + + mnt->fPacket.fPacketLba = start; + mnt->fPacket.fPacketSize = sizeof(HEFS_INDEX_NODE_DIRECTORY); + mnt->fPacket.fPacketContent = dir; + + mnt->fOutput(mnt->fPacket); + } + + hefsi_traverse_tree(dir, mnt, boot->fStartIND, start); + } + + err_global_get() = kErrorSuccess; + return YES; + } + + err_global_get() = kErrorDisk; + return NO; + } +} // namespace Detail +} // namespace Kernel + +/// @note OpenHeFS will allocate inodes and ind in advance, to avoid having to allocate them in +/// real-time. +/// @note This is certainly take longer to format a disk with it, but worth-it in the long run. + +namespace Kernel { +/// @brief Make a EPM+OpenHeFS mnt out of the disk. +/// @param mnt The mnt to write on. +/// @return If it was sucessful, see err_local_get(). +_Output Bool HeFileSystemParser::Format(_Input _Output DriveTrait* mnt, _Input const Int32 flags, + _Input const Utf8Char* vol_name) { + // Verify Disk. + mnt->fVerify(mnt->fPacket); + + // if disk isn't good, then error out. + if (false == mnt->fPacket.fPacketGood) { + err_global_get() = kErrorDiskIsCorrupted; + return NO; + } + + if (drv_std_get_size() < kOpenHeFSMinimumDiskSize) { + (Void)(kout << "OpenHeFS recommends at least 128 GiB of free space." << kendl); + } + + HEFS_BOOT_NODE* boot = (HEFS_BOOT_NODE*) RTL_ALLOCA(sizeof(HEFS_BOOT_NODE)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fInput(mnt->fPacket); + + if (!mnt->fPacket.fPacketGood) { + err_global_get() = kErrorDiskIsCorrupted; + + return NO; + } + + // Check if the disk is already formatted. + + if (KStringBuilder::Equals(boot->fMagic, kOpenHeFSMagic) && boot->fVersion == kOpenHeFSVersion) { + if (ke_calculate_crc32((Char*) boot, sizeof(HEFS_BOOT_NODE)) != boot->fChecksum && + boot->fChecksum > 0) { + err_global_get() = kErrorDiskIsCorrupted; + return NO; + } + + err_global_get() = kErrorSuccess; + return YES; + } + + if (ke_calculate_crc32((Char*) boot, sizeof(HEFS_BOOT_NODE)) != boot->fChecksum && + boot->fChecksum > 0) { + err_global_get() = kErrorDiskIsCorrupted; + return NO; + } + + rt_copy_memory_safe((VoidPtr) "fs/hefs-packet", mnt->fPacket.fPacketMime, + rt_string_len("fs/hefs-packet"), sizeof(mnt->fPacket.fPacketMime)); + + urt_copy_memory((VoidPtr) vol_name, boot->fVolName, urt_string_len(vol_name) + 1); + rt_copy_memory_safe((VoidPtr) kOpenHeFSMagic, boot->fMagic, kOpenHeFSMagicLen - 1, sizeof(boot->fMagic)); + + if (mnt->fLbaStart > mnt->fLbaEnd) { + err_global_get() = kErrorDiskIsCorrupted; + + return NO; + } + + boot->fBadSectors = 0; + + boot->fSectorCount = drv_std_get_sector_count(); + boot->fSectorSize = mnt->fSectorSz; + + MUST_PASS(boot->fSectorSize); + + /// @note all OpenHeFS strucutres are equal to 512, so here it's fine, unless fSectoSize is 2048. + const SizeT max_lba = (drv_std_get_size()) / boot->fSectorSize; + + const SizeT dir_max = max_lba / 300; // 5% for directory inodes + const SizeT inode_max = max_lba / 400; // 5% for inodes + + boot->fStartIND = mnt->fLbaStart + kOpenHeFSINDStartOffset; + boot->fEndIND = boot->fStartIND + dir_max; + + boot->fStartIN = boot->fEndIND; + boot->fEndIN = boot->fStartIN + inode_max; + + boot->fStartBlock = boot->fEndIN; + boot->fEndBlock = drv_std_get_size(); + + boot->fINDCount = 0; + + boot->fDiskSize = drv_std_get_size(); + boot->fDiskStatus = kOpenHeFSStatusUnlocked; + + boot->fDiskFlags = flags; + + if (mnt->fKind & kMassStorageDrive) { + boot->fDiskKind = kOpenHeFSMassStorageDevice; + } else if (mnt->fKind & kOpenHeFSOpticalDrive) { + boot->fDiskKind = kOpenHeFSOpticalDrive; + } else { + boot->fDiskKind = kOpenHeFSUnknown; + } + + boot->fVersion = kOpenHeFSVersion; + + boot->fVID = kOpenHeFSInvalidVID; + + boot->fChecksum = ke_calculate_crc32((Char*) boot, sizeof(HEFS_BOOT_NODE)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fOutput(mnt->fPacket); + + (Void)(kout << "Protocol: " << mnt->fProtocol() << kendl); + (Void)(kout8 << u8"Volume Name: " << boot->fVolName << kendl8); + (Void)(kout << "Start IND: " << hex_number(boot->fStartIND) << kendl); + (Void)(kout << "End IND: " << hex_number(boot->fEndIND) << kendl); + (Void)(kout << "Start IN: " << hex_number(boot->fStartIN) << kendl); + (Void)(kout << "End IN: " << hex_number(boot->fEndIN) << kendl); + (Void)(kout << "Start Block: " << hex_number(boot->fStartBlock) << kendl); + (Void)(kout << "End Block: " << hex_number(boot->fEndBlock) << kendl); + (Void)(kout << "Number of IND: " << hex_number(boot->fINDCount) << kendl); + (Void)(kout << "Sector Size: " << hex_number(boot->fSectorSize) << kendl); + (Void)(kout << "Drive Kind: " << Detail::hefs_drive_kind_to_string(boot->fDiskKind) << kendl); + + if (!mnt->fPacket.fPacketGood) { + err_global_get() = kErrorDiskIsCorrupted; + return NO; + } + + /// AMLALE: Better way to create default directories than before. + const Utf8Char* kFileMap[] = {u8"/", u8"/boot", u8"/system", u8"/network", + u8"/devices", u8"/media", u8"/dev", (Utf8Char*) nullptr}; + + SizeT i = 0; + while (kFileMap[++i] != nullptr) { + this->CreateINodeDirectory(mnt, kOpenHeFSEncodingFlagsUTF8, kFileMap[i]); + } + + err_global_get() = kErrorSuccess; + + return YES; +} + +/// @brief Create a new directory on the disk. +/// @param mnt The mnt to write on. +/// @param flags The flags to use. +/// @param dir The directory to create the file in. +/// @return If it was sucessful, see err_local_get(). +_Output Bool HeFileSystemParser::INodeDirectoryCtlManip(_Input DriveTrait* mnt, + _Input const Int32 flags, + const Utf8Char* dir, + const BOOL delete_or_create) { + if (urt_string_len(dir) > kOpenHeFSFileNameLen) { + err_global_get() = kErrorDisk; + return NO; + } + + HEFS_BOOT_NODE* boot = (HEFS_BOOT_NODE*) mm_alloc_ptr(sizeof(HEFS_BOOT_NODE), Yes, No); + + rt_copy_memory_safe((VoidPtr) "fs/hefs-packet", mnt->fPacket.fPacketMime, + rt_string_len("fs/hefs-packet"), sizeof(mnt->fPacket.fPacketMime)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fInput(mnt->fPacket); + + if (!KStringBuilder::Equals(boot->fMagic, kOpenHeFSMagic) || boot->fVersion != kOpenHeFSVersion) { + err_global_get() = kErrorDisk; + return YES; + } + + if (!KStringBuilder::Equals(boot->fMagic, kOpenHeFSMagic) || boot->fVersion != kOpenHeFSVersion) { + err_global_get() = kErrorDiskIsCorrupted; + + kout << "Invalid Boot Node, this can't continue!\r"; + + return NO; + } + + if (KStringBuilder::Equals(dir, kOpenHeFSSearchAllStr)) { + kout << "Error: Invalid directory name.\r"; + + err_global_get() = kErrorInvalidData; + + return NO; + } + + if (Detail::hefsi_update_ind_status(boot, mnt, dir, flags, delete_or_create)) { + // todo: make it smarter for high-throughput. + Detail::hefsi_balance_ind(boot, mnt); + + mm_free_ptr((VoidPtr) boot); + return YES; + } + + mm_free_ptr((VoidPtr) boot); + return NO; +} + +_Output Bool HeFileSystemParser::RemoveINodeDirectory(_Input DriveTrait* mnt, + _Input const Int32 flags, + const Utf8Char* dir) { + return this->INodeDirectoryCtlManip(mnt, flags, dir, YES); +} + +_Output Bool HeFileSystemParser::CreateINodeDirectory(_Input DriveTrait* mnt, + _Input const Int32 flags, + const Utf8Char* dir) { + return this->INodeDirectoryCtlManip(mnt, flags, dir, NO); +} + +_Output Bool HeFileSystemParser::DeleteINode(_Input DriveTrait* mnt, _Input const Int32 flags, + const Utf8Char* dir, const Utf8Char* name, + const UInt8 kind) { + return this->INodeCtlManip(mnt, flags, dir, name, YES, kind); +} + +_Output Bool HeFileSystemParser::CreateINode(_Input DriveTrait* mnt, _Input const Int32 flags, + const Utf8Char* dir, const Utf8Char* name, + const UInt8 kind) { + return this->INodeCtlManip(mnt, flags, dir, name, NO, kind); +} + +_Output Bool HeFileSystemParser::INodeManip(_Input DriveTrait* mnt, VoidPtr block, SizeT block_sz, + const Utf8Char* dir, const Utf8Char* name, + const UInt8 kind, const BOOL is_input) { + if (urt_string_len(dir) > kOpenHeFSFileNameLen) { + err_global_get() = kErrorDisk; + return NO; + } + + if (urt_string_len(name) > kOpenHeFSFileNameLen) { + err_global_get() = kErrorDisk; + return NO; + } + + HEFS_BOOT_NODE* boot = (HEFS_BOOT_NODE*) mm_alloc_ptr(sizeof(HEFS_BOOT_NODE), Yes, No); + + if (!boot) { + err_global_get() = kErrorInvalidData; + return NO; + } + + rt_copy_memory_safe((VoidPtr) "fs/hefs-packet", mnt->fPacket.fPacketMime, + rt_string_len("fs/hefs-packet"), sizeof(mnt->fPacket.fPacketMime)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fInput(mnt->fPacket); + + if (!KStringBuilder::Equals(boot->fMagic, kOpenHeFSMagic) || boot->fVersion != kOpenHeFSVersion) { + (Void)(kout << "Invalid Boot Node, OpenHeFS partition is invalid." << kendl); + mm_free_ptr((VoidPtr) boot); + err_global_get() = kErrorDisk; + return NO; + } + + auto start = Detail::hefsi_fetch_in(boot, mnt, dir, name, kind); + + if (start) { + (Void)(kout << hex_number(start->fHashPath) << kendl); + (Void)(kout << hex_number(start->fOffsetSliceLow) << kendl); + + if (start->fOffsetSliceLow && start->fHashPath) { + mnt->fPacket.fPacketLba = ((UInt64) start->fOffsetSliceHigh << 32) | start->fOffsetSliceLow; + mnt->fPacket.fPacketSize = block_sz; + mnt->fPacket.fPacketContent = block; + + if (is_input) { + mnt->fInput(mnt->fPacket); + } else { + if (start->fFlags & kOpenHeFSFlagsReadOnly) { + mm_free_ptr((VoidPtr) boot); + delete start; + + kout << "Error: File is read-only\r"; + + return NO; + } + + mnt->fOutput(mnt->fPacket); + } + } + } + + mm_free_ptr((VoidPtr) boot); + delete start; + return YES; +} + +/// @brief Create a new file on the disk. +/// @param mnt The mnt to write on. +/// @param flags The flags to use. +/// @param dir The directory to create the file in. +/// @param name The name of the file. +/// @return If it was sucessful, see err_local_get(). +_Output Bool HeFileSystemParser::INodeCtlManip(_Input DriveTrait* mnt, _Input const Int32 flags, + const Utf8Char* dir, const Utf8Char* name, + const BOOL delete_or_create, const UInt8 kind) { + if (urt_string_len(name) > kOpenHeFSFileNameLen) { + err_global_get() = kErrorDisk; + return NO; + } + + if (urt_string_len(dir) > kOpenHeFSFileNameLen) { + err_global_get() = kErrorDisk; + return NO; + } + + HEFS_INDEX_NODE* node = (HEFS_INDEX_NODE*) mm_alloc_ptr(sizeof(HEFS_INDEX_NODE), Yes, No); + + if (!node) { + err_global_get() = kErrorInvalidData; + return NO; + } + + rt_set_memory(node, 0, sizeof(HEFS_INDEX_NODE)); + + HEFS_BOOT_NODE* boot = (HEFS_BOOT_NODE*) RTL_ALLOCA(sizeof(HEFS_BOOT_NODE)); + + if (!boot) { + mm_free_ptr((VoidPtr) node); + err_global_get() = kErrorInvalidData; + + return NO; + } + + rt_copy_memory_safe((VoidPtr) "fs/hefs-packet", mnt->fPacket.fPacketMime, + rt_string_len("fs/hefs-packet"), sizeof(mnt->fPacket.fPacketMime)); + + mnt->fPacket.fPacketLba = mnt->fLbaStart; + mnt->fPacket.fPacketSize = sizeof(HEFS_BOOT_NODE); + mnt->fPacket.fPacketContent = boot; + + mnt->fInput(mnt->fPacket); + + if (!KStringBuilder::Equals(boot->fMagic, kOpenHeFSMagic) || boot->fVersion != kOpenHeFSVersion) { + err_global_get() = kErrorDisk; + return YES; + } + + if (KStringBuilder::Equals(dir, kOpenHeFSSearchAllStr)) { + kout << "Error: Invalid file name.\r"; + + err_global_get() = kErrorInvalidData; + return NO; + } + + for (SizeT i_name = 0UL; i_name < urt_string_len(name); ++i_name) { + if (name[i_name] == u'/') { + err_global_get() = kErrorInvalidData; + return NO; + } + } + + if (KStringBuilder::Equals(dir, kOpenHeFSSearchAllStr)) { + kout << "Error: Invalid directory name.\r"; + + err_global_get() = kErrorInvalidData; + return NO; + } + + node->fAccessed = 0; + node->fCreated = delete_or_create ? NO : YES; + node->fDeleted = delete_or_create ? 1UL : NO; + node->fModified = 0; + node->fSize = 0; + node->fKind = kind; + node->fFlags = flags; + node->fChecksum = 0; + node->fGID = 0; + node->fUID = 0; + node->fHashPath = Detail::hefsi_hash_64(name); + + if (Detail::hefsi_update_in_status(boot, mnt, dir, node, delete_or_create)) { + mm_free_ptr((VoidPtr) node); + + Detail::hefsi_balance_ind(boot, mnt); + + err_global_get() = kErrorSuccess; + return YES; + } + + mm_free_ptr((VoidPtr) node); + err_global_get() = kErrorDirectoryNotFound; + + return NO; +} + +STATIC IMountpoint kMountpoint; + +/// @brief Initialize the OpenHeFS filesystem. +/// @return To check its status, see err_local_get(). +Boolean OpenHeFS::fs_init_openhefs(Void) noexcept { + io_construct_main_drive(kMountpoint.A()); + + if (kMountpoint.A().fPacket.fPacketReadOnly == YES) { + kout << "Main disk cannot be mounted (read-only media).\r"; + return YES; + } + + return HeFileSystemParser{}.Format(&kMountpoint.A(), kOpenHeFSEncodingFlagsUTF8, kOpenHeFSDefaultVolumeName); +} +} // namespace Kernel + +#endif // ifdef __FSKIT_INCLUDES_OPENHEFS__ diff --git a/src/kernel/src/FileMgr.cc b/src/kernel/src/FileMgr.cc new file mode 100644 index 00000000..88b17470 --- /dev/null +++ b/src/kernel/src/FileMgr.cc @@ -0,0 +1,49 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +/***********************************************************************************/ +/// @file FileMgr.cc +//! @brief File System Manager API. +///! @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +namespace Kernel { +STATIC IFilesystemMgr* kMountedFilesystem = nullptr; + +/// @brief FilesystemMgr getter. +/// @return The mounted filesystem. +_Output IFilesystemMgr* IFilesystemMgr::GetMounted() { + return kMountedFilesystem; +} + +/// @brief Unmount filesystem. +/// @return The unmounted filesystem. +_Output IFilesystemMgr* IFilesystemMgr::Unmount() { + if (kMountedFilesystem) { + auto mount = kMountedFilesystem; + kMountedFilesystem = nullptr; + + return mount; + } + + return nullptr; +} + +/// @brief Mount filesystem. +/// @param mount_ptr The filesystem to mount. +/// @return if it succeeded true, otherwise false. +_Output Bool IFilesystemMgr::Mount(_Input IFilesystemMgr* mount_ptr) { + if (mount_ptr != nullptr) { + kMountedFilesystem = mount_ptr; + return Yes; + } + + return No; +} +} // namespace Kernel diff --git a/src/kernel/src/GUIDWizard.cc b/src/kernel/src/GUIDWizard.cc new file mode 100644 index 00000000..2d1218ed --- /dev/null +++ b/src/kernel/src/GUIDWizard.cc @@ -0,0 +1,67 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: GUIDWizard.cc + Purpose: GUID helper code + + Revision History: + +======================================== */ + +#include +#include + +// begin of ascii 'readable' characters. (A, C, C, 1, 2) +#define kGUIDAsciiBegin 47 +// @brief Size of UUID. +#define kGUIDSize 37 + +namespace Kernel::CF::XRN::Version1 { +auto cf_make_sequence(const ArrayList& uuidSeq) -> Ref { + GUIDSequence* seq = new GUIDSequence(); + MUST_PASS(seq); + + Ref seq_ref{seq}; + + if (!seq) return seq_ref; + + seq_ref.Leak()->fUuid.fMs1 = uuidSeq[0]; + seq_ref.Leak()->fUuid.fMs2 = uuidSeq[1]; + seq_ref.Leak()->fUuid.fMs3 = uuidSeq[2]; + seq_ref.Leak()->fUuid.fMs4[0] = uuidSeq[3]; + seq_ref.Leak()->fUuid.fMs4[1] = uuidSeq[4]; + seq_ref.Leak()->fUuid.fMs4[2] = uuidSeq[5]; + seq_ref.Leak()->fUuid.fMs4[3] = uuidSeq[6]; + seq_ref.Leak()->fUuid.fMs4[4] = uuidSeq[7]; + seq_ref.Leak()->fUuid.fMs4[5] = uuidSeq[8]; + seq_ref.Leak()->fUuid.fMs4[6] = uuidSeq[9]; + seq_ref.Leak()->fUuid.fMs4[7] = uuidSeq[10]; + + return seq_ref; +} + +// @brief Tries to make a guid out of a string. +// This function is not complete for now +auto cf_try_guid_to_string(Ref& seq) -> ErrorOr> { + Char buf[kGUIDSize]; + + for (SizeT index = 0; index < 16; ++index) { + buf[index] = seq.Leak()->fU8[index] + kGUIDAsciiBegin; + } + + for (SizeT index = 16; index < 24; ++index) { + buf[index] = seq.Leak()->fU16[index] + kGUIDAsciiBegin; + } + + for (SizeT index = 24; index < 28; ++index) { + buf[index] = seq.Leak()->fU32[index] + kGUIDAsciiBegin; + } + + auto view = KStringBuilder::Construct(buf); + + if (view) return ErrorOr>{view.Leak()}; + + return ErrorOr>{kErrorInvalidData}; +} +} // namespace Kernel::CF::XRN::Version1 diff --git a/src/kernel/src/GUIDWrapper.cc b/src/kernel/src/GUIDWrapper.cc new file mode 100644 index 00000000..ee29f911 --- /dev/null +++ b/src/kernel/src/GUIDWrapper.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel::CF::XRN {} diff --git a/src/kernel/src/Gfx/FBDeviceInterface.cc b/src/kernel/src/Gfx/FBDeviceInterface.cc new file mode 100644 index 00000000..c5fac330 --- /dev/null +++ b/src/kernel/src/Gfx/FBDeviceInterface.cc @@ -0,0 +1,50 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; + +/// @brief Class constructor +/// @param Out Drive output +/// @param In Drive input +/// @param Cleanup Drive cleanup. +FBDeviceInterface::FBDeviceInterface(void (*out)(DeviceInterface* self, FBDevicePacket* outpacket), + void (*in)(DeviceInterface* self, FBDevicePacket* inpacket)) + : DeviceInterface(out, in) {} + +/// @brief Class desctructor +FBDeviceInterface::~FBDeviceInterface() = default; + +/// @brief Output operator. +/// @param mnt the disk mountpoint. +/// @return the class itself after operation. +FBDeviceInterface& FBDeviceInterface::operator<<(FBDevicePacket* pckt) { + if (!pckt) return *this; + + if (pckt->fHeight == 0 || pckt->fWidth == 0) return *this; + + this->fOut(this, pckt); + + return *this; +} + +/// @brief Input operator. +/// @param mnt the disk mountpoint. +/// @return the class itself after operation. +FBDeviceInterface& FBDeviceInterface::operator>>(FBDevicePacket* pckt) { + if (!pckt) return *this; + + this->fIn(this, pckt); + + return *this; +} + +/// @brief Returns the name of the device interface. +/// @return it's name as a string. +const Char* FBDeviceInterface::Name() const { + return "/devices/fb{}"; +} \ No newline at end of file diff --git a/src/kernel/src/HardwareThreadScheduler.cc b/src/kernel/src/HardwareThreadScheduler.cc new file mode 100644 index 00000000..41d927f6 --- /dev/null +++ b/src/kernel/src/HardwareThreadScheduler.cc @@ -0,0 +1,184 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include + +/***********************************************************************************/ +///! @file HardwareThreadScheduler.cc +///! @brief This file handles multi processing in the Kernel. +///! @brief Multi processing is needed for multi-tasking operations. +/***********************************************************************************/ + +namespace Kernel { +/***********************************************************************************/ +/// @note Those symbols are needed in order to switch and validate the stack. +/***********************************************************************************/ + +EXTERN_C Bool hal_check_task(HAL::StackFramePtr frame); +EXTERN_C Bool mp_register_task(HAL::StackFramePtr frame, ProcessID pid); + +STATIC HardwareThreadScheduler kHardwareThreadScheduler; + +///! A HardwareThread class takes care of it's owned hardware thread. +///! It has a stack for it's core. + +/***********************************************************************************/ +///! @brief C++ constructor. +/***********************************************************************************/ +HardwareThread::HardwareThread() = default; + +/***********************************************************************************/ +///! @brief C++ destructor. +/***********************************************************************************/ +HardwareThread::~HardwareThread() = default; + +/***********************************************************************************/ +//! @brief returns the id of the thread. +/***********************************************************************************/ +ThreadID& HardwareThread::ID() noexcept { + return fID; +} + +/***********************************************************************************/ +//! @brief returns the kind of thread we have. +/***********************************************************************************/ +ThreadKind& HardwareThread::Kind() noexcept { + return fKind; +} + +/***********************************************************************************/ +//! @brief is the thread busy? +//! @return whether the thread is busy or not. +/***********************************************************************************/ +Bool HardwareThread::IsBusy() noexcept { + return fBusy; +} + +/***********************************************************************************/ +/// @brief Get processor stack frame. +/***********************************************************************************/ + +HAL::StackFramePtr HardwareThread::StackFrame() noexcept { + MUST_PASS(this->fStack); + return this->fStack; +} + +Void HardwareThread::Busy(Bool busy) noexcept { + this->fBusy = busy; +} + +HardwareThread::operator bool() { + return this->fStack && !this->fBusy; +} + +/***********************************************************************************/ +/// @brief Wakeup the processor. +/***********************************************************************************/ + +Void HardwareThread::Wake(const bool wakeup) noexcept { + this->fWakeup = wakeup; +} + +/***********************************************************************************/ +/// @brief Switch to hardware thread. +/// @param stack the new hardware thread. +/// @retval true stack was changed, code is running. +/// @retval false stack is invalid, previous code is running. +/***********************************************************************************/ +Bool HardwareThread::Switch(HAL::StackFramePtr frame) { + if (!frame) { + return NO; + } + + if (!hal_check_task(frame)) { + return NO; + } + + this->fStack = frame; + return mp_register_task(fStack, this->fID); +} + +/***********************************************************************************/ +///! @brief Tells if processor is waked up. +/***********************************************************************************/ +bool HardwareThread::IsWakeup() noexcept { + return this->fWakeup; +} + +/***********************************************************************************/ +///! @brief Constructor and destructors. +///! @brief Default constructor. +/***********************************************************************************/ + +HardwareThreadScheduler::HardwareThreadScheduler() = default; + +/***********************************************************************************/ +///! @brief Default destructor. +/***********************************************************************************/ +HardwareThreadScheduler::~HardwareThreadScheduler() = default; + +/***********************************************************************************/ +/// @brief Shared singleton function +/***********************************************************************************/ +HardwareThreadScheduler& HardwareThreadScheduler::The() { + return kHardwareThreadScheduler; +} + +/***********************************************************************************/ +/// @brief Get Stack Frame of AP. +/***********************************************************************************/ +HAL::StackFramePtr HardwareThreadScheduler::Leak() noexcept { + return fThreadList[fCurrentThreadIdx].fStack; +} + +/***********************************************************************************/ +/** + * Get Hardware thread at index. + * @param idx the index + * @return the reference to the hardware thread. + */ +/***********************************************************************************/ +Ref HardwareThreadScheduler::operator[](SizeT idx) { + if (idx > kMaxAPInsideSched) { + HardwareThread* kFakeThread = nullptr; + return {kFakeThread}; + } + + fCurrentThreadIdx = idx; + return &fThreadList[idx]; +} + +/***********************************************************************************/ +/** + * Check if thread pool isn't empty. + * @return + */ +/***********************************************************************************/ +HardwareThreadScheduler::operator bool() noexcept { + return !fThreadList.Empty(); +} + +/***********************************************************************************/ +/** + * Reverse operator bool + * @return + */ +/***********************************************************************************/ +bool HardwareThreadScheduler::operator!() noexcept { + return fThreadList.Empty(); +} + +/***********************************************************************************/ +/// @brief Returns the amount of core present. +/// @return the number of APs. +/***********************************************************************************/ +SizeT HardwareThreadScheduler::Capacity() noexcept { + return fThreadList.Count(); +} +} // namespace Kernel diff --git a/src/kernel/src/HeapMgr.cc b/src/kernel/src/HeapMgr.cc new file mode 100644 index 00000000..848a0377 --- /dev/null +++ b/src/kernel/src/HeapMgr.cc @@ -0,0 +1,260 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include + +/* ======================================== + + Revision History: + 10/8/24: FIX: Fix useless long name, alongside a new WR (WriteRead) field. + 20/10/24: FIX: Fix mm_new_ and mm_delete_ APIs inside HeapMgr.h header. (amlal) + 27/01/25: REFACTOR: Reworked code as the memory manager. + 25/03/25: REFACTOR: Refactor HeapMgr code and log freed address location. + + ======================================== */ + +//! @file HeapMgr.cc +//! @brief Heap system that serves as the main memory manager. + +#define kHeapMgrMagic (0xD4D75) +#define kHeapMgrAlignSz (4U) + +namespace Kernel { +/// @brief Implementation details. +namespace Detail { + struct PACKED MM_INFORMATION_BLOCK; + + /// @brief Kernel heap information block. + /// Located before the address bytes. + /// | HIB | CLASS/STRUCT/DATA TYPES... | + struct PACKED MM_INFORMATION_BLOCK final { + ///! @brief 32-bit value which contains the magic number of the heap. + UInt32 fMagic : 24; + + ///! @brief Is the heap present? + UInt8 fPresent : 1; + + /// @brief Is this value writable? + UInt8 fWriteRead : 1; + + /// @brief Is this value owned by the user? + UInt8 fUser : 1; + + /// @brief Is this a page pointer? + UInt8 fPage : 1; + + /// @brief 32-bit CRC checksum. + UInt32 fCRC32; + + /// @brief 64-bit Allocation flags. + UInt16 fFlags; + + /// @brief 64-bit pointer size. + SizeT fSize; + + /// @brief 64-bit target offset pointer. + UIntPtr fOffset; + + /// @brief Padding. + UInt32 fPad; + + /// @brief Padding bytes for header. + UInt8 fPadding[kHeapMgrAlignSz]; + }; + + /// @brief Check for heap address validity. + /// @param heap_ptr The address_ptr to check. + /// @return Bool if the pointer is valid or not. + _Output auto mm_check_ptr_address(VoidPtr heap_ptr) -> Bool { + if (!heap_ptr) return false; + + IntPtr base_ptr = ((IntPtr) heap_ptr) - sizeof(Detail::MM_INFORMATION_BLOCK); + + /// Add that check in case we're having an integer underflow. /// + + if (base_ptr < 0) { + return false; + } + + return true; + } + + typedef MM_INFORMATION_BLOCK* MM_INFORMATION_BLOCK_PTR; +} // namespace Detail + +STATIC PageMgr kPageMgr; + +/// @brief Allocate chunk of memory. +/// @param sz Size of pointer +/// @param wr Read Write bit. +/// @param user User enable bit. +/// @return The newly allocated pointer. +_Output VoidPtr mm_alloc_ptr(SizeT sz, Bool wr, Bool user, SizeT pad_amount) { + auto sz_fix = sz; + + if (sz_fix == 0) return nullptr; + + sz_fix += sizeof(Detail::MM_INFORMATION_BLOCK); + + auto wrapper = kPageMgr.Request(wr, user, No, sz_fix, pad_amount); + + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast(wrapper.VirtualAddress() + + sizeof(Detail::MM_INFORMATION_BLOCK)); + + heap_info_ptr->fSize = sz_fix; + heap_info_ptr->fMagic = kHeapMgrMagic; + heap_info_ptr->fCRC32 = 0U; // dont fill it for now. + heap_info_ptr->fOffset = + reinterpret_cast(heap_info_ptr) + sizeof(Detail::MM_INFORMATION_BLOCK); + heap_info_ptr->fPage = No; + heap_info_ptr->fWriteRead = wr; + heap_info_ptr->fUser = user; + heap_info_ptr->fPresent = Yes; + heap_info_ptr->fPad = pad_amount; + + rt_set_memory(heap_info_ptr->fPadding, 0, kHeapMgrAlignSz); + + auto result = reinterpret_cast(heap_info_ptr->fOffset); + + (Void)(kout << "HeapMgr: Registered heap address: " + << hex_number(reinterpret_cast(heap_info_ptr)) << kendl); + + return result; +} + +/// @brief Makes a page heap. +/// @param heap_ptr the pointer to make a page heap. +/// @return kErrorSuccess if successful, otherwise an error code. +_Output Int32 mm_make_page(VoidPtr heap_ptr) { + if (Detail::mm_check_ptr_address(heap_ptr) == No) return kErrorHeapNotPresent; + + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast((UIntPtr) heap_ptr - + sizeof(Detail::MM_INFORMATION_BLOCK)); + + if (!heap_info_ptr) return kErrorHeapNotPresent; + + heap_info_ptr->fPage = true; + + (Void)(kout << "HeapMgr: Registered page from heap address: " + << hex_number(reinterpret_cast(heap_info_ptr)) << kendl); + + return kErrorSuccess; +} + +/// @brief Overwrites and set the flags of a heap header. +/// @param heap_ptr the pointer to update. +/// @param flags the flags to set. +_Output Int32 mm_set_ptr_flags(VoidPtr heap_ptr, UInt64 flags) { + if (Detail::mm_check_ptr_address(heap_ptr) == No) return kErrorHeapNotPresent; + + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast((UIntPtr) heap_ptr - + sizeof(Detail::MM_INFORMATION_BLOCK)); + + if (!heap_info_ptr) return kErrorHeapNotPresent; + + heap_info_ptr->fFlags = flags; + + return kErrorSuccess; +} + +/// @brief Gets the flags of a heap header. +/// @param heap_ptr the pointer to get. +_Output UInt64 mm_get_ptr_flags(VoidPtr heap_ptr) { + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast((UIntPtr) heap_ptr - + sizeof(Detail::MM_INFORMATION_BLOCK)); + + if (!heap_info_ptr) return kErrorHeapNotPresent; + + return heap_info_ptr->fFlags; +} + +/// @brief Declare pointer as free. +/// @param heap_ptr the pointer. +/// @return +_Output Int32 mm_free_ptr(VoidPtr heap_ptr) { + if (Detail::mm_check_ptr_address(heap_ptr) == No) return kErrorHeapNotPresent; + + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast((UIntPtr) (heap_ptr) - + sizeof(Detail::MM_INFORMATION_BLOCK)); + + if (heap_info_ptr && heap_info_ptr->fMagic == kHeapMgrMagic) { + if (!heap_info_ptr->fPresent) { + return kErrorHeapNotPresent; + } + + heap_info_ptr->fSize = 0UL; + heap_info_ptr->fPresent = No; + heap_info_ptr->fOffset = 0; + heap_info_ptr->fCRC32 = 0; + heap_info_ptr->fWriteRead = No; + heap_info_ptr->fUser = No; + heap_info_ptr->fMagic = 0; + heap_info_ptr->fPad = 0; + + (Void)(kout << "HeapMgr: Freed heap address: " + << hex_number(reinterpret_cast(heap_info_ptr)) << kendl); + + PTEWrapper page_wrapper( + No, No, No, + reinterpret_cast(heap_info_ptr) - sizeof(Detail::MM_INFORMATION_BLOCK)); + + Ref pte_address{page_wrapper}; + + kPageMgr.Free(pte_address); + + return kErrorSuccess; + } + + return kErrorInternal; +} + +/// @brief Check if pointer is a valid Kernel pointer. +/// @param heap_ptr the pointer +/// @return if it exists. +_Output Boolean mm_is_valid_ptr(VoidPtr heap_ptr) { + if (heap_ptr && HAL::mm_is_bitmap(heap_ptr)) { + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast((UIntPtr) (heap_ptr) - + sizeof(Detail::MM_INFORMATION_BLOCK)); + + return (heap_info_ptr && heap_info_ptr->fPresent && heap_info_ptr->fMagic == kHeapMgrMagic); + } + + return No; +} + +/// @brief Protect the heap with a CRC value. +/// @param heap_ptr HIB pointer. +/// @return if it valid: point has crc now., otherwise fail. +_Output Boolean mm_protect_ptr(VoidPtr heap_ptr) { + if (heap_ptr) { + Detail::MM_INFORMATION_BLOCK_PTR heap_info_ptr = + reinterpret_cast((UIntPtr) heap_ptr - + sizeof(Detail::MM_INFORMATION_BLOCK)); + + /// if valid, present and is heap header, then compute crc32 + if (heap_info_ptr && heap_info_ptr->fPresent && kHeapMgrMagic == heap_info_ptr->fMagic) { + heap_info_ptr->fCRC32 = + ke_calculate_crc32((Char*) heap_info_ptr->fOffset, heap_info_ptr->fSize); + + return Yes; + } + } + + return No; +} +} // namespace Kernel diff --git a/src/kernel/src/IDylibObject.cc b/src/kernel/src/IDylibObject.cc new file mode 100644 index 00000000..61191af1 --- /dev/null +++ b/src/kernel/src/IDylibObject.cc @@ -0,0 +1,13 @@ +/* + * ======================================================== + * + * NeKernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#include +#include + +using namespace Kernel; diff --git a/src/kernel/src/IFS.cc b/src/kernel/src/IFS.cc new file mode 100644 index 00000000..4679b8a3 --- /dev/null +++ b/src/kernel/src/IFS.cc @@ -0,0 +1,89 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +/************************************************************* + * + * File: IFS.cc + * Purpose: Filesystem to mountpoint interface. + * Date: 05/26/2025 + * + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + *************************************************************/ + +/// Useful macros regarding the IFS. + +#define fsi_ifs_write(DRV, TRAITS, MP) (MP->DRV()).fOutput(TRAITS) +#define fsi_ifs_read(DRV, TRAITS, MP) (MP->DRV()).fInput(TRAITS) + +namespace Kernel { +/// @brief Read from fs disk. +/// @param Mnt mounted interface. +/// @param DrvTrait drive info +/// @param DrvIndex drive index. +/// @return +Int32 fs_ifs_read(IMountpoint* Mnt, DriveTrait& DrvTrait, Int32 DrvIndex) { + if (!Mnt) return kErrorDisk; + + DrvTrait.fPacket.fPacketGood = false; + + switch (DrvIndex) { + case IMountpoint::kDriveIndexA: { + fsi_ifs_read(A, DrvTrait.fPacket, Mnt); + break; + } + case IMountpoint::kDriveIndexB: { + fsi_ifs_read(B, DrvTrait.fPacket, Mnt); + break; + } + case IMountpoint::kDriveIndexC: { + fsi_ifs_read(C, DrvTrait.fPacket, Mnt); + break; + } + case IMountpoint::kDriveIndexD: { + fsi_ifs_read(D, DrvTrait.fPacket, Mnt); + break; + } + } + + return DrvTrait.fPacket.fPacketGood ? kErrorSuccess : kErrorDisk; +} + +/// @brief Write to fs disk. +/// @param Mnt mounted interface. +/// @param DrvTrait drive info +/// @param DrvIndex drive index. +/// @return +Int32 fs_ifs_write(IMountpoint* Mnt, DriveTrait& DrvTrait, Int32 DrvIndex) { + if (!Mnt) return kErrorDisk; + + DrvTrait.fPacket.fPacketGood = false; + + switch (DrvIndex) { + case IMountpoint::kDriveIndexA: { + fsi_ifs_write(A, DrvTrait.fPacket, Mnt); + break; + } + case IMountpoint::kDriveIndexB: { + fsi_ifs_write(B, DrvTrait.fPacket, Mnt); + break; + } + case IMountpoint::kDriveIndexC: { + fsi_ifs_write(C, DrvTrait.fPacket, Mnt); + break; + } + case IMountpoint::kDriveIndexD: { + fsi_ifs_write(D, DrvTrait.fPacket, Mnt); + break; + } + } + + return DrvTrait.fPacket.fPacketGood ? kErrorSuccess : kErrorDisk; +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/IPEFDylibObject.cc b/src/kernel/src/IPEFDylibObject.cc new file mode 100644 index 00000000..4ba9278d --- /dev/null +++ b/src/kernel/src/IPEFDylibObject.cc @@ -0,0 +1,109 @@ +/* + * ======================================================== + * + * NeKernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#include +#include +#include +#include +#include +#include + +/* ======================================== + + Revision History: + + 01/02/24: Reworked dll ABI, expect a rtl_init_dylib_pef and + rtl_fini_dylib_pef (amlel) + + 15/02/24: Breaking changes, changed the name of the + routines. (amlel) + + 07/28/24: Replace rt_library_free with rtl_fini_dylib_pef + + 10/8/24: FIX: Fix log comment. + + ======================================== */ + +using namespace Kernel; + +/***********************************************************************************/ +/// @file IPEFDylibObject.cc +/// @brief PEF's Dylib runtime. +///! @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +/***********************************************************************************/ +/** @brief Library initializer. */ +/***********************************************************************************/ + +EXTERN_C IDylibRef rtl_init_dylib_pef(USER_PROCESS& process) { + IDylibRef dll_obj = tls_new_class(); + + if (!dll_obj) { + process.Crash(); + return nullptr; + } + + dll_obj->Mount(new IPEFDylibObject::DylibTraits()); + + if (!dll_obj->Get()) { + tls_delete_class(dll_obj); + dll_obj = nullptr; + + process.Crash(); + + return nullptr; + } + + dll_obj->Get()->ImageObject = process.Image.LeakBlob().Leak().Leak(); + + if (!dll_obj->Get()->ImageObject) { + delete dll_obj->Get(); + + tls_delete_class(dll_obj); + dll_obj = nullptr; + + process.Crash(); + + return nullptr; + } + + dll_obj->Get()->ImageEntrypointOffset = + dll_obj->Load(kPefStart, rt_string_len(kPefStart, 0), kPefCode); + + return dll_obj; +} + +/***********************************************************************************/ +/** @brief Frees the dll_obj. */ +/** @note Please check if the dll_obj got freed! */ +/** @param dll_obj The dll_obj to free. */ +/** @param successful Reports if successful or not. */ +/***********************************************************************************/ + +EXTERN_C Void rtl_fini_dylib_pef(USER_PROCESS& process, IDylibRef dll_obj, BOOL* successful) { + MUST_PASS(successful); + + if (!successful) { + return; + } + + // sanity check (will also trigger a bug check if this fails) + if (dll_obj == nullptr) { + *successful = false; + process.Crash(); + } + + delete dll_obj->Get(); + delete dll_obj; + + dll_obj = nullptr; + + *successful = true; +} diff --git a/src/kernel/src/IndexableProperty.cc b/src/kernel/src/IndexableProperty.cc new file mode 100644 index 00000000..85207d4b --- /dev/null +++ b/src/kernel/src/IndexableProperty.cc @@ -0,0 +1,45 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include + +/// @brief File indexer API for fast path access. +/// BUGS: 0 + +#define kMaxLenIndexer (256U) + +namespace Kernel { +namespace Indexer { + Index& IndexableProperty::Leak() noexcept { return fIndex; } + + Void IndexableProperty::AddFlag(UInt16 flag) { fFlags |= flag; } + + Void IndexableProperty::RemoveFlag(UInt16 flag) { fFlags &= ~(flag); } + + UInt16 IndexableProperty::HasFlag(UInt16 flag) { return fFlags & flag; } + + /// @brief Index a file into the indexer instance. + /// @param filename filesystem path to access. + /// @param filenameLen used bytes in path. + /// @param indexer the filesystem indexer. + /// @return none, check before if indexer can be claimed (using indexer.HasFlag(kIndexerClaimed)). + Void fs_index_file(const Char* filename, SizeT filenameLen, IndexableProperty& indexer) { + if (!indexer.HasFlag(kIndexerClaimed)) { + indexer.RemoveFlag(kIndexerUnclaimed); + indexer.AddFlag(kIndexerClaimed); + rt_copy_memory_safe(reinterpret_cast(const_cast(filename)), + (VoidPtr) indexer.Leak().Path, filenameLen, kIndexerCatalogNameLength); + + (Void)(kout << "FSKit: Indexed new file: " << filename << kendl); + } + } +} // namespace Indexer +} // namespace Kernel diff --git a/src/kernel/src/Json.cc b/src/kernel/src/Json.cc new file mode 100644 index 00000000..198aed99 --- /dev/null +++ b/src/kernel/src/Json.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel {} \ No newline at end of file diff --git a/src/kernel/src/KPC.cc b/src/kernel/src/KPC.cc new file mode 100644 index 00000000..4318b9cd --- /dev/null +++ b/src/kernel/src/KPC.cc @@ -0,0 +1,39 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +namespace Kernel { +STATIC Bool kRaiseOnBugCheck = false; + +/// @brief Does a system wide bug check. +/// @param void no params are needed. +/// @return if error-free: false, otherwise true. +Boolean err_bug_check_raise(Void) noexcept { + Char* ptr = new Char[512]; + + if (ptr == nullptr) goto bug_check_fail; + + if (!mm_is_valid_ptr(ptr)) goto bug_check_fail; + + delete[] ptr; + + return Yes; + +bug_check_fail: + if (ptr) delete[] ptr; + + ptr = nullptr; + + if (kRaiseOnBugCheck) { + ke_panic(RUNTIME_CHECK_BAD_BEHAVIOR); + } + + return No; +} +} // namespace Kernel diff --git a/src/kernel/src/KernelTaskScheduler.cc b/src/kernel/src/KernelTaskScheduler.cc new file mode 100644 index 00000000..c3628765 --- /dev/null +++ b/src/kernel/src/KernelTaskScheduler.cc @@ -0,0 +1,37 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + FILE: KernelTaskScheduler.cc + PURPOSE: Kernel Task scheduler. + +======================================== */ + +#include + +/***********************************************************************************/ +/// @file KernelTaskScheduler.cc +/// @brief Kernel Task scheduler. +/// @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +namespace Kernel { +EXTERN_C Void hal_switch_kernel_task(HAL::StackFramePtr frame, KID kid); + +Bool KernelTaskHelper::Add(HAL::StackFramePtr frame_ptr, KID new_kid) { + NE_UNUSED(frame_ptr); + NE_UNUSED(new_kid); + + return NO; +} + +Bool KernelTaskHelper::Remove(const KID kid) { + NE_UNUSED(kid); + + return NO; +} + +Bool KernelTaskHelper::CanBeScheduled(const KERNEL_TASK& task) { + return task.Kid > 0 && task.Image.HasCode(); +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/LockDelegate.cc b/src/kernel/src/LockDelegate.cc new file mode 100644 index 00000000..2fe2b464 --- /dev/null +++ b/src/kernel/src/LockDelegate.cc @@ -0,0 +1,11 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel { +/// @note Leave it empty for now. +} // namespace Kernel diff --git a/src/kernel/src/MutableArray.cc b/src/kernel/src/MutableArray.cc new file mode 100644 index 00000000..3c86e8b8 --- /dev/null +++ b/src/kernel/src/MutableArray.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/src/Network/IPAddress.cc b/src/kernel/src/Network/IPAddress.cc new file mode 100644 index 00000000..98279979 --- /dev/null +++ b/src/kernel/src/Network/IPAddress.cc @@ -0,0 +1,113 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +UInt8* RawIPAddress::Address() { + return fAddr; +} + +RawIPAddress::RawIPAddress(UInt8 bytes[4]) { + rt_copy_memory(bytes, fAddr, 4); +} + +BOOL RawIPAddress::operator==(const RawIPAddress& ipv4) { + for (Size index = 0; index < 4; ++index) { + if (ipv4.fAddr[index] != fAddr[index]) return false; + } + + return true; +} + +BOOL RawIPAddress::operator!=(const RawIPAddress& ipv4) { + for (Size index = 0; index < 4; ++index) { + if (ipv4.fAddr[index] == fAddr[index]) return false; + } + + return true; +} + +UInt8& RawIPAddress::operator[](const Size& index) { + kout << "[RawIPAddress::operator[]] Fetching Index...\r"; + + static UInt8 IP_PLACEHOLDER = '0'; + if (index >= 4) return IP_PLACEHOLDER; + + return fAddr[index]; +} + +RawIPAddress6::RawIPAddress6(UInt8 bytes[16]) { + rt_copy_memory(bytes, fAddr, 16); +} + +UInt8& RawIPAddress6::operator[](const Size& index) { + kout << "[RawIPAddress6::operator[]] Fetching Index...\r"; + + static UInt8 IP_PLACEHOLDER = '0'; + if (index >= 16) return IP_PLACEHOLDER; + + return fAddr[index]; +} + +bool RawIPAddress6::operator==(const RawIPAddress6& ipv6) { + for (SizeT index = 0; index < 16; ++index) { + if (ipv6.fAddr[index] != fAddr[index]) return false; + } + + return true; +} + +bool RawIPAddress6::operator!=(const RawIPAddress6& ipv6) { + for (SizeT index = 0; index < 16; ++index) { + if (ipv6.fAddr[index] == fAddr[index]) return false; + } + + return true; +} + +/// @todo +ErrorOr IPFactory::ToKString(Ref& ipv6) { + NE_UNUSED(ipv6); + auto str = KStringBuilder::Construct(""); + return str; +} + +/// @todo +ErrorOr IPFactory::ToKString(Ref& ipv4) { + NE_UNUSED(ipv4); + auto str = KStringBuilder::Construct(""); + return str; +} + +/// @note Doesn't catch IPs such as 256.999.0.1, UNSAFE! +bool IPFactory::IpCheckVersion4(const Char* ip) { + if (!ip) return NO; + + Int32 cnter = 0; + Int32 dot_cnter = 0; + + constexpr const auto kIP4DotCharacter = '.'; + + for (SizeT base = 0; base < rt_string_len(ip); ++base) { + if (ip[base] == kIP4DotCharacter) { + cnter = 0; + ++dot_cnter; + } else { + if (ip[base] > '5' || ip[base] < '0') return NO; + if (!rt_is_alnum(ip[base])) return NO; + if (cnter == 3) return NO; + + ++cnter; + } + } + + if (dot_cnter != 3) return NO; + + return YES; +} +} // namespace Kernel diff --git a/src/kernel/src/Network/IPCAddress.cc b/src/kernel/src/Network/IPCAddress.cc new file mode 100644 index 00000000..2da5ac0a --- /dev/null +++ b/src/kernel/src/Network/IPCAddress.cc @@ -0,0 +1,27 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + ======================================== */ + +#include +#include +#include + +namespace Kernel { +bool IPC_ADDR::operator==(const IPC_ADDR& addr) noexcept { + return addr.UserProcessID == this->UserProcessID && addr.UserProcessTeam == this->UserProcessTeam; +} + +bool IPC_ADDR::operator==(IPC_ADDR& addr) noexcept { + return addr.UserProcessID == this->UserProcessID && addr.UserProcessTeam == this->UserProcessTeam; +} + +bool IPC_ADDR::operator!=(const IPC_ADDR& addr) noexcept { + return addr.UserProcessID != this->UserProcessID || addr.UserProcessTeam != this->UserProcessTeam; +} + +bool IPC_ADDR::operator!=(IPC_ADDR& addr) noexcept { + return addr.UserProcessID != this->UserProcessID || addr.UserProcessTeam != this->UserProcessTeam; +} +} // namespace Kernel diff --git a/src/kernel/src/Network/IPCMessage.cc b/src/kernel/src/Network/IPCMessage.cc new file mode 100644 index 00000000..b376b7c8 --- /dev/null +++ b/src/kernel/src/Network/IPCMessage.cc @@ -0,0 +1,129 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @internal internal use for IPC system only. +/// @brief The internal sanitize function. +/***********************************************************************************/ +Bool ipc_int_sanitize_packet(IPC_MSG* pckt) { + auto endian = RTL_ENDIAN(pckt, ((Char*) pckt)[0]); + + switch (endian) { + case Endian::kEndianBig: { + if (pckt->IpcEndianess == kIPCLittleEndian) goto ipc_check_failed; + + break; + } + case Endian::kEndianLittle: { + if (pckt->IpcEndianess == kIPCBigEndian) goto ipc_check_failed; + + break; + } + case Endian::kEndianMixed: { + if (pckt->IpcEndianess == kIPCMixedEndian) goto ipc_check_failed; + + break; + } + default: + goto ipc_check_failed; + } + + if (pckt->IpcFrom == pckt->IpcTo || pckt->IpcPacketSize > kIPCMsgSize) { + goto ipc_check_failed; + } + + return pckt->IpcPacketSize > 1 && pckt->IpcHeaderMagic == kIPCHeaderMagic; + +ipc_check_failed: + err_local_get() = kErrorIPC; + return false; +} + +/***********************************************************************************/ +/// @brief Sanitize packet function +/// @retval true packet is correct. +/// @retval false packet is incorrect and process has crashed. +/***********************************************************************************/ +Bool ipc_sanitize_packet(IPC_MSG* pckt) { + if (!pckt || !ipc_int_sanitize_packet(pckt)) { + return false; + } + + return true; +} + +/***********************************************************************************/ +/// @brief Construct packet function +/// @retval true packet is correct. +/// @retval false packet is incorrect and process has crashed. +/***********************************************************************************/ +Bool ipc_construct_packet(_Output IPC_MSG** pckt_in) { + // don't act if it's not even valid. + if (!pckt_in) return false; + + if (!*pckt_in) *pckt_in = new IPC_MSG(); + + MUST_PASS(*pckt_in); + + if (*pckt_in) { + const auto endianess = RTL_ENDIAN((*pckt_in), ((Char*) (*pckt_in))[0]); + + (*pckt_in)->IpcHeaderMagic = kIPCHeaderMagic; + + (*pckt_in)->IpcEndianess = static_cast(endianess); + (*pckt_in)->IpcPacketSize = sizeof(IPC_MSG); + + (*pckt_in)->IpcTo.UserProcessID = 0; + (*pckt_in)->IpcTo.UserProcessTeam = 0; + + (*pckt_in)->IpcFrom.UserProcessID = 0; + (*pckt_in)->IpcFrom.UserProcessTeam = 0; + + (*pckt_in)->IpcLock = kIPCLockFree; + + return Yes; + } + + return No; +} + +/***********************************************************************************/ +/// @brief Pass message from **src** to **target** +/// @param src Source message. +/// @param target Target message. +/***********************************************************************************/ +Bool IPC_MSG::Pass(IPC_MSG* src, IPC_MSG* target) noexcept { + if (src && target && (target != src)) { + if (src->IpcMsgSz > target->IpcMsgSz) return No; + if (target->IpcMsgSz > src->IpcMsgSz) return No; + + auto timeout = 0U; + + const auto kLimitTimeout = 1000000U; + + while ((target->IpcLock % kIPCLockUsed) != 0) { + if (timeout > kLimitTimeout) { + return No; + } + } + + ++target->IpcLock; + + rt_copy_memory_safe(src->IpcData, target->IpcData, src->IpcMsgSz, kIPCMsgSize); + + --target->IpcLock; + + return Yes; + } + + return No; +} +} // namespace Kernel diff --git a/src/kernel/src/Network/MACAddressGetter.cc b/src/kernel/src/Network/MACAddressGetter.cc new file mode 100644 index 00000000..09d82a78 --- /dev/null +++ b/src/kernel/src/Network/MACAddressGetter.cc @@ -0,0 +1,13 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel { +Array& MacAddressGetter::AsBytes() { + return this->fMacAddress; +} +} // namespace Kernel diff --git a/src/kernel/src/Network/NetworkDevice.cc b/src/kernel/src/Network/NetworkDevice.cc new file mode 100644 index 00000000..450c0191 --- /dev/null +++ b/src/kernel/src/Network/NetworkDevice.cc @@ -0,0 +1,29 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +/// \brief Getter for fNetworkName. +/// \return Network device name. +const Char* NetworkDevice::Name() const { + return "/devices/net/net{}"; +} + +/// \brief Setter for fNetworkName. +Boolean NetworkDevice::Name(const Char* name) { + if (name == nullptr) return NO; + + if (*name == 0) return NO; + + if (rt_string_len(name) > rt_string_len(this->Name())) return NO; + + rt_copy_memory((VoidPtr) name, (VoidPtr) this->Name(), rt_string_len(this->Name())); + + return YES; +} +} // namespace Kernel diff --git a/src/kernel/src/New+Delete.cc b/src/kernel/src/New+Delete.cc new file mode 100644 index 00000000..ba37e7ec --- /dev/null +++ b/src/kernel/src/New+Delete.cc @@ -0,0 +1,48 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +void* operator new[](size_t sz) { + if (sz == 0) ++sz; + + return Kernel::mm_alloc_ptr(sz, true, false); +} + +void* operator new(size_t sz) { + if (sz == 0) ++sz; + + return Kernel::mm_alloc_ptr(sz, true, false); +} + +void operator delete[](void* ptr) noexcept { + if (ptr == nullptr) return; + + Kernel::mm_free_ptr(ptr); +} + +void operator delete(void* ptr) noexcept { + if (ptr == nullptr) return; + + Kernel::mm_free_ptr(ptr); +} + +void operator delete(void* ptr, size_t sz) { + if (ptr == nullptr) return; + + NE_UNUSED(sz); + + Kernel::mm_free_ptr(ptr); +} + +void operator delete[](void* ptr, size_t sz) { + if (ptr == nullptr) return; + + NE_UNUSED(sz); + + Kernel::mm_free_ptr(ptr); +} diff --git a/src/kernel/src/OwnPtr.cc b/src/kernel/src/OwnPtr.cc new file mode 100644 index 00000000..e223d448 --- /dev/null +++ b/src/kernel/src/OwnPtr.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/src/PE32CodeMgr.cc b/src/kernel/src/PE32CodeMgr.cc new file mode 100644 index 00000000..a8440c23 --- /dev/null +++ b/src/kernel/src/PE32CodeMgr.cc @@ -0,0 +1,258 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { +namespace Detail { + /***********************************************************************************/ + /// @brief Get the PE32+ platform signature according to the compiled architecture. + /***********************************************************************************/ + + UInt32 ldr_get_platform_pe(void) noexcept { +#if defined(__NE_AMD64__) + return kPEPlatformAMD64; +#elif defined(__NE_ARM64__) + return kPEPlatformARM64; +#else + return kPEPlatformInvalid; +#endif // __32x0__ || __64x0__ || __x86_64__ + } +} // namespace Detail + +/***********************************************************************************/ +/// @brief PE32+ loader constructor w/ blob. +/// @param blob file blob. +/***********************************************************************************/ + +PE32Loader::PE32Loader(const VoidPtr blob) : fCachedBlob(blob) { + MUST_PASS(fCachedBlob); + fBad = false; +} + +/***********************************************************************************/ +/// @brief PE32+ loader constructor. +/// @param path the filesystem path. +/***********************************************************************************/ + +PE32Loader::PE32Loader(const Char* path) : fCachedBlob(nullptr), fBad(false) { + fFile.New(const_cast(path), kRestrictRB); + fPath = KStringBuilder::Construct(path).Leak(); + + auto kPefHeader = "PE32_BLOB"; + fCachedBlob = fFile->Read(kPefHeader, 0); + + if (!fCachedBlob) fBad = YES; +} + +/***********************************************************************************/ +/// @brief PE32+ destructor. +/***********************************************************************************/ + +PE32Loader::~PE32Loader() { + if (fCachedBlob) mm_free_ptr(fCachedBlob); + + fFile.Delete(); +} + +/***********************************************************************************/ +/// @brief Finds the section according to its name. +/// @param name name of section. +/***********************************************************************************/ + +ErrorOr PE32Loader::FindSectionByName(const Char* name) { + if (!fCachedBlob || fBad || !name) return ErrorOr{kErrorInvalidData}; + + LDR_EXEC_HEADER_PTR header_ptr = CF::ldr_find_exec_header((const Char*) fCachedBlob); + LDR_OPTIONAL_HEADER_PTR opt_header_ptr = CF::ldr_find_opt_exec_header((const Char*) fCachedBlob); + + if (!header_ptr || !opt_header_ptr) return ErrorOr{kErrorInvalidData}; + +#ifdef __NE_AMD64__ + if (header_ptr->Machine != kPeMachineAMD64 || header_ptr->Signature != kPeSignature) { + return ErrorOr{kErrorInvalidData}; + } + +#elif defined(__NE_ARM64__) + if (header_ptr->Machine != kPeMachineARM64 || header_ptr->Signature != kPeSignature) { + return ErrorOr{kErrorInvalidData}; + } +#endif // __NE_AMD64__ || __NE_ARM64__ + + if (header_ptr->NumberOfSections < 1) { + return ErrorOr{kErrorInvalidData}; + } + + LDR_SECTION_HEADER_PTR secs = + (LDR_SECTION_HEADER_PTR) (((Char*) opt_header_ptr) + header_ptr->SizeOfOptionalHeader); + + for (SizeT sectIndex = 0; sectIndex < header_ptr->NumberOfSections; ++sectIndex) { + LDR_SECTION_HEADER_PTR sect = &secs[sectIndex]; + + if (KStringBuilder::Equals(name, sect->Name)) { + return ErrorOr(sect); + } + } + + return ErrorOr{kErrorInvalidData}; +} + +/***********************************************************************************/ +/// @brief Finds the symbol according to it's name. +/// @param name name of symbol. +/// @param kind kind of symbol we want. +/***********************************************************************************/ + +ErrorOr PE32Loader::FindSymbol(const Char* name, Int32 kind) { + if (!name || *name == 0) return ErrorOr{kErrorInvalidData}; + + auto section_name = "\0"; + + switch (kind) { + case kPETypeData: + section_name = ".data"; + break; + case kPETypeBSS: + section_name = ".bss"; + break; + case kPETypeText: + section_name = ".text"; + break; + default: + return ErrorOr{kErrorInvalidData}; + } + + auto sec = this->FindSectionByName(section_name); + LDR_SECTION_HEADER_PTR* sec_ptr = (LDR_SECTION_HEADER_PTR*) sec.Leak().Leak(); + + if (!sec_ptr || !*sec_ptr) return ErrorOr{kErrorInvalidData}; + + LDR_OPTIONAL_HEADER_PTR opt_header_ptr = CF::ldr_find_opt_exec_header((const Char*) fCachedBlob); + + if (opt_header_ptr) { + LDR_DATA_DIRECTORY_PTR data_dirs = + (LDR_DATA_DIRECTORY_PTR) ((UInt8*) opt_header_ptr + sizeof(LDR_OPTIONAL_HEADER)); + + LDR_DATA_DIRECTORY_PTR export_dir_entry = &data_dirs[0]; + + if (export_dir_entry->VirtualAddress == 0 || export_dir_entry->Size == 0) + return ErrorOr{kErrorInvalidData}; + + LDR_EXPORT_DIRECTORY* export_dir = + (LDR_EXPORT_DIRECTORY*) ((UIntPtr) fCachedBlob + export_dir_entry->VirtualAddress); + + UInt32* name_table = (UInt32*) ((UIntPtr) fCachedBlob + export_dir->AddressOfNames); + UInt16* ordinal_table = (UInt16*) ((UIntPtr) fCachedBlob + export_dir->AddressOfNameOrdinal); + UInt32* function_table = (UInt32*) ((UIntPtr) fCachedBlob + export_dir->AddressOfFunctions); + + for (UInt32 i = 0; i < export_dir->NumberOfNames; ++i) { + const char* exported_name = (const char*) ((UIntPtr) fCachedBlob + name_table[i]); + + if (KStringBuilder::Equals(exported_name, name)) { + UInt16 ordinal = ordinal_table[i]; + UInt32 rva = function_table[ordinal]; + + VoidPtr symbol_addr = (VoidPtr) ((UIntPtr) fCachedBlob + rva); + + return ErrorOr{symbol_addr}; + } + } + } + + return ErrorOr{kErrorInvalidData}; +} + +/// @brief Finds the executable entrypoint. +/// @return +ErrorOr PE32Loader::FindStart() { + if (auto sym = this->FindSymbol(kPeImageStart, 0); sym) return sym; + + return ErrorOr(kErrorExecutable); +} + +/// @brief Tells if the executable is loaded or not. +/// @return Whether it's not bad and is cached. +bool PE32Loader::IsLoaded() noexcept { + return !fBad && fCachedBlob; +} + +const Char* PE32Loader::Path() { + return fPath.Leak().CData(); +} + +const Char* PE32Loader::AsString() { +#ifdef __32x0__ + return "32x0 PE"; +#elif defined(__64x0__) + return "64x0 PE"; +#elif defined(__x86_64__) + return "x86_64 PE"; +#elif defined(__aarch64__) + return "AARCH64 PE"; +#elif defined(__powerpc64__) + return "POWER64 PE"; +#else + return "???? PE"; +#endif // __32x0__ || __64x0__ || __x86_64__ || __powerpc64__ +} + +const Char* PE32Loader::MIME() { + return kPeApplicationMime; +} + +ErrorOr PE32Loader::GetBlob() { + return ErrorOr{this->fCachedBlob}; +} + +namespace Utils { + ProcessID rtl_create_user_process(PE32Loader& exec, const Int32& process_kind) noexcept { + auto errOrStart = exec.FindStart(); + + if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID; + + auto symname = exec.FindSymbol(kPeImageStart, 0); + + if (!symname) { + symname = ErrorOr{(VoidPtr) rt_alloc_string("USER_PROCESS_PE32+")}; + } + + auto id = + UserProcessScheduler::The().Spawn(reinterpret_cast(symname.Leak().Leak()), + errOrStart.Leak().Leak(), exec.GetBlob().Leak().Leak()); + + mm_free_ptr(symname.Leak().Leak()); + + if (id != kSchedInvalidPID) { + auto stacksym = exec.FindSymbol(kPeStackSizeSymbol, 0); + + if (!stacksym) { + stacksym = ErrorOr{(VoidPtr) new UIntPtr(kSchedMaxStackSz)}; + } + + if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) { + *(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz; + } + + UserProcessScheduler::The().TheCurrentTeam().AsArray()[id].Kind = process_kind; + UserProcessScheduler::The().TheCurrentTeam().AsArray()[id].StackSize = + *(UIntPtr*) stacksym.Leak().Leak(); + + mm_free_ptr(stacksym.Leak().Leak()); + stacksym.Leak().Leak() = nullptr; + } + + return id; + } +} // namespace Utils +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/PEFCodeMgr.cc b/src/kernel/src/PEFCodeMgr.cc new file mode 100644 index 00000000..c0a79c32 --- /dev/null +++ b/src/kernel/src/PEFCodeMgr.cc @@ -0,0 +1,335 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + Copyright (C) 2025, Amlal El Mahrouss & NeKernel contributors, licensed under the Apache 2.0 +license. + +======================================== */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// @author Amlal El Mahrouss (amlal@nekernel.org) +/// @brief PEF backend for the Code Manager. +/// @file PEFCodeMgr.cc + +/// @brief PEF stack size symbol. +#define kPefStackSizeSymbol "__PEFSizeOfReserveStack" +#define kPefHeapSizeSymbol "__PEFSizeOfReserveHeap" +#define kPefNameSymbol "__PEFProgramName" + +namespace Kernel { +namespace Detail { + /***********************************************************************************/ + /// @brief Get the PEF platform signature according to the compiled architecture. + /***********************************************************************************/ + static UInt32 ldr_get_platform(void) noexcept { +#if defined(__NE_32X0__) + return kPefArch32x0; +#elif defined(__NE_64X0__) + return kPefArch64x0; +#elif defined(__NE_AMD64__) + return kPefArchAMD64; +#elif defined(__NE_PPC64__) + return kPefArchPowerPC; +#elif defined(__NE_ARM64__) + return kPefArchARM64; +#else + return kPefArchInvalid; +#endif // __32x0__ || __64x0__ || __x86_64__ + } +} // namespace Detail + +/***********************************************************************************/ +/// @brief PEF loader constructor w/ blob. +/// @param blob file blob. +/***********************************************************************************/ +PEFLoader::PEFLoader(const VoidPtr blob) : fCachedBlob(blob) { + MUST_PASS(fCachedBlob); + + if (!fCachedBlob) { + this->fBad = YES; + return; + } + + PEFContainer* container = reinterpret_cast(fCachedBlob); + + if (container->Abi == kPefAbi && + container->Count >= + 3) { /* if same ABI, AND: .text, .bss, .data (or at least similar) exists */ + if (container->Cpu == Detail::ldr_get_platform() && container->Magic[0] == kPefMagic[0] && + container->Magic[1] == kPefMagic[1] && container->Magic[2] == kPefMagic[2] && + container->Magic[3] == kPefMagic[3] && container->Magic[4] == kPefMagic[4]) { + return; + } else if (container->Magic[0] == kPefMagicFat[0] && container->Magic[1] == kPefMagicFat[1] && + container->Magic[2] == kPefMagicFat[2] && container->Magic[3] == kPefMagicFat[3] && + container->Magic[4] == kPefMagicFat[4]) { + /// This is a fat binary. Treat it as such. + this->fFatBinary = YES; + return; + } + } + + kout << "PEFLoader: warning: Binary format error!\r"; + + this->fFatBinary = NO; + this->fBad = YES; + + if (this->fCachedBlob) mm_free_ptr(this->fCachedBlob); + this->fCachedBlob = nullptr; +} + +/***********************************************************************************/ +/// @brief PEF loader constructor. +/// @param path the filesystem path. +/***********************************************************************************/ +PEFLoader::PEFLoader(const Char* path) : fCachedBlob(nullptr), fFatBinary(false), fBad(false) { + fFile.New(const_cast(path), kRestrictRB); + fPath = KStringBuilder::Construct(path).Leak(); + + constexpr auto kPefHeader = "PEF_CONTAINER"; + + /// @note zero here means that the FileMgr will read every container header inside the file. + fCachedBlob = fFile->Read(kPefHeader, 0UL); + + if (!fCachedBlob) { + this->fBad = YES; + return; + } + + PEFContainer* container = reinterpret_cast(fCachedBlob); + + if (container->Abi == kPefAbi && + container->Count >= + 3) { /* if same ABI, AND: .text, .bss, .data (or at least similar) exists */ + if (container->Cpu == Detail::ldr_get_platform() && container->Magic[0] == kPefMagic[0] && + container->Magic[1] == kPefMagic[1] && container->Magic[2] == kPefMagic[2] && + container->Magic[3] == kPefMagic[3] && container->Magic[4] == kPefMagic[4]) { + return; + } else if (container->Magic[0] == kPefMagicFat[0] && container->Magic[1] == kPefMagicFat[1] && + container->Magic[2] == kPefMagicFat[2] && container->Magic[3] == kPefMagicFat[3] && + container->Magic[4] == kPefMagicFat[4]) { + /// This is a fat binary, treat it as such. + this->fFatBinary = YES; + return; + } + } + + kout << "PEFLoader: warning: Binary format error!\r"; + + this->fFatBinary = NO; + this->fBad = YES; + + if (this->fCachedBlob) mm_free_ptr(this->fCachedBlob); + this->fCachedBlob = nullptr; +} + +/***********************************************************************************/ +/// @brief PEF destructor. +/***********************************************************************************/ +PEFLoader::~PEFLoader() { + if (fCachedBlob) mm_free_ptr(fCachedBlob); + + fFile.Delete(); +} + +/***********************************************************************************/ +/// @brief Finds the symbol according to it's name. +/// @param name name of symbol. +/// @param kind kind of symbol we want. +/***********************************************************************************/ +ErrorOr PEFLoader::FindSymbol(const Char* name, Int32 kind) { + if (!fCachedBlob || fBad || !name) return ErrorOr{kErrorInvalidData}; + + auto blob = fFile->Read(name, sizeof(PEFCommandHeader)); + + if (!blob) return ErrorOr{kErrorInvalidData}; + + PEFContainer* container = reinterpret_cast(fCachedBlob); + + if (!container) return ErrorOr{kErrorInvalidData}; + + PEFCommandHeader* command_header = reinterpret_cast(blob); + + if (command_header->VMSize < 1 || command_header->VMAddress == 0) + return ErrorOr{kErrorInvalidData}; + + /// fat binary check. + if (command_header->Cpu != container->Cpu && !this->fFatBinary) + return ErrorOr{kErrorInvalidData}; + + const auto kMangleCharacter = '$'; + const Char* kContainerKinds[] = {".code64", ".data64", ".zero64", nullptr}; + + ErrorOr error_or_symbol; + + switch (kind) { + case kPefCode: { + error_or_symbol = KStringBuilder::Construct(kContainerKinds[0]); // code symbol. + break; + } + case kPefData: { + error_or_symbol = KStringBuilder::Construct(kContainerKinds[1]); // data symbol. + break; + } + case kPefZero: { + error_or_symbol = KStringBuilder::Construct(kContainerKinds[2]); // block starting symbol. + break; + } + default: + return ErrorOr{kErrorInvalidData}; + // prevent that from the kernel's mode perspective, let that happen if it + // were a user process. + } + + Char* unconst_symbol = const_cast(name); + + for (SizeT i = 0UL; i < rt_string_len(unconst_symbol, kPefNameLen); ++i) { + if (rt_is_space(unconst_symbol[i])) { + unconst_symbol[i] = kMangleCharacter; + } + } + + error_or_symbol.Leak().Leak() += name; + + if (KStringBuilder::Equals(command_header->Name, error_or_symbol.Leak().Leak().CData())) { + if (command_header->Kind == kind) { + if (command_header->Cpu != Detail::ldr_get_platform()) { + if (!this->fFatBinary) { + mm_free_ptr(blob); + blob = nullptr; + + return ErrorOr{kErrorInvalidData}; + } + } + + Char* container_blob_value = new Char[command_header->VMSize]; + + rt_copy_memory_safe((VoidPtr) ((Char*) blob + sizeof(PEFCommandHeader)), container_blob_value, + command_header->VMSize, command_header->VMSize); + + mm_free_ptr(blob); + + kout << "PEFLoader: info: Load stub: " << command_header->Name << "!\r"; + + Int32 ret = 0; + SizeT pages_count = (command_header->VMSize + kPageSize - 1) / kPageSize; + + for (SizeT i_vm{}; i_vm < pages_count; ++i_vm) { + ret = HAL::mm_map_page( + (VoidPtr) (command_header->VMAddress + (i_vm * kPageSize)), + (VoidPtr) (HAL::mm_get_page_addr(container_blob_value) + (i_vm * kPageSize)), + HAL::kMMFlagsPresent | HAL::kMMFlagsUser); + + if (ret != kErrorSuccess) { + /// We set the VMAddress to nullptr, if the mapping fail here. + for (SizeT i_vm_unmap{}; i_vm_unmap < i_vm; ++i_vm_unmap) { + ret = HAL::mm_map_page((VoidPtr) (command_header->VMAddress + (i_vm * kPageSize)), + nullptr, HAL::kMMFlagsPresent | HAL::kMMFlagsUser); + } + + delete[] container_blob_value; + container_blob_value = nullptr; + + return ErrorOr{kErrorInvalidData}; + } + } + + return ErrorOr{container_blob_value}; + } + } + + mm_free_ptr(blob); + return ErrorOr{kErrorInvalidData}; +} + +/// @brief Finds the executable entrypoint. +/// @return +ErrorOr PEFLoader::FindStart() { + if (auto sym = this->FindSymbol(kPefStart, kPefCode); sym) return sym; + + return ErrorOr(kErrorExecutable); +} + +/// @brief Tells if the executable is loaded or not. +/// @return Whether it's not bad and is cached. +bool PEFLoader::IsLoaded() noexcept { + return !fBad && fCachedBlob; +} + +const Char* PEFLoader::Path() { + return fPath.Leak().CData(); +} + +const Char* PEFLoader::AsString() { +#ifdef __32x0__ + return "32x0 PEF"; +#elif defined(__64x0__) + return "64x0 PEF"; +#elif defined(__x86_64__) + return "x86_64 PEF"; +#elif defined(__aarch64__) + return "AARCH64 PEF"; +#elif defined(__powerpc64__) + return "POWER64 PEF"; +#else + return "???? PEF"; +#endif // __32x0__ || __64x0__ || __x86_64__ || __powerpc64__ +} + +const Char* PEFLoader::MIME() { + return kPefApplicationMime; +} + +ErrorOr PEFLoader::GetBlob() { + return ErrorOr{this->fCachedBlob}; +} + +namespace Utils { + ProcessID rtl_create_user_process(PEFLoader& exec, const Int32& process_kind) noexcept { + auto errOrStart = exec.FindStart(); + + if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID; + + auto symname = exec.FindSymbol(kPefNameSymbol, kPefData); + + if (!symname) { + symname = ErrorOr{(VoidPtr) rt_alloc_string("USER_PROCESS_PEF")}; + } + + ProcessID id = + UserProcessScheduler::The().Spawn(reinterpret_cast(symname.Leak().Leak()), + errOrStart.Leak().Leak(), exec.GetBlob().Leak().Leak()); + + mm_free_ptr(symname.Leak().Leak()); + + if (id != kSchedInvalidPID) { + auto stacksym = exec.FindSymbol(kPefStackSizeSymbol, kPefData); + + if (!stacksym) { + stacksym = ErrorOr{(VoidPtr) new UIntPtr(kSchedMaxStackSz)}; + } + + if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) { + *(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz; + } + + UserProcessScheduler::The().TheCurrentTeam().AsArray()[id].Kind = process_kind; + UserProcessScheduler::The().TheCurrentTeam().AsArray()[id].StackSize = + *(UIntPtr*) stacksym.Leak().Leak(); + + mm_free_ptr(stacksym.Leak().Leak()); + } + + return id; + } +} // namespace Utils +} // namespace Kernel diff --git a/src/kernel/src/PRDT.cc b/src/kernel/src/PRDT.cc new file mode 100644 index 00000000..626a7e16 --- /dev/null +++ b/src/kernel/src/PRDT.cc @@ -0,0 +1,22 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief constructs a new PRD. +/// @param prd PRD reference. +/// @note This doesnt construct a valid, please fill it by yourself. +/***********************************************************************************/ +void construct_prdt(Ref& prd) { + prd.Leak().fPhysAddress = 0x0; + prd.Leak().fSectorCount = 0x0; + prd.Leak().fEndBit = 0x0; +} +} // namespace Kernel diff --git a/src/kernel/src/PageMgr.cc b/src/kernel/src/PageMgr.cc new file mode 100644 index 00000000..81833ee2 --- /dev/null +++ b/src/kernel/src/PageMgr.cc @@ -0,0 +1,95 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +#ifdef __NE_AMD64__ +#include +#elif defined(__NE_ARM64__) +#include +#endif // ifdef __NE_AMD64__ || defined(__NE_ARM64__) + +namespace Kernel { +PTEWrapper::PTEWrapper(Boolean Rw, Boolean User, Boolean ExecDisable, UIntPtr VirtAddr) + : fRw(Rw), + fUser(User), + fExecDisable(ExecDisable), + fVirtAddr(VirtAddr), + fCache(false), + fShareable(false), + fWt(false), + fPresent(true), + fAccessed(false) {} + +PTEWrapper::~PTEWrapper() = default; + +/// @brief Flush virtual address. +/// @param VirtAddr +Void PageMgr::FlushTLB() { +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + hal_flush_tlb(); +#endif // !__NE_VIRTUAL_MEMORY_SUPPORT__ +} + +/// @brief Reclaim freed page. +/// @return +Bool PTEWrapper::Reclaim() { + if (!this->fPresent) { + this->fPresent = true; + return true; + } + + return false; +} + +/// @brief Request a PTE. +/// @param Rw Is it read write? or is it read only? +/// @param User user mode? +/// @param ExecDisable disable execution on page? +/// @return +PTEWrapper PageMgr::Request(Boolean Rw, Boolean User, Boolean ExecDisable, SizeT Sz, SizeT Pad) { + // Store PTE wrapper right after PTE. + VoidPtr ptr = Kernel::HAL::mm_alloc_bitmap(Rw, User, Sz, NO, Pad); + + return PTEWrapper{Rw, User, ExecDisable, reinterpret_cast(ptr)}; +} + +/// @brief Disable BitMap. +/// @param wrapper the wrapper. +/// @return If the page bitmap was cleared or not. +Bool PageMgr::Free(Ref& wrapper) { + if (!Kernel::HAL::mm_free_bitmap((VoidPtr) wrapper.Leak().VirtualAddress())) return false; + + return true; +} + +/// @brief Virtual PTE address. +/// @return The virtual address of the page. +UIntPtr PTEWrapper::VirtualAddress() { + return (fVirtAddr); +} + +Bool PTEWrapper::Shareable() { + return fShareable; +} + +Bool PTEWrapper::Present() { + return fPresent; +} + +Bool PTEWrapper::Access() { + return fAccessed; +} + +Void PTEWrapper::NoExecute(const bool enable) { + fExecDisable = enable; +} + +Bool PTEWrapper::NoExecute() { + return fExecDisable; +} +} // namespace Kernel diff --git a/src/kernel/src/Pmm.cc b/src/kernel/src/Pmm.cc new file mode 100644 index 00000000..84086929 --- /dev/null +++ b/src/kernel/src/Pmm.cc @@ -0,0 +1,83 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +#if defined(__NE_ARM64__) +#include +#endif // defined(__NE_ARM64__) + +#if defined(__NE_AMD64__) +#include +#endif // defined(__NE_AMD64__) + +namespace Kernel { +/***********************************************************************************/ +/// @brief Pmm constructor. +/***********************************************************************************/ +Pmm::Pmm() : fPageMgr() { + kout << "[PMM] Allocate PageMemoryMgr.\r"; +} + +Pmm::~Pmm() = default; + +/***********************************************************************************/ +/// @param If this returns Null pointer, enter emergency mode. +/// @param user is this a user page? +/// @param readWrite is it r/w? +/***********************************************************************************/ +Ref Pmm::RequestPage(Boolean user, Boolean readWrite) { + PTEWrapper pt = fPageMgr.Leak().Request(user, readWrite, false, kPageSize, 0); + + if (pt.fPresent) { + kout << "[PMM]: Allocation failed.\r"; + return {pt}; + } + + return Ref(pt); +} + +Boolean Pmm::FreePage(Ref PageRef) { + if (!PageRef) return false; + + PageRef.Leak().fPresent = false; + + return true; +} + +Boolean Pmm::TogglePresent(Ref PageRef, Boolean Enable) { + if (!PageRef) return false; + + PageRef.Leak().fPresent = Enable; + + return true; +} + +Boolean Pmm::ToggleUser(Ref PageRef, Boolean Enable) { + if (!PageRef) return false; + + PageRef.Leak().fRw = Enable; + + return true; +} + +Boolean Pmm::ToggleRw(Ref PageRef, Boolean Enable) { + if (!PageRef) return false; + + PageRef.Leak().fRw = Enable; + + return true; +} + +Boolean Pmm::ToggleShare(Ref PageRef, Boolean Enable) { + if (!PageRef) return false; + + PageRef.Leak().fShareable = Enable; + + return true; +} +} // namespace Kernel diff --git a/src/kernel/src/Property.cc b/src/kernel/src/Property.cc new file mode 100644 index 00000000..8b7454fe --- /dev/null +++ b/src/kernel/src/Property.cc @@ -0,0 +1,41 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel::CF { +/***********************************************************************************/ +/// @brief Destructor. +/***********************************************************************************/ +Property::~Property() = default; + +/***********************************************************************************/ +/// @brief Constructor. +/***********************************************************************************/ +Property::Property() = default; + +/***********************************************************************************/ +/// @brief Check if property's name equals to name. +/// @param name string to check. +/***********************************************************************************/ +Bool Property::StringEquals(KBasicString<>& name) { + return this->fName && this->fName == name; +} + +/***********************************************************************************/ +/// @brief Gets the key (name) of property. +/***********************************************************************************/ +KBasicString<>& Property::GetKey() { + return this->fName; +} + +/***********************************************************************************/ +/// @brief Gets the value of the property. +/***********************************************************************************/ +PropertyId& Property::GetValue() { + return fValue; +} +} // namespace Kernel::CF diff --git a/src/kernel/src/Ref.cc b/src/kernel/src/Ref.cc new file mode 100644 index 00000000..90b1c22d --- /dev/null +++ b/src/kernel/src/Ref.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include diff --git a/src/kernel/src/Semaphore.cc b/src/kernel/src/Semaphore.cc new file mode 100644 index 00000000..484c89ca --- /dev/null +++ b/src/kernel/src/Semaphore.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include \ No newline at end of file diff --git a/src/kernel/src/SoftwareTimer.cc b/src/kernel/src/SoftwareTimer.cc new file mode 100644 index 00000000..eafe8db6 --- /dev/null +++ b/src/kernel/src/SoftwareTimer.cc @@ -0,0 +1,36 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +/// ================================================================================ +/// @brief SoftwareTimer class, meant to be generic. +///! @author Amlal El Mahrouss (amlal@nekernel.org) +/// ================================================================================ + +using namespace Kernel; + +SoftwareTimer::SoftwareTimer(Int64 seconds) : fWaitFor(seconds) { + fDigitalTimer = new UIntPtr(); + MUST_PASS(fDigitalTimer); +} + +SoftwareTimer::~SoftwareTimer() { + delete fDigitalTimer; + fDigitalTimer = nullptr; + + fWaitFor = 0; +} + +BOOL SoftwareTimer::Wait() noexcept { + if (fWaitFor < 1) return NO; + + while (*fDigitalTimer < (*fDigitalTimer + fWaitFor)) { + ++(*fDigitalTimer); + } + + return YES; +} diff --git a/src/kernel/src/Storage/AHCIDeviceInterface.cc b/src/kernel/src/Storage/AHCIDeviceInterface.cc new file mode 100644 index 00000000..02b39fb5 --- /dev/null +++ b/src/kernel/src/Storage/AHCIDeviceInterface.cc @@ -0,0 +1,87 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; + +/// @brief Class constructor +/// @param Out Drive output +/// @param In Drive input +/// @param Cleanup Drive cleanup. +AHCIDeviceInterface::AHCIDeviceInterface(void (*out)(DeviceInterface* self, IMountpoint* outpacket), + void (*in)(DeviceInterface* self, IMountpoint* inpacket)) + : DeviceInterface(out, in) {} + +/// @brief Class desctructor +AHCIDeviceInterface::~AHCIDeviceInterface() = default; + +/// @brief Returns the name of the device interface. +/// @return it's name as a string. +const Char* AHCIDeviceInterface::Name() const { + return "/devices/sda{}"; +} + +/// @brief Output operator. +/// @param mnt the disk mountpoint. +/// @return the class itself after operation. +AHCIDeviceInterface& AHCIDeviceInterface::operator<<(IMountpoint* mnt) { + if (!mnt) return *this; + + for (SizeT driveCount = 0; driveCount < kDriveMaxCount; ++driveCount) { + auto interface = mnt->GetAddressOf(driveCount); + + if ((interface) && + rt_string_cmp((interface)->fProtocol(), "AHCI", rt_string_len("AHCI")) == 0) { + continue; + } else if ((interface) && + rt_string_cmp((interface)->fProtocol(), "AHCI", rt_string_len("AHCI")) != 0) { + return *this; + } + } + + return (AHCIDeviceInterface&) DeviceInterface::operator<<(mnt); +} + +/// @brief Input operator. +/// @param mnt the disk mountpoint. +/// @return the class itself after operation. +AHCIDeviceInterface& AHCIDeviceInterface::operator>>(IMountpoint* mnt) { + if (!mnt) return *this; + + for (SizeT driveCount = 0; driveCount < kDriveMaxCount; ++driveCount) { + auto interface = mnt->GetAddressOf(driveCount); + + // really check if it's ATA. + if ((interface) && + rt_string_cmp((interface)->fProtocol(), "AHCI", rt_string_len("AHCI")) == 0) { + continue; + } else if ((interface) && + rt_string_cmp((interface)->fProtocol(), "AHCI", rt_string_len("AHCI")) != 0) { + return *this; + } + } + + return (AHCIDeviceInterface&) DeviceInterface::operator>>(mnt); +} + +const UInt16& AHCIDeviceInterface::GetPortsImplemented() { + return this->fPortsImplemented; +} + +Void AHCIDeviceInterface::SetPortsImplemented(const UInt16& pi) { + MUST_PASS(pi > 0); + this->fPortsImplemented = pi; +} + +const UInt32& AHCIDeviceInterface::GetIndex() { + return this->fDriveIndex; +} + +Void AHCIDeviceInterface::SetIndex(const UInt32& drv) { + MUST_PASS(IMountpoint::kDriveIndexInvalid < drv); + this->fDriveIndex = drv; +} \ No newline at end of file diff --git a/src/kernel/src/Storage/ATADeviceInterface.cc b/src/kernel/src/Storage/ATADeviceInterface.cc new file mode 100644 index 00000000..8e15fdea --- /dev/null +++ b/src/kernel/src/Storage/ATADeviceInterface.cc @@ -0,0 +1,96 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; + +/// @brief Class constructor +/// @param Out Drive output +/// @param In Drive input +/// @param Cleanup Drive cleanup. +ATADeviceInterface::ATADeviceInterface(void (*Out)(DeviceInterface*, IMountpoint* outpacket), + void (*In)(DeviceInterface*, IMountpoint* inpacket)) + : DeviceInterface(Out, In) {} + +/// @brief Class desctructor +ATADeviceInterface::~ATADeviceInterface() = default; + +/// @brief Returns the name of the device interface. +/// @return it's name as a string. +const Char* ATADeviceInterface::Name() const { + return "/devices/hda{}"; +} + +/// @brief Output operator. +/// @param Data the disk mountpoint. +/// @return the class itself after operation. +ATADeviceInterface& ATADeviceInterface::operator<<(IMountpoint* Data) { + if (!Data) return *this; + + for (SizeT driveCount = 0; driveCount < kDriveMaxCount; ++driveCount) { + auto interface = Data->GetAddressOf(driveCount); + + if ((interface) && + rt_string_cmp((interface)->fProtocol(), "ATA-", rt_string_len("ATA-")) == 0) { + continue; + } else if ((interface) && + rt_string_cmp((interface)->fProtocol(), "ATA-", rt_string_len("ATA-")) != 0) { + return *this; + } + } + + return (ATADeviceInterface&) DeviceInterface::operator<<(Data); +} + +/// @brief Input operator. +/// @param Data the disk mountpoint. +/// @return the class itself after operation. +ATADeviceInterface& ATADeviceInterface::operator>>(IMountpoint* Data) { + if (!Data) return *this; + + for (SizeT driveCount = 0; driveCount < kDriveMaxCount; ++driveCount) { + auto interface = Data->GetAddressOf(driveCount); + + // really check if it's ATA. + if ((interface) && + rt_string_cmp((interface)->fProtocol(), "ATA-", rt_string_len("ATA-")) == 0) { + continue; + } else if ((interface) && + rt_string_cmp((interface)->fProtocol(), "ATA-", rt_string_len("ATA-")) != 0) { + return *this; + } + } + + return (ATADeviceInterface&) DeviceInterface::operator>>(Data); +} + +const UInt32& ATADeviceInterface::GetIndex() { + return this->fDriveIndex; +} + +Void ATADeviceInterface::SetIndex(const UInt32& drv) { + MUST_PASS(IMountpoint::kDriveIndexInvalid < drv); + this->fDriveIndex = drv; +} + +const UInt16& ATADeviceInterface::GetIO() { + return this->fIO; +} + +Void ATADeviceInterface::SetIO(const UInt16& drv) { + MUST_PASS(0xFFFF != drv); + this->fIO = drv; +} + +const UInt16& ATADeviceInterface::GetMaster() { + return this->fIO; +} + +Void ATADeviceInterface::SetMaster(const UInt16& drv) { + MUST_PASS(0xFFFF != drv); + this->fMaster = drv; +} \ No newline at end of file diff --git a/src/kernel/src/Storage/NVMEDeviceInterface.cc b/src/kernel/src/Storage/NVMEDeviceInterface.cc new file mode 100644 index 00000000..f05d384e --- /dev/null +++ b/src/kernel/src/Storage/NVMEDeviceInterface.cc @@ -0,0 +1,22 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel { +NVMEDeviceInterface::NVMEDeviceInterface(void (*out)(DeviceInterface*, IMountpoint* outpacket), + void (*in)(DeviceInterface*, IMountpoint* inpacket), + void (*cleanup)(void)) + : DeviceInterface(out, in), fCleanup(cleanup) {} + +NVMEDeviceInterface::~NVMEDeviceInterface() { + if (fCleanup) fCleanup(); +} + +const Char* NVMEDeviceInterface::Name() const { + return ("/devices/nvme{}"); +} +} // namespace Kernel diff --git a/src/kernel/src/Storage/SCSIDeviceInterface.cc b/src/kernel/src/Storage/SCSIDeviceInterface.cc new file mode 100644 index 00000000..2e331ae9 --- /dev/null +++ b/src/kernel/src/Storage/SCSIDeviceInterface.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +using namespace Kernel; diff --git a/src/kernel/src/Stream.cc b/src/kernel/src/Stream.cc new file mode 100644 index 00000000..8572e222 --- /dev/null +++ b/src/kernel/src/Stream.cc @@ -0,0 +1,12 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + File: Stream.cc + Purpose: Stream object + + Revision History: + +======================================== */ + +#include diff --git a/src/kernel/src/Swap/DiskSwap.cc b/src/kernel/src/Swap/DiskSwap.cc new file mode 100644 index 00000000..f0fa5915 --- /dev/null +++ b/src/kernel/src/Swap/DiskSwap.cc @@ -0,0 +1,64 @@ +/* ======================================== + + Copyright (C) 2024-2025 Amlal El Mahrouss , licensed under the Apache 2.0 license. + +======================================== */ + +#include +#include + +namespace Kernel { +static constexpr UInt32 kSwapDiskHeaderMagic = 0x44535750; // 'DSWP' + +/***********************************************************************************/ +/// @brief Write memory chunk onto disk. +/// @param fork_name The swap name to recognize this memory region. +/// @param fork_name_len length of fork name. +/// @param data the data packet. +/// @return Whether the swap was written to disk, or not. +/***********************************************************************************/ +BOOL DiskSwapInterface::Write(const Char* fork_name, SizeT fork_name_len, SWAP_DISK_HEADER* data) { + if (!fork_name || !fork_name_len) return NO; + + if (*fork_name == 0) return NO; + + if (!data || data->fMagic != kSwapDiskHeaderMagic) return NO; + + FileStream file(kSwapPageFilePath, kRestrictWRB); + + ErrorOr ret = file.Write(fork_name, data, sizeof(SWAP_DISK_HEADER) + data->fBlobSz); + + if (ret.Error()) return NO; + + return YES; +} + +/***********************************************************************************/ +/// @brief Read memory chunk from disk. +/// @param fork_name The swap name to recognize this memory region. +/// @param fork_name_len length of fork name. +/// @param data the data packet length. +/// @return Whether the swap was fetched to disk, or not. +/***********************************************************************************/ +SWAP_DISK_HEADER* DiskSwapInterface::Read(const Char* fork_name, SizeT fork_name_len, + SizeT data_len) { + if (!fork_name || !fork_name_len) return nullptr; + + if (*fork_name == 0) return nullptr; + + if (data_len > kSwapBlockMaxSize) return nullptr; + + if (data_len == 0) return nullptr; + + FileStream file(kSwapPageFilePath, kRestrictRB); + + VoidPtr blob = file.Read(fork_name, sizeof(SWAP_DISK_HEADER) + data_len); + + if (!blob || ((SWAP_DISK_HEADER*) blob)->fMagic != kSwapDiskHeaderMagic) { + if (blob) mm_free_ptr(blob); + return nullptr; + } + + return reinterpret_cast(blob); +} +} // namespace Kernel diff --git a/src/kernel/src/ThreadLocalStorage.cc b/src/kernel/src/ThreadLocalStorage.cc new file mode 100644 index 00000000..fa445d84 --- /dev/null +++ b/src/kernel/src/ThreadLocalStorage.cc @@ -0,0 +1,52 @@ +/* + * ======================================================== + * + * NeKernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * ======================================================== + */ + +#include +#include +#include +#include + +/***********************************************************************************/ +/// @bugs: 0 +/// @file ThreadLocalStorage.cc +/// @brief NeKernel Thread Local Storage. +///! @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +using namespace Kernel; + +/** + * @brief Checks for cookie inside the TIB. + * @param tib_ptr the TIB to check. + * @return if the cookie is enabled, true; false otherwise + */ + +Boolean tls_check_tib(THREAD_INFORMATION_BLOCK* tib_ptr) { + if (!tib_ptr) return false; + + return tib_ptr->Cookie[kCookieMag0Idx] == kCookieMag0 && + tib_ptr->Cookie[kCookieMag1Idx] == kCookieMag1 && + tib_ptr->Cookie[kCookieMag2Idx] == kCookieMag2; +} + +/** + * @brief System call implementation of the TLS check. + * @param tib_ptr The TIB record. + * @return if the TIB record is valid or not. + */ +EXTERN_C Bool tls_check_syscall_impl(Kernel::VoidPtr tib_ptr) noexcept { + if (!tib_ptr) { + kout << "TLS: Failed because of an invalid TIB...\r"; + return No; + } + + THREAD_INFORMATION_BLOCK* tib = reinterpret_cast(tib_ptr); + + return tls_check_tib(tib); +} diff --git a/src/kernel/src/Timer.cc b/src/kernel/src/Timer.cc new file mode 100644 index 00000000..648cc92b --- /dev/null +++ b/src/kernel/src/Timer.cc @@ -0,0 +1,19 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +///! BUGS: 0 +///! @file Timer.cc +///! @brief Software Timer implementation +///! @author Amlal El Mahrouss (amlal@nekernel.org) + +using namespace Kernel; + +/// @brief Unimplemented as it is an interface. +BOOL TimerInterface::Wait() noexcept { + return NO; +} diff --git a/src/kernel/src/UserMgr.cc b/src/kernel/src/UserMgr.cc new file mode 100644 index 00000000..103e8ec9 --- /dev/null +++ b/src/kernel/src/UserMgr.cc @@ -0,0 +1,131 @@ +/* + * ======================================================== + * + * NeKernel + * Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + * File: UserMgr.cc + * Purpose: User Manager, used to provide authentication and security. + * + * ======================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define kStdUserType (0xEE) +#define kSuperUserType (0xEF) + +/// @file UserMgr.cc +/// @brief Multi-user support. + +namespace Kernel { +namespace Detail { + //////////////////////////////////////////////////////////// + /// \brief Constructs a password by hashing the password. + /// \param password password to hash. + /// \return the hashed password + //////////////////////////////////////////////////////////// + STATIC UInt64 user_fnv_generator(const Char* password, User* user) { + if (!password || !user) return 0; + if (*password == 0) return 0; + + kout << "user_fnv_generator: Hashing user password...\r"; + + const UInt64 kFnvOffsetBasis = 0xcbf29ce484222325ULL; + const UInt64 fFnvPrime = 0x100000001b3ULL; + + UInt64 hash = kFnvOffsetBasis; + + while (*password) { + hash ^= (Char) (*password++); + hash *= fFnvPrime; + } + + kout << "user_fnv_generator: Hashed user password.\r"; + + return hash; + } +} // namespace Detail + +//////////////////////////////////////////////////////////// +/// @brief User ring constructor. +//////////////////////////////////////////////////////////// +User::User(const Int32& sel, const Char* user_name) : mUserRing((UserRingKind) sel) { + MUST_PASS(sel >= 0); + rt_copy_memory_safe((VoidPtr) user_name, this->mUserName, rt_string_len(user_name), + kMaxUserNameLen); +} + +//////////////////////////////////////////////////////////// +/// @brief User ring constructor. +//////////////////////////////////////////////////////////// +User::User(const UserRingKind& ring_kind, const Char* user_name) : mUserRing(ring_kind) { + rt_copy_memory_safe((VoidPtr) user_name, this->mUserName, rt_string_len(user_name), + kMaxUserNameLen); +} + +//////////////////////////////////////////////////////////// +/// @brief User destructor class. +//////////////////////////////////////////////////////////// +User::~User() = default; + +Bool User::Save(const UserPublicKey password) noexcept { + if (!password || *password == 0) return No; + + this->mUserFNV = Detail::user_fnv_generator(password, this); + + kout << "User::Save: Saved password successfully...\r"; + + return Yes; +} + +Bool User::Login(const UserPublicKey password) noexcept { + if (!password || !*password) return No; + + auto ret = this->mUserFNV == Detail::user_fnv_generator(password, this); + + // now check if the password matches. + kout << (ret ? "User::Login: Password matches.\r" : "User::Login: Password doesn't match.\r"); + return ret; +} + +Bool User::operator==(const User& lhs) { + return lhs.mUserRing == this->mUserRing; +} + +Bool User::operator!=(const User& lhs) { + return lhs.mUserRing != this->mUserRing; +} + +//////////////////////////////////////////////////////////// +/// @brief Returns the user's name. +//////////////////////////////////////////////////////////// + +Char* User::Name() noexcept { + return this->mUserName; +} + +//////////////////////////////////////////////////////////// +/// @brief Returns the user's ring. +/// @return The king of ring the user is attached to. +//////////////////////////////////////////////////////////// + +const UserRingKind& User::Ring() noexcept { + return this->mUserRing; +} + +Bool User::IsStdUser() noexcept { + return this->Ring() == UserRingKind::kRingStdUser; +} + +Bool User::IsSuperUser() noexcept { + return this->Ring() == UserRingKind::kRingSuperUser; +} +} // namespace Kernel diff --git a/src/kernel/src/UserProcessScheduler.cc b/src/kernel/src/UserProcessScheduler.cc new file mode 100644 index 00000000..264bddf3 --- /dev/null +++ b/src/kernel/src/UserProcessScheduler.cc @@ -0,0 +1,690 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + + FILE: UserProcessScheduler.cc + PURPOSE: Low-Privilege/Ring-3 process scheduler. + +======================================== */ + +/***********************************************************************************/ +/// @file UserProcessScheduler.cc +/// @brief Unprivileged/Ring-3 process scheduler. +/// @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///! BUGS: 0 + +namespace Kernel { +USER_PROCESS::USER_PROCESS() = default; +USER_PROCESS::~USER_PROCESS() = default; + +/// @brief Gets the last exit code. +/// @note Not thread-safe. +/// @return Int32 the last exit code. + +/***********************************************************************************/ +/// @brief Crashes the current process. +/***********************************************************************************/ + +Void USER_PROCESS::Crash() { + if (this->Status != ProcessStatusKind::kRunning) return; + + this->Status = ProcessStatusKind::kKilled; + + (Void)(kout << this->Name << ": crashed, error id: " << number(-kErrorProcessFault) << kendl); +} + +/***********************************************************************************/ +/// @brief boolean operator, check status. +/***********************************************************************************/ + +USER_PROCESS::operator bool() { + return this->Status == ProcessStatusKind::kRunning; +} + +/***********************************************************************************/ +/// @brief Gets the local last exit code. +/// @note Not thread-safe. +/// @return Int32 the last exit code. +/***********************************************************************************/ + +KPCError& USER_PROCESS::GetExitCode() noexcept { + return this->LastExitCode; +} + +/***********************************************************************************/ +/// @brief Error code variable getter. +/***********************************************************************************/ + +KPCError& USER_PROCESS::GetLocalCode() noexcept { + return this->LocalCode; +} + +/***********************************************************************************/ +/// @brief Wakes process header. +/// @param should_wakeup if the program shall wakeup or not. +/***********************************************************************************/ + +Void USER_PROCESS::Wake(Bool should_wakeup) { + this->Status = should_wakeup ? ProcessStatusKind::kRunning : ProcessStatusKind::kFrozen; +} + +/***********************************************************************************/ +/** @brief Allocate pointer to heap tree. */ +/** @param tree The tree to calibrate */ +/***********************************************************************************/ + +template +STATIC T* sched_try_go_upper_ptr_tree(T* tree) { + if (!tree) { + return nullptr; + } + + tree = tree->Parent; + + if (tree) { + auto tree_tmp = tree->Next; + + if (!tree_tmp) { + return tree; + } + + return tree_tmp; + } + + return tree; +} + +/***********************************************************************************/ +/** @brief Allocate pointer to heap/file tree. */ +/***********************************************************************************/ + +ErrorOr USER_PROCESS::New(SizeT sz, SizeT pad_amount) { + if (this->UsedMemory > kSchedMaxMemoryLimit) return ErrorOr(-kErrorHeapOutOfMemory); + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + auto vm_register = kKernelVM; + + hal_write_cr3(this->VMRegister); + + auto ptr = mm_alloc_ptr(sz, Yes, Yes, pad_amount); + + hal_write_cr3(vm_register); +#else + auto ptr = mm_alloc_ptr(sz, Yes, Yes, pad_amount); +#endif + + if (!this->HeapTree) { + this->HeapTree = new PROCESS_HEAP_TREE(); + + if (!this->HeapTree) { + this->Crash(); + return ErrorOr(-kErrorHeapOutOfMemory); + } + + this->HeapTree->EntryPad = pad_amount; + this->HeapTree->EntrySize = sz; + + this->HeapTree->Entry = ptr; + + this->HeapTree->Color = kBlackTreeKind; + + this->HeapTree->Prev = nullptr; + this->HeapTree->Next = nullptr; + this->HeapTree->Parent = nullptr; + this->HeapTree->Child = nullptr; + } else { + PROCESS_HEAP_TREE* entry = this->HeapTree; + PROCESS_HEAP_TREE* prev_entry = entry; + + BOOL is_parent = NO; + + while (entry) { + if (entry->EntrySize < 1) break; + + prev_entry = entry; + + if (entry->Child && entry->Child->EntrySize > 0 && entry->Child->EntrySize == sz) { + entry = entry->Child; + is_parent = YES; + } else if (entry->Next && entry->Next->EntrySize > 0 && entry->Next->EntrySize == sz) { + is_parent = NO; + entry = entry->Next; + } else { + entry = sched_try_go_upper_ptr_tree(entry); + if (entry && entry->Color == kBlackTreeKind) break; + } + } + + auto new_entry = new PROCESS_HEAP_TREE(); + + if (!new_entry) { + this->Crash(); + return ErrorOr(-kErrorHeapOutOfMemory); + } + + new_entry->Entry = ptr; + new_entry->EntrySize = sz; + new_entry->EntryPad = pad_amount; + new_entry->Parent = entry; + new_entry->Child = nullptr; + new_entry->Next = nullptr; + new_entry->Prev = nullptr; + + new_entry->Color = kBlackTreeKind; + prev_entry->Color = kRedTreeKind; + + if (is_parent) { + prev_entry->Child = new_entry; + new_entry->Parent = prev_entry; + } else { + prev_entry->Next = new_entry; + new_entry->Prev = prev_entry; + } + } + + this->UsedMemory += sz; + + return ErrorOr(ptr); +} + +/***********************************************************************************/ +/// @brief Gets the name of the current process. +/***********************************************************************************/ + +const Char* USER_PROCESS::GetName() noexcept { + return this->Name; +} + +/***********************************************************************************/ +/// @brief Gets the owner of the process. +/***********************************************************************************/ + +const User* USER_PROCESS::GetOwner() noexcept { + return this->Owner; +} + +/// @brief USER_PROCESS status getter. +const ProcessStatusKind& USER_PROCESS::GetStatus() noexcept { + return this->Status; +} + +/***********************************************************************************/ +/** +@brief Affinity is the time slot allowed for the process. +*/ +/***********************************************************************************/ + +const AffinityKind& USER_PROCESS::GetAffinity() noexcept { + return this->Affinity; +} + +/***********************************************************************************/ +/** @brief Free heap tree. */ +/***********************************************************************************/ + +template +STATIC Void sched_free_ptr_tree(T* tree) { + // Deleting memory lists. Make sure to free all of them. + while (tree) { + if (tree->Entry) { + MUST_PASS(mm_free_ptr(tree->Entry)); + } + + auto next = tree->Next; + + if (next->Child) sched_free_ptr_tree(next->Child); + + tree->Child = nullptr; + + mm_free_ptr(tree); + + tree = nullptr; + tree = next; + } +} + +/***********************************************************************************/ +/** +@brief Exit process method. +@param exit_code The process's exit code. +*/ +/***********************************************************************************/ + +Void USER_PROCESS::Exit(const Int32& exit_code) { + this->Status = exit_code > 0 ? ProcessStatusKind::kKilled : ProcessStatusKind::kFrozen; + this->LastExitCode = exit_code; + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + auto pd = kKernelVM; + hal_write_cr3(this->VMRegister); +#endif + + sched_free_ptr_tree(this->HeapTree); + this->HeapTree = nullptr; + + sched_free_ptr_tree(this->FileTree); + this->FileTree = nullptr; + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + hal_write_cr3(pd); +#endif + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + //! Free the memory's page directory. + if (this->VMRegister) HAL::mm_free_bitmap(this->VMRegister); +#endif + + //! Delete image if not done already. + if (this->Image.fCode && mm_is_valid_ptr(this->Image.fCode)) mm_free_ptr(this->Image.fCode); + + //! Delete blob too. + if (this->Image.fBlob && mm_is_valid_ptr(this->Image.fBlob)) mm_free_ptr(this->Image.fBlob); + + //! Delete stack frame. + if (this->StackFrame && mm_is_valid_ptr(this->StackFrame)) + mm_free_ptr((VoidPtr) this->StackFrame); + + //! Avoid use after free. + this->Image.fBlob = nullptr; + this->Image.fCode = nullptr; + this->StackFrame = nullptr; + + if (this->Kind == kExecutableDylibKind) { + Bool success = false; + + rtl_fini_dylib_pef(*this, reinterpret_cast(this->DylibDelegate), &success); + + if (!success) { + ke_panic(RUNTIME_CHECK_PROCESS); + } + + this->DylibDelegate = nullptr; + } + + this->ProcessId = 0UL; + this->Status = ProcessStatusKind::kFinished; + + --this->ParentTeam->mProcessCur; +} + +/***********************************************************************************/ +/// @brief Add dylib to the process object. +/***********************************************************************************/ + +Bool USER_PROCESS::InitDylib() { + // React according to the process's kind. + switch (this->Kind) { + case USER_PROCESS::kExecutableDylibKind: { + this->DylibDelegate = rtl_init_dylib_pef(*this); + + if (!this->DylibDelegate) { + this->Crash(); + return NO; + } + + return YES; + } + case USER_PROCESS::kExecutableKind: { + return NO; + } + default: { + break; + } + } + + (Void)(kout << "Unknown process kind: " << hex_number(this->Kind) << kendl); + this->Crash(); + + return NO; +} + +/***********************************************************************************/ +/// @brief Add process to team. +/// @param process the process *Ref* class. +/// @return the process index inside the team. +/***********************************************************************************/ + +ProcessID UserProcessScheduler::Spawn(const Char* name, VoidPtr code, VoidPtr image) { + if (!name || !code) { + return -kErrorProcessFault; + } + + if (*name == 0) { + return -kErrorProcessFault; + } + + ProcessID pid = this->mTeam.mProcessCur; + + if (pid > kSchedProcessLimitPerTeam) { + return -kErrorProcessFault; + } + + ++this->mTeam.mProcessCur; + + USER_PROCESS& process = this->mTeam.mProcessList[pid]; + + process.Image.fCode = code; + process.Image.fBlob = image; + + SizeT len = rt_string_len(name); + + if (len > kSchedNameLen) { + return -kErrorProcessFault; + } + + rt_copy_memory_safe(reinterpret_cast(const_cast(name)), process.Name, len, + kSchedNameLen); + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + process.VMRegister = kKernelVM; +#else + process.VMRegister = 0UL; +#endif // ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + + process.StackFrame = new HAL::StackFrame(); + + if (!process.StackFrame) { + process.Crash(); + return -kErrorProcessFault; + } + + rt_set_memory(process.StackFrame, 0, sizeof(HAL::StackFrame)); + + process.StackFrame->IP = reinterpret_cast(code); + process.StackFrame->SP = reinterpret_cast(&process.StackReserve[0] + process.StackSize); + +#ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + HAL::mm_map_page((VoidPtr) process.StackFrame->IP, + (VoidPtr) HAL::mm_get_page_addr((VoidPtr) process.StackFrame->IP), + HAL::kMMFlagsUser | HAL::kMMFlagsPresent); + HAL::mm_map_page((VoidPtr) process.StackFrame->SP, + (VoidPtr) HAL::mm_get_page_addr((VoidPtr) process.StackFrame->SP), + HAL::kMMFlagsUser | HAL::kMMFlagsPresent); +#endif // ifdef __NE_VIRTUAL_MEMORY_SUPPORT__ + + process.StackSize = kSchedMaxStackSz; + + rt_set_memory(process.StackReserve, 0, process.StackSize); + + process.ParentTeam = &mTeam; + + process.ProcessId = pid; + process.Status = ProcessStatusKind::kRunning; + process.PTime = 0; + process.UTime = 0; + process.RTime = 0; + + if (!process.FileTree) { + process.FileTree = new PROCESS_FILE_TREE(); + + if (!process.FileTree) { + process.Crash(); + return ErrorOr(-kErrorHeapOutOfMemory); + } + + /// @todo File Tree allocation and dispose methods (amlal) + } + + (Void)(kout << "ProcessID: " << number(process.ProcessId) << kendl); + (Void)(kout << "ProcesName: " << process.Name << kendl); + + return pid; +} + +/***********************************************************************************/ +/// @brief Retrieves the singleton of the process scheduler. +/***********************************************************************************/ + +UserProcessScheduler& UserProcessScheduler::The() { + STATIC UserProcessScheduler kScheduler; + return kScheduler; +} + +/***********************************************************************************/ + +/// @brief Remove process from the team. +/// @param process_id process slot inside team. +/// @retval true process was removed. +/// @retval false process doesn't exist in team. + +/***********************************************************************************/ + +Void UserProcessScheduler::Remove(ProcessID process_id) { + if (process_id < 0 || process_id > kSchedProcessLimitPerTeam) return; + if (this->mTeam.mProcessList[process_id].Status == ProcessStatusKind::kInvalid) return; + + mTeam.mProcessList[process_id].Exit(kErrorSuccess); +} + +/// @brief Is it a user scheduler? + +Bool UserProcessScheduler::IsUser() { + return Yes; +} + +/// @brief Is it a kernel scheduler? + +Bool UserProcessScheduler::IsKernel() { + return No; +} + +/// @brief Is it a SMP scheduler? + +Bool UserProcessScheduler::HasMP() { + MUST_PASS(kHandoverHeader); + return kHandoverHeader->f_HardwareTables.f_MultiProcessingEnabled; +} + +/***********************************************************************************/ +/// @brief Run User scheduler object. +/// @return USER_PROCESS count executed within a team. +/***********************************************************************************/ + +SizeT UserProcessScheduler::Run() noexcept { + if (mTeam.mProcessCur < 1) { + return 0UL; + } + + SizeT process_index = 0UL; //! we store this guy to tell the scheduler how many + //! things we have scheduled. + + for (; process_index < mTeam.AsArray().Capacity(); ++process_index) { + auto& process = mTeam.AsArray()[process_index]; + + //! Check if the process needs to be run. + if (UserProcessHelper::CanBeScheduled(process)) { + kout << process.Name << " will be run...\r"; + + //! Increase the usage time of the process. + if (process.UTime < process.PTime) { + ++process.UTime; + } + + this->TheCurrentProcess() = process; + + if (UserProcessHelper::Switch(process.StackFrame, process.ProcessId)) { + process.PTime = static_cast(process.Affinity); + + // We add a bigger cooldown according to the RTime and affinity here. + if (process.PTime < process.RTime && AffinityKind::kRealTime != process.Affinity) { + if (process.RTime < (Int32) AffinityKind::kVeryHigh) + process.RTime += (Int32) AffinityKind::kLowUsage; + else if (process.RTime < (Int32) AffinityKind::kHigh) + process.RTime += (Int32) AffinityKind::kStandard; + else if (process.RTime < (Int32) AffinityKind::kStandard) + process.RTime += (Int32) AffinityKind::kHigh; + + process.PTime -= process.RTime; + process.RTime = 0UL; + } + } + } else { + ++process.RTime; + --process.PTime; + } + } + + return process_index; +} + +/// @brief Gets the current scheduled team. +/// @return +UserProcessTeam& UserProcessScheduler::TheCurrentTeam() { + return mTeam; +} + +/***********************************************************************************/ +/// @brief Switches the current team. +/// @param team the new team to switch to. +/// @retval true team was switched. +/// @retval false team was not switched. +/***********************************************************************************/ + +BOOL UserProcessScheduler::SwitchTeam(UserProcessTeam& team) { + if (team.AsArray().Count() < 1) return No; + + this->mTeam = team; + + return Yes; +} + +/// @brief Gets current running process. +/// @return +Ref& UserProcessScheduler::TheCurrentProcess() { + return mTeam.AsRef(); +} + +/// @brief Current proccess id getter. +/// @return USER_PROCESS ID integer. +ErrorOr UserProcessHelper::TheCurrentPID() { + if (!UserProcessScheduler::The().TheCurrentProcess()) + return ErrorOr{-kErrorProcessFault}; + + kout << "UserProcessHelper::TheCurrentPID: Leaking ProcessId...\r"; + return ErrorOr{UserProcessScheduler::The().TheCurrentProcess().Leak().ProcessId}; +} + +/// @brief Check if process can be schedulded. +/// @param process the process reference. +/// @retval true can be schedulded. +/// @retval false cannot be schedulded. +Bool UserProcessHelper::CanBeScheduled(const USER_PROCESS& process) { + if (process.Affinity == AffinityKind::kRealTime) return Yes; + + if (process.Status != ProcessStatusKind::kRunning) return No; + if (process.Affinity == AffinityKind::kInvalid) return No; + if (process.StackSize > kSchedMaxStackSz) return No; + if (!process.Name[0]) return No; + if (process.Signal.SignalID == sig_generate_unique()) return No; + + return process.PTime < 1; +} + +/***********************************************************************************/ +/** + * @brief Start scheduling the current team. + */ +/***********************************************************************************/ + +SizeT UserProcessHelper::StartScheduling() { + return UserProcessScheduler::The().Run(); +} + +/***********************************************************************************/ +/** + * \brief Does a context switch in a CPU. + * \param the_stack the stackframe of the running app. + * \param new_pid the process's ProcessID. + */ +/***********************************************************************************/ + +Bool UserProcessHelper::Switch(HAL::StackFramePtr frame_ptr, ProcessID new_pid) { + (Void)(kout << "IP: " << hex_number(frame_ptr->IP) << kendl); + + for (SizeT index = 0UL; index < HardwareThreadScheduler::The().Capacity(); ++index) { + if (!HardwareThreadScheduler::The()[index].Leak()) continue; + + if (HardwareThreadScheduler::The()[index].Leak()->Kind() == kAPInvalid || + HardwareThreadScheduler::The()[index].Leak()->Kind() == kAPBoot) + continue; + + (Void)(kout << "AP_" << hex_number(index) << kendl); + + if (HardwareThreadScheduler::The()[index].Leak()->IsBusy()) { + (Void)(kout << "AP_" << hex_number(index)); + kout << " is busy\r"; + + continue; + } + + (Void)(kout << "AP_" << hex_number(index)); + kout << " is now trying to run a new task!\r"; + + //////////////////////////////////////////////////////////// + /// Prepare task switch. /// + //////////////////////////////////////////////////////////// + + HardwareThreadScheduler::The()[index].Leak()->Busy(YES); + + Bool ret = HardwareThreadScheduler::The()[index].Leak()->Switch(frame_ptr); + + //////////////////////////////////////////////////////////// + /// Rollback on fail. /// + //////////////////////////////////////////////////////////// + + if (!ret) continue; + + UserProcessHelper::TheCurrentPID().Leak().Leak() = new_pid; + + HardwareThreadScheduler::The()[index].Leak()->fPTime = + UserProcessScheduler::The().TheCurrentTeam().AsArray()[new_pid].PTime; + + (Void)(kout << "AP_" << hex_number(index)); + kout << " is now running a new task!\r"; + + return YES; + } + + kout << "Couldn't find a suitable core for the current process!\r"; + + return NO; +} + +//////////////////////////////////////////////////////////// +/// @brief this checks if any process is on the team. +//////////////////////////////////////////////////////////// +UserProcessScheduler::operator bool() { + for (auto process_index = 0UL; process_index < mTeam.AsArray().Count(); ++process_index) { + auto& process = mTeam.AsArray()[process_index]; + if (UserProcessHelper::CanBeScheduled(process)) return true; + } + + return false; +} + +//////////////////////////////////////////////////////////// +/// @brief this checks if no process is on the team. +//////////////////////////////////////////////////////////// +Bool UserProcessScheduler::operator!() { + SInt64 cnt = 0UL; + + for (auto process_index = 0UL; process_index < mTeam.AsArray().Count(); ++process_index) { + auto& process = mTeam.AsArray()[process_index]; + if (UserProcessHelper::CanBeScheduled(process)) ++cnt; + } + + return cnt == 0L; +} +} // namespace Kernel diff --git a/src/kernel/src/UserProcessTeam.cc b/src/kernel/src/UserProcessTeam.cc new file mode 100644 index 00000000..e5b7a7eb --- /dev/null +++ b/src/kernel/src/UserProcessTeam.cc @@ -0,0 +1,57 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/***********************************************************************************/ +/// @file UserProcessTeam.cc +/// @brief Process teams implementation. +/// @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +#include + +namespace Kernel { +UserProcessTeam::UserProcessTeam() { + for (SizeT i = 0U; i < this->mProcessList.Count(); ++i) { + this->mProcessList[i] = USER_PROCESS(); + this->mProcessList[i].PTime = 0; + this->mProcessList[i].RTime = 0; + this->mProcessList[i].UTime = 0; + this->mProcessList[i].Status = ProcessStatusKind::kKilled; + this->mProcessList[i].ParentTeam = this; + } + + this->mProcessCur = 0UL; +} + +/***********************************************************************************/ +/// @brief Process list array getter. +/// @return The list of process to schedule. +/***********************************************************************************/ + +Array& UserProcessTeam::AsArray() { + return this->mProcessList; +} + +/***********************************************************************************/ +/// @brief Get team ID. +/// @return The team's ID. +/***********************************************************************************/ + +ProcessID& UserProcessTeam::Id() noexcept { + return this->mTeamId; +} + +/***********************************************************************************/ +/// @brief Get current process getter as Ref. +/// @return The current process header. +/***********************************************************************************/ + +Ref& UserProcessTeam::AsRef() { + return this->mCurrentProcess; +} +} // namespace Kernel + +// last rev 05-03-24 diff --git a/src/kernel/src/UtfUtils.cc b/src/kernel/src/UtfUtils.cc new file mode 100644 index 00000000..47ac2729 --- /dev/null +++ b/src/kernel/src/UtfUtils.cc @@ -0,0 +1,62 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +/// @author Amlal El Mahrouss (amlal@nekernel.org) + +namespace Kernel { +Size urt_string_len(const Utf8Char* str) { + if (!str) return 0; + + SizeT len{0}; + + while (str[len] != u8'\0') ++len; + + return len; +} + +Void urt_set_memory(const voidPtr src, UInt32 dst, Size len) { + if (!src) return; + + Utf8Char* srcChr = reinterpret_cast(src); + Size index = 0; + + while (index < len) { + srcChr[index] = dst; + ++index; + } +} + +Int32 urt_string_cmp(const Utf8Char* src, const Utf8Char* cmp, Size size) { + if (!src) return 0; + + Int32 counter = 0; + + for (Size index = 0; index < size; ++index) { + if (src[index] != cmp[index]) ++counter; + } + + return counter; +} + +Int32 urt_copy_memory(const VoidPtr src, VoidPtr dst, Size len) { + if (!src) return 0; + if (!dst) return 0; + + Utf8Char* srcChr = reinterpret_cast(src); + Utf8Char* dstChar = reinterpret_cast(dst); + + Size index = 0; + + while (index < len) { + dstChar[index] = srcChr[index]; + ++index; + } + + return index; +} +} // namespace Kernel diff --git a/src/kernel/src/Variant.cc b/src/kernel/src/Variant.cc new file mode 100644 index 00000000..fcf2f443 --- /dev/null +++ b/src/kernel/src/Variant.cc @@ -0,0 +1,38 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include + +namespace Kernel { +const Char* Variant::ToString() { + switch (fKind) { + case VariantKind::kTOML: + return ("Class:{TOML}"); + case VariantKind::kJson: + return ("Class:{Json}"); + case VariantKind::kString: + return ("Class:{String}"); + case VariantKind::kBlob: + return ("Class:{Blob}"); + case VariantKind::kNull: + return ("Class:{Null}"); + case VariantKind::kSwap: + return ("Class:{Swap}"); + default: + return ("Class:{Unknown}"); + } +} + +/// @brief Return variant's kind. +Variant::VariantKind& Variant::Kind() { + return this->fKind; +} + +/// @brief Leak variant's instance. +VoidPtr Variant::Leak() { + return this->fPtr; +} +} // namespace Kernel diff --git a/src/kernel/src/ZXD.cc b/src/kernel/src/ZXD.cc new file mode 100644 index 00000000..1baa18f3 --- /dev/null +++ b/src/kernel/src/ZXD.cc @@ -0,0 +1,7 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include -- cgit v1.2.3