diff options
| author | swurl <swurl@swurl.xyz> | 2025-05-31 02:33:02 -0400 |
|---|---|---|
| committer | swurl <swurl@swurl.xyz> | 2025-05-31 02:33:02 -0400 |
| commit | 6c655321e6b04b8648802e296bb5ca11d290ae68 (patch) | |
| tree | 5c906511e4a2910ce8b806bb9d763b524c28619e /externals/libadrenotools/src/bcenabler.cpp | |
| parent | c0cceff365f754f8859f4c9a0362ebdba7ea388b (diff) | |
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'externals/libadrenotools/src/bcenabler.cpp')
| -rw-r--r-- | externals/libadrenotools/src/bcenabler.cpp | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/externals/libadrenotools/src/bcenabler.cpp b/externals/libadrenotools/src/bcenabler.cpp new file mode 100644 index 0000000000..1deb41df40 --- /dev/null +++ b/externals/libadrenotools/src/bcenabler.cpp @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright © 2021 Billy Laws + +#include <fstream> +#include <string> +#include <cstring> +#include <sys/mman.h> +#include <unistd.h> +#include <adrenotools/bcenabler.h> +#include "gen/bcenabler_patch.h" + +enum adrenotools_bcn_type adrenotools_get_bcn_type(uint32_t major, uint32_t minor, uint32_t vendorId) { + if (vendorId != 0x5143 || major != 512) + return ADRENOTOOLS_BCN_INCOMPATIBLE; + + if (minor >= 514) + return ADRENOTOOLS_BCN_BLOB; + + return ADRENOTOOLS_BCN_PATCH; +} + +// Searches /proc/self/maps for the first free page after the given address +static void *find_free_page(uintptr_t address) { + std::ifstream procMaps("/proc/self/maps"); + + uintptr_t end{}; + + for (std::string line; std::getline(procMaps, line); ) { + std::size_t addressSeparator{line.find('-')}; + uintptr_t start{std::strtoull(line.substr(0, addressSeparator).c_str(), nullptr, 16)}; + + if (end > address && start != end) + return reinterpret_cast<void *>(end); + + end = std::strtoull(line.substr(addressSeparator + 1, line.find( ' ')).c_str(), nullptr, 16); + } + + return nullptr; +} + +static void *align_ptr(void *ptr) { + return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) & ~(getpagesize() - 1)); +} + +bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { + union Branch { + struct { + int32_t offset : 26; //!< 26-bit branch offset + uint8_t sig : 6; //!< 6-bit signature (0x25 for linked, 0x5 for jump) + }; + + uint32_t raw{}; + }; + static_assert(sizeof(Branch) == 4, "Branch size is invalid"); + + // Find the nearest unmapped page where we can place patch code + void *patchPage{find_free_page(reinterpret_cast<uintptr_t>(vkGetPhysicalDeviceFormatPropertiesFn))}; + if (!patchPage) + return false; + + // Map patch region + void *ptr{mmap(patchPage, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0)}; + if (ptr != patchPage) + return false; + + // Allow reading from the blob's .text section since some devices enable ---X + // Protect two pages just in case we happen to land on a page boundary + if (mprotect(align_ptr(vkGetPhysicalDeviceFormatPropertiesFn), getpagesize() * 2, PROT_WRITE | PROT_READ | PROT_EXEC)) + return false; + + // First branch in this function is targeted at the function we want to patch + Branch *blInst{reinterpret_cast<Branch *>(vkGetPhysicalDeviceFormatPropertiesFn)}; + + constexpr uint8_t BranchLinkSignature{0x25}; + + // Search for first instruction with the BL signature + while (blInst->sig != BranchLinkSignature) + blInst++; + + // Internal QGL format conversion function that we need to patch + uint32_t *convFormatFn{reinterpret_cast<uint32_t *>(blInst) + blInst->offset}; + + // See mprotect call above + // This time we also set PROT_WRITE so we can write our patch to the page + if (mprotect(align_ptr(convFormatFn), getpagesize() * 2, PROT_WRITE | PROT_READ | PROT_EXEC)) + return false; + + // This would normally set the default result to 0 (error) in the format not found case + constexpr uint32_t ClearResultSignature{0x2a1f03e0}; + + // We replace it with a branch to our own extended if statement which adds in the extra things for BCn + uint32_t *clearResultPtr{convFormatFn}; + while (*clearResultPtr != ClearResultSignature) + clearResultPtr++; + + // Ensure we don't write out of bounds + if (PatchRawData_size > getpagesize()) + return false; + + // Copy the patch function to our mapped page + memcpy(patchPage, PatchRawData, PatchRawData_size); + + // Fixup the patch code so it correctly returns back to the driver after running + constexpr uint32_t PatchReturnFixupMagic{0xffffffff}; + constexpr uint8_t BranchSignature{0x5}; + + uint32_t *fixupTargetPtr{clearResultPtr + 1}; + auto *fixupPtr{reinterpret_cast<uint32_t *>(patchPage)}; + for (long unsigned int i{}; i < (PatchRawData_size / sizeof(uint32_t)); i++, fixupPtr++) { + if (*fixupPtr == PatchReturnFixupMagic) { + Branch branchToDriver{ + { + .offset = static_cast<int32_t>((reinterpret_cast<intptr_t>(fixupTargetPtr) - reinterpret_cast<intptr_t>(fixupPtr)) / sizeof(int32_t)), + .sig = BranchSignature, + } + }; + + *fixupPtr = branchToDriver.raw; + } + } + + Branch branchToPatch{ + { + .offset = static_cast<int32_t>((reinterpret_cast<intptr_t>(patchPage) - reinterpret_cast<intptr_t>(clearResultPtr)) / sizeof(int32_t)), + .sig = BranchSignature, + } + }; + + *clearResultPtr = branchToPatch.raw; + + asm volatile("ISB"); + + // Done! + return true; +} |
