mirror of
https://github.com/azaion/loader.git
synced 2026-04-22 11:46:32 +00:00
cfed26ff8c
Made-with: Cursor
293 lines
8.7 KiB
Bash
Executable File
293 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ENV_FILE="$SCRIPT_DIR/.env"
|
|
|
|
HARDEN="${HARDEN:-true}"
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--no-harden) HARDEN="false"; shift ;;
|
|
*) echo "ERROR: Unknown option: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
NVIDIA_VENDOR="0955"
|
|
|
|
declare -A PID_TO_MODEL=(
|
|
["7523"]="Orin Nano"
|
|
["7323"]="Orin NX 16GB"
|
|
["7423"]="Orin NX 8GB"
|
|
)
|
|
|
|
declare -A PID_TO_BOARD_CONFIG=(
|
|
["7523"]="jetson-orin-nano-devkit"
|
|
["7323"]="jetson-orin-nx-devkit"
|
|
["7423"]="jetson-orin-nx-devkit"
|
|
)
|
|
|
|
require_env_var() {
|
|
local name="$1"
|
|
local val="${!name:-}"
|
|
if [[ -z "$val" ]]; then
|
|
echo "ERROR: $name is not set in $ENV_FILE" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
api_post() {
|
|
local url="$1"; shift
|
|
local response
|
|
response="$(curl -sS -w "\n%{http_code}" -X POST "$url" -H "Content-Type: application/json" "$@")"
|
|
echo "$response"
|
|
}
|
|
|
|
provision_single_device() {
|
|
local device_line="$1"
|
|
local usb_id="$2"
|
|
local board_config="$3"
|
|
|
|
echo "[Step 1/5] Registering device with admin API..."
|
|
local reg_response reg_http reg_body
|
|
reg_response="$(api_post "${API_URL}/devices" -H "Authorization: Bearer $TOKEN")"
|
|
reg_http="$(echo "$reg_response" | tail -1)"
|
|
reg_body="$(echo "$reg_response" | sed '$d')"
|
|
|
|
if [[ "$reg_http" != "200" && "$reg_http" != "201" ]]; then
|
|
echo "ERROR: Device registration failed (HTTP $reg_http)" >&2
|
|
echo "$reg_body" >&2
|
|
RESULTS+=("$usb_id | FAILED | registration error HTTP $reg_http")
|
|
return 1
|
|
fi
|
|
|
|
local serial dev_email dev_password
|
|
serial="$(echo "$reg_body" | jq -r '.serial // .Serial // empty')"
|
|
dev_email="$(echo "$reg_body" | jq -r '.email // .Email // empty')"
|
|
dev_password="$(echo "$reg_body" | jq -r '.password // .Password // empty')"
|
|
|
|
if [[ -z "$serial" || -z "$dev_email" || -z "$dev_password" ]]; then
|
|
echo "ERROR: Incomplete response from POST /devices" >&2
|
|
RESULTS+=("$usb_id | FAILED | incomplete API response")
|
|
return 1
|
|
fi
|
|
|
|
echo " Assigned serial: $serial"
|
|
echo " Email: $dev_email"
|
|
|
|
echo "[Step 2/5] Writing device.conf to rootfs staging..."
|
|
local conf_dir="${ROOTFS_DIR}/etc/azaion"
|
|
sudo mkdir -p "$conf_dir"
|
|
local conf_path="${conf_dir}/device.conf"
|
|
printf 'AZAION_DEVICE_EMAIL=%s\nAZAION_DEVICE_PASSWORD=%s\n' "$dev_email" "$dev_password" \
|
|
| sudo tee "$conf_path" > /dev/null
|
|
sudo chmod 600 "$conf_path"
|
|
echo " Written: $conf_path"
|
|
|
|
echo "[Step 3/5] Fusing device (odmfuse.sh)..."
|
|
if ! sudo "$L4T_DIR/odmfuse.sh" --instance "$usb_id" 2>&1; then
|
|
echo "ERROR: Fusing failed for $serial ($usb_id)" >&2
|
|
RESULTS+=("$usb_id | $serial | FAILED | fuse error")
|
|
return 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "[$serial] Fuse complete."
|
|
read -rp " Power-cycle the device and put it back in recovery mode. Press Enter when ready..."
|
|
|
|
echo "[Step 4/5] Flashing device (flash.sh)..."
|
|
if ! sudo "$L4T_DIR/flash.sh" "$board_config" "$FLASH_TARGET" --instance "$usb_id" 2>&1; then
|
|
echo "ERROR: Flashing failed for $serial ($usb_id)" >&2
|
|
RESULTS+=("$usb_id | $serial | FAILED | flash error")
|
|
return 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "[$serial] Flash complete."
|
|
echo " >>> Apply sticker with serial: $serial <<<"
|
|
read -rp " Power-cycle for first boot. Press Enter when done..."
|
|
|
|
echo "[Step 5/5] $serial provisioned successfully."
|
|
RESULTS+=("$usb_id | $serial | OK")
|
|
}
|
|
|
|
# --- main ---
|
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
echo "ERROR: $ENV_FILE not found. Copy .env.example to .env and fill in values." >&2
|
|
exit 1
|
|
fi
|
|
|
|
set -a
|
|
source "$ENV_FILE"
|
|
set +a
|
|
|
|
for var in ADMIN_EMAIL ADMIN_PASSWORD API_URL LOADER_IMAGE_TAR; do
|
|
require_env_var "$var"
|
|
done
|
|
API_URL="${API_URL%/}"
|
|
|
|
RESOURCE_API_URL="${RESOURCE_API_URL:-$API_URL}"
|
|
LOADER_DEV_STAGE="${LOADER_DEV_STAGE:-main}"
|
|
LOADER_IMAGE="${LOADER_IMAGE:-localhost:5000/loader:arm}"
|
|
FLASH_TARGET="${FLASH_TARGET:-nvme0n1p1}"
|
|
|
|
L4T_VERSION="${L4T_VERSION:-r36.4.4}"
|
|
L4T_DIR="${L4T_DIR:-/opt/nvidia/Linux_for_Tegra}"
|
|
ROOTFS_DIR="${ROOTFS_DIR:-$L4T_DIR/rootfs}"
|
|
|
|
export L4T_VERSION L4T_DIR ROOTFS_DIR RESOURCE_API_URL LOADER_DEV_STAGE LOADER_IMAGE LOADER_IMAGE_TAR
|
|
|
|
echo "=== Installing host dependencies ==="
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y usbutils curl jq wget
|
|
[[ "$(uname -m)" != "aarch64" ]] && sudo apt-get install -y qemu-user-static binfmt-support
|
|
echo ""
|
|
|
|
echo "=== L4T BSP setup ==="
|
|
"$SCRIPT_DIR/ensure_l4t.sh"
|
|
echo ""
|
|
|
|
echo "=== Setting up rootfs (Docker + application) ==="
|
|
"$SCRIPT_DIR/setup_rootfs_docker.sh"
|
|
echo ""
|
|
|
|
if [[ "$HARDEN" == "true" ]]; then
|
|
echo "=== Applying security hardening ==="
|
|
"$SCRIPT_DIR/harden_rootfs.sh"
|
|
echo ""
|
|
else
|
|
echo "=== Security hardening SKIPPED (--no-harden) ==="
|
|
echo ""
|
|
fi
|
|
|
|
echo "=== Authenticating with admin API ==="
|
|
LOGIN_JSON="$(printf '{"email":"%s","password":"%s"}' "$ADMIN_EMAIL" "$ADMIN_PASSWORD")"
|
|
LOGIN_RESPONSE="$(api_post "${API_URL}/login" -d "$LOGIN_JSON")"
|
|
LOGIN_HTTP="$(echo "$LOGIN_RESPONSE" | tail -1)"
|
|
LOGIN_BODY="$(echo "$LOGIN_RESPONSE" | sed '$d')"
|
|
|
|
if [[ "$LOGIN_HTTP" != "200" ]]; then
|
|
echo "ERROR: Login failed (HTTP $LOGIN_HTTP)" >&2
|
|
echo "$LOGIN_BODY" >&2
|
|
exit 1
|
|
fi
|
|
|
|
TOKEN="$(echo "$LOGIN_BODY" | jq -r '.token // .Token // empty')"
|
|
if [[ -z "$TOKEN" ]]; then
|
|
echo "ERROR: No token in login response" >&2
|
|
echo "$LOGIN_BODY" >&2
|
|
exit 1
|
|
fi
|
|
echo "Authenticated successfully."
|
|
echo ""
|
|
|
|
echo "=== Scanning for Jetson devices in recovery mode ==="
|
|
LSUSB_OUTPUT="$(lsusb -d "${NVIDIA_VENDOR}:" 2>/dev/null || true)"
|
|
|
|
if [[ -z "$LSUSB_OUTPUT" ]]; then
|
|
echo "No Jetson devices found in recovery mode."
|
|
echo "Ensure devices are connected via USB and in recovery mode (hold Force Recovery, press Power)."
|
|
exit 0
|
|
fi
|
|
|
|
DEVICES=()
|
|
DEVICE_PIDS=()
|
|
DEVICE_MODELS=()
|
|
while IFS= read -r line; do
|
|
pid="$(echo "$line" | grep -oP "${NVIDIA_VENDOR}:\K[0-9a-fA-F]+")"
|
|
if [[ -n "${PID_TO_BOARD_CONFIG[$pid]:-}" ]]; then
|
|
DEVICES+=("$line")
|
|
DEVICE_PIDS+=("$pid")
|
|
DEVICE_MODELS+=("${PID_TO_MODEL[$pid]:-Unknown (PID $pid)}")
|
|
fi
|
|
done <<< "$LSUSB_OUTPUT"
|
|
|
|
DEVICE_COUNT="${#DEVICES[@]}"
|
|
|
|
if [[ "$DEVICE_COUNT" -eq 0 ]]; then
|
|
echo "No supported Jetson devices found."
|
|
echo "Detected NVIDIA USB devices but none matched known Jetson Orin product IDs."
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
echo "Select device(s) to provision."
|
|
echo " one device, e.g. 1"
|
|
echo " some devices, e.g. 1 3 4"
|
|
echo " or all devices: 0"
|
|
echo ""
|
|
echo "--------------------------------------------"
|
|
echo "Connected Jetson devices (recovery mode):"
|
|
echo "--------------------------------------------"
|
|
for i in "${!DEVICES[@]}"; do
|
|
num=$((i + 1))
|
|
printf "%-3s %-16s %s\n" "$num" "[${DEVICE_MODELS[$i]}]" "${DEVICES[$i]}"
|
|
done
|
|
echo "--------------------------------------------"
|
|
echo "0 - provision all devices"
|
|
echo ""
|
|
|
|
read -rp "Your selection: " SELECTION
|
|
|
|
SELECTED_INDICES=()
|
|
if [[ "$SELECTION" == "0" ]]; then
|
|
for i in "${!DEVICES[@]}"; do
|
|
SELECTED_INDICES+=("$i")
|
|
done
|
|
else
|
|
for num in $SELECTION; do
|
|
if [[ "$num" =~ ^[0-9]+$ ]] && (( num >= 1 && num <= DEVICE_COUNT )); then
|
|
SELECTED_INDICES+=("$((num - 1))")
|
|
else
|
|
echo "ERROR: Invalid selection: $num (must be 1-$DEVICE_COUNT or 0 for all)" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ ${#SELECTED_INDICES[@]} -eq 0 ]]; then
|
|
echo "No devices selected."
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== Provisioning ${#SELECTED_INDICES[@]} device(s) ==="
|
|
echo ""
|
|
|
|
RESULTS=()
|
|
|
|
for idx in "${SELECTED_INDICES[@]}"; do
|
|
DEVICE_LINE="${DEVICES[$idx]}"
|
|
USB_ID="$(echo "$DEVICE_LINE" | grep -oP 'Bus \K[0-9]+')-$(echo "$DEVICE_LINE" | grep -oP 'Device \K[0-9]+')"
|
|
BOARD_CONFIG="${PID_TO_BOARD_CONFIG[${DEVICE_PIDS[$idx]}]:-}"
|
|
if [[ -z "$BOARD_CONFIG" ]]; then
|
|
echo "ERROR: Unknown Jetson product ID: $NVIDIA_VENDOR:${DEVICE_PIDS[$idx]}" >&2
|
|
RESULTS+=("$USB_ID | FAILED | unknown product ID")
|
|
continue
|
|
fi
|
|
echo "--------------------------------------------"
|
|
echo "Device: ${DEVICE_MODELS[$idx]} — $DEVICE_LINE"
|
|
echo "USB instance: $USB_ID"
|
|
echo "Board config: $BOARD_CONFIG"
|
|
echo "--------------------------------------------"
|
|
|
|
provision_single_device "$DEVICE_LINE" "$USB_ID" "$BOARD_CONFIG" || true
|
|
echo ""
|
|
done
|
|
|
|
CONF_CLEANUP="${ROOTFS_DIR}/etc/azaion/device.conf"
|
|
if [[ -f "$CONF_CLEANUP" ]]; then
|
|
sudo rm -f "$CONF_CLEANUP"
|
|
fi
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Provisioning Summary"
|
|
echo "========================================"
|
|
printf "%-12s | %-10s | %s\n" "USB ID" "Serial" "Status"
|
|
echo "----------------------------------------"
|
|
for r in "${RESULTS[@]}"; do
|
|
echo "$r"
|
|
done
|
|
echo "========================================"
|