数码之家

 找回密码
 立即注册
搜索
查看: 3691|回复: 16

[硬件] Intel PCIe 千兆网卡 EEPROM 故障处理

[复制链接]
发表于 2022-11-1 14:58:43 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
刚看到坛友linribo发贴问:YL-SKUL6怎么修改网卡eeprom! 正好之前刷过同型号网卡的固件,把笔记分享一下。

Intel PCIe 千兆网卡 EEPROM 故障处理关键信息:
[size=0.9em]e1000e 0000:04:00.0: The NVM Checksum Is Not Valid

[size=0.9]
Intel PCIe 千兆网卡 EEPROM 故障处理
故障现象
故障分析与修复
分析与尝试
修复
找官方刷写工具【未果】
找到参考资料
Linux 下临时启用网卡
工具链安装
修改、编译、加载 e1000e 驱动模块
读取EEPROM内容
EEPROM 数据准备
写入 EEPROM
确认网卡工作正常
深入挖掘
EEPROM 内容对比
e1000e 驱动的 NVM Checksum 机制
写入错误的 EEPROM 内容测试
最快的修复方法
Todo: 读驱动代码其他部分
总结
参考资料

故障现象一块板载双 Intel 82583V 千兆网卡的主板,在经过某个种未知操作后,Linux系统下网络不通。
  • ip addr、nmcli dev 都看不到网卡。
    [size=0.9em][root@localhost ~]# nmcli dev
    DEVICE TYPE STATE CONNECTION
    lo loopback unmanaged --

  • lspci 能看到网卡
    [size=0.9em][root@localhost ~]# lspci | grep -i net
    01:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
    04:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection


故障分析与修复分析与尝试分析日志,看到:
[size=0.9em][root@localhost ~]# dmesg | grep -i error -B3
...
--
[ 4.537044] e1000e 0000:04:00.0: enabling device (0000 -> 0002)
[ 4.537250] e1000e 0000:04:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 4.587298] e1000e 0000:04:00.0: The NVM Checksum Is Not Valid
[ 4.601409] e1000e: probe of 0000:04:00.0 failed with error -5
...

以 "e1000e The NVM Checksum Is Not Valid" 搜索,多数链接内容指向网卡故障。
硬件故障处理起来太麻烦,先考虑操作系统故障的可能性。
  • 重置BIOS配置、主板放电,故障依旧。
  • 重装系统,故障依旧。基本排除操作系统问题。
  • 用 Windows 10 win-to-go 系统启动尝试运行检测工具时,发现网卡识别并能正常上网。这导致我走了弯路,认为是Linux系统问题。
  • 试用不同的Linux发行版LiveCD,故障依旧。

想起手头还有块同型号主板。用Linux启动,发现网卡工作正常。确认是硬件故障。
经过比较,发现故障主板把网卡型号识别为 82574L,在工作正常的主板上该网卡识别为 82583V。回头翻查关于 "e1000e The NVM Checksum Is Not Valid" 的网页。基本判定为网卡EEPROM问题。
修复既然是EEPROM问题,自然是重新刷写。好在有块同型号主板,可供提取EEPROM。
找官方刷写工具【未果】但在Intel网站上找了一圈并没有找到合适的刷写工具,换个路子。
找到参考资料这里
尝试该帖1楼的方法(Intel Preboot工具包中的./bootutil64e -NIC=1 -DEFAULTCONFIG命令),在Linux和Windows下都没能修复。
据该帖2楼线索找到主要参考资料 Unbricking an Intel Pro1000 e1000 network interface,尝试用 ethtool 重写EEPROM。
下面是操作记录(部分是事后重演)
Linux 下临时启用网卡根据参考资料,需先加载打过补丁的 e1000e 驱动模块,使系统识别到网卡后才能通过 ethtool -E 命令刷写网卡EEPROM。但新装的 CentOS-8 系统缺少所需的工具链,所以首先是安装工具。
工具链安装手头有个USB无线网卡,插上直接识别。
[size=0.9em][root@localhost ~]# nmcli dev
DEVICE TYPE STATE CONNECTION
wlp0s29u1u4 wifi disconnected HCIDC
lo loopback unmanaged --

为方便设置 wifi,先安装NetworkManager-wifi(从 CentOS-8 光盘安装)
[size=0.9em]mkdir /media/CentOS
mount /dev/cdrom -t iso9660 CentOS/
cd /media/CentOS/BaseOS/Packages/
rpm -vih wpa_supplicant-2.7-1.el8.x86_64.rpm NetworkManager-wifi-1.14.0-14.el8.x86_64.rpm

这里也可用yum或dnf安装所需的软件包,前提是编辑/etc/yum.repos.d/CentOS-Media.repo文件,修改以下参数:
[size=0.9em]enabled = 1 # 使能光盘源
gpgcheck = 0 # 如果这里不修改会导致使用yum安装软件包过不了校验。因此我改用rpm安装来绕过gpg签名校验。

然用 nmtui 配置 wifi 连接。
联网后,用 yum 官方镜像安装编译 e1000e 驱动所需的工具
[size=0.9em]yum install kernel-devel.x86_64
yum install -y ncurses-devel make gcc bc bison flex elfutils-libelf-devel

修改、编译、加载 e1000e 驱动模块
[size=0.9em]find /usr/src/kernels/4.18.0-80.7.1.el8_0.x86_64/ -name netdev.c

内核目录树下没有找到参考资料所说的 e1000e 的源码文件,改从intel网站下载驱动源码。
[size=0.9em]yum -y install wget tar
wget https://downloadmirror.intel.com/15817/eng/e1000e-3.4.2.4.tar.gz
tar zxvf e1000e-3.4.2.4.tar.gz
cd e1000e-3.4.2.4
less README # 先浏览一下 README 是个好习惯

在 e1000e 驱动源码树下搜索相关错误信息,确认源文件
[size=0.9em][root@localhost src]# grep "The NVM Checksum Is Not Valid" *
netdev.c: "The NVM Checksum Is Not Valid\n");

编辑netdev.c,跳过NVM Checksum 所在的整个代码块
[size=0.9em]8330 /* systems with ASPM and others may see the checksum fail on the first
8331 * attempt. Let's give it a few tries
8332 */
8333 for (i = 0; false; i++) {
/* 编辑上一行,跳过整个 for 循环。原为: for (i = 0; ; i++) { */
8334 if (e1000_validate_nvm_checksum(&adapter->hw) >= 0)
8335 break;
8336 if (i == 2) {
8337 dev_err(pci_dev_to_dev(pdev),
8338 "The NVM Checksum Is Not Valid\n");
/* 上一行就是输出错误信息的代码 */
8339 err = -EIO;
8340 goto err_eeprom;
8341 }
8342 }

编译安装、移除原有驱动并加载修改后的驱动模块
[size=0.9em]make install
modprobe -r e1000e && modprobe e1000e

网卡认出。其中一块网卡在加载修改后驱动前后的dmesg信息:
[size=0.9em][root@localhost ~]# dmesg | grep 04:00.0
# 系统对网卡进行初始化:
[ 0.294138] pci 0000:04:00.0: [8086:10d3] type 00 class 0x020000
[ 0.294176] pci 0000:04:00.0: reg 0x10: [mem 0xd0680000-0xd069ffff]
[ 0.294190] pci 0000:04:00.0: reg 0x14: [mem 0xd0600000-0xd067ffff]
[ 0.294203] pci 0000:04:00.0: reg 0x18: [io 0xc000-0xc01f]
[ 0.294217] pci 0000:04:00.0: reg 0x1c: [mem 0xd06a0000-0xd06a3fff]
[ 0.294398] pci 0000:04:00.0: 2.000 Gb/s available PCIe bandwidth, limited by 2.5 GT/s x1 link at 0000:00:1c.3 (capable of 7.876 Gb/s with 8 GT/s x1 link)
# 系统自动加载原有驱动并出错:
[ 4.590137] e1000e 0000:04:00.0: Disabling ASPM L1
[ 4.590156] e1000e 0000:04:00.0: enabling device (0000 -> 0002)
[ 4.590353] e1000e 0000:04:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 4.641190] e1000e 0000:04:00.0: The NVM Checksum Is Not Valid
[ 4.710148] e1000e: probe of 0000:04:00.0 failed with error -5
# 加载修改后的驱动,网卡被识别:
[ 95.570061] e1000e 0000:04:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 95.624821] e1000e 0000:04:00.0 0000:04:00.0 (uninitialized): registered PHC clock
[ 95.784553] e1000e 0000:04:00.0 eth0: (PCI Express:2.5GT/s:Width x1) 00:90:27:e8:00:1e
[ 95.784558] e1000e 0000:04:00.0 eth0: Intel(R) PRO/1000 Network Connection
[ 95.784571] e1000e 0000:04:00.0 eth0: MAC: 3, PHY: 8, PBA No: FF20FF-0FF
[ 95.800882] e1000e 0000:04:00.0 enp4s0: renamed from eth0
# 这里还能看到 e1000e 驱动先把接口识别为 eth0,后改名为 enp4s0

需注意的是,系统启动时使用的 e1000e 驱动来自 /boot/ 目录下与当前内核对应的 initramfs。也就是说,重启后被默认加载的仍是原先不能识别故障网卡的 e1000e 驱动。如果要在启动时自动加载修改后的驱动,需更新 initramfs.
-- 引用自 e1000e 驱动包 README 文件 -- Note: For certain distributions like (but not limited to) RedHat Enterprise Linux 7 and Ubuntu, once the driver is installed the initrd/initramfs file may need to be updated to prevent the OS loading old versions of the e1000e driver. The dracut utility may be used on RedHat distributions: # dracut --force For Ubuntu: # update-initramfs -u
因为这里只需要刷写EEPROM,所以不更新 initramfs.
读取EEPROM内容分别读取网卡正常工作主板的两块网卡EEPROM
[size=0.9em]ethtool -e enp1s0 > enp1s0-rom.dump
ethtool -e enp4s0 > enp4s0-rom.dump

用vim查看和比较dump文件,可以看到EEPROM大小为 0x1000(4kB). 比较发现:
  • 故障网卡的EEPROM内容与正常网卡不同
  • 正常网卡的EEPROM中,0x0000 - 0x0005 确认为网卡 MAC 地址
  • 除 MAC 地址的6个byte及最后1个byte(0x007f)外,两块正常网卡的 内容完全相同。
  • 两块正常网卡EEPROM的 0x0080 - 0x0fff 不同且无规律。

猜测网卡EEPROM可能只有 0x0000 - 0x007f (前128Bytes)才是真正有用的内容。事后的驱动源代码分析也证实了这点。
EEPROM 数据准备ethtool -E 命令每次只能写入一个字节,为了加快速度&避免出错,先准备刷写批处理命令。
根据 Unbricking an Intel Pro1000 e1000 network interface 及回帖内容,首先找到 ethtool 写入 EEPROM 数据需用到的 magicnumber。对于 e1000e, magicnumber 是设备对应的 DeviceID + VendorID
[size=0.9em][root@localhost ~]# lspci -nn | grep -i net
04:00.0 Ethernet controller [0200]: Intel Corporation 82574L Gigabit Network Connection [8086:10d3]

所以这块网卡的 magicnumber 是 0x10d38086
把正常网卡的 0x0000 - 0x007f 内容复制到 2burn.dump,并改写前6字节为其中一块故障网卡 enp4s0 的 mac 地址。编辑后的文件内容如下
[size=0.9em][root@localhost repair]# cat 2burn.dump
Offset Values 00:90:27:e8:00:1d enp1s0
------ ------
0x0000: 00 90 27 e8 00 1e 20 04 46 f7 a0 10 ff ff ff ff
0x0010: ff ff ff ff 6b 02 00 00 86 80 0c 15 ff ff 58 8c
0x0020: 00 00 01 20 94 7e ff ff 00 10 48 00 00 00 04 27
0x0030: c9 6c 50 31 3e 07 0b 4b 83 2d 40 01 00 f0 02 02
0x0040: 00 60 80 00 04 0f ff ff 01 4d 00 c6 00 00 ff 20
0x0050: 28 00 43 02 50 00 01 01 01 00 b3 05 98 00 ff ff
0x0060: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
0x0070: ff ff ff ff ff ff ff ff ff ff 50 01 ff ff d8 35

生成 ethtool 命令序列
[size=0.9em]magic=0x10d38086; j=0; for i in `sed -e '1,2d' ./2burn.dump | cut -c 9- | tr -d "\n"`; do echo "ethtool -E enp4s0 magic $magic offset 0x$(printf %x ${j}) value 0x${i}"; j=$(($j+1)); done > enp4s0-burn-batch.sh

上面的命令先用 sed 先去掉2burn.dump文件的前两行,再用cut去掉每行前8个字符(保留9-最后),用tr去掉换行符,最后用for循环把每个数据字节生成对应的一条ethtool命令。最终输出到 enp4s0-burn-batch.sh 文件准备进行批处理。
[size=0.9em][root@localhost repair]# head enp4s0-burn-batch.sh
ethtool -E enp4s0 magic 0x10d38086 offset 0x0 value 0x00
ethtool -E enp4s0 magic 0x10d38086 offset 0x1 value 0x90
ethtool -E enp4s0 magic 0x10d38086 offset 0x2 value 0x27
ethtool -E enp4s0 magic 0x10d38086 offset 0x3 value 0xe8
ethtool -E enp4s0 magic 0x10d38086 offset 0x4 value 0x00
ethtool -E enp4s0 magic 0x10d38086 offset 0x5 value 0x1e
ethtool -E enp4s0 magic 0x10d38086 offset 0x6 value 0x20
ethtool -E enp4s0 magic 0x10d38086 offset 0x7 value 0x04
ethtool -E enp4s0 magic 0x10d38086 offset 0x8 value 0x46
ethtool -E enp4s0 magic 0x10d38086 offset 0x9 value 0xf7

批处理文件的部分内容。每行写入一个字节,共128行,对应地址 0x00 - 0x7f
写入 EEPROM检查无误后
[size=0.9em][root@localhost repair]# chmod +x enp4s0-burn-batch.sh
[root@localhost repair]# ./enp4s0-burn-batch.sh

等待约10s,写入完成。
确认网卡工作正常重启后,网口驱动正常,网卡型号正常识别为82583V
[size=0.9em][root@localhost ~]# dmesg | grep 04:00.0
[ 0.294198] pci 0000:04:00.0: [8086:150c] type 00 class 0x020000
[ 0.294236] pci 0000:04:00.0: reg 0x10: [mem 0xd0680000-0xd069ffff]
[ 0.294250] pci 0000:04:00.0: reg 0x14: [mem 0xd0600000-0xd067ffff]
[ 0.294263] pci 0000:04:00.0: reg 0x18: [io 0xc000-0xc01f]
[ 0.294276] pci 0000:04:00.0: reg 0x1c: [mem 0xd06a0000-0xd06a3fff]
[ 0.294416] pci 0000:04:00.0: PME# supported from D0 D3hot D3cold
[ 0.294466] pci 0000:04:00.0: 2.000 Gb/s available PCIe bandwidth, limited by 2.5 GT/s x1 link at 0000:00:1c.3 (capable of 7.876 Gb/s with 8 GT/s x1 link)
[ 4.587174] e1000e 0000:04:00.0: Disabling ASPM L1
[ 4.587193] e1000e 0000:04:00.0: enabling device (0000 -> 0002)
[ 4.587410] e1000e 0000:04:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 4.637672] e1000e 0000:04:00.0 0000:04:00.0 (uninitialized): registered PHC clock
[ 4.692895] e1000e 0000:04:00.0 eth1: (PCI Express:2.5GT/s:Width x1) 00:90:27:e8:00:1e
[ 4.692899] e1000e 0000:04:00.0 eth1: Intel(R) PRO/1000 Network Connection
[ 4.692912] e1000e 0000:04:00.0 eth1: MAC: 4, PHY: 8, PBA No: FFFFFF-0FF
[ 4.723250] e1000e 0000:04:00.0 enp4s0: renamed from eth1
[root@localhost ~]# lspci | grep 04:00.0
04:00.0 Ethernet controller: Intel Corporation 82583V Gigabit Network Connection

两个网卡都修复后,都能正常工作。
[size=0.9em][root@localhost ~]# nmcli device
DEVICE TYPE STATE CONNECTION
enp1s0 ethernet connected Wired connection 1
enp4s0 ethernet unavailable --
lo loopback unmanaged --
[root@localhost repair]# nmcli conn
NAME UUID TYPE DEVICE
Wired connection 1 e496abfa-b5a3-3fce-8f97-e4bedbd81286 ethernet enp1s0
HCIDC 6a14432c-77c6-4e30-b1e4-7b87de556943 wifi --
Wired connection 2 5f2b5db2-1d47-3da8-8844-9c1431b2da59 ethernet --
[root@localhost repair]# ping www.baidu.com -c 2
PING www.a.shifen.com (180.101.49.12) 56(84) bytes of data.
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=1 ttl=51 time=11.0 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=2 ttl=51 time=11.0 ms

--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 11.014/11.023/11.033/0.105 ms

深入挖掘趁着国庆假期有点闲暇,“调戏”一下。
EEPROM 内容对比
  • 比较修复前后的 enp4s0 EEPROM 内容
    [size=0.9em][root@localhost ~]# ethtool -e enp4s0 | diff - NIC_EEPROM_backup/enp4s0.eeprom
    3,10c3,10
    < 0x0000: 00 90 27 e8 00 1e 20 04 46 f7 a0 10 ff ff ff ff
    < 0x0010: ff ff ff ff 6b 02 00 00 86 80 0c 15 ff ff 58 8c
    < 0x0020: 00 00 01 20 94 7e ff ff 00 10 48 00 00 00 04 27
    < 0x0030: c9 6c 50 31 3e 07 0b 4b 83 2d 40 01 00 f0 02 02
    < 0x0040: 00 60 80 00 04 0f ff ff 01 4d 00 c6 00 00 ff 20
    < 0x0050: 28 00 43 02 50 00 01 01 01 00 b3 05 98 00 ff ff
    < 0x0060: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    < 0x0070: ff ff ff ff ff ff ff ff ff ff 50 01 ff ff 6a 5e
    ---
    > 0x0000: 00 90 27 e8 00 1e 20 04 00 f7 a0 10 20 ff ff ff
    > 0x0010: 20 ff ff ff 20 02 00 00 00 80 0c 15 20 ff 58 8c
    > 0x0020: 00 00 01 20 00 7e ff ff 00 10 48 00 00 00 04 27
    > 0x0030: 00 6c 50 31 20 07 0b 4b 00 2d 40 01 00 f0 02 02
    > 0x0040: 00 60 80 00 00 0f ff ff 00 4d 00 c6 00 00 ff 20
    > 0x0050: 20 00 43 02 00 00 01 01 00 00 b3 05 00 00 ff ff
    > 0x0060: 20 ff ff ff 20 ff ff ff 20 ff ff ff 20 ff ff ff
    > 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 14 5e

    意料之中,不同的只是经过重写的 0x0000-0x007f
  • 修复后的EEPROM内容与修复时写入的数据进行比较
    [size=0.9em][root@localhost ~]# ethtool -e enp4s0 | head -n 10 | diff repair/2burn.dump -
    1c1
    < Offset Values 00:90:27:e8:00:1d enp1s0
    ---
    > Offset Values
    10c10
    < 0x0070: ff ff ff ff ff ff ff ff ff ff 50 01 ff ff d8 35
    ---
    > 0x0070: ff ff ff ff ff ff ff ff ff ff 50 01 ff ff 6a 5e

    最后两个字节对不上
    尝试刷回备份的故障EEPROM内容,刷写完成并重启后,依然是 0x7e, 0x7f 两个字节对不上。网卡依然被识别为 82583V,看起来存在校验机制。
    [size=0.9em][root@localhost NIC_EEPROM_backup]# ethtool -e enp4s0 | diff - enp4s0.eeprom
    10c10
    < 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 0a 6c
    ---
    > 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 14 5e

    再尝试修改 enp4s0 mac 地址最后一位为0xff, 写入、重启、比较:
    [size=0.9em][root@localhost NIC_EEPROM_backup]# ethtool -e enp4s0 | diff - enp4s0.eeprom
    3c3
    < 0x0000: 00 90 27 e8 00 ff 20 04 00 f7 a0 10 20 ff ff ff
    ---
    > 0x0000: 00 90 27 e8 00 1e 20 04 00 f7 a0 10 20 ff ff ff
    10c10
    < 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 0a 8b
    ---
    > 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 14 5e

    发现,除了MAC地址对应 的 0x5 字节改变外,0x7e, 0x7f 两个字节也变化了。
    再尝试直接修改 0x7f 内容,发现更改并不会生效。将MAC地址恢复原有数值,这两个字节也变为与之前相同的"0a 6c"。
    推测:最后两个字节应该是前面数据的校验码。

e1000e 驱动的 NVM Checksum 机制读驱动源代码
这是前面被注释掉的代码块中关键的调用
[size=0.9em]######## src file: e1000e-3.4.2.4/src/netdev.c ########
8334 if (e1000_validate_nvm_checksum(&adapter->hw) >= 0)

调用涉及的一些内联函数、结构体
[size=0.9em]######## src file: e1000e-3.4.2.4/src/e1000.h ########
320 struct e1000_hw hw;
...
633 static inline s32 e1000_validate_nvm_checksum(struct e1000_hw *hw)
634 {
635 return hw->nvm.ops.validate(hw);
636 }

######## src file: e1000e-3.4.2.4/src/hw.h ########
524 /* Function pointers for the NVM. */
525 struct e1000_nvm_operations {
526 s32 (*acquire) (struct e1000_hw *);
527 s32 (*read) (struct e1000_hw *, u16, u16, u16 *);
528 void (*release) (struct e1000_hw *);
529 void (*reload) (struct e1000_hw *);
530 s32 (*update) (struct e1000_hw *);
531 s32 (*valid_led_default) (struct e1000_hw *, u16 *);
532 s32 (*validate) (struct e1000_hw *);
533 s32 (*write) (struct e1000_hw *, u16, u16, u16 *);
534 };
...
613 struct e1000_nvm_info {
614 struct e1000_nvm_operations ops;
...
684 struct e1000_hw {
...
693 struct e1000_nvm_info nvm;

######## src file: e1000e-3.4.2.4/src/base.c ########
120 s32 e1000_init_nvm_params_base(struct e1000_hw *hw)
121 {
...
172 nvm->ops.validate = e1000e_validate_nvm_checksum_generic;

最终调用的校验函数 e1000e_validate_nvm_checksum_generic(struct e1000_hw *hw):
[size=0.9em]######## src file: e1000e-3.4.2.4/src/netdev.c ########
540 /**
541 * e1000e_validate_nvm_checksum_generic - Validate EEPROM checksum
542 * @hw: pointer to the HW structure
543 *
544 * Calculates the EEPROM checksum by reading/adding each word of the EEPROM
545 * and then verifies that the sum of the EEPROM is equal to 0xBABA.
546 **/
547 s32 e1000e_validate_nvm_checksum_generic(struct e1000_hw *hw)
548 {
549 s32 ret_val;
550 u16 checksum = 0;
551 u16 i, nvm_data;
552
553 for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
// defines.h:#define NVM_CHECKSUM_REG 0x003F
// 注意,这里单位是word, 所以对byte来说,最后是0x007F
554 ret_val = e1000_read_nvm(hw, i, 1, &nvm_data);
// 读nvram,每次1word, 数据存&nvm_data,错误返回值存 ret_val
555 if (ret_val) {
556 e_dbg("NVM Read Error\n");
557 return ret_val;
558 }
559 checksum += nvm_data;
// 校验:按word累加,取u16
560 }
561
562 if (checksum != (u16)NVM_SUM) {
// 校验值必须等于 0xbaba
// defines.h:#define NVM_SUM 0xBABA
563 e_dbg("NVM Checksum Invalid\n");
564 return -E1000_ERR_NVM;
565 }
566
567 return 0;
568 }

与函数 e1000e_validate_nvm_checksum_generic() 相关的函数和定义:
[size=0.9em]######## src file: e1000e-3.4.2.4/src/e1000.h ########
643 static inline s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words,
644 u16 *data)
645 {
646 return hw->nvm.ops.read(hw, offset, words, data);
647 }

######## src file: e1000e-3.4.2.4/src/base.c ########
120 s32 e1000_init_nvm_params_base(struct e1000_hw *hw)
121 {
...
166 if (nvm->word_size < (1 << 15))
167 nvm->ops.read = e1000e_read_nvm_eerd;
168 else
169 nvm->ops.read = e1000_read_nvm_spi;

######## src file: e1000e-3.4.2.4/src/nvm.c ########
292 s32 e1000e_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
293 {
294 struct e1000_nvm_info *nvm = &hw->nvm;
295 u32 i, eerd = 0;
296 s32 ret_val = 0;
297
298 /* A check for invalid values: offset too large, too many words,
299 * too many words for the offset, and not enough words.
300 */
301 if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
302 (words == 0)) {
303 e_dbg("nvm parameter(s) out of bounds\n");
304 return -E1000_ERR_NVM;
305 }
306
307 for (i = 0; i < words; i++) {
308 eerd = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) +
309 E1000_NVM_RW_REG_START;
// defines.h:#define E1000_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */
// defines.h:#define E1000_NVM_RW_REG_START 1 /* Start operation */
310
311 ew32(EERD, eerd);
312 ret_val = e1000e_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ);
313 if (ret_val)
314 break;
315
316 data = (er32(EERD) >> E1000_NVM_RW_REG_DATA);
317 }
318
319 if (ret_val)
320 e_dbg("NVM read error: %d\n", ret_val);
321
322 return ret_val;
323 }

e1000e 驱动读取并计算了 EEPROM 的前128字节(0x80Bytes 或 0x40words) 的累加和,当累加和等于 0xBABA 时,校验通过;否则报“The NVM Checksum Is Not Valid”错误信息。
写入错误的 EEPROM 内容测试在前面的故障分析阶段,在 windows 下测试故障网卡时没注意到 windows 把网卡识别为什么型号,所以尝试刷回故障卡的 EEPROM 内容确认一下。
之前作 EEPROM 内容对比时发现写入数据时,校验位会自动更新,所以得先绕过这个自动更新机制。
读驱动代码可以看到,用 ethtool 重写 EEPROM 时会自动更新校验码,需要对 e1000e 驱动打补丁才能将网卡 EEPROM 内容重新刷写回故障状态
修改 ethtool.c:
[size=0.9em]912 static int e1000_set_eeprom(struct net_device *netdev,
913 struct ethtool_eeprom *eeprom, u8 *bytes)
914 {
...
974 /* Update the checksum over the first part of the EEPROM if needed
975 * and flush shadow RAM for applicable controllers
976 */
977 if ((first_word <= NVM_CHECKSUM_REG) ||
978 (hw->mac.type == e1000_82583) ||
979 (hw->mac.type == e1000_82574) ||
980 (hw->mac.type == e1000_82573))
981 ret_val = e1000e_update_nvm_checksum(hw);
// 修改上一行跳过 e1000e_update_nvm_checksum(hw);
// 修改为 ret_val = 0;

跳过的一行最终调用的是nvm.c文件中的这个函数:
[size=0.9em]570 /**
571 * e1000e_update_nvm_checksum_generic - Update EEPROM checksum
572 * @hw: pointer to the HW structure
573 *
574 * Updates the EEPROM checksum by reading/adding each word of the EEPROM
575 * up to the checksum. Then calculates the EEPROM checksum and writes the
576 * value to the EEPROM.
577 **/
578 e32 e1000e_update_nvm_checksum_generic(struct e1000_hw *hw)
579 {
580 s32 ret_val;
581 u16 checksum = 0;
582 u16 i, nvm_data;
583
584 for (i = 0; i < NVM_CHECKSUM_REG; i++) {
585 ret_val = e1000_read_nvm(hw, i, 1, &nvm_data);
586 if (ret_val) {
587 e_dbg("NVM Read Error while updating checksum.\n");
588 return ret_val;
589 }
590 checksum += nvm_data;
591 }
592 checksum = (u16)NVM_SUM - checksum;
593 ret_val = e1000_write_nvm(hw, NVM_CHECKSUM_REG, 1, &checksum);
594 if (ret_val)
595 e_dbg("NVM Write Error while updating checksum.\n");
596
597 return ret_val;
598 }

编译安装并启用修改后的驱动
[size=0.9em]make install
modprobe -r e1000e && modrobe e1000e

重新写入故障的 EEPROM 内容
[size=0.9em][root@localhost ~]# head NIC_EEPROM_backup/enp4s0.eeprom
Offset Values
------ ------
0x0000: 00 90 27 e8 00 1e 20 04 00 f7 a0 10 20 ff ff ff
0x0010: 20 ff ff ff 20 02 00 00 00 80 d3 10 20 ff 58 8c
0x0020: 00 00 01 20 00 7e ff ff 00 10 48 00 00 00 04 27
0x0030: 00 6c 50 31 20 07 0b 4b 00 2d 40 01 00 f0 02 02
0x0040: 00 60 80 00 00 0f ff ff 00 4d 00 c6 00 00 ff 20
0x0050: 20 00 43 02 00 00 01 01 00 00 b3 05 00 00 ff ff
0x0060: 20 ff ff ff 20 ff ff ff 20 ff ff ff 20 ff ff ff
0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 14 5e

# 略过批处理文件准备过程
[root@localhost ~]# ./enp4s0-burn-batch.sh
[root@localhost ~]# ethtool -e enp4s0 | diff - NIC_EEPROM_backup/enp4s0.eeprom

看到 diff 输出为空,说明 EEPROM 内容恢复成故障状态。
重启后
[size=0.9em][root@localhost ~]# lspci -nn | grep -i net
01:00.0 Ethernet controller [0200]: Intel Corporation 82583V Gigabit Network Connection [8086:150c]
04:00.0 Ethernet controller [0200]: Intel Corporation 82583V Gigabit Network Connection [8086:150c]
[root@localhost ~]# nmcli dev
DEVICE TYPE STATE CONNECTION
enp1s0 ethernet connected Wired connection 1
lo loopback unmanaged --
[root@localhost ~]# dmesg | grep 04:00 | grep e1000e
[ 4.540731] e1000e 0000:04:00.0: Disabling ASPM L1
[ 4.540749] e1000e 0000:04:00.0: enabling device (0000 -> 0002)
[ 4.540964] e1000e 0000:04:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 4.591195] e1000e 0000:04:00.0: The NVM Checksum Is Not Valid
[ 4.673179] e1000e: probe of 0000:04:00.0 failed with error -5

enp4s0 不识别,但 lspci 识别的网卡型号仍为82583V. 难道写入的“故障 EEPROM 数据”搞错了?
Win to go 引导,两个网卡都识别为 82583V 且正常工作。windows 系统日志也中未见有价值的错误信息。
很可惜没能将网卡恢复成原始的故障状态。只能猜测:windows 驱动也许不做 EEPROM 内容校验?
最快的修复方法驱动代码上看,在 ethtool 写入过程中会计算并更新 0x7e 0x7f 两字节,使整个EEPROM数据区域的按字(16bits)累加和等于 0xBABA.
也就是说,写入哪怕一个字节也能使校验数据通过检查。
将前面修改的 ethtool.c 第981行恢复原样:
[size=0.9em]981 ret_val = e1000e_update_nvm_checksum(hw);

编译安装、重启
[size=0.9em]make install
reboot

重启后
[size=0.9em][root@localhost ~]# dmesg | grep e1000e | grep 04:00
[ 4.569221] e1000e 0000:04:00.0: Disabling ASPM L1
[ 4.569239] e1000e 0000:04:00.0: enabling device (0000 -> 0002)
[ 4.569457] e1000e 0000:04:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 4.620313] e1000e 0000:04:00.0: The NVM Checksum Is Not Valid
[ 4.682543] e1000e: probe of 0000:04:00.0 failed with error -5
[root@localhost ~]# modprobe -r e1000e && modprobe e1000e

ethtool 读出仍然有问题的数据并保存
[size=0.9em][root@localhost ~]# ethtool -e enp4s0 | head -n 10 > bad_rom.dump && cat bad_rom.dump
Offset Values
------ ------
0x0000: 00 90 27 e8 00 1e 20 04 00 f7 a0 10 20 ff ff ff
0x0010: 20 ff ff ff 20 02 00 00 00 80 d3 10 20 ff 58 8c
0x0020: 00 00 01 20 00 7e ff ff 00 10 48 00 00 00 04 27
0x0030: 00 6c 50 31 20 07 0b 4b 00 2d 40 01 00 f0 02 02
0x0040: 00 60 80 00 00 0f ff ff 00 4d 00 c6 00 00 ff 20
0x0050: 20 00 43 02 00 00 01 01 00 00 b3 05 00 00 ff ff
0x0060: 20 ff ff ff 20 ff ff ff 20 ff ff ff 20 ff ff ff
0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 14 5e

啥也不改,把第1字节原样写回。等一小会儿(几秒)后,再读出整块数据进行比较
[size=0.9em][root@localhost ~]# ethtool -E enp4s0 magic 0x150c8086 offset 0x0 value 0x00
[root@localhost ~]# ethtool -e enp4s0 | head -n 10 | diff - bad_rom.dump
10c10
< 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 43 70
---
> 0x0070: 20 ff ff ff 20 ff ff ff 20 ff 50 01 20 ff 14 5e

可以看到校验字已更新。
重启,故障解决。
Todo: 读驱动代码其他部分这是个万年坑,取决于什么时候能得闲 && 这事还能记得多久。
目标:
  • 了解EEPROM中其他数据的意义
  • 加深对该网卡工作过程、机制、模式的了解
  • 了解 e1000e 的初始化过程

总结
  • Intel Corporation 82583V Gigabit Network 的 EEPROM 中,有效数据为前 128Byte(0x00 - 0x7f),其中最前面的6个字节为MAC地址,最后面两个字节为校验码。Linux 下的 Intel 网卡驱动 e1000e 在加载时会校验 EEPROM 数据,如果这 128Byte 数据的按字(16bit)累加和不等于 0xBABA,驱动会报错并停止加载。Windows驱动可能不作校验。
  • 由于 e1000e 驱动中负责 ethtool 写入的代码会自动进行校验数据的计算和写入,如果只是为了修复校验错误而不管其他数据的正确性,只要写入任意一个byte(原样写回都行),驱动程序会完成剩下的工作。

参考资料



 楼主| 发表于 2022-11-1 15:18:37 | 显示全部楼层
笔记直接贴过来排版乱了,这是PDF版本

Intel PCIe 千兆网卡 EEPROM 故障处理.pdf (514.54 KB, 下载次数: 0)


回复 支持 反对

使用道具 举报

发表于 2022-11-1 21:40:51 | 显示全部楼层
请问这款不能在dos在对网卡的eeprom进行读写么?

这款网卡没玩过,感觉在linux系统下弄eeprom很麻烦。

intel 网卡在pe下编辑好eeprom以及其他配置,然后在dos下写入,感觉比linux 系统弄要方便不少。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-2 09:20:28 | 显示全部楼层
abingge 发表于 2022-11-1 21:40
请问这款不能在dos在对网卡的eeprom进行读写么?

这款网卡没玩过,感觉在linux系统下弄eeprom很麻烦。

DOS 下没有试过,看厂商支持。我当时在Intel网站上没找到工具。
当时刚好是十一长假,抱着玩的心态慢慢搞的,时间上不受限。
回复 支持 反对

使用道具 举报

发表于 2022-11-2 10:27:58 来自手机浏览器 | 显示全部楼层
本帖最后由 badcrazy 于 2022-11-2 10:31 编辑

板载网卡的eeprom整合在bios里,所以刷主板bios就行了,难道intel网卡不是这样?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-2 10:59:01 | 显示全部楼层
badcrazy 发表于 2022-11-2 10:27
板载网卡的eeprom整合在bios里,所以刷主板bios就行了,难道intel网卡不是这样? ...

按理应该是这样

我这就是折腾
回复 支持 反对

使用道具 举报

发表于 2022-11-2 15:11:11 来自手机浏览器 | 显示全部楼层
bigfatfish 发表于 2022-11-2 10:59
按理应该是这样

我这就是折腾

嗯,折腾无罪
回复 支持 反对

使用道具 举报

发表于 2022-11-2 22:08:32 | 显示全部楼层
bigfatfish 发表于 2022-11-2 10:59
按理应该是这样

我这就是折腾

感觉很麻烦。为什么这么弄??

我是这么弄的。您参考下。

一片新的网卡芯片焊上去后,
首先用intel的工具备份出原机器其他芯片/其他机器同款网卡的eeprom,根据原eeprom修改配置文件,然后在dos下给新芯片写入eeprom。最后给新的网卡芯片写入mac地址。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-3 10:07:31 | 显示全部楼层
abingge 发表于 2022-11-2 22:08
感觉很麻烦。为什么这么弄??

我是这么弄的。您参考下。

如果只是处理故障,您的方法肯定无疑比我的要好

前面也说了,折腾的原因 一是当时没找到Intel的工具,二是享受折腾和刨根问底的过程。
如果仅仅是修复,即便是用我笔记上经过简化的方法,对熟悉Linux操作的人来说也不会比官方工具麻烦太多。
回复 支持 反对

使用道具 举报

发表于 2022-11-3 14:15:36 | 显示全部楼层
这个不错,弄软件我来就迷糊了。
回复 支持 反对

使用道具 举报

发表于 2022-11-3 14:17:13 | 显示全部楼层
badcrazy 发表于 2022-11-2 10:27
板载网卡的eeprom整合在bios里,所以刷主板bios就行了,难道intel网卡不是这样? ...

你这刷的还是主板的bios模块,网卡模块的eeprom 没变。
回复 支持 反对

使用道具 举报

发表于 2022-11-3 16:21:01 来自手机浏览器 | 显示全部楼层
maithon 发表于 2022-11-3 14:17
你这刷的还是主板的bios模块,网卡模块的eeprom 没变。

网卡的eeprom也在bios里啊,独立网卡有flash芯片存eeprom,板载没有独立flash就存bios里,因为我以前玩的都是板载螃蟹卡,所以我才说intel是不是不一样,比如芯片内置flash存eeprom
回复 支持 反对

使用道具 举报

发表于 2022-11-3 16:41:41 | 显示全部楼层
badcrazy 发表于 2022-11-3 16:21
网卡的eeprom也在bios里啊,独立网卡有flash芯片存eeprom,板载没有独立flash就存bios里,因为我以前玩的 ...

我原来说的有歧义。主板bios升级,一般只升级bios内的主板模块,不涉及bios内的网卡配置模块。
回复 支持 反对

使用道具 举报

发表于 2022-11-3 22:12:04 来自手机浏览器 | 显示全部楼层
maithon 发表于 2022-11-3 16:41
我原来说的有歧义。主板bios升级,一般只升级bios内的主板模块,不涉及bios内的网卡配置模块。 ...

现在是这样,特别是笔记本,但以前都是整体刷写的
回复 支持 反对

使用道具 举报

发表于 2022-11-9 16:40:04 | 显示全部楼层
学习了 啊,网卡还可以刷。。。
回复 支持 反对

使用道具 举报

发表于 2022-12-12 18:13:27 | 显示全部楼层
谢谢老佬!!!才看见。。。
回复 支持 反对

使用道具 举报

发表于 2022-12-12 19:17:38 | 显示全部楼层
badcrazy 发表于 2022-11-2 10:27
板载网卡的eeprom整合在bios里,所以刷主板bios就行了,难道intel网卡不是这样? ...

大神在BIOS怎么搞!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-4-28 01:47 , Processed in 0.390001 second(s), 13 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

快速回复 返回顶部 返回列表