From 9fa97919b3b1275298e453854afd72cc8d6c0188 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Mon, 6 Jul 2026 02:58:03 +0200 Subject: [PATCH] implementing foundations of xeon phi daemon --- .clang-format | 295 ++++++++++++++++++++++++++++++ README.md | 201 ++++++++++++++++++++- build.zig | 327 ++++++++++++++++++++++------------ compile_flags.txt | 5 + src/phi/PhiDevice.zig | 8 + src/phi/PhiDeviceMemory.zig | 49 ++++- src/phi/PhiInstance.zig | 2 +- src/phi/PhiPhysicalDevice.zig | 12 +- src/phi/PhiTransport.zig | 155 ++++++++++++++++ src/phi/lib.zig | 5 + src/phi/mic/Daemon.c | 193 ++++++++++++++++++++ src/phi/mic/Daemon.h | 20 +++ src/phi/mic/Logger.c | 93 ++++++++++ src/phi/mic/Logger.h | 24 +++ src/phi/mic/Memory.c | 73 ++++++++ src/phi/mic/Memory.h | 9 + src/phi/mic/main.c | 32 ++++ src/phi/scif.zig | 77 ++++++++ src/phi/shared/Protocol.h | 84 +++++++++ 19 files changed, 1541 insertions(+), 123 deletions(-) create mode 100644 .clang-format create mode 100644 compile_flags.txt create mode 100644 src/phi/PhiTransport.zig create mode 100644 src/phi/mic/Daemon.c create mode 100644 src/phi/mic/Daemon.h create mode 100644 src/phi/mic/Logger.c create mode 100644 src/phi/mic/Logger.h create mode 100644 src/phi/mic/Memory.c create mode 100644 src/phi/mic/Memory.h create mode 100644 src/phi/mic/main.c create mode 100644 src/phi/scif.zig create mode 100644 src/phi/shared/Protocol.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c56178a --- /dev/null +++ b/.clang-format @@ -0,0 +1,295 @@ +--- +BasedOnStyle: Mozilla +AccessModifierOffset: 0 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: false +BinPackLongBracedList: true +BinPackParameters: OnePerLine +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Never + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: true + IndentBraces: true + SplitEmptyFunction: true + SplitEmptyRecord: false + SplitEmptyNamespace: true +BracedInitializerIndentWidth: -1 +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTemplateCloser: false +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeComma +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeComma +BreakStringLiterals: true +BreakTemplateDeclarations: Yes +ColumnLimit: 128 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +EnumTrailingComma: Leave +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: ^"(llvm|llvm-c|clang|clang-c)/ + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: ^(<|"(gtest|gmock|isl|json)/) + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: .* + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: (Test)?$ +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseBlocks: true +IndentCaseLabels: true +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: true + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: "" +MacroBlockEnd: "" +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: false +OneLineFormatOffRegex: "" +PPIndentWidth: -1 +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: + Enabled: true + IgnoreCase: false +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterOperatorKeyword: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: Never +SpaceBeforeParensOptions: + AfterControlStatements: false + AfterForeachMacros: false + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterNot: false + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: false +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InConditionalStatements: false + InCStyleCasts: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +TableGenBreakInsideDAGArg: DontBreak +UseTab: ForIndentation +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave diff --git a/README.md b/README.md index 3f242e3..1bf5871 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ To understand Vulkan - not as a humble API mere mortals call upon, but as a laby It does not seek to produce a performant or production-worthy driver. \ *The gods are merciful, but not that merciful.* -## Soft [software implementation] +## Soft [software] Soft be a software implementation of the Vulkan specification, abiding within this driver's own codebase.\ It maketh use of a bespoke [SPIR-V interpreter](https://git.kbz8.me/kbz_8/SPIRV-Interpreter) and renderer, by whose workings its labours are carried forth. @@ -218,6 +218,205 @@ vkWaitForFences | ✅ Implemented [Here](https://vulkan-driver.kbz8.me/cts/soft/) shalt thou find a most meticulous account of the Vulkan 1.0 conformance trials, set forth for thy scrutiny. +## Phi [Xeon Phi KNC] + +Phi be an implementation of the Vulkan specification, wrought for the Xeon Phi Knights Corner cards. +Whether the Knights Landing cards shall one day receive the same providence remaineth to be seen. + +### Build + +If thou art truly determined: +``` +zig build phi --release=[fast|safe|small] +``` + +And should thou seek additional build options with: +``` +zig build --help +``` + +The same rites apply as for the Soft build. + +#### Vulkan 1.0 specification +
+ + Here standeth the present reckoning of thy Vulkan 1.0 implementation + + +\ +⚠️ Implemented, yet perchance not fully tested nor proven conformant, but rather working in a manner most general to thee and thine.\ +Assume thou that functions lacking in this array are, for now, not intended to be wrought. + +Name | Status +-------------------------------------------------|-------- +vkAcquireNextImage2KHR | ⚙️ WIP +vkAcquireNextImageKHR | ⚙️ WIP +vkAllocateCommandBuffers | ⚙️ WIP +vkAllocateDescriptorSets | ⚙️ WIP +vkAllocateMemory | ✅ Implemented +vkBeginCommandBuffer | ⚙️ WIP +vkBindBufferMemory | ⚙️ WIP +vkBindImageMemory | ⚙️ WIP +vkCmdBeginQuery | ⚙️ WIP +vkCmdBeginRenderPass | ⚙️ WIP +vkCmdBindDescriptorSets | ⚙️ WIP +vkCmdBindIndexBuffer | ⚙️ WIP +vkCmdBindPipeline | ⚙️ WIP +vkCmdBindVertexBuffers | ⚙️ WIP +vkCmdBlitImage | ⚙️ WIP +vkCmdClearAttachments | ⚙️ WIP +vkCmdClearColorImage | ⚙️ WIP +vkCmdClearDepthStencilImage | ⚙️ WIP +vkCmdCopyBuffer | ⚙️ WIP +vkCmdCopyBufferToImage | ⚙️ WIP +vkCmdCopyImage | ⚙️ WIP +vkCmdCopyImageToBuffer | ⚙️ WIP +vkCmdCopyQueryPoolResults | ⚙️ WIP +vkCmdDispatch | ⚙️ WIP +vkCmdDispatchBaseKHR | ⚙️ WIP +vkCmdDispatchIndirect | ⚙️ WIP +vkCmdDraw | ⚙️ WIP +vkCmdDrawIndexed | ⚙️ WIP +vkCmdDrawIndexedIndirect | ⚙️ WIP +vkCmdDrawIndirect | ⚙️ WIP +vkCmdEndQuery | ⚙️ WIP +vkCmdEndRenderPass | ⚙️ WIP +vkCmdExecuteCommands | ⚙️ WIP +vkCmdFillBuffer | ⚙️ WIP +vkCmdNextSubpass | ⚙️ WIP +vkCmdPipelineBarrier | ⚙️ WIP +vkCmdPushConstants | ⚙️ WIP +vkCmdResetEvent | ⚙️ WIP +vkCmdResetQueryPool | ⚙️ WIP +vkCmdResolveImage | ⚙️ WIP +vkCmdSetBlendConstants | ⚙️ WIP +vkCmdSetDepthBias | ⚙️ WIP +vkCmdSetDepthBounds | ⚙️ WIP +vkCmdSetDeviceMaskKHR | ⚙️ WIP +vkCmdSetEvent | ⚙️ WIP +vkCmdSetLineWidth | ⚙️ WIP +vkCmdSetScissor | ⚙️ WIP +vkCmdSetStencilCompareMask | ⚙️ WIP +vkCmdSetStencilReference | ⚙️ WIP +vkCmdSetStencilWriteMask | ⚙️ WIP +vkCmdSetViewport | ⚙️ WIP +vkCmdUpdateBuffer | ⚙️ WIP +vkCmdWaitEvents | ⚙️ WIP +vkCmdWriteTimestamp | ⚙️ WIP +vkCreateBuffer | ⚙️ WIP +vkCreateBufferView | ⚙️ WIP +vkCreateCommandPool | ⚙️ WIP +vkCreateComputePipelines | ⚙️ WIP +vkCreateDescriptorPool | ⚙️ WIP +vkCreateDescriptorSetLayout | ⚙️ WIP +vkCreateDevice | ✅ Implemented +vkCreateEvent | ⚙️ WIP +vkCreateFence | ⚙️ WIP +vkCreateFramebuffer | ⚙️ WIP +vkCreateGraphicsPipelines | ⚙️ WIP +vkCreateImage | ⚙️ WIP +vkCreateImageView | ⚙️ WIP +vkCreateInstance | ✅ Implemented +vkCreatePipelineCache | ⚙️ WIP +vkCreatePipelineLayout | ⚙️ WIP +vkCreateQueryPool | ⚙️ WIP +vkCreateRenderPass | ⚙️ WIP +vkCreateSampler | ⚙️ WIP +vkCreateSemaphore | ⚙️ WIP +vkCreateShaderModule | ⚙️ WIP +vkCreateSwapchainKHR | ⚙️ WIP +vkCreateWaylandSurfaceKHR | ⚙️ WIP +vkCreateWin32SurfaceKHR | ⚙️ WIP +vkCreateXcbSurfaceKHR | ⚙️ WIP +vkCreateXlibSurfaceKHR | ⚙️ WIP +vkDestroyBuffer | ⚙️ WIP +vkDestroyBufferView | ⚙️ WIP +vkDestroyCommandPool | ⚙️ WIP +vkDestroyDescriptorPool | ⚙️ WIP +vkDestroyDescriptorSetLayout | ⚙️ WIP +vkDestroyDevice | ✅ Implemented +vkDestroyEvent | ⚙️ WIP +vkDestroyFence | ⚙️ WIP +vkDestroyFramebuffer | ⚙️ WIP +vkDestroyImage | ⚙️ WIP +vkDestroyImageView | ⚙️ WIP +vkDestroyInstance | ✅ Implemented +vkDestroyPipeline | ⚙️ WIP +vkDestroyPipelineCache | ⚙️ WIP +vkDestroyPipelineLayout | ⚙️ WIP +vkDestroyQueryPool | ⚙️ WIP +vkDestroyRenderPass | ⚙️ WIP +vkDestroySampler | ⚙️ WIP +vkDestroySemaphore | ⚙️ WIP +vkDestroyShaderModule | ⚙️ WIP +vkDestroySurfaceKHR | ⚙️ WIP +vkDestroySwapchainKHR | ⚙️ WIP +vkDeviceWaitIdle | ⚙️ WIP +vkEndCommandBuffer | ⚙️ WIP +vkEnumerateDeviceExtensionProperties | ⚙️ WIP +vkEnumerateDeviceLayerProperties | ⚙️ WIP +vkEnumerateInstanceExtensionProperties | ⚙️ WIP +vkEnumerateInstanceLayerProperties | ⚙️ WIP +vkEnumeratePhysicalDeviceGroupsKHR | ⚙️ WIP +vkEnumeratePhysicalDevices | ✅ Implemented +vkFlushMappedMemoryRanges | ⚙️ WIP +vkFreeCommandBuffers | ⚙️ WIP +vkFreeDescriptorSets | ⚙️ WIP +vkFreeMemory | ✅ Implemented +vkGetBufferDeviceAddress | ⚙️ WIP +vkGetBufferDeviceAddressEXT | ⚙️ WIP +vkGetBufferDeviceAddressKHR | ⚙️ WIP +vkGetBufferMemoryRequirements | ⚙️ WIP +vkGetDeviceGroupPeerMemoryFeaturesKHR | ⚙️ WIP +vkGetDeviceGroupPresentCapabilitiesKHR | ⚙️ WIP +vkGetDeviceGroupSurfacePresentModesKHR | ⚙️ WIP +vkGetDeviceMemoryCommitment | ⚙️ WIP +vkGetDeviceProcAddr | ⚙️ WIP +vkGetDeviceQueue | ⚙️ WIP +vkGetEventStatus | ⚙️ WIP +vkGetFenceStatus | ⚙️ WIP +vkGetImageMemoryRequirements | ⚙️ WIP +vkGetImageSparseMemoryRequirements | ⚙️ WIP +vkGetImageSubresourceLayout | ⚙️ WIP +vkGetInstanceProcAddr | ⚙️ WIP +vkGetPhysicalDeviceFeatures | ✅ Implemented +vkGetPhysicalDeviceFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceImageFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceMemoryProperties | ✅ Implemented +vkGetPhysicalDeviceProperties | ✅ Implemented +vkGetPhysicalDeviceQueueFamilyProperties | ⚙️ WIP +vkGetPhysicalDeviceSparseImageFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfaceFormatsKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfacePresentModesKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfaceSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceWaylandPresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP +vkGetPipelineCacheData | ⚙️ WIP +vkGetQueryPoolResults | ⚙️ WIP +vkGetRenderAreaGranularity | ⚙️ WIP +vkGetSwapchainImagesKHR | ⚙️ WIP +vkInvalidateMappedMemoryRanges | ⚙️ WIP +vkMapMemory | ⚙️ WIP +vkMergePipelineCaches | ⚙️ WIP +vkQueueBindSparse | ⚙️ WIP +vkQueuePresentKHR | ⚙️ WIP +vkQueueSubmit | ⚙️ WIP +vkQueueWaitIdle | ⚙️ WIP +vkResetCommandBuffer | ⚙️ WIP +vkResetCommandPool | ⚙️ WIP +vkResetDescriptorPool | ⚙️ WIP +vkResetEvent | ⚙️ WIP +vkResetFences | ⚙️ WIP +vkResetQueryPool | ⚙️ WIP +vkSetEvent | ⚙️ WIP +vkUnmapMemory | ⚙️ WIP +vkUpdateDescriptorSets | ⚙️ WIP +vkWaitForFences | ⚙️ WIP +
+ ## License Released unto the world as MIT for study, experimentation, and the occasional horrified whisper. diff --git a/build.zig b/build.zig index 23979c9..05250fb 100644 --- a/build.zig +++ b/build.zig @@ -218,120 +218,6 @@ pub fn build(b: *std.Build) !void { docs_step.dependOn(&install_docs.step); } -fn customApe( - b: *std.Build, - lib: *Step.Compile, - lib_mod: *std.Build.Module, - base_mod: *std.Build.Module, - vulkan: *std.Build.Module, - base_c_mod: *std.Build.Module, - target: std.Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, - use_llvm: bool, -) !void { - for (implementations) |impl| { - if (std.mem.eql(u8, impl.name, "ape")) - continue; - - const mod = b.createModule(.{ - .root_source_file = b.path(impl.root_source_file), - .target = target, - .optimize = optimize, - .imports = &.{ - .{ .name = "base", .module = base_mod }, - .{ .name = "vulkan", .module = vulkan }, - }, - }); - - if (impl.custom) |func| { - func(b, lib, mod, base_mod, vulkan, base_c_mod, target, optimize, use_llvm) catch continue; - } - - lib_mod.addImport(impl.name, mod); - } -} - -fn customSoft( - b: *std.Build, - _: *Step.Compile, - lib_mod: *std.Build.Module, - _: *std.Build.Module, - _: *std.Build.Module, - base_c_mod: *std.Build.Module, - target: std.Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, - use_llvm: bool, -) !void { - const spv = b.lazyDependency("SPIRV_Interpreter", .{ - .target = target, - .optimize = optimize, - .@"use-llvm" = use_llvm, - }) orelse return error.UnresolvedDependency; - - lib_mod.addImport("soft_c", base_c_mod); - lib_mod.addImport("spv", spv.module("spv")); -} - -fn optionsSoft(b: *std.Build, options: *Step.Options) !void { - const single_threaded_option = b.option(bool, "soft-single-threaded", "Single threaded runtime mode") orelse false; - const shaders_simd_option = b.option(bool, "soft-shader-simd", "Shaders SIMD acceleration") orelse true; - const compute_dump_early_results_table_option = b.option(u32, "soft-compute-dump-early-results-table", "Dump compute shaders results table before invocation"); - const compute_dump_final_results_table_option = b.option(u32, "soft-compute-dump-final-results-table", "Dump compute shaders results table after invocation"); - const approxiamte_rgb_option = b.option(bool, "soft-approximates-rgb", "Approximate sRGB <-> RGB conversions") orelse true; - - options.addOption(bool, "soft_single_threaded", single_threaded_option); - options.addOption(bool, "soft_shaders_simd", shaders_simd_option); - options.addOption(?u32, "soft_compute_dump_early_results_table", compute_dump_early_results_table_option); - options.addOption(?u32, "soft_compute_dump_final_results_table", compute_dump_final_results_table_option); - options.addOption(bool, "soft_approximates_rgb", approxiamte_rgb_option); -} - -fn customFlint( - _: *std.Build, - _: *Step.Compile, - lib_mod: *std.Build.Module, - _: *std.Build.Module, - _: *std.Build.Module, - base_c_mod: *std.Build.Module, - _: std.Build.ResolvedTarget, - _: std.builtin.OptimizeMode, - _: bool, -) !void { - lib_mod.addImport("intel_c", base_c_mod); -} - -fn optionsFlint(b: *std.Build, options: *Step.Options) !void { - _ = b; - _ = options; -} - -fn customPhi( - b: *std.Build, - _: *Step.Compile, - lib_mod: *std.Build.Module, - _: *std.Build.Module, - _: *std.Build.Module, - base_c_mod: *std.Build.Module, - target: std.Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, - use_llvm: bool, -) !void { - lib_mod.addImport("phi_c", base_c_mod); - - const miclib = b.lazyDependency("miclib", .{ - .target = target, - .optimize = optimize, - .@"use-llvm" = use_llvm, - }) orelse return error.UnresolvedDependency; - - lib_mod.addImport("miclib", miclib.module("miclib")); -} - -fn optionsPhi(b: *std.Build, options: *Step.Options) !void { - _ = b; - _ = options; -} - fn addCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const ImplementationDesc, impl_lib: *Step.Compile, comptime mode: RunningMode) !*Step { const cts = b.dependency("cts_bin", .{}); @@ -502,3 +388,216 @@ fn addMultithreadedCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *c return &run.step; } + +// Ape specialized functions + +fn customApe( + b: *std.Build, + lib: *Step.Compile, + lib_mod: *std.Build.Module, + base_mod: *std.Build.Module, + vulkan: *std.Build.Module, + base_c_mod: *std.Build.Module, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + use_llvm: bool, +) !void { + for (implementations) |impl| { + if (std.mem.eql(u8, impl.name, "ape")) + continue; + + const mod = b.createModule(.{ + .root_source_file = b.path(impl.root_source_file), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "base", .module = base_mod }, + .{ .name = "vulkan", .module = vulkan }, + }, + }); + + if (impl.custom) |func| { + func(b, lib, mod, base_mod, vulkan, base_c_mod, target, optimize, use_llvm) catch continue; + } + + lib_mod.addImport(impl.name, mod); + } +} + +// Soft specialized functions + +fn customSoft( + b: *std.Build, + _: *Step.Compile, + lib_mod: *std.Build.Module, + _: *std.Build.Module, + _: *std.Build.Module, + base_c_mod: *std.Build.Module, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + use_llvm: bool, +) !void { + const spv = b.lazyDependency("SPIRV_Interpreter", .{ + .target = target, + .optimize = optimize, + .@"use-llvm" = use_llvm, + }) orelse return error.UnresolvedDependency; + + lib_mod.addImport("soft_c", base_c_mod); + lib_mod.addImport("spv", spv.module("spv")); +} + +fn optionsSoft(b: *std.Build, options: *Step.Options) !void { + const single_threaded_option = b.option(bool, "soft-single-threaded", "Single threaded runtime mode") orelse false; + const shaders_simd_option = b.option(bool, "soft-shader-simd", "Shaders SIMD acceleration") orelse true; + const compute_dump_early_results_table_option = b.option(u32, "soft-compute-dump-early-results-table", "Dump compute shaders results table before invocation"); + const compute_dump_final_results_table_option = b.option(u32, "soft-compute-dump-final-results-table", "Dump compute shaders results table after invocation"); + const approxiamte_rgb_option = b.option(bool, "soft-approximates-rgb", "Approximate sRGB <-> RGB conversions") orelse true; + + options.addOption(bool, "soft_single_threaded", single_threaded_option); + options.addOption(bool, "soft_shaders_simd", shaders_simd_option); + options.addOption(?u32, "soft_compute_dump_early_results_table", compute_dump_early_results_table_option); + options.addOption(?u32, "soft_compute_dump_final_results_table", compute_dump_final_results_table_option); + options.addOption(bool, "soft_approximates_rgb", approxiamte_rgb_option); +} + +// Flint specialized functions + +fn customFlint( + _: *std.Build, + _: *Step.Compile, + lib_mod: *std.Build.Module, + _: *std.Build.Module, + _: *std.Build.Module, + base_c_mod: *std.Build.Module, + _: std.Build.ResolvedTarget, + _: std.builtin.OptimizeMode, + _: bool, +) !void { + lib_mod.addImport("intel_c", base_c_mod); +} + +fn optionsFlint(b: *std.Build, options: *Step.Options) !void { + _ = b; + _ = options; +} + +// Phi specialized functions + +fn customPhi( + b: *std.Build, + lib: *Step.Compile, + lib_mod: *std.Build.Module, + _: *std.Build.Module, + _: *std.Build.Module, + base_c_mod: *std.Build.Module, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + use_llvm: bool, +) !void { + lib_mod.addImport("phi_c", base_c_mod); + + const miclib = b.lazyDependency("miclib", .{ + .target = target, + .optimize = optimize, + .@"use-llvm" = use_llvm, + }) orelse return error.UnresolvedDependency; + + lib_mod.addImport("miclib", miclib.module("miclib")); + + const phi_protocol_c = b.addTranslateC(.{ + .root_source_file = b.path("src/phi/shared/Protocol.h"), + .target = target, + .optimize = optimize, + .link_libc = false, + }); + + lib_mod.addImport("phi_protocol_c", phi_protocol_c.createModule()); + + // To avoid duplicated options due to Ape's custom function + if (!std.mem.eql(u8, lib.name, "vulkan_phi")) + return; + + const build_card = b.option( + bool, + "phi-build-card", + "Build Xeon Phi card daemon", + ) orelse true; + + if (!build_card) + return; + + const cc = b.option( + []const u8, + "phi-card-cc", + "Path to k1om-mpss-linux-gcc", + ) orelse "k1om-mpss-linux-gcc"; + + const sysroot = b.option( + []const u8, + "phi-card-sysroot", + "MPSS sysroot path", + ); + + const daemon = try addPhiCardDaemon(b, optimize, cc, sysroot); + const install_daemon = b.addInstallFile(daemon, "lib/phi_device.mic"); + lib.step.dependOn(&install_daemon.step); +} + +fn optionsPhi(b: *std.Build, options: *Step.Options) !void { + _ = b; + _ = options; +} + +fn addPhiCardDaemon( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + cc: []const u8, + sysroot: ?[]const u8, +) !std.Build.LazyPath { + const cmd = b.addSystemCommand(&.{cc}); + + cmd.addArgs(&.{ + "-std=c11", + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-pthread", + }); + + cmd.addArg("-I"); + cmd.addDirectoryArg(b.path("src/phi/mic")); + cmd.addArg("-I"); + cmd.addDirectoryArg(b.path("src/phi/shared")); + + if (sysroot) |path| { + cmd.addArg("--sysroot"); + cmd.addArg(path); + } + + switch (optimize) { + .Debug => cmd.addArgs(&.{ "-O0", "-g3" }), + .ReleaseSafe => cmd.addArgs(&.{ "-O2", "-g", "-DNDEBUG" }), + .ReleaseFast => cmd.addArgs(&.{ "-O3", "-DNDEBUG" }), + .ReleaseSmall => cmd.addArgs(&.{ "-Os", "-DNDEBUG" }), + } + + const sources = [_][]const u8{ + "src/phi/mic/main.c", + "src/phi/mic/Daemon.c", + "src/phi/mic/Logger.c", + "src/phi/mic/Memory.c", + // Add new files here + }; + + for (sources) |source| { + cmd.addFileArg(b.path(source)); + } + + cmd.addArgs(&.{ + "-lscif", + "-o", + }); + + return cmd.addOutputFileArg("phi_device.mic"); +} diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..11a0a8b --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,5 @@ +-xc +-std=c11 +-Isrc/phi/shared +-Isrc/phi/mic +-isystem/opt/mpss/3.8.6/sysroots/k1om-mpss-linux/usr/include/ diff --git a/src/phi/PhiDevice.zig b/src/phi/PhiDevice.zig index 2221286..f98957d 100644 --- a/src/phi/PhiDevice.zig +++ b/src/phi/PhiDevice.zig @@ -3,6 +3,8 @@ const vk = @import("vulkan"); const base = @import("base"); const PhiQueue = @import("PhiQueue.zig"); +const PhiPhysicalDevice = @import("PhiPhysicalDevice.zig"); +const PhiTransport = @import("PhiTransport.zig"); pub const PhiBinarySemaphore = @import("PhiBinarySemaphore.zig"); pub const PhiBuffer = @import("PhiBuffer.zig"); @@ -32,6 +34,7 @@ const Self = @This(); pub const Interface = base.Device; interface: Interface, +transport: PhiTransport, pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; @@ -71,8 +74,12 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a .getDeviceGroupSurfacePresentModesKHR = getDeviceGroupSurfacePresentModesKHR, }; + const phi_physical_device: *PhiPhysicalDevice = @alignCast(@fieldParentPtr("interface", physical_device)); + const transport = try PhiTransport.init(phi_physical_device.scif_node_id); + self.* = .{ .interface = interface, + .transport = transport, }; try self.interface.createQueues(allocator, info); @@ -81,6 +88,7 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + self.transport.deinit(); allocator.destroy(self); } diff --git a/src/phi/PhiDeviceMemory.zig b/src/phi/PhiDeviceMemory.zig index cd24d18..14664a0 100644 --- a/src/phi/PhiDeviceMemory.zig +++ b/src/phi/PhiDeviceMemory.zig @@ -11,6 +11,8 @@ const Self = @This(); pub const Interface = base.DeviceMemory; interface: Interface, +remote_handle: u64, +data: ?[]u8, pub fn create(device: *PhiDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; @@ -26,14 +28,44 @@ pub fn create(device: *PhiDevice, allocator: std.mem.Allocator, size: vk.DeviceS .invalidateRange = invalidateRange, }; + if (memory_type_index >= device.interface.physical_device.mem_props.memory_type_count) { + return VkError.ValidationFailed; + } + + const memory_type = device.interface.physical_device.mem_props.memory_types[memory_type_index]; + const host_visible = memory_type.property_flags.host_visible_bit; + const device_local = memory_type.property_flags.device_local_bit; + const allocation_size = std.math.cast(usize, size) orelse return VkError.OutOfDeviceMemory; + + const remote_handle = if (device_local) blk: { + const remote = try device.transport.allocMemory(size, memory_type_index); + break :blk remote.remote_handle; + } else 0; + errdefer if (remote_handle != 0) device.transport.freeMemory(remote_handle); + + const data = if (host_visible) + device.interface.device_allocator.allocator().alloc(u8, allocation_size) catch return VkError.OutOfDeviceMemory + else + null; + self.* = .{ .interface = interface, + .remote_handle = remote_handle, + .data = data, }; + return self; } pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const device: *PhiDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); + if (self.data) |data| { + interface.owner.device_allocator.allocator().free(data); + } + if (self.remote_handle != 0) { + device.transport.freeMemory(self.remote_handle); + } allocator.destroy(self); } @@ -51,10 +83,19 @@ pub fn invalidateRange(interface: *Interface, offset: vk.DeviceSize, size: vk.De pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - _ = self; - _ = offset; - _ = size; - return VkError.Unknown; + const data = self.data orelse return VkError.MemoryMapFailed; + const map_offset = std.math.cast(usize, offset) orelse return VkError.MemoryMapFailed; + if (map_offset >= data.len) { + return VkError.MemoryMapFailed; + } + const map_size = if (size == vk.WHOLE_SIZE) + data.len - map_offset + else + std.math.cast(usize, size) orelse return VkError.MemoryMapFailed; + if (map_size > data.len - map_offset) { + return VkError.MemoryMapFailed; + } + return data[map_offset..(map_offset + map_size)]; } pub fn unmap(_: *Interface) void {} diff --git a/src/phi/PhiInstance.zig b/src/phi/PhiInstance.zig index baa8b91..fbec076 100644 --- a/src/phi/PhiInstance.zig +++ b/src/phi/PhiInstance.zig @@ -97,7 +97,7 @@ fn requestPhysicalDevices(interface: *Interface, allocator: std.mem.Allocator, _ }; defer device.deinit(); - const physical_device = try PhiPhysicalDevice.create(allocator, interface, device); + const physical_device = try PhiPhysicalDevice.create(allocator, interface, device, device_num); errdefer physical_device.interface.release(allocator) catch {}; const dispatchable = try Dispatchable(base.PhysicalDevice).wrap(allocator, &physical_device.interface); diff --git a/src/phi/PhiPhysicalDevice.zig b/src/phi/PhiPhysicalDevice.zig index 7ff4743..33456e7 100644 --- a/src/phi/PhiPhysicalDevice.zig +++ b/src/phi/PhiPhysicalDevice.zig @@ -31,8 +31,9 @@ pub const EXTENSIONS = [_]vk.ExtensionProperties{ }; interface: Interface, +scif_node_id: u16, -pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, mic_device: mic.Device) VkError!*Self { +pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, mic_device: mic.Device, mic_device_num: u32) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); @@ -233,12 +234,12 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, mic_device .size = memory.size() catch 0, .flags = .{ .device_local_bit = true }, }; - interface.mem_props.memory_heaps[0] = .{ + interface.mem_props.memory_heaps[1] = .{ .size = std.process.totalSystemMemory() catch 0, .flags = .{}, }; } else |err| { - std.log.scoped(.MIC).err("Failed to fetch device PCI config: {s}", .{@errorName(err)}); + std.log.scoped(.MIC).err("Failed to fetch device memory infos: {s}", .{@errorName(err)}); return VkError.InitializationFailed; } @@ -260,11 +261,16 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, mic_device self.* = .{ .interface = interface, + .scif_node_id = deviceNumToScifNode(mic_device_num), }; return self; } +fn deviceNumToScifNode(device_num: u32) u16 { + return @intCast(device_num + 1); +} + pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); allocator.destroy(self); diff --git a/src/phi/PhiTransport.zig b/src/phi/PhiTransport.zig new file mode 100644 index 0000000..6c3413c --- /dev/null +++ b/src/phi/PhiTransport.zig @@ -0,0 +1,155 @@ +const std = @import("std"); +const base = @import("base"); +const lib = @import("lib.zig"); +const scif = @import("scif.zig"); + +const VkError = base.VkError; +const proto = lib.proto; + +const Self = @This(); + +epd: scif.epd_t, +sequence: u64 = 1, +mutex: base.SpinMutex = .{}, + +pub fn init(node_id: u16) VkError!Self { + try scif.load(); + errdefer scif.unload(); + + const epd = scif.open(); + if (epd < 0) { + std.log.scoped(.PhiTransport).err("SCIF open failed", .{}); + return VkError.InitializationFailed; + } + errdefer _ = scif.close(epd); + + var dst: scif.PortId = .{ + .node = node_id, + .port = @intCast(proto.PHI_SCIF_PORT), + }; + + if (scif.connect(epd, &dst) < 0) { + std.log.scoped(.PhiTransport).err("SCIF connection to node {d} port {d} failed", .{ dst.node, dst.port }); + return VkError.InitializationFailed; + } + + var self: Self = .{ .epd = epd }; + try self.handshake(); + return self; +} + +pub fn deinit(self: *Self) void { + _ = scif.close(self.epd); + scif.unload(); +} + +pub fn allocMemory(self: *Self, size: u64, memory_type_index: u32) VkError!proto.PhiAllocMemoryReply { + self.mutex.lock(); + defer self.mutex.unlock(); + + const alloc_request: proto.PhiAllocMemoryRequest = .{ + .size = size, + .memory_type_index = memory_type_index, + .flags = 0, + }; + var reply: proto.PhiAllocMemoryReply = undefined; + try self.request(proto.PHI_COMMAND_ALLOC_MEMORY, std.mem.asBytes(&alloc_request), std.mem.asBytes(&reply)); + if (reply.result.status != proto.PHI_STATUS_OK) { + return mapStatus(reply.result.status); + } + return reply; +} + +pub fn freeMemory(self: *Self, remote_handle: u64) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + const request_payload: proto.PhiFreeMemoryRequest = .{ + .remote_handle = remote_handle, + }; + var reply: proto.PhiFreeMemoryReply = undefined; + self.request(proto.PHI_COMMAND_FREE_MEMORY, std.mem.asBytes(&request_payload), std.mem.asBytes(&reply)) catch |err| { + std.log.scoped(.PhiTransport).err("Remote free failed: {s}", .{@errorName(err)}); + return; + }; + if (reply.result.status != proto.PHI_STATUS_OK) { + std.log.scoped(.PhiTransport).err("Remote free returned status {d}", .{reply.result.status}); + } +} + +fn handshake(self: *Self) VkError!void { + const request_payload: proto.PhiHelloRequest = .{ + .host_protocol_version = proto.PHI_PROTOCOL_VERSION, + .reserved = 0, + }; + var reply: proto.PhiHelloReply = undefined; + try self.request(proto.PHI_COMMAND_HELLO, std.mem.asBytes(&request_payload), std.mem.asBytes(&reply)); + if (reply.result.status != proto.PHI_STATUS_OK) { + return mapStatus(reply.result.status); + } + if (reply.device_protocol_version != proto.PHI_PROTOCOL_VERSION) { + std.log.scoped(.PhiTransport).err("Unsupported Phi protocol version {d}", .{reply.device_protocol_version}); + return VkError.InitializationFailed; + } +} + +fn request(self: *Self, command: c_uint, payload: []const u8, reply_payload: []u8) VkError!void { + const sequence = self.sequence; + self.sequence += 1; + + const header: proto.PhiMessageHeader = .{ + .magic = proto.PHI_PROTOCOL_MAGIC, + .version = proto.PHI_PROTOCOL_VERSION, + .type = @intCast(command), + .sequence = sequence, + .payload_size = payload.len, + }; + + try self.writeAll(std.mem.asBytes(&header)); + try self.writeAll(payload); + + var reply_header: proto.PhiMessageHeader = undefined; + try self.readAll(std.mem.asBytes(&reply_header)); + + if (reply_header.magic != proto.PHI_PROTOCOL_MAGIC or + reply_header.version != proto.PHI_PROTOCOL_VERSION or + reply_header.type != header.type or + reply_header.sequence != sequence or + reply_header.payload_size != reply_payload.len) + { + std.log.scoped(.PhiTransport).err("Invalid Phi reply header", .{}); + return VkError.InitializationFailed; + } + + try self.readAll(reply_payload); +} + +fn writeAll(self: *Self, bytes: []const u8) VkError!void { + var offset: usize = 0; + while (offset < bytes.len) { + const written = scif.send(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.SEND_BLOCK); + if (written <= 0) { + return VkError.InitializationFailed; + } + offset += @intCast(written); + } +} + +fn readAll(self: *Self, bytes: []u8) VkError!void { + var offset: usize = 0; + while (offset < bytes.len) { + const read = scif.recv(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.RECV_BLOCK); + if (read <= 0) { + return VkError.InitializationFailed; + } + offset += @intCast(read); + } +} + +fn mapStatus(status: c_int) VkError { + return switch (status) { + proto.PHI_STATUS_OUT_OF_MEMORY => VkError.OutOfDeviceMemory, + proto.PHI_STATUS_UNSUPPORTED_VERSION => VkError.InitializationFailed, + else => VkError.Unknown, + }; +} diff --git a/src/phi/lib.zig b/src/phi/lib.zig index 2329a72..da38a15 100644 --- a/src/phi/lib.zig +++ b/src/phi/lib.zig @@ -3,13 +3,16 @@ const vk = @import("vulkan"); pub const base = @import("base"); pub const c = @import("phi_c"); +pub const proto = @import("phi_protocol_c"); pub const config = base.config; pub const mic = @import("miclib"); +pub const scif = @import("scif.zig"); pub const PhiInstance = @import("PhiInstance.zig"); pub const PhiDevice = @import("PhiDevice.zig"); pub const PhiPhysicalDevice = @import("PhiPhysicalDevice.zig"); pub const PhiQueue = @import("PhiQueue.zig"); +pub const PhiTransport = @import("PhiTransport.zig"); pub const PhiBinarySemaphore = @import("PhiBinarySemaphore.zig"); pub const PhiBuffer = @import("PhiBuffer.zig"); @@ -70,6 +73,8 @@ test { std.testing.refAllDecls(PhiImageView); std.testing.refAllDecls(PhiInstance); std.testing.refAllDecls(PhiPhysicalDevice); + std.testing.refAllDecls(PhiTransport); + std.testing.refAllDecls(scif); std.testing.refAllDecls(PhiPipeline); std.testing.refAllDecls(PhiPipelineCache); std.testing.refAllDecls(PhiPipelineLayout); diff --git a/src/phi/mic/Daemon.c b/src/phi/mic/Daemon.c new file mode 100644 index 0000000..c8ca1eb --- /dev/null +++ b/src/phi/mic/Daemon.c @@ -0,0 +1,193 @@ +#include +#include +#include + +static int HandleHello(scif_epd_t endpoint, const PhiMessageHeader* header) +{ + PhiHelloRequest request; + PhiHelloReply reply = { + .result = { + .status = PHI_STATUS_OK, + .reserved = 0, + }, + .device_protocol_version = PHI_PROTOCOL_VERSION, + .pointer_bits = (uint32_t)(sizeof(void *) * 8u), + }; + + if(header->payload_size != sizeof(request)) + { + if(DrainPayload(endpoint, header->payload_size) < 0) + return -1; + reply.result.status = PHI_STATUS_BAD_MESSAGE; + return SendReply(endpoint, header, &reply, sizeof(reply)); + } + + if(ReadAll(endpoint, &request, sizeof(request)) < 0) + return -1; + + if(request.host_protocol_version != PHI_PROTOCOL_VERSION) + reply.result.status = PHI_STATUS_UNSUPPORTED_VERSION; + + return SendReply(endpoint, header, &reply, sizeof(reply)); +} + +scif_epd_t StartDaemon() +{ + PhiLogInfo("Starting the daemon..."); + + scif_epd_t endpoint = scif_open(); + if(endpoint < 0) + { + PhiLogError("Failed to create SCIF endpoint"); + return 0; + } + + if(scif_bind(endpoint, PHI_SCIF_PORT) < 0) + { + PhiLogError("Failed to bind SCIF port"); + scif_close(endpoint); + return 0; + } + + if(scif_listen(endpoint, 1) < 0) + { + PhiLogError("Could not listen to SCIF port"); + scif_close(endpoint); + return 0; + } + + return endpoint; +} + +void ShutdownDaemon(scif_epd_t endpoint) +{ + PhiLogInfo("Shuting down the daemon..."); + scif_close(endpoint); +} + +int HandlePacket(scif_epd_t endpoint) +{ + for(;;) + { + PhiMessageHeader header; + + if(ReadAll(endpoint, &header, sizeof(header)) < 0) + return -1; + + if(header.magic != PHI_PROTOCOL_MAGIC || header.version != PHI_PROTOCOL_VERSION) + { + if(DrainPayload(endpoint, header.payload_size) < 0) + return -1; + if(SendStatus(endpoint, &header, PHI_STATUS_BAD_MESSAGE) < 0) + return -1; + continue; + } + + switch((PhiCommandType)header.type) + { + case PHI_COMMAND_HELLO: + if(HandleHello(endpoint, &header) < 0) + return -1; + break; + + case PHI_COMMAND_ALLOC_MEMORY: + if(HandleAllocMemory(endpoint, &header) < 0) + return -1; + break; + + case PHI_COMMAND_FREE_MEMORY: + if(HandleFreeMemory(endpoint, &header) < 0) + return -1; + break; + + case PHI_COMMAND_SHUTDOWN: + if(DrainPayload(endpoint, header.payload_size) < 0) + return -1; + if(SendStatus(endpoint, &header, PHI_STATUS_OK) < 0) + return -1; + return 0; + + default: + if(DrainPayload(endpoint, header.payload_size) < 0) + return -1; + if(SendStatus(endpoint, &header, PHI_STATUS_UNSUPPORTED_COMMAND) < 0) + return -1; + break; + } + } +} + +int ReadAll(scif_epd_t endpoint, void* data, size_t size) +{ + uint8_t* bytes = data; + size_t offset = 0; + + while(offset < size) + { + int got = scif_recv(endpoint, bytes + offset, size - offset, SCIF_RECV_BLOCK); + if(got <= 0) + return -1; + offset += (size_t)got; + } + + return 0; +} + +int WriteAll(scif_epd_t endpoint, const void* data, size_t size) +{ + const uint8_t* bytes = data; + size_t offset = 0; + + while(offset < size) + { + int sent = scif_send(endpoint, (void*)(bytes + offset), size - offset, SCIF_SEND_BLOCK); + if(sent <= 0) + return -1; + offset += (size_t)sent; + } + + return 0; +} + +int SendReply(scif_epd_t endpoint, const PhiMessageHeader* request, const void* payload, uint64_t payload_size) +{ + PhiMessageHeader reply = { + .magic = PHI_PROTOCOL_MAGIC, + .version = PHI_PROTOCOL_VERSION, + .type = request->type, + .sequence = request->sequence, + .payload_size = payload_size, + }; + + if(WriteAll(endpoint, &reply, sizeof(reply)) < 0) + return -1; + + return WriteAll(endpoint, payload, (size_t)payload_size); +} + +int SendStatus(scif_epd_t endpoint, const PhiMessageHeader* request, PhiStatus status) +{ + PhiFreeMemoryReply reply = { + .result = { + .status = status, + .reserved = 0, + }, + }; + + return SendReply(endpoint, request, &reply, sizeof(reply)); +} + +int DrainPayload(scif_epd_t endpoint, uint64_t size) +{ + uint8_t buffer[256]; + + while(size > 0) + { + size_t chunk = size < sizeof(buffer) ? (size_t)size : sizeof(buffer); + if(ReadAll(endpoint, buffer, chunk) < 0) + return -1; + size -= chunk; + } + + return 0; +} diff --git a/src/phi/mic/Daemon.h b/src/phi/mic/Daemon.h new file mode 100644 index 0000000..82bcd62 --- /dev/null +++ b/src/phi/mic/Daemon.h @@ -0,0 +1,20 @@ +#ifndef APE_PHI_DAEMON_H +#define APE_PHI_DAEMON_H + +#include + +#include + +#include + +scif_epd_t StartDaemon(); +void ShutdownDaemon(scif_epd_t endpoint); + +int DrainPayload(scif_epd_t endpoint, uint64_t size); +int HandlePacket(scif_epd_t endpoint); +int ReadAll(scif_epd_t endpoint, void* data, size_t size); +int SendReply(scif_epd_t endpoint, const PhiMessageHeader* request, const void* payload, uint64_t payload_size); +int SendStatus(scif_epd_t endpoint, const PhiMessageHeader* request, PhiStatus status); +int WriteAll(scif_epd_t endpoint, const void* data, size_t size); + +#endif diff --git a/src/phi/mic/Logger.c b/src/phi/mic/Logger.c new file mode 100644 index 0000000..3f2ad60 --- /dev/null +++ b/src/phi/mic/Logger.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +#include + +#define RED 31 +#define GREEN 32 +#define BLUE 34 +#define DEF 0 +#define BLACK 30 +#define YELLOW 33 +#define MAGENTA 35 +#define CYAN 36 +#define WHITE 37 +#define BG_RED 41 +#define BG_GREEN 42 +#define BG_BLUE 44 +#define BG_DEF 0 +#define BG_BLACK 40 +#define BG_YELLOW 43 +#define BG_MAGENTA 45 +#define BG_CYAN 46 +#define BG_WHITE 47 +#define RESET 0 +#define BOLD 1 +#define UNDERLINE 4 +#define INVERSE 7 +#define BOLD_OFF 21 +#define UNDERLINE_OFF 24 +#define INVERSE_OFF 27 + +inline static void SetConsoleColor(FILE* file, int code) +{ + fprintf(file, "\033[1;%dm", code); +} + +void PhiLog(PhiLogLevel level, const char* fmt, const char* file, const char* function, int line, ...) +{ + time_t now = time(0); + struct tm tstruct = *localtime(&now); + char buffer[128]; + strftime(buffer, sizeof(buffer), "[%X] ", &tstruct); + + FILE* out = stdout; + + if(level != PHI_LOG_LEVEL_INFO) + out = stderr; + + // Way too much printf calls + SetConsoleColor(out, MAGENTA); + fprintf(out, "[Phi device "); + SetConsoleColor(out, GREEN); + fprintf(out, "Phi"); + SetConsoleColor(out, YELLOW); + fprintf(out, "%s", buffer); + SetConsoleColor(out, MAGENTA); + fputc(']', out); + + switch(level) + { + case PHI_LOG_LEVEL_INFO: + SetConsoleColor(out, BLUE); + fprintf(out, "[info] "); + break; + case PHI_LOG_LEVEL_WARN: + SetConsoleColor(out, MAGENTA); + fprintf(out, "[warn] "); + break; + case PHI_LOG_LEVEL_ERR: + case PHI_LOG_LEVEL_FATAL: + SetConsoleColor(out, RED); + fprintf(out, "[err] "); + break; + } + + va_list argptr; + va_start(argptr, line); + + SetConsoleColor(out, RESET); + fprintf(out, fmt, argptr); + fputc('\n', out); + + if(level == PHI_LOG_LEVEL_FATAL) + { + SetConsoleColor(out, BG_RED); + fprintf(out, "Fatal Error: emergency exit\n"); + SetConsoleColor(out, BG_DEF); + abort(); + } +} diff --git a/src/phi/mic/Logger.h b/src/phi/mic/Logger.h new file mode 100644 index 0000000..b0bb4e7 --- /dev/null +++ b/src/phi/mic/Logger.h @@ -0,0 +1,24 @@ +#ifndef APE_PHI_LOGGER_H +#define APE_PHI_LOGGER_H + +typedef enum PhiLogLevel +{ + PHI_LOG_LEVEL_INFO = 0, + PHI_LOG_LEVEL_WARN = 1, + PHI_LOG_LEVEL_ERR = 2, + PHI_LOG_LEVEL_FATAL = 3, +} PhiLogLevel; + +void PhiLog(PhiLogLevel level, const char* fmt, const char* file, const char* function, int line, ...); + +#define PhiLogError(msg) PhiLog(PHI_LOG_LEVEL_ERR, msg, __FILE__, __FUNCTION__, __LINE__) +#define PhiLogWarning(msg) PhiLog(PHI_LOG_LEVEL_WARN, msg, __FILE__, __FUNCTION__, __LINE__) +#define PhiLogInfo(msg) PhiLog(PHI_LOG_LEVEL_INFO, msg, __FILE__, __FUNCTION__, __LINE__) +#define PhiLogFatal(msg) PhiLog(PHI_LOG_LEVEL_FATAL, msg, __FILE__, __FUNCTION__, __LINE__) + +#define PhiLogErrorFmt(msg, ...) PhiLog(PHI_LOG_LEVEL_ERR, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) +#define PhiLogWarningFmt(msg, ...) PhiLog(PHI_LOG_LEVEL_WARN, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) +#define PhiLogInfoFmt(msg, ...) PhiLog(PHI_LOG_LEVEL_INFO, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) +#define PhiLogFatalFmt(msg, ...) PhiLog(PHI_LOG_LEVEL_FATAL, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + +#endif diff --git a/src/phi/mic/Memory.c b/src/phi/mic/Memory.c new file mode 100644 index 0000000..cd17892 --- /dev/null +++ b/src/phi/mic/Memory.c @@ -0,0 +1,73 @@ +#include + +#include +#include + +int HandleAllocMemory(scif_epd_t endpoint, const PhiMessageHeader* header) +{ + PhiAllocMemoryRequest request; + PhiAllocMemoryReply reply = { + .result = { + .status = PHI_STATUS_OK, + .reserved = 0, + }, + .remote_handle = 0, + .size = 0, + }; + void* memory; + + if(header->payload_size != sizeof(request)) + { + if(DrainPayload(endpoint, header->payload_size) < 0) + return -1; + reply.result.status = PHI_STATUS_BAD_MESSAGE; + return SendReply(endpoint, header, &reply, sizeof(reply)); + } + if(ReadAll(endpoint, &request, sizeof(request)) < 0) + return -1; + + memory = calloc(1, (size_t)request.size); + + if(memory == NULL) + reply.result.status = PHI_STATUS_OUT_OF_MEMORY; + else + { + reply.remote_handle = (uint64_t)(uintptr_t)memory; + reply.size = request.size; + PhiLogInfoFmt("Allocated %zu bytes", (size_t)request.size); + } + + return SendReply(endpoint, header, &reply, sizeof(reply)); +} + +int HandleFreeMemory(scif_epd_t endpoint, const PhiMessageHeader* header) +{ + PhiFreeMemoryRequest request; + PhiFreeMemoryReply reply = { + .result = { + .status = PHI_STATUS_OK, + .reserved = 0, + }, + }; + + if(header->payload_size != sizeof(request)) + { + if(DrainPayload(endpoint, header->payload_size) < 0) + return -1; + reply.result.status = PHI_STATUS_BAD_MESSAGE; + return SendReply(endpoint, header, &reply, sizeof(reply)); + } + if(ReadAll(endpoint, &request, sizeof(request)) < 0) + return -1; + + if(request.remote_handle == 0) + { + reply.result.status = PHI_STATUS_INVALID_HANDLE; + } + else + { + free((void*)(uintptr_t)request.remote_handle); + } + + return SendReply(endpoint, header, &reply, sizeof(reply)); +} diff --git a/src/phi/mic/Memory.h b/src/phi/mic/Memory.h new file mode 100644 index 0000000..7832183 --- /dev/null +++ b/src/phi/mic/Memory.h @@ -0,0 +1,9 @@ +#ifndef APE_PHI_MEMORY_H +#define APE_PHI_MEMORY_H + +#include + +int HandleAllocMemory(scif_epd_t endpoint, const PhiMessageHeader* header); +int HandleFreeMemory(scif_epd_t endpoint, const PhiMessageHeader* header); + +#endif diff --git a/src/phi/mic/main.c b/src/phi/mic/main.c new file mode 100644 index 0000000..23301e8 --- /dev/null +++ b/src/phi/mic/main.c @@ -0,0 +1,32 @@ +#include +#include + +int main(void) +{ + scif_epd_t endpoint = StartDaemon(); + + if(endpoint == 0) + return 1; + + for(;;) + { + struct scif_portID peer; + scif_epd_t client; + + if(scif_accept(endpoint, &peer, &client, SCIF_ACCEPT_SYNC) < 0) + { + if(errno == EINTR) + continue; + PhiLogError("Could not accept SCIF connection"); + break; + } + + (void)HandlePacket(client); + + scif_close(client); + } + + ShutdownDaemon(endpoint); + + return 0; +} diff --git a/src/phi/scif.zig b/src/phi/scif.zig new file mode 100644 index 0000000..2e249ab --- /dev/null +++ b/src/phi/scif.zig @@ -0,0 +1,77 @@ +const std = @import("std"); +const base = @import("base"); + +const VkError = base.VkError; + +pub const epd_t = c_int; + +pub const PortId = extern struct { + node: u16, + port: u16, +}; + +pub const SEND_BLOCK = 1; +pub const RECV_BLOCK = 1; + +var scif_open: *const fn () callconv(.c) epd_t = undefined; +var scif_close: *const fn (epd: epd_t) callconv(.c) c_int = undefined; +var scif_connect: *const fn (epd: epd_t, dst: *const PortId) callconv(.c) c_int = undefined; +var scif_send: *const fn (epd: epd_t, msg: ?*const anyopaque, len: usize, flags: c_int) callconv(.c) isize = undefined; +var scif_recv: *const fn (epd: epd_t, msg: ?*anyopaque, len: usize, flags: c_int) callconv(.c) isize = undefined; + +var module: std.DynLib = undefined; +var ref_count = std.atomic.Value(usize).init(0); +var load_mutex: base.SpinMutex = .{}; + +pub fn load() VkError!void { + load_mutex.lock(); + defer load_mutex.unlock(); + + if (ref_count.load(.monotonic) != 0) { + _ = ref_count.fetchAdd(1, .monotonic); + return; + } + + module = std.DynLib.open("libscif.so") catch { + std.log.scoped(.SCIF).err("Could not open libscif.so", .{}); + return VkError.InitializationFailed; + }; + errdefer module.close(); + + scif_open = module.lookup(@TypeOf(scif_open), "scif_open") orelse return VkError.InitializationFailed; + scif_close = module.lookup(@TypeOf(scif_close), "scif_close") orelse return VkError.InitializationFailed; + scif_connect = module.lookup(@TypeOf(scif_connect), "scif_connect") orelse return VkError.InitializationFailed; + scif_send = module.lookup(@TypeOf(scif_send), "scif_send") orelse return VkError.InitializationFailed; + scif_recv = module.lookup(@TypeOf(scif_recv), "scif_recv") orelse return VkError.InitializationFailed; + + _ = ref_count.fetchAdd(1, .monotonic); +} + +pub fn unload() void { + load_mutex.lock(); + defer load_mutex.unlock(); + + if (ref_count.fetchSub(1, .release) == 1) { + module.close(); + } +} + +pub inline fn open() epd_t { + return scif_open(); +} + +pub inline fn close(epd: epd_t) c_int { + return scif_close(epd); +} + +pub inline fn connect(epd: epd_t, dst: *const PortId) c_int { + return scif_connect(epd, dst); +} + +pub inline fn send(epd: epd_t, msg: ?*const anyopaque, len: usize, flags: c_int) isize { + return scif_send(epd, msg, len, flags); +} + +pub inline fn recv(epd: epd_t, msg: ?*anyopaque, len: usize, flags: c_int) isize { + return scif_recv(epd, msg, len, flags); +} diff --git a/src/phi/shared/Protocol.h b/src/phi/shared/Protocol.h new file mode 100644 index 0000000..70dfa7f --- /dev/null +++ b/src/phi/shared/Protocol.h @@ -0,0 +1,84 @@ +#ifndef APE_PHI_PROTOCOL_H +#define APE_PHI_PROTOCOL_H + +#include + +#define PHI_PROTOCOL_MAGIC 0x50484941u +#define PHI_PROTOCOL_VERSION 1u +#define PHI_RPOTOCOL_VERSION PHI_PROTOCOL_VERSION +#define PHI_SCIF_PORT 43616u + +typedef enum PhiCommandType +{ + PHI_COMMAND_HELLO = 1, + PHI_COMMAND_ALLOC_MEMORY = 2, + PHI_COMMAND_FREE_MEMORY = 3, + PHI_COMMAND_UPLOAD = 4, + PHI_COMMAND_DOWNLOAD = 5, + PHI_COMMAND_SUBMIT = 6, + PHI_COMMAND_SHUTDOWN = 7, +} PhiCommandType; + +typedef enum PhiStatus +{ + PHI_STATUS_OK = 0, + PHI_STATUS_BAD_MESSAGE = 1, + PHI_STATUS_UNSUPPORTED_VERSION = 2, + PHI_STATUS_UNSUPPORTED_COMMAND = 3, + PHI_STATUS_OUT_OF_MEMORY = 4, + PHI_STATUS_INVALID_HANDLE = 5, +} PhiStatus; + +typedef struct PhiMessageHeader +{ + uint32_t magic; + uint16_t version; + uint16_t type; + uint64_t sequence; + uint64_t payload_size; +} PhiMessageHeader; + +typedef struct PhiResult +{ + int32_t status; + uint32_t reserved; +} PhiResult; + +typedef struct PhiHelloRequest +{ + uint32_t host_protocol_version; + uint32_t reserved; +} PhiHelloRequest; + +typedef struct PhiHelloReply +{ + PhiResult result; + uint32_t device_protocol_version; + uint32_t pointer_bits; +} PhiHelloReply; + +typedef struct PhiAllocMemoryRequest +{ + uint64_t size; + uint32_t memory_type_index; + uint32_t flags; +} PhiAllocMemoryRequest; + +typedef struct PhiAllocMemoryReply +{ + PhiResult result; + uint64_t remote_handle; + uint64_t size; +} PhiAllocMemoryReply; + +typedef struct PhiFreeMemoryRequest +{ + uint64_t remote_handle; +} PhiFreeMemoryRequest; + +typedef struct PhiFreeMemoryReply +{ + PhiResult result; +} PhiFreeMemoryReply; + +#endif