S03usbdev: derive USB serial number from SoC hardware UID - #746
JakeHillion wants to merge 1 commit into
Conversation
The USB gadget serial number was hardcoded to '0123456789ABCDEF',
meaning every NanoKVM device presented the same serial to the host.
Read the SoC unique identifier from /sys/class/cvi-base/base_uid on
each boot and concatenate its two halves into a single hex string for
use as the USB serial number. Falls back to the old hardcoded value if
the UID file is not present.
This gives each device a stable, hardware-derived serial number
without requiring any persistent state, and satisfies the USB Mass
Storage requirement of at least 12 characters. This means you can query
the USB interface on the machine the KVM is plugged into to find out
_which_ NanoKVM is plugged in, instead of that there is a NanoKVM
plugged in.
Test plan:
Installed script on two NanoKVMs with:
scp kvmapp/system/init.d/S03usbdev root@kvm04:/etc/init.d/S03usbdev
Plugged into a Linux machine, before I got:
```
[jake@li-bl-eq14-1:~]$ nix shell nixpkgs#usbutils --command lsusb -v | grep NanoKVM -A 50
Bus 003 Device 006: ID 3346:1009 sipeed NanoKVM
Negotiated speed: High Speed (480Mbps)
Device Descriptor:
...
idVendor 0x3346 sipeed
idProduct 0x1009 NanoKVM
bcdDevice 5.10
iManufacturer 1 sipeed
iProduct 2 NanoKVM
iSerial 3 0123456789ABCDEF
```
After the update, I get the same, but the serial number is now unique
per device. I unplugged the devices and plugged them back in, this is
now a consistent and unique identifier between my NanoKVMs from the host
device.
Use the unique hardware UID from /sys/class/cvi-base/base_uid as the USB serial number instead of a hardcoded value. Falls back to the default if the UID file is not available. Cherry-picked from: sipeed#746 Original author: JakeHillion
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Every NanoKVM reports the same USB serial, 0123456789ABCDEF, so a host that sees two of them cannot tell them apart. Udev rules, drive letters and per-device settings all key off that string, so the second device inherits whatever the first one was given. This is upstream sipeed#746. The SG2002 exposes a per-chip UID at /sys/class/cvi-base/base_uid, which the same script already reads to derive the gadget MACs. Use it here too. The constant stays as a fallback, and a length guard decides when. Mass storage requires a serial of at least 12 characters, so a short or unexpected UID keeps the old value rather than handing the gadget a serial the host rejects and failing to enumerate at all.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
|
I had written the same change independently before finding this PR, so I have dropped mine. One difference is worth folding in here. The That matters more than it looks. As you note, mass storage requires a serial of at least 12 characters. A serial that fails that requirement does not degrade to the old behaviour — the gadget fails to enumerate, so the operator loses keyboard, mouse and disk rather than just losing the ability to tell two devices apart. The fallback would be the safer outcome in that case, but the current shape cannot reach it once the file exists. A length check picks the fallback instead: serial='0123456789ABCDEF'
if [ -f /sys/class/cvi-base/base_uid ]
then
uid_serial=$(awk '{print $2}' /sys/class/cvi-base/base_uid | tr -d '_')
if [ ${#uid_serial} -ge 12 ]
then
serial="$uid_serial"
fi
fi
echo "$serial" > strings/0x409/serialnumber
I have not seen a board where Otherwise this reads correctly to me, and the per-device serial is genuinely useful once you have more than one on a bench. |
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects. (cherry picked from commit f6cceba)
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects. (cherry picked from commit f6cceba)
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects. (cherry picked from commit f6cceba)
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects. (cherry picked from commit f6cceba)
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects. (cherry picked from commit f6cceba)
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
Four upstream fixes to the same gadget setup, none of them merged. Do not expose the eMMC as a USB disk (upstream sipeed#741). With /boot/usb.disk0 empty - the out-of-box state - the gadget's backing file pointed at /dev/mmcblk0p3. That partition has no MBR, so a Legacy BIOS reads sector 0, finds no 0x55AA signature and hangs in a HLT loop with no keyboard; the only way out is a physical power cycle. Leaving the backing file unset makes the device report "no media" and the BIOS moves on. MountImage wrote the same device when unmounting, so the raw partition came back on every eject. GetMountedImage still reads it as "nothing mounted" for devices that have not rebooted yet. Default wakeup_on_write to off (upstream sipeed#754). Every HID write attempted a USB remote wakeup. Hosts that refuse it - common in BIOS, during restart, and on several chipsets - leave the DWC2 endpoint wedged, so keyboard and mouse die until the gadget is rebuilt. The opt-out flag /boot/usb.notwakeup becomes the opt-in flag /boot/usb.wakeup. Derive both gadget MACs from the chip UID (upstream sipeed#828). dev_addr and host_addr were left unset, so the kernel picked random MACs on every bind and the attached PC registered a new network adapter each reboot. Derive the USB serial from the chip UID (upstream sipeed#746). Every NanoKVM reported 0123456789ABCDEF, so a host could not tell two of them apart. Added a length guard the original lacks: mass storage requires at least 12 characters, so a short UID keeps the old constant rather than handing the gadget a serial the host rejects.
The USB gadget serial number was hardcoded to '0123456789ABCDEF', meaning every NanoKVM device presented the same serial to the host.
Read the SoC unique identifier from /sys/class/cvi-base/base_uid on each boot and concatenate its two halves into a single hex string for use as the USB serial number. Falls back to the old hardcoded value if the UID file is not present.
This gives each device a stable, hardware-derived serial number without requiring any persistent state, and satisfies the USB Mass Storage requirement of at least 12 characters. This means you can query the USB interface on the machine the KVM is plugged into to find out which NanoKVM is plugged in, instead of that there is a NanoKVM plugged in.
Test plan:
Installed script on two NanoKVMs with:
scp kvmapp/system/init.d/S03usbdev root@kvm04:/etc/init.d/S03usbdev
Plugged into a Linux machine, before I got:
After the update, I get the same, but the serial number is now unique per device. I unplugged the devices and plugged them back in, this is now a consistent and unique identifier between my NanoKVMs from the host device.