|
#!/usr/bin/env bash |
|
# |
|
# vm_hide.sh — Présente une VM VirtualBox au guest OS comme une machine physique. |
|
# |
|
# Modifie : |
|
# - DMI/SMBIOS (BIOS, System, Board, Chassis) lus par dmidecode |
|
# - Table ACPI (OEM ID/TableId/Creator) |
|
# - Identité du disque (Model, Serial, Firmware) — auto-détection du contrôleur |
|
# - Type de NIC (Intel PRO/1000 MT Server) |
|
# - CPUID brand string + masquage du bit "hypervisor present" |
|
# - Désactivation du provider paravirt (sinon le guest voit "KVM/VBox") |
|
# |
|
# Usage : ./vm_hide.sh <nom_de_la_VM> |
|
# |
|
# La VM doit être ÉTEINTE. Faites un snapshot avant exécution. |
|
|
|
set -euo pipefail |
|
|
|
# --------------------------------------------------------------------------- |
|
# Profil matériel "physique" cible |
|
# |
|
# Les valeurs sont désormais chargées depuis 'profiles.json' (à côté du script) |
|
# via un menu interactif. Voir la section "Sélection du profil" plus bas. |
|
# |
|
# Variables peuplées par load_profile() : |
|
# BIOS_VENDOR BIOS_VERSION BIOS_RELEASE_DATE |
|
# SYS_VENDOR SYS_PRODUCT SYS_VERSION SYS_SERIAL SYS_SKU SYS_FAMILY SYS_UUID |
|
# BOARD_VENDOR BOARD_PRODUCT BOARD_VERSION BOARD_SERIAL BOARD_ASSET_TAG BOARD_LOCATION |
|
# CHASSIS_VENDOR CHASSIS_VERSION CHASSIS_SERIAL CHASSIS_ASSET_TAG CHASSIS_TYPE |
|
# DISK_MODEL DISK_SERIAL DISK_FW |
|
# CPU_BRAND |
|
# NIC_OUI |
|
# |
|
# ATTENTION (rappels conservés depuis l'ancienne version inline) : |
|
# - SYS_VERSION ne doit JAMAIS être purement numérique (ex: "01"). VBox |
|
# stockerait l'extradata comme INTEGER et le BIOS échouerait au démarrage |
|
# avec VERR_CFGM_NOT_STRING. Toujours inclure au moins une lettre. |
|
# set_ed_str() refuse de toute façon les valeurs purement numériques. |
|
# - CHASSIS_TYPE : 23 = Rack Mount Chassis. |
|
# - NIC_OUI : 6 hex sans séparateur (ex: 001B21 = Intel Corporate). Les 3 |
|
# derniers octets de chaque NIC sont conservés -> unicité par VM préservée. |
|
# - VirtualBox n'expose AUCUNE clé CFGM pour overrider les PCI subsystem IDs |
|
# (ni e1000, ni ahci, ni pcbios). Toute tentative -> VERR_CFGM_CONFIG_UNKNOWN_VALUE. |
|
# --------------------------------------------------------------------------- |
|
|
|
# --------------------------------------------------------------------------- |
|
# Vérifications préalables |
|
# --------------------------------------------------------------------------- |
|
if [[ $# -ne 1 ]]; then |
|
echo "Usage : $0 <nom_de_la_VM>" >&2 |
|
exit 1 |
|
fi |
|
|
|
VM_NAME="$1" |
|
|
|
if ! command -v VBoxManage >/dev/null 2>&1; then |
|
echo "Erreur : VBoxManage introuvable dans le PATH." >&2 |
|
exit 1 |
|
fi |
|
|
|
if ! command -v jq >/dev/null 2>&1; then |
|
echo "Erreur : jq introuvable dans le PATH (requis pour lire profiles.json)." >&2 |
|
echo " Installation : apt install jq (Debian/Ubuntu)" >&2 |
|
exit 1 |
|
fi |
|
|
|
if ! VBoxManage showvminfo "$VM_NAME" >/dev/null 2>&1; then |
|
echo "Erreur : la VM '$VM_NAME' n'existe pas." >&2 |
|
echo "Liste : VBoxManage list vms" >&2 |
|
exit 1 |
|
fi |
|
|
|
VM_STATE=$(VBoxManage showvminfo "$VM_NAME" --machinereadable \ |
|
| awk -F= '/^VMState=/ {gsub(/"/,"",$2); print $2}') |
|
if [[ "$VM_STATE" != "poweroff" && "$VM_STATE" != "aborted" && "$VM_STATE" != "saved" ]]; then |
|
echo "Erreur : la VM doit être éteinte (état actuel : $VM_STATE)." >&2 |
|
exit 1 |
|
fi |
|
|
|
# --------------------------------------------------------------------------- |
|
# Sélection du profil matériel depuis profiles.json |
|
# |
|
# profiles.json doit se trouver dans le même dossier que ce script et avoir |
|
# la structure : { "profiles": [ { "id": "...", "label": "...", "bios": {...}, |
|
# "system": {...}, "board": {...}, "chassis": {...}, "disk": {...}, |
|
# "cpu": {...}, "nic": {...} }, ... ] } |
|
# |
|
# Un unique appel jq émet toutes les valeurs en TSV (ordre figé) pour éviter |
|
# 30 invocations successives et garantir la cohérence atomique du profil. |
|
# --------------------------------------------------------------------------- |
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|
PROFILES_FILE="$SCRIPT_DIR/profiles.json" |
|
|
|
if [[ ! -f "$PROFILES_FILE" ]]; then |
|
echo "Erreur : fichier de profils introuvable : $PROFILES_FILE" >&2 |
|
exit 1 |
|
fi |
|
if ! jq empty "$PROFILES_FILE" 2>/dev/null; then |
|
echo "Erreur : $PROFILES_FILE n'est pas un JSON valide." >&2 |
|
exit 1 |
|
fi |
|
|
|
# Liste (id, label) des profils disponibles |
|
mapfile -t PROFILE_IDS < <(jq -r '.profiles[].id' "$PROFILES_FILE") |
|
mapfile -t PROFILE_LABELS < <(jq -r '.profiles[].label' "$PROFILES_FILE") |
|
|
|
if [[ ${#PROFILE_IDS[@]} -eq 0 ]]; then |
|
echo "Erreur : aucun profil défini dans $PROFILES_FILE." >&2 |
|
exit 1 |
|
fi |
|
|
|
echo |
|
echo "Profils disponibles dans $PROFILES_FILE :" |
|
PS3=$'\n'"Choisissez un profil pour la VM '$VM_NAME' (numéro) : " |
|
select choice in "${PROFILE_LABELS[@]}"; do |
|
if [[ -n "${choice:-}" ]]; then |
|
PROFILE_INDEX=$((REPLY - 1)) |
|
PROFILE_ID="${PROFILE_IDS[$PROFILE_INDEX]}" |
|
PROFILE_LABEL="${PROFILE_LABELS[$PROFILE_INDEX]}" |
|
break |
|
fi |
|
echo "Choix invalide. Réessayez (ou Ctrl-C pour annuler)." >&2 |
|
done |
|
|
|
echo "[*] Profil sélectionné : $PROFILE_LABEL (id=$PROFILE_ID)" |
|
|
|
# Extraction atomique de toutes les valeurs (une par ligne), ordre figé. |
|
# NB : on n'utilise PAS @tsv + 'read -d $\t' car le tab est traité comme |
|
# whitespace par 'read' et les champs vides consécutifs seraient fusionnés |
|
# (BOARD_ASSET_TAG="", CHASSIS_ASSET_TAG="" -> tout le reste décalé). |
|
# Une ligne par valeur + mapfile préserve correctement les champs vides. |
|
# Aucun champ de profil ne contient de saut de ligne. |
|
mapfile -t PROFILE_VALS < <(jq -r --arg id "$PROFILE_ID" ' |
|
.profiles[] | select(.id == $id) | |
|
.bios.vendor, .bios.version, .bios.release_date, |
|
.system.vendor, .system.product, .system.version, .system.serial, |
|
.system.sku, .system.family, .system.uuid, |
|
.board.vendor, .board.product, .board.version, .board.serial, |
|
.board.asset_tag, .board.location, |
|
.chassis.vendor, .chassis.version, .chassis.serial, |
|
.chassis.asset_tag, (.chassis.type | tostring), |
|
.disk.model, .disk.serial, .disk.fw, |
|
.cpu.brand, |
|
.nic.oui |
|
' "$PROFILES_FILE") |
|
|
|
if [[ ${#PROFILE_VALS[@]} -ne 26 ]]; then |
|
echo "Erreur : profil '$PROFILE_ID' introuvable ou incomplet dans $PROFILES_FILE" >&2 |
|
echo " (${#PROFILE_VALS[@]} valeurs lues, 26 attendues)." >&2 |
|
exit 1 |
|
fi |
|
|
|
BIOS_VENDOR="${PROFILE_VALS[0]}" |
|
BIOS_VERSION="${PROFILE_VALS[1]}" |
|
BIOS_RELEASE_DATE="${PROFILE_VALS[2]}" |
|
SYS_VENDOR="${PROFILE_VALS[3]}" |
|
SYS_PRODUCT="${PROFILE_VALS[4]}" |
|
SYS_VERSION="${PROFILE_VALS[5]}" |
|
SYS_SERIAL="${PROFILE_VALS[6]}" |
|
SYS_SKU="${PROFILE_VALS[7]}" |
|
SYS_FAMILY="${PROFILE_VALS[8]}" |
|
SYS_UUID="${PROFILE_VALS[9]}" |
|
BOARD_VENDOR="${PROFILE_VALS[10]}" |
|
BOARD_PRODUCT="${PROFILE_VALS[11]}" |
|
BOARD_VERSION="${PROFILE_VALS[12]}" |
|
BOARD_SERIAL="${PROFILE_VALS[13]}" |
|
BOARD_ASSET_TAG="${PROFILE_VALS[14]}" |
|
BOARD_LOCATION="${PROFILE_VALS[15]}" |
|
CHASSIS_VENDOR="${PROFILE_VALS[16]}" |
|
CHASSIS_VERSION="${PROFILE_VALS[17]}" |
|
CHASSIS_SERIAL="${PROFILE_VALS[18]}" |
|
CHASSIS_ASSET_TAG="${PROFILE_VALS[19]}" |
|
CHASSIS_TYPE="${PROFILE_VALS[20]}" |
|
DISK_MODEL="${PROFILE_VALS[21]}" |
|
DISK_SERIAL="${PROFILE_VALS[22]}" |
|
DISK_FW="${PROFILE_VALS[23]}" |
|
CPU_BRAND="${PROFILE_VALS[24]}" |
|
NIC_OUI="${PROFILE_VALS[25]}" |
|
|
|
set_ed() { |
|
# set_ed <key> <value> => définit |
|
# set_ed <key> => supprime (valeur vide) |
|
if [[ $# -eq 2 && -n "$2" ]]; then |
|
VBoxManage setextradata "$VM_NAME" "$1" "$2" |
|
else |
|
VBoxManage setextradata "$VM_NAME" "$1" |
|
fi |
|
} |
|
|
|
# set_ed_str : comme set_ed, mais REFUSE une valeur purement numérique. |
|
# Indispensable pour les champs DMI typés "string" (sinon VBox stocke |
|
# l'extradata comme INTEGER -> VERR_CFGM_NOT_STRING au démarrage). |
|
set_ed_str() { |
|
local key="$1" val="${2:-}" |
|
if [[ -n "$val" && "$val" =~ ^[0-9]+$ ]]; then |
|
echo "Erreur : la valeur '$val' pour '$key' est purement numérique." >&2 |
|
echo " VBox la stockerait comme INTEGER et le BIOS échouerait" >&2 |
|
echo " (VERR_CFGM_NOT_STRING). Ajoutez au moins une lettre." >&2 |
|
exit 1 |
|
fi |
|
set_ed "$key" "$val" |
|
} |
|
|
|
# --------------------------------------------------------------------------- |
|
# 1. NIC : type Intel 82545EM + OUI MAC |
|
# |
|
# Nettoyage préalable des clés PCI subsystem fautives écrites par d'anciennes |
|
# versions du script (VBox n'accepte pas ces clés -> plantage au démarrage). |
|
# --------------------------------------------------------------------------- |
|
echo "[*] Nettoyage des clés PCI subsystem obsolètes éventuelles..." |
|
while IFS= read -r oldkey; do |
|
if [[ -n "$oldkey" ]]; then |
|
echo " - suppression clé obsolète : $oldkey" |
|
VBoxManage setextradata "$VM_NAME" "$oldkey" || true |
|
fi |
|
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \ |
|
| sed -nE 's@^Key: (VBoxInternal/Devices/[^/]+/[0-9]+/Config/(SubsystemVendorId|SubsystemId)),.*@\1@p') |
|
|
|
echo "[*] Configuration des cartes réseau (type 82545EM + OUI MAC)..." |
|
INFO=$(VBoxManage showvminfo "$VM_NAME" --machinereadable) |
|
|
|
# Validation du format de l'OUI : 6 hex exactement, pas de séparateur. |
|
if [[ ! "$NIC_OUI" =~ ^[0-9A-Fa-f]{6}$ ]]; then |
|
echo "Erreur : NIC_OUI doit faire 6 caractères hex (ex: 001B21)." >&2 |
|
exit 1 |
|
fi |
|
# Un OUI valide pour une carte unicast doit avoir le bit 0 du 1er octet à 0. |
|
first_byte=$((16#${NIC_OUI:0:2})) |
|
if (( (first_byte & 0x01) != 0 )); then |
|
echo "Erreur : OUI '$NIC_OUI' a le bit multicast à 1." >&2 |
|
exit 1 |
|
fi |
|
|
|
for i in 1 2 3 4 5 6 7 8; do |
|
nic=$(echo "$INFO" | awk -F= "/^nic${i}=/ {gsub(/\"/,\"\",\$2); print \$2}") |
|
if [[ -z "$nic" || "$nic" == "none" ]]; then |
|
continue |
|
fi |
|
|
|
# a) Type de NIC |
|
VBoxManage modifyvm "$VM_NAME" --nictype${i} 82545EM |
|
|
|
# b) MAC : remplace l'OUI (3 premiers octets) en conservant le NIC ID |
|
current_mac=$(echo "$INFO" \ |
|
| awk -F= "/^macaddress${i}=/ {gsub(/\"/,\"\",\$2); print \$2}") |
|
if [[ -n "$current_mac" && "$current_mac" =~ ^[0-9A-Fa-f]{12}$ ]]; then |
|
new_mac="${NIC_OUI^^}${current_mac:6:6}" |
|
VBoxManage modifyvm "$VM_NAME" --macaddress${i} "$new_mac" |
|
echo " - NIC${i} MAC : $current_mac -> $new_mac" |
|
else |
|
echo " - NIC${i} MAC : format inattendu ('$current_mac'), non modifié." |
|
fi |
|
done |
|
|
|
# --------------------------------------------------------------------------- |
|
# 2. DMI / SMBIOS (lus par dmidecode dans le guest) |
|
# --------------------------------------------------------------------------- |
|
echo "[*] Injection des chaînes DMI/SMBIOS..." |
|
PCBIOS="VBoxInternal/Devices/pcbios/0/Config" |
|
|
|
# --- Champs typés STRING (set_ed_str refuse les valeurs purement numériques) --- |
|
set_ed_str "$PCBIOS/DmiBIOSVendor" "$BIOS_VENDOR" |
|
set_ed_str "$PCBIOS/DmiBIOSVersion" "$BIOS_VERSION" |
|
set_ed_str "$PCBIOS/DmiBIOSReleaseDate" "$BIOS_RELEASE_DATE" |
|
|
|
set_ed_str "$PCBIOS/DmiSystemVendor" "$SYS_VENDOR" |
|
set_ed_str "$PCBIOS/DmiSystemProduct" "$SYS_PRODUCT" |
|
set_ed_str "$PCBIOS/DmiSystemVersion" "$SYS_VERSION" |
|
set_ed_str "$PCBIOS/DmiSystemSerial" "$SYS_SERIAL" |
|
set_ed_str "$PCBIOS/DmiSystemSKU" "$SYS_SKU" |
|
set_ed_str "$PCBIOS/DmiSystemFamily" "$SYS_FAMILY" |
|
set_ed_str "$PCBIOS/DmiSystemUuid" "$SYS_UUID" |
|
|
|
set_ed_str "$PCBIOS/DmiBoardVendor" "$BOARD_VENDOR" |
|
set_ed_str "$PCBIOS/DmiBoardProduct" "$BOARD_PRODUCT" |
|
set_ed_str "$PCBIOS/DmiBoardVersion" "$BOARD_VERSION" |
|
set_ed_str "$PCBIOS/DmiBoardSerial" "$BOARD_SERIAL" |
|
set_ed_str "$PCBIOS/DmiBoardAssetTag" "$BOARD_ASSET_TAG" |
|
set_ed_str "$PCBIOS/DmiBoardLocInChass" "$BOARD_LOCATION" |
|
|
|
set_ed_str "$PCBIOS/DmiChassisVendor" "$CHASSIS_VENDOR" |
|
set_ed_str "$PCBIOS/DmiChassisVersion" "$CHASSIS_VERSION" |
|
set_ed_str "$PCBIOS/DmiChassisSerial" "$CHASSIS_SERIAL" |
|
set_ed_str "$PCBIOS/DmiChassisAssetTag" "$CHASSIS_ASSET_TAG" |
|
|
|
# --- Champs typés INTEGER (valeurs numériques attendues) --- |
|
set_ed "$PCBIOS/DmiBIOSReleaseMajor" "2" |
|
set_ed "$PCBIOS/DmiBIOSReleaseMinor" "18" |
|
set_ed "$PCBIOS/DmiBIOSFirmwareMajor" "5" |
|
set_ed "$PCBIOS/DmiBIOSFirmwareMinor" "13" |
|
set_ed "$PCBIOS/DmiBoardBoardType" "10" # 10 = Motherboard |
|
set_ed "$PCBIOS/DmiChassisType" "$CHASSIS_TYPE" |
|
|
|
# --------------------------------------------------------------------------- |
|
# 3. ACPI : (intentionnellement vide) |
|
# |
|
# Le device ACPI de VirtualBox valide strictement ses clés CFGM et n'expose |
|
# AUCUNE clé du type AcpiOemId/AcpiOemTableId/AcpiCreatorId. Toute tentative |
|
# de les définir via VBoxInternal/Devices/acpi/0/Config/... fait échouer le |
|
# démarrage avec VERR_CFGM_CONFIG_UNKNOWN_VALUE. |
|
# |
|
# Le seul levier officiel est 'CustomTable' (chemin vers un blob ACPI DSDT/SSDT |
|
# pré-compilé), ce qui sort du périmètre de ce script. |
|
# |
|
# Nettoyage des éventuelles clés ACPI fautives écrites par d'anciennes versions. |
|
# --------------------------------------------------------------------------- |
|
echo "[*] Nettoyage des clés ACPI obsolètes éventuelles..." |
|
while IFS= read -r oldkey; do |
|
if [[ -n "$oldkey" ]]; then |
|
echo " - suppression clé obsolète : $oldkey" |
|
VBoxManage setextradata "$VM_NAME" "$oldkey" || true |
|
fi |
|
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \ |
|
| sed -nE 's@^Key: (VBoxInternal/Devices/acpi/0/Config/(AcpiOemId|AcpiOemTableId|AcpiCreatorId|CustomTable)),.*@\1@p') |
|
|
|
# --------------------------------------------------------------------------- |
|
# 4. Disque : auto-détection du contrôleur, du device VBox, port et device |
|
# |
|
# IMPORTANT : ces clés vont au niveau DEVICE (ahci/piix3ide), PAS au niveau |
|
# driver (LUN#x/Config). Sinon le driver DrvVD renvoie |
|
# VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES et le disque ne s'attache pas. |
|
# Seuls ahci (SATA) et piix3ide (IDE) supportent ces clés via extradata. |
|
# Les contrôleurs SCSI/SAS/NVMe/VirtIO ne les exposent pas -> on ignore. |
|
# --------------------------------------------------------------------------- |
|
echo "[*] Détection du contrôleur de stockage hébergeant le disque système..." |
|
|
|
# Nettoyage défensif d'anciennes clés erronées (LUN#x/Config/...) potentiellement |
|
# écrites par une version précédente du script et qui font planter le démarrage. |
|
# NB : le délimiteur sed est '@' car le motif contient '#' (LUN#). |
|
while IFS= read -r oldkey; do |
|
if [[ -n "$oldkey" ]]; then |
|
echo " - suppression clé obsolète : $oldkey" |
|
VBoxManage setextradata "$VM_NAME" "$oldkey" || true |
|
fi |
|
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \ |
|
| sed -nE 's@^Key: (VBoxInternal/Devices/[^/]+/0/LUN#[0-9]+/Config/(ModelNumber|SerialNumber|FirmwareRevision)),.*@\1@p') |
|
|
|
declare -A CTRL_TYPE_MAP=( |
|
[IntelAhci]="ahci" |
|
[PIIX4]="piix3ide" |
|
[PIIX3]="piix3ide" |
|
[ICH6]="piix3ide" |
|
) |
|
|
|
DISK_CTRL_DEV="" |
|
DISK_PORT="" |
|
DISK_DEVNUM="" |
|
DISK_FOUND=0 |
|
|
|
while IFS= read -r line; do |
|
if [[ "$line" =~ ^storagecontrollername([0-9]+)=\"(.+)\"$ ]]; then |
|
idx="${BASH_REMATCH[1]}" |
|
name="${BASH_REMATCH[2]}" |
|
type=$(echo "$INFO" | awk -F= "/^storagecontrollertype${idx}=/ {gsub(/\"/,\"\",\$2); print \$2}") |
|
dev="${CTRL_TYPE_MAP[$type]:-}" |
|
if [[ -z "$dev" ]]; then |
|
echo " - Contrôleur '$name' (type=$type) : non géré pour Model/Serial, ignoré." |
|
continue |
|
fi |
|
# Cherche le 1er disque attaché (exclut DVD/ISO et "none") |
|
attach=$(echo "$INFO" | grep -E "^\"${name}-[0-9]+-[0-9]+\"=\"" \ |
|
| grep -viE '"none"|\.iso"' | head -n1 || true) |
|
if [[ -n "$attach" ]]; then |
|
port=$(echo "$attach" | sed -E 's/^"[^"]+-([0-9]+)-([0-9]+)".*/\1/') |
|
devnum=$(echo "$attach" | sed -E 's/^"[^"]+-([0-9]+)-([0-9]+)".*/\2/') |
|
DISK_CTRL_DEV="$dev" |
|
DISK_PORT="$port" |
|
DISK_DEVNUM="$devnum" |
|
DISK_FOUND=1 |
|
echo " -> Disque sur '$name' (type=$type, device VBox=$dev, port=$port, dev=$devnum)" |
|
break |
|
fi |
|
fi |
|
done <<< "$INFO" |
|
|
|
if [[ $DISK_FOUND -eq 1 ]]; then |
|
case "$DISK_CTRL_DEV" in |
|
ahci) |
|
# SATA : sous-clé Port<n>/... (device toujours 0) |
|
DISK_BASE="VBoxInternal/Devices/ahci/0/Config/Port${DISK_PORT}" |
|
;; |
|
piix3ide) |
|
# IDE : port 0/1 = Primary/Secondary, device 0/1 = Master/Slave |
|
case "${DISK_PORT}-${DISK_DEVNUM}" in |
|
0-0) IDE_SLOT="PrimaryMaster" ;; |
|
0-1) IDE_SLOT="PrimarySlave" ;; |
|
1-0) IDE_SLOT="SecondaryMaster" ;; |
|
1-1) IDE_SLOT="SecondarySlave" ;; |
|
*) IDE_SLOT="" ;; |
|
esac |
|
if [[ -z "$IDE_SLOT" ]]; then |
|
echo " !! Position IDE inattendue port=$DISK_PORT dev=$DISK_DEVNUM, étape ignorée." |
|
DISK_FOUND=0 |
|
else |
|
DISK_BASE="VBoxInternal/Devices/piix3ide/0/Config/${IDE_SLOT}" |
|
fi |
|
;; |
|
esac |
|
fi |
|
|
|
if [[ $DISK_FOUND -eq 1 ]]; then |
|
set_ed "$DISK_BASE/ModelNumber" "$DISK_MODEL" |
|
set_ed "$DISK_BASE/SerialNumber" "$DISK_SERIAL" |
|
set_ed "$DISK_BASE/FirmwareRevision" "$DISK_FW" |
|
else |
|
echo " !! Aucun disque détecté — étape disque ignorée." |
|
fi |
|
|
|
# --------------------------------------------------------------------------- |
|
# 5. CPUID + capacités 64 bits |
|
# |
|
# IMPORTANT : on n'utilise PAS l'ancienne API VBoxInternal/CPUM/HostCPUID/... |
|
# Sur VBox 7.x, ces clés sont obsolètes et leur écriture peut perturber le |
|
# filtrage CPUID, jusqu'à faire perdre l'annonce du long mode (leaf |
|
# 0x80000001 EDX bit 29) -> le guest démarre en i686. |
|
# On utilise la vraie API : VBoxManage modifyvm --cpuidset / --cpuidremove. |
|
# |
|
# Le bit "hypervisor present" (leaf 1, ECX bit 31) est déjà géré par |
|
# --paravirtprovider none : on n'y touche plus manuellement. |
|
# --------------------------------------------------------------------------- |
|
echo "[*] Nettoyage des éventuelles clés HostCPUID obsolètes..." |
|
while IFS= read -r oldkey; do |
|
if [[ -n "$oldkey" ]]; then |
|
echo " - suppression clé obsolète : $oldkey" |
|
VBoxManage setextradata "$VM_NAME" "$oldkey" || true |
|
fi |
|
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \ |
|
| sed -nE 's@^Key: (VBoxInternal/CPUM/HostCPUID/[^,]+),.*@\1@p') |
|
|
|
echo "[*] Activation explicite des capacités 64 bits..." |
|
VBoxManage modifyvm "$VM_NAME" --longmode on --pae on --ioapic on |
|
|
|
echo "[*] Réécriture du brand string CPU via --cpuidset..." |
|
|
|
# Pad/tronque à exactement 48 caractères |
|
brand_padded=$(printf '%-48.48s' "$CPU_BRAND") |
|
|
|
# Encode 4 caractères ASCII en mot 32 bits little-endian -> "DDCCBBAA" (8 hex) |
|
encode_le4() { |
|
local s="$1" |
|
local b0 b1 b2 b3 |
|
printf -v b0 '%02x' "'${s:0:1}" |
|
printf -v b1 '%02x' "'${s:1:1}" |
|
printf -v b2 '%02x' "'${s:2:1}" |
|
printf -v b3 '%02x' "'${s:3:1}" |
|
echo "${b3}${b2}${b1}${b0}" |
|
} |
|
|
|
leaves=(0x80000002 0x80000003 0x80000004) |
|
offset=0 |
|
for leaf in "${leaves[@]}"; do |
|
eax_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4)) |
|
ebx_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4)) |
|
ecx_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4)) |
|
edx_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4)) |
|
# Supprime un éventuel override antérieur avant de poser le nouveau |
|
VBoxManage modifyvm "$VM_NAME" --cpuidremove "$leaf" 2>/dev/null || true |
|
VBoxManage modifyvm "$VM_NAME" --cpuidset "$leaf" "$eax_v" "$ebx_v" "$ecx_v" "$edx_v" |
|
done |
|
|
|
# Désactive le provider paravirt (masque aussi le bit hypervisor de leaf 1 ECX) |
|
VBoxManage modifyvm "$VM_NAME" --paravirtprovider none |
|
|
|
# --------------------------------------------------------------------------- |
|
cat <<EOF |
|
|
|
============================================================ |
|
VM '$VM_NAME' reconfigurée. |
|
- DMI/SMBIOS : $SYS_VENDOR $SYS_PRODUCT (S/N $SYS_SERIAL) |
|
- Disque : $DISK_MODEL (device=$DISK_CTRL_DEV port=$DISK_PORT) |
|
- NIC : Intel 82545EM (PRO/1000 MT Server) |
|
OUI MAC=$NIC_OUI (PCI subsys non modifiable côté VBox) |
|
- CPU : $CPU_BRAND |
|
long mode=on, PAE=on, IOAPIC=on |
|
- Paravirt : désactivé (masque aussi le bit hypervisor de CPUID 1.ECX[31]) |
|
|
|
Démarrez la VM. En cas de souci, restaurez le snapshot. |
|
|
|
Annuler une clé : VBoxManage setextradata "$VM_NAME" <clé> |
|
Tout lister : VBoxManage getextradata "$VM_NAME" enumerate |
|
============================================================ |
|
EOF |
{ "id": "unique-id", // selector key (string, kebab-case recommended) "label": "Human readable label", // displayed in the interactive menu "bios": { "vendor": "...", "version": "...", "release_date": "MM/DD/YYYY" }, "system": { "vendor": "...", "product": "...", "version": "...", "serial": "...", "sku": "...", "family": "...", "uuid": "..." }, "board": { "vendor": "...", "product": "...", "version": "...", "serial": "...", "asset_tag": "", "location": "..." }, "chassis": { "vendor": "...", "version": "...", "serial": "...", "asset_tag": "", "type": 23 }, // 23 = Rack Mount Chassis "disk": { "model": "...", "serial": "...", "fw": "..." }, "cpu": { "brand": "Intel(R) Xeon(R) ... CPU @ X.XXGHz" }, "nic": { "oui": "001B21" } // 6 hex digits, no separator }