Skip to content

Instantly share code, notes, and snippets.

@g3rhard
Last active March 23, 2026 15:15
Show Gist options
  • Select an option

  • Save g3rhard/73b3476736217d0d6a53e8c8b5800d6e to your computer and use it in GitHub Desktop.

Select an option

Save g3rhard/73b3476736217d0d6a53e8c8b5800d6e to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -Eeuo pipefail
TS="$(date +%F-%H%M%S)"
HOST="$(hostname -s 2>/dev/null || hostname)"
OUT="reverse-driver-audit-${HOST}-${TS}"
mkdir -p "$OUT"/{
base,
hardware,
kernel,
modules,
firmware,
packages,
apt,
etc,
runtime,
boot,
logs
}
run() {
local name="$1"
shift
{
echo "### CMD: $*"
echo
"$@"
} >"$name" 2>&1 || true
}
copy_if_exists() {
local src="$1"
local dst="$2"
[[ -e "$src" ]] && cp -a "$src" "$dst" 2>/dev/null || true
}
# ------------------------------------------------------------------------------
# Base system
# ------------------------------------------------------------------------------
run "$OUT/base/uname.txt" uname -a
run "$OUT/base/os-release.txt" cat /etc/os-release
run "$OUT/base/cmdline.txt" cat /proc/cmdline
run "$OUT/base/lsb_release.txt" lsb_release -a
run "$OUT/base/date.txt" date -Is
run "$OUT/base/mount.txt" mount
run "$OUT/base/findmnt.txt" findmnt -A
run "$OUT/base/secureboot.txt" mokutil --sb-state
run "$OUT/base/dmesg-version.txt" dmesg --version
# ------------------------------------------------------------------------------
# Hardware inventory
# ------------------------------------------------------------------------------
run "$OUT/hardware/lspci-nnk.txt" lspci -nnk
run "$OUT/hardware/lspci-vvvnn.txt" lspci -vvvnn
run "$OUT/hardware/lsusb-v.txt" lsusb -v
run "$OUT/hardware/lsblk.txt" lsblk -a
run "$OUT/hardware/lshw-short.txt" sudo lshw -short
run "$OUT/hardware/lshw-json.txt" sudo lshw -json
run "$OUT/hardware/lshw-display.txt" sudo lshw -C display
run "$OUT/hardware/lshw-network.txt" sudo lshw -C network
run "$OUT/hardware/lshw-storage.txt" sudo lshw -C storage
run "$OUT/hardware/lshw-multimedia.txt" sudo lshw -C multimedia
run "$OUT/hardware/usb-devices.txt" bash -lc '[[ -r /sys/kernel/debug/usb/devices ]] && cat /sys/kernel/debug/usb/devices || true'
run "$OUT/hardware/cpu-lscpu.txt" lscpu
run "$OUT/hardware/mem-free.txt" free -h
# sysfs summaries
run "$OUT/hardware/sysfs-pci-tree.txt" bash -lc 'find /sys/bus/pci/devices -maxdepth 2 -type f \\( -name vendor -o -name device -o -name modalias -o -name driver_override \\) -print -exec cat {} \\;'
run "$OUT/hardware/sysfs-usb-tree.txt" bash -lc 'find /sys/bus/usb/devices -maxdepth 2 -type f \\( -name idVendor -o -name idProduct -o -name modalias -o -name driver_override \\) -print -exec cat {} \\;'
# Bound drivers in sysfs
run "$OUT/hardware/pci-bound-drivers.txt" bash -lc '
for d in /sys/bus/pci/devices/*; do
echo "## $d"
[[ -f "$d/vendor" ]] && echo -n "vendor=" && cat "$d/vendor"
[[ -f "$d/device" ]] && echo -n "device=" && cat "$d/device"
[[ -L "$d/driver" ]] && echo "driver=$(basename "$(readlink -f "$d/driver")")" || echo "driver=<none>"
[[ -f "$d/modalias" ]] && echo -n "modalias=" && cat "$d/modalias"
echo
done
'
run "$OUT/hardware/usb-bound-drivers.txt" bash -lc '
for d in /sys/bus/usb/devices/*; do
[[ -d "$d" ]] || continue
echo "## $d"
[[ -f "$d/idVendor" ]] && echo -n "idVendor=" && cat "$d/idVendor"
[[ -f "$d/idProduct" ]] && echo -n "idProduct=" && cat "$d/idProduct"
[[ -L "$d/driver" ]] && echo "driver=$(basename "$(readlink -f "$d/driver")")" || echo "driver=<none>"
[[ -f "$d/modalias" ]] && echo -n "modalias=" && cat "$d/modalias"
echo
done
'
# ------------------------------------------------------------------------------
# Kernel / modules / initramfs
# ------------------------------------------------------------------------------
run "$OUT/kernel/version.txt" cat /proc/version
run "$OUT/kernel/config.txt" bash -lc 'zcat /proc/config.gz'
run "$OUT/kernel/modules-loaded.txt" lsmod
run "$OUT/kernel/modules-builtin.txt" bash -lc 'modprobe -c | sed -n "/^options /p;/^alias /p;/^blacklist /p"'
run "$OUT/kernel/all-modules-tree.txt" bash -lc 'find /lib/modules/$(uname -r) -type f | sort'
run "$OUT/kernel/modules-alias.txt" bash -lc 'grep -R . /lib/modules/$(uname -r)/modules.* 2>/dev/null'
run "$OUT/kernel/depmod-config.txt" bash -lc 'grep -R . /etc/depmod.d /lib/depmod.d 2>/dev/null'
run "$OUT/kernel/modprobe-effective.txt" modprobe -c
run "$OUT/kernel/proc-modules.txt" cat /proc/modules
run "$OUT/kernel/interrupts.txt" cat /proc/interrupts
run "$OUT/kernel/iomem.txt" cat /proc/iomem
run "$OUT/kernel/ioports.txt" cat /proc/ioports
# initramfs contents if available
if command -v lsinitramfs >/dev/null 2>&1; then
run "$OUT/boot/lsinitramfs-current.txt" bash -lc 'lsinitramfs /boot/initrd.img-$(uname -r)'
fi
copy_if_exists "/boot/config-$(uname -r)" "$OUT/boot/"
copy_if_exists "/boot/System.map-$(uname -r)" "$OUT/boot/"
copy_if_exists "/boot/vmlinuz-$(uname -r)" "$OUT/boot/"
copy_if_exists "/boot/initrd.img-$(uname -r)" "$OUT/boot/"
# ------------------------------------------------------------------------------
# Modules -> file -> package -> repo -> firmware
# ------------------------------------------------------------------------------
: > "$OUT/modules/module-map.tsv"
: > "$OUT/modules/firmware-map.tsv"
: > "$OUT/packages/package-list-from-modules.txt"
while read -r mod _; do
[[ "$mod" == "Module" ]] && continue
modfile="$(modinfo -F filename "$mod" 2>/dev/null || true)"
depends="$(modinfo -F depends "$mod" 2>/dev/null || true)"
firmware="$(modinfo -F firmware "$mod" 2>/dev/null || true)"
vermagic="$(modinfo -F vermagic "$mod" 2>/dev/null || true)"
srcversion="$(modinfo -F srcversion "$mod" 2>/dev/null || true)"
pkg=""
if [[ -n "$modfile" && -e "$modfile" ]]; then
pkg="$(dpkg -S "$modfile" 2>/dev/null || true)"
fi
printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
"$mod" "$modfile" "$pkg" "$depends" "$vermagic" "$srcversion" \
>> "$OUT/modules/module-map.tsv"
if [[ -n "$firmware" ]]; then
while IFS= read -r fw; do
[[ -z "$fw" ]] && continue
fwpkg="$(dpkg -S "/lib/firmware/$fw" 2>/dev/null || true)"
printf '%s\t%s\t%s\n' "$mod" "$fw" "$fwpkg" >> "$OUT/modules/firmware-map.tsv"
done <<< "$firmware"
fi
{
echo "### MODULE: $mod"
modinfo "$mod" 2>/dev/null || true
echo
} > "$OUT/modules/${mod}.modinfo.txt"
done < <(lsmod)
cut -f3 "$OUT/modules/module-map.tsv" \
| cut -d: -f1 \
| sed '/^$/d' \
| sort -u \
> "$OUT/packages/package-list-from-modules.txt"
# Also map every .ko/.ko.zst file under current kernel tree
: > "$OUT/modules/all-kernel-module-files.tsv"
find "/lib/modules/$(uname -r)" -type f \( -name '*.ko' -o -name '*.ko.zst' -o -name '*.ko.xz' \) | sort \
| while read -r f; do
pkg="$(dpkg -S "$f" 2>/dev/null || true)"
printf '%s\t%s\n' "$f" "$pkg" >> "$OUT/modules/all-kernel-module-files.tsv"
done
# ------------------------------------------------------------------------------
# DKMS / out-of-tree
# ------------------------------------------------------------------------------
run "$OUT/modules/dkms-status.txt" dkms status
copy_if_exists "/var/lib/dkms" "$OUT/modules/var-lib-dkms"
copy_if_exists "/usr/src" "$OUT/modules/usr-src"
# ------------------------------------------------------------------------------
# APT / package provenance
# ------------------------------------------------------------------------------
run "$OUT/packages/dpkg-query-all.tsv" dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n'
run "$OUT/packages/apt-mark-manual.txt" apt-mark showmanual
run "$OUT/apt/sources-list.txt" bash -lc 'grep -R ^deb /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null'
run "$OUT/apt/preferences.txt" bash -lc 'grep -R . /etc/apt/preferences /etc/apt/preferences.d 2>/dev/null'
run "$OUT/apt/apt-config-dump.txt" apt-config dump
copy_if_exists "/etc/apt" "$OUT/etc/"
while read -r pkg; do
[[ -z "$pkg" ]] && continue
run "$OUT/apt/policy-${pkg}.txt" apt-cache policy "$pkg"
run "$OUT/apt/show-${pkg}.txt" apt-cache show "$pkg"
done < "$OUT/packages/package-list-from-modules.txt"
# Packages with "driver-ish" names
run "$OUT/packages/driver-like-packages.txt" bash -lc \
"dpkg-query -W -f='${binary:Package}\t${Version}\n' | grep -Ei '(^linux-(image|modules|headers)|firmware|nvidia|amdgpu|intel.*(media|gpu)|broadcom|brcm|realtek|rtl|mt76|iwlwifi|wireless|wl|dkms|displaylink|evdi|virtualbox|v4l|xserver-xorg-video|mesa|va-driver|vulkan)'"
# Installation history
run "$OUT/logs/apt-history-grep.txt" bash -lc 'zgrep -hEi "install|upgrade|remove" /var/log/apt/history.log* 2>/dev/null'
run "$OUT/logs/dpkg-log-grep.txt" bash -lc 'zgrep -hEi " install | upgrade | remove " /var/log/dpkg.log* 2>/dev/null'
# ------------------------------------------------------------------------------
# Firmware / microcode
# ------------------------------------------------------------------------------
run "$OUT/firmware/linux-firmware-package-policy.txt" apt-cache policy linux-firmware
run "$OUT/firmware/firmware-dir-list.txt" bash -lc 'find /lib/firmware -maxdepth 3 -type f | sort'
run "$OUT/firmware/dmesg-firmware.txt" bash -lc 'dmesg | grep -Ei "firmware|microcode"'
run "$OUT/firmware/cpu-microcode.txt" bash -lc 'dmesg | grep -Ei "microcode|intel-ucode|amd-ucode"'
# ------------------------------------------------------------------------------
# /etc and selected /usr runtime-relevant content
# ------------------------------------------------------------------------------
copy_if_exists "/etc/modprobe.d" "$OUT/etc/"
copy_if_exists "/etc/modules" "$OUT/etc/"
copy_if_exists "/etc/modules-load.d" "$OUT/etc/"
copy_if_exists "/etc/udev" "$OUT/etc/"
copy_if_exists "/etc/default" "$OUT/etc/"
copy_if_exists "/etc/initramfs-tools" "$OUT/etc/"
copy_if_exists "/etc/kernel" "$OUT/etc/"
copy_if_exists "/etc/systemd" "$OUT/etc/"
copy_if_exists "/etc/NetworkManager" "$OUT/etc/"
copy_if_exists "/etc/netplan" "$OUT/etc/"
copy_if_exists "/etc/X11" "$OUT/etc/"
copy_if_exists "/etc/gdm3" "$OUT/etc/"
copy_if_exists "/etc/default/grub" "$OUT/etc/default-grub" || true
# Selected /usr locations that often carry driver/runtime rules
mkdir -p "$OUT/runtime/usr"
copy_if_exists "/usr/lib/modprobe.d" "$OUT/runtime/usr/"
copy_if_exists "/usr/lib/modules-load.d" "$OUT/runtime/usr/"
copy_if_exists "/usr/lib/udev/rules.d" "$OUT/runtime/usr/"
copy_if_exists "/usr/share/X11/xorg.conf.d" "$OUT/runtime/usr/"
copy_if_exists "/usr/lib/firmware" "$OUT/runtime/usr/" # may not exist on all systems
# Systemd and udev state
run "$OUT/runtime/systemctl-units.txt" systemctl list-unit-files
run "$OUT/runtime/systemctl-failed.txt" systemctl --failed
run "$OUT/runtime/udevadm-info-all.txt" bash -lc 'udevadm info --export-db'
run "$OUT/runtime/journal-kernel-current.txt" journalctl -k -b
run "$OUT/runtime/journal-driverish.txt" bash -lc 'journalctl -b | grep -Ei "firmware|driver|dkms|nvidia|amdgpu|iwl|rtl|brcm|wlan|bluetooth|audio|snd|drm|gpu|xorg|wayland"'
# ------------------------------------------------------------------------------
# Network / graphics / audio runtime state
# ------------------------------------------------------------------------------
run "$OUT/runtime/ip-link.txt" ip -details link
run "$OUT/runtime/ip-addr.txt" ip addr
run "$OUT/runtime/ip-route.txt" ip route
run "$OUT/runtime/ethtool-summary.txt" bash -lc '
for i in $(ls /sys/class/net 2>/dev/null); do
echo "## $i"
ethtool "$i" 2>/dev/null || true
ethtool -i "$i" 2>/dev/null || true
echo
done
'
run "$OUT/runtime/nmcli-devices.txt" nmcli device show
run "$OUT/runtime/rfkill.txt" rfkill list
run "$OUT/runtime/pactl-cards.txt" pactl list cards
run "$OUT/runtime/pactl-sinks.txt" pactl list sinks
run "$OUT/runtime/loginctl-sessions.txt" loginctl session-status
if command -v nvidia-smi >/dev/null 2>&1; then
run "$OUT/runtime/nvidia-smi.txt" nvidia-smi
fi
if command -v glxinfo >/dev/null 2>&1; then
run "$OUT/runtime/glxinfo-B.txt" glxinfo -B
fi
if command -v vulkaninfo >/dev/null 2>&1; then
run "$OUT/runtime/vulkaninfo-summary.txt" bash -lc 'vulkaninfo --summary'
fi
# ------------------------------------------------------------------------------
# Explicit "what module would handle this modalias"
# ------------------------------------------------------------------------------
run "$OUT/modules/modalias-resolution.txt" bash -lc '
for f in /sys/bus/pci/devices/*/modalias /sys/bus/usb/devices/*/modalias; do
[[ -f "$f" ]] || continue
alias="$(cat "$f")"
echo "## $f"
echo "$alias"
modprobe -R "$alias" 2>/dev/null || true
echo
done
'
# ------------------------------------------------------------------------------
# Final tarball
# ------------------------------------------------------------------------------
tar -czf "${OUT}.tar.gz" "$OUT"
echo "Created: ${OUT}.tar.gz"
echo "Directory: $OUT"
#!/bin/sh
# =============================================================================
# e1000e Data Collector - MINIMAL BusyBox Version (Enhanced v2)
# Purpose: Collect data in initramfs environment with minimal dependencies
# =============================================================================
#
# DEPENDENCIES (ALL standard BusyBox applets):
# sh, cat, echo, test, mkdir, cp, ls, readlink, cut, tr, basename
# find, sort, grep, head, tail, date, sync, dd, od
#
# OPTIONAL (if available in your BusyBox build):
# md5sum, ip, dmesg, mount, umount
#
# OPTIONAL EXTERNAL TOOLS (greatly enhance diagnostics):
# ethtool - NIC configuration and register dumps
#
# ENHANCED DATA COLLECTED (v2):
# - PCI config space dump
# - PCIe link status (speed/width)
# - MSI/MSI-X state
# - ACPI/power management
# - Full ethtool diagnostics (pause, coalesce, rings, EEE, register dump)
# - TX/RX queue information
# - IRQ affinity
# - debugfs data (if accessible)
# - Boot timing extraction
#
# TO INCLUDE IN INITRAMFS:
# 1. Copy this script to /etc/initramfs-tools/scripts/init-premount/
# or /usr/lib/dracut/modules.d/99e1000e-debug/
# 2. Rebuild initramfs: update-initramfs -u -k $(uname -r)
#
# =============================================================================
# Output directory - change to your mounted USB/boot partition
OUT="${1:-/tmp/e1000e-busybox-$(cat /proc/uptime | cut -d. -f1)}"
# PCI device for e1000e NIC
PCIDEV="0000:00:1f.6"
# Create output directory
mkdir -p "$OUT"
echo "=== E1000E BusyBox Data Collector ===" > "$OUT/00_header.txt"
echo "Timestamp: $(date 2>/dev/null || echo 'unknown')" >> "$OUT/00_header.txt"
echo "Uptime: $(cat /proc/uptime)" >> "$OUT/00_header.txt"
# -----------------------------------------------------------------------------
# 1. Boot Identity
# -----------------------------------------------------------------------------
echo "Collecting boot identity..."
uname -a > "$OUT/uname_a.txt" 2>&1
cat /proc/cmdline > "$OUT/proc_cmdline.txt" 2>&1
cat /proc/version > "$OUT/proc_version.txt" 2>&1
# UEFI detection
if [ -d /sys/firmware/efi ]; then
echo "UEFI" > "$OUT/firmware_mode.txt"
else
echo "BIOS" > "$OUT/firmware_mode.txt"
fi
# PID 1 (init system)
cat /proc/1/comm > "$OUT/pid1_comm.txt" 2>&1
tr '\0' ' ' < /proc/1/cmdline > "$OUT/pid1_cmdline.txt" 2>/dev/null
# Secure boot indicators
grep -oE '(module\.sig_enforce|secureboot|efi=)[^ ]*' /proc/cmdline > "$OUT/secureboot_cmdline.txt" 2>/dev/null || echo "none" > "$OUT/secureboot_cmdline.txt"
cat /proc/sys/kernel/module_sig_enforce > "$OUT/module_sig_enforce.txt" 2>/dev/null || echo "unavailable" > "$OUT/module_sig_enforce.txt"
# -----------------------------------------------------------------------------
# 2. Modules
# -----------------------------------------------------------------------------
echo "Collecting modules info..."
cat /proc/modules > "$OUT/proc_modules.txt" 2>&1
ls -1 /sys/module > "$OUT/sys_module_list.txt" 2>&1
# e1000e parameters
if [ -d /sys/module/e1000e/parameters ]; then
echo "loaded" > "$OUT/e1000e_loaded.txt"
for p in /sys/module/e1000e/parameters/*; do
[ -r "$p" ] && echo "$(basename "$p")=$(cat "$p" 2>/dev/null)" >> "$OUT/e1000e_params.txt"
done
# === NEW: Full e1000e module sysfs tree ===
ls -laR /sys/module/e1000e/ > "$OUT/e1000e_sysfs_full.txt" 2>&1
# === NEW: Driver info from sysfs ===
for attr in /sys/module/e1000e/*; do
[ -f "$attr" ] && [ -r "$attr" ] && echo "$(basename "$attr")=$(cat "$attr" 2>/dev/null)" >> "$OUT/e1000e_module_attrs.txt"
done
else
echo "not_loaded" > "$OUT/e1000e_loaded.txt"
fi
# Modprobe config (if accessible)
if [ -d /etc/modprobe.d ]; then
cat /etc/modprobe.d/*.conf > "$OUT/modprobe_d.txt" 2>/dev/null || echo "empty" > "$OUT/modprobe_d.txt"
fi
# -----------------------------------------------------------------------------
# 3. PCI Device State
# -----------------------------------------------------------------------------
echo "Collecting PCI device state..."
PCISYS="/sys/bus/pci/devices/$PCIDEV"
if [ -d "$PCISYS" ]; then
echo "present" > "$OUT/pci_device_status.txt"
# Basic attributes
for attr in vendor device subsystem_vendor subsystem_device class irq enable; do
[ -r "$PCISYS/$attr" ] && echo "$attr=$(cat "$PCISYS/$attr" 2>/dev/null)" >> "$OUT/pci_attrs.txt"
done
# Driver binding
readlink "$PCISYS/driver" > "$OUT/pci_driver_link.txt" 2>&1 || echo "none" > "$OUT/pci_driver_link.txt"
# Power state
if [ -d "$PCISYS/power" ]; then
for attr in control runtime_status runtime_suspended_time; do
[ -r "$PCISYS/power/$attr" ] && echo "$attr=$(cat "$PCISYS/power/$attr" 2>/dev/null)" >> "$OUT/pci_power.txt"
done
fi
# Resources
cat "$PCISYS/resource" > "$OUT/pci_resource.txt" 2>/dev/null
# === NEW: PCI config space (first 256 bytes as hex) ===
if [ -r "$PCISYS/config" ]; then
od -A x -t x1 -N 256 "$PCISYS/config" > "$OUT/pci_config_space.txt" 2>&1
fi
# === NEW: MSI/MSI-X capability ===
[ -d "$PCISYS/msi_irqs" ] && ls "$PCISYS/msi_irqs" > "$OUT/pci_msi_irqs.txt" 2>&1
[ -r "$PCISYS/msi_bus" ] && cat "$PCISYS/msi_bus" > "$OUT/pci_msi_bus.txt" 2>&1
# === NEW: PCIe link status ===
for attr in current_link_speed current_link_width max_link_speed max_link_width; do
[ -r "$PCISYS/$attr" ] && echo "$attr=$(cat "$PCISYS/$attr" 2>/dev/null)" >> "$OUT/pci_link_status.txt"
done
# === NEW: D3cold and ASPM status ===
[ -r "$PCISYS/d3cold_allowed" ] && echo "d3cold_allowed=$(cat "$PCISYS/d3cold_allowed")" >> "$OUT/pci_power_extended.txt"
[ -r "$PCISYS/link/l1_aspm" ] && echo "l1_aspm=$(cat "$PCISYS/link/l1_aspm" 2>/dev/null)" >> "$OUT/pci_power_extended.txt"
# === NEW: All sysfs attributes for this device ===
ls -la "$PCISYS/" > "$OUT/pci_sysfs_listing.txt" 2>&1
else
echo "not_found" > "$OUT/pci_device_status.txt"
fi
# List all PCI devices
ls /sys/bus/pci/devices/ > "$OUT/all_pci_devices.txt" 2>&1
# -----------------------------------------------------------------------------
# 3b. ACPI & Power Management (NEW SECTION)
# -----------------------------------------------------------------------------
echo "Collecting ACPI/power management info..."
# ACPI tables list
ls /sys/firmware/acpi/tables/ > "$OUT/acpi_tables.txt" 2>/dev/null || echo "no acpi tables" > "$OUT/acpi_tables.txt"
# ACPI power state
cat /sys/power/state > "$OUT/power_state.txt" 2>/dev/null || echo "unavailable" > "$OUT/power_state.txt"
cat /sys/power/mem_sleep > "$OUT/power_mem_sleep.txt" 2>/dev/null || true
# PCI device power management for all devices
for dev in /sys/bus/pci/devices/*/power/runtime_status; do
[ -r "$dev" ] && echo "$(dirname "$dev" | xargs basename): $(cat "$dev")" >> "$OUT/all_pci_power.txt"
done 2>/dev/null
# Check for ASPM state
if [ -r /sys/module/pcie_aspm/parameters/policy ]; then
cat /sys/module/pcie_aspm/parameters/policy > "$OUT/pcie_aspm_policy.txt" 2>&1
fi
# Intel ME status if available
[ -d /sys/bus/mei/devices ] && ls -la /sys/bus/mei/devices/ > "$OUT/mei_devices.txt" 2>&1
# -----------------------------------------------------------------------------
# 4. Interrupts
# -----------------------------------------------------------------------------
echo "Collecting interrupt info..."
cat /proc/interrupts > "$OUT/proc_interrupts.txt" 2>&1
grep -E "(e1000|eth|enp|eno)" /proc/interrupts > "$OUT/nic_interrupts.txt" 2>/dev/null || echo "none" > "$OUT/nic_interrupts.txt"
# -----------------------------------------------------------------------------
# 5. Network State
# -----------------------------------------------------------------------------
echo "Collecting network state..."
# sysfs network interfaces
ls -la /sys/class/net/ > "$OUT/net_interfaces.txt" 2>&1
# Find our interface
IFACE=""
for i in eth0 enp0s31f6 eno1 eno2; do
if [ -d "/sys/class/net/$i" ]; then
IFACE="$i"
break
fi
done
echo "$IFACE" > "$OUT/detected_iface.txt"
# Interface sysfs data
if [ -n "$IFACE" ] && [ -d "/sys/class/net/$IFACE" ]; then
NETSYS="/sys/class/net/$IFACE"
for attr in operstate carrier speed duplex mtu address; do
[ -r "$NETSYS/$attr" ] && echo "$attr=$(cat "$NETSYS/$attr" 2>/dev/null)" >> "$OUT/iface_attrs.txt"
done
readlink "$NETSYS/device" >> "$OUT/iface_pci_link.txt" 2>/dev/null
# === NEW: TX/RX queue information ===
if [ -d "$NETSYS/queues" ]; then
ls -la "$NETSYS/queues/" > "$OUT/iface_queues_list.txt" 2>&1
for q in $NETSYS/queues/tx-*; do
[ -d "$q" ] && {
QN=$(basename "$q")
for attr in tx_timeout byte_queue_limits/limit byte_queue_limits/limit_max; do
[ -r "$q/$attr" ] && echo "$QN/$attr=$(cat "$q/$attr" 2>/dev/null)" >> "$OUT/iface_tx_queues.txt"
done
}
done
for q in $NETSYS/queues/rx-*; do
[ -d "$q" ] && {
QN=$(basename "$q")
for attr in rps_cpus rps_flow_cnt; do
[ -r "$q/$attr" ] && echo "$QN/$attr=$(cat "$q/$attr" 2>/dev/null)" >> "$OUT/iface_rx_queues.txt"
done
}
done
fi
# === NEW: Interface statistics from sysfs ===
if [ -d "$NETSYS/statistics" ]; then
for stat in $NETSYS/statistics/*; do
[ -r "$stat" ] && echo "$(basename "$stat")=$(cat "$stat" 2>/dev/null)" >> "$OUT/iface_sysfs_stats.txt"
done
fi
# === NEW: Interface flags ===
[ -r "$NETSYS/flags" ] && echo "flags=$(cat "$NETSYS/flags")" >> "$OUT/iface_flags.txt"
[ -r "$NETSYS/tx_queue_len" ] && echo "tx_queue_len=$(cat "$NETSYS/tx_queue_len")" >> "$OUT/iface_flags.txt"
fi
# ip command if available
if command -v ip >/dev/null 2>&1; then
ip link show > "$OUT/ip_link.txt" 2>&1
ip addr show > "$OUT/ip_addr.txt" 2>&1
fi
# /proc/net/dev
cat /proc/net/dev > "$OUT/proc_net_dev.txt" 2>&1
# ethtool if available (comprehensive collection)
if command -v ethtool >/dev/null 2>&1 && [ -n "$IFACE" ]; then
ethtool "$IFACE" > "$OUT/ethtool.txt" 2>&1 || true
ethtool -i "$IFACE" > "$OUT/ethtool_i.txt" 2>&1 || true
ethtool -S "$IFACE" > "$OUT/ethtool_S.txt" 2>&1 || true
# Additional ethtool diagnostics
ethtool -a "$IFACE" > "$OUT/ethtool_pause.txt" 2>&1 || true # Pause/flow control
ethtool -c "$IFACE" > "$OUT/ethtool_coalesce.txt" 2>&1 || true # Interrupt coalescing
ethtool -g "$IFACE" > "$OUT/ethtool_ring.txt" 2>&1 || true # Ring buffer sizes
ethtool -k "$IFACE" > "$OUT/ethtool_offload.txt" 2>&1 || true # Offload features
ethtool -d "$IFACE" > "$OUT/ethtool_dump.txt" 2>&1 || true # Register dump (PHY!)
ethtool --show-eee "$IFACE" > "$OUT/ethtool_eee.txt" 2>&1 || true # Energy Efficient Ethernet
ethtool -m "$IFACE" > "$OUT/ethtool_module.txt" 2>&1 || true # Module/SFP info
ethtool -e "$IFACE" > "$OUT/ethtool_eeprom.txt" 2>&1 || true # EEPROM dump
ethtool --show-priv-flags "$IFACE" > "$OUT/ethtool_privflags.txt" 2>&1 || true
fi
# -----------------------------------------------------------------------------
# 6. Kernel Messages
# -----------------------------------------------------------------------------
echo "Collecting kernel messages..."
if command -v dmesg >/dev/null 2>&1; then
dmesg > "$OUT/dmesg_full.txt" 2>&1
dmesg | grep -iE "(e1000|intel.*eth)" > "$OUT/dmesg_e1000e.txt" 2>/dev/null || true
dmesg | grep -iE "(error|fail|timeout|reset|watchdog)" > "$OUT/dmesg_errors.txt" 2>/dev/null || true
dmesg | tail -n 200 > "$OUT/dmesg_tail.txt" 2>&1
# === NEW: More specific dmesg filters ===
dmesg | grep -iE "(pci|pcie|aspm|d3cold|d3hot)" > "$OUT/dmesg_pci.txt" 2>/dev/null || true
dmesg | grep -iE "(acpi|power|pm|sleep|wake)" > "$OUT/dmesg_power.txt" 2>/dev/null || true
dmesg | grep -iE "(dma|iommu|iova|swiotlb)" > "$OUT/dmesg_dma.txt" 2>/dev/null || true
dmesg | grep -iE "(irq|interrupt|msi)" > "$OUT/dmesg_irq.txt" 2>/dev/null || true
dmesg | grep -iE "(firmware|microcode|mei)" > "$OUT/dmesg_firmware.txt" 2>/dev/null || true
# === NEW: Boot timing extraction ===
dmesg | grep -E "^\[[ ]*[0-9]+\.[0-9]+\]" | head -50 > "$OUT/dmesg_boot_timing.txt" 2>&1
else
echo "dmesg not available" > "$OUT/dmesg_full.txt"
fi
# === NEW: Kernel ring buffer size ===
[ -r /proc/sys/kernel/printk ] && cat /proc/sys/kernel/printk > "$OUT/kernel_printk.txt"
# -----------------------------------------------------------------------------
# 6b. Debug Filesystem (NEW SECTION)
# -----------------------------------------------------------------------------
echo "Checking debugfs..."
if [ -d /sys/kernel/debug ]; then
ls /sys/kernel/debug/ > "$OUT/debugfs_root.txt" 2>&1 || echo "no access" > "$OUT/debugfs_root.txt"
# e1000e debug info if available
if [ -d /sys/kernel/debug/e1000e ]; then
ls -la /sys/kernel/debug/e1000e/ > "$OUT/debugfs_e1000e.txt" 2>&1
for f in /sys/kernel/debug/e1000e/$PCIDEV/*; do
[ -r "$f" ] && {
echo "=== $(basename "$f") ===" >> "$OUT/debugfs_e1000e_data.txt"
cat "$f" >> "$OUT/debugfs_e1000e_data.txt" 2>&1
}
done
fi
# PCIe debug info
[ -d /sys/kernel/debug/pcie_aspm ] && cat /sys/kernel/debug/pcie_aspm/* > "$OUT/debugfs_aspm.txt" 2>/dev/null
# DMA debug
[ -r /sys/kernel/debug/dma-api/stats ] && cat /sys/kernel/debug/dma-api/stats > "$OUT/debugfs_dma_stats.txt" 2>&1
fi
# -----------------------------------------------------------------------------
# 7. Processes, Mounts & System State
# -----------------------------------------------------------------------------
echo "Collecting process/mount/system info..."
cat /proc/mounts > "$OUT/proc_mounts.txt" 2>&1
cat /proc/meminfo > "$OUT/meminfo.txt" 2>&1
if command -v ps >/dev/null 2>&1; then
ps > "$OUT/ps.txt" 2>&1
fi
# === NEW: CPU info (may affect interrupt handling) ===
cat /proc/cpuinfo > "$OUT/cpuinfo.txt" 2>&1 || true
grep -E "^(processor|model name|cpu MHz|flags)" /proc/cpuinfo > "$OUT/cpuinfo_summary.txt" 2>/dev/null
# === NEW: IRQ affinity for NIC ===
if [ -n "$IFACE" ]; then
for irqdir in /proc/irq/*; do
if grep -q "$IFACE\|e1000" "$irqdir/smp_affinity_list" 2>/dev/null; then
IRQNUM=$(basename "$irqdir")
echo "IRQ $IRQNUM:" >> "$OUT/nic_irq_affinity.txt"
cat "$irqdir/smp_affinity" >> "$OUT/nic_irq_affinity.txt" 2>&1
cat "$irqdir/smp_affinity_list" >> "$OUT/nic_irq_affinity.txt" 2>&1
fi
done 2>/dev/null
fi
# === NEW: Kernel timers/delays ===
[ -r /proc/timer_list ] && head -100 /proc/timer_list > "$OUT/timer_list.txt" 2>&1
# === NEW: Softirq statistics ===
cat /proc/softirqs > "$OUT/softirqs.txt" 2>&1 || true
# === NEW: System uptime at collection moment ===
echo "Collection uptime: $(cat /proc/uptime)" > "$OUT/collection_timing.txt"
echo "System clock: $(date 2>/dev/null || echo 'unavailable')" >> "$OUT/collection_timing.txt"
# -----------------------------------------------------------------------------
# 8. Initramfs contents (if we can see /boot)
# -----------------------------------------------------------------------------
echo "Checking initramfs..."
ls -la /boot/init* /boot/vmlinuz* > "$OUT/boot_files.txt" 2>/dev/null || echo "no /boot access" > "$OUT/boot_files.txt"
# Detect initramfs compression via magic bytes
INITRD=""
KVER=$(uname -r)
for f in "/boot/initrd.img-$KVER" "/boot/initramfs-$KVER.img"; do
[ -r "$f" ] && INITRD="$f" && break
done
if [ -n "$INITRD" ]; then
echo "Found: $INITRD" > "$OUT/initrd_found.txt"
# Magic byte detection
MAGIC=$(dd if="$INITRD" bs=1 count=4 2>/dev/null | od -An -tx1 | tr -d ' ')
case "$MAGIC" in
1f8b*) echo "gzip" ;;
fd37*) echo "xz" ;;
28b5*) echo "zstd" ;;
0422*) echo "lz4" ;;
3037*) echo "cpio" ;;
*) echo "unknown:$MAGIC" ;;
esac > "$OUT/initrd_compression.txt"
# Hash if possible
if command -v md5sum >/dev/null 2>&1; then
md5sum "$INITRD" > "$OUT/initrd_md5.txt" 2>&1
fi
else
echo "no initrd found" > "$OUT/initrd_found.txt"
fi
# -----------------------------------------------------------------------------
# 9. Create file list manifest
# -----------------------------------------------------------------------------
echo "Creating manifest..."
ls -la "$OUT"/ > "$OUT/00_manifest.txt" 2>&1
# Checksums
if command -v md5sum >/dev/null 2>&1; then
(cd "$OUT" && md5sum * 2>/dev/null) > "$OUT/00_checksums.txt"
fi
# -----------------------------------------------------------------------------
# 10. Post-hang Repeated Collection (NEW SECTION)
# -----------------------------------------------------------------------------
# If the hang is happening, this captures state AFTER the hang started
echo "Capturing post-hang state..."
# Wait a brief moment then re-capture critical info
sleep 2 2>/dev/null || true
if command -v ethtool >/dev/null 2>&1 && [ -n "$IFACE" ]; then
ethtool -S "$IFACE" > "$OUT/ethtool_S_post.txt" 2>&1 || true
fi
cat /proc/interrupts > "$OUT/proc_interrupts_post.txt" 2>&1
dmesg | tail -100 > "$OUT/dmesg_tail_post.txt" 2>&1 || true
# === NEW: Capture multiple PHY/MAC status samples over time ===
echo "Sampling dmesg for Hardware Unit Hang patterns..."
dmesg | grep -A15 "Detected Hardware Unit Hang" | head -50 > "$OUT/hang_details.txt" 2>/dev/null || true
# -----------------------------------------------------------------------------
# 11. Diagnostic Commands Hints
# -----------------------------------------------------------------------------
cat > "$OUT/DIAGNOSTIC_HINTS.txt" << 'HINTS_EOF'
=== Manual Diagnostic Commands to Try ===
1. PHY Reset (try before hang occurs):
ethtool -s enp0s31f6 autoneg off speed 100 duplex full
sleep 2
ethtool -s enp0s31f6 autoneg on
2. Disable Flow Control:
ethtool -A enp0s31f6 rx off tx off autoneg off
3. Force PCI device reset:
echo 1 > /sys/bus/pci/devices/0000:00:1f.6/reset
4. Check if PHY registers are readable:
ethtool -d enp0s31f6
5. Try different autoneg settings:
ethtool -s enp0s31f6 autoneg on advertise 0x020
6. Try disabling EEE (Energy Efficient Ethernet):
ethtool --set-eee enp0s31f6 eee off
7. If lspci available, check device status:
lspci -vvv -s 00:1f.6
8. Reload module with debug:
rmmod e1000e
modprobe e1000e debug=16
HINTS_EOF
# -----------------------------------------------------------------------------
# Done
# -----------------------------------------------------------------------------
echo ""
echo "=== Collection Complete (Enhanced v2) ==="
echo "Output: $OUT"
echo "Files collected: $(ls -1 "$OUT" | wc -l)"
echo ""
echo "To copy to USB:"
echo " mount /dev/sda1 /mnt"
echo " cp -a $OUT /mnt/"
echo " sync && umount /mnt"
echo ""
echo "See $OUT/DIAGNOSTIC_HINTS.txt for manual commands to try"
echo ""
#!/bin/sh
# =============================================================================
# e1000e Data Collector Script
# Purpose: Collect comprehensive system/driver data for comparison between
# BusyBox/initramfs and full system environments
# =============================================================================
# =============================================================================
# DEPENDENCIES / TOOLS REQUIRED
# =============================================================================
#
# MINIMAL (Required for BusyBox/initramfs - include these in initramfs):
# - sh (BusyBox sh or dash)
# - cat, echo, test, mkdir, cp, ls, readlink
# - dd (for magic byte detection)
# - od (for hex output)
# - cut, tr, basename, dirname
# - find (BusyBox find)
# - sort (BusyBox sort)
# - grep (BusyBox grep)
# - head, tail
# - date (for timestamp)
# - sync
# - mount, umount (if saving to external media)
#
# OPTIONAL (Enhanced data collection - nice to have):
# - md5sum or sha256sum (checksums)
# - ip (iproute2 - network state)
# - ethtool (NIC diagnostics)
# - dmesg (kernel ring buffer)
# - lspci (PCI device info)
# - modinfo (module information)
# - lsmod (formatted module list)
# - zcat/xzcat/zstdcat (config.gz extraction)
# - file (magic detection)
# - cpio (initramfs extraction)
#
# FULL SYSTEM ONLY:
# - journalctl (systemd logs)
# - lsinitramfs / unmkinitramfs (initramfs inspection)
# - update-initramfs / dracut / mkinitcpio (rebuild)
#
# =============================================================================
set -e
# =============================================================================
# CONFIGURATION
# =============================================================================
# Target PCI device (e1000e NIC)
PCI_DEVICE="0000:00:1f.6"
# Network interface name (adjust if different)
NET_IFACE="${NET_IFACE:-eth0}"
# Output directory (override with environment variable or first argument)
OUT_DIR="${1:-${OUT_DIR:-/tmp/e1000e-manifest-$(date +%Y%m%d-%H%M%S)}}"
# =============================================================================
# HELPER FUNCTIONS
# =============================================================================
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
log_section() {
echo ""
echo "=============================================="
echo " $*"
echo "=============================================="
}
# Check if command exists
cmd_exists() {
command -v "$1" >/dev/null 2>&1
}
# Safe cat with error handling
safe_cat() {
cat "$1" 2>/dev/null || echo "[ERROR: Cannot read $1]"
}
# Safe read of sysfs/procfs value
sysfs_read() {
local path="$1"
local name="$2"
if [ -e "$path" ]; then
echo "${name}=$(cat "$path" 2>/dev/null || echo '[unreadable]')"
else
echo "${name}=[not present]"
fi
}
# Detect environment type
detect_environment() {
local pid1_comm=""
if [ -r /proc/1/comm ]; then
pid1_comm=$(cat /proc/1/comm 2>/dev/null)
fi
case "$pid1_comm" in
systemd)
echo "systemd"
;;
init)
# Could be sysvinit or busybox init
if [ -x /bin/busybox ] || grep -q "BusyBox" /proc/1/cmdline 2>/dev/null; then
echo "busybox"
else
echo "sysvinit"
fi
;;
sh|ash|bash|dash)
echo "initramfs-shell"
;;
*)
if [ -x /bin/busybox ]; then
echo "busybox"
elif [ -d /run/systemd/system ]; then
echo "systemd"
else
echo "unknown"
fi
;;
esac
}
# =============================================================================
# DATA COLLECTION FUNCTIONS
# =============================================================================
collect_boot_identity() {
log_section "Boot Identity"
local section_dir="$OUT_DIR/01_boot_identity"
mkdir -p "$section_dir"
# Kernel version
log "Collecting kernel version..."
uname -a > "$section_dir/uname_a.txt" 2>&1
uname -r > "$section_dir/uname_r.txt" 2>&1
# Kernel command line (authoritative boot args)
log "Collecting kernel command line..."
safe_cat /proc/cmdline > "$section_dir/proc_cmdline.txt"
# UEFI vs BIOS detection
log "Detecting firmware mode..."
if [ -d /sys/firmware/efi ]; then
echo "UEFI" > "$section_dir/firmware_mode.txt"
ls -la /sys/firmware/efi/ > "$section_dir/efi_vars_ls.txt" 2>&1 || true
else
echo "BIOS/Legacy" > "$section_dir/firmware_mode.txt"
fi
# PID 1 info (what's running the system)
log "Collecting PID 1 info..."
safe_cat /proc/1/comm > "$section_dir/pid1_comm.txt"
tr '\0' ' ' < /proc/1/cmdline > "$section_dir/pid1_cmdline.txt" 2>/dev/null || echo "[unreadable]" > "$section_dir/pid1_cmdline.txt"
# Environment detection
detect_environment > "$section_dir/environment_type.txt"
# Secure Boot / Module signature enforcement
log "Checking secure boot / module signing..."
{
echo "=== Kernel cmdline secure boot indicators ==="
grep -oE '(module\.sig_enforce=[^ ]*|efi=[^ ]*|secureboot[^ ]*)' /proc/cmdline 2>/dev/null || echo "[none found]"
echo ""
echo "=== Module signature enforce sysctl ==="
sysfs_read /proc/sys/kernel/module_sig_enforce "module_sig_enforce"
echo ""
echo "=== Secure boot EFI variable ==="
if [ -r /sys/firmware/efi/efivars/SecureBoot-* ]; then
echo "SecureBoot EFI var present"
ls -la /sys/firmware/efi/efivars/SecureBoot-* 2>/dev/null || true
else
echo "SecureBoot EFI var not found"
fi
} > "$section_dir/secure_boot_info.txt" 2>&1
}
collect_kernel_config() {
log_section "Kernel Configuration"
local section_dir="$OUT_DIR/02_kernel_config"
mkdir -p "$section_dir"
log "Collecting kernel config..."
# Try /proc/config.gz first (if CONFIG_IKCONFIG_PROC=y)
if [ -r /proc/config.gz ]; then
log " Found /proc/config.gz"
if cmd_exists zcat; then
zcat /proc/config.gz > "$section_dir/kernel_config.txt" 2>&1
elif cmd_exists gunzip; then
gunzip -c /proc/config.gz > "$section_dir/kernel_config.txt" 2>&1
else
cp /proc/config.gz "$section_dir/config.gz"
echo "[config.gz copied, no zcat available to extract]" > "$section_dir/kernel_config.txt"
fi
# Try /boot/config-<version>
elif [ -r "/boot/config-$(uname -r)" ]; then
log " Found /boot/config-$(uname -r)"
cp "/boot/config-$(uname -r)" "$section_dir/kernel_config.txt"
else
echo "[Kernel config not available]" > "$section_dir/kernel_config.txt"
fi
# Extract e1000e-specific config if available
if [ -s "$section_dir/kernel_config.txt" ] && [ "$(head -c 1 "$section_dir/kernel_config.txt")" != "[" ]; then
log " Extracting e1000e config options..."
grep -E '^CONFIG_(E1000|E1000E|INTEL)' "$section_dir/kernel_config.txt" > "$section_dir/e1000e_config.txt" 2>/dev/null || echo "[no e1000e config found]" > "$section_dir/e1000e_config.txt"
# Module vs built-in determination
grep -E '^CONFIG_E1000E=' "$section_dir/kernel_config.txt" > "$section_dir/e1000e_builtin_or_module.txt" 2>/dev/null || echo "[CONFIG_E1000E not found]" > "$section_dir/e1000e_builtin_or_module.txt"
fi
}
collect_modules_info() {
log_section "Modules Information"
local section_dir="$OUT_DIR/03_modules"
mkdir -p "$section_dir"
# Loaded modules
log "Collecting loaded modules..."
safe_cat /proc/modules > "$section_dir/proc_modules.txt"
if cmd_exists lsmod; then
lsmod > "$section_dir/lsmod.txt" 2>&1
fi
# All modules in /sys/module
log "Listing /sys/module entries..."
ls -1 /sys/module > "$section_dir/sys_module_list.txt" 2>&1
# e1000e specific module info
log "Collecting e1000e module parameters..."
if [ -d /sys/module/e1000e ]; then
echo "e1000e module is loaded" > "$section_dir/e1000e_status.txt"
# Parameters
if [ -d /sys/module/e1000e/parameters ]; then
mkdir -p "$section_dir/e1000e_parameters"
for param in /sys/module/e1000e/parameters/*; do
if [ -r "$param" ]; then
pname=$(basename "$param")
echo "${pname}=$(cat "$param" 2>/dev/null)" >> "$section_dir/e1000e_params.txt"
cat "$param" > "$section_dir/e1000e_parameters/${pname}" 2>/dev/null
fi
done
else
echo "[no parameters directory]" > "$section_dir/e1000e_params.txt"
fi
# Module refcount and holders
sysfs_read /sys/module/e1000e/refcnt "refcnt" >> "$section_dir/e1000e_status.txt"
if [ -d /sys/module/e1000e/holders ]; then
echo "holders: $(ls /sys/module/e1000e/holders 2>/dev/null | tr '\n' ' ')" >> "$section_dir/e1000e_status.txt"
fi
else
echo "e1000e module is NOT loaded (may be built-in or not present)" > "$section_dir/e1000e_status.txt"
echo "[module not loaded]" > "$section_dir/e1000e_params.txt"
fi
# modinfo if available
if cmd_exists modinfo; then
log "Collecting modinfo for e1000e..."
modinfo e1000e > "$section_dir/modinfo_e1000e.txt" 2>&1 || echo "[modinfo failed]" > "$section_dir/modinfo_e1000e.txt"
fi
# modprobe configuration
log "Collecting modprobe configuration..."
{
echo "=== /etc/modprobe.d/ ==="
if [ -d /etc/modprobe.d ]; then
ls -la /etc/modprobe.d/ 2>/dev/null
echo ""
for f in /etc/modprobe.d/*.conf; do
if [ -r "$f" ]; then
echo "--- $f ---"
cat "$f"
echo ""
fi
done
else
echo "[directory not present]"
fi
echo ""
echo "=== /lib/modprobe.d/ ==="
if [ -d /lib/modprobe.d ]; then
ls -la /lib/modprobe.d/ 2>/dev/null
for f in /lib/modprobe.d/*.conf; do
if [ -r "$f" ]; then
echo "--- $f ---"
cat "$f"
echo ""
fi
done
else
echo "[directory not present]"
fi
echo ""
echo "=== /usr/lib/modprobe.d/ ==="
if [ -d /usr/lib/modprobe.d ]; then
ls -la /usr/lib/modprobe.d/ 2>/dev/null
else
echo "[directory not present]"
fi
} > "$section_dir/modprobe_config.txt" 2>&1
# e1000e specific modprobe options
log "Extracting e1000e modprobe options..."
grep -r -h "e1000e" /etc/modprobe.d/ /lib/modprobe.d/ /usr/lib/modprobe.d/ 2>/dev/null | grep -v "^#" > "$section_dir/e1000e_modprobe_opts.txt" || echo "[no e1000e options found]" > "$section_dir/e1000e_modprobe_opts.txt"
}
collect_pci_info() {
log_section "PCI Device Information"
local section_dir="$OUT_DIR/04_pci"
mkdir -p "$section_dir"
local pci_path="/sys/bus/pci/devices/${PCI_DEVICE}"
log "Collecting PCI device info for ${PCI_DEVICE}..."
if [ -d "$pci_path" ]; then
echo "PCI device ${PCI_DEVICE} exists" > "$section_dir/device_status.txt"
# Basic device info
{
sysfs_read "$pci_path/vendor" "vendor"
sysfs_read "$pci_path/device" "device"
sysfs_read "$pci_path/subsystem_vendor" "subsystem_vendor"
sysfs_read "$pci_path/subsystem_device" "subsystem_device"
sysfs_read "$pci_path/class" "class"
sysfs_read "$pci_path/revision" "revision"
sysfs_read "$pci_path/irq" "irq"
sysfs_read "$pci_path/numa_node" "numa_node"
sysfs_read "$pci_path/enable" "enable"
sysfs_read "$pci_path/broken_parity_status" "broken_parity_status"
sysfs_read "$pci_path/msi_bus" "msi_bus"
sysfs_read "$pci_path/d3cold_allowed" "d3cold_allowed"
} > "$section_dir/pci_device_attrs.txt"
# Driver binding
log " Checking driver binding..."
if [ -L "$pci_path/driver" ]; then
readlink "$pci_path/driver" > "$section_dir/driver_link.txt" 2>&1
basename "$(readlink "$pci_path/driver")" > "$section_dir/bound_driver.txt" 2>&1
else
echo "[no driver bound]" > "$section_dir/driver_link.txt"
echo "[no driver bound]" > "$section_dir/bound_driver.txt"
fi
# Power management
log " Collecting power management state..."
if [ -d "$pci_path/power" ]; then
{
echo "=== Power directory listing ==="
ls -la "$pci_path/power/"
echo ""
echo "=== Power attributes ==="
for attr in control runtime_status runtime_suspended_time runtime_active_time autosuspend_delay_ms; do
sysfs_read "$pci_path/power/$attr" "$attr"
done
} > "$section_dir/power_state.txt" 2>&1
else
echo "[power directory not present]" > "$section_dir/power_state.txt"
fi
# Resource info
log " Collecting resource info..."
safe_cat "$pci_path/resource" > "$section_dir/resource.txt"
# MSI/MSI-X info
if [ -d "$pci_path/msi_irqs" ]; then
ls -la "$pci_path/msi_irqs/" > "$section_dir/msi_irqs.txt" 2>&1
else
echo "[no msi_irqs directory]" > "$section_dir/msi_irqs.txt"
fi
# Full sysfs dump of the device
ls -la "$pci_path/" > "$section_dir/sysfs_ls.txt" 2>&1
else
echo "PCI device ${PCI_DEVICE} NOT FOUND" > "$section_dir/device_status.txt"
log " WARNING: PCI device ${PCI_DEVICE} not found!"
fi
# lspci if available
if cmd_exists lspci; then
log "Collecting lspci output..."
lspci -v > "$section_dir/lspci_v.txt" 2>&1
lspci -vvv -s "$PCI_DEVICE" > "$section_dir/lspci_vvv_device.txt" 2>&1 || true
lspci -nn > "$section_dir/lspci_nn.txt" 2>&1
fi
# All network-class PCI devices
log "Listing all PCI devices..."
ls -la /sys/bus/pci/devices/ > "$section_dir/all_pci_devices.txt" 2>&1
}
collect_interrupt_info() {
log_section "Interrupt Information"
local section_dir="$OUT_DIR/05_interrupts"
mkdir -p "$section_dir"
log "Collecting interrupt info..."
safe_cat /proc/interrupts > "$section_dir/proc_interrupts.txt"
# Filter for e1000e/eth related interrupts
grep -E "(e1000e|eth|enp|eno)" /proc/interrupts > "$section_dir/nic_interrupts.txt" 2>/dev/null || echo "[no NIC interrupts found]" > "$section_dir/nic_interrupts.txt"
# IRQ info
if [ -d /proc/irq ]; then
ls -la /proc/irq/ > "$section_dir/proc_irq_ls.txt" 2>&1
fi
# Softirqs
safe_cat /proc/softirqs > "$section_dir/proc_softirqs.txt"
}
collect_network_info() {
log_section "Network Information"
local section_dir="$OUT_DIR/06_network"
mkdir -p "$section_dir"
# Detect actual interface name
log "Detecting network interfaces..."
ls -la /sys/class/net/ > "$section_dir/net_interfaces_sysfs.txt" 2>&1
# Try to find e1000e interface
local actual_iface="$NET_IFACE"
if [ -d "/sys/class/net/${NET_IFACE}/device" ]; then
actual_iface="$NET_IFACE"
else
# Try common names
for iface in eth0 enp0s31f6 eno1 eno2 enp0s25; do
if [ -d "/sys/class/net/${iface}/device" ]; then
# Check if it's our PCI device
if [ "$(readlink /sys/class/net/${iface}/device 2>/dev/null | grep -o '[0-9a-f:.]*$')" = "$PCI_DEVICE" ]; then
actual_iface="$iface"
break
fi
fi
done
fi
echo "$actual_iface" > "$section_dir/detected_interface.txt"
log " Using interface: $actual_iface"
# ip commands if available
if cmd_exists ip; then
log "Collecting ip command output..."
ip link show > "$section_dir/ip_link.txt" 2>&1
ip addr show > "$section_dir/ip_addr.txt" 2>&1
ip -s link show > "$section_dir/ip_s_link.txt" 2>&1
ip route show > "$section_dir/ip_route.txt" 2>&1
ip -d link show "$actual_iface" > "$section_dir/ip_d_link_iface.txt" 2>&1 || true
fi
# Interface sysfs info
local net_sysfs="/sys/class/net/${actual_iface}"
if [ -d "$net_sysfs" ]; then
log "Collecting interface sysfs info..."
{
sysfs_read "$net_sysfs/operstate" "operstate"
sysfs_read "$net_sysfs/carrier" "carrier"
sysfs_read "$net_sysfs/speed" "speed"
sysfs_read "$net_sysfs/duplex" "duplex"
sysfs_read "$net_sysfs/mtu" "mtu"
sysfs_read "$net_sysfs/flags" "flags"
sysfs_read "$net_sysfs/tx_queue_len" "tx_queue_len"
sysfs_read "$net_sysfs/addr_len" "addr_len"
sysfs_read "$net_sysfs/address" "address"
sysfs_read "$net_sysfs/broadcast" "broadcast"
} > "$section_dir/interface_sysfs.txt"
# Device link
readlink "$net_sysfs/device" > "$section_dir/interface_pci_device.txt" 2>&1 || echo "[no device link]" > "$section_dir/interface_pci_device.txt"
# Statistics
if [ -d "$net_sysfs/statistics" ]; then
for stat in "$net_sysfs/statistics"/*; do
if [ -r "$stat" ]; then
echo "$(basename "$stat")=$(cat "$stat")"
fi
done > "$section_dir/interface_statistics.txt" 2>&1
fi
else
echo "[interface $actual_iface not found in sysfs]" > "$section_dir/interface_sysfs.txt"
fi
# ethtool if available
if cmd_exists ethtool; then
log "Collecting ethtool output..."
ethtool "$actual_iface" > "$section_dir/ethtool.txt" 2>&1 || true
ethtool -i "$actual_iface" > "$section_dir/ethtool_i.txt" 2>&1 || true
ethtool -k "$actual_iface" > "$section_dir/ethtool_k.txt" 2>&1 || true
ethtool -S "$actual_iface" > "$section_dir/ethtool_S.txt" 2>&1 || true
ethtool -c "$actual_iface" > "$section_dir/ethtool_c.txt" 2>&1 || true
ethtool -g "$actual_iface" > "$section_dir/ethtool_g.txt" 2>&1 || true
ethtool -a "$actual_iface" > "$section_dir/ethtool_a.txt" 2>&1 || true
ethtool -T "$actual_iface" > "$section_dir/ethtool_T.txt" 2>&1 || true
ethtool --show-eee "$actual_iface" > "$section_dir/ethtool_eee.txt" 2>&1 || true
ethtool -d "$actual_iface" > "$section_dir/ethtool_d.txt" 2>&1 || true
ethtool -e "$actual_iface" > "$section_dir/ethtool_e.txt" 2>&1 || true
fi
# /proc/net info
log "Collecting /proc/net info..."
safe_cat /proc/net/dev > "$section_dir/proc_net_dev.txt"
safe_cat /proc/net/dev_mcast > "$section_dir/proc_net_dev_mcast.txt"
}
collect_dmesg() {
log_section "Kernel Messages (dmesg)"
local section_dir="$OUT_DIR/07_dmesg"
mkdir -p "$section_dir"
if cmd_exists dmesg; then
log "Collecting dmesg..."
dmesg > "$section_dir/dmesg_full.txt" 2>&1
# Timestamps if available
dmesg -T > "$section_dir/dmesg_T.txt" 2>&1 || true
# Filter for relevant messages
log "Extracting e1000e-related messages..."
grep -iE "(e1000e|e1000|intel.*ethernet)" "$section_dir/dmesg_full.txt" > "$section_dir/dmesg_e1000e.txt" 2>/dev/null || echo "[no e1000e messages]" > "$section_dir/dmesg_e1000e.txt"
log "Extracting network-related messages..."
grep -iE "(eth|enp|eno|link|carrier|NIC|network)" "$section_dir/dmesg_full.txt" > "$section_dir/dmesg_network.txt" 2>/dev/null || true
log "Extracting PCI-related messages..."
grep -iE "(pci|00:1f)" "$section_dir/dmesg_full.txt" > "$section_dir/dmesg_pci.txt" 2>/dev/null || true
log "Extracting error/warning messages..."
grep -iE "(error|fail|warn|timeout|reset|hang|watchdog)" "$section_dir/dmesg_full.txt" > "$section_dir/dmesg_errors.txt" 2>/dev/null || true
# Last 500 lines
tail -n 500 "$section_dir/dmesg_full.txt" > "$section_dir/dmesg_tail500.txt"
else
# Fallback: try /dev/kmsg if readable
if [ -r /dev/kmsg ]; then
log "dmesg not available, trying /dev/kmsg..."
timeout 1 cat /dev/kmsg > "$section_dir/kmsg.txt" 2>&1 || true
else
echo "[dmesg not available]" > "$section_dir/dmesg_full.txt"
fi
fi
# Kernel log levels
safe_cat /proc/sys/kernel/printk > "$section_dir/printk_levels.txt"
}
collect_journalctl() {
log_section "Systemd Journal (if available)"
local section_dir="$OUT_DIR/08_journal"
mkdir -p "$section_dir"
if cmd_exists journalctl; then
log "Collecting journalctl output..."
journalctl -b --no-pager > "$section_dir/journalctl_b.txt" 2>&1 || true
journalctl -b -k --no-pager > "$section_dir/journalctl_b_k.txt" 2>&1 || true
journalctl -b -p err --no-pager > "$section_dir/journalctl_b_err.txt" 2>&1 || true
# e1000e specific
journalctl -b --no-pager | grep -iE "e1000e" > "$section_dir/journalctl_e1000e.txt" 2>/dev/null || true
else
echo "[journalctl not available - likely BusyBox/initramfs environment]" > "$section_dir/journalctl_status.txt"
fi
}
collect_initramfs_info() {
log_section "Initramfs Information"
local section_dir="$OUT_DIR/09_initramfs"
mkdir -p "$section_dir"
# Identify which initramfs framework
log "Detecting initramfs framework..."
{
echo "=== Framework Detection ==="
if [ -d /usr/share/initramfs-tools ]; then
echo "initramfs-tools detected"
echo " /usr/share/initramfs-tools exists"
fi
if [ -d /usr/lib/dracut ]; then
echo "dracut detected"
echo " /usr/lib/dracut exists"
fi
if [ -f /etc/mkinitcpio.conf ]; then
echo "mkinitcpio detected"
echo " /etc/mkinitcpio.conf exists"
fi
} > "$section_dir/framework_detection.txt"
# List initramfs files
log "Listing initramfs images..."
{
echo "=== /boot contents ==="
ls -la /boot/initrd* /boot/initramfs* 2>/dev/null || echo "[no initrd/initramfs found in /boot]"
echo ""
echo "=== Current kernel initramfs ==="
local kver=$(uname -r)
for f in "/boot/initrd.img-${kver}" "/boot/initramfs-${kver}.img" "/boot/initrd-${kver}" "/boot/initramfs-linux.img"; do
if [ -e "$f" ]; then
echo "Found: $f"
ls -la "$f"
fi
done
} > "$section_dir/initramfs_files.txt" 2>&1
# Hash initramfs if found
log "Computing initramfs checksums..."
local kver=$(uname -r)
local initrd=""
for f in "/boot/initrd.img-${kver}" "/boot/initramfs-${kver}.img" "/boot/initrd-${kver}"; do
if [ -e "$f" ]; then
initrd="$f"
break
fi
done
if [ -n "$initrd" ] && [ -e "$initrd" ]; then
if cmd_exists sha256sum; then
sha256sum "$initrd" > "$section_dir/initramfs_sha256.txt" 2>&1
elif cmd_exists md5sum; then
md5sum "$initrd" > "$section_dir/initramfs_md5.txt" 2>&1
fi
# Detect compression type
log "Detecting initramfs compression..."
if cmd_exists file; then
file "$initrd" > "$section_dir/initramfs_type.txt" 2>&1
else
# Manual magic detection
local magic=$(dd if="$initrd" bs=1 count=6 2>/dev/null | od -An -tx1 | tr -d ' \n')
case "$magic" in
1f8b*) echo "gzip compressed" ;;
fd377a585a00*) echo "xz compressed" ;;
28b52ffd*) echo "zstd compressed" ;;
04224d18*) echo "lz4 compressed" ;;
303730*) echo "cpio archive (uncompressed or multi-segment)" ;;
*) echo "unknown (magic: $magic)" ;;
esac > "$section_dir/initramfs_type.txt"
fi
else
echo "[no initramfs found for kernel $kver]" > "$section_dir/initramfs_type.txt"
fi
# lsinitramfs if available
if cmd_exists lsinitramfs && [ -n "$initrd" ] && [ -e "$initrd" ]; then
log "Listing initramfs contents..."
lsinitramfs "$initrd" > "$section_dir/lsinitramfs_full.txt" 2>&1 || true
# Filter for interesting items
grep -E "(e1000|modprobe|modules)" "$section_dir/lsinitramfs_full.txt" > "$section_dir/lsinitramfs_modules.txt" 2>/dev/null || true
grep -E "\.conf$" "$section_dir/lsinitramfs_full.txt" > "$section_dir/lsinitramfs_configs.txt" 2>/dev/null || true
fi
# initramfs-tools config
if [ -d /etc/initramfs-tools ]; then
log "Collecting initramfs-tools config..."
mkdir -p "$section_dir/initramfs-tools"
cp /etc/initramfs-tools/initramfs.conf "$section_dir/initramfs-tools/" 2>/dev/null || true
cp /etc/initramfs-tools/modules "$section_dir/initramfs-tools/" 2>/dev/null || true
ls -la /etc/initramfs-tools/ > "$section_dir/initramfs-tools/ls.txt" 2>&1
fi
# dracut config
if [ -d /etc/dracut.conf.d ] || [ -f /etc/dracut.conf ]; then
log "Collecting dracut config..."
mkdir -p "$section_dir/dracut"
cp /etc/dracut.conf "$section_dir/dracut/" 2>/dev/null || true
if [ -d /etc/dracut.conf.d ]; then
cp -r /etc/dracut.conf.d "$section_dir/dracut/" 2>/dev/null || true
fi
fi
}
collect_process_info() {
log_section "Process Information"
local section_dir="$OUT_DIR/10_processes"
mkdir -p "$section_dir"
log "Collecting process list..."
if cmd_exists ps; then
ps aux > "$section_dir/ps_aux.txt" 2>&1 || ps > "$section_dir/ps.txt" 2>&1 || true
fi
# Mount points
log "Collecting mount points..."
cat /proc/mounts > "$section_dir/proc_mounts.txt" 2>&1
if cmd_exists mount; then
mount > "$section_dir/mount.txt" 2>&1
fi
# Memory info
safe_cat /proc/meminfo > "$section_dir/meminfo.txt"
# CPU info
safe_cat /proc/cpuinfo > "$section_dir/cpuinfo.txt"
}
collect_system_info() {
log_section "Additional System Information"
local section_dir="$OUT_DIR/11_system"
mkdir -p "$section_dir"
# DMI/SMBIOS info
log "Collecting DMI info..."
if [ -d /sys/class/dmi/id ]; then
{
for f in /sys/class/dmi/id/*; do
if [ -r "$f" ] && [ ! -d "$f" ]; then
echo "$(basename "$f")=$(cat "$f" 2>/dev/null || echo '[unreadable]')"
fi
done
} > "$section_dir/dmi_info.txt" 2>&1
fi
# Boot time
safe_cat /proc/uptime > "$section_dir/uptime.txt"
# ACPI info (power management related)
if [ -d /sys/firmware/acpi ]; then
ls -la /sys/firmware/acpi/ > "$section_dir/acpi_ls.txt" 2>&1
fi
# /proc/version
safe_cat /proc/version > "$section_dir/proc_version.txt"
# Available kernel modules in /lib/modules
local kver=$(uname -r)
if [ -d "/lib/modules/${kver}" ]; then
log "Listing available kernel modules..."
find "/lib/modules/${kver}" -name "*.ko*" 2>/dev/null | head -n 1000 > "$section_dir/available_modules.txt"
# Check for e1000e specifically
find "/lib/modules/${kver}" -name "e1000e*" > "$section_dir/e1000e_module_path.txt" 2>/dev/null || echo "[not found]" > "$section_dir/e1000e_module_path.txt"
fi
}
generate_manifest() {
log_section "Generating Manifest"
local manifest="$OUT_DIR/MANIFEST.txt"
{
echo "========================================"
echo " E1000E Data Collection Manifest"
echo "========================================"
echo ""
echo "Collection timestamp: $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "Kernel version: $(uname -r)"
echo "Environment type: $(detect_environment)"
echo "PCI device: ${PCI_DEVICE}"
echo "Network interface: ${NET_IFACE}"
echo "Output directory: ${OUT_DIR}"
echo ""
echo "========================================"
echo " Available Tools at Collection Time"
echo "========================================"
for cmd in sh cat ls find grep sort dmesg ip ethtool lspci lsmod modinfo journalctl lsinitramfs file sha256sum md5sum zcat; do
if cmd_exists "$cmd"; then
echo " [X] $cmd"
else
echo " [ ] $cmd"
fi
done
echo ""
echo "========================================"
echo " Collected Files"
echo "========================================"
find "$OUT_DIR" -type f | sort | while read -r f; do
echo " $f"
done
} > "$manifest"
# Create checksums for all collected files
log "Creating checksums..."
if cmd_exists sha256sum; then
find "$OUT_DIR" -type f ! -name "CHECKSUMS.txt" -exec sha256sum {} \; > "$OUT_DIR/CHECKSUMS.txt" 2>/dev/null || true
elif cmd_exists md5sum; then
find "$OUT_DIR" -type f ! -name "CHECKSUMS.txt" -exec md5sum {} \; > "$OUT_DIR/CHECKSUMS.txt" 2>/dev/null || true
fi
}
# =============================================================================
# MAIN EXECUTION
# =============================================================================
main() {
echo "========================================"
echo " E1000E Data Collector"
echo " Output: ${OUT_DIR}"
echo "========================================"
echo ""
mkdir -p "$OUT_DIR"
# Detect and log environment
ENV_TYPE=$(detect_environment)
log "Detected environment: $ENV_TYPE"
echo "$ENV_TYPE" > "$OUT_DIR/environment_type.txt"
# Run all collection functions
collect_boot_identity
collect_kernel_config
collect_modules_info
collect_pci_info
collect_interrupt_info
collect_network_info
collect_dmesg
collect_journalctl
collect_initramfs_info
collect_process_info
collect_system_info
generate_manifest
log_section "Collection Complete"
echo ""
echo "Data collected to: $OUT_DIR"
echo ""
echo "To save to external media (USB/boot partition):"
echo " mount /dev/sdX1 /mnt"
echo " cp -a $OUT_DIR /mnt/"
echo " sync && umount /mnt"
echo ""
echo "For comparison, run this script on both:"
echo " 1. BusyBox/initramfs environment (boot with break=premount)"
echo " 2. Fully booted system"
echo ""
echo "Then diff the results:"
echo " diff -r /path/to/busybox-manifest /path/to/system-manifest"
echo ""
}
# Run main
main "$@"
#!/usr/bin/env bash
set -Eeuo pipefail
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
echo "Run as root: sudo $0 [-i iface] [-w seconds] [-o output_dir]" >&2
exit 1
fi
WAIT_SECS=10
IFACE=""
OUTDIR=""
usage() {
cat <<'EOF'
Usage: e1000e_ethtool_matrix_test.sh [-i iface] [-w seconds] [-o output_dir]
Tests all supported combinations of rx/tx/gso/tso using ethtool -K,
plus pause-flow-control rx/tx using ethtool -A.
After each apply it forces a link flap, waits N seconds, and captures
new dmesg output plus actual current NIC parameters.
EOF
}
while getopts ":i:w:o:h" opt; do
case "$opt" in
i) IFACE="$OPTARG" ;;
w) WAIT_SECS="$OPTARG" ;;
o) OUTDIR="$OPTARG" ;;
h)
usage
exit 0
;;
\?)
usage >&2
exit 2
;;
esac
done
detect_iface() {
local path iface
for path in /sys/class/net/*; do
iface="$(basename "$path")"
[[ "$iface" == "lo" ]] && continue
[[ -d "$path/wireless" ]] && continue
if ethtool -i "$iface" >/dev/null 2>&1; then
printf '%s\n' "$iface"
return 0
fi
done
return 1
}
IFACE="${IFACE:-$(detect_iface || true)}"
if [[ -z "$IFACE" ]]; then
echo "Could not auto-detect a wired interface. Use -i <iface>." >&2
exit 1
fi
if ! ethtool -k "$IFACE" >/dev/null 2>&1; then
echo "ethtool cannot query $IFACE" >&2
exit 1
fi
TS="$(date +%F-%H%M%S)"
OUTDIR="${OUTDIR:-ethtool-matrix-${IFACE}-${TS}}"
mkdir -p "$OUTDIR/cases"
MAIN_LOG="$OUTDIR/run.log"
SUMMARY_TSV="$OUTDIR/summary.tsv"
declare -a KNOBS=()
declare -a PAUSE_KNOBS=()
declare -A FEATURE_NAME=()
declare -A ORIGINAL_STATE=()
declare -A ORIGINAL_PAUSE_STATE=()
FEATURE_NAME[rx]="rx-checksumming"
FEATURE_NAME[tx]="tx-checksumming"
FEATURE_NAME[gso]="generic-segmentation-offload"
FEATURE_NAME[tso]="tcp-segmentation-offload"
log() {
printf '%s %s\n' "[$(date +%F' '%T)]" "$*" | tee -a "$MAIN_LOG"
}
current_feature_state() {
local feature="$1"
ethtool -k "$IFACE" | awk -F': ' -v f="$feature" '
$1 ~ "^[[:space:]]*" f "$" {
val=$2
sub(/ .*/, "", val)
print val
exit
}'
}
feature_mutable() {
local feature="$1"
local line
line="$(ethtool -k "$IFACE" | awk -F': ' -v f="$feature" '$1 ~ "^[[:space:]]*" f "$" { print $0; exit }')"
[[ -n "$line" ]] || return 1
[[ "$line" == *"[fixed]"* ]] && return 1
return 0
}
pause_state() {
local key="$1"
ethtool -a "$IFACE" | awk -F': ' -v k="$key" '
$1 ~ "^[[:space:]]*" k "$" {
val=$2
sub(/ .*/, "", val)
print val
exit
}'
}
pause_mutable() {
local key="$1"
local line
line="$(ethtool -a "$IFACE" | awk -F': ' -v k="$key" '$1 ~ "^[[:space:]]*" k "$" { print $0; exit }')"
[[ -n "$line" ]] || return 1
[[ "$line" == *"[fixed]"* ]] && return 1
return 0
}
collect_card_state() {
local outfile="$1"
{
echo "### ethtool -i $IFACE"
ethtool -i "$IFACE" || true
echo
echo "### ethtool $IFACE"
ethtool "$IFACE" || true
echo
echo "### ethtool -k $IFACE"
ethtool -k "$IFACE" || true
echo
echo "### ethtool -a $IFACE"
ethtool -a "$IFACE" || true
echo
echo "### ip -details link show $IFACE"
ip -details link show "$IFACE" || true
} >"$outfile" 2>&1
}
link_flap() {
ip link set "$IFACE" down >/dev/null 2>&1 || true
sleep 2
ip link set "$IFACE" up >/dev/null 2>&1 || true
sleep 2
}
for knob in rx tx gso tso; do
if feature_mutable "${FEATURE_NAME[$knob]}"; then
KNOBS+=("$knob")
ORIGINAL_STATE[$knob]="$(current_feature_state "${FEATURE_NAME[$knob]}")"
fi
done
for knob in rx tx; do
if pause_mutable "$knob"; then
PAUSE_KNOBS+=("$knob")
ORIGINAL_PAUSE_STATE[$knob]="$(pause_state "$knob")"
fi
done
if [[ ${#KNOBS[@]} -eq 0 && ${#PAUSE_KNOBS[@]} -eq 0 ]]; then
echo "No mutable offload or pause options found on $IFACE" >&2
exit 1
fi
restore_original() {
local args=()
local pargs=(autoneg off)
local knob
for knob in "${KNOBS[@]}"; do
args+=("$knob" "${ORIGINAL_STATE[$knob]}")
done
if [[ ${#args[@]} -gt 0 ]]; then
ethtool -K "$IFACE" "${args[@]}" >/dev/null 2>&1 || true
fi
for knob in "${PAUSE_KNOBS[@]}"; do
pargs+=("$knob" "${ORIGINAL_PAUSE_STATE[$knob]}")
done
if [[ ${#PAUSE_KNOBS[@]} -gt 0 ]]; then
ethtool -A "$IFACE" "${pargs[@]}" >/dev/null 2>&1 || true
fi
}
trap restore_original EXIT
{
printf 'case_id\tapply_status\thang_detected\t'
printf '%s\t' "${KNOBS[@]}"
for knob in "${PAUSE_KNOBS[@]}"; do
printf 'pause_%s\t' "$knob"
done
printf 'delta_log\n'
} >"$SUMMARY_TSV"
log "interface: $IFACE"
log "wait seconds: $WAIT_SECS"
log "output dir: $OUTDIR"
log "mutable knobs: ${KNOBS[*]}"
log "mutable pause knobs: ${PAUSE_KNOBS[*]:-<none>}"
collect_card_state "$OUTDIR/baseline-card-state.txt"
{
echo "### initial dmesg grep"
dmesg | grep -Ei 'e1000e|hardware unit hang|NETDEV WATCHDOG|tx_timeout|dma|iommu|dmar|pci status' || true
} >"$OUTDIR/baseline-dmesg.txt" 2>&1
total=$((1 << (${#KNOBS[@]} + ${#PAUSE_KNOBS[@]})))
log "testing $total combinations"
for ((mask = 0; mask < total; mask++)); do
case_id=$(printf 'case_%0*d' 3 ${mask})
before="$OUTDIR/cases/${case_id}.dmesg.before"
after="$OUTDIR/cases/${case_id}.dmesg.after"
delta="$OUTDIR/cases/${case_id}.dmesg.delta"
statefile="$OUTDIR/cases/${case_id}.ethtool-k.txt"
pausefile="$OUTDIR/cases/${case_id}.ethtool-a.txt"
infofile="$OUTDIR/cases/${case_id}.ethtool-i.txt"
linkfile="$OUTDIR/cases/${case_id}.ip-link.txt"
cardstate="$OUTDIR/cases/${case_id}.card-state.txt"
args=()
pargs=(autoneg off)
row=("$case_id")
for ((idx = 0; idx < ${#KNOBS[@]}; idx++)); do
knob="${KNOBS[$idx]}"
if (((mask >> idx) & 1)); then
state="on"
else
state="off"
fi
args+=("$knob" "$state")
row+=("$state")
done
base=${#KNOBS[@]}
for ((idx = 0; idx < ${#PAUSE_KNOBS[@]}; idx++)); do
knob="${PAUSE_KNOBS[$idx]}"
if (((mask >> (base + idx)) & 1)); then
state="on"
else
state="off"
fi
pargs+=("$knob" "$state")
row+=("$state")
done
log "${case_id}: applying offload=[${args[*]:-none}] pause=[${pargs[*]}]"
dmesg >"$before" 2>/dev/null || true
apply_status="ok"
if [[ ${#args[@]} -gt 0 ]]; then
if ! ethtool -K "$IFACE" "${args[@]}" >>"$MAIN_LOG" 2>&1; then
apply_status="failed"
fi
fi
if [[ "$apply_status" == "ok" && ${#PAUSE_KNOBS[@]} -gt 0 ]]; then
if ! ethtool -A "$IFACE" "${pargs[@]}" >>"$MAIN_LOG" 2>&1; then
apply_status="failed"
fi
fi
link_flap
sleep "$WAIT_SECS"
dmesg >"$after" 2>/dev/null || true
before_lines=$(wc -l <"$before")
sed -n "$((before_lines + 1)),\$p" "$after" >"$delta" || true
ethtool -k "$IFACE" >"$statefile" 2>&1 || true
ethtool -a "$IFACE" >"$pausefile" 2>&1 || true
ethtool -i "$IFACE" >"$infofile" 2>&1 || true
ip -details link show "$IFACE" >"$linkfile" 2>&1 || true
collect_card_state "$cardstate"
if grep -Eqi 'Detected Hardware Unit Hang|hardware unit hang|NETDEV WATCHDOG|tx_timeout|PCI Status' "$delta"; then
hang_detected="yes"
log "${case_id}: hang-related message detected"
else
hang_detected="no"
fi
{
printf '%s\t%s\t%s\t' "$case_id" "$apply_status" "$hang_detected"
printf '%s\t' "${row[@]:1}"
printf '%s\n' "$delta"
} >>"$SUMMARY_TSV"
done
log "restoring original settings"
restore_original
link_flap
collect_card_state "$OUTDIR/restored-card-state.txt"
log "done"
log "summary: $SUMMARY_TSV"
#!/bin/sh
# net_collect.sh - collect NIC and driver diagnostics in busybox/initramfs
OUT="${1:-/tmp/netdiag-$(date +%Y%m%d-%H%M%S).log}"
echo "==== NET DIAGNOSTIC DUMP $(date) ====" > "$OUT"
echo >> "$OUT"
echo "== uname -a ==" >> "$OUT"
uname -a >> "$OUT" 2>&1
echo >> "$OUT"
echo "== /proc/cmdline ==" >> "$OUT"
cat /proc/cmdline >> "$OUT" 2>&1
echo >> "$OUT"
echo "== Loaded modules ==" >> "$OUT"
# busybox lsmod is fine
lsmod >> "$OUT" 2>&1
echo >> "$OUT"
echo "== PCI info for Intel NICs (if any) ==" >> "$OUT"
if [ -d /sys/bus/pci/devices ]; then
for p in /sys/bus/pci/devices/*; do
[ -e "$p/vendor" ] || continue
vend=$(cat "$p/vendor" 2>/dev/null)
dev=$(cat "$p/device" 2>/dev/null)
# 0x8086 is Intel
if [ "$vend" = "0x8086" ]; then
echo "--- $p ---" >> "$OUT"
echo "vendor=$vend device=$dev" >> "$OUT"
[ -e "$p/class" ] && echo "class=$(cat "$p/class")" >> "$OUT"
[ -e "$p/subsystem_vendor" ] && echo "subsystem_vendor=$(cat "$p/subsystem_vendor")" >> "$OUT"
[ -e "$p/subsystem_device" ] && echo "subsystem_device=$(cat "$p/subsystem_device")" >> "$OUT"
fi
done
fi
echo >> "$OUT"
echo "== Network interfaces ==" >> "$OUT"
for n in /sys/class/net/*; do
IF=$(basename "$n")
echo "--- $IF ---" >> "$OUT"
if [ -e "$n/device/driver" ]; then
drv=$(readlink -f "$n/device/driver" 2>/dev/null | sed 's#.*/##')
echo "driver=$drv" >> "$OUT"
else
echo "driver=(none)" >> "$OUT"
fi
echo "ip link show $IF:" >> "$OUT"
ip link show "$IF" >> "$OUT" 2>&1
echo "ethtool $IF:" >> "$OUT"
ethtool "$IF" >> "$OUT" 2>&1
echo "ethtool -k $IF:" >> "$OUT"
ethtool -k "$IF" >> "$OUT" 2>&1
echo "ethtool -g $IF:" >> "$OUT"
ethtool -g "$IF" >> "$OUT" 2>&1
echo "ethtool -S $IF:" >> "$OUT"
ethtool -S "$IF" >> "$OUT" 2>&1
done
echo >> "$OUT"
echo "== e1000e module parameters (if present) ==" >> "$OUT"
if [ -d /sys/module/e1000e ]; then
for p in /sys/module/e1000e/parameters/*; do
name=$(basename "$p")
val=$(cat "$p" 2>/dev/null)
echo "$name=$val" >> "$OUT"
done
else
echo "e1000e module not loaded" >> "$OUT"
fi
echo >> "$OUT"
echo "== dmesg (last 200 lines) ==" >> "$OUT"
dmesg | tail -n 200 >> "$OUT" 2>&1
echo >> "$OUT"
echo "==== END ====" >> "$OUT"
echo "Written diagnostics to: $OUT"
#!/bin/sh
# net_fuzz_e1000e.sh - brute-test e1000e + ethtool combinations in busybox
LOG="${1:-/tmp/e1000e-fuzz-$(date +%Y%m%d-%H%M%S).log}"
SLEEP_SEC="${SLEEP_SEC:-8}" # time to wait after bringing link up
MAX_ITERS="${MAX_ITERS:-999999}" # safety cap if you tweak loops
echo "==== e1000e fuzz run $(date) ====" > "$LOG"
echo "LOG=$LOG" >> "$LOG"
echo >> "$LOG"
# Find all interfaces driven by e1000e
find_e1000e_ifs() {
for n in /sys/class/net/*; do
IF=$(basename "$n")
drv=$(readlink -f "$n"/device/driver 2>/dev/null | sed 's#.*/##')
[ "$drv" = "e1000e" ] && echo "$IF"
done
}
IFS_LIST=$(find_e1000e_ifs)
if [ -z "$IFS_LIST" ]; then
echo "No interfaces with driver e1000e found" | tee -a "$LOG"
exit 1
fi
echo "Interfaces on e1000e: $IFS_LIST" | tee -a "$LOG"
echo >> "$LOG"
# Helper: reload e1000e with options
reload_e1000e() {
INTMODE="$1"
ITR="$2"
echo "Reloading e1000e IntMode=$INTMODE ITR=$ITR" >> "$LOG"
# remove if loaded
if lsmod | grep -q '^e1000e'; then
rmmod e1000e 2>>"$LOG"
sleep 1
fi
# now load with given options
modprobe e1000e IntMode="$INTMODE" InterruptThrottleRate="$ITR" 2>>"$LOG"
RET=$?
if [ $RET -ne 0 ]; then
echo "modprobe failed: IntMode=$INTMODE ITR=$ITR ret=$RET" >> "$LOG"
return 1
fi
return 0
}
# Helper: configure one interface with ethtool + speed mode
config_if() {
IF="$1"
OFFLOAD_MODE="$2" # on/off
SPEED_MODE="$3" # auto/100
echo "Configuring $IF offload=$OFFLOAD_MODE speed=$SPEED_MODE" >> "$LOG"
# reset link down
ip link set "$IF" down 2>>"$LOG"
# speed
case "$SPEED_MODE" in
auto)
ethtool -s "$IF" autoneg on 2>>"$LOG"
;;
100)
# you might need matching config on switch side
ethtool -s "$IF" speed 100 duplex full autoneg off 2>>"$LOG"
;;
esac
# offloads
if [ "$OFFLOAD_MODE" = "off" ]; then
ethtool -K "$IF" tso off gso off gro off lro off rx off tx off 2>>"$LOG"
ethtool --set-eee "$IF" eee off 2>>"$LOG"
else
# try to restore defaults best-effort
ethtool -K "$IF" tso on gso on gro on rx on tx on 2>>"$LOG"
ethtool --set-eee "$IF" eee on 2>>"$LOG"
fi
# bring up
ip link set "$IF" up 2>>"$LOG"
}
# Helper: capture dmesg delta
snapshot_dmesg() {
dmesg > "$1"
}
dmesg_has_error() {
# grep for patterns that indicate trouble
dmesg_diff="$1"
grep -E "e1000e.*Hardware Unit Hang|NETDEV WATCHDOG|TX Unit Hang|Detected Hardware Unit Hang" "$dmesg_diff" >/dev/null 2>&1
}
# main test loop
ITER=0
for INTMODE in 0 1 2; do
for ITR in 0 3000 1; do
# reload driver for this pair
reload_e1000e "$INTMODE" "$ITR" || continue
# after reloading, recompute interface list (names may change in theory)
IFS_LIST=$(find_e1000e_ifs)
for OFFLOAD_MODE in off on; do
for SPEED_MODE in auto 100; do
ITER=$((ITER+1))
[ $ITER -gt $MAX_ITERS ] && echo "Reached MAX_ITERS=$MAX_ITERS, stopping" >> "$LOG" && exit 0
echo "---- TEST #$ITER ----" >> "$LOG"
echo "IntMode=$INTMODE ITR=$ITR offload=$OFFLOAD_MODE speed=$SPEED_MODE" >> "$LOG"
# snapshot dmesg before
BEFORE="/tmp/dmesg_before_$$"
AFTER="/tmp/dmesg_after_$$"
DIFF="/tmp/dmesg_diff_$$"
snapshot_dmesg "$BEFORE"
# apply config to each e1000e interface
for IF in $IFS_LIST; do
echo "Testing interface $IF" >> "$LOG"
config_if "$IF" "$OFFLOAD_MODE" "$SPEED_MODE"
# Optional: you can add a simple ping here if you know a target
# ping -c 2 -w 3 <gateway_or_host> >/dev/null 2>&1
done
# wait for potential hang/watchdog
sleep "$SLEEP_SEC"
snapshot_dmesg "$AFTER"
# busybox 'diff' might not exist; use a crude way:
# cut new messages from AFTER that are not in BEFORE by tailing
# simplest: just 'tail' and search for patterns
tail -n 100 "$AFTER" > "$DIFF"
if dmesg_has_error "$DIFF"; then
echo "RESULT: PROBLEM DETECTED" >> "$LOG"
echo "Relevant dmesg lines:" >> "$LOG"
grep -E "e1000e.*Hardware Unit Hang|NETDEV WATCHDOG|TX Unit Hang|Detected Hardware Unit Hang" "$DIFF" >> "$LOG"
else
echo "RESULT: OK (no hang/watchdog in last $SLEEP_SEC s)" >> "$LOG"
fi
echo >> "$LOG"
rm -f "$BEFORE" "$AFTER" "$DIFF"
done
done
done
done
echo "==== DONE $(date) ====" >> "$LOG"
echo "Fuzz log written to: $LOG"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment