Skip to content

Instantly share code, notes, and snippets.

@ixs
Last active April 25, 2024 07:19
Show Gist options
  • Star 62 You must be signed in to star a gist
  • Fork 13 You must be signed in to fork a gist
  • Save ixs/dbaac42730dea9bd124f26cbd439c58e to your computer and use it in GitHub Desktop.
Save ixs/dbaac42730dea9bd124f26cbd439c58e to your computer and use it in GitHub Desktop.
Intel x520 EEPROM Patcher allows to unlock the x520 network card to work with non-intel branded SFP modules.
#!/usr/bin/env python3
#
# Simple Intel x520 EEPROM patcher
# Modifies the EEPROM to unlock the card for non-intel branded SFP modules.
#
# Copyright 2020,2021,2022 Andreas Thienemann <andreas@bawue.net>
#
# Licensed under the GPLv3
#
# Based on research described at https://forums.servethehome.com/index.php?threads/patching-intel-x520-eeprom-to-unlock-all-sfp-transceivers.24634/
#
# Quick explanation of what's going on:
# Looking at the Intel driver at e.g. https://elixir.bootlin.com/linux/v5.8/source/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h#L2140 we can see
# that the bit 0x1 at Address 0x58 contains a configuration setting whether the card allows any SFP modules or if Intel specific ones are enforced
# by the driver.
#
# Addr Bitstring
# 0x58 xxxxxxx0 means Intel specific SFPs
# 0x58 xxxxxxx1 means any SFP is allowed.
#
# Using the parameter allow_unsupported_sfp for the kernel module we can tell the driver to accept any SFPs.
# But this tool will flip the right bit 1 to make that change permanent in the configuration bits in the EEPROM,
# thus making kernel module parameters unnecessary.
#
import subprocess
import sys
try:
intf = sys.argv[1]
except IndexError:
print("%s <interface>" % sys.argv[0])
sys.exit(255)
try:
with open("/sys/class/net/%s/device/vendor" % intf) as f:
vdr_id = f.read().strip()
with open("/sys/class/net/%s/device/device" % intf) as f:
dev_id = f.read().strip()
except IOError:
print("Can't read interface data.")
sys.exit(2)
if vdr_id not in ('0x8086') or dev_id not in ('0x10fb', '0x154d'):
print("Not a recognized Intel x520 card.")
sys.exit(3)
output = subprocess.check_output(['ethtool', '-e', intf, 'offset', '0x58', 'length', '1'])
val = output.strip().split('\n')[-1].split()[-1]
val_bin = int(val, 16)
print("EEPROM Value at 0x58 is 0x%s (%s)" % (val, bin(val_bin)))
if val_bin & 0b00000001 == 1:
print("Card is already unlocked for all SFP modules. Nothing to do.")
exit(1)
if val_bin & 0b00000001 == 0:
print("Card is locked to Intel only SFP modules. Patching EEPROM...")
new_val = val_bin | 0b00000001
print("New EEPROM Value at 0x58 will be %s (%s)" % (hex(new_val), bin(new_val)))
magic = "%s%s" % (dev_id, vdr_id[2:])
cmd = ['ethtool', '-E', intf, 'magic', magic, 'offset', '0x58', 'value', hex(new_val), 'length', 1]
print("Running %s" % " ".join(cmd))
subprocess.call(cmd)
print("Reboot the machine for changes to take effect...")
@tinbotu
Copy link

tinbotu commented Aug 15, 2020

Hi, I came from your comment on my gist https://gist.github.com/tinbotu/7ad2d21dcb7b2a8142df16792a46ce3b
This script sounds easy and nice.

The EEPROM seems to maybe re-overwrite by updating the Intel genuine driver(or utility) on Windows, If that happens I use this!

Thanks ixs. Amsterdam is beautiful I love, yummy matjes herring!

@ixs
Copy link
Author

ixs commented Aug 15, 2020

Hi, I came from your comment on my gist https://gist.github.com/tinbotu/7ad2d21dcb7b2a8142df16792a46ce3b

Yeah, I saw that after I had written it. Nice description you did there.

The EEPROM seems to maybe re-overwrite by updating the Intel genuine driver(or utility) on Windows, If that happens I use this!

I do not believe that happens, as this is the NVRAM area that has configuration data and is normally untouched during firmware upgrades or similar. If you ever see this overwritten, I'd love a headsup with a description of the situation.

@tinbotu
Copy link

tinbotu commented Aug 15, 2020

sorry, actually I haven't seen the flag on EEPROM change when the intel driver is updated.

At first I didn't know that X520 is locked against 3rd party transceiver. It could not work nice even though X520's status seen from the Windows10 OS was working, so I downloaded and installed new drivers and utilities from Intel. After that, the X520's status seen from Windows10 is completely down.

I was guessing that updateing the driver Intel genuine changed the behavior of the X520, but maybe the driver update would just show a more accurate status.

@paganinihot
Copy link

Python 3.9 - error while executing this script.
Line 52 in module
val = output.strip().split('\n')[-1].split()[-1]
TypeError: a bytes-like object is required, not 'str'
May be, you can fix? I really need this script...

@ixs
Copy link
Author

ixs commented Sep 17, 2021

@paganinihot

Python 3.9 - error while executing this script.
Line 52 in module
val = output.strip().split('\n')[-1].split()[-1]
TypeError: a bytes-like object is required, not 'str'
May be, you can fix? I really need this script...

I am running on python 3.9 and this works fine.
What's your output? Add a line containing print(output) before line 52 and it will tell you.

@Mike9370
Copy link

changing output = subprocess.check_output(['ethtool', '-e', intf, 'offset', '0x58', 'length', '1'])
to: output = subprocess.check_output(['ethtool', '-e', intf, 'offset', '0x58', 'length', '1']).decode('utf-8')

worked for me.

@eitch
Copy link

eitch commented Jun 30, 2022

This worked like a charm! I did have to add the .decode('utf-8') to make it work, though.

Thanks for this awesome script!

@newroc
Copy link

newroc commented Jul 3, 2022

ethtool -E ens106f0 magic 0x10FB8086 offset 0x58 value 0xfd
cause reply: offset & length out of bounds
Please, help! I need this patch!!

@narutoxboy
Copy link

ethtool -E ens106f0 magic 0x10FB8086 offset 0x58 value 0xfd cause reply: offset & length out of bounds Please, help! I need this patch!!

Same problem, need help. Thanks you

@ixs
Copy link
Author

ixs commented Sep 7, 2022

@narutoxboy @newroc you have an old ethtool.

Updated the code to pass length 1 as well, which works around the issue. Good luck.

@janipewter
Copy link

janipewter commented Sep 14, 2022

Running this on Ubuntu 22.04 gives following error:

Traceback (most recent call last):
  File "/home/jani/intel_x520_patcher.py", line 52, in <module>
    val = output.strip().split('\n')[-1].split()[-1]
TypeError: a bytes-like object is required, not 'str'

Solution:
On line 52 find: val = output.strip().split('\n')[-1].split()[-1]
Change to: val = output.decode().strip().split('\n')[-1].split()[-1]

Result:

~$ sudo python3 intel_x520_patcher.py enp1s0f0
EEPROM Value at 0x58 is 0xfd (0b11111101)
Card is already unlocked for all SFP modules. Nothing to do.

@tokehgekko
Copy link

Running this on Ubuntu 22.04 gives following error:

Traceback (most recent call last):
  File "/home/jani/intel_x520_patcher.py", line 52, in <module>
    val = output.strip().split('\n')[-1].split()[-1]
TypeError: a bytes-like object is required, not 'str'

Solution: On line 52 find: val = output.strip().split('\n')[-1].split()[-1] Change to: val = output.decode().strip().split('\n')[-1].split()[-1]

Result:

~$ sudo python3 intel_x520_patcher.py enp1s0f0
EEPROM Value at 0x58 is 0xfd (0b11111101)
Card is already unlocked for all SFP modules. Nothing to do.

I tried this solution but now i get this instead:
Traceback (most recent call last):
File "/home/tokehgekko/intel_x520_patcher.py", line 66, in
print("Running %s" % " ".join(cmd))
TypeError: sequence item 10: expected str instance, int found

@habibulilalbaab
Copy link

habibulilalbaab commented Feb 7, 2023

Traceback (most recent call last):
File "/home/tokehgekko/intel_x520_patcher.py", line 66, in
print("Running %s" % " ".join(cmd))
TypeError: sequence item 10: expected str instance, int found

add:
import os

line 66:
from
print("Running %s" % " ".join(cmd))
to:
print("Running {}".format(cmd))

below line 65 (new line) add convert tuple to string:
cmd = ' '.join(map(str, cmd))

from:
subprocess.call(cmd)
to:
os.system(cmd)

@bluerockny
Copy link

Here's the script with all the modifications above. Works on Ubuntu 22.04

#!/usr/bin/env python3
#
# Simple Intel x520 EEPROM patcher
# Modifies the EEPROM to unlock the card for non-intel branded SFP modules.
#
# Copyright 2020,2021,2022 Andreas Thienemann <andreas@bawue.net>
#
# Licensed under the GPLv3
#
# Based on research described at https://forums.servethehome.com/index.php?threads/patching-intel-x520-eeprom-to-unlock-all-sfp-transceivers.24634/
#
# Quick explanation of what's going on:
# Looking at the Intel driver at e.g. https://elixir.bootlin.com/linux/v5.8/source/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h#L2140 we can see
# that the bit 0x1 at Address 0x58 contains a configuration setting whether the card allows any SFP modules or if Intel specific ones are enforced
# by the driver.
#
# Addr Bitstring
# 0x58 xxxxxxx0 means Intel specific SFPs
# 0x58 xxxxxxx1 means any SFP is allowed.
#
# Using the parameter allow_unsupported_sfp for the kernel module we can tell the driver to accept any SFPs.
# But this tool will flip the right bit 1 to make that change permanent in the configuration bits in the EEPROM,
# thus making kernel module parameters unnecessary.
#
import os
import subprocess
import sys

try:
    intf = sys.argv[1]
except IndexError:
    print("%s <interface>" % sys.argv[0])
    sys.exit(255)

try:
    with open("/sys/class/net/%s/device/vendor" % intf) as f:
        vdr_id = f.read().strip()

    with open("/sys/class/net/%s/device/device" % intf) as f:
        dev_id = f.read().strip()
except IOError:
    print("Can't read interface data.")
    sys.exit(2)

if vdr_id not in ('0x8086') or dev_id not in ('0x10fb', '0x154d'):
    print("Not a recognized Intel x520 card.")
    sys.exit(3)


output = subprocess.check_output(['ethtool', '-e', intf, 'offset', '0x58', 'length', '1']).decode('utf-8')

val = output.strip().split('\n')[-1].split()[-1]
val_bin = int(val, 16)

print("EEPROM Value at 0x58 is 0x%s (%s)" % (val, bin(val_bin)))
if val_bin & 0b00000001 == 1:
    print("Card is already unlocked for all SFP modules. Nothing to do.")
    exit(1)
if val_bin & 0b00000001 == 0:
    print("Card is locked to Intel only SFP modules. Patching EEPROM...")
    new_val = val_bin | 0b00000001
    print("New EEPROM Value at 0x58 will be %s (%s)" % (hex(new_val), bin(new_val)))

magic = "%s%s" % (dev_id, vdr_id[2:])
cmd = ['ethtool', '-E', intf, 'magic', magic, 'offset', '0x58', 'value', hex(new_val), 'length', 1]
print("Running {}".format(cmd))
cmd = ' '.join(map(str, cmd))
os.system(cmd)
print("Reboot the machine for changes to take effect...")

@dburkland
Copy link

@bluerockny you are a life saver, this worked perfectly!

@AaronMcGuirk007
Copy link

@bluerockny Nice work! After an hour of trouble shooting, you were the golden winner. Kudos to you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment