Skip to content

Mounted images fail to boot #633

@rootxnet

Description

@rootxnet

On NanoKVM PCIe version (img: 1.4.0/app 2.2.9), I've noticed that my .iso images are failing to boot - the boot sector seems to be fine but the rest of .iso is missing.
My BIOS reported all .iso I've used to be 162 MB in size which after some research with LLMs pointed me to this:

Why your ISO mounted as only 162 MB

  1. Wrong backing device was used
    • The original code set imageNone = /dev/mmcblk0p3.
    • When no file was passed (or logic didn’t flip correctly), the gadget exposed your real eMMC partition (~162 MB) as the storage device, not the ISO.
    • BIOS therefore reported the partition’s true size = 162 MB.

I've built a workaround via AI:
kvm-mass-storage.sh

# kvm-mass-storage.sh — Recreate & mount USB gadget mass_storage properly
# Works with BusyBox ash (no bashisms).

set -eu

# Defaults
ISO_OR_IMG=""
MODE="iso"                 # iso | img
GADGET="g0"
LUN="0"
CFG="c.1"
UDC=""                     # auto-detect if empty
RO=""                      # auto by MODE: iso->1, img->0 (overridable)
REMOVABLE=""               # empty = skip; set 0/1 to force
NOFUA=""                   # empty = skip; set 0/1 to force
UNMOUNT=0

usage() {
  cat <<EOF
Usage:
  $0 -i /data/image.iso [-m iso|img] [-g g0] [-l 0] [-c c.1] [-u <udc>] [--ro 0|1] [--removable 0|1] [--nofua 0|1]
  $0 --unmount [-g g0] [-l 0] [-c c.1] [-u <udc>]

Options:
  -i PATH     Path to ISO/IMG file (required except with --unmount)
  -m MODE     iso (CD-ROM, 2048B sectors, RO) or img (disk). Default: iso
  -g NAME     Gadget name. Default: g0
  -l N        LUN index. Default: 0  (path: lun.N)
  -c NAME     Config name. Default: c.1
  -u UDC      UDC controller to bind. Default: auto-detect first in /sys/class/udc
  --ro X      Force ro flag (0/1). Overrides mode default.
  --removable X   Set lun.N/removable (0/1)
  --nofua X       Set lun.N/nofua (0/1)
  --unmount       Detach backing file and unbind/bind to signal media change

Examples:
  # Mount ISO as CD-ROM (RO):
  $0 -i /data/my.iso

  # Mount IMG as writable disk:
  $0 -i /data/disk.img -m img

  # Unmount / detach:
  $0 --unmount
EOF
}

# --- Parse args (POSIX) ---
while [ $# -gt 0 ]; do
  case "$1" in
    -i) ISO_OR_IMG="$2"; shift 2 ;;
    -m) MODE="$2"; shift 2 ;;
    -g) GADGET="$2"; shift 2 ;;
    -l) LUN="$2"; shift 2 ;;
    -c) CFG="$2"; shift 2 ;;
    -u) UDC="$2"; shift 2 ;;
    --ro) RO="$2"; shift 2 ;;
    --removable) REMOVABLE="$2"; shift 2 ;;
    --nofua) NOFUA="$2"; shift 2 ;;
    --unmount) UNMOUNT=1; shift ;;
    -h|--help) usage; exit 0 ;;
    *) echo "Unknown option: $1" >&2; usage; exit 1 ;;
  esac
done

G="/sys/kernel/config/usb_gadget/$GADGET"
L="$G/functions/mass_storage.disk0/lun.$LUN"
C="$G/configs/$CFG"

die() { echo "ERR: $*" >&2; exit 1; }
exists_dir() { [ -d "$1" ] || die "Missing dir: $1"; }
exists_file() { [ -f "$1" ] || die "Missing file: $1"; }

# Auto RO by mode if not forced
if [ -z "${RO:-}" ]; then
  if [ "$MODE" = "iso" ]; then RO=1; else RO=0; fi
fi

# Resolve UDC if empty
if [ -z "${UDC:-}" ]; then
  # shellcheck disable=SC2012
  UDC=$(ls -1 /sys/class/udc 2>/dev/null | head -n1 || true)
fi

# Sanity checks
exists_dir "$G" || true
[ "$UNMOUNT" -eq 1 ] || exists_file "$ISO_OR_IMG"

[ -n "$UDC" ] || [ "$UNMOUNT" -eq 1 ] || die "No UDC found; specify with -u"

# Helpers to write sysfs without newline
w() { echo -n "$2" > "$1"; }

unbind() { w "$G/UDC" ""; }
bind()   { w "$G/UDC" "$UDC"; }

unlink_func() {
  if [ -L "$C/mass_storage.disk0" ]; then rm -f "$C/mass_storage.disk0" || true; fi
}

detach_file() {
  if [ -e "$L/file" ]; then w "$L/file" ""; fi
}

remove_func() {
  if [ -d "$G/functions/mass_storage.disk0" ]; then
    rmdir "$G/functions/mass_storage.disk0" 2>/dev/null || true
  fi
}

create_func() {
  mkdir -p "$G/functions/mass_storage.disk0"
}

ensure_config() {
  mkdir -p "$C"
  mkdir -p "$C/strings/0x409"
  [ -f "$C/strings/0x409/configuration" ] || w "$C/strings/0x409/configuration" "Config 1"
}

link_func() {
  ln -s "$G/functions/mass_storage.disk0" "$C/mass_storage.disk0"
}

set_flags() {
  # MODE determines cdrom; RO already chosen
  if [ "$MODE" = "iso" ]; then
    w "$L/cdrom" "1"
  else
    w "$L/cdrom" "0"
  fi
  w "$L/ro" "$RO"

  # Optional flags if requested and nodes exist
  if [ -n "${REMOVABLE:-}" ] && [ -e "$L/removable" ]; then w "$L/removable" "$REMOVABLE"; fi
  if [ -n "${NOFUA:-}" ] && [ -e "$L/nofua" ]; then w "$L/nofua" "$NOFUA"; fi
}

attach_file() {
  w "$L/file" "$ISO_OR_IMG"
}

verify() {
  echo "UDC:   $(cat "$G/UDC" 2>/dev/null || echo)"
  [ -e "$L/file" ] && echo "file:  $(cat "$L/file")" || true
  [ -e "$L/cdrom" ] && echo "cdrom: $(cat "$L/cdrom")" || true
  [ -e "$L/ro" ] && echo "ro:    $(cat "$L/ro")" || true
}

# --- Actions ---
if [ "$UNMOUNT" -eq 1 ]; then
  # Cleanly detach whatever is mounted and unbind/bind to signal media change
  exists_dir "$G"
  exists_dir "$C" || true
  echo "[*] Unmounting from $G (LUN $LUN) ..."
  unbind || true
  detach_file || true
  unlink_func || true
  remove_func || true
  # Recreate empty function so host sees 'no media'
  create_func
  ensure_config
  link_func
  # Bind back (still no file attached)
  if [ -n "$UDC" ]; then bind || true; fi
  verify
  exit 0
fi

# Mount flow
echo "[*] Mounting '$ISO_OR_IMG' as $MODE on gadget '$GADGET', LUN $LUN, cfg $CFG, UDC=${UDC:-auto-none}"

exists_dir "$G" || die "Gadget $GADGET not found in configfs"
ensure_config

# 1) Unbind to avoid host probing mid-change
unbind || true

# 2) Unlink & remove any existing mass_storage function
unlink_func || true
detach_file || true
# small settle helps some controllers
sleep 0.2
remove_func || true

# 3) Recreate function fresh
create_func

# 4) Set flags BEFORE attaching file
set_flags

# 5) Attach file (no newline)
attach_file

# 6) Link function into the config
link_func

# 7) Bind to UDC
[ -n "$UDC" ] && bind

# 8) Verify
verify

echo "[*] Done."

chmod +x kvm-mass-storage.sh
./kvm-mass-storage.sh -i /data/my.iso - this properly mounts it, BIOS recognizes the proper ISO size and it is booting properly

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions