Extract real FX2 firmware from I2C EEPROM
Previous RAM dumps via 0xA0 vendor request turned out to be live FIFO data, not firmware - the Genpix FX2 firmware overrides the standard 0xA0 handler. Discovered that I2C_READ (0x84) with wValue=0x51 and wIndex=offset reads the boot EEPROM directly. EEPROM contents (Cypress C2 format): - VID:PID 09C0:0203, config 0x40 (400kHz I2C) - 9,472 bytes of 8051 firmware in 10 load records - Code range 0x0000-0x24FF, entry at LJMP 0x188D - Ghidra auto-analysis finds 61 functions Tools: eeprom_dump.py (full dump), eeprom_probe.py (I2C protocol discovery)
This commit is contained in:
parent
757da08987
commit
ba37105e2a
5 changed files with 352 additions and 0 deletions
101
tools/eeprom_probe.py
Normal file
101
tools/eeprom_probe.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Probe I2C EEPROM address setting on Genpix SkyWalker-1."""
|
||||
import usb.core, usb.util, time
|
||||
|
||||
dev = usb.core.find(idVendor=0x09C0, idProduct=0x0203)
|
||||
for cfg in dev:
|
||||
for intf in cfg:
|
||||
if dev.is_kernel_driver_active(intf.bInterfaceNumber):
|
||||
dev.detach_kernel_driver(intf.bInterfaceNumber)
|
||||
break
|
||||
try:
|
||||
dev.set_configuration()
|
||||
except:
|
||||
pass
|
||||
|
||||
I2C_READ = 0x84
|
||||
I2C_WRITE = 0x83
|
||||
|
||||
def vin(req, value=0, index=0, length=64):
|
||||
try:
|
||||
return dev.ctrl_transfer(
|
||||
usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_IN,
|
||||
req, value, index, length, 2000)
|
||||
except:
|
||||
return None
|
||||
|
||||
def vout(req, value=0, index=0, data=b''):
|
||||
try:
|
||||
return dev.ctrl_transfer(
|
||||
usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_OUT,
|
||||
req, value, index, data, 2000)
|
||||
except Exception as e:
|
||||
print(f" OUT error: {e}")
|
||||
return None
|
||||
|
||||
# Known first 8 bytes at offset 0: c2 c0 09 03 02 00 00 40
|
||||
# Known bytes at offset 8 (from our read): 03 ff 00 00 02 18 8d 30
|
||||
REF_0 = "c2 c0 09 03 02 00 00 40"
|
||||
REF_8 = "03 ff 00 00 02 18 8d 30"
|
||||
|
||||
def show(label, data):
|
||||
if data is not None:
|
||||
h = bytes(data[:8]).hex(' ')
|
||||
match = ""
|
||||
if h == REF_0: match = " <-- OFFSET 0"
|
||||
elif h == REF_8: match = " <-- OFFSET 8"
|
||||
print(f" {label}: {h}{match}")
|
||||
else:
|
||||
print(f" {label}: FAILED")
|
||||
|
||||
print("=== Approach 1: I2C_WRITE data=[addr_h, addr_l], then I2C_READ ===")
|
||||
vout(I2C_WRITE, 0x51, 0, bytes([0x00, 0x08]))
|
||||
show("After set 0x0008", vin(I2C_READ, 0x51, 0, 8))
|
||||
vout(I2C_WRITE, 0x51, 0, bytes([0x00, 0x00]))
|
||||
show("After set 0x0000", vin(I2C_READ, 0x51, 0, 8))
|
||||
|
||||
print("\n=== Approach 2: wValue=addr, wIndex=slave ===")
|
||||
vout(I2C_WRITE, 0x0008, 0x51)
|
||||
show("After set 0x0008", vin(I2C_READ, 0x51, 0, 8))
|
||||
vout(I2C_WRITE, 0x0000, 0x51)
|
||||
show("After set 0x0000", vin(I2C_READ, 0x51, 0, 8))
|
||||
|
||||
print("\n=== Approach 3: wValue=slave, wIndex=addr ===")
|
||||
vout(I2C_WRITE, 0x51, 0x0008)
|
||||
show("After set 0x0008", vin(I2C_READ, 0x51, 0, 8))
|
||||
vout(I2C_WRITE, 0x51, 0x0000)
|
||||
show("After set 0x0000", vin(I2C_READ, 0x51, 0, 8))
|
||||
|
||||
print("\n=== Approach 4: I2C_READ with wIndex=offset ===")
|
||||
show("wIndex=0x0000", vin(I2C_READ, 0x51, 0x0000, 8))
|
||||
show("wIndex=0x0008", vin(I2C_READ, 0x51, 0x0008, 8))
|
||||
show("wIndex=0x0040", vin(I2C_READ, 0x51, 0x0040, 8))
|
||||
|
||||
print("\n=== Approach 5: I2C_READ with (slave<<8|offset) in wValue ===")
|
||||
show("wValue=0x5100", vin(I2C_READ, 0x5100, 0, 8))
|
||||
show("wValue=0x5108", vin(I2C_READ, 0x5108, 0, 8))
|
||||
|
||||
print("\n=== Approach 6: I2C_READ with wValue=offset (no slave) ===")
|
||||
show("wValue=0x0000", vin(I2C_READ, 0x0000, 0, 8))
|
||||
show("wValue=0x0008", vin(I2C_READ, 0x0008, 0, 8))
|
||||
show("wValue=0x0040", vin(I2C_READ, 0x0040, 0, 8))
|
||||
|
||||
print("\n=== Approach 7: Larger reads to check page boundaries ===")
|
||||
data = vin(I2C_READ, 0x51, 0, 64)
|
||||
if data:
|
||||
show("First 8 of 64", data[:8])
|
||||
show("Bytes 8-15", data[8:16])
|
||||
show("Bytes 56-63", data[56:64])
|
||||
# Check if bytes 8-15 match REF_8
|
||||
if bytes(data[8:16]).hex(' ') == REF_8:
|
||||
print(" ** Bytes 8-15 match expected offset 8 data!")
|
||||
print(" ** The 64-byte read IS returning sequential EEPROM data!")
|
||||
|
||||
# Reattach
|
||||
for cfg in dev:
|
||||
for intf in cfg:
|
||||
try:
|
||||
usb.util.release_interface(dev, intf.bInterfaceNumber)
|
||||
dev.attach_kernel_driver(intf.bInterfaceNumber)
|
||||
except:
|
||||
pass
|
||||
Loading…
Add table
Add a link
Reference in a new issue