Skip to content

Instantly share code, notes, and snippets.

@slideep
Created February 25, 2026 10:04
Show Gist options
  • Select an option

  • Save slideep/7aa14e01e5e5a4cd7b099e5ee45138d3 to your computer and use it in GitHub Desktop.

Select an option

Save slideep/7aa14e01e5e5a4cd7b099e5ee45138d3 to your computer and use it in GitHub Desktop.

Flashing NVIDIA Jetson Orin Nano (reComputer J3011) from Windows Using WSL2

A complete technical guide to flashing JetPack 6.1 on a Seeed Studio reComputer J3011 using only Windows + WSL2 — no native Linux machine required.


Table of Contents

  1. Overview
  2. Hardware & Software Inventory
  3. Architecture Overview
  4. Phase 1: Environment Setup
  5. Phase 2: Download & Prepare L4T BSP
  6. Phase 3: Recovery Mode & USB Passthrough
  7. Phase 4: Flash Attempts & Troubleshooting
  8. Phase 5: Building a Custom WSL2 Kernel with RNDIS Support
  9. Phase 6: Successful Flash
  10. Post-Flash Verification
  11. Troubleshooting Reference
  12. Quick Reference: Reflash Procedure
  13. Key Takeaways

Overview

The Challenge

NVIDIA's official Jetson flashing tools (l4t_initrd_flash.sh) are designed for native Linux hosts. The toolchain uses USB recovery mode (RCM), boots a Linux initrd onto the Jetson via USB, then establishes a USB RNDIS network link between the host and device to transfer flash images via SSH/SCP.

The problem: WSL2's default kernel does not include the CONFIG_USB_NET_RNDIS_HOST driver. Without it, the host cannot create the usb0 network interface needed to communicate with the Jetson during initrd flash. This guide documents how we solved this — and every obstacle along the way.

The Solution (TL;DR)

  1. Install usbipd-win to forward USB devices from Windows to WSL2
  2. Build a custom WSL2 kernel with RNDIS network driver support
  3. Use NVIDIA's l4t_initrd_flash.sh inside WSL2 to flash the Jetson

Result

Successfully flashed JetPack 6.1 (L4T R36.4.0) onto a reComputer J3011 (Orin Nano 8GB, NVMe) entirely from Windows + WSL2.


Hardware & Software Inventory

Hardware

Component Details
Jetson Module NVIDIA Orin Nano 8GB (boardid: 3767, boardsku: 0003, chipsku: D5)
Carrier Board Seeed Studio J401 (reComputer J3011 enclosure)
Storage 128GB NVMe SSD (internal to reComputer)
Host PC Windows laptop with USB-A/USB-C ports
USB Cable USB-C (Jetson) to USB-A (host) data cable
Jumper Wire For shorting FC REC to GND (recovery mode)

Software

Component Version
Windows 10.0.26300.7733
WSL2 v2.6.3.0, kernel 6.6.87.2-microsoft-standard-WSL2
WSL2 Distro Ubuntu 26.04 "Resolute Raccoon"
usbipd-win v5.3.0
L4T BSP R36.4.0 (JetPack 6.1), released Sep 13 2024
GCC (for kernel build) gcc-13 (13.4.0) — see GCC 15 incompatibility

Architecture Overview

Data Flow During Initrd Flash

┌─────────────────────────────────────────────────────────────────┐
│                        WINDOWS HOST                             │
│                                                                 │
│  ┌──────────────┐     USB/IP      ┌──────────────────────────┐  │
│  │  usbipd-win  │◄──────────────►│        WSL2 Ubuntu        │  │
│  │  (service)   │   (forwarding)  │                          │  │
│  │              │                 │  ┌────────────────────┐  │  │
│  │  BUSID 1-1   │                 │  │  vhci_hcd (USB/IP) │  │  │
│  │  auto-attach │                 │  │  rndis_host.ko ◄───┼──┼──── Custom kernel module
│  └──────┬───────┘                 │  │  usbnet.ko         │  │  │
│         │                         │  │  cdc_ether.ko      │  │  │
│         │ Physical USB            │  │  mii.ko            │  │  │
│         │                         │  └────────┬───────────┘  │  │
│         │                         │           │ usb0         │  │
│         │                         │  ┌────────▼───────────┐  │  │
│         │                         │  │ l4t_initrd_flash.sh│  │  │
│         │                         │  │  ├─ tegraflash.py  │  │  │
│         │                         │  │  ├─ SSH/SCP ───────┼──┼──── Over usb0 RNDIS
│         │                         │  │  └─ NFS server     │  │  │
│         │                         │  └────────────────────┘  │  │
│         │                         └──────────────────────────┘  │
└─────────┼───────────────────────────────────────────────────────┘
          │
          │ USB-C Cable
          │
┌─────────▼───────────────────────────────────────────────────────┐
│                    JETSON ORIN NANO (J3011)                      │
│                                                                 │
│  Phase 1: APX Recovery Mode (VID:PID 0955:7523)                 │
│    └─► RCM boot: Host sends initrd kernel via USB               │
│                                                                 │
│  Phase 2: Initrd Flash Mode (VID:PID 0955:7035)                 │
│    └─► USB re-enumerates as RNDIS network device                │
│    └─► Host establishes usb0 network link (192.168.55.1)        │
│    └─► Host SSHes into Jetson initrd environment                │
│    └─► Flash images transferred via SSH/SCP/NFS                 │
│    └─► Partitions written to QSPI (internal) + NVMe             │
│                                                                 │
│  Phase 3: Reboot into flashed OS                                │
│    └─► JetPack 6.1 boots from NVMe                              │
└─────────────────────────────────────────────────────────────────┘

USB Device State Transitions During Flash

Power On + FC REC → GND
        │
        ▼
  ┌─────────────┐     tegraflash.py rcmboot
  │ APX Mode    │─────────────────────────────►┌──────────────┐
  │ 0955:7523   │                              │ Initrd Boot  │
  │ (Recovery)  │                              │ 0955:7035    │
  └─────────────┘                              │ (RNDIS)      │
                                               └──────┬───────┘
                                                      │
                                     SSH over usb0    │  Flash partitions
                                                      │
                                               ┌──────▼───────┐
                                               │ Reboot       │
                                               │ Normal OS    │
                                               │ (JetPack)    │
                                               └──────────────┘

Critical insight: The USB device re-enumerates (disconnects and reconnects with a different VID:PID) during flash. This is why usbipd --auto-attach is essential — it must re-bind and re-attach the new device automatically.


Phase 1: Environment Setup

Step 1.1: Verify WSL2

# Check WSL version and kernel
wsl --version
wsl -d Ubuntu -- uname -r
# Expected: 6.6.87.2-microsoft-standard-WSL2

Step 1.2: Install usbipd-win

winget install usbipd
# Installed: usbipd-win v5.3.0

usbipd-win creates a Windows service that forwards USB devices to WSL2 via the USB/IP protocol. WSL2 runs in a lightweight Hyper-V VM and doesn't have direct USB access — usbipd bridges this gap.

Step 1.3: Install Flash Dependencies in WSL2

sudo apt update
sudo apt install -y \
    qemu-user-static \
    sshpass \
    abootimg \
    nfs-kernel-server \
    libxml2-utils \
    network-manager \
    python3 \
    python3-yaml \
    usbutils \
    xxd \
    cpio \
    binutils \
    dosfstools \
    lbzip2 \
    libxml2-utils \
    netcat-traditional

Troubleshooting: Ubuntu 26.04 Quirks

Ubuntu 26.04 (development branch) introduced several breaking changes:

Issue Symptom Fix
qemu-aarch64-static missing apply_binaries.sh fails ln -sf /usr/bin/qemu-aarch64 /usr/bin/qemu-aarch64-static
netcat is virtual package apt install netcat fails Install netcat-traditional explicitly
ssh-keygen -t dsa removed Recovery ramdisk generation fails Patch the script (see Phase 4)
GCC 15 is default Kernel build fails Use gcc-13 explicitly

Phase 2: Download & Prepare L4T BSP

Step 2.1: Download L4T R36.4.0

mkdir -p ~/jetson-flash && cd ~/jetson-flash

# BSP Driver Package (~677MB)
wget https://developer.nvidia.com/downloads/embedded/l4t/r36_release_v4.0/release/jetson_linux_r36.4.0_aarch64.tbz2

# Sample Root Filesystem (~1.7GB)
wget https://developer.nvidia.com/downloads/embedded/l4t/r36_release_v4.0/release/tegra_linux_sample-root-filesystem_r36.4.0_aarch64.tbz2

Step 2.2: Extract and Apply

# Extract BSP
tar xf jetson_linux_r36.4.0_aarch64.tbz2

# Extract rootfs into the BSP
cd Linux_for_Tegra/rootfs
sudo tar xpf ../../tegra_linux_sample-root-filesystem_r36.4.0_aarch64.tbz2

# Apply NVIDIA binaries to rootfs
cd ..
sudo ./apply_binaries.sh

Step 2.3: Configure Default User

# Pre-create user so first-boot wizard is skipped
sudo ./tools/l4t_create_default_user.sh \
    -u nvidia \
    -p nvidia \
    -n jetson-orin \
    --accept-license

Choosing the Correct Flash Target

L4T R36.x uses a different naming convention than JetPack 5.x:

JetPack Version Target Name Format Example
JP 5.x (L4T R35.x) p3509-a02+p3767-0000 Plus-sign separated
JP 6.x (L4T R36.x) jetson-orin-nano-devkit Human-readable name

The target jetson-orin-nano-devkit auto-detects the module SKU via EEPROM (reading boardid=3767, boardsku=0003 at runtime).


Phase 3: Recovery Mode & USB Passthrough

Step 3.1: Enter Recovery Mode

The reComputer J3011 (J401 carrier board) has FC REC and GND pins on the board header.

┌─────────────────────────────┐
│     J401 Carrier Board      │
│                             │
│   [FC REC] ──jumper── [GND] │  ◄── Short these with a jumper wire
│                             │
│   Then power on / plug USB  │
└─────────────────────────────┘

Procedure:

  1. Power off the Jetson completely
  2. Connect a jumper wire between FC REC and GND
  3. Connect USB-C cable between Jetson and host PC
  4. Power on the Jetson

Step 3.2: Verify APX Device on Windows

usbipd list
# Should show:
# 1-1    0955:7523  APX                    Not shared

The NVIDIA APX device (VID:PID 0955:7523) indicates the Jetson is in USB recovery mode.

Step 3.3: Bind and Attach to WSL2

# Run in Administrator PowerShell:
usbipd bind --busid 1-1 --force

# Start auto-attach (keeps running, handles re-enumeration):
usbipd attach --wsl --busid 1-1 --auto-attach

Important: The --auto-attach flag is critical. During flash, the Jetson disconnects and reconnects as a different USB device (RNDIS). Auto-attach monitors the bus ID and re-attaches automatically.

Important: WSL2 must be running before usbipd attach. If you get "There is no WSL 2 distribution running", open a WSL terminal first: wsl -d Ubuntu -- sleep 999999

Step 3.4: Verify in WSL2

lsusb | grep -i nvidia
# Bus 001 Device 002: ID 0955:7523 NVIDIA Corp. APX

Phase 4: Flash Attempts & Troubleshooting

The Flash Command

cd ~/jetson-flash/Linux_for_Tegra

sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
    --external-device nvme0n1p1 \
    -c tools/kernel_flash/flash_l4t_external.xml \
    -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
    --showlogs \
    --network usb0 \
    jetson-orin-nano-devkit internal

Parameter breakdown:

Parameter Purpose
--external-device nvme0n1p1 Flash the OS to the NVMe SSD
-c flash_l4t_external.xml Partition layout for external (NVMe) storage
-p "-c flash_t234_qspi.xml" QSPI flash layout for bootloader partitions
--showlogs Verbose output
--network usb0 Use USB RNDIS network for image transfer
jetson-orin-nano-devkit Target board configuration
internal Flash internal (QSPI) bootloader partitions too

Flash Process Steps (What the Script Does)

Step 1: Generate flash packages
  ├─ Read device ECID, fuses, EEPROM
  ├─ Generate signed bootloader images
  ├─ Create recovery ramdisk
  ├─ Build system.img from rootfs (~7GB, takes 5-10 min)
  └─ Generate partition tables

Step 2: Generate RCM boot command line
  ├─ Prepare rcmboot blob
  └─ Save flash parameters

Step 3: Start the flashing process
  ├─ Boot Jetson via RCM with initrd kernel
  ├─ Wait for USB RNDIS device (usb0) ◄── THIS IS WHERE IT FAILS WITHOUT RNDIS
  ├─ Configure usb0 network (192.168.55.1)
  ├─ SSH into Jetson initrd environment
  ├─ Transfer images via SSH/SCP
  ├─ Write QSPI partitions (bootloader, BCT, firmware)
  ├─ Write NVMe partitions (APP/rootfs)
  └─ Reboot

Issue 1: Invalid Target Board Name

Symptom:

Error: Invalid target board: p3509-a02+p3767-0000

Root Cause: Used JetPack 5.x naming convention with L4T R36.x BSP.

Fix: Use jetson-orin-nano-devkit instead of p3509-a02+p3767-0000.


Issue 2: ssh-keygen -t dsa Fails

Symptom:

gzip: kernel/Image: not in gzip format
# (misleading — actual error is deeper in the recovery ramdisk generation)

Root Cause: Ubuntu 26.04's OpenSSH removed DSA key type support. The script ota_make_recovery_img_dtb.sh calls ssh-keygen -t dsa which fails, triggering a cascade of errors.

Fix: Patch the script:

# File: tools/ota_tools/version_upgrade/ota_make_recovery_img_dtb.sh
# Line 112: Replace:
#   ssh-keygen -t dsa -f ...
# With:
#   # DSA disabled on Ubuntu 26.04
#   true

Issue 3: "Waiting for target to boot-up..." Timeout

Symptom:

************************************
*  Step 3: Start the flashing process *
************************************
Waiting for target to boot-up...
Waiting for target to boot-up...
... (repeats for 120 seconds)
Timeout
Device failed to boot to the initrd flash kernel.

Root Cause: This is the critical failure. After RCM boot, the Jetson boots into initrd and re-enumerates as a USB RNDIS device (VID:PID 0955:7035). The flash script waits for a usb0 network interface to appear. But WSL2's default kernel lacks CONFIG_USB_NET_RNDIS_HOST, so the RNDIS device is never recognized as a network adapter.

Diagnosis:

# Check dmesg during the wait:
dmesg | grep -iE "usb|rndis"
# Shows USB device enumerated but no driver binds:
# usb 1-1: new high-speed USB device number 5 using vhci_hcd
# usb 1-1: SetAddress Request (5) to port 0
# (no rndis_host binding follows)

# Check kernel config:
zcat /proc/config.gz | grep RNDIS
# CONFIG_USB_NET_RNDIS_HOST is not set  ◄── THE PROBLEM

What the waiting code actually checks:

# From l4t_initrd_flash_internal.sh, line 508-520:
while IFS= read -r; do
    netpath=/sys/class/net/${REPLY}
    netconfiguration=$(get_udev_attribute "${netpath}" configuration)
    if [[ "${netconfiguration}" =~ RNDIS\+L4T${device_instance}.* ]]; then
        serialnumber="${netserialnumber}"
        break
    fi
done < <(ls /sys/class/net)

It iterates over network interfaces in /sys/class/net, looking for one whose USB configuration attribute matches RNDIS+L4T*. Without the RNDIS driver, no such interface exists.

Fix: Build a custom WSL2 kernel with RNDIS support → Phase 5.


Issue 4: GCC 15 Incompatible with Kernel 6.6

Symptom:

error: '-fzero-call-used-regs=' is not supported on this target

Root Cause: Ubuntu 26.04 ships GCC 15.2.0 as default. The WSL2 kernel (6.6.x) was built with GCC 11.2.0 and is not compatible with GCC 15.

Fix:

sudo apt install gcc-13
# Then build with:
make CC=gcc-13 HOSTCC=gcc-13 -j$(nproc) ...

Issue 5: Module Vermagic Mismatch (+ Suffix)

Symptom:

insmod: ERROR: could not insert module rndis_host.ko: Unknown symbol in module

Root Cause: Building from a git checkout adds a + suffix to the kernel version (indicating "dirty tree"). The module vermagic becomes 6.6.87.2-microsoft-standard-WSL2+ while the running kernel is 6.6.87.2-microsoft-standard-WSL2 (no +).

Fix:

# Create empty .scmversion to suppress git dirty suffix
touch .scmversion

# Also fix cached version strings:
echo "6.6.87.2-microsoft-standard-WSL2" > include/config/kernel.release
echo '#define UTS_RELEASE "6.6.87.2-microsoft-standard-WSL2"' > include/generated/utsrelease.h

Note: Even with matching vermagic, force-loaded modules (with insmod --force) may have CRC mismatches on symbol versions, causing the RNDIS driver to fail to bind to USB devices. The definitive solution is to run the custom-built kernel, not just load modules into the stock kernel.


Issue 6: NFS Server Fails in WSL2

Symptom:

A dependency job for nfs-server.service failed.

Root Cause: The nfsd kernel module isn't loaded, so /proc/fs/nfsd can't be mounted.

Fix:

sudo modprobe nfsd
sudo mount -t nfsd nfsd /proc/fs/nfsd
sudo systemctl start nfs-server

Phase 5: Building a Custom WSL2 Kernel with RNDIS Support

This is the key enabling step for the entire operation.

Step 5.1: Clone WSL2 Kernel Source

cd ~
git clone --depth 1 --branch linux-msft-wsl-6.6.87.2 \
    https://github.com/microsoft/WSL2-Linux-Kernel.git wsl-kernel
cd wsl-kernel

Step 5.2: Configure the Kernel

# Start from running kernel's config
zcat /proc/config.gz > .config

# Enable RNDIS and dependencies (as modules, since USB itself is =m)
# Using menuconfig or manual edit:
scripts/config --module CONFIG_MII
scripts/config --module CONFIG_USB_USBNET
scripts/config --module CONFIG_USB_NET_CDCETHER
scripts/config --module CONFIG_USB_NET_RNDIS_HOST

# Prevent git dirty suffix
touch .scmversion

# Prepare build
make CC=gcc-13 HOSTCC=gcc-13 olddefconfig

Why modules (=m) not built-in (=y)? Because CONFIG_USB=m in the WSL2 kernel. When USB support itself is a module, all USB sub-drivers must also be modules — the build system enforces this automatically.

Step 5.3: Build the Kernel

# Build bzImage (~5-10 minutes)
make CC=gcc-13 HOSTCC=gcc-13 -j$(nproc) bzImage

# Build all modules
make CC=gcc-13 HOSTCC=gcc-13 -j$(nproc) modules

# Install modules to /lib/modules/
sudo make modules_install

Step 5.4: Install the Custom Kernel

# Copy bzImage to Windows filesystem
cp arch/x86/boot/bzImage /mnt/c/Users/<username>/wsl-kernel-rndis

Create C:\Users\<username>\.wslconfig:

[wsl2]
kernel=C:\\Users\\<username>\\wsl-kernel-rndis
# Restart WSL2 to use the new kernel
wsl --shutdown
# Then start WSL again
wsl -d Ubuntu

Step 5.5: Verify and Load Modules

# Verify custom kernel is running
uname -r
# 6.6.87.2-microsoft-standard-WSL2+

cat /proc/version
# Should show your username and gcc-13

# Load RNDIS modules
sudo modprobe rndis_host

# Verify
lsmod | grep -iE "rndis|usbnet|cdc_ether|mii"
# rndis_host    20480  0
# cdc_ether     24576  1 rndis_host
# usbnet        61440  2 rndis_host,cdc_ether
# mii           20480  1 usbnet

Module Dependency Chain

┌──────────┐
│   mii    │  Media Independent Interface (Ethernet PHY)
└────┬─────┘
     │ depends
┌────▼─────┐
│  usbnet  │  USB network driver framework
└────┬─────┘
     │ depends
┌────▼──────┐
│ cdc_ether │  CDC Ethernet (USB networking base class)
└────┬──────┘
     │ depends
┌────▼───────────┐
│  rndis_host    │  Remote NDIS host driver (Microsoft USB networking)
└────────────────┘

Phase 6: Successful Flash

Pre-Flight Checklist

# 1. Custom kernel running
uname -r  # Should show your custom build

# 2. RNDIS modules loaded
lsmod | grep rndis_host  # Should show rndis_host

# 3. NFS server running
sudo modprobe nfsd
sudo mount -t nfsd nfsd /proc/fs/nfsd 2>/dev/null
sudo systemctl start nfs-server

# 4. Jetson in APX mode
lsusb | grep -i nvidia  # Should show 0955:7523

# 5. vhci_hcd loaded (for USB/IP)
lsmod | grep vhci_hcd

Execute Flash

# Windows Admin PowerShell — keep running:
usbipd bind --busid 1-1 --force
usbipd attach --wsl --busid 1-1 --auto-attach
# WSL2:
cd ~/jetson-flash/Linux_for_Tegra

sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
    --external-device nvme0n1p1 \
    -c tools/kernel_flash/flash_l4t_external.xml \
    -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
    --showlogs \
    --network usb0 \
    jetson-orin-nano-devkit internal

Expected Flash Output Timeline

~T+0:00   Step 1: Generate flash packages
~T+0:30   Reading ECID, fuses, EEPROM from device
~T+1:00   Generating signed bootloader images
~T+1:30   Making recovery ramdisk
~T+2:00   Making system.img (populating rootfs) ◄── SLOWEST STEP
~T+10:00  system.img complete
~T+10:30  Step 2: Generate RCM boot commandline
~T+11:00  rcmboot_blob generated
~T+11:30  Step 3: Start the flashing process
~T+12:00  RCM boot sent to device
~T+12:30  USB re-enumerates as RNDIS (0955:7035)
~T+13:00  usb0 interface appears ◄── THIS IS THE MOMENT OF TRUTH
~T+13:30  SSH connection established to Jetson initrd
~T+14:00  Writing QSPI partitions (BCT, MB1, firmware)
~T+18:00  "Flashing success"
~T+18:30  "Reboot device"

Success Output

[ 185]: l4t_flash_from_kernel: Successfully flash the qspi
[ 185]: l4t_flash_from_kernel: Flashing success
Flash is successful
Reboot device
Cleaning up...
Log is saved to Linux_for_Tegra/initrdlog/flash_1-1_0_20260225-095314.log

Post-Flash Verification

Remove Recovery Jumper

Remove the jumper wire between FC REC and GND. The Jetson will boot normally on next power cycle.

SSH to the Jetson

ssh nvidia@<jetson-ip>
# Password: nvidia

Verify the Installation

# OS version
cat /etc/nv_tegra_release
# R36 (release), REVISION: 4.0, GCID: 37537400, BOARD: generic

# Kernel
uname -a
# Linux jetson-orin 5.15.148-tegra #1 SMP PREEMPT ... aarch64

# Storage
df -h /
# /dev/nvme0n1p1  116G  7.0G  103G   7% /

# Memory
free -h
#                total   used   free
# Mem:           7.4Gi  1.1Gi  5.3Gi
# Swap:          3.7Gi     0B  3.7Gi

Troubleshooting Reference

Quick Diagnostic Commands

# Check if RNDIS module is loaded
lsmod | grep rndis_host

# Check USB devices
lsusb

# Check for USB network interfaces
ip link show | grep usb

# Check kernel messages for USB events
dmesg | grep -iE "usb|rndis" | tail -20

# Check if Jetson is in recovery mode (from Windows)
usbipd list  # Look for 0955:7523 (APX) or 0955:7035 (RNDIS)

# Check NFS server
systemctl status nfs-server

# Check flash logs
tail -f /tmp/flash_log.txt

Common Failure Modes

Symptom Likely Cause Fix
Invalid target board Wrong target name for L4T version Use jetson-orin-nano-devkit for R36.x
gzip: kernel/Image: not in gzip format DSA keygen failure in recovery ramdisk Patch ota_make_recovery_img_dtb.sh
Waiting for target to boot-up... Timeout No RNDIS driver in WSL2 kernel Build custom kernel with RNDIS
Unknown symbol in module Vermagic mismatch or missing dependency Run your own kernel + modules_install
usbipd: error: Device is not shared Need admin privileges Run usbipd bind in admin PowerShell
There is no WSL 2 distribution running WSL2 not active when attaching USB Start WSL first: wsl -d Ubuntu -- sleep 999999
NFS server dependency failed nfsd module not loaded modprobe nfsd && systemctl start nfs-server
The operation was canceled by the user UAC prompt dismissed/blocked Manually run commands in admin PowerShell

Quick Reference: Reflash Procedure

For subsequent flashes (assuming you kept the files):

# 1. Ensure .wslconfig points to custom kernel
#    C:\Users\<username>\.wslconfig should have:
#    [wsl2]
#    kernel=C:\\Users\\<username>\\wsl-kernel-rndis

# 2. Start WSL2
wsl -d Ubuntu -- bash -c 'sleep 999999' &

# 3. Load modules and start NFS (in WSL2 as root)
wsl -d Ubuntu -u root -- bash -c '
    modprobe rndis_host
    modprobe nfsd
    mount -t nfsd nfsd /proc/fs/nfsd 2>/dev/null
    systemctl start nfs-server
'

# 4. Put Jetson in recovery (FC REC jumper + power on)

# 5. Admin PowerShell:
usbipd bind --busid 1-1 --force
usbipd attach --wsl --busid 1-1 --auto-attach

# 6. Flash (in WSL2 as root):
wsl -d Ubuntu -u root -- bash -c '
    cd /home/<user>/jetson-flash/Linux_for_Tegra &&
    ./tools/kernel_flash/l4t_initrd_flash.sh \
        --external-device nvme0n1p1 \
        -c tools/kernel_flash/flash_l4t_external.xml \
        -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
        --showlogs --network usb0 \
        jetson-orin-nano-devkit internal
'

Key Takeaways

Why This Is Hard

  1. WSL2 is a VM, not bare metal. USB devices must be forwarded via USB/IP (usbipd-win), adding a layer of abstraction that NVIDIA's tools don't account for.

  2. The WSL2 kernel is minimal. Microsoft strips out drivers that aren't typically needed in a VM, including USB network drivers like RNDIS. This is the single biggest blocker.

  3. USB device re-enumeration. The Jetson changes its USB identity mid-flash (APX → RNDIS). Both usbipd auto-attach and the RNDIS kernel driver must handle this seamlessly.

  4. Ubuntu 26.04 bleeding edge. Running a development distro means dealing with GCC 15 incompatibilities, removed DSA key support, and package naming changes.

What We Built

  • A custom WSL2 kernel (16MB bzImage) with USB RNDIS network support
  • A complete flash environment inside WSL2 with all NVIDIA dependencies
  • A repeatable procedure for flashing Jetson devices from Windows

Performance

Phase Duration
Initial setup (downloads, extract, dependencies) ~45 min
Custom kernel build ~15 min
Flash execution ~18 min
Total first-time ~1.5 hours
Subsequent reflash ~20 min

Files to Preserve

Path Size Purpose
~/jetson-flash/Linux_for_Tegra/ ~15GB L4T BSP with rootfs and flash images
~/wsl-kernel/ ~5GB (2GB after make clean) Custom kernel source
C:\Users\<user>\wsl-kernel-rndis 16MB Custom kernel bzImage
C:\Users\<user>\.wslconfig <1KB WSL2 kernel configuration

Appendix: Version Matrix

reComputer Model Module boardid boardsku L4T Target
J3010 Orin Nano 4GB 3767 0004 jetson-orin-nano-devkit
J3011 Orin Nano 8GB 3767 0003 jetson-orin-nano-devkit
J4011 Orin NX 8GB 3767 0001 jetson-orin-nano-devkit
J4012 Orin NX 16GB 3767 0000 jetson-orin-nano-devkit

All use the same target name — the flash tool auto-detects the module via EEPROM.


Document generated from a real flashing session on February 25, 2026. All commands were executed and verified.

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