Uploaded sanitized BC250/ROCm Repository.

This commit is contained in:
Fabian
2026-08-20 00:45:43 +02:00
parent 7d2184f1e8
commit d7d22e93b3
678 changed files with 65963 additions and 1 deletions
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""
Patch 1: gfx_v10_0.c — Disable GFXOFF for Cyan Skillfish (IP 10.1.3)
Layer 1 of the v3 three-layer protection. Prevents the GPU from entering the
GFXOFF power-saving state, from which it cannot reliably wake. When the GPU is
unresponsive, any MMIO register read hangs the CPU indefinitely on the BC-250's
internal PCIe fabric (no completion timeout).
Usage:
python3 patch1_gfxoff.py /path/to/drivers/gpu/drm/amd/amdgpu
"""
import sys
import os
if len(sys.argv) < 2:
print("Usage: python3 patch1_gfxoff.py <amdgpu_source_dir>")
print(" e.g.: python3 patch1_gfxoff.py ~/kernel-build/linux-6.19.6/drivers/gpu/drm/amd/amdgpu")
sys.exit(1)
AMDGPU = sys.argv[1]
filepath = os.path.join(AMDGPU, "gfx_v10_0.c")
if not os.path.isfile(filepath):
print(f"ERROR: File not found: {filepath}")
sys.exit(1)
with open(filepath, 'r') as f:
content = f.read()
if 'BC-250' in content:
print("Already patched, skipping.")
sys.exit(0)
# Find gfx_v10_0_check_gfxoff_flag function
func_start = content.find('static void gfx_v10_0_check_gfxoff_flag')
if func_start == -1:
print("ERROR: gfx_v10_0_check_gfxoff_flag not found")
sys.exit(1)
# Find the 'default:' case in the switch inside this function
func_region_end = func_start + 2000
default_pos = content.find('\tdefault:', func_start, func_region_end)
if default_pos == -1:
default_pos = content.find('default:', func_start, func_region_end)
if default_pos == -1:
print("ERROR: default case not found in gfx_v10_0_check_gfxoff_flag")
print("Region:", content[func_start:func_start+500])
sys.exit(1)
# Insert our case BEFORE the default case
new_case = (
'\t/* ===== BC-250 v3 PATCH START ===== */\n'
'\tcase IP_VERSION(10, 1, 3):\n'
'\t\t/*\n'
'\t\t * BC-250 / Cyan Skillfish (gfx1013): GFXOFF causes the GPU to\n'
'\t\t * enter a power-saving state from which it cannot reliably wake.\n'
'\t\t * Unconditionally disable GFXOFF to prevent GPU hangs.\n'
'\t\t */\n'
'\t\tadev->pm.pp_feature &= ~PP_GFXOFF_MASK;\n'
'\t\tdev_info(adev->dev,\n'
'\t\t\t "BC-250: GFXOFF disabled to prevent GPU power-state hangs\\n");\n'
'\t\tbreak;\n'
'\t/* ===== BC-250 v3 PATCH END ===== */\n'
)
content = content[:default_pos] + new_case + content[default_pos:]
with open(filepath, 'w') as f:
f.write(content)
print("Patch 1 applied: gfx_v10_0.c — GFXOFF disable for Cyan Skillfish (IP 10.1.3)")
+217
View File
@@ -0,0 +1,217 @@
#!/usr/bin/env python3
"""
Patch 2: gmc_v10_0.c — KIQ bypass + Dead-GPU detection (5 sub-patches)
Part of Layer 2 of the v3 three-layer protection. Modifies the GPU-generation-specific
TLB flush code to:
a) Bypass KIQ ring for all gfx10.1.x → goto use_mmio
b) Add pre-spinlock 0xFFFFFFFF health check
c) Add dead-GPU detection in semaphore acquire loop
d) Add dead-GPU detection in ACK-wait loop
e) Disable KIQ-based PASID flush in hw_init
Usage:
python3 patch2_gmc.py /path/to/drivers/gpu/drm/amd/amdgpu
"""
import sys
import os
if len(sys.argv) < 2:
print("Usage: python3 patch2_gmc.py <amdgpu_source_dir>")
print(" e.g.: python3 patch2_gmc.py ~/kernel-build/linux-6.19.6/drivers/gpu/drm/amd/amdgpu")
sys.exit(1)
AMDGPU = sys.argv[1]
filepath = os.path.join(AMDGPU, "gmc_v10_0.c")
if not os.path.isfile(filepath):
print(f"ERROR: File not found: {filepath}")
sys.exit(1)
with open(filepath, 'r') as f:
content = f.read()
if 'BC-250' in content:
print("Already patched, skipping.")
sys.exit(0)
# ========================================
# Patch 2a: KIQ bypass in gmc_v10_0_flush_gpu_tlb
# Insert BEFORE the KIQ check: if (adev->gfx.kiq[0].ring.sched.ready
# ========================================
flush_func = content.find('gmc_v10_0_flush_gpu_tlb(struct amdgpu_device')
if flush_func == -1:
print("ERROR: gmc_v10_0_flush_gpu_tlb not found")
sys.exit(1)
# Find the KIQ readiness check
kiq_check = content.find('adev->gfx.kiq[0].ring.sched.ready', flush_func)
if kiq_check == -1:
kiq_check = content.find('kiq[0].ring.sched.ready', flush_func)
if kiq_check == -1:
print("ERROR: KIQ readiness check not found in flush_gpu_tlb")
sys.exit(1)
# Go back to the 'if' statement start
if_start = content.rfind('if (', flush_func, kiq_check)
if if_start == -1:
if_start = kiq_check
# Find start of line
line_start = content.rfind('\n', 0, if_start) + 1
bypass = (
'\t/* ===== BC-250 v2 PATCH: KIQ bypass ===== */\n'
'\t/* BC-250 / Cyan Skillfish (gfx1013): KIQ ring TLB flush hangs.\n'
'\t * Skip to direct MMIO register path. Widen to all gfx10.1.x.\n'
'\t */\n'
'\t{\n'
'\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);\n'
'\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0)))\n'
'\t\t\tgoto use_mmio;\n'
'\t}\n'
'\t/* ===== BC-250 v2 PATCH END ===== */\n'
)
content = content[:line_start] + bypass + content[line_start:]
# ========================================
# Add 'use_mmio:' label before the MMIO path
# Find "hub_ip = (vmhub ==" which starts the MMIO code path
# ========================================
hub_ip_assign = content.find('hub_ip = (vmhub ==', flush_func)
if hub_ip_assign == -1:
hub_ip_assign = content.find('hub_ip =', flush_func)
if hub_ip_assign == -1:
print("ERROR: hub_ip assignment not found")
sys.exit(1)
hub_line_start = content.rfind('\n', 0, hub_ip_assign) + 1
preceding_text = content[hub_line_start - 50:hub_line_start].strip()
if 'use_mmio' not in preceding_text:
content = content[:hub_line_start] + 'use_mmio:\n' + content[hub_line_start:]
# ========================================
# Patch 2b: Pre-spinlock health check after hub_ip assignment
# ========================================
hub_ip_assign = content.find('hub_ip = (vmhub ==', flush_func)
if hub_ip_assign == -1:
hub_ip_assign = content.find('hub_ip =', flush_func)
hub_line_end = content.find('\n', hub_ip_assign)
# Check for multi-line ternary
next_line = content[hub_line_end+1:hub_line_end+100]
if next_line.strip().startswith(':') or next_line.strip().startswith('?'):
hub_line_end = content.find('\n', hub_line_end + 1)
health_check = (
'\n'
'\t/* ===== BC-250 v3 PATCH: Pre-spinlock health check ===== */\n'
'\t{\n'
'\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);\n'
'\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) &&\n'
'\t\t (gc_ver < IP_VERSION(10, 2, 0))) {\n'
'\t\t\ttmp = RREG32_RLC_NO_KIQ(ack, hub_ip);\n'
'\t\t\tif (tmp == 0xFFFFFFFF) {\n'
'\t\t\t\tdev_err_ratelimited(adev->dev,\n'
'\t\t\t\t\t"BC-250: GPU unreachable (MMIO 0xFFFFFFFF), "\n'
'\t\t\t\t\t"skipping TLB flush vmid=%u hub=%u\\n",\n'
'\t\t\t\t\tvmid, vmhub);\n'
'\t\t\t\treturn;\n'
'\t\t\t}\n'
'\t\t}\n'
'\t}\n'
'\t/* ===== BC-250 v3 PATCH END ===== */\n'
)
content = content[:hub_line_end] + health_check + content[hub_line_end:]
# ========================================
# Patch 2c: In-spinlock semaphore dead-GPU check
# Find: "if (tmp & 0x1)" inside the sem acquire loop
# ========================================
sem_marker = content.find('semaphore acq', flush_func)
if sem_marker == -1:
sem_marker = content.find('a read return value of 1 means semaphore', flush_func)
if sem_marker != -1:
tmp_check = content.find('if (tmp & 0x1)', sem_marker)
if tmp_check != -1:
tmp_line_start = content.rfind('\n', 0, tmp_check) + 1
sem_dead = (
'\t\t\t\t/* ===== BC-250 v3 PATCH: sem dead-GPU check ===== */\n'
'\t\t\t\tif (tmp == 0xFFFFFFFF) {\n'
'\t\t\t\t\tdev_err_ratelimited(adev->dev,\n'
'\t\t\t\t\t\t"BC-250: GPU died during sem acquire (0xFFFFFFFF)\\n");\n'
'\t\t\t\t\tspin_unlock(&adev->gmc.invalidate_lock);\n'
'\t\t\t\t\treturn;\n'
'\t\t\t\t}\n'
'\t\t\t\t/* ===== BC-250 v3 PATCH END ===== */\n'
)
content = content[:tmp_line_start] + sem_dead + content[tmp_line_start:]
else:
print("WARNING: 'if (tmp & 0x1)' not found after semaphore comment")
else:
print("WARNING: semaphore comment not found — skipping sem dead-GPU check")
# ========================================
# Patch 2d: ACK-wait loop dead-GPU check
# Find: "Wait for ACK with a delay" or "tmp &= 1 << vmid"
# ========================================
ack_comment = content.find('Wait for ACK with a delay', flush_func)
if ack_comment != -1:
tmp_mask = content.find('tmp &= 1 << vmid', ack_comment)
if tmp_mask != -1:
mask_line_start = content.rfind('\n', 0, tmp_mask) + 1
ack_dead = (
'\t\t\t\t/* ===== BC-250 v3 PATCH: ACK-wait dead-GPU check ===== */\n'
'\t\t\t\tif (tmp == 0xFFFFFFFF) {\n'
'\t\t\t\t\tdev_err_ratelimited(adev->dev,\n'
'\t\t\t\t\t\t"BC-250: GPU died during TLB flush ACK wait (0xFFFFFFFF)\\n");\n'
'\t\t\t\t\tif (use_semaphore)\n'
'\t\t\t\t\t\tWREG32_RLC_NO_KIQ(sem, 0, hub_ip);\n'
'\t\t\t\t\tspin_unlock(&adev->gmc.invalidate_lock);\n'
'\t\t\t\t\treturn;\n'
'\t\t\t\t}\n'
'\t\t\t\t/* ===== BC-250 v3 PATCH END ===== */\n'
)
content = content[:mask_line_start] + ack_dead + content[mask_line_start:]
else:
print("WARNING: 'tmp &= 1 << vmid' not found")
else:
print("WARNING: 'Wait for ACK with a delay' comment not found")
# ========================================
# Patch 2e: gmc_v10_0_hw_init — PASID KIQ disable
# ========================================
hw_init = content.find('static int gmc_v10_0_hw_init')
if hw_init == -1:
print("ERROR: gmc_v10_0_hw_init not found")
sys.exit(1)
pasid_kiq = content.find('flush_pasid_uses_kiq', hw_init)
if pasid_kiq == -1:
print("ERROR: flush_pasid_uses_kiq not found in hw_init")
sys.exit(1)
# Get the full line
pasid_line_start = content.rfind('\n', 0, pasid_kiq) + 1
pasid_line_end = content.find('\n', pasid_kiq)
new_block = (
'\t/* ===== BC-250 v2 PATCH: Disable KIQ-based PASID flush ===== */\n'
'\t{\n'
'\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);\n'
'\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0)))\n'
'\t\t\tadev->gmc.flush_pasid_uses_kiq = false;\n'
'\t\telse\n'
'\t\t\tadev->gmc.flush_pasid_uses_kiq = !amdgpu_emu_mode;\n'
'\t}\n'
'\t/* ===== BC-250 v2 PATCH END ===== */'
)
content = content[:pasid_line_start] + new_block + content[pasid_line_end:]
with open(filepath, 'w') as f:
f.write(content)
print("Patch 2 applied: gmc_v10_0.c — 5 sub-patches (KIQ bypass + dead-GPU detection)")
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
"""
Patch 3: amdgpu_gmc.c — KIQ bypass + Dead-GPU detection (2 sub-patches)
Part of Layer 2 of the v3 three-layer protection. Modifies the centralized
(generation-agnostic) TLB flush code to:
a) Bypass KIQ in amdgpu_gmc_flush_gpu_tlb_pasid — direct MMIO callout
b) Bypass KIQ + dead-GPU detection in amdgpu_gmc_fw_reg_write_reg_wait
Usage:
python3 patch3_amdgpu_gmc.py /path/to/drivers/gpu/drm/amd/amdgpu
"""
import sys
import os
if len(sys.argv) < 2:
print("Usage: python3 patch3_amdgpu_gmc.py <amdgpu_source_dir>")
print(" e.g.: python3 patch3_amdgpu_gmc.py ~/kernel-build/linux-6.19.6/drivers/gpu/drm/amd/amdgpu")
sys.exit(1)
AMDGPU = sys.argv[1]
filepath = os.path.join(AMDGPU, "amdgpu_gmc.c")
if not os.path.isfile(filepath):
print(f"ERROR: File not found: {filepath}")
sys.exit(1)
with open(filepath, 'r') as f:
content = f.read()
if 'BC-250' in content:
print("Already patched, skipping.")
sys.exit(0)
# ========================================
# Patch 3a: KIQ bypass in amdgpu_gmc_flush_gpu_tlb_pasid
# Insert AFTER down_read_trylock block, BEFORE KIQ ring code
# ========================================
func_start = content.find('int amdgpu_gmc_flush_gpu_tlb_pasid')
if func_start == -1:
print("ERROR: amdgpu_gmc_flush_gpu_tlb_pasid not found")
sys.exit(1)
# Find the trylock check
trylock = content.find('down_read_trylock', func_start)
if trylock == -1:
print("ERROR: down_read_trylock not found")
sys.exit(1)
# Find "return 0;" after trylock
return_0 = content.find('return 0;', trylock)
end_line = content.find('\n', return_0) + 1
bypass = (
'\n'
'\t/* ===== BC-250 v2 PATCH: KIQ bypass for PASID flush ===== */\n'
'\t{\n'
'\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);\n'
'\t\tpr_warn_once("amdgpu: flush_gpu_tlb_pasid called, GC_HWIP=0x%08x "\n'
'\t\t\t "(10.1.3=0x%08x) kiq_flag=%d\\n",\n'
'\t\t\t gc_ver, IP_VERSION(10, 1, 3),\n'
'\t\t\t adev->gmc.flush_pasid_uses_kiq);\n'
'\n'
'\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0))) {\n'
'\t\t\tpr_warn_once("amdgpu: BC-250 KIQ bypass active "\n'
'\t\t\t\t "(gc_ver=0x%08x)\\n", gc_ver);\n'
'\t\t\tadev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid,\n'
'\t\t\t\t\t\t\t\t flush_type, all_hub,\n'
'\t\t\t\t\t\t\t\t inst);\n'
'\t\t\tr = 0;\n'
'\t\t\tgoto error_unlock_reset;\n'
'\t\t}\n'
'\t}\n'
'\t/* ===== BC-250 v2 PATCH END ===== */\n'
)
content = content[:end_line] + bypass + content[end_line:]
# ========================================
# Patch 3b: KIQ bypass + dead-GPU in amdgpu_gmc_fw_reg_write_reg_wait
# Insert BEFORE the KIQ ring submission code
# ========================================
func2_start = content.find('void amdgpu_gmc_fw_reg_write_reg_wait')
if func2_start == -1:
print("ERROR: amdgpu_gmc_fw_reg_write_reg_wait not found")
sys.exit(1)
# Find the first operational code after variable declarations
spinlock = content.find('spin_lock_irqsave', func2_start)
if spinlock == -1:
spinlock = content.find('ring->sched.ready', func2_start)
if spinlock == -1:
spinlock = content.find('if (', func2_start + 200)
if spinlock == -1:
print("ERROR: Could not find KIQ code in fw_reg_write_reg_wait")
sys.exit(1)
spinlock_line_start = content.rfind('\n', 0, spinlock) + 1
bypass2 = (
'\t/* ===== BC-250 v2+v3 PATCH: KIQ bypass + dead-GPU detection ===== */\n'
'\t{\n'
'\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);\n'
'\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0))) {\n'
'\t\t\tuint32_t tmp;\n'
'\n'
'\t\t\tpr_warn_once("amdgpu: BC-250 KIQ bypass active in "\n'
'\t\t\t\t "fw_reg_write_reg_wait (gc=0x%08x)\\n", gc_ver);\n'
'\n'
'\t\t\t/* v3: Health-check read before writing */\n'
'\t\t\ttmp = RREG32_NO_KIQ(reg1);\n'
'\t\t\tif (tmp == 0xFFFFFFFF) {\n'
'\t\t\t\tdev_err_ratelimited(adev->dev,\n'
'\t\t\t\t\t"BC-250: GPU unreachable in fw_reg_write_reg_wait "\n'
'\t\t\t\t\t"(reg1=0x%x returned 0xFFFFFFFF), skipping\\n", reg1);\n'
'\t\t\t\treturn;\n'
'\t\t\t}\n'
'\n'
'\t\t\tWREG32_NO_KIQ(reg0, ref);\n'
'\t\t\tfor (cnt = 0; cnt < adev->usec_timeout; cnt++) {\n'
'\t\t\t\ttmp = RREG32_NO_KIQ(reg1);\n'
'\t\t\t\t/* v3: Dead-GPU detection in polling loop */\n'
'\t\t\t\tif (tmp == 0xFFFFFFFF) {\n'
'\t\t\t\t\tdev_err_ratelimited(adev->dev,\n'
'\t\t\t\t\t\t"BC-250: GPU died during reg_write_reg_wait "\n'
'\t\t\t\t\t\t"(0xFFFFFFFF at reg1=0x%x)\\n", reg1);\n'
'\t\t\t\t\treturn;\n'
'\t\t\t\t}\n'
'\t\t\t\tif ((tmp & mask) == (ref & mask))\n'
'\t\t\t\t\treturn;\n'
'\t\t\t\tudelay(1);\n'
'\t\t\t}\n'
'\t\t\tdev_warn(adev->dev, "BC-250: MMIO reg write/wait timeout "\n'
'\t\t\t\t "reg0=0x%x reg1=0x%x\\n", reg0, reg1);\n'
'\t\t\treturn;\n'
'\t\t}\n'
'\t}\n'
'\t/* ===== BC-250 v2+v3 PATCH END ===== */\n'
)
content = content[:spinlock_line_start] + bypass2 + content[spinlock_line_start:]
with open(filepath, 'w') as f:
f.write(content)
print("Patch 3 applied: amdgpu_gmc.c — 2 sub-patches (KIQ bypass + dead-GPU detection)")
+73
View File
@@ -0,0 +1,73 @@
import struct
MODULE = '/tmp/amdgpu_installed.ko'
OUTPUT = '/tmp/amdgpu_v3.ko'
old_lea = bytes.fromhex('49f7ffff') # -(2231) for MAX=2230
new_lea = bytes.fromhex('3bf6ffff') # -(2501) for MAX=2500
old_cmp = bytes.fromhex('a6f8ffff') # -(1882) for range 350-2230
new_cmp = bytes.fromhex('98f7ffff') # -(2152) for range 350-2500
with open(MODULE, 'rb') as f:
data = bytearray(f.read())
print(f'Module size: {len(data)} bytes')
total_patches = 0
# Search ALL occurrences of old_lea and patch nearby old_cmp
pos = 0
while True:
idx = data.find(old_lea, pos)
if idx == -1:
break
ctx = bytes(data[max(0,idx-8):idx+16]).hex()
print(f'\nFound old_lea at 0x{idx:x}: ...{ctx}...')
# Look for matching cmp within 20 bytes
found_cmp = False
for j in range(idx+4, min(idx+30, len(data)-4)):
if data[j:j+4] == old_cmp:
print(f' Found matching cmp at 0x{j:x}')
data[idx:idx+4] = new_lea
data[j:j+4] = new_cmp
print(f' Patched lea: {old_lea.hex()} -> {new_lea.hex()}')
print(f' Patched cmp: {old_cmp.hex()} -> {new_cmp.hex()}')
total_patches += 2
found_cmp = True
break
if not found_cmp:
# Maybe cmp is after more bytes, or uses a different range
# Check wider range and also look for the original cmp value
for j in range(max(0, idx-30), min(idx+60, len(data)-4)):
if j == idx: continue
val = data[j:j+4]
if val == old_cmp:
print(f' Found cmp at 0x{j:x} (wider search)')
data[idx:idx+4] = new_lea
data[j:j+4] = new_cmp
total_patches += 2
found_cmp = True
break
if not found_cmp:
print(f' WARNING: No matching cmp found near 0x{idx:x}')
# Patch just the lea anyway — it might still help
data[idx:idx+4] = new_lea
print(f' Patched lea only: {old_lea.hex()} -> {new_lea.hex()}')
total_patches += 1
pos = idx + 1
# Double check: any remaining old patterns?
remaining_lea = bytes(data).count(old_lea)
remaining_cmp = bytes(data).count(old_cmp)
print(f'\nRemaining old_lea (49f7ffff): {remaining_lea}')
print(f'Remaining old_cmp (a6f8ffff): {remaining_cmp}')
with open(OUTPUT, 'wb') as f:
f.write(data)
print(f'\nTotal patches applied: {total_patches}')
print(f'Output: {OUTPUT}')
+68
View File
@@ -0,0 +1,68 @@
import struct, sys
MODULE = '/tmp/amdgpu_installed.ko'
OUTPUT = '/tmp/amdgpu_repatched.ko'
# Current validation values (from patch_validation.py): MIN=350, MAX=2230
# lea -(2231) = 0xFFFFF749 -> 49 F7 FF FF
# cmp -(2230-350+2) = -1882 = 0xFFFFF8A6 -> A6 F8 FF FF
# New validation values: MIN=350, MAX=2500
# lea -(2501) = 0xFFFFF63B -> 3B F6 FF FF
# cmp -(2500-350+2) = -2152 = 0xFFFFF798 -> 98 F7 FF FF
old_lea = bytes.fromhex('49f7ffff')
new_lea = bytes.fromhex('3bf6ffff')
old_cmp = bytes.fromhex('a6f8ffff')
new_cmp = bytes.fromhex('98f7ffff')
import shutil
shutil.copy2(MODULE, OUTPUT)
with open(OUTPUT, 'rb') as f:
data = f.read()
print(f'Module size: {len(data)} bytes')
print()
# Find and patch all validation lea/cmp pairs
# Expected offsets: around 0x330186, 0x33018D, 0x3301F9, 0x3301FE
# But these may have shifted if the module binary changed
patches_applied = 0
# Search for old lea displacement in the cyan_skillfish function region (0x330000-0x330400)
for search_start in range(0x330000, 0x330400):
if data[search_start:search_start+4] == old_lea:
# Check if there's the cmp value nearby (within 20 bytes)
for j in range(search_start+4, search_start+24):
if data[j:j+4] == old_cmp:
print(f' Found validation pair: lea@0x{search_start:x}, cmp@0x{j:x}')
# Patch both
data = bytearray(data)
data[search_start:search_start+4] = new_lea
data[j:j+4] = new_cmp
patches_applied += 2
print(f' Patched lea: {old_lea.hex()} -> {new_lea.hex()}')
print(f' Patched cmp: {old_cmp.hex()} -> {new_cmp.hex()}')
break
if patches_applied == 0:
print('WARNING: No validation patches found at expected offsets!')
print('Searching wider range...')
# Wider search
pos = 0
while True:
idx = bytes(data).find(old_lea, pos)
if idx == -1: break
print(f' Found old_lea at 0x{idx:x}')
for j in range(idx+4, min(idx+24, len(data)-4)):
if data[j:j+4] == old_cmp:
print(f' Found matching cmp at 0x{j:x}')
break
pos = idx + 1
with open(OUTPUT, 'wb') as f:
f.write(data)
print(f'\nDone. Applied {patches_applied} patches to {OUTPUT}')
+31
View File
@@ -0,0 +1,31 @@
import struct
with open('/tmp/amdgpu_installed.ko', 'rb') as f:
data = f.read()
# Check if the old validation values are still present
old_lea = bytes.fromhex('49f7ffff') # -(2231)
new_lea = bytes.fromhex('3bf6ffff') # -(2501)
old_cmp = bytes.fromhex('a6f8ffff') # -(1882)
new_cmp = bytes.fromhex('98f7ffff') # -(2152)
orig_lea = bytes.fromhex('2ff8ffff') # -(2001) original
print("Checking offsets in INSTALLED module:")
for offset in [0x330186, 0x33018D, 0x3301F9, 0x3301FE]:
val = data[offset:offset+4]
print(f" 0x{offset:x}: {val.hex()}", end="")
if val == old_lea: print(" <-- OLD lea (2230)")
elif val == new_lea: print(" <-- NEW lea (2500)")
elif val == old_cmp: print(" <-- OLD cmp (2230)")
elif val == new_cmp: print(" <-- NEW cmp (2500)")
elif val == orig_lea: print(" <-- ORIGINAL lea (2000)")
else: print(" <-- UNKNOWN")
print("\nSearching for old validation pattern (49f7ffff) in module:")
pos = 0
while True:
idx = data.find(old_lea, pos)
if idx == -1: break
ctx = data[max(0,idx-8):idx+12].hex()
print(f" Found at 0x{idx:x}: {ctx}")
pos = idx + 1