#!/bin/bash export LC_ALL=C CE="\033[31m" # Color red CB="\033[1m" # Color bold CR="\033[0m" # Color reset CODE_OK=0 CODE_USAGE=1 # Bad usage CODE_PERM=2 # Invalid rights CODE_VALUE=3 # Bad value CODE_CMD=4 # Missing command CODE_FATAL=5 # Fatal error ARCH="" PRGNAME=$(basename "$0") CONFDIR="/etc/memtest86-efi" CONFFILE="$CONFDIR/memtest86-efi.conf" # shellcheck source=/etc/memtest86-efi/memtest86-efi.conf source "$CONFFILE" || exit $CODE_FATAL shopt -s extglob warn() { echo -e "${CB}${CE}This script is unofficial, written by an AUR (Arch User Repository) user. Use it at YOUR OWN RISK.${CR}" } checkuid() { if [[ $EUID -ne 0 ]]; then echo -e "${CE}You must be root to run $PRGNAME. Aborted.${CR}" >&2 exit $CODE_PERM fi } checkcommand() { if ! $1 &> /dev/null; then echo -e "${CE}Command $1 not found. Aborted.${CR}" >&2 exit $CODE_CMD fi } promptuser() { local defaultvalue="$1" local newvalue="" while true; do read -r newvalue if [[ -n "$defaultvalue" ]] || [[ -n "$newvalue" ]]; then break else echo -en "Please enter a valid value: " >&2 fi done [[ -n "$newvalue" ]] && echo "$newvalue" || echo "$defaultvalue" } _mount_esp() { local esp_partition="$1" local esp_mount_path="$2" if ! grep -q "$esp_mount_path" "/proc/mounts"; then echo -e "ESP ${CB}$esp_partition${CR} is not mounted, mounting..." if ! mount "$esp_partition" "$esp_mount_path"; then echo -e "${CE}Fail to mount $esp_partition on $esp_mount_path. Aborted.${CR}" >&2 exit $CODE_FATAL fi fi } _common_install() { local efidir="$1" local efifile="$2" echo -e "MemTest86 is installed into ${CB}$efidir/${CR} directory." mkdir -pv "$efidir" cp -rv "$MEMTEST86_PATH/"!(*.efi) "$efidir/" # Move files in memtest ESP directory cp -v "$MEMTEST86_PATH/boot$ARCH.efi" "$efidir/$efifile" # Copy and rename .efi file } _write_grub_cfg() { local uuid="$1" local loader_filename="$2" cat > "/etc/grub.d/86_memtest" < "$systemdbootdir/memtest86-efi.conf" <&2 exit $CODE_USAGE fi # Find ESP device number esp_partition=$(fdisk -l | grep "EFI System" | awk '{print $1}' | tail -n1) if [[ -n "$esp_partition" ]]; then echo -en "Press Enter if ${CB}$esp_partition${CR} is your ESP partition, " echo -en "else enter device path manually (like ${CB}/dev/sdXY${CR} or ${CB}/dev/nvme0nXpY${CR}): " else echo -en "Enter device path for your ESP partition (like ${CB}/dev/sdXY${CR} or ${CB}/dev/nvme0nXpY${CR}): " fi esp_partition=$(promptuser "$esp_partition") if [[ ! -b "$esp_partition" ]]; then echo -e "${CE}'$esp_partition' is not a block device. Aborted.${CR}" >&2 exit $CODE_VALUE fi partnumber=$(echo "$esp_partition" | grep -Eo '[0-9]+$') [[ $esp_partition == "/dev/nvme"* ]] && device=$(echo "$esp_partition" | cut -dp -f1) || device=${esp_partition//$partnumber} if [[ -z "$partnumber" ]] || [[ -z "$device" ]]; then echo -e "${CE}Not able to find partition number for '$esp_partition'. Aborted.${CR}" >&2 exit $CODE_VALUE fi # Find ESP mount point esp_mount_path=$(mount | grep "$esp_partition" | awk '{print $3}' | tail -n1) if [[ -n "$esp_mount_path" ]]; then echo -en "Press Enter if ${CB}$esp_mount_path${CR} is your mount point for ESP partition, " echo -en "else enter mount point manually (like ${CB}/boot/efi${CR}): " else echo -en "Enter the mount point for the ESP partition (like ${CB}/boot/efi${CR}): " fi esp_mount_path=$(promptuser "$esp_mount_path") # Prompt for destination directory memtest86_esp_path="/EFI/memtest86" echo -en "Press Enter if ${CB}$memtest86_esp_path${CR} is the directory where you want to install MemTest86, " echo -en "else enter something else (e.g. ${CB}/EFI/tools/memtest86${CR}, or ${CB}/${CR} to install on ESP's root): " memtest86_esp_path=$(promptuser "$memtest86_esp_path") # Check if ESP is mounted or mount it otherwise _mount_esp "$esp_partition" "$esp_mount_path" echo -e "The target is: ${CB}$esp_partition${CR} (mounted on ${CB}$esp_mount_path${CR}).\n" # Get user choice echo "Select $PRGNAME action to perform:" echo -e "${CB}1${CR}: Copy shellx64.efi file on ESP at ${CB}$memtest86_esp_path${CR} (bit safe)" echo -e "${CB}2${CR}: Add a new EFI boot entry (more safe)" echo -e "${CB}3${CR}: Add a boot entry for GRUB2 menu" echo -e "${CB}4${CR}: Add a boot entry for systemd-boot menu" echo -e "${CB}5${CR}: Cancel" choice=0 while [[ $choice -lt 1 ]] || [[ $choice -gt 5 ]]; do read -r choice echo done case $choice in 1) # Install MemTest86 in ${esp_mount_path}${memtest86_esp_path}/ memtest86_esp_bin="shell$ARCH.efi" old_esp_bin="${esp_mount_path}${memtest86_esp_path}/$memtest86_esp_bin" [[ -f "$old_esp_bin" ]] && mv -v "$old_esp_bin" "${old_esp_bin}.bak" # Backup if exist _common_install "${esp_mount_path}${memtest86_esp_path}" "$memtest86_esp_bin" ;; 2) # Install MemTest86 in ${esp_mount_path}${memtest86_esp_path}/ & add an EFI boot entry memtest86_esp_bin="memtest$ARCH.efi" # shellcheck disable=SC1003 loader_filename="$(echo "$memtest86_esp_path/$memtest86_esp_bin" | tr '/' '\\')" checkcommand efibootmgr _common_install "${esp_mount_path}${memtest86_esp_path}" "$memtest86_esp_bin" echo -e "\nAdd a new EFI boot entry..." efibootmgr --create-only \ --write-signature \ --disk "$device" \ --part "$partnumber" \ --label "MemTest86" \ --loader "$loader_filename" ;; 3) # Install MemTest86 in ${esp_mount_path}${memtest86_esp_path}/ & add a file for GRUB2 memtest86_esp_bin="memtest$ARCH.efi" loader_filename="$memtest86_esp_path/$memtest86_esp_bin" checkcommand grub-mkconfig if [[ ! -d "/etc/grub.d/" ]]; then echo -e "${CE}GRUB2 seems not installed on your system. Aborted.${CR}" >&2 exit $CODE_CMD fi _common_install "${esp_mount_path}${memtest86_esp_path}" "$memtest86_esp_bin" echo -e "\nAdd a new configuration file for GRUB..." uuid=$(blkid "$esp_partition" -s UUID -o value) _write_grub_cfg "$uuid" "$loader_filename" grub-mkconfig -o "/boot/grub/grub.cfg" ;; 4) # Install MemTest86 in ${esp_mount_path}${memtest86_esp_path}/ & add a file for systemd-boot memtest86_esp_bin="memtest$ARCH.efi" loader_filename="$memtest86_esp_path/$memtest86_esp_bin" checkcommand bootctl _common_install "${esp_mount_path}${memtest86_esp_path}" "$memtest86_esp_bin" echo -e "\nAdd a new configuration file for systemd-boot..." systemdbootdir="$esp_mount_path/loader/entries" mkdir -pv "$systemdbootdir" _write_systemd_boot_cfg "$systemdbootdir" "$loader_filename" bootctl --path="$esp_mount_path" update ;; *) # Do nothing and quit echo -e "Canceled. MemTest86 will not be installed." exit $CODE_OK ;; esac echo "Writting configuration..." sed -i "s|@ESP_PARTITION@|$esp_partition|" "$CONFFILE" sed -i "s|@ESP_MOUNT_PATH@|$esp_mount_path|" "$CONFFILE" sed -i "s|@MEMTEST86_ESP_PATH@|$memtest86_esp_path|" "$CONFFILE" sed -i "s|@MEMTEST86_ESP_BIN@|$memtest86_esp_bin|" "$CONFFILE" sed -i "s|@CHOICE@|$choice|" "$CONFFILE" sed -i "s|install=0|install=1|" "$CONFFILE" echo "MemTest86 has been installed in ESP." } update() { if [[ $install == 0 ]]; then echo "MemTest86 is not installed in ESP: no update to do." >&2 exit $CODE_OK fi # Check if ESP is mounted or mount it otherwise _mount_esp "$esp_partition" "$esp_mount_path" case $choice in 1|2|3|4) # Update files in ${esp_mount_path}${memtest86_esp_path}/ _common_install "${esp_mount_path}${memtest86_esp_path}" "$memtest86_esp_bin" ;; esac echo "MemTest86 has been updated in ESP." } remove() { if [[ $install == 0 ]]; then echo "MemTest86 is not installed in ESP: no deletion to do." >&2 exit $CODE_OK fi # Check if ESP is mounted or mount it otherwise _mount_esp "$esp_partition" "$esp_mount_path" case $choice in 1) # Remove files in ${esp_mount_path}${memtest86_esp_path}/ echo -e "MemTest86 will be removed from ${CB}${esp_mount_path}${memtest86_esp_path}/${CR}." old_esp_bin="${esp_mount_path}${memtest86_esp_path}/$memtest86_esp_bin" rm -v "${esp_mount_path}${memtest86_esp_path}"/{$memtest86_esp_bin,blacklist.cfg,mt86.png,unifont.bin} [[ -f "${old_esp_bin}.bak" ]] && mv -v "${old_esp_bin}.bak" "$old_esp_bin" ;; 2) # Remove files in ${esp_mount_path}${memtest86_esp_path}/ & delete EFI boot entry checkcommand efibootmgr echo -e "MemTest86 will be removed from ${CB}${esp_mount_path}${memtest86_esp_path}/${CR}." rm -rfv "${esp_mount_path:?}${memtest86_esp_path:?}" echo -e "\nRemove MemTest86 EFI boot entry..." entry=$(efibootmgr | grep MemTest86 | cut -c 5-8) [[ -n $entry ]] && efibootmgr -b "$entry" -B ;; 3) # Remove files in ${esp_mount_path}${memtest86_esp_path}/ & delete file for GRUB2 checkcommand grub-mkconfig echo -e "MemTest86 will be removed from ${CB}${esp_mount_path}${memtest86_esp_path}/${CR}." rm -rfv "${esp_mount_path:?}${memtest86_esp_path:?}" echo -e "\nRemove configuration file for GRUB..." rm -v "/etc/grub.d/86_memtest" grub-mkconfig -o "/boot/grub/grub.cfg" ;; 4) # Remove files in ${esp_mount_path}${memtest86_esp_path}/ & delete file for systemd-boot checkcommand bootctl echo -e "MemTest86 will be removed from ${CB}${esp_mount_path}${memtest86_esp_path}/${CR}." rm -rfv "${esp_mount_path:?}${memtest86_esp_path:?}" echo -e "\nRemove configuration file for systemd-boot..." rm -v "$esp_mount_path/loader/entries/memtest86-efi.conf" bootctl --path="$esp_mount_path" update ;; esac echo "Writting configuration..." sed -i "s|$esp_partition|@ESP_PARTITION@|" "$CONFFILE" sed -i "s|$esp_mount_path|@ESP_MOUNT_PATH@|" "$CONFFILE" sed -i "s|$memtest86_esp_path|@MEMTEST86_ESP_PATH@|" "$CONFFILE" sed -i "s|$memtest86_esp_bin|@MEMTEST86_ESP_BIN@|" "$CONFFILE" sed -i "s|$choice|@CHOICE@|" "$CONFFILE" sed -i "s|install=1|install=0|" "$CONFFILE" echo "MemTest86 has been removed from ESP." } status() { echo -e "${CB}Default MemTest86 directories:${CR}" echo -e "Configuration directory: $CONFDIR/" echo -e "Data directory: $MEMTEST86_PATH/\n" if [[ $install == 0 ]]; then echo -e "${CB}MemTest86 is not installed on your system.${CR}" exit $CODE_USAGE else echo -e "${CB}MemTest86 is installed on your system with following parameters:${CR}" echo -e "ESP device name: $esp_partition" echo -e "ESP mount point: $esp_mount_path" echo -e "MemTest86 installation directory (in ESP): $memtest86_esp_path" echo -e "MemTest86 binary name (in ESP): $memtest86_esp_bin" echo -e "Type of installation: $choice" exit $CODE_OK fi } help() { echo -e "Usage: $PRGNAME ACTION\n" echo -e "Available ACTION:" echo -e "\t-i, --install\t Install MemTest86 in ESP" echo -e "\t-u, --update\t Update an existing installation of MemTest86" echo -e "\t-r, --remove\t Remove MemTest86 from ESP" echo -e "\t-s, --status\t Print and return status" echo -e "\t-h, --help\t Print this help and exit" echo -e "\t-a, --about\t Print informations about $PRGNAME and exit" } about() { echo -e "MemTest86 is a stand alone memory testing software, it cannot be run under an operating system." echo -e "$PRGNAME is a script which helps you to easily use MemTest86 with your UEFI, as an EFI application." } case "$(uname -m)" in i686) ARCH="ia32";; x86_64) ARCH="x64";; aarch64) ARCH="aa64";; *) echo -e "${CE} Unsupported ARCH: $(uname -m). Aborted.${CR}" >&2 ; exit $CODE_FATAL;; esac case "$1" in -i|--install) warn; checkuid; install; exit $CODE_OK;; -u|--update) checkuid; update; exit $CODE_OK;; -r|--remove) checkuid; remove; exit $CODE_OK;; -s|--status) status;; -h|--help) help; exit $CODE_OK;; -a|--about) warn; about; exit $CODE_OK;; *) help; exit $CODE_USAGE;; esac