590 lines
21 KiB
Python
590 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
BC-250 v3 amdgpu kernel module build script.
|
|
Downloads kernel source, applies v3 patches, builds the module.
|
|
Runs entirely over SSH on the BC250.
|
|
"""
|
|
import paramiko
|
|
import time
|
|
import sys
|
|
|
|
KERNEL_VER = "6.19.6"
|
|
KERNEL_FULL = "6.19.6-2-cachyos"
|
|
BUILD_DIR = "/home/fabian/kernel-build"
|
|
SRC_DIR = f"{BUILD_DIR}/linux-{KERNEL_VER}"
|
|
AMDGPU_DIR = f"{SRC_DIR}/drivers/gpu/drm/amd/amdgpu"
|
|
|
|
ssh = paramiko.SSHClient()
|
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
ssh.connect('192.168.178.150', username='fabian', key_filename=r'C:\Users\fabia\.ssh\id_ed25519')
|
|
sftp = ssh.open_sftp()
|
|
|
|
def run(cmd, desc="", timeout=300):
|
|
if desc:
|
|
print(f"\n{'='*60}")
|
|
print(f" {desc}")
|
|
print(f"{'='*60}")
|
|
stdin, stdout, stderr = ssh.exec_command(cmd, timeout=timeout)
|
|
out = stdout.read().decode().strip()
|
|
err = stderr.read().decode().strip()
|
|
rc = stdout.channel.recv_exit_status()
|
|
if out:
|
|
# Truncate very long output
|
|
lines = out.split('\n')
|
|
if len(lines) > 50:
|
|
print('\n'.join(lines[:20]))
|
|
print(f" ... ({len(lines)-40} lines omitted) ...")
|
|
print('\n'.join(lines[-20:]))
|
|
else:
|
|
print(out)
|
|
if err and rc != 0:
|
|
print(f"STDERR: {err[:500]}")
|
|
if rc != 0:
|
|
print(f"EXIT CODE: {rc}")
|
|
return out, rc
|
|
|
|
# ============================================================
|
|
# Step 1: Download kernel source
|
|
# ============================================================
|
|
out, rc = run(f"test -d {SRC_DIR} && echo EXISTS || echo MISSING")
|
|
if "EXISTS" in out:
|
|
print(f"\nKernel source already exists at {SRC_DIR}")
|
|
else:
|
|
run(f"mkdir -p {BUILD_DIR}", "Creating build directory")
|
|
|
|
# Download kernel source tarball
|
|
tarball_url = f"https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-{KERNEL_VER}.tar.xz"
|
|
run(f"cd {BUILD_DIR} && curl -LO {tarball_url}",
|
|
f"Downloading linux-{KERNEL_VER}.tar.xz from kernel.org", timeout=600)
|
|
|
|
# Extract
|
|
run(f"cd {BUILD_DIR} && tar xf linux-{KERNEL_VER}.tar.xz",
|
|
"Extracting kernel source", timeout=300)
|
|
|
|
# Clean up tarball
|
|
run(f"rm {BUILD_DIR}/linux-{KERNEL_VER}.tar.xz")
|
|
|
|
# ============================================================
|
|
# Step 2: Prepare build environment
|
|
# ============================================================
|
|
run(f"""cd {SRC_DIR} && \\
|
|
cp /usr/lib/modules/{KERNEL_FULL}/build/.config . && \\
|
|
cp /usr/lib/modules/{KERNEL_FULL}/build/Module.symvers . && \\
|
|
cp /usr/lib/modules/{KERNEL_FULL}/build/localversion.10-pkgrel . && \\
|
|
cp /usr/lib/modules/{KERNEL_FULL}/build/localversion.20-pkgname . && \\
|
|
echo 'Build files copied'""",
|
|
"Copying kernel config, symvers, and localversion files")
|
|
|
|
# Prepare the kernel tree (generate required headers)
|
|
run(f"cd {SRC_DIR} && make LLVM=1 olddefconfig 2>&1 | tail -5",
|
|
"Running olddefconfig", timeout=120)
|
|
run(f"cd {SRC_DIR} && make LLVM=1 modules_prepare 2>&1 | tail -10",
|
|
"Preparing modules build", timeout=120)
|
|
|
|
# ============================================================
|
|
# Step 3: Verify the source files exist and find patch targets
|
|
# ============================================================
|
|
print("\n" + "="*60)
|
|
print(" Verifying source files")
|
|
print("="*60)
|
|
|
|
for f in ["gfx_v10_0.c", "gmc_v10_0.c", "amdgpu_gmc.c"]:
|
|
out, rc = run(f"wc -l {AMDGPU_DIR}/{f}")
|
|
print(f" {f}: {out.split()[0]} lines")
|
|
|
|
# ============================================================
|
|
# Step 4: Apply v3 patches
|
|
# ============================================================
|
|
|
|
# --- Patch 1: gfx_v10_0.c - Disable GFXOFF for Cyan Skillfish ---
|
|
print("\n" + "="*60)
|
|
print(" Patch 1: gfx_v10_0.c — GFXOFF Disable")
|
|
print("="*60)
|
|
|
|
# Find the exact function and add our case
|
|
# Look for the switch statement in gfx_v10_0_check_gfxoff_flag
|
|
out, rc = run(f"grep -n 'case IP_VERSION(10, 1, 10)' {AMDGPU_DIR}/gfx_v10_0.c")
|
|
if rc != 0:
|
|
print("ERROR: Could not find IP_VERSION(10,1,10) in gfx_v10_0.c!")
|
|
sys.exit(1)
|
|
|
|
# Check if already patched
|
|
out, rc = run(f"grep -c 'BC-250' {AMDGPU_DIR}/gfx_v10_0.c")
|
|
if out.strip() != "0":
|
|
print("Already patched, skipping.")
|
|
else:
|
|
# The patch: add a case for IP_VERSION(10, 1, 3) after the existing default: break;
|
|
# We need to find the closing "default:" case in gfx_v10_0_check_gfxoff_flag
|
|
# and insert our case before it
|
|
|
|
patch1_script = r"""
|
|
import re
|
|
|
|
filepath = '""" + AMDGPU_DIR + r"""/gfx_v10_0.c'
|
|
with open(filepath, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Find the pattern: after the IP_VERSION(10,1,10) case block, before 'default:'
|
|
# in gfx_v10_0_check_gfxoff_flag function
|
|
old_pattern = '\tdefault:\n\t\tbreak;\n\t}\n}'
|
|
# Find it specifically near gfx_v10_0_check_gfxoff_flag
|
|
func_start = content.find('static void gfx_v10_0_check_gfxoff_flag')
|
|
if func_start == -1:
|
|
print("ERROR: function not found")
|
|
exit(1)
|
|
|
|
# Find the 'default: break; } }' within this function
|
|
func_region = content[func_start:func_start+2000]
|
|
default_pos = func_region.find('\tdefault:\n\t\tbreak;\n\t}\n}')
|
|
if default_pos == -1:
|
|
# Try different whitespace
|
|
default_pos = func_region.find('default:\n')
|
|
if default_pos == -1:
|
|
print("ERROR: default case not found")
|
|
print("Function region:\n" + func_region[:500])
|
|
exit(1)
|
|
# Get a bit more context
|
|
context = func_region[default_pos-100:default_pos+100]
|
|
print(f"Found default at offset {default_pos}, context:\n{context}")
|
|
exit(1)
|
|
|
|
insert_pos = func_start + default_pos
|
|
|
|
new_case = '''\t/* ===== BC-250 v3 PATCH START ===== */
|
|
\tcase IP_VERSION(10, 1, 3):
|
|
\t\t/*
|
|
\t\t * BC-250 / Cyan Skillfish (gfx1013): GFXOFF causes the GPU to
|
|
\t\t * enter a power-saving state from which it cannot reliably wake.
|
|
\t\t * Unconditionally disable GFXOFF to prevent GPU hangs.
|
|
\t\t */
|
|
\t\tadev->pm.pp_feature &= ~PP_GFXOFF_MASK;
|
|
\t\tdev_info(adev->dev,
|
|
\t\t\t "BC-250: GFXOFF disabled to prevent GPU power-state hangs\\n");
|
|
\t\tbreak;
|
|
\t/* ===== BC-250 v3 PATCH END ===== */
|
|
'''
|
|
|
|
content = content[:insert_pos] + new_case + content[insert_pos:]
|
|
|
|
with open(filepath, 'w') as f:
|
|
f.write(content)
|
|
|
|
print("Patch 1 applied successfully")
|
|
"""
|
|
|
|
# Write patch script to remote
|
|
with sftp.open('/tmp/patch1.py', 'w') as f:
|
|
f.write(patch1_script)
|
|
run("python3 /tmp/patch1.py", "Applying Patch 1")
|
|
run(f"grep -A15 'gfx_v10_0_check_gfxoff_flag' {AMDGPU_DIR}/gfx_v10_0.c | head -30",
|
|
"Verify Patch 1")
|
|
|
|
# --- Patch 2: gmc_v10_0.c - KIQ bypass + Dead-GPU detection ---
|
|
print("\n" + "="*60)
|
|
print(" Patch 2: gmc_v10_0.c — KIQ bypass + Dead-GPU")
|
|
print("="*60)
|
|
|
|
out, rc = run(f"grep -c 'BC-250' {AMDGPU_DIR}/gmc_v10_0.c")
|
|
if out.strip() != "0":
|
|
print("Already patched, skipping.")
|
|
else:
|
|
patch2_script = r"""
|
|
filepath = '""" + AMDGPU_DIR + r"""/gmc_v10_0.c'
|
|
with open(filepath, 'r') as f:
|
|
content = f.read()
|
|
|
|
# ===== Patch 2a: KIQ bypass in gmc_v10_0_flush_gpu_tlb =====
|
|
# Find the line: if (adev->gfx.kiq[0].ring.sched.ready && !adev->enable_mes &&
|
|
# Insert KIQ bypass BEFORE it
|
|
|
|
kiq_check = 'if (adev->gfx.kiq[0].ring.sched.ready && !adev->enable_mes &&'
|
|
pos = content.find(kiq_check)
|
|
if pos == -1:
|
|
# Try alternate: might use kiq_inst or different formatting
|
|
kiq_check = 'if (adev->gfx.kiq['
|
|
pos = content.find(kiq_check)
|
|
if pos == -1:
|
|
print("ERROR: Could not find KIQ check in gmc_v10_0_flush_gpu_tlb")
|
|
exit(1)
|
|
|
|
# Make sure we're in gmc_v10_0_flush_gpu_tlb
|
|
func_start = content.rfind('gmc_v10_0_flush_gpu_tlb', 0, pos)
|
|
if func_start == -1:
|
|
print("ERROR: Not in gmc_v10_0_flush_gpu_tlb?")
|
|
exit(1)
|
|
|
|
# Find the start of the line (go back to newline)
|
|
line_start = content.rfind('\n', 0, pos) + 1
|
|
# Get indentation
|
|
indent = ''
|
|
for ch in content[line_start:pos]:
|
|
if ch in ' \t':
|
|
indent += ch
|
|
else:
|
|
break
|
|
|
|
kiq_bypass = indent + """/* ===== BC-250 v2 PATCH: KIQ bypass ===== */
|
|
""" + indent + """/* BC-250 / Cyan Skillfish (gfx1013): KIQ ring TLB flush hangs this GPU.
|
|
""" + indent + """ * Skip to direct MMIO register path. Widen to all gfx10.1.x for safety.
|
|
""" + indent + """ */
|
|
""" + indent + """{
|
|
""" + indent + """\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
|
|
""" + indent + """\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0)))
|
|
""" + indent + """\t\tgoto use_mmio;
|
|
""" + indent + """}
|
|
""" + indent + """/* ===== BC-250 v2 PATCH END ===== */
|
|
"""
|
|
|
|
content = content[:line_start] + kiq_bypass + content[line_start:]
|
|
|
|
# ===== Patch 2b: Pre-spinlock health check before MMIO =====
|
|
# Find: "hub_ip = (vmhub == AMDGPU_GFXHUB(0))" in same function
|
|
# This is the start of the MMIO path ("use_mmio:" label or the code after it)
|
|
|
|
use_mmio_label = content.find('use_mmio:', content.find('gmc_v10_0_flush_gpu_tlb'))
|
|
if use_mmio_label == -1:
|
|
# The label might not exist yet - we need to find where the MMIO path starts
|
|
# In vanilla kernel it should be after "/* This path is needed before KIQ/MES/GFXOFF"
|
|
mmio_comment = content.find('This path is needed before KIQ')
|
|
if mmio_comment == -1:
|
|
print("WARNING: Could not find MMIO path - label may already exist from bypass")
|
|
|
|
# Find "hub_ip = (vmhub == AMDGPU_GFXHUB(0))" after our inserted code
|
|
hub_ip_line = 'hub_ip = (vmhub == AMDGPU_GFXHUB(0))'
|
|
hub_pos = content.find(hub_ip_line, content.find('gmc_v10_0_flush_gpu_tlb'))
|
|
if hub_pos == -1:
|
|
print("ERROR: Could not find hub_ip assignment")
|
|
exit(1)
|
|
|
|
# If there's no use_mmio label, add one before the hub_ip line
|
|
if content.find('use_mmio:', content.find('gmc_v10_0_flush_gpu_tlb')) == -1:
|
|
# Find the line start before hub_ip
|
|
hub_line_start = content.rfind('\n', 0, hub_pos) + 1
|
|
content = content[:hub_line_start] + "use_mmio:\n" + content[hub_line_start:]
|
|
|
|
# Re-find hub_ip position after possible label insertion
|
|
hub_pos = content.find(hub_ip_line, content.find('gmc_v10_0_flush_gpu_tlb'))
|
|
hub_line_end = content.find('\n', hub_pos)
|
|
|
|
# Insert health check AFTER hub_ip assignment line
|
|
health_check = """
|
|
|
|
\t/* ===== BC-250 v3 PATCH: Pre-spinlock health check ===== */
|
|
\t/*
|
|
\t * BC-250: GPU health check before entering the spinlock-protected
|
|
\t * MMIO section. On this SoC the internal PCIe fabric has NO completion
|
|
\t * timeout - readl() on an unresponsive GPU hangs CPU indefinitely.
|
|
\t */
|
|
\t{
|
|
\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
|
|
|
|
\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) &&
|
|
\t\t (gc_ver < IP_VERSION(10, 2, 0))) {
|
|
\t\t\ttmp = RREG32_RLC_NO_KIQ(ack, hub_ip);
|
|
\t\t\tif (tmp == 0xFFFFFFFF) {
|
|
\t\t\t\tdev_err_ratelimited(adev->dev,
|
|
\t\t\t\t\t"BC-250: GPU unreachable (MMIO returned 0xFFFFFFFF), "
|
|
\t\t\t\t\t"skipping TLB flush vmid=%u hub=%u\\n",
|
|
\t\t\t\t\tvmid, vmhub);
|
|
\t\t\t\treturn;
|
|
\t\t\t}
|
|
\t\t}
|
|
\t}
|
|
\t/* ===== BC-250 v3 PATCH END ===== */
|
|
"""
|
|
content = content[:hub_line_end] + health_check + content[hub_line_end:]
|
|
|
|
# ===== Patch 2c: In-spinlock semaphore dead-GPU check =====
|
|
# Find the semaphore acquire loop: "if (tmp & 0x1)" inside the TLB flush function
|
|
# We need to add 0xFFFFFFFF check right after the RREG32 in the sem loop
|
|
|
|
# Find "a read return value of 1 means semaphore" comment (unique marker)
|
|
sem_comment = content.find('a read return value of 1 means semaphore', content.find('gmc_v10_0_flush_gpu_tlb'))
|
|
if sem_comment == -1:
|
|
sem_comment = content.find('semaphore acq', content.find('gmc_v10_0_flush_gpu_tlb'))
|
|
|
|
if sem_comment != -1:
|
|
# Find "if (tmp & 0x1)" after this comment
|
|
tmp_check = content.find('if (tmp & 0x1)', sem_comment)
|
|
if tmp_check != -1:
|
|
# Insert dead-GPU check before "if (tmp & 0x1)"
|
|
tmp_line_start = content.rfind('\n', 0, tmp_check) + 1
|
|
sem_dead_check = """\t\t\t\t/* ===== BC-250 v3 PATCH: In-spinlock sem dead-GPU check ===== */
|
|
\t\t\t\tif (tmp == 0xFFFFFFFF) {
|
|
\t\t\t\t\tdev_err_ratelimited(adev->dev,
|
|
\t\t\t\t\t\t"BC-250: GPU died during sem acquire (0xFFFFFFFF)\\n");
|
|
\t\t\t\t\tspin_unlock(&adev->gmc.invalidate_lock);
|
|
\t\t\t\t\treturn;
|
|
\t\t\t\t}
|
|
\t\t\t\t/* ===== BC-250 v3 PATCH END ===== */
|
|
"""
|
|
content = content[:tmp_line_start] + sem_dead_check + content[tmp_line_start:]
|
|
else:
|
|
print("WARNING: Could not find semaphore comment - skipping sem dead-GPU check")
|
|
|
|
# ===== Patch 2d: ACK-wait loop dead-GPU check =====
|
|
# Find the ACK wait loop: "Wait for ACK with a delay" comment
|
|
ack_comment = content.find('Wait for ACK with a delay', content.find('gmc_v10_0_flush_gpu_tlb'))
|
|
if ack_comment != -1:
|
|
# Find "tmp &= 1 << vmid;" after this - that's inside the ACK loop
|
|
tmp_mask = content.find('tmp &= 1 << vmid', ack_comment)
|
|
if tmp_mask != -1:
|
|
tmp_mask_line_start = content.rfind('\n', 0, tmp_mask) + 1
|
|
ack_dead_check = """\t\t\t\t/* ===== BC-250 v3 PATCH: ACK-wait dead-GPU check ===== */
|
|
\t\t\t\tif (tmp == 0xFFFFFFFF) {
|
|
\t\t\t\t\tdev_err_ratelimited(adev->dev,
|
|
\t\t\t\t\t\t"BC-250: GPU died during TLB flush ACK wait (0xFFFFFFFF)\\n");
|
|
\t\t\t\t\tif (use_semaphore)
|
|
\t\t\t\t\t\tWREG32_RLC_NO_KIQ(sem, 0, hub_ip);
|
|
\t\t\t\t\tspin_unlock(&adev->gmc.invalidate_lock);
|
|
\t\t\t\t\treturn;
|
|
\t\t\t\t}
|
|
\t\t\t\t/* ===== BC-250 v3 PATCH END ===== */
|
|
"""
|
|
content = content[:tmp_mask_line_start] + ack_dead_check + content[tmp_mask_line_start:]
|
|
else:
|
|
print("WARNING: Could not find ACK wait comment")
|
|
|
|
# ===== Patch 2e: gmc_v10_0_hw_init - PASID KIQ disable =====
|
|
# Find gmc_v10_0_hw_init function, specifically the line:
|
|
# adev->gmc.flush_pasid_uses_kiq = !amdgpu_emu_mode;
|
|
hw_init_func = content.find('static int gmc_v10_0_hw_init')
|
|
if hw_init_func == -1:
|
|
print("ERROR: Could not find gmc_v10_0_hw_init")
|
|
exit(1)
|
|
|
|
pasid_kiq = content.find('flush_pasid_uses_kiq', hw_init_func)
|
|
if pasid_kiq == -1:
|
|
print("ERROR: Could not find flush_pasid_uses_kiq in hw_init")
|
|
exit(1)
|
|
|
|
# Find the full line
|
|
pasid_line_start = content.rfind('\n', 0, pasid_kiq) + 1
|
|
pasid_line_end = content.find('\n', pasid_kiq)
|
|
old_line = content[pasid_line_start:pasid_line_end]
|
|
|
|
new_block = """\t/* ===== BC-250 v2 PATCH: Disable KIQ-based PASID flush ===== */
|
|
\t{
|
|
\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
|
|
\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0)))
|
|
\t\t\tadev->gmc.flush_pasid_uses_kiq = false;
|
|
\t\telse
|
|
\t\t\tadev->gmc.flush_pasid_uses_kiq = !amdgpu_emu_mode;
|
|
\t}
|
|
\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 successfully (5 sub-patches to gmc_v10_0.c)")
|
|
"""
|
|
|
|
with sftp.open('/tmp/patch2.py', 'w') as f:
|
|
f.write(patch2_script)
|
|
run("python3 /tmp/patch2.py", "Applying Patch 2")
|
|
run(f"grep -c 'BC-250' {AMDGPU_DIR}/gmc_v10_0.c", "Count BC-250 markers in gmc_v10_0.c")
|
|
|
|
# --- Patch 3: amdgpu_gmc.c - KIQ bypass + Dead-GPU detection ---
|
|
print("\n" + "="*60)
|
|
print(" Patch 3: amdgpu_gmc.c — KIQ bypass + Dead-GPU")
|
|
print("="*60)
|
|
|
|
out, rc = run(f"grep -c 'BC-250' {AMDGPU_DIR}/amdgpu_gmc.c")
|
|
if out.strip() != "0":
|
|
print("Already patched, skipping.")
|
|
else:
|
|
patch3_script = r"""
|
|
filepath = '""" + AMDGPU_DIR + r"""/amdgpu_gmc.c'
|
|
with open(filepath, 'r') as f:
|
|
content = f.read()
|
|
|
|
# ===== Patch 3a: KIQ bypass in amdgpu_gmc_flush_gpu_tlb_pasid =====
|
|
# Find down_read_trylock check, then insert bypass after it
|
|
|
|
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")
|
|
exit(1)
|
|
|
|
# Find the trylock return 0 line
|
|
trylock = content.find('down_read_trylock', func_start)
|
|
if trylock == -1:
|
|
print("ERROR: down_read_trylock not found")
|
|
exit(1)
|
|
|
|
# Find the end of the if block (return 0;)
|
|
return_0 = content.find('return 0;', trylock)
|
|
end_of_block = content.find('\n', return_0) + 1
|
|
|
|
bypass_code = """
|
|
\t/* ===== BC-250 v2 PATCH: KIQ bypass for PASID flush ===== */
|
|
\t{
|
|
\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
|
|
\t\tpr_warn_once("amdgpu: flush_gpu_tlb_pasid called, GC_HWIP=0x%08x "
|
|
\t\t\t "(10.1.3=0x%08x) kiq_flag=%d\\n",
|
|
\t\t\t gc_ver, IP_VERSION(10, 1, 3),
|
|
\t\t\t adev->gmc.flush_pasid_uses_kiq);
|
|
|
|
\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0))) {
|
|
\t\t\tpr_warn_once("amdgpu: BC-250 KIQ bypass active "
|
|
\t\t\t\t "(gc_ver=0x%08x)\\n", gc_ver);
|
|
\t\t\tadev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid,
|
|
\t\t\t\t\t\t\t\t flush_type, all_hub,
|
|
\t\t\t\t\t\t\t\t inst);
|
|
\t\t\tr = 0;
|
|
\t\t\tgoto error_unlock_reset;
|
|
\t\t}
|
|
\t}
|
|
\t/* ===== BC-250 v2 PATCH END ===== */
|
|
"""
|
|
|
|
content = content[:end_of_block] + bypass_code + content[end_of_block:]
|
|
|
|
# ===== Patch 3b: KIQ bypass + dead-GPU in amdgpu_gmc_fw_reg_write_reg_wait =====
|
|
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")
|
|
exit(1)
|
|
|
|
# Find the first KIQ-related code after the function signature
|
|
# Look for "ring->sched.ready" or "spin_lock"
|
|
# We need to insert BEFORE the KIQ ring submission code
|
|
# Find the first operational line after variable declarations
|
|
|
|
# Look for spin_lock_irqsave which starts the KIQ path
|
|
spinlock = content.find('spin_lock_irqsave', func2_start)
|
|
if spinlock == -1:
|
|
# Try another marker
|
|
spinlock = content.find('ring->sched.ready', func2_start)
|
|
if spinlock == -1:
|
|
print("ERROR: Could not find KIQ code in fw_reg_write_reg_wait")
|
|
exit(1)
|
|
|
|
spinlock_line_start = content.rfind('\n', 0, spinlock) + 1
|
|
|
|
bypass2_code = """\t/* ===== BC-250 v2+v3 PATCH: KIQ bypass + dead-GPU detection ===== */
|
|
\t{
|
|
\t\tuint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
|
|
\t\tif ((gc_ver >= IP_VERSION(10, 1, 0)) && (gc_ver < IP_VERSION(10, 2, 0))) {
|
|
\t\t\tuint32_t tmp;
|
|
|
|
\t\t\tpr_warn_once("amdgpu: BC-250 KIQ bypass active in "
|
|
\t\t\t\t "fw_reg_write_reg_wait (gc=0x%08x)\\n", gc_ver);
|
|
|
|
\t\t\t/* v3: Health-check read before writing */
|
|
\t\t\ttmp = RREG32_NO_KIQ(reg1);
|
|
\t\t\tif (tmp == 0xFFFFFFFF) {
|
|
\t\t\t\tdev_err_ratelimited(adev->dev,
|
|
\t\t\t\t\t"BC-250: GPU unreachable in fw_reg_write_reg_wait "
|
|
\t\t\t\t\t"(reg1=0x%x returned 0xFFFFFFFF), skipping\\n", reg1);
|
|
\t\t\t\treturn;
|
|
\t\t\t}
|
|
|
|
\t\t\tWREG32_NO_KIQ(reg0, ref);
|
|
\t\t\tfor (cnt = 0; cnt < adev->usec_timeout; cnt++) {
|
|
\t\t\t\ttmp = RREG32_NO_KIQ(reg1);
|
|
\t\t\t\t/* v3: Dead-GPU detection in polling loop */
|
|
\t\t\t\tif (tmp == 0xFFFFFFFF) {
|
|
\t\t\t\t\tdev_err_ratelimited(adev->dev,
|
|
\t\t\t\t\t\t"BC-250: GPU died during reg_write_reg_wait "
|
|
\t\t\t\t\t\t"(0xFFFFFFFF at reg1=0x%x)\\n", reg1);
|
|
\t\t\t\t\treturn;
|
|
\t\t\t\t}
|
|
\t\t\t\tif ((tmp & mask) == (ref & mask))
|
|
\t\t\t\t\treturn;
|
|
\t\t\t\tudelay(1);
|
|
\t\t\t}
|
|
\t\t\tdev_warn(adev->dev, "BC-250: MMIO reg write/wait timeout "
|
|
\t\t\t\t "reg0=0x%x reg1=0x%x\\n", reg0, reg1);
|
|
\t\t\treturn;
|
|
\t\t}
|
|
\t}
|
|
\t/* ===== BC-250 v2+v3 PATCH END ===== */
|
|
"""
|
|
|
|
content = content[:spinlock_line_start] + bypass2_code + content[spinlock_line_start:]
|
|
|
|
with open(filepath, 'w') as f:
|
|
f.write(content)
|
|
|
|
print("Patch 3 applied successfully (2 sub-patches to amdgpu_gmc.c)")
|
|
"""
|
|
|
|
with sftp.open('/tmp/patch3.py', 'w') as f:
|
|
f.write(patch3_script)
|
|
run("python3 /tmp/patch3.py", "Applying Patch 3")
|
|
run(f"grep -c 'BC-250' {AMDGPU_DIR}/amdgpu_gmc.c", "Count BC-250 markers in amdgpu_gmc.c")
|
|
|
|
# ============================================================
|
|
# Step 5: Verify all patches
|
|
# ============================================================
|
|
print("\n" + "="*60)
|
|
print(" Patch summary — BC-250 markers in all files")
|
|
print("="*60)
|
|
for f in ["gfx_v10_0.c", "gmc_v10_0.c", "amdgpu_gmc.c"]:
|
|
run(f"grep -n 'BC-250' {AMDGPU_DIR}/{f}")
|
|
|
|
# ============================================================
|
|
# Step 6: Build the module
|
|
# ============================================================
|
|
print("\n" + "="*60)
|
|
print(" Building amdgpu module (LLVM=1)")
|
|
print(" This may take several minutes...")
|
|
print("="*60)
|
|
|
|
# Build using nohup + log file for long build
|
|
run(f"cd {SRC_DIR} && nohup make LLVM=1 -j$(nproc) M=drivers/gpu/drm/amd/amdgpu modules > /tmp/amdgpu-build.log 2>&1; echo BUILD_EXIT=$?",
|
|
"Building amdgpu module", timeout=900)
|
|
|
|
# Check build result
|
|
run("tail -30 /tmp/amdgpu-build.log", "Build log (last 30 lines)")
|
|
|
|
# Check if module was built
|
|
out, rc = run(f"ls -la {AMDGPU_DIR}/amdgpu.ko 2>&1")
|
|
if rc != 0:
|
|
print("\nERROR: Module build failed!")
|
|
run("grep -i error /tmp/amdgpu-build.log | head -20", "Build errors")
|
|
sys.exit(1)
|
|
|
|
# ============================================================
|
|
# Step 7: Strip, compress, and install
|
|
# ============================================================
|
|
print("\n" + "="*60)
|
|
print(" Stripping and compressing module")
|
|
print("="*60)
|
|
|
|
run(f"ls -lh {AMDGPU_DIR}/amdgpu.ko", "Module size before strip")
|
|
run(f"strip --strip-debug {AMDGPU_DIR}/amdgpu.ko")
|
|
run(f"ls -lh {AMDGPU_DIR}/amdgpu.ko", "Module size after strip")
|
|
run(f"zstd -19 -f {AMDGPU_DIR}/amdgpu.ko", "Compressing with zstd-19", timeout=120)
|
|
run(f"ls -lh {AMDGPU_DIR}/amdgpu.ko.zst", "Compressed module size")
|
|
|
|
# Backup original module
|
|
MODULE_DIR = f"/usr/lib/modules/{KERNEL_FULL}/kernel/drivers/gpu/drm/amd/amdgpu"
|
|
run(f"sudo cp {MODULE_DIR}/amdgpu.ko.zst {MODULE_DIR}/amdgpu.ko.zst.original 2>/dev/null; echo done",
|
|
"Backing up original module")
|
|
|
|
# Install new module
|
|
run(f"sudo cp {AMDGPU_DIR}/amdgpu.ko.zst {MODULE_DIR}/amdgpu.ko.zst",
|
|
"Installing v3 patched module")
|
|
|
|
# Update module dependencies
|
|
run("sudo depmod -a", "Updating module dependencies")
|
|
|
|
# Verify installed module has BC-250 strings
|
|
run(f"zstd -d -c {MODULE_DIR}/amdgpu.ko.zst | strings | grep 'BC-250'",
|
|
"Verifying BC-250 strings in installed module")
|
|
|
|
print("\n" + "="*60)
|
|
print(" BUILD AND INSTALL COMPLETE")
|
|
print(" Reboot required to load the v3 patched module.")
|
|
print("="*60)
|
|
|
|
# Clean up temp files
|
|
run("rm -f /tmp/patch1.py /tmp/patch2.py /tmp/patch3.py")
|
|
|
|
sftp.close()
|
|
ssh.close()
|