From f0acad6f3206079d804b2f59aace0dc32dbeb6dc Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Thu, 19 Feb 2026 08:14:48 +0100 Subject: kernel: lots of tweaks and improvements, WIP: ASN, FileMgr support for OpenHeFS. Signed-off-by: Amlal El Mahrouss --- src/kernel/HALKit/AMD64/CxxAbi.cc | 77 - src/kernel/HALKit/AMD64/CxxAbi.cpp | 77 + src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc | 111 -- .../HALKit/AMD64/HalACPIFactoryInterface.cpp | 111 ++ src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc | 37 - src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cpp | 37 + src/kernel/HALKit/AMD64/HalApplicationProcessor.cc | 218 --- .../HALKit/AMD64/HalApplicationProcessor.cpp | 218 +++ src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc | 177 --- .../HALKit/AMD64/HalCoreInterruptHandler.cpp | 177 +++ src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cc | 7 - src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cpp | 7 + .../HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cc | 7 - .../HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cpp | 7 + src/kernel/HALKit/AMD64/HalDebugOutput.cc | 225 --- src/kernel/HALKit/AMD64/HalDebugOutput.cpp | 225 +++ src/kernel/HALKit/AMD64/HalDebugProtocol.cc | 11 - src/kernel/HALKit/AMD64/HalDebugProtocol.cpp | 11 + src/kernel/HALKit/AMD64/HalDescriptorLoader.cc | 69 - src/kernel/HALKit/AMD64/HalDescriptorLoader.cpp | 69 + src/kernel/HALKit/AMD64/HalKernelMain.cc | 164 --- src/kernel/HALKit/AMD64/HalKernelMain.cpp | 164 +++ src/kernel/HALKit/AMD64/HalKernelPanic.cc | 56 - src/kernel/HALKit/AMD64/HalKernelPanic.cpp | 56 + src/kernel/HALKit/AMD64/HalPagingMgr.cc | 162 -- src/kernel/HALKit/AMD64/HalPagingMgr.cpp | 162 ++ src/kernel/HALKit/AMD64/HalProcessor.cc | 84 -- src/kernel/HALKit/AMD64/HalProcessor.cpp | 84 ++ .../HALKit/AMD64/HalSchedulerCorePrimitives.cc | 49 - .../HALKit/AMD64/HalSchedulerCorePrimitives.cpp | 49 + src/kernel/HALKit/AMD64/HalTimer.cc | 92 -- src/kernel/HALKit/AMD64/HalTimer.cpp | 92 ++ .../HALKit/AMD64/Network/Generic+Basic+RTL8139.cc | 127 -- .../HALKit/AMD64/Network/Generic+Basic+RTL8139.cpp | 127 ++ src/kernel/HALKit/AMD64/PCI/DMA.cc | 70 - src/kernel/HALKit/AMD64/PCI/DMA.cpp | 70 + src/kernel/HALKit/AMD64/PCI/Database.cc | 7 - src/kernel/HALKit/AMD64/PCI/Database.cpp | 7 + src/kernel/HALKit/AMD64/PCI/Device.cc | 140 -- src/kernel/HALKit/AMD64/PCI/Device.cpp | 140 ++ src/kernel/HALKit/AMD64/PCI/Express.cc | 7 - src/kernel/HALKit/AMD64/PCI/Express.cpp | 7 + src/kernel/HALKit/AMD64/PCI/IO.cc | 5 - src/kernel/HALKit/AMD64/PCI/IO.cpp | 5 + src/kernel/HALKit/AMD64/PCI/Iterator.cc | 28 - src/kernel/HALKit/AMD64/PCI/Iterator.cpp | 28 + src/kernel/HALKit/AMD64/PCI/PCI.cc | 5 - src/kernel/HALKit/AMD64/PCI/PCI.cpp | 5 + src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc | 588 -------- src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cpp | 588 ++++++++ src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc | 184 --- src/kernel/HALKit/AMD64/Storage/DMA+Generic.cpp | 184 +++ src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc | 7 - src/kernel/HALKit/AMD64/Storage/NVME+Generic.cpp | 7 + src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc | 262 ---- src/kernel/HALKit/AMD64/Storage/PIO+Generic.cpp | 262 ++++ src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc | 11 - src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cpp | 11 + src/kernel/HALKit/ARM64/APM/APM+IO.cc | 33 - src/kernel/HALKit/ARM64/APM/APM+IO.cpp | 33 + src/kernel/HALKit/ARM64/CxxAbi.cc | 85 -- src/kernel/HALKit/ARM64/CxxAbi.cpp | 85 ++ src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc | 24 - .../HALKit/ARM64/HalACPIFactoryInterface.cpp | 24 + src/kernel/HALKit/ARM64/HalApplicationProcessor.cc | 138 -- .../HALKit/ARM64/HalApplicationProcessor.cpp | 138 ++ src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc | 156 -- .../HALKit/ARM64/HalCoreInterruptHandler.cpp | 156 ++ src/kernel/HALKit/ARM64/HalDebugOutput.cc | 69 - src/kernel/HALKit/ARM64/HalDebugOutput.cpp | 69 + src/kernel/HALKit/ARM64/HalKernelMain.cc | 60 - src/kernel/HALKit/ARM64/HalKernelMain.cpp | 60 + src/kernel/HALKit/ARM64/HalKernelPanic.cc | 52 - src/kernel/HALKit/ARM64/HalKernelPanic.cpp | 52 + src/kernel/HALKit/ARM64/HalPagingMgr.cc | 81 - src/kernel/HALKit/ARM64/HalPagingMgr.cpp | 81 + src/kernel/HALKit/ARM64/HalSchedulerCore.cc | 19 - src/kernel/HALKit/ARM64/HalSchedulerCore.cpp | 19 + .../HALKit/ARM64/HalSchedulerCorePrimitives.cc | 28 - .../HALKit/ARM64/HalSchedulerCorePrimitives.cpp | 28 + src/kernel/HALKit/ARM64/HalTimer.cc | 6 - src/kernel/HALKit/ARM64/HalTimer.cpp | 6 + src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc | 11 - src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cpp | 11 + src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc | 3 - src/kernel/HALKit/ARM64/Storage/UFS+Generic.cpp | 3 + src/kernel/HALKit/POWER/HalApplicationProcessor.cc | 40 - .../HALKit/POWER/HalApplicationProcessor.cpp | 40 + src/kernel/HALKit/POWER/HalDebugOutput.cc | 22 - src/kernel/HALKit/POWER/HalDebugOutput.cpp | 22 + src/kernel/HALKit/POWER/HalHardwareThread.cc | 6 - src/kernel/HALKit/POWER/HalHardwareThread.cpp | 6 + src/kernel/HALKit/POWER/HalVirtualMemory.cc | 44 - src/kernel/HALKit/POWER/HalVirtualMemory.cpp | 44 + src/kernel/HALKit/RISCV/HalApplicationProcessor.cc | 37 - .../HALKit/RISCV/HalApplicationProcessor.cpp | 37 + src/kernel/amd64-ci.make | 2 +- src/kernel/amd64-desktop.make | 2 +- src/kernel/arm64-desktop.make | 10 +- src/kernel/src/ACPIFactoryInterface.cc | 90 -- src/kernel/src/ACPIFactoryInterface.cpp | 90 ++ src/kernel/src/Array.cc | 5 - src/kernel/src/Array.cpp | 5 + src/kernel/src/AsciiUtils.cc | 158 -- src/kernel/src/AsciiUtils.cpp | 158 ++ src/kernel/src/Atom.cc | 8 - src/kernel/src/Atom.cpp | 8 + src/kernel/src/BinaryMutex.cc | 65 - src/kernel/src/BinaryMutex.cpp | 65 + src/kernel/src/BitMapMgr.cc | 187 --- src/kernel/src/BitMapMgr.cpp | 187 +++ src/kernel/src/CRuntimeOverrides.cc | 25 - src/kernel/src/CRuntimeOverrides.cpp | 25 + src/kernel/src/CodeMgr.cc | 35 - src/kernel/src/CodeMgr.cpp | 35 + src/kernel/src/Crc32.cc | 63 - src/kernel/src/Crc32.cpp | 63 + src/kernel/src/Defines.cc | 5 - src/kernel/src/Defines.cpp | 5 + src/kernel/src/DeviceMgr.cc | 7 - src/kernel/src/DeviceMgr.cpp | 7 + src/kernel/src/DriveMgr.cc | 255 ---- src/kernel/src/DriveMgr.cpp | 255 ++++ src/kernel/src/ErrorOr.cc | 10 - src/kernel/src/ErrorOr.cpp | 10 + src/kernel/src/FS/Ext2+IFS.cc | 1558 -------------------- src/kernel/src/FS/Ext2+IFS.cpp | 1558 ++++++++++++++++++++ src/kernel/src/FS/NeFS+FileMgr.cc | 274 ---- src/kernel/src/FS/NeFS+FileMgr.cpp | 274 ++++ src/kernel/src/FS/NeFS+FileSystemParser.cc | 868 ----------- src/kernel/src/FS/NeFS+FileSystemParser.cpp | 868 +++++++++++ src/kernel/src/FS/OpenHeFS+FileMgr.cc | 203 --- src/kernel/src/FS/OpenHeFS+FileMgr.cpp | 250 ++++ src/kernel/src/FS/OpenHeFS+FileSystemParser.cc | 1160 --------------- src/kernel/src/FS/OpenHeFS+FileSystemParser.cpp | 1160 +++++++++++++++ src/kernel/src/FileMgr.cc | 47 - src/kernel/src/FileMgr.cpp | 47 + src/kernel/src/GUIDWizard.cc | 60 - src/kernel/src/GUIDWizard.cpp | 60 + src/kernel/src/GUIDWrapper.cc | 7 - src/kernel/src/GUIDWrapper.cpp | 7 + src/kernel/src/Gfx/FBDeviceInterface.cc | 48 - src/kernel/src/Gfx/FBDeviceInterface.cpp | 48 + src/kernel/src/HardwareThreadScheduler.cc | 182 --- src/kernel/src/HardwareThreadScheduler.cpp | 182 +++ src/kernel/src/HeapMgr.cc | 260 ---- src/kernel/src/HeapMgr.cpp | 260 ++++ src/kernel/src/IDylibObject.cc | 6 - src/kernel/src/IDylibObject.cpp | 6 + src/kernel/src/IFS.cc | 87 -- src/kernel/src/IFS.cpp | 87 ++ src/kernel/src/IPEFDylibObject.cc | 104 -- src/kernel/src/IPEFDylibObject.cpp | 104 ++ src/kernel/src/IndexableProperty.cc | 51 - src/kernel/src/IndexableProperty.cpp | 51 + src/kernel/src/InitializerList.cc | 5 - src/kernel/src/InitializerList.cpp | 5 + src/kernel/src/Json.cc | 7 - src/kernel/src/Json.cpp | 7 + src/kernel/src/KPC.cc | 37 - src/kernel/src/KPC.cpp | 37 + src/kernel/src/KernelTaskScheduler.cc | 32 - src/kernel/src/KernelTaskScheduler.cpp | 32 + src/kernel/src/LockDelegate.cc | 9 - src/kernel/src/LockDelegate.cpp | 9 + src/kernel/src/MutableArray.cc | 5 - src/kernel/src/MutableArray.cpp | 5 + src/kernel/src/Network/IPAddress.cc | 114 -- src/kernel/src/Network/IPAddress.cpp | 114 ++ src/kernel/src/Network/IPCAddress.cc | 25 - src/kernel/src/Network/IPCAddress.cpp | 25 + src/kernel/src/Network/IPCMessage.cc | 126 -- src/kernel/src/Network/IPCMessage.cpp | 126 ++ src/kernel/src/Network/MACAddressGetter.cc | 11 - src/kernel/src/Network/MACAddressGetter.cpp | 11 + src/kernel/src/Network/NetworkDevice.cc | 27 - src/kernel/src/Network/NetworkDevice.cpp | 27 + src/kernel/src/New+Delete.cc | 46 - src/kernel/src/New+Delete.cpp | 46 + src/kernel/src/OwnPtr.cc | 5 - src/kernel/src/OwnPtr.cpp | 5 + src/kernel/src/PE32CodeMgr.cc | 287 ---- src/kernel/src/PE32CodeMgr.cpp | 287 ++++ src/kernel/src/PEFCodeMgr.cc | 348 ----- src/kernel/src/PEFCodeMgr.cpp | 348 +++++ src/kernel/src/PRDT.cc | 20 - src/kernel/src/PRDT.cpp | 20 + src/kernel/src/PageMgr.cc | 93 -- src/kernel/src/PageMgr.cpp | 93 ++ src/kernel/src/Pmm.cc | 74 - src/kernel/src/Pmm.cpp | 74 + src/kernel/src/Property.cc | 39 - src/kernel/src/Property.cpp | 39 + src/kernel/src/Ref.cc | 5 - src/kernel/src/Ref.cpp | 5 + src/kernel/src/Semaphore.cc | 5 - src/kernel/src/Semaphore.cpp | 5 + src/kernel/src/SoftwareTimer.cc | 34 - src/kernel/src/SoftwareTimer.cpp | 34 + src/kernel/src/Storage/AHCIDeviceInterface.cc | 87 -- src/kernel/src/Storage/AHCIDeviceInterface.cpp | 87 ++ src/kernel/src/Storage/ATADeviceInterface.cc | 96 -- src/kernel/src/Storage/ATADeviceInterface.cpp | 96 ++ src/kernel/src/Storage/NVMEDeviceInterface.cc | 29 - src/kernel/src/Storage/NVMEDeviceInterface.cpp | 29 + src/kernel/src/Storage/SCSIDeviceInterface.cc | 20 - src/kernel/src/Storage/SCSIDeviceInterface.cpp | 20 + src/kernel/src/Stream.cc | 5 - src/kernel/src/Stream.cpp | 5 + src/kernel/src/Swap/DiskSwap.cc | 47 - src/kernel/src/Swap/DiskSwap.cpp | 47 + src/kernel/src/ThreadLocalStorage.cc | 50 - src/kernel/src/ThreadLocalStorage.cpp | 50 + src/kernel/src/Timer.cc | 17 - src/kernel/src/Timer.cpp | 17 + src/kernel/src/UserMgr+User.cc | 122 -- src/kernel/src/UserMgr+User.cpp | 122 ++ src/kernel/src/UserProcessScheduler.cc | 671 --------- src/kernel/src/UserProcessScheduler.cpp | 671 +++++++++ src/kernel/src/UserProcessTeam.cc | 52 - src/kernel/src/UserProcessTeam.cpp | 52 + src/kernel/src/UtfUtils.cc | 60 - src/kernel/src/UtfUtils.cpp | 60 + src/kernel/src/Variant.cc | 36 - src/kernel/src/Variant.cpp | 36 + src/kernel/src/ZXDCodeMgr.cc | 6 - src/kernel/src/ZXDCodeMgr.cpp | 6 + 227 files changed, 12435 insertions(+), 12388 deletions(-) delete mode 100644 src/kernel/HALKit/AMD64/CxxAbi.cc create mode 100644 src/kernel/HALKit/AMD64/CxxAbi.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc create mode 100644 src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc create mode 100644 src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/AMD64/HalApplicationProcessor.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc create mode 100644 src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cc create mode 100644 src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cc create mode 100644 src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalDebugOutput.cc create mode 100644 src/kernel/HALKit/AMD64/HalDebugOutput.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalDebugProtocol.cc create mode 100644 src/kernel/HALKit/AMD64/HalDebugProtocol.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalDescriptorLoader.cc create mode 100644 src/kernel/HALKit/AMD64/HalDescriptorLoader.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalKernelMain.cc create mode 100644 src/kernel/HALKit/AMD64/HalKernelMain.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalKernelPanic.cc create mode 100644 src/kernel/HALKit/AMD64/HalKernelPanic.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalPagingMgr.cc create mode 100644 src/kernel/HALKit/AMD64/HalPagingMgr.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalProcessor.cc create mode 100644 src/kernel/HALKit/AMD64/HalProcessor.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc create mode 100644 src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cpp delete mode 100644 src/kernel/HALKit/AMD64/HalTimer.cc create mode 100644 src/kernel/HALKit/AMD64/HalTimer.cpp delete mode 100644 src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc create mode 100644 src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/DMA.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/DMA.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/Database.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Database.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/Device.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Device.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/Express.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Express.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/IO.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/IO.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/Iterator.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/Iterator.cpp delete mode 100644 src/kernel/HALKit/AMD64/PCI/PCI.cc create mode 100644 src/kernel/HALKit/AMD64/PCI/PCI.cpp delete mode 100644 src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cpp delete mode 100644 src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/DMA+Generic.cpp delete mode 100644 src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/NVME+Generic.cpp delete mode 100644 src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/PIO+Generic.cpp delete mode 100644 src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc create mode 100644 src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cpp delete mode 100644 src/kernel/HALKit/ARM64/APM/APM+IO.cc create mode 100644 src/kernel/HALKit/ARM64/APM/APM+IO.cpp delete mode 100644 src/kernel/HALKit/ARM64/CxxAbi.cc create mode 100644 src/kernel/HALKit/ARM64/CxxAbi.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc create mode 100644 src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/ARM64/HalApplicationProcessor.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc create mode 100644 src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalDebugOutput.cc create mode 100644 src/kernel/HALKit/ARM64/HalDebugOutput.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalKernelMain.cc create mode 100644 src/kernel/HALKit/ARM64/HalKernelMain.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalKernelPanic.cc create mode 100644 src/kernel/HALKit/ARM64/HalKernelPanic.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalPagingMgr.cc create mode 100644 src/kernel/HALKit/ARM64/HalPagingMgr.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalSchedulerCore.cc create mode 100644 src/kernel/HALKit/ARM64/HalSchedulerCore.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cc create mode 100644 src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cpp delete mode 100644 src/kernel/HALKit/ARM64/HalTimer.cc create mode 100644 src/kernel/HALKit/ARM64/HalTimer.cpp delete mode 100644 src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc create mode 100644 src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cpp delete mode 100644 src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc create mode 100644 src/kernel/HALKit/ARM64/Storage/UFS+Generic.cpp delete mode 100644 src/kernel/HALKit/POWER/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/POWER/HalApplicationProcessor.cpp delete mode 100644 src/kernel/HALKit/POWER/HalDebugOutput.cc create mode 100644 src/kernel/HALKit/POWER/HalDebugOutput.cpp delete mode 100644 src/kernel/HALKit/POWER/HalHardwareThread.cc create mode 100644 src/kernel/HALKit/POWER/HalHardwareThread.cpp delete mode 100644 src/kernel/HALKit/POWER/HalVirtualMemory.cc create mode 100644 src/kernel/HALKit/POWER/HalVirtualMemory.cpp delete mode 100644 src/kernel/HALKit/RISCV/HalApplicationProcessor.cc create mode 100644 src/kernel/HALKit/RISCV/HalApplicationProcessor.cpp delete mode 100644 src/kernel/src/ACPIFactoryInterface.cc create mode 100644 src/kernel/src/ACPIFactoryInterface.cpp delete mode 100644 src/kernel/src/Array.cc create mode 100644 src/kernel/src/Array.cpp delete mode 100644 src/kernel/src/AsciiUtils.cc create mode 100644 src/kernel/src/AsciiUtils.cpp delete mode 100644 src/kernel/src/Atom.cc create mode 100644 src/kernel/src/Atom.cpp delete mode 100644 src/kernel/src/BinaryMutex.cc create mode 100644 src/kernel/src/BinaryMutex.cpp delete mode 100644 src/kernel/src/BitMapMgr.cc create mode 100644 src/kernel/src/BitMapMgr.cpp delete mode 100644 src/kernel/src/CRuntimeOverrides.cc create mode 100644 src/kernel/src/CRuntimeOverrides.cpp delete mode 100644 src/kernel/src/CodeMgr.cc create mode 100644 src/kernel/src/CodeMgr.cpp delete mode 100644 src/kernel/src/Crc32.cc create mode 100644 src/kernel/src/Crc32.cpp delete mode 100644 src/kernel/src/Defines.cc create mode 100644 src/kernel/src/Defines.cpp delete mode 100644 src/kernel/src/DeviceMgr.cc create mode 100644 src/kernel/src/DeviceMgr.cpp delete mode 100644 src/kernel/src/DriveMgr.cc create mode 100644 src/kernel/src/DriveMgr.cpp delete mode 100644 src/kernel/src/ErrorOr.cc create mode 100644 src/kernel/src/ErrorOr.cpp delete mode 100644 src/kernel/src/FS/Ext2+IFS.cc create mode 100644 src/kernel/src/FS/Ext2+IFS.cpp delete mode 100644 src/kernel/src/FS/NeFS+FileMgr.cc create mode 100644 src/kernel/src/FS/NeFS+FileMgr.cpp delete mode 100644 src/kernel/src/FS/NeFS+FileSystemParser.cc create mode 100644 src/kernel/src/FS/NeFS+FileSystemParser.cpp delete mode 100644 src/kernel/src/FS/OpenHeFS+FileMgr.cc create mode 100644 src/kernel/src/FS/OpenHeFS+FileMgr.cpp delete mode 100644 src/kernel/src/FS/OpenHeFS+FileSystemParser.cc create mode 100644 src/kernel/src/FS/OpenHeFS+FileSystemParser.cpp delete mode 100644 src/kernel/src/FileMgr.cc create mode 100644 src/kernel/src/FileMgr.cpp delete mode 100644 src/kernel/src/GUIDWizard.cc create mode 100644 src/kernel/src/GUIDWizard.cpp delete mode 100644 src/kernel/src/GUIDWrapper.cc create mode 100644 src/kernel/src/GUIDWrapper.cpp delete mode 100644 src/kernel/src/Gfx/FBDeviceInterface.cc create mode 100644 src/kernel/src/Gfx/FBDeviceInterface.cpp delete mode 100644 src/kernel/src/HardwareThreadScheduler.cc create mode 100644 src/kernel/src/HardwareThreadScheduler.cpp delete mode 100644 src/kernel/src/HeapMgr.cc create mode 100644 src/kernel/src/HeapMgr.cpp delete mode 100644 src/kernel/src/IDylibObject.cc create mode 100644 src/kernel/src/IDylibObject.cpp delete mode 100644 src/kernel/src/IFS.cc create mode 100644 src/kernel/src/IFS.cpp delete mode 100644 src/kernel/src/IPEFDylibObject.cc create mode 100644 src/kernel/src/IPEFDylibObject.cpp delete mode 100644 src/kernel/src/IndexableProperty.cc create mode 100644 src/kernel/src/IndexableProperty.cpp delete mode 100644 src/kernel/src/InitializerList.cc create mode 100644 src/kernel/src/InitializerList.cpp delete mode 100644 src/kernel/src/Json.cc create mode 100644 src/kernel/src/Json.cpp delete mode 100644 src/kernel/src/KPC.cc create mode 100644 src/kernel/src/KPC.cpp delete mode 100644 src/kernel/src/KernelTaskScheduler.cc create mode 100644 src/kernel/src/KernelTaskScheduler.cpp delete mode 100644 src/kernel/src/LockDelegate.cc create mode 100644 src/kernel/src/LockDelegate.cpp delete mode 100644 src/kernel/src/MutableArray.cc create mode 100644 src/kernel/src/MutableArray.cpp delete mode 100644 src/kernel/src/Network/IPAddress.cc create mode 100644 src/kernel/src/Network/IPAddress.cpp delete mode 100644 src/kernel/src/Network/IPCAddress.cc create mode 100644 src/kernel/src/Network/IPCAddress.cpp delete mode 100644 src/kernel/src/Network/IPCMessage.cc create mode 100644 src/kernel/src/Network/IPCMessage.cpp delete mode 100644 src/kernel/src/Network/MACAddressGetter.cc create mode 100644 src/kernel/src/Network/MACAddressGetter.cpp delete mode 100644 src/kernel/src/Network/NetworkDevice.cc create mode 100644 src/kernel/src/Network/NetworkDevice.cpp delete mode 100644 src/kernel/src/New+Delete.cc create mode 100644 src/kernel/src/New+Delete.cpp delete mode 100644 src/kernel/src/OwnPtr.cc create mode 100644 src/kernel/src/OwnPtr.cpp delete mode 100644 src/kernel/src/PE32CodeMgr.cc create mode 100644 src/kernel/src/PE32CodeMgr.cpp delete mode 100644 src/kernel/src/PEFCodeMgr.cc create mode 100644 src/kernel/src/PEFCodeMgr.cpp delete mode 100644 src/kernel/src/PRDT.cc create mode 100644 src/kernel/src/PRDT.cpp delete mode 100644 src/kernel/src/PageMgr.cc create mode 100644 src/kernel/src/PageMgr.cpp delete mode 100644 src/kernel/src/Pmm.cc create mode 100644 src/kernel/src/Pmm.cpp delete mode 100644 src/kernel/src/Property.cc create mode 100644 src/kernel/src/Property.cpp delete mode 100644 src/kernel/src/Ref.cc create mode 100644 src/kernel/src/Ref.cpp delete mode 100644 src/kernel/src/Semaphore.cc create mode 100644 src/kernel/src/Semaphore.cpp delete mode 100644 src/kernel/src/SoftwareTimer.cc create mode 100644 src/kernel/src/SoftwareTimer.cpp delete mode 100644 src/kernel/src/Storage/AHCIDeviceInterface.cc create mode 100644 src/kernel/src/Storage/AHCIDeviceInterface.cpp delete mode 100644 src/kernel/src/Storage/ATADeviceInterface.cc create mode 100644 src/kernel/src/Storage/ATADeviceInterface.cpp delete mode 100644 src/kernel/src/Storage/NVMEDeviceInterface.cc create mode 100644 src/kernel/src/Storage/NVMEDeviceInterface.cpp delete mode 100644 src/kernel/src/Storage/SCSIDeviceInterface.cc create mode 100644 src/kernel/src/Storage/SCSIDeviceInterface.cpp delete mode 100644 src/kernel/src/Stream.cc create mode 100644 src/kernel/src/Stream.cpp delete mode 100644 src/kernel/src/Swap/DiskSwap.cc create mode 100644 src/kernel/src/Swap/DiskSwap.cpp delete mode 100644 src/kernel/src/ThreadLocalStorage.cc create mode 100644 src/kernel/src/ThreadLocalStorage.cpp delete mode 100644 src/kernel/src/Timer.cc create mode 100644 src/kernel/src/Timer.cpp delete mode 100644 src/kernel/src/UserMgr+User.cc create mode 100644 src/kernel/src/UserMgr+User.cpp delete mode 100644 src/kernel/src/UserProcessScheduler.cc create mode 100644 src/kernel/src/UserProcessScheduler.cpp delete mode 100644 src/kernel/src/UserProcessTeam.cc create mode 100644 src/kernel/src/UserProcessTeam.cpp delete mode 100644 src/kernel/src/UtfUtils.cc create mode 100644 src/kernel/src/UtfUtils.cpp delete mode 100644 src/kernel/src/Variant.cc create mode 100644 src/kernel/src/Variant.cpp delete mode 100644 src/kernel/src/ZXDCodeMgr.cc create mode 100644 src/kernel/src/ZXDCodeMgr.cpp (limited to 'src/kernel') diff --git a/src/kernel/HALKit/AMD64/CxxAbi.cc b/src/kernel/HALKit/AMD64/CxxAbi.cc deleted file mode 100644 index f0f18266..00000000 --- a/src/kernel/HALKit/AMD64/CxxAbi.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/CxxAbi.cpp b/src/kernel/HALKit/AMD64/CxxAbi.cpp new file mode 100644 index 00000000..f0f18266 --- /dev/null +++ b/src/kernel/HALKit/AMD64/CxxAbi.cpp @@ -0,0 +1,77 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 7368ea10..00000000 --- a/src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalACPIFactoryInterface.cpp b/src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cpp new file mode 100644 index 00000000..7368ea10 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalACPIFactoryInterface.cpp @@ -0,0 +1,111 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index b262f2b3..00000000 --- a/src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - 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) { - 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/HalAPICDmaWrapper.cpp b/src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cpp new file mode 100644 index 00000000..b262f2b3 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalAPICDmaWrapper.cpp @@ -0,0 +1,37 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + 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) { + 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 deleted file mode 100644 index 0450c71c..00000000 --- a/src/kernel/HALKit/AMD64/HalApplicationProcessor.cc +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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(stack_frame); - return YES; - } - - if (thrdid > kSMPCount) return NO; - - kHWThread[thrdid].mThreadID = thrdid; - kHWThread[thrdid].mFramePtr = stack_frame; - - hal_send_ipi_msg(thrdid, kAPICLocales[thrdid], kSMPInterrupt + 32); - - return YES; -} - -/***********************************************************************************/ -/// @brief Is the current config SMP aware? -/// @return True if YES, False if not. -/***********************************************************************************/ - -Bool mp_is_smp(Void) { - return kSMPAware; -} - -/***********************************************************************************/ -/// @brief Fetch and enable SMP scheduler. -/// @param vendor_ptr SMP containing structure. -/***********************************************************************************/ - -Void mp_init_cores(VoidPtr vendor_ptr) { - 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; - } - } -} -} // namespace Kernel::HAL - -/////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/kernel/HALKit/AMD64/HalApplicationProcessor.cpp b/src/kernel/HALKit/AMD64/HalApplicationProcessor.cpp new file mode 100644 index 00000000..0450c71c --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalApplicationProcessor.cpp @@ -0,0 +1,218 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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(stack_frame); + return YES; + } + + if (thrdid > kSMPCount) return NO; + + kHWThread[thrdid].mThreadID = thrdid; + kHWThread[thrdid].mFramePtr = stack_frame; + + hal_send_ipi_msg(thrdid, kAPICLocales[thrdid], kSMPInterrupt + 32); + + return YES; +} + +/***********************************************************************************/ +/// @brief Is the current config SMP aware? +/// @return True if YES, False if not. +/***********************************************************************************/ + +Bool mp_is_smp(Void) { + return kSMPAware; +} + +/***********************************************************************************/ +/// @brief Fetch and enable SMP scheduler. +/// @param vendor_ptr SMP containing structure. +/***********************************************************************************/ + +Void mp_init_cores(VoidPtr vendor_ptr) { + 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; + } + } +} +} // namespace Kernel::HAL + +/////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc b/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc deleted file mode 100644 index a171ac57..00000000 --- a/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#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(); - - if (process) process.Crash(); - - hal_idt_send_eoi(13); - - if (process) { - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.Status; - } -} - -/// @brief Handle page fault. -/// @param rsp -EXTERN_C void idt_handle_pf(Kernel::UIntPtr rsp) { - auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); - - if (process) process.Crash(); - - hal_idt_send_eoi(14); - - if (process) { - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.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(); - - if (process) process.Crash(); - - hal_idt_send_eoi(8); - - if (process) { - process.Signal.SignalArg = rsp; - process.Signal.SignalID = sig_generate_unique(); - process.Signal.Status = process.Status; - } -} - -/// @brief Handle any generic fault. -/// @param rsp -EXTERN_C void idt_handle_generic(Kernel::UIntPtr rsp) { - auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); - - if (process) process.Crash(); - - hal_idt_send_eoi(30); - - Kernel::kout << "Kernel: Generic Process Fault.\r"; - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = sig_generate_unique(); - ; - process.Signal.Status = process.Status; - - Kernel::kout << "Kernel: SIGKILL status.\r"; -} - -EXTERN_C Kernel::Void idt_handle_breakpoint(Kernel::UIntPtr rip) { - auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); - - if (process) process.Crash(); - - hal_idt_send_eoi(3); - - process.Signal.SignalArg = rip; - process.Signal.SignalID = sig_generate_unique(); - - process.Signal.Status = process.Status; - - process.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(); - - if (process) process.Crash(); - - hal_idt_send_eoi(6); - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = sig_generate_unique(); - process.Signal.Status = process.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/HalCoreInterruptHandler.cpp b/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cpp new file mode 100644 index 00000000..a171ac57 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalCoreInterruptHandler.cpp @@ -0,0 +1,177 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#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(); + + if (process) process.Crash(); + + hal_idt_send_eoi(13); + + if (process) { + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.Status; + } +} + +/// @brief Handle page fault. +/// @param rsp +EXTERN_C void idt_handle_pf(Kernel::UIntPtr rsp) { + auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + + if (process) process.Crash(); + + hal_idt_send_eoi(14); + + if (process) { + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.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(); + + if (process) process.Crash(); + + hal_idt_send_eoi(8); + + if (process) { + process.Signal.SignalArg = rsp; + process.Signal.SignalID = sig_generate_unique(); + process.Signal.Status = process.Status; + } +} + +/// @brief Handle any generic fault. +/// @param rsp +EXTERN_C void idt_handle_generic(Kernel::UIntPtr rsp) { + auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + + if (process) process.Crash(); + + hal_idt_send_eoi(30); + + Kernel::kout << "Kernel: Generic Process Fault.\r"; + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = sig_generate_unique(); + ; + process.Signal.Status = process.Status; + + Kernel::kout << "Kernel: SIGKILL status.\r"; +} + +EXTERN_C Kernel::Void idt_handle_breakpoint(Kernel::UIntPtr rip) { + auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + + if (process) process.Crash(); + + hal_idt_send_eoi(3); + + process.Signal.SignalArg = rip; + process.Signal.SignalID = sig_generate_unique(); + + process.Signal.Status = process.Status; + + process.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(); + + if (process) process.Crash(); + + hal_idt_send_eoi(6); + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = sig_generate_unique(); + process.Signal.Status = process.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+DDK.cc b/src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cc deleted file mode 100644 index 7322ae93..00000000 --- a/src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2026, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel {} \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cpp b/src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cpp new file mode 100644 index 00000000..7322ae93 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalCoreSystemCalls+DDK.cpp @@ -0,0 +1,7 @@ +// Copyright 2026, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel {} \ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cc b/src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cc deleted file mode 100644 index 33d1243a..00000000 --- a/src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2026, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cpp b/src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cpp new file mode 100644 index 00000000..33d1243a --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalCoreSystemCalls+NeLaunch.cpp @@ -0,0 +1,7 @@ +// Copyright 2026, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/HalDebugOutput.cc b/src/kernel/HALKit/AMD64/HalDebugOutput.cc deleted file mode 100644 index dbddd092..00000000 --- a/src/kernel/HALKit/AMD64/HalDebugOutput.cc +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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() { - 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(IDevice* 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(IDevice* 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(IDevice*, 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() { - TerminalDevice out(Kernel::ke_io_write, Kernel::ke_io_read); - return out; -} - -Utf8TerminalDevice::~Utf8TerminalDevice() = default; - -Utf8TerminalDevice Utf8TerminalDevice::The() { - Utf8TerminalDevice out(Kernel::ke_utf_io_write, - [](IDevice*, const Utf8Char*) -> Void {}); - return out; -} - -} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalDebugOutput.cpp b/src/kernel/HALKit/AMD64/HalDebugOutput.cpp new file mode 100644 index 00000000..dbddd092 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalDebugOutput.cpp @@ -0,0 +1,225 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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() { + 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(IDevice* 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(IDevice* 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(IDevice*, 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() { + TerminalDevice out(Kernel::ke_io_write, Kernel::ke_io_read); + return out; +} + +Utf8TerminalDevice::~Utf8TerminalDevice() = default; + +Utf8TerminalDevice Utf8TerminalDevice::The() { + Utf8TerminalDevice out(Kernel::ke_utf_io_write, + [](IDevice*, const Utf8Char*) -> Void {}); + return out; +} + +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalDebugProtocol.cc b/src/kernel/HALKit/AMD64/HalDebugProtocol.cc deleted file mode 100644 index e7a2881b..00000000 --- a/src/kernel/HALKit/AMD64/HalDebugProtocol.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include - -// after that we have start of additional data. - -namespace Kernel {} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/HalDebugProtocol.cpp b/src/kernel/HALKit/AMD64/HalDebugProtocol.cpp new file mode 100644 index 00000000..e7a2881b --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalDebugProtocol.cpp @@ -0,0 +1,11 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index e803f7a8..00000000 --- a/src/kernel/HALKit/AMD64/HalDescriptorLoader.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalDescriptorLoader.cpp b/src/kernel/HALKit/AMD64/HalDescriptorLoader.cpp new file mode 100644 index 00000000..e803f7a8 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalDescriptorLoader.cpp @@ -0,0 +1,69 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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/HalKernelMain.cc b/src/kernel/HALKit/AMD64/HalKernelMain.cc deleted file mode 100644 index 7e6579de..00000000 --- a/src/kernel/HALKit/AMD64/HalKernelMain.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#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 Kernel::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) { - 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); - -#ifdef __FSKIT_INCLUDES_OPENHEFS__ - OpenHeFS::fs_init_openhefs(); - HeFileSystemMgr::Mount(new HeFileSystemMgr()); -#endif - -#ifdef __FSKIT_INCLUDES_NEFS__ - NeFS::fs_init_nefs(); - NeFileSystemMgr::Mount(new NeFileSystemMgr()); -#endif - - UserProcessScheduler::The().SwitchTeam(kRTUserTeam); - - PEFLoader ldr("/system/init.out"); - - if (ldr.IsLoaded()) rtl_create_user_process(ldr, UserProcess::ExecutableKind::kExecutableKind); - - UserProcessScheduler::The().SwitchTeam(kMidUserTeam); - - while (YES); -} -#endif // ifndef __NE_MODULAR_KERNEL_COMPONENTS__ diff --git a/src/kernel/HALKit/AMD64/HalKernelMain.cpp b/src/kernel/HALKit/AMD64/HalKernelMain.cpp new file mode 100644 index 00000000..7e6579de --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalKernelMain.cpp @@ -0,0 +1,164 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#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 Kernel::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) { + 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); + +#ifdef __FSKIT_INCLUDES_OPENHEFS__ + OpenHeFS::fs_init_openhefs(); + HeFileSystemMgr::Mount(new HeFileSystemMgr()); +#endif + +#ifdef __FSKIT_INCLUDES_NEFS__ + NeFS::fs_init_nefs(); + NeFileSystemMgr::Mount(new NeFileSystemMgr()); +#endif + + UserProcessScheduler::The().SwitchTeam(kRTUserTeam); + + PEFLoader ldr("/system/init.out"); + + if (ldr.IsLoaded()) rtl_create_user_process(ldr, UserProcess::ExecutableKind::kExecutableKind); + + UserProcessScheduler::The().SwitchTeam(kMidUserTeam); + + while (YES); +} +#endif // ifndef __NE_MODULAR_KERNEL_COMPONENTS__ diff --git a/src/kernel/HALKit/AMD64/HalKernelPanic.cc b/src/kernel/HALKit/AMD64/HalKernelPanic.cc deleted file mode 100644 index ed6a190b..00000000 --- a/src/kernel/HALKit/AMD64/HalKernelPanic.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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(); -}; - -/***********************************************************************************/ -/// @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() { - 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/HalKernelPanic.cpp b/src/kernel/HALKit/AMD64/HalKernelPanic.cpp new file mode 100644 index 00000000..ed6a190b --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalKernelPanic.cpp @@ -0,0 +1,56 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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(); +}; + +/***********************************************************************************/ +/// @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() { + 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 deleted file mode 100644 index 52561b5c..00000000 --- a/src/kernel/HALKit/AMD64/HalPagingMgr.cc +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalPagingMgr.cpp b/src/kernel/HALKit/AMD64/HalPagingMgr.cpp new file mode 100644 index 00000000..52561b5c --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalPagingMgr.cpp @@ -0,0 +1,162 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 50c2ca53..00000000 --- a/src/kernel/HALKit/AMD64/HalProcessor.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -/** - * @file HalCPU.cc - * @brief Common CPU API. - */ - -namespace Kernel::HAL { -inline Bool hal_has_msr() { - 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) { - if (!lo || !hi) return; - asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); -} - -Void hal_set_msr(UInt32 msr, UInt32 lo, UInt32 hi) { - 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/HalProcessor.cpp b/src/kernel/HALKit/AMD64/HalProcessor.cpp new file mode 100644 index 00000000..50c2ca53 --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalProcessor.cpp @@ -0,0 +1,84 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +/** + * @file HalCPU.cc + * @brief Common CPU API. + */ + +namespace Kernel::HAL { +inline Bool hal_has_msr() { + 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) { + if (!lo || !hi) return; + asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); +} + +Void hal_set_msr(UInt32 msr, UInt32 lo, UInt32 hi) { + 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/HalSchedulerCorePrimitives.cc b/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc deleted file mode 100644 index 3571d2af..00000000 --- a/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -namespace Kernel { -/***********************************************************************************/ -/// @brief Unimplemented function (crashes by default) -/// @param -/***********************************************************************************/ - -EXTERN_C Void __ne_pure_call(UserProcess* 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/HalSchedulerCorePrimitives.cpp b/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cpp new file mode 100644 index 00000000..3571d2af --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalSchedulerCorePrimitives.cpp @@ -0,0 +1,49 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief Unimplemented function (crashes by default) +/// @param +/***********************************************************************************/ + +EXTERN_C Void __ne_pure_call(UserProcess* 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 deleted file mode 100644 index 275c1ece..00000000 --- a/src/kernel/HALKit/AMD64/HalTimer.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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() { - 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/HalTimer.cpp b/src/kernel/HALKit/AMD64/HalTimer.cpp new file mode 100644 index 00000000..275c1ece --- /dev/null +++ b/src/kernel/HALKit/AMD64/HalTimer.cpp @@ -0,0 +1,92 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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() { + 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/Network/Generic+Basic+RTL8139.cc b/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc deleted file mode 100644 index 2f6cc6da..00000000 --- a/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - 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/Network/Generic+Basic+RTL8139.cpp b/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cpp new file mode 100644 index 00000000..2f6cc6da --- /dev/null +++ b/src/kernel/HALKit/AMD64/Network/Generic+Basic+RTL8139.cpp @@ -0,0 +1,127 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + 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 deleted file mode 100644 index 73e5e401..00000000 --- a/src/kernel/HALKit/AMD64/PCI/DMA.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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 = - make_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/DMA.cpp b/src/kernel/HALKit/AMD64/PCI/DMA.cpp new file mode 100644 index 00000000..73e5e401 --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/DMA.cpp @@ -0,0 +1,70 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 = + make_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 deleted file mode 100644 index 7eb098da..00000000 --- a/src/kernel/HALKit/AMD64/PCI/Database.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/PCI/Database.cpp b/src/kernel/HALKit/AMD64/PCI/Database.cpp new file mode 100644 index 00000000..7eb098da --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Database.cpp @@ -0,0 +1,7 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/PCI/Device.cc b/src/kernel/HALKit/AMD64/PCI/Device.cc deleted file mode 100644 index dcc16e6a..00000000 --- a/src/kernel/HALKit/AMD64/PCI/Device.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/Device.cpp b/src/kernel/HALKit/AMD64/PCI/Device.cpp new file mode 100644 index 00000000..dcc16e6a --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Device.cpp @@ -0,0 +1,140 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index e0264d9e..00000000 --- a/src/kernel/HALKit/AMD64/PCI/Express.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/PCI/Express.cpp b/src/kernel/HALKit/AMD64/PCI/Express.cpp new file mode 100644 index 00000000..e0264d9e --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Express.cpp @@ -0,0 +1,7 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel {} diff --git a/src/kernel/HALKit/AMD64/PCI/IO.cc b/src/kernel/HALKit/AMD64/PCI/IO.cc deleted file mode 100644 index 8cc9c83a..00000000 --- a/src/kernel/HALKit/AMD64/PCI/IO.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/HALKit/AMD64/PCI/IO.cpp b/src/kernel/HALKit/AMD64/PCI/IO.cpp new file mode 100644 index 00000000..8cc9c83a --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/IO.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/HALKit/AMD64/PCI/Iterator.cc b/src/kernel/HALKit/AMD64/PCI/Iterator.cc deleted file mode 100644 index 8216befa..00000000 --- a/src/kernel/HALKit/AMD64/PCI/Iterator.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/Iterator.cpp b/src/kernel/HALKit/AMD64/PCI/Iterator.cpp new file mode 100644 index 00000000..8216befa --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/Iterator.cpp @@ -0,0 +1,28 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index b8a1302d..00000000 --- a/src/kernel/HALKit/AMD64/PCI/PCI.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/HALKit/AMD64/PCI/PCI.cpp b/src/kernel/HALKit/AMD64/PCI/PCI.cpp new file mode 100644 index 00000000..b8a1302d --- /dev/null +++ b/src/kernel/HALKit/AMD64/PCI/PCI.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc deleted file mode 100644 index 456b6a2a..00000000 --- a/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc +++ /dev/null @@ -1,588 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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); - -STATIC Int32 drv_find_cmd_slot_ahci(HbaPort* port); - -STATIC Void drv_compute_disk_ahci(); - -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() { - 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) { - 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) { - 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: tf-error when doing 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) { - /// 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(IDevice* 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(IDevice* 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/AHCI+Generic.cpp b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cpp new file mode 100644 index 00000000..456b6a2a --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cpp @@ -0,0 +1,588 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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); + +STATIC Int32 drv_find_cmd_slot_ahci(HbaPort* port); + +STATIC Void drv_compute_disk_ahci(); + +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() { + 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) { + 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) { + 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: tf-error when doing 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) { + /// 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(IDevice* 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(IDevice* 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 deleted file mode 100644 index 8bf7b161..00000000 --- a/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/DMA+Generic.cpp b/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cpp new file mode 100644 index 00000000..8bf7b161 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cpp @@ -0,0 +1,184 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 7b0726e7..00000000 --- a/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -using namespace Kernel; diff --git a/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cpp b/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cpp new file mode 100644 index 00000000..7b0726e7 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cpp @@ -0,0 +1,7 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +using namespace Kernel; diff --git a/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc deleted file mode 100644 index 19497b5f..00000000 --- a/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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(IDevice* 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(IDevice* 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/PIO+Generic.cpp b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cpp new file mode 100644 index 00000000..19497b5f --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cpp @@ -0,0 +1,262 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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(IDevice* 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(IDevice* 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 deleted file mode 100644 index f73946d7..00000000 --- a/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/AMD64/Storage/SCSI+Generic.cpp b/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cpp new file mode 100644 index 00000000..f73946d7 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cpp @@ -0,0 +1,11 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 91b8255e..00000000 --- a/src/kernel/HALKit/ARM64/APM/APM+IO.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/APM/APM+IO.cpp b/src/kernel/HALKit/ARM64/APM/APM+IO.cpp new file mode 100644 index 00000000..91b8255e --- /dev/null +++ b/src/kernel/HALKit/ARM64/APM/APM+IO.cpp @@ -0,0 +1,33 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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/CxxAbi.cc b/src/kernel/HALKit/ARM64/CxxAbi.cc deleted file mode 100644 index 7b19aa71..00000000 --- a/src/kernel/HALKit/ARM64/CxxAbi.cc +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/CxxAbi.cpp b/src/kernel/HALKit/ARM64/CxxAbi.cpp new file mode 100644 index 00000000..7b19aa71 --- /dev/null +++ b/src/kernel/HALKit/ARM64/CxxAbi.cpp @@ -0,0 +1,85 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 544d1ee1..00000000 --- a/src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalACPIFactoryInterface.cpp b/src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cpp new file mode 100644 index 00000000..544d1ee1 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalACPIFactoryInterface.cpp @@ -0,0 +1,24 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 33921a56..00000000 --- a/src/kernel/HALKit/ARM64/HalApplicationProcessor.cc +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - 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/HalApplicationProcessor.cpp b/src/kernel/HALKit/ARM64/HalApplicationProcessor.cpp new file mode 100644 index 00000000..33921a56 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalApplicationProcessor.cpp @@ -0,0 +1,138 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + 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/HalCoreInterruptHandler.cc b/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc deleted file mode 100644 index f0085e13..00000000 --- a/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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.Crash(); - - hal_int_send_eoi(13); - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.Status; -} - -/// @brief Handle page fault. -/// @param rsp -EXTERN_C void int_handle_pf(Kernel::UIntPtr rsp) { - auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); - process.Crash(); - - hal_int_send_eoi(14); - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.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.Crash(); - - hal_int_send_eoi(8); - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.Status; -} - -/// @brief Handle any generic fault. -/// @param rsp -EXTERN_C void int_handle_generic(Kernel::UIntPtr rsp) { - auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); - process.Crash(); - - hal_int_send_eoi(30); - - Kernel::kout << "Kernel: Generic Process Fault.\r"; - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.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.Signal.SignalArg = rip; - process.Signal.SignalID = SIGTRAP; - - process.Signal.Status = process.Status; - - process.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.Crash(); - - hal_int_send_eoi(6); - - process.Signal.SignalArg = rsp; - process.Signal.SignalID = SIGKILL; - process.Signal.Status = process.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/HalCoreInterruptHandler.cpp b/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cpp new file mode 100644 index 00000000..f0085e13 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalCoreInterruptHandler.cpp @@ -0,0 +1,156 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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.Crash(); + + hal_int_send_eoi(13); + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.Status; +} + +/// @brief Handle page fault. +/// @param rsp +EXTERN_C void int_handle_pf(Kernel::UIntPtr rsp) { + auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Crash(); + + hal_int_send_eoi(14); + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.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.Crash(); + + hal_int_send_eoi(8); + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.Status; +} + +/// @brief Handle any generic fault. +/// @param rsp +EXTERN_C void int_handle_generic(Kernel::UIntPtr rsp) { + auto process = Kernel::UserProcessScheduler::The().TheCurrentProcess(); + process.Crash(); + + hal_int_send_eoi(30); + + Kernel::kout << "Kernel: Generic Process Fault.\r"; + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.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.Signal.SignalArg = rip; + process.Signal.SignalID = SIGTRAP; + + process.Signal.Status = process.Status; + + process.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.Crash(); + + hal_int_send_eoi(6); + + process.Signal.SignalArg = rsp; + process.Signal.SignalID = SIGKILL; + process.Signal.Status = process.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 deleted file mode 100644 index 4e47d38e..00000000 --- a/src/kernel/HALKit/ARM64/HalDebugOutput.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include -#include - -namespace Kernel { -EXTERN_C void ke_io_write(IDevice* 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(IDevice* 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() { - TerminalDevice out(Kernel::ke_io_write, Kernel::ke_io_read); - return out; -} - -} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalDebugOutput.cpp b/src/kernel/HALKit/ARM64/HalDebugOutput.cpp new file mode 100644 index 00000000..4e47d38e --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalDebugOutput.cpp @@ -0,0 +1,69 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include +#include + +namespace Kernel { +EXTERN_C void ke_io_write(IDevice* 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(IDevice* 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() { + TerminalDevice out(Kernel::ke_io_write, Kernel::ke_io_read); + return out; +} + +} // namespace Kernel diff --git a/src/kernel/HALKit/ARM64/HalKernelMain.cc b/src/kernel/HALKit/ARM64/HalKernelMain.cc deleted file mode 100644 index 3a6e4085..00000000 --- a/src/kernel/HALKit/ARM64/HalKernelMain.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalKernelMain.cpp b/src/kernel/HALKit/ARM64/HalKernelMain.cpp new file mode 100644 index 00000000..3a6e4085 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalKernelMain.cpp @@ -0,0 +1,60 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 59397611..00000000 --- a/src/kernel/HALKit/ARM64/HalKernelPanic.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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(); -}; - -/***********************************************************************************/ -/// @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() { - 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/HalKernelPanic.cpp b/src/kernel/HALKit/ARM64/HalKernelPanic.cpp new file mode 100644 index 00000000..59397611 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalKernelPanic.cpp @@ -0,0 +1,52 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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(); +}; + +/***********************************************************************************/ +/// @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() { + 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 deleted file mode 100644 index 0fe4d759..00000000 --- a/src/kernel/HALKit/ARM64/HalPagingMgr.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalPagingMgr.cpp b/src/kernel/HALKit/ARM64/HalPagingMgr.cpp new file mode 100644 index 00000000..0fe4d759 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalPagingMgr.cpp @@ -0,0 +1,81 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index ecf666e8..00000000 --- a/src/kernel/HALKit/ARM64/HalSchedulerCore.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalSchedulerCore.cpp b/src/kernel/HALKit/ARM64/HalSchedulerCore.cpp new file mode 100644 index 00000000..ecf666e8 --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalSchedulerCore.cpp @@ -0,0 +1,19 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 960dbc6d..00000000 --- a/src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -namespace Kernel { -/***********************************************************************************/ -/// @brief Unimplemented function (crashes by default) -/// @param process The process handle. -/***********************************************************************************/ - -EXTERN_C Void __ne_pure_call(UserProcess* 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/HalSchedulerCorePrimitives.cpp b/src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cpp new file mode 100644 index 00000000..960dbc6d --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalSchedulerCorePrimitives.cpp @@ -0,0 +1,28 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief Unimplemented function (crashes by default) +/// @param process The process handle. +/***********************************************************************************/ + +EXTERN_C Void __ne_pure_call(UserProcess* 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 deleted file mode 100644 index 3084838d..00000000 --- a/src/kernel/HALKit/ARM64/HalTimer.cc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include \ No newline at end of file diff --git a/src/kernel/HALKit/ARM64/HalTimer.cpp b/src/kernel/HALKit/ARM64/HalTimer.cpp new file mode 100644 index 00000000..3084838d --- /dev/null +++ b/src/kernel/HALKit/ARM64/HalTimer.cpp @@ -0,0 +1,6 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include \ 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 deleted file mode 100644 index f73946d7..00000000 --- a/src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/SCSI+Generic.cpp b/src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cpp new file mode 100644 index 00000000..f73946d7 --- /dev/null +++ b/src/kernel/HALKit/ARM64/Storage/SCSI+Generic.cpp @@ -0,0 +1,11 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index ba8d285b..00000000 --- a/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cc +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel diff --git a/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cpp b/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cpp new file mode 100644 index 00000000..ba8d285b --- /dev/null +++ b/src/kernel/HALKit/ARM64/Storage/UFS+Generic.cpp @@ -0,0 +1,3 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel diff --git a/src/kernel/HALKit/POWER/HalApplicationProcessor.cc b/src/kernel/HALKit/POWER/HalApplicationProcessor.cc deleted file mode 100644 index 64333086..00000000 --- a/src/kernel/HALKit/POWER/HalApplicationProcessor.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalApplicationProcessor.cpp b/src/kernel/HALKit/POWER/HalApplicationProcessor.cpp new file mode 100644 index 00000000..64333086 --- /dev/null +++ b/src/kernel/HALKit/POWER/HalApplicationProcessor.cpp @@ -0,0 +1,40 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index c43e575d..00000000 --- a/src/kernel/HALKit/POWER/HalDebugOutput.cc +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalDebugOutput.cpp b/src/kernel/HALKit/POWER/HalDebugOutput.cpp new file mode 100644 index 00000000..c43e575d --- /dev/null +++ b/src/kernel/HALKit/POWER/HalDebugOutput.cpp @@ -0,0 +1,22 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index fff5b68b..00000000 --- a/src/kernel/HALKit/POWER/HalHardwareThread.cc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include diff --git a/src/kernel/HALKit/POWER/HalHardwareThread.cpp b/src/kernel/HALKit/POWER/HalHardwareThread.cpp new file mode 100644 index 00000000..fff5b68b --- /dev/null +++ b/src/kernel/HALKit/POWER/HalHardwareThread.cpp @@ -0,0 +1,6 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include diff --git a/src/kernel/HALKit/POWER/HalVirtualMemory.cc b/src/kernel/HALKit/POWER/HalVirtualMemory.cc deleted file mode 100644 index 0c954f29..00000000 --- a/src/kernel/HALKit/POWER/HalVirtualMemory.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalVirtualMemory.cpp b/src/kernel/HALKit/POWER/HalVirtualMemory.cpp new file mode 100644 index 00000000..0c954f29 --- /dev/null +++ b/src/kernel/HALKit/POWER/HalVirtualMemory.cpp @@ -0,0 +1,44 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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/RISCV/HalApplicationProcessor.cc b/src/kernel/HALKit/RISCV/HalApplicationProcessor.cc deleted file mode 100644 index 68242f97..00000000 --- a/src/kernel/HALKit/RISCV/HalApplicationProcessor.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/HalApplicationProcessor.cpp b/src/kernel/HALKit/RISCV/HalApplicationProcessor.cpp new file mode 100644 index 00000000..68242f97 --- /dev/null +++ b/src/kernel/HALKit/RISCV/HalApplicationProcessor.cpp @@ -0,0 +1,37 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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/amd64-ci.make b/src/kernel/amd64-ci.make index 7db79b03..38e2cccf 100644 --- a/src/kernel/amd64-ci.make +++ b/src/kernel/amd64-ci.make @@ -48,7 +48,7 @@ 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) + $(CXX) $(CCFLAGS) $(DISK_DRV) $(DEBUG_MACRO) $(wildcard src/*.cpp) $(wildcard src/Gfx/*.cpp) $(wildcard HALKit/AMD64/PCI/*.cpp) $(wildcard src/Network/*.cpp) $(wildcard src/Storage/*.cpp) $(wildcard src/FS/*.cpp) $(wildcard HALKit/AMD64/Storage/*.cpp) $(wildcard HALKit/AMD64/*.cpp) $(wildcard src/Swap/*.cpp) $(wildcard HALKit/AMD64/*.s) $(ASM) $(ASMFLAGS) HALKit/AMD64/HalInterruptAPI.asm $(ASM) $(ASMFLAGS) HALKit/AMD64/HalCommonAPI.asm $(ASM) $(ASMFLAGS) HALKit/AMD64/HalHandoverStub.asm diff --git a/src/kernel/amd64-desktop.make b/src/kernel/amd64-desktop.make index cbaef256..a0c4b957 100644 --- a/src/kernel/amd64-desktop.make +++ b/src/kernel/amd64-desktop.make @@ -51,7 +51,7 @@ 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) + $(CXX) $(CCFLAGS) $(DISK_DRV) $(DEBUG_MACRO) $(wildcard src/*.cpp) $(wildcard src/Gfx/*.cpp) $(wildcard HALKit/AMD64/Network/*.cpp) $(wildcard HALKit/AMD64/PCI/*.cpp) $(wildcard src/Network/*.cpp) $(wildcard src/Storage/*.cpp) $(wildcard src/FS/*.cpp) $(wildcard HALKit/AMD64/Storage/*.cpp) $(wildcard HALKit/AMD64/*.cpp) $(wildcard src/Swap/*.cpp) $(wildcard HALKit/AMD64/*.s) $(ASM) $(ASMFLAGS) HALKit/AMD64/HalInterruptAPI.asm $(ASM) $(ASMFLAGS) HALKit/AMD64/HalCommonAPI.asm $(ASM) $(ASMFLAGS) HALKit/AMD64/HalHandoverStub.asm diff --git a/src/kernel/arm64-desktop.make b/src/kernel/arm64-desktop.make index db48b217..d38f0cbd 100644 --- a/src/kernel/arm64-desktop.make +++ b/src/kernel/arm64-desktop.make @@ -34,11 +34,11 @@ 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) + $(CC) $(CCFLAGS) $(DISKDRIVER) $(DEBUG) $(wildcard src/*.cpp) \ + $(wildcard src/FS/*.cpp) $(wildcard HALKit/ARM64/Storage/*.cpp) \ + $(wildcard HALKit/ARM64/PCI/*.cpp) $(wildcard src/Network/*.cpp) $(wildcard src/Storage/*.cpp) \ + $(wildcard HALKit/ARM64/*.cpp) $(wildcard HALKit/ARM64/*.cpp) \ + $(wildcard HALKit/ARM64/*.s) $(wildcard HALKit/ARM64/APM/*.cpp) $(MOVEALL) diff --git a/src/kernel/src/ACPIFactoryInterface.cc b/src/kernel/src/ACPIFactoryInterface.cc deleted file mode 100644 index 0b5ad18e..00000000 --- a/src/kernel/src/ACPIFactoryInterface.cc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include -#include - -namespace Kernel { -/// \note This has been incremented to version two, as NeKernel doesn't support 32-bit targets. See -/// specs. -constexpr STATIC const auto kMinACPIVer = 2U; - -/// @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... - 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{}; index < this->fEntries; ++index) { - SDT* sdt = reinterpret_cast(xsdt->AddressArr[index]); - - if (!sdt) continue; - - (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/ACPIFactoryInterface.cpp b/src/kernel/src/ACPIFactoryInterface.cpp new file mode 100644 index 00000000..0b5ad18e --- /dev/null +++ b/src/kernel/src/ACPIFactoryInterface.cpp @@ -0,0 +1,90 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include +#include + +namespace Kernel { +/// \note This has been incremented to version two, as NeKernel doesn't support 32-bit targets. See +/// specs. +constexpr STATIC const auto kMinACPIVer = 2U; + +/// @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... + 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{}; index < this->fEntries; ++index) { + SDT* sdt = reinterpret_cast(xsdt->AddressArr[index]); + + if (!sdt) continue; + + (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 deleted file mode 100644 index 45b38e41..00000000 --- a/src/kernel/src/Array.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/Array.cpp b/src/kernel/src/Array.cpp new file mode 100644 index 00000000..45b38e41 --- /dev/null +++ b/src/kernel/src/Array.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/AsciiUtils.cc b/src/kernel/src/AsciiUtils.cc deleted file mode 100644 index 08d6464b..00000000 --- a/src/kernel/src/AsciiUtils.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -Int32 rt_string_cmp(const Char* src, const Char* cmp, Size size) { - for (Size i{}; 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{}; - while (len < max_len && str[len] != '\0') ++len; - return len; -} - -Size rt_string_len(const Char* ptr) { - Size cnt{}; - 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{}; 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{}; 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{}; 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{}; 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{}; - do { - str[i++] = rt_to_char(value, base); - value /= base; - } while (value); - str[i] = '\0'; - // in-place - for (Int j{}; 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{}; 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/AsciiUtils.cpp b/src/kernel/src/AsciiUtils.cpp new file mode 100644 index 00000000..08d6464b --- /dev/null +++ b/src/kernel/src/AsciiUtils.cpp @@ -0,0 +1,158 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { +Int32 rt_string_cmp(const Char* src, const Char* cmp, Size size) { + for (Size i{}; 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{}; + while (len < max_len && str[len] != '\0') ++len; + return len; +} + +Size rt_string_len(const Char* ptr) { + Size cnt{}; + 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{}; 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{}; 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{}; 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{}; 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{}; + do { + str[i++] = rt_to_char(value, base); + value /= base; + } while (value); + str[i] = '\0'; + // in-place + for (Int j{}; 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{}; 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 deleted file mode 100644 index 9b2ac5e5..00000000 --- a/src/kernel/src/Atom.cc +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -// @file Atom.cc -// @brief Atomic primitives diff --git a/src/kernel/src/Atom.cpp b/src/kernel/src/Atom.cpp new file mode 100644 index 00000000..9b2ac5e5 --- /dev/null +++ b/src/kernel/src/Atom.cpp @@ -0,0 +1,8 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +// @file Atom.cc +// @brief Atomic primitives diff --git a/src/kernel/src/BinaryMutex.cc b/src/kernel/src/BinaryMutex.cc deleted file mode 100644 index 4edaed89..00000000 --- a/src/kernel/src/BinaryMutex.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -namespace Kernel { -/***********************************************************************************/ -/// @brief Unlocks the binary mutex. -/***********************************************************************************/ - -Bool BinaryMutex::Unlock() { - if (fLockingProcess->Status == ProcessStatusKind::kRunning) { - fLockingProcess = nullptr; - - return Yes; - } - - return No; -} - -/***********************************************************************************/ -/// @brief Locks process in the binary mutex. -/***********************************************************************************/ - -Bool BinaryMutex::Lock(BinaryMutex::LockedPtr 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 && this->fLockingProcess->Status == ProcessStatusKind::kRunning; -} - -/***********************************************************************************/ -/// @brief Try lock or wait. -/***********************************************************************************/ - -Bool BinaryMutex::LockAndWait(BinaryMutex::LockedPtr process, ITimer* timer) { - if (timer == nullptr) return No; - - 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) { - HardwareTimer hw_timer(rtl_milliseconds(sec)); - hw_timer.Wait(); - - return !this->IsLocked(); -} -} // namespace Kernel diff --git a/src/kernel/src/BinaryMutex.cpp b/src/kernel/src/BinaryMutex.cpp new file mode 100644 index 00000000..4edaed89 --- /dev/null +++ b/src/kernel/src/BinaryMutex.cpp @@ -0,0 +1,65 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +namespace Kernel { +/***********************************************************************************/ +/// @brief Unlocks the binary mutex. +/***********************************************************************************/ + +Bool BinaryMutex::Unlock() { + if (fLockingProcess->Status == ProcessStatusKind::kRunning) { + fLockingProcess = nullptr; + + return Yes; + } + + return No; +} + +/***********************************************************************************/ +/// @brief Locks process in the binary mutex. +/***********************************************************************************/ + +Bool BinaryMutex::Lock(BinaryMutex::LockedPtr 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 && this->fLockingProcess->Status == ProcessStatusKind::kRunning; +} + +/***********************************************************************************/ +/// @brief Try lock or wait. +/***********************************************************************************/ + +Bool BinaryMutex::LockAndWait(BinaryMutex::LockedPtr process, ITimer* timer) { + if (timer == nullptr) return No; + + 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) { + 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 deleted file mode 100644 index 1aee57b6..00000000 --- a/src/kernel/src/BitMapMgr.cc +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#ifdef __NE_AMD64__ -#include -#elif defined(__NE_ARM64__) -#include -#endif - -#include -#include - -#define kBitMapMagic (0x10210U) -#define kBitMapMaxSz gib_cast(4) -#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); - - if (!page_ptr) return No; - - if (ptr_bit_set[kBitMapMagIdx] != kBitMapMagic) return No; - - this->GetBitMapStatus(ptr_bit_set); - - kBitMapCursor += ptr_bit_set[kBitMapSizeIdx]; - - ptr_bit_set[kBitMapMagIdx] = 0UL; - ptr_bit_set[kBitMapSizeIdx] = 0UL; - ptr_bit_set[kBitMapUsedIdx] = No; - - return Yes; - } - - UInt32 MakeMMFlags(const Bool& wr, const 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 (size > kBitMapMaxSz) return nullptr; - - VoidPtr base = reinterpret_cast(base_ptr); - - 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); - } - - return nullptr; - } - - /// @brief Print Bitmap status - auto GetBitMapStatus(UIntPtr* ptr_bit_set) -> Void { - (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 ptr_new; - - ptr_new = kBitMapMgr.FindBitMap(kKernelBitMpStart, size, wr, user, pad); - 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/BitMapMgr.cpp b/src/kernel/src/BitMapMgr.cpp new file mode 100644 index 00000000..1aee57b6 --- /dev/null +++ b/src/kernel/src/BitMapMgr.cpp @@ -0,0 +1,187 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#ifdef __NE_AMD64__ +#include +#elif defined(__NE_ARM64__) +#include +#endif + +#include +#include + +#define kBitMapMagic (0x10210U) +#define kBitMapMaxSz gib_cast(4) +#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); + + if (!page_ptr) return No; + + if (ptr_bit_set[kBitMapMagIdx] != kBitMapMagic) return No; + + this->GetBitMapStatus(ptr_bit_set); + + kBitMapCursor += ptr_bit_set[kBitMapSizeIdx]; + + ptr_bit_set[kBitMapMagIdx] = 0UL; + ptr_bit_set[kBitMapSizeIdx] = 0UL; + ptr_bit_set[kBitMapUsedIdx] = No; + + return Yes; + } + + UInt32 MakeMMFlags(const Bool& wr, const 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 (size > kBitMapMaxSz) return nullptr; + + VoidPtr base = reinterpret_cast(base_ptr); + + 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); + } + + return nullptr; + } + + /// @brief Print Bitmap status + auto GetBitMapStatus(UIntPtr* ptr_bit_set) -> Void { + (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 ptr_new; + + ptr_new = kBitMapMgr.FindBitMap(kKernelBitMpStart, size, wr, user, pad); + 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 deleted file mode 100644 index c102022f..00000000 --- a/src/kernel/src/CRuntimeOverrides.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/CRuntimeOverrides.cpp b/src/kernel/src/CRuntimeOverrides.cpp new file mode 100644 index 00000000..c102022f --- /dev/null +++ b/src/kernel/src/CRuntimeOverrides.cpp @@ -0,0 +1,25 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 726fe574..00000000 --- a/src/kernel/src/CodeMgr.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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. -/// @param kid the Kernel ID of the new task. -/// @return The process started or not. -BOOL rtl_create_kernel_task(HAL::StackFramePtr task, const KID kid) { - 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_start_kind main, const Char* process_name) { - 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/CodeMgr.cpp b/src/kernel/src/CodeMgr.cpp new file mode 100644 index 00000000..726fe574 --- /dev/null +++ b/src/kernel/src/CodeMgr.cpp @@ -0,0 +1,35 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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. +/// @param kid the Kernel ID of the new task. +/// @return The process started or not. +BOOL rtl_create_kernel_task(HAL::StackFramePtr task, const KID kid) { + 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_start_kind main, const Char* process_name) { + 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 deleted file mode 100644 index 9918ce93..00000000 --- a/src/kernel/src/Crc32.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - 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/Crc32.cpp b/src/kernel/src/Crc32.cpp new file mode 100644 index 00000000..9918ce93 --- /dev/null +++ b/src/kernel/src/Crc32.cpp @@ -0,0 +1,63 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + 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 deleted file mode 100644 index 509c40af..00000000 --- a/src/kernel/src/Defines.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/Defines.cpp b/src/kernel/src/Defines.cpp new file mode 100644 index 00000000..509c40af --- /dev/null +++ b/src/kernel/src/Defines.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/DeviceMgr.cc b/src/kernel/src/DeviceMgr.cc deleted file mode 100644 index 7fceb85c..00000000 --- a/src/kernel/src/DeviceMgr.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel {} // namespace Kernel diff --git a/src/kernel/src/DeviceMgr.cpp b/src/kernel/src/DeviceMgr.cpp new file mode 100644 index 00000000..7fceb85c --- /dev/null +++ b/src/kernel/src/DeviceMgr.cpp @@ -0,0 +1,7 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel {} // namespace Kernel diff --git a/src/kernel/src/DriveMgr.cc b/src/kernel/src/DriveMgr.cc deleted file mode 100644 index 4affff4b..00000000 --- a/src/kernel/src/DriveMgr.cc +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - NE_UNUSED(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) { - NE_UNUSED(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) { - NE_UNUSED(pckt); -} - -/// @brief Makes a new drive. -/// @return the new blank drive. -DriveTrait io_construct_blank_drive() { - 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() { - 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) { - 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/DriveMgr.cpp b/src/kernel/src/DriveMgr.cpp new file mode 100644 index 00000000..4affff4b --- /dev/null +++ b/src/kernel/src/DriveMgr.cpp @@ -0,0 +1,255 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + NE_UNUSED(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) { + NE_UNUSED(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) { + NE_UNUSED(pckt); +} + +/// @brief Makes a new drive. +/// @return the new blank drive. +DriveTrait io_construct_blank_drive() { + 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() { + 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) { + 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 deleted file mode 100644 index 32f36583..00000000 --- a/src/kernel/src/ErrorOr.cc +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -/***********************************************************************************/ -/// @file ErrorOr.cc /// -/// @brief ErrorOr container class. /// -/***********************************************************************************/ diff --git a/src/kernel/src/ErrorOr.cpp b/src/kernel/src/ErrorOr.cpp new file mode 100644 index 00000000..32f36583 --- /dev/null +++ b/src/kernel/src/ErrorOr.cpp @@ -0,0 +1,10 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 3c724de5..00000000 --- a/src/kernel/src/FS/Ext2+IFS.cc +++ /dev/null @@ -1,1558 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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; - -namespace Detail { -static inline SizeT ext2_min(SizeT a, SizeT b) { - return a < b ? a : b; -} - -struct Ext2GroupInfo final { - EXT2_GROUP_DESCRIPTOR* groupDesc; - UInt32 groupDescriptorBlock; - UInt32 offsetInGroupDescBlock; - UInt8* blockBuffer; -}; -} // namespace Detail - -// 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 = ::Detail::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<::Detail::Ext2GroupInfo*> ext2_get_group_descriptor_info(Ext2Context* ctx, - UInt32 targetBlockOrInode) { - if (!ctx || !ctx->superblock || !ctx->drive) - return ErrorOr<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*>(kErrorHeapOutOfMemory); - - UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); - if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { - mm_free_ptr(blockBuffer); - return ErrorOr<::Detail::Ext2GroupInfo*>(kErrorDisk); - } - - auto groupInfo = - (::Detail::Ext2GroupInfo*) mm_alloc_ptr(sizeof(::Detail::Ext2GroupInfo), true, false); - if (!groupInfo) { - mm_free_ptr(blockBuffer); - return ErrorOr<::Detail::Ext2GroupInfo*>(kErrorHeapOutOfMemory); - } - - groupInfo->groupDesc = reinterpret_cast( - reinterpret_cast(blockBuffer) + offsetInGroupDescBlock); - groupInfo->groupDescriptorBlock = groupDescriptorBlock; - groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; - groupInfo->blockBuffer = reinterpret_cast(blockBuffer); - - return ErrorOr<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*> - 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().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().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().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 ::Detail::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 ::Detail::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 ::Detail::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(::Detail::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/Ext2+IFS.cpp b/src/kernel/src/FS/Ext2+IFS.cpp new file mode 100644 index 00000000..3c724de5 --- /dev/null +++ b/src/kernel/src/FS/Ext2+IFS.cpp @@ -0,0 +1,1558 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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; + +namespace Detail { +static inline SizeT ext2_min(SizeT a, SizeT b) { + return a < b ? a : b; +} + +struct Ext2GroupInfo final { + EXT2_GROUP_DESCRIPTOR* groupDesc; + UInt32 groupDescriptorBlock; + UInt32 offsetInGroupDescBlock; + UInt8* blockBuffer; +}; +} // namespace Detail + +// 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 = ::Detail::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<::Detail::Ext2GroupInfo*> ext2_get_group_descriptor_info(Ext2Context* ctx, + UInt32 targetBlockOrInode) { + if (!ctx || !ctx->superblock || !ctx->drive) + return ErrorOr<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*>(kErrorHeapOutOfMemory); + + UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); + if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { + mm_free_ptr(blockBuffer); + return ErrorOr<::Detail::Ext2GroupInfo*>(kErrorDisk); + } + + auto groupInfo = + (::Detail::Ext2GroupInfo*) mm_alloc_ptr(sizeof(::Detail::Ext2GroupInfo), true, false); + if (!groupInfo) { + mm_free_ptr(blockBuffer); + return ErrorOr<::Detail::Ext2GroupInfo*>(kErrorHeapOutOfMemory); + } + + groupInfo->groupDesc = reinterpret_cast( + reinterpret_cast(blockBuffer) + offsetInGroupDescBlock); + groupInfo->groupDescriptorBlock = groupDescriptorBlock; + groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; + groupInfo->blockBuffer = reinterpret_cast(blockBuffer); + + return ErrorOr<::Detail::Ext2GroupInfo*>(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<::Detail::Ext2GroupInfo*> + 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().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().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().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 ::Detail::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 ::Detail::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 ::Detail::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(::Detail::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 deleted file mode 100644 index b351c65e..00000000 --- a/src/kernel/src/FS/NeFS+FileMgr.cc +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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() { - 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+FileMgr.cpp b/src/kernel/src/FS/NeFS+FileMgr.cpp new file mode 100644 index 00000000..b351c65e --- /dev/null +++ b/src/kernel/src/FS/NeFS+FileMgr.cpp @@ -0,0 +1,274 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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() { + 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 deleted file mode 100644 index 88a847ec..00000000 --- a/src/kernel/src/FS/NeFS+FileSystemParser.cc +++ /dev/null @@ -1,868 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - 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/NeFS+FileSystemParser.cpp b/src/kernel/src/FS/NeFS+FileSystemParser.cpp new file mode 100644 index 00000000..88a847ec --- /dev/null +++ b/src/kernel/src/FS/NeFS+FileSystemParser.cpp @@ -0,0 +1,868 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + 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 deleted file mode 100644 index c33cb349..00000000 --- a/src/kernel/src/FS/OpenHeFS+FileMgr.cc +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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...\r"; -} - -HeFileSystemMgr::~HeFileSystemMgr() { - if (mParser) { - kout << "Destroying HeFileSystemParser...\r"; - 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\r"; - return NO; - } - - 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\r"; - return nullptr; - } - - // TODO: its own helper! - SizeT len = oe_string_len(path); - - Utf8Char* out = static_cast(RTL_ALLOCA(sizeof(Utf8Char) * len)); - - for (SizeT indx = 0UL; indx < len; ++indx) { - out[indx] = path[indx]; - } - - if (mParser->CreateINode(&mDriveTrait, 0, nullptr, out, 0)) - return nullptr; // AMLALE TODO: FetchINode method! - - 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\r"; - 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\r"; - return nullptr; - } - return nullptr; -} - -NodePtr HeFileSystemMgr::CreateSwapFile(const Char* path) { - if (!path || *path == 0) { - kout << "OpenHeFS: CreateSwapFile called with null or empty path\r"; - 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\r"; - return nullptr; - } - if (!r || *r == 0) { - kout << "OpenHeFS: Open called with null or empty mode string\r"; - 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() { - return mParser; -} -} // namespace Kernel - -#endif // ifdef __FSKIT_INCLUDES_OPENHEFS__ -#endif // ifndef __NE_MINIMAL_OS__ diff --git a/src/kernel/src/FS/OpenHeFS+FileMgr.cpp b/src/kernel/src/FS/OpenHeFS+FileMgr.cpp new file mode 100644 index 00000000..82eb6460 --- /dev/null +++ b/src/kernel/src/FS/OpenHeFS+FileMgr.cpp @@ -0,0 +1,250 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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...\r"; +} + +HeFileSystemMgr::~HeFileSystemMgr() { + if (mParser) { + kout << "Destroying HeFileSystemParser...\r"; + 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\r"; + return NO; + } + + return YES; +} + +/// @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\r"; + return nullptr; + } + + // TODO: It needs its own helper! + SizeT len = oe_string_len(path); + + Utf8Char* out = static_cast(RTL_ALLOCA(sizeof(Utf8Char) * len)); + + for (SizeT indx = 0UL; indx < len; ++indx) { + out[indx] = path[indx]; + } + + err_local_get() = kErrorSuccess; + + if (auto node = mParser->CreateINode(&mDriveTrait, 0, nullptr, out, 0); + node) + return nullptr; + + kout << "OpenHeFS: ERROR: Check KPC.\r"; + + err_local_get() = kErrorDiskIsFull; + + 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\r"; + return nullptr; + } + + // TODO: It needs its own helper! + SizeT len = oe_string_len(path); + + Utf8Char* out = static_cast(RTL_ALLOCA(sizeof(Utf8Char) * len)); + + for (SizeT indx = 0UL; indx < len; ++indx) { + out[indx] = path[indx]; + } + + err_local_get() = kErrorSuccess; + + if (auto node = mParser->CreateINodeDirectory(&mDriveTrait, 0, out); + node) + return nullptr; + + kout << "OpenHeFS: ERROR: Check KPC.\r"; + + err_local_get() = kErrorDiskIsFull; + + 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\r"; + return nullptr; + } + + // TODO: It needs its own helper! + SizeT len = oe_string_len(path); + + Utf8Char* out = static_cast(RTL_ALLOCA(sizeof(Utf8Char) * len)); + + for (SizeT indx = 0UL; indx < len; ++indx) { + out[indx] = path[indx]; + } + + err_local_get() = kErrorSuccess; + + if (auto node = mParser->CreateINode(&mDriveTrait, kOpenHeFSFileKindSymbolicLink, nullptr, out, 0); + node) + return nullptr; + + kout << "OpenHeFS: ERROR: Check KPC.\r"; + + err_local_get() = kErrorDiskIsFull; + + return nullptr; +} + +NodePtr HeFileSystemMgr::CreateSwapFile(const Char* path) { + if (!path || *path == 0) { + kout << "OpenHeFS: CreateSwapFile called with null or empty path\r"; + 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\r"; + return nullptr; + } + if (!r || *r == 0) { + kout << "OpenHeFS: Open called with null or empty mode string\r"; + 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() { + 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 deleted file mode 100644 index d51a95d2..00000000 --- a/src/kernel/src/FS/OpenHeFS+FileSystemParser.cc +++ /dev/null @@ -1,1160 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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; - } - - /// TODO: 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) { - 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/FS/OpenHeFS+FileSystemParser.cpp b/src/kernel/src/FS/OpenHeFS+FileSystemParser.cpp new file mode 100644 index 00000000..d51a95d2 --- /dev/null +++ b/src/kernel/src/FS/OpenHeFS+FileSystemParser.cpp @@ -0,0 +1,1160 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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; + } + + /// TODO: 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) { + 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 deleted file mode 100644 index b33e54c3..00000000 --- a/src/kernel/src/FileMgr.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/FileMgr.cpp b/src/kernel/src/FileMgr.cpp new file mode 100644 index 00000000..b33e54c3 --- /dev/null +++ b/src/kernel/src/FileMgr.cpp @@ -0,0 +1,47 @@ +// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 7a41f1bc..00000000 --- a/src/kernel/src/GUIDWizard.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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 Array& 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/GUIDWizard.cpp b/src/kernel/src/GUIDWizard.cpp new file mode 100644 index 00000000..7a41f1bc --- /dev/null +++ b/src/kernel/src/GUIDWizard.cpp @@ -0,0 +1,60 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 Array& 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 deleted file mode 100644 index 87d462d8..00000000 --- a/src/kernel/src/GUIDWrapper.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel::CF::XRN {} diff --git a/src/kernel/src/GUIDWrapper.cpp b/src/kernel/src/GUIDWrapper.cpp new file mode 100644 index 00000000..87d462d8 --- /dev/null +++ b/src/kernel/src/GUIDWrapper.cpp @@ -0,0 +1,7 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel::CF::XRN {} diff --git a/src/kernel/src/Gfx/FBDeviceInterface.cc b/src/kernel/src/Gfx/FBDeviceInterface.cc deleted file mode 100644 index 87ea0699..00000000 --- a/src/kernel/src/Gfx/FBDeviceInterface.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -using namespace Kernel; - -/// @brief Class constructor -/// @param Out Drive output -/// @param In Drive input -/// @param Cleanup Drive cleanup. -FBDeviceInterface::FBDeviceInterface(void (*out)(IDevice* self, FBDevicePacket* outpacket), - void (*in)(IDevice* self, FBDevicePacket* inpacket)) - : IDevice(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 kDeviceMgrRootDirPath "fb{}"; -} \ No newline at end of file diff --git a/src/kernel/src/Gfx/FBDeviceInterface.cpp b/src/kernel/src/Gfx/FBDeviceInterface.cpp new file mode 100644 index 00000000..87ea0699 --- /dev/null +++ b/src/kernel/src/Gfx/FBDeviceInterface.cpp @@ -0,0 +1,48 @@ +// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +using namespace Kernel; + +/// @brief Class constructor +/// @param Out Drive output +/// @param In Drive input +/// @param Cleanup Drive cleanup. +FBDeviceInterface::FBDeviceInterface(void (*out)(IDevice* self, FBDevicePacket* outpacket), + void (*in)(IDevice* self, FBDevicePacket* inpacket)) + : IDevice(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 kDeviceMgrRootDirPath "fb{}"; +} \ No newline at end of file diff --git a/src/kernel/src/HardwareThreadScheduler.cc b/src/kernel/src/HardwareThreadScheduler.cc deleted file mode 100644 index a15f5cc0..00000000 --- a/src/kernel/src/HardwareThreadScheduler.cc +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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. -/***********************************************************************************/ -_Output const ThreadID& HardwareThread::ID() { - return fID; -} - -/***********************************************************************************/ -//! @brief returns the kind of thread we have. -/***********************************************************************************/ -_Output const ThreadKind& HardwareThread::Kind() { - return fKind; -} - -/***********************************************************************************/ -//! @brief is the thread busy? -//! @return whether the thread is busy or not. -/***********************************************************************************/ -Bool HardwareThread::IsBusy() { - return fBusy; -} - -/***********************************************************************************/ -/// @brief Get processor stack frame. -/***********************************************************************************/ - -HAL::StackFramePtr HardwareThread::StackFrame() { - MUST_PASS(this->fStack); - return this->fStack; -} - -Void HardwareThread::Busy(Bool busy) { - this->fBusy = busy; -} - -HardwareThread::operator bool() { - return this->fStack && !this->fBusy; -} - -/***********************************************************************************/ -/// @brief Wakeup the processor. -/***********************************************************************************/ - -Void HardwareThread::Wake(const bool wakeup) { - 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() { - 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() { - 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) { - STATIC HardwareThread* kFakeThread = nullptr; - return {kFakeThread}; - } - - fCurrentThreadIdx = idx; - return &fThreadList[idx]; -} - -/***********************************************************************************/ -/** - * Check if thread pool isn't empty. - * @return - */ -/***********************************************************************************/ -HardwareThreadScheduler::operator bool() { - return !fThreadList.Empty(); -} - -/***********************************************************************************/ -/** - * Reverse operator bool - * @return - */ -/***********************************************************************************/ -bool HardwareThreadScheduler::operator!() { - return fThreadList.Empty(); -} - -/***********************************************************************************/ -/// @brief Returns the amount of core present. -/// @return the number of APs. -/***********************************************************************************/ -SizeT HardwareThreadScheduler::Capacity() { - return fThreadList.Count(); -} -} // namespace Kernel diff --git a/src/kernel/src/HardwareThreadScheduler.cpp b/src/kernel/src/HardwareThreadScheduler.cpp new file mode 100644 index 00000000..a15f5cc0 --- /dev/null +++ b/src/kernel/src/HardwareThreadScheduler.cpp @@ -0,0 +1,182 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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. +/***********************************************************************************/ +_Output const ThreadID& HardwareThread::ID() { + return fID; +} + +/***********************************************************************************/ +//! @brief returns the kind of thread we have. +/***********************************************************************************/ +_Output const ThreadKind& HardwareThread::Kind() { + return fKind; +} + +/***********************************************************************************/ +//! @brief is the thread busy? +//! @return whether the thread is busy or not. +/***********************************************************************************/ +Bool HardwareThread::IsBusy() { + return fBusy; +} + +/***********************************************************************************/ +/// @brief Get processor stack frame. +/***********************************************************************************/ + +HAL::StackFramePtr HardwareThread::StackFrame() { + MUST_PASS(this->fStack); + return this->fStack; +} + +Void HardwareThread::Busy(Bool busy) { + this->fBusy = busy; +} + +HardwareThread::operator bool() { + return this->fStack && !this->fBusy; +} + +/***********************************************************************************/ +/// @brief Wakeup the processor. +/***********************************************************************************/ + +Void HardwareThread::Wake(const bool wakeup) { + 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() { + 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() { + 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) { + STATIC HardwareThread* kFakeThread = nullptr; + return {kFakeThread}; + } + + fCurrentThreadIdx = idx; + return &fThreadList[idx]; +} + +/***********************************************************************************/ +/** + * Check if thread pool isn't empty. + * @return + */ +/***********************************************************************************/ +HardwareThreadScheduler::operator bool() { + return !fThreadList.Empty(); +} + +/***********************************************************************************/ +/** + * Reverse operator bool + * @return + */ +/***********************************************************************************/ +bool HardwareThreadScheduler::operator!() { + return fThreadList.Empty(); +} + +/***********************************************************************************/ +/// @brief Returns the amount of core present. +/// @return the number of APs. +/***********************************************************************************/ +SizeT HardwareThreadScheduler::Capacity() { + return fThreadList.Count(); +} +} // namespace Kernel diff --git a/src/kernel/src/HeapMgr.cc b/src/kernel/src/HeapMgr.cc deleted file mode 100644 index ef25abb7..00000000 --- a/src/kernel/src/HeapMgr.cc +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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); - - if (result) - (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)); - - /// TODO: if valid, present and is heap header, then compute crc32 - if (heap_info_ptr && heap_info_ptr->fPresent && kHeapMgrMagic == heap_info_ptr->fMagic) { - /// TODO: Protect only the header, information in it may change. - heap_info_ptr->fCRC32 = - ke_calculate_crc32((Char*) heap_info_ptr, sizeof(Detail::MM_INFORMATION_BLOCK)); - - return Yes; - } - } - - return No; -} -} // namespace Kernel diff --git a/src/kernel/src/HeapMgr.cpp b/src/kernel/src/HeapMgr.cpp new file mode 100644 index 00000000..ef25abb7 --- /dev/null +++ b/src/kernel/src/HeapMgr.cpp @@ -0,0 +1,260 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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); + + if (result) + (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)); + + /// TODO: if valid, present and is heap header, then compute crc32 + if (heap_info_ptr && heap_info_ptr->fPresent && kHeapMgrMagic == heap_info_ptr->fMagic) { + /// TODO: Protect only the header, information in it may change. + heap_info_ptr->fCRC32 = + ke_calculate_crc32((Char*) heap_info_ptr, sizeof(Detail::MM_INFORMATION_BLOCK)); + + return Yes; + } + } + + return No; +} +} // namespace Kernel diff --git a/src/kernel/src/IDylibObject.cc b/src/kernel/src/IDylibObject.cc deleted file mode 100644 index 503ea3f4..00000000 --- a/src/kernel/src/IDylibObject.cc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include diff --git a/src/kernel/src/IDylibObject.cpp b/src/kernel/src/IDylibObject.cpp new file mode 100644 index 00000000..503ea3f4 --- /dev/null +++ b/src/kernel/src/IDylibObject.cpp @@ -0,0 +1,6 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include diff --git a/src/kernel/src/IFS.cc b/src/kernel/src/IFS.cc deleted file mode 100644 index 133a358b..00000000 --- a/src/kernel/src/IFS.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -/************************************************************* - * - * File: \file IFS.cc - * Purpose: \brief Filesystem to mountpoint interface. - * Date: \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 KPC status code from the IFS. -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/IFS.cpp b/src/kernel/src/IFS.cpp new file mode 100644 index 00000000..133a358b --- /dev/null +++ b/src/kernel/src/IFS.cpp @@ -0,0 +1,87 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +/************************************************************* + * + * File: \file IFS.cc + * Purpose: \brief Filesystem to mountpoint interface. + * Date: \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 KPC status code from the IFS. +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 deleted file mode 100644 index 6e42c877..00000000 --- a/src/kernel/src/IPEFDylibObject.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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(UserProcess& 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(UserProcess& 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/IPEFDylibObject.cpp b/src/kernel/src/IPEFDylibObject.cpp new file mode 100644 index 00000000..6e42c877 --- /dev/null +++ b/src/kernel/src/IPEFDylibObject.cpp @@ -0,0 +1,104 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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(UserProcess& 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(UserProcess& 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 deleted file mode 100644 index 49063950..00000000 --- a/src/kernel/src/IndexableProperty.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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() { - 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/IndexableProperty.cpp b/src/kernel/src/IndexableProperty.cpp new file mode 100644 index 00000000..49063950 --- /dev/null +++ b/src/kernel/src/IndexableProperty.cpp @@ -0,0 +1,51 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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() { + 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/InitializerList.cc b/src/kernel/src/InitializerList.cc deleted file mode 100644 index 9f19aacb..00000000 --- a/src/kernel/src/InitializerList.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/InitializerList.cpp b/src/kernel/src/InitializerList.cpp new file mode 100644 index 00000000..9f19aacb --- /dev/null +++ b/src/kernel/src/InitializerList.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/Json.cc b/src/kernel/src/Json.cc deleted file mode 100644 index a960154b..00000000 --- a/src/kernel/src/Json.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel {} \ No newline at end of file diff --git a/src/kernel/src/Json.cpp b/src/kernel/src/Json.cpp new file mode 100644 index 00000000..a960154b --- /dev/null +++ b/src/kernel/src/Json.cpp @@ -0,0 +1,7 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel {} \ No newline at end of file diff --git a/src/kernel/src/KPC.cc b/src/kernel/src/KPC.cc deleted file mode 100644 index d0f7c4b5..00000000 --- a/src/kernel/src/KPC.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - 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/KPC.cpp b/src/kernel/src/KPC.cpp new file mode 100644 index 00000000..d0f7c4b5 --- /dev/null +++ b/src/kernel/src/KPC.cpp @@ -0,0 +1,37 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + 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 deleted file mode 100644 index 540fe613..00000000 --- a/src/kernel/src/KernelTaskScheduler.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -/***********************************************************************************/ -/// @file KernelTaskScheduler.cc -/// @brief Kernel Task scheduler. -/// @author Amlal El Mahrouss (amlal@nekernel.org) -/***********************************************************************************/ - -namespace Kernel { - -Bool KernelTaskHelper::Add(HAL::StackFramePtr frame_ptr, KID kid) { - NE_UNUSED(frame_ptr); - NE_UNUSED(kid); - - return NO; -} - -Bool KernelTaskHelper::Remove(const KID kid) { - NE_UNUSED(kid); - - return NO; -} - -Bool KernelTaskHelper::CanBeScheduled(const KernelTask& task) { - return task.Kid > 0 && task.Image.HasCode(); -} - -} // namespace Kernel diff --git a/src/kernel/src/KernelTaskScheduler.cpp b/src/kernel/src/KernelTaskScheduler.cpp new file mode 100644 index 00000000..540fe613 --- /dev/null +++ b/src/kernel/src/KernelTaskScheduler.cpp @@ -0,0 +1,32 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +/***********************************************************************************/ +/// @file KernelTaskScheduler.cc +/// @brief Kernel Task scheduler. +/// @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +namespace Kernel { + +Bool KernelTaskHelper::Add(HAL::StackFramePtr frame_ptr, KID kid) { + NE_UNUSED(frame_ptr); + NE_UNUSED(kid); + + return NO; +} + +Bool KernelTaskHelper::Remove(const KID kid) { + NE_UNUSED(kid); + + return NO; +} + +Bool KernelTaskHelper::CanBeScheduled(const KernelTask& task) { + return task.Kid > 0 && task.Image.HasCode(); +} + +} // namespace Kernel diff --git a/src/kernel/src/LockDelegate.cc b/src/kernel/src/LockDelegate.cc deleted file mode 100644 index 1f679c06..00000000 --- a/src/kernel/src/LockDelegate.cc +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -/// @note Leave it empty for now. -} // namespace Kernel diff --git a/src/kernel/src/LockDelegate.cpp b/src/kernel/src/LockDelegate.cpp new file mode 100644 index 00000000..1f679c06 --- /dev/null +++ b/src/kernel/src/LockDelegate.cpp @@ -0,0 +1,9 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index fde9dba3..00000000 --- a/src/kernel/src/MutableArray.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/MutableArray.cpp b/src/kernel/src/MutableArray.cpp new file mode 100644 index 00000000..fde9dba3 --- /dev/null +++ b/src/kernel/src/MutableArray.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/Network/IPAddress.cc b/src/kernel/src/Network/IPAddress.cc deleted file mode 100644 index 3972c248..00000000 --- a/src/kernel/src/Network/IPAddress.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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 (SizeT index{}; index < 4; ++index) { - if (ipv4.fAddr[index] != fAddr[index]) return false; - } - - return true; -} - -BOOL RawIPAddress::operator!=(const RawIPAddress& ipv4) { - for (SizeT index{}; 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 kIPPlaceholder = '0'; - if (index >= 4) return kIPPlaceholder; - - return fAddr[index]; -} - -RawIPAddress6::RawIPAddress6(UInt8 bytes[16]) { - rt_copy_memory(bytes, fAddr, 16); -} - -UInt8* RawIPAddress6::Address() { - return fAddr; -} - -UInt8& RawIPAddress6::operator[](const Size& index) { - kout << "[RawIPAddress6::operator[]] Fetching Index...\r"; - - static UInt8 kIPPlaceholder = '0'; - if (index >= 16) return kIPPlaceholder; - - return fAddr[index]; -} - -bool RawIPAddress6::operator==(const RawIPAddress6& ipv6) { - for (SizeT index{}; index < 16; ++index) { - if (ipv6.fAddr[index] != fAddr[index]) return false; - } - - return true; -} - -bool RawIPAddress6::operator!=(const RawIPAddress6& ipv6) { - for (SizeT index{}; index < 16; ++index) { - if (ipv6.fAddr[index] == fAddr[index]) return false; - } - - return true; -} - -ErrorOr> IPFactory::ToKString(Ref& ipv6) { - NE_UNUSED(ipv6); - auto kipv6 = KStringBuilder::Construct(ipv6.Leak().Address()); - return kipv6; -} - -ErrorOr> IPFactory::ToKString(Ref& ipv4) { - NE_UNUSED(ipv4); - auto kipv4 = KStringBuilder::Construct(ipv4.Leak().Address()); - return kipv4; -} - -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/IPAddress.cpp b/src/kernel/src/Network/IPAddress.cpp new file mode 100644 index 00000000..3972c248 --- /dev/null +++ b/src/kernel/src/Network/IPAddress.cpp @@ -0,0 +1,114 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 (SizeT index{}; index < 4; ++index) { + if (ipv4.fAddr[index] != fAddr[index]) return false; + } + + return true; +} + +BOOL RawIPAddress::operator!=(const RawIPAddress& ipv4) { + for (SizeT index{}; 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 kIPPlaceholder = '0'; + if (index >= 4) return kIPPlaceholder; + + return fAddr[index]; +} + +RawIPAddress6::RawIPAddress6(UInt8 bytes[16]) { + rt_copy_memory(bytes, fAddr, 16); +} + +UInt8* RawIPAddress6::Address() { + return fAddr; +} + +UInt8& RawIPAddress6::operator[](const Size& index) { + kout << "[RawIPAddress6::operator[]] Fetching Index...\r"; + + static UInt8 kIPPlaceholder = '0'; + if (index >= 16) return kIPPlaceholder; + + return fAddr[index]; +} + +bool RawIPAddress6::operator==(const RawIPAddress6& ipv6) { + for (SizeT index{}; index < 16; ++index) { + if (ipv6.fAddr[index] != fAddr[index]) return false; + } + + return true; +} + +bool RawIPAddress6::operator!=(const RawIPAddress6& ipv6) { + for (SizeT index{}; index < 16; ++index) { + if (ipv6.fAddr[index] == fAddr[index]) return false; + } + + return true; +} + +ErrorOr> IPFactory::ToKString(Ref& ipv6) { + NE_UNUSED(ipv6); + auto kipv6 = KStringBuilder::Construct(ipv6.Leak().Address()); + return kipv6; +} + +ErrorOr> IPFactory::ToKString(Ref& ipv4) { + NE_UNUSED(ipv4); + auto kipv4 = KStringBuilder::Construct(ipv4.Leak().Address()); + return kipv4; +} + +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 deleted file mode 100644 index efddfeb2..00000000 --- a/src/kernel/src/Network/IPCAddress.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include - -namespace Kernel { -bool IPC_ADDR::operator==(const IPC_ADDR& addr) { - return addr.UserProcessID == this->UserProcessID && addr.UserProcessTeam == this->UserProcessTeam; -} - -bool IPC_ADDR::operator==(IPC_ADDR& addr) { - return addr.UserProcessID == this->UserProcessID && addr.UserProcessTeam == this->UserProcessTeam; -} - -bool IPC_ADDR::operator!=(const IPC_ADDR& addr) { - return addr.UserProcessID != this->UserProcessID || addr.UserProcessTeam != this->UserProcessTeam; -} - -bool IPC_ADDR::operator!=(IPC_ADDR& addr) { - return addr.UserProcessID != this->UserProcessID || addr.UserProcessTeam != this->UserProcessTeam; -} -} // namespace Kernel diff --git a/src/kernel/src/Network/IPCAddress.cpp b/src/kernel/src/Network/IPCAddress.cpp new file mode 100644 index 00000000..efddfeb2 --- /dev/null +++ b/src/kernel/src/Network/IPCAddress.cpp @@ -0,0 +1,25 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include + +namespace Kernel { +bool IPC_ADDR::operator==(const IPC_ADDR& addr) { + return addr.UserProcessID == this->UserProcessID && addr.UserProcessTeam == this->UserProcessTeam; +} + +bool IPC_ADDR::operator==(IPC_ADDR& addr) { + return addr.UserProcessID == this->UserProcessID && addr.UserProcessTeam == this->UserProcessTeam; +} + +bool IPC_ADDR::operator!=(const IPC_ADDR& addr) { + return addr.UserProcessID != this->UserProcessID || addr.UserProcessTeam != this->UserProcessTeam; +} + +bool IPC_ADDR::operator!=(IPC_ADDR& addr) { + 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 deleted file mode 100644 index 331f7ba7..00000000 --- a/src/kernel/src/Network/IPCMessage.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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 < 1) { - 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) { - if (src && target && (target != src)) { - if (target->IpcSize > src->IpcSize) return No; - - auto timeout = 0U; - - constexpr auto kLimitTimeout = 1000000U; - - while ((target->IpcLock % kIPCLockUsed) != 0) { - if (timeout > kLimitTimeout) { - return No; - } - } - - ++target->IpcLock; - - rt_copy_memory_safe(src->IpcData, target->IpcData, src->IpcSize, target->IpcSize); - - --target->IpcLock; - - return Yes; - } - - return No; -} -} // namespace Kernel diff --git a/src/kernel/src/Network/IPCMessage.cpp b/src/kernel/src/Network/IPCMessage.cpp new file mode 100644 index 00000000..331f7ba7 --- /dev/null +++ b/src/kernel/src/Network/IPCMessage.cpp @@ -0,0 +1,126 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 < 1) { + 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) { + if (src && target && (target != src)) { + if (target->IpcSize > src->IpcSize) return No; + + auto timeout = 0U; + + constexpr auto kLimitTimeout = 1000000U; + + while ((target->IpcLock % kIPCLockUsed) != 0) { + if (timeout > kLimitTimeout) { + return No; + } + } + + ++target->IpcLock; + + rt_copy_memory_safe(src->IpcData, target->IpcData, src->IpcSize, target->IpcSize); + + --target->IpcLock; + + return Yes; + } + + return No; +} +} // namespace Kernel diff --git a/src/kernel/src/Network/MACAddressGetter.cc b/src/kernel/src/Network/MACAddressGetter.cc deleted file mode 100644 index 6f7dea67..00000000 --- a/src/kernel/src/Network/MACAddressGetter.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -Array& MacAddressGetter::AsBytes() { - return this->fMacAddress; -} -} // namespace Kernel diff --git a/src/kernel/src/Network/MACAddressGetter.cpp b/src/kernel/src/Network/MACAddressGetter.cpp new file mode 100644 index 00000000..6f7dea67 --- /dev/null +++ b/src/kernel/src/Network/MACAddressGetter.cpp @@ -0,0 +1,11 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 70c8ecde..00000000 --- a/src/kernel/src/Network/NetworkDevice.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -namespace Kernel { -/// \brief Getter for fNetworkName. -/// \return Network device name. -const Char* NetworkDevice::Name() const { - return kDeviceMgrRootDirPath "net/nic{}"; -} - -/// \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/Network/NetworkDevice.cpp b/src/kernel/src/Network/NetworkDevice.cpp new file mode 100644 index 00000000..70c8ecde --- /dev/null +++ b/src/kernel/src/Network/NetworkDevice.cpp @@ -0,0 +1,27 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +namespace Kernel { +/// \brief Getter for fNetworkName. +/// \return Network device name. +const Char* NetworkDevice::Name() const { + return kDeviceMgrRootDirPath "net/nic{}"; +} + +/// \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 deleted file mode 100644 index 18ec32bf..00000000 --- a/src/kernel/src/New+Delete.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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) { - if (ptr == nullptr) return; - - Kernel::mm_free_ptr(ptr); -} - -void operator delete(void* ptr) { - 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/New+Delete.cpp b/src/kernel/src/New+Delete.cpp new file mode 100644 index 00000000..18ec32bf --- /dev/null +++ b/src/kernel/src/New+Delete.cpp @@ -0,0 +1,46 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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) { + if (ptr == nullptr) return; + + Kernel::mm_free_ptr(ptr); +} + +void operator delete(void* ptr) { + 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 deleted file mode 100644 index a112be13..00000000 --- a/src/kernel/src/OwnPtr.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/OwnPtr.cpp b/src/kernel/src/OwnPtr.cpp new file mode 100644 index 00000000..a112be13 --- /dev/null +++ b/src/kernel/src/OwnPtr.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/PE32CodeMgr.cc b/src/kernel/src/PE32CodeMgr.cc deleted file mode 100644 index 1cfaa524..00000000 --- a/src/kernel/src/PE32CodeMgr.cc +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define kPeStackSizeSymbol "__NESizeOfReserveStack" -#define kPeHeapSizeSymbol "__NESizeOfReserveHeap" -#define kPeNameSymbol "__NEProgramName" -#define kPeImageStart "__ImageStart" - -namespace Kernel { - -namespace Detail { - /***********************************************************************************/ - /// @brief Get the PE32+ platform signature according to the compiled architecture. - /***********************************************************************************/ - - UInt32 ldr_get_platform_pe(void) { -#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) { - fBad = false; -} - -/***********************************************************************************/ -/// @brief PE32+ loader constructor. -/// @param path the filesystem path. -/***********************************************************************************/ - -PE32Loader::PE32Loader(const Char* path) : fCachedBlob(static_cast(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.HasError() || !fCachedBlob.Leak().Leak()) fBad = YES; -} - -/***********************************************************************************/ -/// @brief PE32+ destructor. -/***********************************************************************************/ - -PE32Loader::~PE32Loader() { - if (fCachedBlob) { - mm_free_ptr(fCachedBlob.Leak().Leak()); - fFile.Reset(); - } -} - -/***********************************************************************************/ -/// @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.Leak().Leak()); - LDR_OPTIONAL_HEADER_PTR opt_header_ptr = - CF::ldr_find_opt_exec_header((const Char*) fCachedBlob.Leak().Leak()); - - 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}; - } - -#if !defined(__nekernel_allow_non_nekernel_pe) - if (opt_header_ptr->Subsystem != kNeKernelPESubsystem) { - return ErrorOr{kErrorInvalidData}; - } -#endif - - 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 (!fCachedBlob || fBad || !name) 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.Leak().Leak()); - - 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.Leak().Leak() + - export_dir_entry->VirtualAddress); - - UInt32* name_table = - (UInt32*) ((UIntPtr) fCachedBlob.Leak().Leak() + export_dir->AddressOfNames); - UInt16* ordinal_table = - (UInt16*) ((UIntPtr) fCachedBlob.Leak().Leak() + export_dir->AddressOfNameOrdinal); - UInt32* function_table = - (UInt32*) ((UIntPtr) fCachedBlob.Leak().Leak() + export_dir->AddressOfFunctions); - - for (UInt32 i = 0; i < export_dir->NumberOfNames; ++i) { - const char* exported_name = - (const char*) ((UIntPtr) fCachedBlob.Leak().Leak() + 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.Leak().Leak() + 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() { - return !fBad; -} - -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.Leak().Leak()}; -} - -namespace Utils { - ProcessID rtl_create_user_process(PE32Loader& exec, - const UserProcess::ExecutableKind& process_kind) { - if (!exec.IsLoaded()) return kSchedInvalidPID; - - ErrorOrAny errOrStart = exec.FindStart(); - - if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID; - - ErrorOrAny symname = exec.FindSymbol(kPeNameSymbol, 0); - - if (!symname.Leak().Leak()) - symname = ErrorOr{(VoidPtr) rt_alloc_string(kPeImageStart)}; - - if (!symname.Leak().Leak()) return kSchedInvalidPID; - - 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(kPeStackSizeSymbol, 0); - - if (!stacksym.Leak().Leak()) { - stacksym = ErrorOr{(VoidPtr) new UIntPtr(kSchedMaxStackSz)}; - } - - if (!stacksym.Leak().Leak()) { - UserProcessScheduler::The().Remove(id); - mm_free_ptr(stacksym.Leak().Leak()); - return kSchedInvalidPID; - } - - if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) { - *(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz; - } - - UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[id].Kind = process_kind; - UserProcessScheduler::The().TheCurrentTeam().Leak().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/PE32CodeMgr.cpp b/src/kernel/src/PE32CodeMgr.cpp new file mode 100644 index 00000000..1cfaa524 --- /dev/null +++ b/src/kernel/src/PE32CodeMgr.cpp @@ -0,0 +1,287 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define kPeStackSizeSymbol "__NESizeOfReserveStack" +#define kPeHeapSizeSymbol "__NESizeOfReserveHeap" +#define kPeNameSymbol "__NEProgramName" +#define kPeImageStart "__ImageStart" + +namespace Kernel { + +namespace Detail { + /***********************************************************************************/ + /// @brief Get the PE32+ platform signature according to the compiled architecture. + /***********************************************************************************/ + + UInt32 ldr_get_platform_pe(void) { +#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) { + fBad = false; +} + +/***********************************************************************************/ +/// @brief PE32+ loader constructor. +/// @param path the filesystem path. +/***********************************************************************************/ + +PE32Loader::PE32Loader(const Char* path) : fCachedBlob(static_cast(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.HasError() || !fCachedBlob.Leak().Leak()) fBad = YES; +} + +/***********************************************************************************/ +/// @brief PE32+ destructor. +/***********************************************************************************/ + +PE32Loader::~PE32Loader() { + if (fCachedBlob) { + mm_free_ptr(fCachedBlob.Leak().Leak()); + fFile.Reset(); + } +} + +/***********************************************************************************/ +/// @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.Leak().Leak()); + LDR_OPTIONAL_HEADER_PTR opt_header_ptr = + CF::ldr_find_opt_exec_header((const Char*) fCachedBlob.Leak().Leak()); + + 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}; + } + +#if !defined(__nekernel_allow_non_nekernel_pe) + if (opt_header_ptr->Subsystem != kNeKernelPESubsystem) { + return ErrorOr{kErrorInvalidData}; + } +#endif + + 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 (!fCachedBlob || fBad || !name) 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.Leak().Leak()); + + 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.Leak().Leak() + + export_dir_entry->VirtualAddress); + + UInt32* name_table = + (UInt32*) ((UIntPtr) fCachedBlob.Leak().Leak() + export_dir->AddressOfNames); + UInt16* ordinal_table = + (UInt16*) ((UIntPtr) fCachedBlob.Leak().Leak() + export_dir->AddressOfNameOrdinal); + UInt32* function_table = + (UInt32*) ((UIntPtr) fCachedBlob.Leak().Leak() + export_dir->AddressOfFunctions); + + for (UInt32 i = 0; i < export_dir->NumberOfNames; ++i) { + const char* exported_name = + (const char*) ((UIntPtr) fCachedBlob.Leak().Leak() + 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.Leak().Leak() + 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() { + return !fBad; +} + +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.Leak().Leak()}; +} + +namespace Utils { + ProcessID rtl_create_user_process(PE32Loader& exec, + const UserProcess::ExecutableKind& process_kind) { + if (!exec.IsLoaded()) return kSchedInvalidPID; + + ErrorOrAny errOrStart = exec.FindStart(); + + if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID; + + ErrorOrAny symname = exec.FindSymbol(kPeNameSymbol, 0); + + if (!symname.Leak().Leak()) + symname = ErrorOr{(VoidPtr) rt_alloc_string(kPeImageStart)}; + + if (!symname.Leak().Leak()) return kSchedInvalidPID; + + 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(kPeStackSizeSymbol, 0); + + if (!stacksym.Leak().Leak()) { + stacksym = ErrorOr{(VoidPtr) new UIntPtr(kSchedMaxStackSz)}; + } + + if (!stacksym.Leak().Leak()) { + UserProcessScheduler::The().Remove(id); + mm_free_ptr(stacksym.Leak().Leak()); + return kSchedInvalidPID; + } + + if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) { + *(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz; + } + + UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[id].Kind = process_kind; + UserProcessScheduler::The().TheCurrentTeam().Leak().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 deleted file mode 100644 index 3c2727e7..00000000 --- a/src/kernel/src/PEFCodeMgr.cc +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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" -#define kPefImageStart "__ImageStart" - -namespace Kernel { - -namespace Detail { - /***********************************************************************************/ - /// @brief Get the PEF platform signature according to the compiled architecture. - /***********************************************************************************/ - static UInt32 ldr_get_platform(void) { -#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) { - if (fCachedBlob.Leak().Leak()) { - this->fBad = YES; - return; - } - - PEFContainer* container = reinterpret_cast(fCachedBlob.Leak().Leak()); - - 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.Leak().Leak()); - this->fCachedBlob.Leak().Leak() = 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); - - if (!fFile) { - this->fBad = YES; - return; - } - - 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.Leak().Leak()) { - kout << "PEFLoader: warning: Binary format error!\r"; - this->fBad = YES; - this->fFatBinary = NO; - - return; - } - - PEFContainer* container = reinterpret_cast(fCachedBlob.Leak().Leak()); - - 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.Leak().Leak()) mm_free_ptr(this->fCachedBlob.Leak().Leak()); - this->fCachedBlob.Leak().Leak() = nullptr; -} - -/***********************************************************************************/ -/// @brief PEF destructor. -/***********************************************************************************/ -PEFLoader::~PEFLoader() { - if (fCachedBlob.Leak().Leak()) { - mm_free_ptr(fCachedBlob.Leak().Leak()); - } -} - -/***********************************************************************************/ -/// @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.Leak().Leak() || fBad || !name) return ErrorOr{kErrorInvalidData}; - - auto blob = fFile->Read(name, sizeof(PEFCommandHeader)); - - if (!blob) return ErrorOr{kErrorInvalidData}; - - PEFContainer* container = reinterpret_cast(fCachedBlob.Leak().Leak()); - - if (!container) return ErrorOr{kErrorInvalidData}; - - PEFCommandHeader* command_header = reinterpret_cast(blob.Leak().Leak()); - - 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.Leak().Leak()); - blob.Leak().Leak() = nullptr; - - return ErrorOr{kErrorInvalidData}; - } - } - - Char* container_blob_value = new Char[command_header->VMSize]; - - rt_copy_memory_safe((VoidPtr) ((Char*) blob.Leak().Leak() + sizeof(PEFCommandHeader)), - container_blob_value, command_header->VMSize, command_header->VMSize); - - mm_free_ptr(blob.Leak().Leak()); - - 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.Leak().Leak()); - 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() { - return fBad == false; -} - -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}; -} - -ProcessID rtl_create_user_process(PEFLoader& exec, - const UserProcess::ExecutableKind& process_kind) { - if (!exec.IsLoaded()) return kSchedInvalidPID; - - auto errOrStart = exec.FindStart(); - - if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID; - - auto symname = exec.FindSymbol(kPefNameSymbol, kPefCode); - - if (!symname.Leak().Leak()) symname = ErrorOr{(VoidPtr) rt_alloc_string(kPefImageStart)}; - - if (!symname.Leak().Leak()) return kSchedInvalidPID; - - ProcessID id = - UserProcessScheduler::The().Spawn(reinterpret_cast(symname.Leak().Leak()), - errOrStart.Leak().Leak(), exec.GetBlob().Leak().Leak()); - - if (symname.Leak().Leak()) mm_free_ptr(symname.Leak().Leak()); - - if (id != kSchedInvalidPID) { - auto stacksym = exec.FindSymbol(kPefStackSizeSymbol, kPefData); - - if (!stacksym.Leak().Leak()) { - stacksym = ErrorOr{(VoidPtr) new UIntPtr(kSchedMaxStackSz)}; - } - - if (!stacksym.Leak().Leak()) { - UserProcessScheduler::The().Remove(id); - mm_free_ptr(stacksym.Leak().Leak()); - return kSchedInvalidPID; - } - - if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) { - *(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz; - } - - UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[id].Kind = process_kind; - UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[id].StackSize = - *(UIntPtr*) stacksym.Leak().Leak(); - - mm_free_ptr(stacksym.Leak().Leak()); - } - - return id; -} - -} // namespace Kernel diff --git a/src/kernel/src/PEFCodeMgr.cpp b/src/kernel/src/PEFCodeMgr.cpp new file mode 100644 index 00000000..3c2727e7 --- /dev/null +++ b/src/kernel/src/PEFCodeMgr.cpp @@ -0,0 +1,348 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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" +#define kPefImageStart "__ImageStart" + +namespace Kernel { + +namespace Detail { + /***********************************************************************************/ + /// @brief Get the PEF platform signature according to the compiled architecture. + /***********************************************************************************/ + static UInt32 ldr_get_platform(void) { +#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) { + if (fCachedBlob.Leak().Leak()) { + this->fBad = YES; + return; + } + + PEFContainer* container = reinterpret_cast(fCachedBlob.Leak().Leak()); + + 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.Leak().Leak()); + this->fCachedBlob.Leak().Leak() = 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); + + if (!fFile) { + this->fBad = YES; + return; + } + + 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.Leak().Leak()) { + kout << "PEFLoader: warning: Binary format error!\r"; + this->fBad = YES; + this->fFatBinary = NO; + + return; + } + + PEFContainer* container = reinterpret_cast(fCachedBlob.Leak().Leak()); + + 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.Leak().Leak()) mm_free_ptr(this->fCachedBlob.Leak().Leak()); + this->fCachedBlob.Leak().Leak() = nullptr; +} + +/***********************************************************************************/ +/// @brief PEF destructor. +/***********************************************************************************/ +PEFLoader::~PEFLoader() { + if (fCachedBlob.Leak().Leak()) { + mm_free_ptr(fCachedBlob.Leak().Leak()); + } +} + +/***********************************************************************************/ +/// @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.Leak().Leak() || fBad || !name) return ErrorOr{kErrorInvalidData}; + + auto blob = fFile->Read(name, sizeof(PEFCommandHeader)); + + if (!blob) return ErrorOr{kErrorInvalidData}; + + PEFContainer* container = reinterpret_cast(fCachedBlob.Leak().Leak()); + + if (!container) return ErrorOr{kErrorInvalidData}; + + PEFCommandHeader* command_header = reinterpret_cast(blob.Leak().Leak()); + + 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.Leak().Leak()); + blob.Leak().Leak() = nullptr; + + return ErrorOr{kErrorInvalidData}; + } + } + + Char* container_blob_value = new Char[command_header->VMSize]; + + rt_copy_memory_safe((VoidPtr) ((Char*) blob.Leak().Leak() + sizeof(PEFCommandHeader)), + container_blob_value, command_header->VMSize, command_header->VMSize); + + mm_free_ptr(blob.Leak().Leak()); + + 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.Leak().Leak()); + 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() { + return fBad == false; +} + +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}; +} + +ProcessID rtl_create_user_process(PEFLoader& exec, + const UserProcess::ExecutableKind& process_kind) { + if (!exec.IsLoaded()) return kSchedInvalidPID; + + auto errOrStart = exec.FindStart(); + + if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID; + + auto symname = exec.FindSymbol(kPefNameSymbol, kPefCode); + + if (!symname.Leak().Leak()) symname = ErrorOr{(VoidPtr) rt_alloc_string(kPefImageStart)}; + + if (!symname.Leak().Leak()) return kSchedInvalidPID; + + ProcessID id = + UserProcessScheduler::The().Spawn(reinterpret_cast(symname.Leak().Leak()), + errOrStart.Leak().Leak(), exec.GetBlob().Leak().Leak()); + + if (symname.Leak().Leak()) mm_free_ptr(symname.Leak().Leak()); + + if (id != kSchedInvalidPID) { + auto stacksym = exec.FindSymbol(kPefStackSizeSymbol, kPefData); + + if (!stacksym.Leak().Leak()) { + stacksym = ErrorOr{(VoidPtr) new UIntPtr(kSchedMaxStackSz)}; + } + + if (!stacksym.Leak().Leak()) { + UserProcessScheduler::The().Remove(id); + mm_free_ptr(stacksym.Leak().Leak()); + return kSchedInvalidPID; + } + + if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) { + *(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz; + } + + UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[id].Kind = process_kind; + UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[id].StackSize = + *(UIntPtr*) stacksym.Leak().Leak(); + + mm_free_ptr(stacksym.Leak().Leak()); + } + + return id; +} + +} // namespace Kernel diff --git a/src/kernel/src/PRDT.cc b/src/kernel/src/PRDT.cc deleted file mode 100644 index 21d86503..00000000 --- a/src/kernel/src/PRDT.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/PRDT.cpp b/src/kernel/src/PRDT.cpp new file mode 100644 index 00000000..21d86503 --- /dev/null +++ b/src/kernel/src/PRDT.cpp @@ -0,0 +1,20 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 8d0e9192..00000000 --- a/src/kernel/src/PageMgr.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/PageMgr.cpp b/src/kernel/src/PageMgr.cpp new file mode 100644 index 00000000..8d0e9192 --- /dev/null +++ b/src/kernel/src/PageMgr.cpp @@ -0,0 +1,93 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 88635518..00000000 --- a/src/kernel/src/Pmm.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include - -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/Pmm.cpp b/src/kernel/src/Pmm.cpp new file mode 100644 index 00000000..88635518 --- /dev/null +++ b/src/kernel/src/Pmm.cpp @@ -0,0 +1,74 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include + +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 deleted file mode 100644 index e6142b6f..00000000 --- a/src/kernel/src/Property.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/Property.cpp b/src/kernel/src/Property.cpp new file mode 100644 index 00000000..e6142b6f --- /dev/null +++ b/src/kernel/src/Property.cpp @@ -0,0 +1,39 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 90ed2dfb..00000000 --- a/src/kernel/src/Ref.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/Ref.cpp b/src/kernel/src/Ref.cpp new file mode 100644 index 00000000..90ed2dfb --- /dev/null +++ b/src/kernel/src/Ref.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/Semaphore.cc b/src/kernel/src/Semaphore.cc deleted file mode 100644 index 06325d0a..00000000 --- a/src/kernel/src/Semaphore.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include \ No newline at end of file diff --git a/src/kernel/src/Semaphore.cpp b/src/kernel/src/Semaphore.cpp new file mode 100644 index 00000000..06325d0a --- /dev/null +++ b/src/kernel/src/Semaphore.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include \ No newline at end of file diff --git a/src/kernel/src/SoftwareTimer.cc b/src/kernel/src/SoftwareTimer.cc deleted file mode 100644 index 6e89df81..00000000 --- a/src/kernel/src/SoftwareTimer.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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() { - if (fWaitFor < 1L) return NO; - - while (*fDigitalTimer < (*fDigitalTimer + fWaitFor)) { - ++(*fDigitalTimer); - } - - return YES; -} diff --git a/src/kernel/src/SoftwareTimer.cpp b/src/kernel/src/SoftwareTimer.cpp new file mode 100644 index 00000000..6e89df81 --- /dev/null +++ b/src/kernel/src/SoftwareTimer.cpp @@ -0,0 +1,34 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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() { + if (fWaitFor < 1L) 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 deleted file mode 100644 index 6d910f37..00000000 --- a/src/kernel/src/Storage/AHCIDeviceInterface.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { - -/// @brief Class constructor -/// @param Out Drive output -/// @param In Drive input -/// @param Cleanup Drive cleanup. -AHCIDeviceInterface::AHCIDeviceInterface(void (*out)(IDevice* self, IMountpoint* outpacket), - void (*in)(IDevice* self, IMountpoint* inpacket)) - : IDevice(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 kDeviceMgrRootDirPath "sd{}{}"; -} - -/// @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&) IDevice::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 AHCI. - 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&) IDevice::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; -} - -} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/Storage/AHCIDeviceInterface.cpp b/src/kernel/src/Storage/AHCIDeviceInterface.cpp new file mode 100644 index 00000000..6d910f37 --- /dev/null +++ b/src/kernel/src/Storage/AHCIDeviceInterface.cpp @@ -0,0 +1,87 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { + +/// @brief Class constructor +/// @param Out Drive output +/// @param In Drive input +/// @param Cleanup Drive cleanup. +AHCIDeviceInterface::AHCIDeviceInterface(void (*out)(IDevice* self, IMountpoint* outpacket), + void (*in)(IDevice* self, IMountpoint* inpacket)) + : IDevice(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 kDeviceMgrRootDirPath "sd{}{}"; +} + +/// @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&) IDevice::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 AHCI. + 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&) IDevice::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; +} + +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/Storage/ATADeviceInterface.cc b/src/kernel/src/Storage/ATADeviceInterface.cc deleted file mode 100644 index 98997579..00000000 --- a/src/kernel/src/Storage/ATADeviceInterface.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { - -/// @brief Class constructor -/// @param Out Drive output -/// @param In Drive input -/// @param Cleanup Drive cleanup. -ATADeviceInterface::ATADeviceInterface(void (*Out)(IDevice*, IMountpoint* outpacket), - void (*In)(IDevice*, IMountpoint* inpacket)) - : IDevice(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 kDeviceMgrRootDirPath "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&) IDevice::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&) IDevice::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; -} - -} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/Storage/ATADeviceInterface.cpp b/src/kernel/src/Storage/ATADeviceInterface.cpp new file mode 100644 index 00000000..98997579 --- /dev/null +++ b/src/kernel/src/Storage/ATADeviceInterface.cpp @@ -0,0 +1,96 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { + +/// @brief Class constructor +/// @param Out Drive output +/// @param In Drive input +/// @param Cleanup Drive cleanup. +ATADeviceInterface::ATADeviceInterface(void (*Out)(IDevice*, IMountpoint* outpacket), + void (*In)(IDevice*, IMountpoint* inpacket)) + : IDevice(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 kDeviceMgrRootDirPath "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&) IDevice::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&) IDevice::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; +} + +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/Storage/NVMEDeviceInterface.cc b/src/kernel/src/Storage/NVMEDeviceInterface.cc deleted file mode 100644 index dd8ca435..00000000 --- a/src/kernel/src/Storage/NVMEDeviceInterface.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -NVMEDeviceInterface::NVMEDeviceInterface(void (*out)(IDevice*, IMountpoint* outpacket), - void (*in)(IDevice*, IMountpoint* inpacket), - void (*cleanup)(void)) - : IDevice(out, in), fCleanup(cleanup) {} - -NVMEDeviceInterface::~NVMEDeviceInterface() { - if (fCleanup) fCleanup(); -} - -const Char* NVMEDeviceInterface::Name() const { - return kDeviceMgrRootDirPath "nvm{}"; -} - -OwnPtr NVMEDeviceInterface::operator()(UInt32 dma_low, UInt32 dma_high, - SizeT dma_sz) { - NE_UNUSED(dma_low); - NE_UNUSED(dma_high); - NE_UNUSED(dma_sz); - - return {}; -} -} // namespace Kernel diff --git a/src/kernel/src/Storage/NVMEDeviceInterface.cpp b/src/kernel/src/Storage/NVMEDeviceInterface.cpp new file mode 100644 index 00000000..dd8ca435 --- /dev/null +++ b/src/kernel/src/Storage/NVMEDeviceInterface.cpp @@ -0,0 +1,29 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { +NVMEDeviceInterface::NVMEDeviceInterface(void (*out)(IDevice*, IMountpoint* outpacket), + void (*in)(IDevice*, IMountpoint* inpacket), + void (*cleanup)(void)) + : IDevice(out, in), fCleanup(cleanup) {} + +NVMEDeviceInterface::~NVMEDeviceInterface() { + if (fCleanup) fCleanup(); +} + +const Char* NVMEDeviceInterface::Name() const { + return kDeviceMgrRootDirPath "nvm{}"; +} + +OwnPtr NVMEDeviceInterface::operator()(UInt32 dma_low, UInt32 dma_high, + SizeT dma_sz) { + NE_UNUSED(dma_low); + NE_UNUSED(dma_high); + NE_UNUSED(dma_sz); + + return {}; +} +} // namespace Kernel diff --git a/src/kernel/src/Storage/SCSIDeviceInterface.cc b/src/kernel/src/Storage/SCSIDeviceInterface.cc deleted file mode 100644 index e82f7006..00000000 --- a/src/kernel/src/Storage/SCSIDeviceInterface.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -SCSIDeviceInterface::SCSIDeviceInterface(void (*out)(IDevice*, IMountpoint* outpacket), - void (*in)(IDevice*, IMountpoint* inpacket), - void (*cleanup)(void)) - : IDevice(out, in), fCleanup(cleanup) {} - -SCSIDeviceInterface::~SCSIDeviceInterface() { - if (fCleanup) fCleanup(); -} - -const Char* SCSIDeviceInterface::Name() const { - return kDeviceMgrRootDirPath "sd{}{}"; -} -} // namespace Kernel diff --git a/src/kernel/src/Storage/SCSIDeviceInterface.cpp b/src/kernel/src/Storage/SCSIDeviceInterface.cpp new file mode 100644 index 00000000..e82f7006 --- /dev/null +++ b/src/kernel/src/Storage/SCSIDeviceInterface.cpp @@ -0,0 +1,20 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { +SCSIDeviceInterface::SCSIDeviceInterface(void (*out)(IDevice*, IMountpoint* outpacket), + void (*in)(IDevice*, IMountpoint* inpacket), + void (*cleanup)(void)) + : IDevice(out, in), fCleanup(cleanup) {} + +SCSIDeviceInterface::~SCSIDeviceInterface() { + if (fCleanup) fCleanup(); +} + +const Char* SCSIDeviceInterface::Name() const { + return kDeviceMgrRootDirPath "sd{}{}"; +} +} // namespace Kernel diff --git a/src/kernel/src/Stream.cc b/src/kernel/src/Stream.cc deleted file mode 100644 index 0765e206..00000000 --- a/src/kernel/src/Stream.cc +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include diff --git a/src/kernel/src/Stream.cpp b/src/kernel/src/Stream.cpp new file mode 100644 index 00000000..0765e206 --- /dev/null +++ b/src/kernel/src/Stream.cpp @@ -0,0 +1,5 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include diff --git a/src/kernel/src/Swap/DiskSwap.cc b/src/kernel/src/Swap/DiskSwap.cc deleted file mode 100644 index 7a926dc5..00000000 --- a/src/kernel/src/Swap/DiskSwap.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include - -namespace Kernel { - -STATIC constexpr UInt32 kSwapDiskHeaderMagic = 0x44535750; // 'DSWP' - -/***********************************************************************************/ -/// @brief Write memory chunk onto disk. -/// @param data the data packet. -/***********************************************************************************/ -Int64 IDiskSwap::Write(SwapDiskHdrPtr data) { - if (!data || data->fMagic != kSwapDiskHeaderMagic) return 0UL; - - FileStream file(kSwapPageFilePath, kRestrictWRB); - - Ref ret = file.Write(data->fOffset, data, sizeof(SwapDiskHdr) + data->fBlobSz); - - return ret.Leak(); -} - -/***********************************************************************************/ -/// @brief Read memory chunk from disk. -/// @param data the data packet length. -/// @return Whether the swap was fetched to disk, or not. -/***********************************************************************************/ -SwapDiskHdrPtr IDiskSwap::Read(const UIntPtr offset, SizeT data_len) { - if (data_len == 0UL) return nullptr; - - FileStream file(kSwapPageFilePath, kRestrictRB); - - ErrorOrAny blob = file.Read(offset, sizeof(SwapDiskHdr) + data_len); - - if (blob.HasError() || - (static_cast(blob.Leak().Leak()))->fMagic != kSwapDiskHeaderMagic) { - if (!blob.HasError()) mm_free_ptr(blob.Leak().Leak()); - return nullptr; - } - - return reinterpret_cast(blob.Leak().Leak()); -} - -} // namespace Kernel diff --git a/src/kernel/src/Swap/DiskSwap.cpp b/src/kernel/src/Swap/DiskSwap.cpp new file mode 100644 index 00000000..7a926dc5 --- /dev/null +++ b/src/kernel/src/Swap/DiskSwap.cpp @@ -0,0 +1,47 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include + +namespace Kernel { + +STATIC constexpr UInt32 kSwapDiskHeaderMagic = 0x44535750; // 'DSWP' + +/***********************************************************************************/ +/// @brief Write memory chunk onto disk. +/// @param data the data packet. +/***********************************************************************************/ +Int64 IDiskSwap::Write(SwapDiskHdrPtr data) { + if (!data || data->fMagic != kSwapDiskHeaderMagic) return 0UL; + + FileStream file(kSwapPageFilePath, kRestrictWRB); + + Ref ret = file.Write(data->fOffset, data, sizeof(SwapDiskHdr) + data->fBlobSz); + + return ret.Leak(); +} + +/***********************************************************************************/ +/// @brief Read memory chunk from disk. +/// @param data the data packet length. +/// @return Whether the swap was fetched to disk, or not. +/***********************************************************************************/ +SwapDiskHdrPtr IDiskSwap::Read(const UIntPtr offset, SizeT data_len) { + if (data_len == 0UL) return nullptr; + + FileStream file(kSwapPageFilePath, kRestrictRB); + + ErrorOrAny blob = file.Read(offset, sizeof(SwapDiskHdr) + data_len); + + if (blob.HasError() || + (static_cast(blob.Leak().Leak()))->fMagic != kSwapDiskHeaderMagic) { + if (!blob.HasError()) mm_free_ptr(blob.Leak().Leak()); + return nullptr; + } + + return reinterpret_cast(blob.Leak().Leak()); +} + +} // namespace Kernel diff --git a/src/kernel/src/ThreadLocalStorage.cc b/src/kernel/src/ThreadLocalStorage.cc deleted file mode 100644 index 7168c749..00000000 --- a/src/kernel/src/ThreadLocalStorage.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include -#include - -/***********************************************************************************/ -/// @bugs: 0 -/// @file ThreadLocalStorage.cc -/// @brief NeKernel Thread Local Storage. -///! @author Amlal El Mahrouss (amlal@nekernel.org) -/***********************************************************************************/ - -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) { - MUST_PASS(tib_ptr); - if (!tib_ptr) return false; - - return tib_ptr->Cookie[kCookieMag0Idx] == kCookieMag0 && - tib_ptr->Cookie[kCookieMag1Idx] == kCookieMag1 && - tib_ptr->Cookie[kCookieMag2Idx] == kCookieMag2; -} - -} // namespace Kernel - -/** - * @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) { - MUST_PASS(tib_ptr); - if (!tib_ptr) { - kout << "TLS: Failed because of an invalid TIB...\r"; - return No; - } - - THREAD_INFORMATION_BLOCK* tib = static_cast(tib_ptr); - return Kernel::tls_check_tib(tib); -} diff --git a/src/kernel/src/ThreadLocalStorage.cpp b/src/kernel/src/ThreadLocalStorage.cpp new file mode 100644 index 00000000..7168c749 --- /dev/null +++ b/src/kernel/src/ThreadLocalStorage.cpp @@ -0,0 +1,50 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include +#include + +/***********************************************************************************/ +/// @bugs: 0 +/// @file ThreadLocalStorage.cc +/// @brief NeKernel Thread Local Storage. +///! @author Amlal El Mahrouss (amlal@nekernel.org) +/***********************************************************************************/ + +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) { + MUST_PASS(tib_ptr); + if (!tib_ptr) return false; + + return tib_ptr->Cookie[kCookieMag0Idx] == kCookieMag0 && + tib_ptr->Cookie[kCookieMag1Idx] == kCookieMag1 && + tib_ptr->Cookie[kCookieMag2Idx] == kCookieMag2; +} + +} // namespace Kernel + +/** + * @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) { + MUST_PASS(tib_ptr); + if (!tib_ptr) { + kout << "TLS: Failed because of an invalid TIB...\r"; + return No; + } + + THREAD_INFORMATION_BLOCK* tib = static_cast(tib_ptr); + return Kernel::tls_check_tib(tib); +} diff --git a/src/kernel/src/Timer.cc b/src/kernel/src/Timer.cc deleted file mode 100644 index 20efcc43..00000000 --- a/src/kernel/src/Timer.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -///! BUGS: 0 -///! @file Timer.cc -///! @brief Software Timer implementation -///! @author Amlal El Mahrouss (amlal@nekernel.org) - -namespace Kernel { -/// @brief Unimplemented as it is an interface. -BOOL ITimer::Wait() { - return NO; -} -} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/Timer.cpp b/src/kernel/src/Timer.cpp new file mode 100644 index 00000000..20efcc43 --- /dev/null +++ b/src/kernel/src/Timer.cpp @@ -0,0 +1,17 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +///! BUGS: 0 +///! @file Timer.cc +///! @brief Software Timer implementation +///! @author Amlal El Mahrouss (amlal@nekernel.org) + +namespace Kernel { +/// @brief Unimplemented as it is an interface. +BOOL ITimer::Wait() { + return NO; +} +} // namespace Kernel \ No newline at end of file diff --git a/src/kernel/src/UserMgr+User.cc b/src/kernel/src/UserMgr+User.cc deleted file mode 100644 index f46a5590..00000000 --- a/src/kernel/src/UserMgr+User.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include -#include -#include -#include -#include -#include - -/// @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) { - kout << "user_fnv_generator: Try hashing user password...\r"; - - if (!password || !user) return 0; - if (*password == 0) return 0; - - const UInt64 kFnvOffsetBasis = 0xcbf29ce484222325ULL; - const UInt64 kFnvPrime = 0x100000001b3ULL; - - UInt64 hash = kFnvOffsetBasis; - - while (*password) { - hash ^= (Char) (*password++); - hash *= kFnvPrime; - } - - 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) { - 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) { - 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() { - return this->mUserName; -} - -//////////////////////////////////////////////////////////// -/// @brief Returns the user's ring. -/// @return The king of ring the user is attached to. -//////////////////////////////////////////////////////////// - -const UserRingKind& User::Ring() { - return this->mUserRing; -} - -Bool User::IsStdUser() { - return this->Ring() == UserRingKind::kRingStdUser; -} - -Bool User::IsSuperUser() { - return this->Ring() == UserRingKind::kRingSuperUser; -} - -} // namespace Kernel diff --git a/src/kernel/src/UserMgr+User.cpp b/src/kernel/src/UserMgr+User.cpp new file mode 100644 index 00000000..f46a5590 --- /dev/null +++ b/src/kernel/src/UserMgr+User.cpp @@ -0,0 +1,122 @@ +// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include +#include +#include +#include +#include +#include + +/// @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) { + kout << "user_fnv_generator: Try hashing user password...\r"; + + if (!password || !user) return 0; + if (*password == 0) return 0; + + const UInt64 kFnvOffsetBasis = 0xcbf29ce484222325ULL; + const UInt64 kFnvPrime = 0x100000001b3ULL; + + UInt64 hash = kFnvOffsetBasis; + + while (*password) { + hash ^= (Char) (*password++); + hash *= kFnvPrime; + } + + 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) { + 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) { + 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() { + return this->mUserName; +} + +//////////////////////////////////////////////////////////// +/// @brief Returns the user's ring. +/// @return The king of ring the user is attached to. +//////////////////////////////////////////////////////////// + +const UserRingKind& User::Ring() { + return this->mUserRing; +} + +Bool User::IsStdUser() { + return this->Ring() == UserRingKind::kRingStdUser; +} + +Bool User::IsSuperUser() { + return this->Ring() == UserRingKind::kRingSuperUser; +} + +} // namespace Kernel diff --git a/src/kernel/src/UserProcessScheduler.cc b/src/kernel/src/UserProcessScheduler.cc deleted file mode 100644 index 07dd4eaa..00000000 --- a/src/kernel/src/UserProcessScheduler.cc +++ /dev/null @@ -1,671 +0,0 @@ -// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -///! BUG COUNT: 0 - -namespace Kernel { - -UserProcess::UserProcess() = default; -UserProcess::~UserProcess() = default; - -/// @brief Gets the last exit code. -/// @note Not thread-safe. -/// @return Int32 the last exit code. - -/***********************************************************************************/ -/// @brief Crashes the current process. -/***********************************************************************************/ - -Void UserProcess::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. -/***********************************************************************************/ - -UserProcess::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& UserProcess::GetExitCode() { - return this->LastExitCode; -} - -/***********************************************************************************/ -/// @brief Error code variable getter. -/***********************************************************************************/ - -KPCError& UserProcess::GetLocalCode() { - return this->LocalCode; -} - -/***********************************************************************************/ -/// @brief Wakes process header. -/// @param should_wakeup if the program shall wakeup or not. -/***********************************************************************************/ - -Void UserProcess::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; - } - - if (tree) { - if (tree->Parent) tree = tree->Parent; - - auto tree_tmp = tree->Next; - - if (!tree_tmp) { - return tree; - } - - return tree_tmp; - } - - return tree; -} - -/***********************************************************************************/ -/** @brief Allocate pointer to heap/file tree. */ -/***********************************************************************************/ - -ErrorOr UserProcess::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 ProcessHeapTree(); - - if (!this->HeapTree) { - this->Crash(); - return ErrorOr(-kErrorHeapOutOfMemory); - } - - this->HeapTree->EntryPad = pad_amount; - this->HeapTree->EntrySize = sz; - - this->HeapTree->Entry = ptr; - - this->HeapTree->Color = TreeKind::kBlackTreeKind; - - this->HeapTree->Prev = nullptr; - this->HeapTree->Next = nullptr; - this->HeapTree->Parent = nullptr; - this->HeapTree->Child = nullptr; - } else { - ProcessHeapTree* entry = this->HeapTree; - ProcessHeapTree* 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 == TreeKind::kBlackTreeKind) break; - } - } - - auto new_entry = new ProcessHeapTree(); - - 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 = TreeKind::kBlackTreeKind; - prev_entry->Color = TreeKind::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* UserProcess::GetName() { - return this->Name; -} - -/***********************************************************************************/ -/// @brief Gets the owner of the process. -/***********************************************************************************/ - -const Ref UserProcess::GetOwner() { - return {this->Owner}; -} - -/// @brief Parent team getter. -UserProcessTeam* UserProcess::GetParentTeam() { - return this->Parent; -} - -/***********************************************************************************/ -/** @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 UserProcess::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 == ExecutableKind::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->Parent->mProcessCur; -} - -/***********************************************************************************/ -/// @brief Initializer dylib of User Process. -/***********************************************************************************/ - -Bool UserProcess::InitDylib() { - // React according to the process's kind. - switch (this->Kind) { - case ExecutableKind::kExecutableDylibKind: { - this->DylibDelegate = rtl_init_dylib_pef(*this); - - if (!this->DylibDelegate) { - this->Crash(); - return NO; - } - - return YES; - } - case ExecutableKind::kExecutableKind: { - return NO; - } - default: { - break; - } - } - - (Void)(kout << "Unknown process kind: " << hex_number(static_cast(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; - - UserProcess& 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); - -#if defined(__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.Parent = &mTeam; - - process.ProcessId = pid; - process.Status = ProcessStatusKind::kRunning; - process.PTime = 0; - process.UTime = 0; - process.RTime = 0; - - if (!process.FileTree) { - process.FileTree = new ProcessFileTree(); - - if (!process.FileTree) { - process.Crash(); - return -kErrorHeapOutOfMemory; - } - - /// @todo File Tree allocation and dispose methods (amlal) - } - - (Void)(kout << "ProcessCur: " << number(this->mTeam.mProcessCur) << kendl); - (Void)(kout << "ProcessID: " << number(pid) << 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 UserProcess count executed within a team. -/***********************************************************************************/ - -SizeT UserProcessScheduler::Run() { - 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 scheduled to 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 -Ref 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 -UserProcess& UserProcessScheduler::TheCurrentProcess() { - return mTeam.mCurrentProcess.Leak(); -} - -/// @brief Current proccess id getter. -/// @return UserProcess ID integer. -ErrorOr UserProcessHelper::TheCurrentPID() { - if (!UserProcessScheduler::The().TheCurrentProcess()) - return ErrorOr{-kErrorProcessFault}; - - kout << "UserProcessHelper::TheCurrentPID: Leaking ProcessId...\r"; - return ErrorOr{UserProcessScheduler::The().TheCurrentProcess().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 UserProcess& 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() == ThreadKind::kAPInvalid || - HardwareThreadScheduler::The()[index].Leak()->Kind() == ThreadKind::kAPBoot) - continue; - - (Void)(kout << "AP_" << hex_number(index)); - - if (HardwareThreadScheduler::The()[index].Leak()->IsBusy()) { - kout << ": is busy\r"; - - continue; - } - - (Void)(kout << kendl); - - 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; - - (Void)(kout << "AP_" << hex_number(index)); - kout << " is now running a new task!\r"; - - UserProcessHelper::TheCurrentPID().Leak().Leak() = new_pid; - - HardwareThreadScheduler::The()[index].Leak()->fPTime = - UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[new_pid].PTime; - - 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/UserProcessScheduler.cpp b/src/kernel/src/UserProcessScheduler.cpp new file mode 100644 index 00000000..07dd4eaa --- /dev/null +++ b/src/kernel/src/UserProcessScheduler.cpp @@ -0,0 +1,671 @@ +// Copyright 2024-2026, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///! BUG COUNT: 0 + +namespace Kernel { + +UserProcess::UserProcess() = default; +UserProcess::~UserProcess() = default; + +/// @brief Gets the last exit code. +/// @note Not thread-safe. +/// @return Int32 the last exit code. + +/***********************************************************************************/ +/// @brief Crashes the current process. +/***********************************************************************************/ + +Void UserProcess::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. +/***********************************************************************************/ + +UserProcess::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& UserProcess::GetExitCode() { + return this->LastExitCode; +} + +/***********************************************************************************/ +/// @brief Error code variable getter. +/***********************************************************************************/ + +KPCError& UserProcess::GetLocalCode() { + return this->LocalCode; +} + +/***********************************************************************************/ +/// @brief Wakes process header. +/// @param should_wakeup if the program shall wakeup or not. +/***********************************************************************************/ + +Void UserProcess::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; + } + + if (tree) { + if (tree->Parent) tree = tree->Parent; + + auto tree_tmp = tree->Next; + + if (!tree_tmp) { + return tree; + } + + return tree_tmp; + } + + return tree; +} + +/***********************************************************************************/ +/** @brief Allocate pointer to heap/file tree. */ +/***********************************************************************************/ + +ErrorOr UserProcess::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 ProcessHeapTree(); + + if (!this->HeapTree) { + this->Crash(); + return ErrorOr(-kErrorHeapOutOfMemory); + } + + this->HeapTree->EntryPad = pad_amount; + this->HeapTree->EntrySize = sz; + + this->HeapTree->Entry = ptr; + + this->HeapTree->Color = TreeKind::kBlackTreeKind; + + this->HeapTree->Prev = nullptr; + this->HeapTree->Next = nullptr; + this->HeapTree->Parent = nullptr; + this->HeapTree->Child = nullptr; + } else { + ProcessHeapTree* entry = this->HeapTree; + ProcessHeapTree* 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 == TreeKind::kBlackTreeKind) break; + } + } + + auto new_entry = new ProcessHeapTree(); + + 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 = TreeKind::kBlackTreeKind; + prev_entry->Color = TreeKind::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* UserProcess::GetName() { + return this->Name; +} + +/***********************************************************************************/ +/// @brief Gets the owner of the process. +/***********************************************************************************/ + +const Ref UserProcess::GetOwner() { + return {this->Owner}; +} + +/// @brief Parent team getter. +UserProcessTeam* UserProcess::GetParentTeam() { + return this->Parent; +} + +/***********************************************************************************/ +/** @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 UserProcess::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 == ExecutableKind::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->Parent->mProcessCur; +} + +/***********************************************************************************/ +/// @brief Initializer dylib of User Process. +/***********************************************************************************/ + +Bool UserProcess::InitDylib() { + // React according to the process's kind. + switch (this->Kind) { + case ExecutableKind::kExecutableDylibKind: { + this->DylibDelegate = rtl_init_dylib_pef(*this); + + if (!this->DylibDelegate) { + this->Crash(); + return NO; + } + + return YES; + } + case ExecutableKind::kExecutableKind: { + return NO; + } + default: { + break; + } + } + + (Void)(kout << "Unknown process kind: " << hex_number(static_cast(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; + + UserProcess& 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); + +#if defined(__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.Parent = &mTeam; + + process.ProcessId = pid; + process.Status = ProcessStatusKind::kRunning; + process.PTime = 0; + process.UTime = 0; + process.RTime = 0; + + if (!process.FileTree) { + process.FileTree = new ProcessFileTree(); + + if (!process.FileTree) { + process.Crash(); + return -kErrorHeapOutOfMemory; + } + + /// @todo File Tree allocation and dispose methods (amlal) + } + + (Void)(kout << "ProcessCur: " << number(this->mTeam.mProcessCur) << kendl); + (Void)(kout << "ProcessID: " << number(pid) << 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 UserProcess count executed within a team. +/***********************************************************************************/ + +SizeT UserProcessScheduler::Run() { + 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 scheduled to 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 +Ref 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 +UserProcess& UserProcessScheduler::TheCurrentProcess() { + return mTeam.mCurrentProcess.Leak(); +} + +/// @brief Current proccess id getter. +/// @return UserProcess ID integer. +ErrorOr UserProcessHelper::TheCurrentPID() { + if (!UserProcessScheduler::The().TheCurrentProcess()) + return ErrorOr{-kErrorProcessFault}; + + kout << "UserProcessHelper::TheCurrentPID: Leaking ProcessId...\r"; + return ErrorOr{UserProcessScheduler::The().TheCurrentProcess().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 UserProcess& 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() == ThreadKind::kAPInvalid || + HardwareThreadScheduler::The()[index].Leak()->Kind() == ThreadKind::kAPBoot) + continue; + + (Void)(kout << "AP_" << hex_number(index)); + + if (HardwareThreadScheduler::The()[index].Leak()->IsBusy()) { + kout << ": is busy\r"; + + continue; + } + + (Void)(kout << kendl); + + 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; + + (Void)(kout << "AP_" << hex_number(index)); + kout << " is now running a new task!\r"; + + UserProcessHelper::TheCurrentPID().Leak().Leak() = new_pid; + + HardwareThreadScheduler::The()[index].Leak()->fPTime = + UserProcessScheduler::The().TheCurrentTeam().Leak().AsArray()[new_pid].PTime; + + 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 deleted file mode 100644 index 1f80f0e1..00000000 --- a/src/kernel/src/UserProcessTeam.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -UserProcessTeam::UserProcessTeam() { - for (SizeT i = 0U; i < this->mProcessList.Count(); ++i) { - this->mProcessList[i] = UserProcess{}; - this->mProcessList[i].PTime = 0; - this->mProcessList[i].RTime = 0; - this->mProcessList[i].UTime = 0; - this->mProcessList[i].Status = ProcessStatusKind::kKilled; - - auto team{this->mProcessList[i].GetParentTeam()}; - team = this; - (void) team; - } - - 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() { - 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/UserProcessTeam.cpp b/src/kernel/src/UserProcessTeam.cpp new file mode 100644 index 00000000..1f80f0e1 --- /dev/null +++ b/src/kernel/src/UserProcessTeam.cpp @@ -0,0 +1,52 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { +UserProcessTeam::UserProcessTeam() { + for (SizeT i = 0U; i < this->mProcessList.Count(); ++i) { + this->mProcessList[i] = UserProcess{}; + this->mProcessList[i].PTime = 0; + this->mProcessList[i].RTime = 0; + this->mProcessList[i].UTime = 0; + this->mProcessList[i].Status = ProcessStatusKind::kKilled; + + auto team{this->mProcessList[i].GetParentTeam()}; + team = this; + (void) team; + } + + 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() { + 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 deleted file mode 100644 index d54ab25f..00000000 --- a/src/kernel/src/UtfUtils.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#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/UtfUtils.cpp b/src/kernel/src/UtfUtils.cpp new file mode 100644 index 00000000..d54ab25f --- /dev/null +++ b/src/kernel/src/UtfUtils.cpp @@ -0,0 +1,60 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#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 deleted file mode 100644 index 0422202a..00000000 --- a/src/kernel/src/Variant.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include - -namespace Kernel { -KString 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 ("Memory:{Blob}"); - case VariantKind::kNull: - return ("Memory:{Null}"); - case VariantKind::kSwap: - return ("Memory:{Swap}"); - default: - return ("Memory:{Unknown}"); - } -} - -/// @brief Return variant's kind. -Variant::VariantKind& Variant::Kind() { - return this->fKind; -} - -/// @brief Leak variant's instance. -RefAny Variant::Leak() { - return {this->fPtr}; -} -} // namespace Kernel diff --git a/src/kernel/src/Variant.cpp b/src/kernel/src/Variant.cpp new file mode 100644 index 00000000..0422202a --- /dev/null +++ b/src/kernel/src/Variant.cpp @@ -0,0 +1,36 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include + +namespace Kernel { +KString 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 ("Memory:{Blob}"); + case VariantKind::kNull: + return ("Memory:{Null}"); + case VariantKind::kSwap: + return ("Memory:{Swap}"); + default: + return ("Memory:{Unknown}"); + } +} + +/// @brief Return variant's kind. +Variant::VariantKind& Variant::Kind() { + return this->fKind; +} + +/// @brief Leak variant's instance. +RefAny Variant::Leak() { + return {this->fPtr}; +} +} // namespace Kernel diff --git a/src/kernel/src/ZXDCodeMgr.cc b/src/kernel/src/ZXDCodeMgr.cc deleted file mode 100644 index c8e8041e..00000000 --- a/src/kernel/src/ZXDCodeMgr.cc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) -// Licensed under the Apache License, Version 2.0 (see LICENSE file) -// Official repository: https://github.com/nekernel-org/nekernel - -#include -#include diff --git a/src/kernel/src/ZXDCodeMgr.cpp b/src/kernel/src/ZXDCodeMgr.cpp new file mode 100644 index 00000000..c8e8041e --- /dev/null +++ b/src/kernel/src/ZXDCodeMgr.cpp @@ -0,0 +1,6 @@ +// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org) +// Licensed under the Apache License, Version 2.0 (see LICENSE file) +// Official repository: https://github.com/nekernel-org/nekernel + +#include +#include -- cgit v1.2.3