From b01bb53c09175f5089d5878af9ac41ec0f2b429f Mon Sep 17 00:00:00 2001 From: Soren Soe <2106410+stsoe@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:15:55 -0700 Subject: [PATCH] Use driver store path for xilinx_xrt on Windows (#7679) * Provide internal access to platform specific xilinx_xrt() Expose xrt_core::environment::xilinx_xrt() in module_loader.cpp Code to platform specific requirements. Add detail platform specific implementation. Default is to return environment variable XILINX_XRT if set. On linux conditionally /usr or /opt/xilinx/xrt. On windows same directory as xrt_coreutil.dll (to be change next commit) Signed-off-by: Soren Soe <2106410+stsoe@users.noreply.github.com> * Use driver store path for xilinx_xrt on Windows Implement xrt_core::environment::xilinx_xrt() on Windows to use driver store path if available. - If XILINX_XRT is set in environment, then return the value of XILINX_XRT - Else compute as driver store path if available (new) - Else return same directory as xrt_coreutil.dll is loaded from Add internal API for obtaining absolute path of xclbin file. For Windows, the xclbin path will be the same as xilinx_xrt(). The code for xilinx_xrt() and xclbin_path() is conditional based on platform. xilinx_xrt() is used to load the xrt_core shim library, hence cannot be implemented at shim layer. Signed-off-by: Soren Soe <2106410+stsoe@users.noreply.github.com> * Conditionally compile for windows when WDK is available Signed-off-by: Soren Soe <2106410+stsoe@users.noreply.github.com> * Use compile defintion for WDK available --------- Signed-off-by: Soren Soe <2106410+stsoe@users.noreply.github.com> --- .../core/common/detail/linux/xilinx_xrt.h | 27 ++ .../core/common/detail/windows/xilinx_xrt.h | 266 ++++++++++++++++++ .../core/common/detail/xilinx_xrt.h | 12 + src/runtime_src/core/common/module_loader.cpp | 53 +++- src/runtime_src/core/common/module_loader.h | 47 +++- 5 files changed, 379 insertions(+), 26 deletions(-) create mode 100644 src/runtime_src/core/common/detail/linux/xilinx_xrt.h create mode 100644 src/runtime_src/core/common/detail/windows/xilinx_xrt.h create mode 100644 src/runtime_src/core/common/detail/xilinx_xrt.h diff --git a/src/runtime_src/core/common/detail/linux/xilinx_xrt.h b/src/runtime_src/core/common/detail/linux/xilinx_xrt.h new file mode 100644 index 00000000000..bd86660773f --- /dev/null +++ b/src/runtime_src/core/common/detail/linux/xilinx_xrt.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. +#include +#include +#include +namespace xrt_core::detail { + +namespace bfs = boost::filesystem; + +bfs::path +xilinx_xrt() +{ +#if defined (__aarch64__) || defined (__arm__) + return bfs::path("/usr"); +#else + return bfs::path("/opt/xilinx/xrt"); +#endif +} + +bfs::path +xclbin_path(const std::string& xclbin) +{ + throw std::runtime_error("xclbin repo path not yet implemented on Linux"); +} + +} // xrt_core::detail + diff --git a/src/runtime_src/core/common/detail/windows/xilinx_xrt.h b/src/runtime_src/core/common/detail/windows/xilinx_xrt.h new file mode 100644 index 00000000000..fa11ee663c1 --- /dev/null +++ b/src/runtime_src/core/common/detail/windows/xilinx_xrt.h @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. + +#include "core/common/debug.h" +#include "core/common/dlfcn.h" + +#pragma warning(disable : 4005) +#include +#include + +#if defined(XRT_WINDOWS_HAS_WDK) +# include +#endif + +#include +#include +#include +#include +#include + +#if defined(XRT_WINDOWS_HAS_WDK) +namespace xrt_core::detail::windows { + +// D3DKMTQueryAdapterInfo returns path rooted in \SystemRoot\, but no +// other API understands this path, so replace it with the actual +// system root. +static std::string +replace_systemroot(std::string str) +{ + auto pos = str.find("\\SystemRoot\\"); + if (pos != 0) + return str; + + char system_root[MAX_PATH] = {}; + if (GetWindowsDirectoryA(system_root, sizeof(system_root)) == 0) + throw std::runtime_error("Unable to get Windows directory"); + + str.replace(pos, std::strlen("\\SystemRoot"), system_root); + return str; +} + +// Windows APIs return wchar_t strings, but we want to use std::string. +// Convert to utf8 multibyte string. +static std::string +utf8(const std::wstring& wstr) +{ + auto size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, NULL, 0, NULL, NULL); + std::string str(size, 0); + auto result = WideCharToMultiByte( + CP_UTF8, // CodePage + 0, // dwFlags conversion type must be 0 for CP_UTF8 + wstr.data(), // lpWideCharStr + -1, // cchWideChar -1 indicates null terminated string + str.data(), // lpMultiByteStr output buffer + size, // cbMultiByte size of output buffer + NULL, // lpDefaultChar, NULL implies system default + NULL); // lpUsedDefaultChar must be NULL for CP_UTF8 + + // strip included null terminator from std::string + while (str.back() == '\0') + str.pop_back(); + + if (result == 0) + throw std::runtime_error("Unable to convert wide string to multi-byte"); + + return str; +} + +// Manage gdi dll loading and symbol lookup +class gdilib +{ + using dll_guard = std::unique_ptr; + dll_guard dll; + +public: + gdilib(const char* dllnm) + : dll(xrt_core::dlopen(dllnm, 0), xrt_core::dlclose) + {} + + template + FunctionType + get(const char* symbol) const + { + if (auto dllsym = xrt_core::dlsym(dll.get(), symbol)) + return static_cast(dllsym); + + throw std::runtime_error("No such symbol '" + std::string(symbol) + "' in gdi32.dll"); + } +}; + +static gdilib gdi("gdi32.dll"); + +// Abstraction for adapter opened by D3DKMTEnumAdapters3. Move semantics for +// storing in container, while making sure all enumerated adapters are eventually +// closed using D3DKMTCloseAdapter +struct adapter +{ + D3DKMT_ADAPTERINFO m_info = {}; + + adapter() = default; + + ~adapter() + { + if (!m_info.hAdapter) + return; + + D3DKMT_CLOSEADAPTER close_adapter_args = {}; + close_adapter_args.hAdapter = m_info.hAdapter; + D3DKMTCloseAdapter(&close_adapter_args); + } + + // Movable + adapter(adapter&& rhs) + : m_info(std::move(rhs.m_info)) + { + // Avoid double close + rhs.m_info.hAdapter = 0; + } + + // Not copyable + adapter(const adapter&) = delete; + adapter& operator=(const adapter&) = delete; + + D3DKMT_HANDLE + handle() const + { + return m_info.hAdapter; + } + + // Query the driver store path for this adapter + std::string + driver_store_path() const + { + auto adapter_info = gdi.get("D3DKMTQueryAdapterInfo"); + + D3DKMT_QUERYADAPTERINFO query_adapter_info = {}; + D3DDDI_QUERYREGISTRY_INFO query_registry_info = {}; + query_adapter_info.hAdapter = handle(); + query_adapter_info.Type = KMTQAITYPE_QUERYREGISTRY; + query_adapter_info.pPrivateDriverData = &query_registry_info; + query_adapter_info.PrivateDriverDataSize = sizeof(query_registry_info); + query_registry_info.QueryType = D3DDDI_QUERYREGISTRY_DRIVERSTOREPATH; + auto status = adapter_info(&query_adapter_info); + if (status != STATUS_SUCCESS) + throw std::runtime_error("D3DKMTQueryAdapterInfo failed KMTQAITYPE_QUERYREGISTRY"); + + if (query_registry_info.Status != D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW) + throw std::runtime_error("Unexpected D3DDDI_QUERYREGISTRY_STATUS"); + + // Save the size of the output value + // Valid only when Status == D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW + auto output_value_size = query_registry_info.OutputValueSize; + + // Allocate variable sized query registey info buffer and query again + std::vector buffer(sizeof(D3DDDI_QUERYREGISTRY_INFO) + output_value_size); + std::memcpy(buffer.data(), &query_registry_info, sizeof(D3DDDI_QUERYREGISTRY_INFO)); + query_adapter_info.pPrivateDriverData = buffer.data(); + query_adapter_info.PrivateDriverDataSize = static_cast(buffer.size()); + status = adapter_info(&query_adapter_info); + if (status != STATUS_SUCCESS) + throw std::runtime_error("D3DKMTQueryAdapterInfo failed KMTQAITYPE_QUERYREGISTRY"); + + // Interpret the data, throw on error + auto query_info = reinterpret_cast(buffer.data()); + if (query_info->Status != D3DDDI_QUERYREGISTRY_STATUS_SUCCESS) + throw std::runtime_error("D3DDDI_QUERYREGISTRY_STATUS_SUCCESS failed"); + + // Return the driver path + std::wstring wstr{query_info->OutputString, query_info->OutputString + output_value_size}; + return replace_systemroot(utf8(wstr)); + } +}; + +// Abstraction to manage list of enumerated adapters +struct adapter_list +{ + std::vector m_adapters; + + adapter_list() + { + // Determine size of adapter list + D3DKMT_ENUMADAPTERS3 enum_adapters_args = {}; + enum_adapters_args.Filter.IncludeComputeOnly = 1; + auto enum_adapters = gdi.get("D3DKMTEnumAdapters3"); + auto status = enum_adapters(&enum_adapters_args); + if (status != STATUS_SUCCESS) + throw std::runtime_error("D3DKMTEnumAdapters3 failed "); + + m_adapters.resize(enum_adapters_args.NumAdapters); + + // Enumerate adapters + enum_adapters_args.pAdapters = reinterpret_cast(m_adapters.data()); + status = enum_adapters(&enum_adapters_args); + if (status != STATUS_SUCCESS) + throw std::runtime_error("D3DKMTEnumAdapters3 failed"); + m_adapters.resize(enum_adapters_args.NumAdapters); + } + + // Return first matching adapter + const adapter* + find(const std::string& match) + { + const std::wstring driver_description{match.begin(), match.end()}; + auto adapter_info = gdi.get("D3DKMTQueryAdapterInfo"); + for (const auto& adapter : m_adapters) { + D3DKMT_QUERYADAPTERINFO query_adapter_info_args = {}; + D3DKMT_DRIVER_DESCRIPTION query_driver_description = {}; + query_adapter_info_args.hAdapter = adapter.handle(); + query_adapter_info_args.Type = KMTQAITYPE_DRIVER_DESCRIPTION; + query_adapter_info_args.pPrivateDriverData = &query_driver_description; + query_adapter_info_args.PrivateDriverDataSize = sizeof(query_driver_description); + auto status = adapter_info(&query_adapter_info_args); + if (status != STATUS_SUCCESS) + throw std::runtime_error("D3DKMTQueryAdapterInfo failed KMTQAITYPE_DRIVER_DESCRIPTION"); + + if (std::wstring(query_driver_description.DriverDescription) == driver_description) + return &adapter; + } + + return nullptr; + } +}; + +} // xrt_core::detail::windows +#endif // XRT_WINDOWS_HAS_WDK + +namespace xrt_core::detail { + +namespace bfs = boost::filesystem; + +bfs::path +xilinx_xrt() +{ +#if defined(XRT_WINDOWS_HAS_WDK) + // For wdf make sure to continue loading from same location as coreutil + windows::adapter_list adapters; + + // Tight coupling with KMD driver description string + auto adapter = adapters.find("IPU Compute Accelerator Device"); + + // If no matching adapter found, return coreutil path (legacy) + if (!adapter) + return bfs::path(xrt_core::dlpath("xrt_coreutil.dll")).parent_path(); + + return adapter->driver_store_path(); +#else + // Without WDK we can't query the driver store path, so return coreutil path + return bfs::path(xrt_core::dlpath("xrt_coreutil.dll")).parent_path(); +#endif +} + +bfs::path +xclbin_path(const std::string& xclbin) +{ + // For time being, xclbin repo is same as xilinx_xrt + static auto repo = xilinx_xrt(); + auto xpath = repo / xclbin; + + if (!bfs::exists(xpath)) + throw std::runtime_error("xclbin not found: " + xpath.string()); + + return xpath; +} + +} // xrt_core::detail + diff --git a/src/runtime_src/core/common/detail/xilinx_xrt.h b/src/runtime_src/core/common/detail/xilinx_xrt.h new file mode 100644 index 00000000000..b566ad6edc1 --- /dev/null +++ b/src/runtime_src/core/common/detail/xilinx_xrt.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. +#ifndef core_common_detail_xilinx_xrt_h +#define core_common_detail_xilinx_xrt_h + +#ifdef _WIN32 +# include "core/common/detail/windows/xilinx_xrt.h" +#else +# include "core/common/detail/linux/xilinx_xrt.h" +#endif + +#endif diff --git a/src/runtime_src/core/common/module_loader.cpp b/src/runtime_src/core/common/module_loader.cpp index 8864c9fdc85..05bd17ad37d 100644 --- a/src/runtime_src/core/common/module_loader.cpp +++ b/src/runtime_src/core/common/module_loader.cpp @@ -18,12 +18,12 @@ #include "core/common/dlfcn.h" #include "core/common/config_reader.h" +#include "detail/xilinx_xrt.h" #include #include #include - #ifdef _WIN32 # pragma warning (disable : 4996) #endif @@ -96,22 +96,35 @@ shim_name() } static bfs::path -xilinx_xrt() +get_xilinx_xrt() { bfs::path xrt(value_or_empty(getenv("XILINX_XRT"))); - if (xrt.empty()){ -#if defined (__aarch64__) || defined (__arm__) - xrt = bfs::path("/usr"); -#elif defined (_WIN32) - xrt = bfs::path(xrt_core::dlpath("xrt_coreutil.dll")).parent_path(); -#else - throw std::runtime_error("XILINX_XRT not set"); -#endif - } + if (!xrt.empty()) + return xrt; + + return xrt_core::detail::xilinx_xrt(); +} +static bfs::path +xilinx_xrt() +{ + static bfs::path xrt = get_xilinx_xrt(); return xrt; } +static bfs::path +xclbin_path(const std::string& xclbin) +{ + bfs::path xpath(xclbin); + if (!xpath.is_absolute()) + xpath = xrt_core::detail::xclbin_path(xclbin); + + if (bfs::exists(xpath) && bfs::is_regular_file(xpath)) + return xpath; + + throw std::runtime_error("No such xclbin '" + xpath.string() + "'"); +} + static bfs::path module_path(const std::string& module) { @@ -133,7 +146,9 @@ static bfs::path shim_path() { auto path = xilinx_xrt(); + std::cout << "xilinx_xrt: " << path << '\n'; auto name = shim_name(); + std::cout << "shim_name: " << name << '\n'; #ifdef _WIN32 path /= name + ".dll"; @@ -224,4 +239,20 @@ driver_loader() load_library(p); } +namespace environment { + +std::string +xilinx_xrt() +{ + return ::xilinx_xrt().string(); +} + +std::string +xclbin_path(const std::string& xclbin_name) +{ + return ::xclbin_path(xclbin_name).string(); +} + +} // environment + } // xrt_core diff --git a/src/runtime_src/core/common/module_loader.h b/src/runtime_src/core/common/module_loader.h index 968cc687acf..3853e4f6996 100644 --- a/src/runtime_src/core/common/module_loader.h +++ b/src/runtime_src/core/common/module_loader.h @@ -1,18 +1,6 @@ -/** - * Copyright (C) 2016-2020 Xilinx, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may - * not use this file except in compliance with the License. A copy of the - * License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2016-2022 Xilinx, Inc. All rights reserved. +// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. #ifndef xrtcore_util_module_loader_h_ #define xrtcore_util_module_loader_h_ @@ -96,6 +84,35 @@ class driver_loader driver_loader(); }; +namespace environment { + +/** + * xilinx_xrt() - Get path to XRT installation + */ +XRT_CORE_COMMON_EXPORT +std::string +xilinx_xrt(); + +/** + * xclbin_path() - Get path to xclbin directory + * + * @xclbin_name : A path relative or absolute to an xclbin file + * Return: Full path the xclbin file + * + * If the specified path is an absolute path then the function returns + * this path or throws if file does not exist. If the path is + * relative, or just a plain file name, then the function prepends the + * absolute path of a platform specific xclbin repository that + * contains the specified file. + * + * The function throws if the file does not exist. + */ +XRT_CORE_COMMON_EXPORT +std::string +xclbin_path(const std::string& xclbin_name); + +} // environment + } // end namespace xrt_core #endif