Stage 1 with USB device mode: what the build produced and what was checked (tasks 2.1-2.5) ========================================================================================== 2026-09-22, solomon, worktree for the-card-is-flashed-over-usb-from-u-boot, nixpkgs 20b1ddd1aa5ace70c9468305030aa4f9ef79671b. Everything is pasted from build logs or from commands run on the results. NOT A BOOT: this stage 1 has not been on a card. What changed, and where ----------------------- nix/uboot-k230-ums.config appended to k230_canmv_v3_defconfig by nix/uboot-k230.nix (extraConfig): CONFIG_USB_GADGET=y CONFIG_DM_USB_GADGET=y CONFIG_USB_GADGET_DWC2_OTG=y CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_USB_GADGET_VENDOR_NUM=0x29f1 CONFIG_USB_GADGET_PRODUCT_NUM=0x0230 CONFIG_CMD_USB_MASS_STORAGE=y # CONFIG_USB_DWC2 is not set nix/patches/uboot-k230/0001-k230_canmv_v3-enable-usbotg0-as-a-peripheral.patch: /* The data USB-C, J3, is wired to usbotg0 (schematic: USB0_P/USB0_N to J3 * A6/A7/B6/B7, with USB0_ID and USB0_VBUS). k230.dtsi leaves usbotg0 * disabled unless CONFIG_CMD_DFU is set; say so explicitly instead, and as * a peripheral, so U-Boot's `ums` can present the TF card to a host there. * Other Canaan boards (mrt, lckfb, gt6700) carry the same override. */ &usbotg0 { status = "okay"; dr_mode = "peripheral"; }; Sources unchanged: the same u-boot-2022.10 tarball and the same SDK commit as before. Three build attempts, two of which the plan did not foresee ----------------------------------------------------------- 1. Fragment appended, nothing else. Turning USB_GADGET on makes symbols visible that the defconfig never answered, and a non-interactive make dies in syncconfig: Enable USB fastboot gadget (USB_FUNCTION_FASTBOOT) [N/y/?] (NEW) Error in reading or end of file. Fix: nix/uboot-k230.nix runs 'make olddefconfig' after the append (postConfigure). 2. Configured, then Canaan's own addition to drivers/usb/gadget/dwc2_udc_otg.c -- code the old configuration never compiled -- fails under GCC 15.3.0: drivers/usb/gadget/dwc2_udc_otg.c:1117:72: error: passing argument 1 of 'readl' makes pointer from integer without a cast [-Wint-conversion] drivers/usb/gadget/dwc2_udc_otg.c:1119:68: error: passing argument 2 of 'writel' makes pointer from integer without a cast [-Wint-conversion] The lines (overlay, dwc2_udc_otg_probe, marked '// kendryte'): #define USB0_TEST_CTL3 (0x9158507cU) #define USB1_TEST_CTL3 (0x9158509cU) #define USB_DMPULLDOWN0 (1<<8) #define USB_DPPULLDOWN0 (1<<9) u32 usb_test_ctl3 = readl((usbotg_reg == 0x91500000)?USB0_TEST_CTL3:USB1_TEST_CTL3); usb_test_ctl3 &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0); writel(usb_test_ctl3, (usbotg_reg == 0x91500000)?USB0_TEST_CTL3:USB1_TEST_CTL3); GCC 13 warned about this; GCC 14 made -Wint-conversion an error. A u32 register address converted to a pointer is well defined on rv64, and the SPL and U-Boot the board booted were compiled by GCC 15, so the fix is the narrowest one: KCFLAGS=-Wno-error=int-conversion (extraMakeFlags), not a GCC 13 pin for the whole tree and not a patch to the vendor driver. 3. Built. Kconfig's later-wins for the three symbols the fragment reassigns, as expected: .config:1727:warning: override: reassigning to symbol USB_GADGET .config:1728:warning: override: reassigning to symbol DM_USB_GADGET .config:1759:warning: override: reassigning to symbol USB_DWC2 build flags: -j32 SHELL=/nix/store/svx59425zxp552p2b8gm11qj5r09b56i-bash-5.3p15/bin/bash DTC=/nix/store/yj48bn5mmzd drivers/usb/gadget/dwc2_udc_otg.c:1117:72: warning: passing argument 1 of 'readl' makes pointer from integer withou drivers/usb/gadget/dwc2_udc_otg.c:1119:68: warning: passing argument 2 of 'writel' makes pointer from integer witho SPL size check: spl/u-boot-spl.bin is 222816 bytes (0x36660) against CONFIG_SPL_SIZE_LIMIT=0x80000 (524288 bytes) The result: nix build .#uboot-k230 -> /nix/store/r55379zj8d5vwgq35ckzcr6m8xidsd9j-uboot-k230_canmv_v3_defconfig-riscv64-unknown-linux-gnu-2022.10 ------------------------------------------------------------------------------------------ 2.1 $ grep -E 'CONFIG_(CMD_USB_MASS_STORAGE|USB_GADGET|DM_USB_GADGET|USB_GADGET_DWC2_OTG|USB_GADGET_DOWNLOAD|USB_GADGET_VENDOR_NUM|USB_GADGET_PRODUCT_NUM|USB_FUNCTION_MASS_STORAGE)=' .config CONFIG_CMD_USB_MASS_STORAGE=y CONFIG_DM_USB_GADGET=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VENDOR_NUM=0x29f1 CONFIG_USB_GADGET_PRODUCT_NUM=0x0230 CONFIG_USB_GADGET_DWC2_OTG=y CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_USB_FUNCTION_MASS_STORAGE=y 2.2 $ grep -E 'CONFIG_(USB_DWC2|USB_HOST|USB_HOST_ETHER|USB_ETHER_RTL8152|USB_STORAGE|CMD_USB|CMD_DFU)\b' .config # CONFIG_CMD_DFU is not set # CONFIG_USB_DWC2 is not set (USB host off, and every host-side class driver went with it; CMD_USB stays as a command with nothing under it) 2.3 $ fdtget k230_canmv_v3.dtb /soc/usb-otg@91500000 status dr_mode; ... @91540000 usb-otg@91500000 status=okay dr_mode=peripheral usb-otg@91540000 status=okay dr_mode=otg 2.4 $ strings u-boot.bin | grep -E 'UMS|ums <' UMS disk %d UMS: LUN %d, dev %s %d, hwpart %d, sector %#x, count %#x Use the UMS [USB Mass Storage] $ riscv64-unknown-linux-gnu-nm u-boot | grep -E ' (do_usb_mass_storage|dwc2_udc_otg_probe|_u_boot_list_2_driver_2_(dwc2_udc_otg|usb_dwc2))$' 000000000001e8aa t do_usb_mass_storage 0000000000044640 t dwc2_udc_otg_probe 0000000000096a80 D _u_boot_list_2_driver_2_dwc2_udc_otg The ums command, the gadget driver's probe and its linker-list entry are in; the host driver's entry (_u_boot_list_2_driver_2_usb_dwc2) is not, so design D3's binding conflict cannot arise in this binary. Sizes: u-boot.bin 690280 bytes (was 721392 without the gadget and with USB host), spl/u-boot-spl.bin 222816 bytes (unchanged; nothing here touches the SPL). nix build .#stage1 -> /nix/store/7s8fkshr67s6impk0syb9y2dl7xsfm54-k230-stage1 ----------------------------------------------------------------------- $ cat result-stage1/SHA256SUMS fe3d537f62c34a3d01127c58fef4caec50f0e65b0192670712ecb1599584a954 fn_u-boot-spl.bin bd562f2bcff46ba9352061b08074f19df2f17dd0ca080293b47c20e3f5a6e50e fn_ug_u-boot.bin cf108755a3cf3a8070a2f0ded84af672108e2301dfd58e5c2aa6a7555dbbd352 env.env 74d08f8701dc72ea166d74be62fc3e5bff437d7c73ab68ad2f113cab289c38f9 fw_jump.bin 9627edbeea9b115d7040beea27cc61b23b6aca61cd502fd7760d7ee179f42c99 fw_jump_add_uboot_head.bin Against the stage 1 the board booted (docs/evidence/stage1-from-source.txt): fn_u-boot-spl.bin, env.env, fw_jump.bin and fw_jump_add_uboot_head.bin are byte-identical; only fn_ug_u-boot.bin changed (805bd543... -> bd562f2bcff46ba9...). fn_ug_u-boot.bin is 350046 bytes against its 1 MiB slot. $ xxd -s 596 -l 4 result-stage1/fn_ug_u-boot.bin (gzip magic, CM 09 -- the packaging is unchanged) 00000254: 1f8b 0900 .... nix build .#sdImage (pure) -> /nix/store/8jpkdpxmhyysafqv5gk8vcimw347ikqq-k230-sd-image.img (task 2.5) ----------------------------------------------------------------------------------------- Built without failing nix/sd-image.nix's slot checks. The three raw files against their slots: fn_u-boot-spl.bin 223348 bytes in a 524288-byte slot fn_ug_u-boot.bin 350046 bytes in a 1048576-byte slot env.env 8192 bytes in a 131072-byte slot (fn_ug_u-boot.bin is 350 046 bytes -- smaller than the 361 896 without the gadget, because USB host and its Ethernet class drivers left the binary with CONFIG_USB_DWC2.) $ sha256 over each slot of the image, for the file's length: 0x100000 fe3d537f62c34a3d01127c58fef4caec50f0e65b0192670712ecb1599584a954 = fn_u-boot-spl.bin (the SPL the board booted) 0x200000 bd562f2bcff46ba9352061b08074f19df2f17dd0ca080293b47c20e3f5a6e50e = fn_ug_u-boot.bin (new: ums) 0x300000 cf108755a3cf3a8070a2f0ded84af672108e2301dfd58e5c2aa6a7555dbbd352 = env.env This is the image task 3.1 would write to the card. Not written, not booted. tools/flash-latest.sh --ums (task 4.1), dry runs only ------------------------------------------------------- $ ./tools/flash-latest.sh --dry-run # the reader path, unchanged image: /nix/store/8jpkdpxmhyysafqv5gk8vcimw347ikqq-k230-sd-image.img stage 1: built from source by this flake (nix/stage1.nix) dry run: not flashing /dev/disk/by-id/usb-Generic-_USB3.0_CRW_-SD_201506301013-0:1 rc=0 $ ./tools/flash-latest.sh --ums /dev/disk/by-id/whatever # --ums discovers its own target error: --ums finds its own target; do not pass one rc=1 $ ./tools/flash.sh /dev/sdz # flash.sh's guardrail, still there error: refusing a bare device node. rc=1 What --ums does when it is used for real (not exercised: no gadget has enumerated yet): snapshot /dev/disk/by-id/usb-* whole disks, build, wait up to 300 s for exactly one new entry, hand that by-id path to tools/flash.sh. flash.sh then prints the target's contents and asks for the path's last 12 characters, as it does for a reader. USB host and UMS coexistence stage 1 — build-host evidence only (task 6.1) ---------------------------------------------------------------------------- Source commit `5133502` adds the gadget driver's bind-time `-ENODEV` refusal for non-peripheral/non-OTG DWC2 nodes, marks `usbotg1` as a host, and restores `CONFIG_USB_DWC2`. On 2026-09-22, `nix build .#stage1` with only `https://cache.nixos.org`, `--max-jobs 2`, and `--cores 8` built three derivations and exited 0 in 22.345 seconds: stage 1: /nix/store/g8r05lrfbb8gcwh1m1nrfy6q870y7mh1-k230-stage1 U-Boot: /nix/store/324sxp64hh9xc7g5pp6izana9c3n1d0v-uboot-k230_canmv_v3_defconfig-riscv64-unknown-linux-gnu-2022.10 The actual generated `.config`, rather than the source fragment, contains: CONFIG_USB_DWC2=y CONFIG_USB_GADGET=y CONFIG_DM_USB_GADGET=y CONFIG_USB_GADGET_DWC2_OTG=y CONFIG_CMD_USB_MASS_STORAGE=y The U-Boot-embedded DTB reports both intended assignments: /soc/usb-otg@91500000 status=okay dr_mode=peripheral /soc/usb-otg@91540000 status=okay dr_mode=host `strings` over the built uncompressed U-Boot contains the `UMS: LUN` command text. The raw stage-1 files fit their image slots: fn_u-boot-spl.bin 223348 bytes <= 524288 bytes fn_ug_u-boot.bin 380844 bytes <= 1048576 bytes env.env 8192 bytes <= 131072 bytes Their SHA-256 hashes are `fe3d537f62c34a3d01127c58fef4caec50f0e65b0192670712ecb1599584a954`, `84241c45dce5d5531240db044005eebfbed0d467fd6eb8026c3b822167b84c0a`, and `cf108755a3cf3a8070a2f0ded84af672108e2301dfd58e5c2aa6a7555dbbd352`, respectively. This is source/build evidence only. Task 6.2 remains unchecked: it requires a board to show the RTL8152 host tree while the J3 UMS gadget is present on the host. USB host match-priority repair — build-host evidence only ---------------------------------------------------------- The first coexistence candidate (`5133502`, stage 1 `/nix/store/g8r05lrfbb8gcwh1m1nrfy6q870y7mh1-k230-stage1`) is retained as a failed hardware diagnostic: it booted and UMS enumerated, but its `docs/evidence/uboot-usb-host-coexist.txt` transcript shows no working host controller and no `usbotg1` in `dm tree`. The source explains why. In this U-Boot, `lists_bind_fdt()` handles the gadget bind's `-ENODEV` by continuing the outer compatible-string loop, not by trying the next driver for the same `snps,dwc2` compatible. With only that one compatible string, the host driver was never considered. Commit `b25702e` retains that bind guard and adds `canaan,k230-usbotg-host` as usbotg1's first compatible, with a matching entry in the DWC2 host driver's ID table. The host driver therefore matches the first compatible before the gadget sees the generic fallback. On 2026-09-22, the repaired `nix build .#stage1` used only `https://cache.nixos.org`, `--max-jobs 2`, and `--cores 8`; it built three derivations and exited 0 in 22.782 seconds: stage 1: /nix/store/bsv0h6hx9w90iyzv3d7imzb5dbafw503-k230-stage1 U-Boot: /nix/store/0lnadpfx4bz99r5z1y22sfyq11wdj53w-uboot-k230_canmv_v3_defconfig-riscv64-unknown-linux-gnu-2022.10 The generated configuration still has `CONFIG_USB_DWC2=y`, `CONFIG_USB_GADGET=y`, `CONFIG_DM_USB_GADGET=y`, `CONFIG_USB_GADGET_DWC2_OTG=y`, and `CONFIG_CMD_USB_MASS_STORAGE=y`. Its embedded DTB has: /soc/usb-otg@91500000 compatible=snps,dwc2 dr_mode=peripheral /soc/usb-otg@91540000 compatible=canaan,k230-usbotg-host,snps,dwc2 dr_mode=host The resulting `fn_ug_u-boot.bin` is 380911 bytes in the 1 MiB slot, with SHA-256 `4cb755ed0b0ac06fcccade582fcbc90f99ffb6f4c35cec65e3564804b5be6226`. SPL, environment, OpenSBI, and wrapped OpenSBI retain their prior hashes. This is source/build evidence only. The repair still requires the task 6.2 hardware proof; it does not claim that the host controller or packet traffic works.