#!/usr/bin/env python3 """ Patch ESP32 QEMU source to fix WiFi/RF peripheral register stubs. Two changes only — no new files, no meson.build modifications: 1. hw/misc/unimp.c — change unimp_read to return 0xFFFFFFFF instead of 0. The ESP32 WiFi library busy-loops on hardware-ready bits. With the default return-0 those loops never exit and the watchdog fires. 0xFFFF... sets all bits, so most active-high "ready/done" flags pass. 2. hw/xtensa/esp32.c — add create_unimplemented_device() calls for all unmapped WiFi/RF peripheral registers that cause LoadStorePIFAddrError. """ import re import sys ESP32_C = 'hw/xtensa/esp32.c' UNIMP_C = 'hw/misc/unimp.c' # ── Stubs to register ───────────────────────────────────────────────────────── STUB_PREFIX = 'rfstub.' # unimp_read returns 0xFF for names starting with this STUBS = [ # (device-name, base-addr, size) # Prefix "rfstub." distinguishes our WiFi stubs from other unimplemented # devices — unimp_read returns 0xFF only for that prefix. # AHB bus ───────────────────────────────────────────────────────────────── (f'{STUB_PREFIX}wifi_modem', 0x60033C00, 0x10000), # Data-bus (0x3FF00000) ─────────────────────────────────────────────────── (f'{STUB_PREFIX}fe2', 0x3FF45000, 0x1000), (f'{STUB_PREFIX}fe', 0x3FF46000, 0x1000), # crash @ 0x3FF460A0 (f'{STUB_PREFIX}bt_bb', 0x3FF51000, 0x2000), # <=0x2000 avoids I2C_EXT0 (f'{STUB_PREFIX}5c', 0x3FF5C000, 0x1000), # crash @ 0x3FF5C01C (f'{STUB_PREFIX}5d', 0x3FF5D000, 0x1000), (f'{STUB_PREFIX}nrx', 0x3FF5E000, 0x1000), # 0x3FF5F000=TG0 untouched (f'{STUB_PREFIX}62', 0x3FF62000, 0x1000), (f'{STUB_PREFIX}63', 0x3FF63000, 0x1000), (f'{STUB_PREFIX}68', 0x3FF68000, 0x1000), (f'{STUB_PREFIX}6a', 0x3FF6A000, 0x1000), (f'{STUB_PREFIX}6b', 0x3FF6B000, 0x1000), (f'{STUB_PREFIX}6c', 0x3FF6C000, 0x1000), # 0x3FF6E000 = UART2 — leave for Modbus ] # ───────────────────────────────────────────────────────────────────────────── # Step 1 — patch unimp_read to return 0xFFFFFFFF # ───────────────────────────────────────────────────────────────────────────── def patch_unimp(path): try: with open(path) as f: src = f.read() except FileNotFoundError: print(f' ERROR: {path} not found', file=sys.stderr) sys.exit(1) if '~(uint64_t)0' in src or '0xffffffff' in src.lower() and 'unimp_read' in src: print(f' skip {path} — already patched') return # Replace "return 0;" inside unimp_read with a prefix-checked return: # devices named "rfstub.*" return 0xFFFFFFFF, all others return 0. replacement = ( 'return strncmp(s->name, "rfstub.", 7) == 0 ? ~(uint64_t)0 : 0;' ) new_src = re.sub( r'(static uint64_t unimp_read\b[^}]+?\breturn\s+)0(;)', lambda m: m.group(0).replace('return 0;', replacement), src, count=1, flags=re.DOTALL, ) # Also ensure is included for strncmp if '#include ' not in new_src and 'strncmp' in new_src: new_src = new_src.replace( '#include "qemu/osdep.h"\n', '#include "qemu/osdep.h"\n#include \n', 1, ) if new_src == src: print(f' WARNING: pattern not found in {path}, skipping', file=sys.stderr) return with open(path, 'w') as f: f.write(new_src) print(f' patched {path}: unimp_read returns 0xFFFF for rfstub.* only') # ───────────────────────────────────────────────────────────────────────────── # Step 2 — register stubs in ESP32 machine # ───────────────────────────────────────────────────────────────────────────── def patch_esp32(path): try: with open(path) as f: src = f.read() except FileNotFoundError: print(f' ERROR: {path} not found', file=sys.stderr) sys.exit(1) # Ensure hw/misc/unimp.h is included if 'hw/misc/unimp.h' not in src: src = src.replace( '#include "qemu/osdep.h"\n', '#include "qemu/osdep.h"\n#include "hw/misc/unimp.h"\n', 1, ) print(' + added #include "hw/misc/unimp.h"') changed = False for name, addr, size in STUBS: hex_addr = f'0x{addr:08X}' if hex_addr in src or hex_addr.lower() in src: print(f' skip {name}: already present') continue stub_line = ( f' create_unimplemented_device("{name}", {hex_addr}, 0x{size:X});\n' ) m = list(re.finditer(r'create_unimplemented_device\([^;]+;\n', src)) if m: pos = m[-1].end() src = src[:pos] + stub_line + src[pos:] else: pos = src.rfind('\n}') src = src[:pos] + '\n' + stub_line + src[pos:] print(f' + stub {name} @ {hex_addr}') changed = True if changed: with open(path, 'w') as f: f.write(src) print(f' wrote {path}') else: print(f' no changes to {path}') # ───────────────────────────────────────────────────────────────────────────── if __name__ == '__main__': print('Step 1: patch unimp_read to return 0xFFFFFFFF') patch_unimp(UNIMP_C) print('Step 2: register WiFi/RF stubs in ESP32 machine') patch_esp32(ESP32_C) print('Patch complete.')