diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index f4dfe10e..88c2e26a 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -238,7 +238,7 @@ export default function Hardware() { // Merge: static fields from initial load, live fields from the 5s poll. // coral_tpus and usb_devices live in the dynamic payload so that the - // "Install Drivers" button disappears immediately after install_coral_pve9.sh + // "Install Drivers" button disappears immediately after install_coral.sh // finishes, without requiring a page reload. const hardwareData = staticHardwareData ? { @@ -2726,8 +2726,8 @@ title="AMD GPU Tools Installation" setShowCoralInstaller(false) mutateStatic() }} - scriptPath="/usr/local/share/proxmenux/scripts/gpu_tpu/install_coral_pve9.sh" - scriptName="install_coral_pve9" + scriptPath="/usr/local/share/proxmenux/scripts/gpu_tpu/install_coral.sh" + scriptName="install_coral" params={{ EXECUTION_MODE: "web", }} diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 8563ebd1..83e01204 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -4901,7 +4901,7 @@ def get_hardware_live_info(): # Coral TPU and USB devices are cheap (sysfs reads + cached lsusb) and we # want them live so the "Install Drivers" button disappears as soon as the - # user finishes running install_coral_pve9.sh without needing a reload. + # user finishes running install_coral.sh without needing a reload. try: result['coral_tpus'] = get_coral_info() except Exception: diff --git a/scripts/gpu_tpu/install_coral.sh b/scripts/gpu_tpu/install_coral.sh new file mode 100644 index 00000000..55444f3b --- /dev/null +++ b/scripts/gpu_tpu/install_coral.sh @@ -0,0 +1,493 @@ +#!/bin/bash +# ProxMenux - Coral TPU Installer (unified: PCIe/M.2 + USB) +# ========================================================= +# Author : MacRimi +# License : MIT +# Version : 2.0 (unified PCIe+USB; auto-detect; feranick fork; libedgetpu runtime) +# Last Updated: 17/04/2026 +# ========================================================= +# +# One entry point for every Coral variant. At startup the script detects +# what Coral hardware is present on the host and installs only what is +# actually needed: +# +# • Coral M.2 / Mini-PCIe (vendor 1ac1 on PCIe) +# → build and install `gasket` + `apex` kernel modules via DKMS +# (feranick/gasket-driver fork; google as fallback with patches) +# → create apex group + udev rules +# → reboot required to load the fresh kernel module +# +# • Coral USB Accelerator (USB IDs 1a6e:089a / 18d1:9302) +# → add the Google Coral APT repository (signed-by keyring) +# → install libedgetpu1-std (Edge TPU runtime) +# → udev rules come with the package +# → no reboot required +# +# • Both present → both paths are run in sequence +# • Neither present → informative dialog and clean exit +# +# The script is idempotent: reruns on already-configured hosts skip work +# that is already done and recover from broken gasket-dkms package state +# (typical after a kernel upgrade on PVE 9). + +# Guarantee a valid working directory before anything else. When the user +# re-runs the installer from a previous /tmp/gasket-driver/... path that our +# own `rm -rf gasket-driver` removed, the inherited cwd is orphaned and bash +# emits `chdir: error retrieving current directory` warnings from every +# subprocess. Moving to / at launch makes the rest of the script immune to +# that state. +cd / 2>/dev/null || true + +LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +LOG_FILE="/tmp/coral_install.log" + +# Hardware detection results, set by detect_coral_hardware(). +CORAL_PCIE_COUNT=0 +CORAL_USB_COUNT=0 + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language +initialize_cache + + +# ============================================================ +# Hardware detection +# ============================================================ +detect_coral_hardware() { + CORAL_PCIE_COUNT=0 + CORAL_USB_COUNT=0 + + # PCIe / M.2 / Mini-PCIe — vendor 0x1ac1 (Global Unichip Corp.) + if [[ -d /sys/bus/pci/devices ]]; then + for dev in /sys/bus/pci/devices/*; do + [[ -e "$dev/vendor" ]] || continue + local vendor + vendor=$(cat "$dev/vendor" 2>/dev/null) + if [[ "$vendor" == "0x1ac1" ]]; then + CORAL_PCIE_COUNT=$((CORAL_PCIE_COUNT + 1)) + fi + done + fi + + # USB Accelerator + # 1a6e:089a Global Unichip Corp. (unprogrammed state — before runtime loads fw) + # 18d1:9302 Google Inc. (programmed state — after runtime talks to it) + if command -v lsusb >/dev/null 2>&1; then + CORAL_USB_COUNT=$(lsusb 2>/dev/null \ + | grep -cE 'ID (1a6e:089a|18d1:9302)' || true) + fi +} + + +# ============================================================ +# Dialogs +# ============================================================ +no_hardware_dialog() { + dialog --backtitle "ProxMenux" \ + --title "$(translate 'No Coral Detected')" \ + --msgbox "\n$(translate 'No Coral TPU device was found on this host (neither PCIe/M.2 nor USB).')\n\n$(translate 'Connect a Coral Accelerator and try again.')" \ + 12 72 +} + +pre_install_prompt() { + local msg="\n" + msg+="$(translate 'Detected Coral hardware:')\n\n" + msg+=" • $(translate 'M.2 / PCIe devices:') ${CORAL_PCIE_COUNT}\n" + msg+=" • $(translate 'USB Accelerators:') ${CORAL_USB_COUNT}\n\n" + + msg+="$(translate 'This installer will:')\n" + if [[ "$CORAL_PCIE_COUNT" -gt 0 ]]; then + msg+=" • $(translate 'Build and install the gasket and apex kernel modules (DKMS)')\n" + msg+=" • $(translate 'Set up the apex group and udev rules')\n" + fi + if [[ "$CORAL_USB_COUNT" -gt 0 ]]; then + msg+=" • $(translate 'Configure the Google Coral APT repository')\n" + msg+=" • $(translate 'Install the Edge TPU runtime (libedgetpu1-std)')\n" + fi + + if [[ "$CORAL_PCIE_COUNT" -gt 0 ]]; then + msg+="\n$(translate 'A reboot is required after installation to load the new kernel modules.')" + fi + + msg+="\n\n$(translate 'Do you want to proceed?')" + + if ! dialog --backtitle "ProxMenux" \ + --title "$(translate 'Coral TPU Installation')" \ + --yesno "$msg" 20 78; then + exit 0 + fi +} + + +# ============================================================ +# PCIe / M.2 branch — gasket + apex kernel modules via DKMS +# ============================================================ + +ensure_apex_group_and_udev() { + msg_info "$(translate 'Ensuring apex group and udev rules...')" + + if ! getent group apex >/dev/null; then + groupadd --system apex || true + msg_ok "$(translate 'System group apex created.')" + else + msg_ok "$(translate 'System group apex already exists.')" + fi + + cat >/etc/udev/rules.d/99-coral-apex.rules <<'EOF' +# Coral / Google APEX TPU (M.2 / PCIe) +# Assign group "apex" and safe permissions to device nodes +KERNEL=="apex_*", GROUP="apex", MODE="0660" +SUBSYSTEM=="apex", GROUP="apex", MODE="0660" +EOF + + if [[ -f /usr/lib/udev/rules.d/60-gasket-dkms.rules ]]; then + sed -i 's/GROUP="[^"]*"/GROUP="apex"/g' /usr/lib/udev/rules.d/60-gasket-dkms.rules || true + fi + + udevadm control --reload-rules + udevadm trigger --subsystem-match=apex || true + + msg_ok "$(translate 'apex group and udev rules are in place.')" + + if ls -l /dev/apex_* 2>/dev/null | grep -q ' apex '; then + msg_ok "$(translate 'Coral TPU device nodes detected with correct group (apex).')" + else + msg_warn "$(translate 'apex device node not found yet; a reboot may be required.')" + fi +} + +cleanup_broken_gasket_dkms() { + # Recover from a broken gasket-dkms .deb state (half-configured, unpacked, + # half-installed). This is a common failure mode on PVE 9 kernel upgrades: + # dkms autoinstall tries to rebuild against the new kernel, fails, and + # leaves dpkg stuck — which in turn blocks every subsequent apt-get call. + local pkg_state + pkg_state=$(dpkg -l gasket-dkms 2>/dev/null | awk '/^[a-zA-Z][a-zA-Z]/ {print $1}' | tail -1) + + [[ -z "$pkg_state" ]] && return 0 # package not present — nothing to clean + + case "$pkg_state" in + ii|rc) + msg_info "$(translate 'Removing any pre-existing gasket-dkms package...')" + dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1 || true + dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true + msg_ok "$(translate 'Pre-existing gasket-dkms package removed.')" + ;; + *) + msg_warn "$(translate 'Detected broken gasket-dkms package state:') ${pkg_state}. $(translate 'Forcing removal...')" + dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1 || true + dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true + dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true + apt-get install -f -y >>"$LOG_FILE" 2>&1 || true + msg_ok "$(translate 'Broken gasket-dkms package state recovered.')" + ;; + esac +} + +clone_gasket_sources() { + # Primary: feranick/gasket-driver — community fork, actively maintained, + # carries patches for kernel 6.10/6.12/6.13. + # Fallback: google/gasket-driver — upstream, stale. Requires manual patches. + # Sets GASKET_SOURCE_USED so the patch step knows whether to apply them. + local FERANICK_URL="https://github.com/feranick/gasket-driver.git" + local GOOGLE_URL="https://github.com/google/gasket-driver.git" + + cd /tmp || exit 1 + rm -rf gasket-driver >>"$LOG_FILE" 2>&1 + + msg_info "$(translate 'Cloning Coral driver repository (feranick fork)...')" + if git clone --depth=1 "$FERANICK_URL" gasket-driver >>"$LOG_FILE" 2>&1; then + GASKET_SOURCE_USED="feranick" + msg_ok "$(translate 'feranick/gasket-driver cloned (actively maintained, kernel 6.12+ ready).')" + return 0 + fi + + msg_warn "$(translate 'feranick fork unreachable. Falling back to google/gasket-driver...')" + rm -rf gasket-driver >>"$LOG_FILE" 2>&1 + if git clone --depth=1 "$GOOGLE_URL" gasket-driver >>"$LOG_FILE" 2>&1; then + GASKET_SOURCE_USED="google" + msg_ok "$(translate 'google/gasket-driver cloned (fallback — will apply local patches).')" + return 0 + fi + + msg_error "$(translate 'Could not clone any gasket-driver repository. Check your internet connection and') ${LOG_FILE}" + exit 1 +} + +show_dkms_build_failure() { + # Print the last 50 lines of make.log on-screen so the user sees the real + # compilation error without having to dig the log file. + local make_log="/var/lib/dkms/gasket/1.0/build/make.log" + echo "" >&2 + msg_warn "$(translate 'DKMS build failed. Last lines of make.log:')" + if [[ -f "$make_log" ]]; then + { + echo "---- /var/lib/dkms/gasket/1.0/build/make.log ----" + cat "$make_log" + } >>"$LOG_FILE" 2>&1 + tail -n 50 "$make_log" >&2 + else + echo "$(translate '(make.log not found — DKMS may have failed before invoking make)')" >&2 + fi + echo "" >&2 + echo -e "${TAB}${BL}$(translate 'Full log:')${CL} ${LOG_FILE}" >&2 + echo "" >&2 +} + +install_gasket_apex_dkms() { + # Detect running kernel — used both to pull matching headers and to apply + # kernel-version-specific patches if we fall back to google/gasket-driver. + local KVER KMAJ KMIN + KVER=$(uname -r) + KMAJ=$(echo "$KVER" | cut -d. -f1) + KMIN=$(echo "$KVER" | cut -d. -f2 | cut -d+ -f1 | cut -d- -f1) + + cleanup_broken_gasket_dkms + + msg_info "$(translate 'Installing build dependencies...')" + apt-get update -qq >>"$LOG_FILE" 2>&1 + if ! apt-get install -y git dkms build-essential "proxmox-headers-${KVER}" >>"$LOG_FILE" 2>&1; then + msg_error "$(translate 'Error installing build dependencies. Check') ${LOG_FILE}" + exit 1 + fi + msg_ok "$(translate 'Build dependencies installed.')" + + clone_gasket_sources + + cd /tmp/gasket-driver || exit 1 + + # Patches are only needed for the stale google fork. feranick already carries + # the equivalent fixes upstream; re-applying them would double-edit sources. + if [[ "$GASKET_SOURCE_USED" == "google" ]]; then + msg_info "$(translate 'Patching source for kernel compatibility...')" + + # no_llseek was removed in kernel 6.5 — replace with noop_llseek + if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 5 ]]; then + sed -i 's/\.llseek = no_llseek/\.llseek = noop_llseek/' src/gasket_core.c + fi + + # MODULE_IMPORT_NS syntax changed to string-literal in 6.13. + # Applying this patch on kernel <6.13 causes a compile error. + if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 13 ]]; then + sed -i 's/^MODULE_IMPORT_NS(DMA_BUF);/MODULE_IMPORT_NS("DMA_BUF");/' src/gasket_page_table.c + fi + + msg_ok "$(translate 'Source patched successfully.') (kernel ${KVER})" + else + msg_info2 "$(translate 'Skipping manual patches — feranick fork already supports this kernel.')" + fi + + local GASKET_SRC="/usr/src/gasket-1.0" + + if [[ ! -d /tmp/gasket-driver/src ]]; then + msg_error "$(translate 'Expected /tmp/gasket-driver/src not found. The clone seems incomplete or uses an unknown layout.')" + { echo "---- /tmp/gasket-driver/ contents ----"; ls -la /tmp/gasket-driver 2>/dev/null || true; } >>"$LOG_FILE" + exit 1 + fi + if [[ ! -f /tmp/gasket-driver/src/Makefile ]]; then + msg_error "$(translate 'Expected Makefile not found in /tmp/gasket-driver/src. Source tree is incomplete.')" + exit 1 + fi + + msg_info "$(translate 'Removing previous DKMS source tree...')" + dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true + if [[ -d "$GASKET_SRC" ]]; then + if ! rm -rf "$GASKET_SRC" 2>>"$LOG_FILE"; then + msg_error "$(translate 'Could not remove previous DKMS tree at') ${GASKET_SRC}. $(translate 'Check') ${LOG_FILE}" + exit 1 + fi + fi + msg_ok "$(translate 'Previous DKMS tree cleared.')" + + # Copy only the `src/` contents (where the kernel sources live) so + # Makefile + *.c + *.h sit at the DKMS tree root, matching the Debian + # packaging layout (`dh_install src/* usr/src/gasket-$(VERSION)/`). + msg_info "$(translate 'Copying sources to') ${GASKET_SRC}..." + mkdir -p "$GASKET_SRC" + if ! cp -a /tmp/gasket-driver/src/. "${GASKET_SRC}/" 2>>"$LOG_FILE"; then + msg_error "$(translate 'Failed to copy sources into') ${GASKET_SRC}. $(translate 'Check') ${LOG_FILE}" + exit 1 + fi + if [[ ! -f "$GASKET_SRC/Makefile" ]]; then + msg_error "$(translate 'Makefile missing in') ${GASKET_SRC} $(translate 'after copy; source tree is incomplete.')" + exit 1 + fi + msg_ok "$(translate 'Sources copied to') ${GASKET_SRC}" + + # The repo ships debian/gasket-dkms.dkms as a template with a + # #MODULE_VERSION# placeholder that the .deb pipeline substitutes. Since we + # install directly from sources (no .deb), we write our own dkms.conf. + # MAKE[0] passes ${kernelver} to the Makefile so multi-kernel rebuilds + # (PVE's autoinstall on new kernel installs) target the right headers. + msg_info "$(translate 'Generating dkms.conf...')" + cat > "$GASKET_SRC/dkms.conf" <<'EOF' +PACKAGE_NAME="gasket" +PACKAGE_VERSION="1.0" +BUILT_MODULE_NAME[0]="gasket" +BUILT_MODULE_NAME[1]="apex" +DEST_MODULE_LOCATION[0]="/updates/dkms" +DEST_MODULE_LOCATION[1]="/updates/dkms" +MAKE[0]="make KVERSION=${kernelver}" +CLEAN="make clean" +AUTOINSTALL="yes" +EOF + if [[ ! -s "$GASKET_SRC/dkms.conf" ]]; then + msg_error "$(translate 'Failed to write') ${GASKET_SRC}/dkms.conf" + exit 1 + fi + msg_ok "$(translate 'dkms.conf generated.')" + + msg_info "$(translate 'Registering module with DKMS...')" + if ! dkms add "$GASKET_SRC" >>"$LOG_FILE" 2>&1; then + msg_error "$(translate 'DKMS add failed. Check') ${LOG_FILE}" + exit 1 + fi + msg_ok "$(translate 'DKMS module registered.')" + + msg_info "$(translate 'Compiling Coral TPU drivers for current kernel...')" + if ! dkms build gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then + show_dkms_build_failure + msg_error "$(translate 'DKMS build failed.')" + exit 1 + fi + if ! dkms install gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then + show_dkms_build_failure + msg_error "$(translate 'DKMS install failed.')" + exit 1 + fi + msg_ok "$(translate 'Drivers compiled and installed via DKMS.') (source: ${GASKET_SOURCE_USED})" + + ensure_apex_group_and_udev + + msg_info "$(translate 'Loading modules...')" + modprobe gasket >>"$LOG_FILE" 2>&1 || true + modprobe apex >>"$LOG_FILE" 2>&1 || true + if lsmod | grep -q '\bapex\b'; then + msg_ok "$(translate 'Modules loaded.')" + else + msg_warn "$(translate 'Installation finished but drivers are not loaded. A reboot may be required.')" + fi + + echo "---- dmesg | grep -i apex (last lines) ----" >>"$LOG_FILE" + dmesg | grep -i apex | tail -n 20 >>"$LOG_FILE" 2>&1 +} + + +# ============================================================ +# USB branch — libedgetpu runtime from Google's APT repository +# ============================================================ + +install_libedgetpu_runtime() { + local KEYRING=/etc/apt/keyrings/coral-edgetpu.gpg + local LIST_FILE=/etc/apt/sources.list.d/coral-edgetpu.list + + # Modern repo configuration: one keyring file under /etc/apt/keyrings plus + # a sources.list.d entry with `signed-by=`. Avoids the deprecated apt-key. + msg_info "$(translate 'Setting up the Google Coral APT repository...')" + mkdir -p /etc/apt/keyrings + + if [[ ! -s "$KEYRING" ]]; then + if ! curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \ + | gpg --dearmor -o "$KEYRING" 2>>"$LOG_FILE"; then + msg_error "$(translate 'Failed to fetch the Google Coral GPG key. Check') ${LOG_FILE}" + exit 1 + fi + chmod 0644 "$KEYRING" + fi + + cat > "$LIST_FILE" <>"$LOG_FILE" 2>&1; then + msg_warn "$(translate 'apt-get update returned warnings. Continuing anyway; check') ${LOG_FILE}" + fi + msg_ok "$(translate 'Coral APT repository ready.')" + + # libedgetpu1-std = standard performance; libedgetpu1-max = overclocked mode + # (more heat). We default to -std; users who explicitly want -max can install + # it manually. Either way the udev rules come with the package. + msg_info "$(translate 'Installing Edge TPU runtime (libedgetpu1-std)...')" + if ! apt-get install -y libedgetpu1-std >>"$LOG_FILE" 2>&1; then + msg_error "$(translate 'Failed to install libedgetpu1-std. Check') ${LOG_FILE}" + exit 1 + fi + msg_ok "$(translate 'Edge TPU runtime installed.')" + + # Reload udev so the rules shipped with libedgetpu1-std apply to any USB + # Coral already plugged in (otherwise they would only apply after replug). + udevadm control --reload-rules >/dev/null 2>&1 || true + udevadm trigger --subsystem-match=usb >/dev/null 2>&1 || true +} + + +# ============================================================ +# Final prompt +# ============================================================ +restart_prompt() { + if whiptail --title "$(translate 'Coral TPU Installation')" --yesno \ + "$(translate 'The installation requires a server restart to apply changes. Do you want to restart now?')" 10 70; then + msg_warn "$(translate 'Restarting the server...')" + reboot + else + msg_success "$(translate 'Completed. Press Enter to return to menu...')" + read -r + fi +} + + +# ============================================================ +# Main orchestrator +# ============================================================ +main() { + : >"$LOG_FILE" + + detect_coral_hardware + + # Nothing plugged in — nothing to do. + if [[ "$CORAL_PCIE_COUNT" -eq 0 && "$CORAL_USB_COUNT" -eq 0 ]]; then + no_hardware_dialog + exit 0 + fi + + pre_install_prompt + + show_proxmenux_logo + msg_title "$(translate 'Coral TPU Installation')" + + # Force non-interactive apt/dpkg for the whole run so cleanup_broken_gasket_dkms + # and the two install paths never get blocked by package-maintainer prompts. + export DEBIAN_FRONTEND=noninteractive + + # Branch 1 — PCIe / M.2 (kernel modules). Runs first so the reboot reminder + # at the end only appears when we actually touched kernel modules. + if [[ "$CORAL_PCIE_COUNT" -gt 0 ]]; then + msg_info2 "$(translate 'Coral M.2 / PCIe detected — installing gasket and apex kernel modules...')" + install_gasket_apex_dkms + fi + + # Branch 2 — USB (user-space runtime). + if [[ "$CORAL_USB_COUNT" -gt 0 ]]; then + msg_info2 "$(translate 'Coral USB Accelerator detected — installing Edge TPU runtime...')" + install_libedgetpu_runtime + fi + + echo + if [[ "$CORAL_PCIE_COUNT" -gt 0 ]]; then + msg_success "$(translate 'Coral TPU drivers installed and loaded successfully.')" + restart_prompt + else + # USB-only install. No reboot required; the udev rules and runtime are + # already active. Ready to passthrough the device to an LXC/VM. + msg_success "$(translate 'Coral USB runtime installed. No reboot required.')" + msg_success "$(translate 'Completed. Press Enter to return to menu...')" + read -r + fi +} + +main diff --git a/scripts/gpu_tpu/install_coral_pve9.sh b/scripts/gpu_tpu/install_coral_pve9.sh deleted file mode 100644 index e3bf0a78..00000000 --- a/scripts/gpu_tpu/install_coral_pve9.sh +++ /dev/null @@ -1,296 +0,0 @@ -#!/bin/bash -# ProxMenux - Coral TPU Installer (PVE 9.x) -# ========================================= -# Author : MacRimi -# License : MIT -# Version : 1.5 (feranick fork primary; kernel 6.12+ support; broken-pkg recovery) -# Last Updated: 17/04/2026 -# ========================================= - -LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -LOG_FILE="/tmp/coral_install.log" - -if [[ -f "$UTILS_FILE" ]]; then - source "$UTILS_FILE" -fi - - -load_language -initialize_cache - - - - -ensure_apex_group_and_udev() { - msg_info "Ensuring apex group and udev rules..." - - - if ! getent group apex >/dev/null; then - groupadd --system apex || true - msg_ok "System group 'apex' created" - else - msg_ok "System group 'apex' already exists" - fi - - - cat >/etc/udev/rules.d/99-coral-apex.rules <<'EOF' -# Coral / Google APEX TPU (M.2 / PCIe) -# Assign group "apex" and safe permissions to device nodes -KERNEL=="apex_*", GROUP="apex", MODE="0660" -SUBSYSTEM=="apex", GROUP="apex", MODE="0660" -EOF - - - if [[ -f /usr/lib/udev/rules.d/60-gasket-dkms.rules ]]; then - sed -i 's/GROUP="[^"]*"/GROUP="apex"/g' /usr/lib/udev/rules.d/60-gasket-dkms.rules || true - fi - - - udevadm control --reload-rules - udevadm trigger --subsystem-match=apex || true - - msg_ok "apex group and udev rules are in place" - - - if ls -l /dev/apex_* 2>/dev/null | grep -q ' apex '; then - msg_ok "Coral TPU device nodes detected with correct group (apex)" - else - msg_warn "apex device node not found yet; a reboot may be required" - fi -} - - - - -pre_install_prompt() { - if ! dialog --title "$(translate 'Coral TPU Installation')" --yesno \ - "\n$(translate 'Installing Coral TPU drivers requires rebooting the server after installation. Do you want to proceed?')" 10 70; then - - exit 0 - fi -} - - -# ============================================================ -# Clean up a broken gasket-dkms package state. -# -# Context: if the user had gasket-dkms installed as a .deb (typical after -# following the Coral docs or via libedgetpu1-std's dependency chain), a -# kernel upgrade on PVE 9 triggers dkms autoinstall → compile fails on -# kernel 6.12+ → dpkg leaves the package half-configured. That broken state -# blocks further apt operations (including our own `apt-get install` below) -# with "E: Sub-process /usr/bin/dpkg returned an error code (1)". -# ============================================================ -cleanup_broken_gasket_dkms() { - # dpkg status codes of interest (first column of `dpkg -l`): - # iF half-configured - # iU unpacked (not configured) - # iH half-installed - # Also catch the case where the package is installed but the DKMS source tree - # is broken (kernel upgrade without rebuild). - local pkg_state - pkg_state=$(dpkg -l gasket-dkms 2>/dev/null | awk '/^[a-zA-Z][a-zA-Z]/ {print $1}' | tail -1) - - if [[ -z "$pkg_state" ]]; then - return 0 # package not present, nothing to clean - fi - - # Any state other than "ii" (installed+configured cleanly) or "rc" - # (removed but config remaining) warrants proactive cleanup. - case "$pkg_state" in - ii|rc) - # Even when state is "ii", a stale DKMS module may exist — drop it to - # ensure our fresh build replaces the old one. - msg_info "$(translate 'Removing any pre-existing gasket-dkms package...')" - dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1 || true - dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true - msg_ok "$(translate 'Pre-existing gasket-dkms package removed.')" - ;; - *) - msg_warn "$(translate 'Detected broken gasket-dkms package state:') ${pkg_state}. $(translate 'Forcing removal...')" - dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1 || true - dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true - dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true - # apt-get install -f resolves any remaining dependency issues left by - # the forced removal above. - apt-get install -f -y >>"$LOG_FILE" 2>&1 || true - msg_ok "$(translate 'Broken gasket-dkms package state recovered.')" - ;; - esac -} - - -# ============================================================ -# Clone the gasket driver sources. -# -# Primary: feranick/gasket-driver — community fork, actively maintained, -# already carries patches for kernel -# 6.10 / 6.12 / 6.13. Preferred. -# Fallback: google/gasket-driver — upstream, stale. Requires the manual -# compatibility patches applied below. -# -# Sets GASKET_SOURCE_USED to "feranick" or "google" so downstream steps know -# whether to apply the local patches. -# ============================================================ -clone_gasket_sources() { - local FERANICK_URL="https://github.com/feranick/gasket-driver.git" - local GOOGLE_URL="https://github.com/google/gasket-driver.git" - - cd /tmp || exit 1 - rm -rf gasket-driver >>"$LOG_FILE" 2>&1 - - msg_info "$(translate 'Cloning Coral driver repository (feranick fork)...')" - if git clone --depth=1 "$FERANICK_URL" gasket-driver >>"$LOG_FILE" 2>&1; then - GASKET_SOURCE_USED="feranick" - msg_ok "$(translate 'feranick/gasket-driver cloned (actively maintained, kernel 6.12+ ready).')" - return 0 - fi - - msg_warn "$(translate 'feranick fork unreachable. Falling back to google/gasket-driver...')" - rm -rf gasket-driver >>"$LOG_FILE" 2>&1 - if git clone --depth=1 "$GOOGLE_URL" gasket-driver >>"$LOG_FILE" 2>&1; then - GASKET_SOURCE_USED="google" - msg_ok "$(translate 'google/gasket-driver cloned (fallback — will apply local patches).')" - return 0 - fi - - msg_error "$(translate 'Could not clone any gasket-driver repository. Check your internet connection and /tmp/coral_install.log')" - exit 1 -} - - -# ============================================================ -# On a failed DKMS build, surface the most relevant lines of make.log -# on-screen so the user (and bug reports) have immediate context without -# having to open the log file manually. -# ============================================================ -show_dkms_build_failure() { - local make_log="/var/lib/dkms/gasket/1.0/build/make.log" - echo "" >&2 - msg_warn "$(translate 'DKMS build failed. Last lines of make.log:')" - if [[ -f "$make_log" ]]; then - # Also append the full log to our install log for post-mortem. - { - echo "---- /var/lib/dkms/gasket/1.0/build/make.log ----" - cat "$make_log" - } >>"$LOG_FILE" 2>&1 - tail -n 50 "$make_log" >&2 - else - echo "$(translate '(make.log not found — DKMS may have failed before invoking make)')" >&2 - fi - echo "" >&2 - echo -e "${TAB}${BL}$(translate 'Full log:')${CL} /tmp/coral_install.log" >&2 - echo "" >&2 -} - -install_coral_host() { - show_proxmenux_logo - : >"$LOG_FILE" - - # Detect running kernel and parse major/minor for conditional patches - local KVER KMAJ KMIN - KVER=$(uname -r) - KMAJ=$(echo "$KVER" | cut -d. -f1) - KMIN=$(echo "$KVER" | cut -d. -f2 | cut -d+ -f1 | cut -d- -f1) - - # Recover from a broken gasket-dkms package state (typical after a kernel - # upgrade on PVE 9) before attempting any apt operations. - cleanup_broken_gasket_dkms - - msg_info "$(translate 'Installing build dependencies...')" - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq >>"$LOG_FILE" 2>&1 - if ! apt-get install -y git dkms build-essential "proxmox-headers-${KVER}" >>"$LOG_FILE" 2>&1; then - msg_error "$(translate 'Error installing build dependencies. Check /tmp/coral_install.log')"; exit 1 - fi - msg_ok "$(translate 'Build dependencies installed.')" - - # Clone sources (feranick fork preferred, google fallback). - # Sets GASKET_SOURCE_USED. - clone_gasket_sources - - cd /tmp/gasket-driver || exit 1 - - # Apply compatibility patches ONLY when using the stale google/gasket-driver - # fallback. feranick/gasket-driver already has equivalent fixes upstream, so - # re-applying them would double-edit (and in some cases break) the sources. - if [[ "$GASKET_SOURCE_USED" == "google" ]]; then - msg_info "$(translate 'Patching source for kernel compatibility...')" - - # Patch 1: no_llseek was removed in kernel 6.5 — replace with noop_llseek - if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 5 ]]; then - sed -i 's/\.llseek = no_llseek/\.llseek = noop_llseek/' src/gasket_core.c - fi - - # Patch 2: MODULE_IMPORT_NS changed to string-literal syntax in kernel 6.13. - # IMPORTANT: applying this patch on kernel < 6.13 causes a compile error. - if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 13 ]]; then - sed -i 's/^MODULE_IMPORT_NS(DMA_BUF);/MODULE_IMPORT_NS("DMA_BUF");/' src/gasket_page_table.c - fi - - msg_ok "$(translate 'Source patched successfully.') (kernel ${KVER})" - else - msg_info2 "$(translate 'Skipping manual patches — feranick fork already supports this kernel.')" - fi - - - msg_info "$(translate 'Preparing DKMS source tree...')" - local GASKET_SRC="/usr/src/gasket-1.0" - # Remove any leftover manual DKMS tree from a previous run (package-level - # cleanup was already handled by cleanup_broken_gasket_dkms above). - dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true - rm -rf "$GASKET_SRC" - cp -r /tmp/gasket-driver/. "$GASKET_SRC" - if ! dkms add "$GASKET_SRC" >>"$LOG_FILE" 2>&1; then - msg_error "$(translate 'DKMS add failed. Check /tmp/coral_install.log')"; exit 1 - fi - msg_ok "$(translate 'DKMS source tree prepared.')" - - - msg_info "$(translate 'Compiling Coral TPU drivers for current kernel...')" - if ! dkms build gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then - show_dkms_build_failure - msg_error "$(translate 'DKMS build failed.')" - exit 1 - fi - if ! dkms install gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then - show_dkms_build_failure - msg_error "$(translate 'DKMS install failed.')" - exit 1 - fi - msg_ok "$(translate 'Drivers compiled and installed via DKMS.') (source: ${GASKET_SOURCE_USED})" - - - ensure_apex_group_and_udev - - msg_info "$(translate 'Loading modules...')" - modprobe gasket >>"$LOG_FILE" 2>&1 || true - modprobe apex >>"$LOG_FILE" 2>&1 || true - if lsmod | grep -q '\bapex\b'; then - msg_ok "$(translate 'Modules loaded.')" - msg_success "$(translate 'Coral TPU drivers installed and loaded successfully.')" - else - msg_warn "$(translate 'Installation finished but drivers are not loaded. Please check dmesg and /tmp/coral_install.log')" - fi - - echo "---- dmesg | grep -i apex (last lines) ----" >>"$LOG_FILE" - dmesg | grep -i apex | tail -n 20 >>"$LOG_FILE" 2>&1 -} - -restart_prompt() { - if whiptail --title "$(translate 'Coral TPU Installation')" --yesno \ - "$(translate 'The installation requires a server restart to apply changes. Do you want to restart now?')" 10 70; then - msg_warn "$(translate 'Restarting the server...')" - reboot - else - msg_success "$(translate 'Completed. Press Enter to return to menu...')" - read -r - fi -} - - -pre_install_prompt -install_coral_host -restart_prompt diff --git a/scripts/menus/hw_grafics_menu.sh b/scripts/menus/hw_grafics_menu.sh index a3d4e0a7..2ea55817 100644 --- a/scripts/menus/hw_grafics_menu.sh +++ b/scripts/menus/hw_grafics_menu.sh @@ -53,7 +53,7 @@ while true; do bash "$LOCAL_SCRIPTS/gpu_tpu/nvidia_installer.sh" ;; 2) - bash "$LOCAL_SCRIPTS/gpu_tpu/install_coral_pve9.sh" + bash "$LOCAL_SCRIPTS/gpu_tpu/install_coral.sh" ;; 3) bash "$LOCAL_SCRIPTS/gpu_tpu/add_gpu_lxc.sh"