Mise à jour du firmware de mon SSD, the hard way

J’ai un SSD pour lequel le constructeur a sorti une mise à jour du firmware. Il s’agit du modèle M550 de chez Crucial qui passe de la version M01 à la version M02. Ce SSD est au format mSata, dans mon routeur qui est un APU1 de PCEngines. Ce routeur n’a pas de sortie vidéo, ne fonctionne pas sous Windows mais sous Linux, et n’a pas de lecteur CD. Quelque chose me dit que cela ne va pas me prendre 5 minutes…

Pour mettre à niveau le firmware, je dispose de deux possibilités :

  • connecter le SSD sur une autre machine plus « standard » et faire la mise à jour depuis cette machine. J’aurais bien opté pour cette solution si j’avais eu un adaptateur SATA > mSATA, et puis cela aurait été moins fun, non ?
  • mettre à jour directement le SSD sans utiliser le tournevis

Crucial met à disposition le package de mise à niveau sous la forme d’une image CD. Je pourrais éventuellement brancher un lecteur CD en USB au routeur, voire même modifier le bootloader pour booter directement sur l’iso mais il est fort à parier que le CD n’a pas été prévu pour un contrôle depuis le port série, voie sans issue donc.

CD, dis-moi qui tu es

Explorons un peu le contenu de cette image.

Contenu de l'iso
Contenu de l’iso

On commence à avoir des choses intéressantes. Le CD contient un noyau linux (vmlinuz) et le bootloader est isolinux, un standard pour booter du linux depuis un CD ou une clé USB. Je serais tombé sur une mise à jour basée sur DOS (Caldera DOS, FreeDOS…), j’aurais été bon pour acheter l’adaptateur.

Examinons le fichier de configuration du bootloader:

DEFAULT sd

LABEL sd
KERNEL /boot/vmlinuz
INITRD /boot/core.gz
APPEND quiet base loglevel=3 waitusb=10 superuser rssd-fw-update rssd-fwdir=/opt/firmware rssd-model=M550   

On remarque un certain nombre de paramètres non standard, ainsi que le initrd.

Hacking the kernel

Pour continuer l’analyse de l’iso, je redémarre redémarre le routeur sur la sdcard (système de secours si le ssd lache) afin de laisser totalement inutilisé le SSD, puis copier le contenu de l’iso dans disons /tmp/crucial/iso . Qui dit initrd dit cpio. Je décompresse donc core.gz puis extrayons le contenu de l’archive cpio dans  /tmp/crucial/root .

Le point d’entrée par défaut est le script /init , vu qu’il n’a pas été redéfini dans les paramètres envoyés au kernel.
Il est d’une extrême simplicité et se résume à :

  • monter /proc
  • lancer /sbin/init

init , s’il s’agit du bon vieux process racine servant à faire fonctionner Linux, va se servir de /etc/init.d afin d’initialiser les différentes partie du système. Certains de ces scripts sont reconnaissable (rc.shutdown) voir inutile à vue (dhcp.sh, settime.sh…). Il y en a un toutefois qui retient mon attention : msd-config.sh car son nom ne m’est absolument pas familier.

Bingo ! Ce script, simple également, récupère les paramètres envoyés au kernel, via le fichier /proc/cmdline , et lancer le processus msecli  avec les paramètres adéquat. Il va notamment utiliser le paramètre kernel rssd-model=M550 , ce qui me conforte que je suis bien sur la bonne piste, le M550 étant pour rappel le modèle de mon SSD.

Au final, le script exécutera la commande suivante : /sbin/msecli -U -i /opt/firmware -m M550 -y .

Allons-y :

apu:/tmp/boot/root# ./sbin/msecli --help
-ash: ./sbin/msecli: not found
apu:/tmp/boot/root# ll ./sbin/msecli
-rwxr-xr-x    1 root     root      635.5K Apr  9 14:22 ./sbin/msecli
apu:/tmp/boot/root# ldd ./sbin/msecli
ldd: ./sbin/msecli: Not a valid dynamic program

Le fichier est bien présent, pourtant il n’est pas possible de l’exécuter. C’est plutôt bizarre…

Go down the rabbit hole

Une analyse sauvage du fichier (sous vim) m’apprend qu’il s’agit d’un exécutable non statique, c’est à dire qu’il utilise des librairies chargées dynamiquement. Sachant que le Linux hôte est Alpine Linux, la libc en vigueur n’est pas l’immonde glibc mais musl. Comme indiqué ci-dessous, la présence de /lib/ld-linux.so.2 trahit le caractère dynamique de l’exécutable. Cela explique également pourquoi le ldd sur l’hôte n’est pas parvenu à parser correctement l’exécutable.

^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@<90>)^E^H4^@^@^@Ì  ^@^@^@^@^@4^@ ^@^H^@(^@^]^@^Z^@^F^@^@^@4^@^@^@4<80>^D^H4<80>^D^H^@^A^@^@^@^A^@^@^E^@^@^@^D^@^@^@^C^@^@^@4^A^@^@4<81>^D^H4<81>^D^H^S^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A^@^@^@^@^@^@^@^@<80>^D^H^@<80>^D^H^T^U       ^@^T^U  ^@^E^@^@^@^@^P^@^@^A^@^@^@^T^U  ^@^T¥^M^H^T¥^M^H^T^C^@^@t1^@^@^F^@^@^@^@^P^@^@^B^@^@^@(^U       ^@(¥^M^H(¥^M^Hà^@^@^@à^@^@^@^F^@^@^@^D^@^@^@^D^@^@^@H^A^@^@H<81>^D^HH<81>^D^H ^@^@^@ ^@^@^@^D^@^@^@^D^@^@^@Påtd^@Ò^H^@^@R^M^H^@R^M^H<84>^N^@^@<84>^N^@^@^D^@^@^@^D^@^@^@Qåtd^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^F^@^@^@^D^@^@^@/lib/ld-linux.so.2^@^@^D^@^@^@^P^@^@^@^A^@^@^@GNU^@^@^@^@^@^B^@^@^@^F^@^@^@ ^@^@^@  ^B^@^@I^@^@^@<80>^@^@^@^L^@^@^@D

Je tente un chroot pour résoudre cela ?

apu:/tmp/boot/root# chroot . sh
apu:/# ldd sbin/msecli
        linux-gate.so.1 (0xe82ba000)
        libpthread.so.0 => /lib/libpthread.so.0 (0xe82a0000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xe81e3000)
        libm.so.6 => /lib/libm.so.6 (0xe81ac000)
        libc.so.6 => /lib/libc.so.6 (0xe808f000)
        /lib/ld-linux.so.2 (0xe82bb000)
        libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xe8079000)
apu:/# msecli --help
USAGE:
msecli <Main Option> -?

    Provides the detailed usage information for the specified main option

NOTE: All options are not supported by all drives

msecli -L [-a] [-d|-P|-f] [-n <device-name>]  [-r ] [-s <out-filename>]

    Lists the basic information for all micron drives available in the system

msecli -S [-a] [-e|-d] [-l <log-type[0 | 1 | 2 | 3 | 6 | 7]>]
    [-t <Self-test type[0 | 1 | 2]>] [-n <device-name>] [-r] [-s <out-filename>]

    Lists the SMART values for the supported parameters for the micron drives
    available in the system.

msecli -M [-i <value> | -w <state-value> | -p <state-value> ] -n <device-name> [-r] [-s <out-filename>]

    To manage the Micron PCIe drives.

msecli -X [-B | -p <password> | -P -p <PSID> ] -n <device-name> [-r] [-s <out-filename>]

    Performs Secure Erase, Sanitize Block Erase, or PSID Revert for the
    specified micron drive

msecli -D -n <device-name> [-r] [-s <out-filename>]

    Displays the Driver and Host Information for the given <device-name>

msecli -T <UBI img-file> [ [-n <device-name> [-b] [-o]] [-i <firmware-slot>] | -d ] [-n <device-name>..] [-r] [-s <out-filename>]

    Updates the UBI image in the specified Micron PCIe drive.

msecli -m -n <device-name>[-d <duration>] [-i <interval>] [-r] [-s <out-filename>]

    Displays the performance data for the specified P320/P420 drive

msecli -G [-e|-E|-g] -n <device-name> [-a] [-d <Event/Error Log id> -t <Time Stamp> ][-r] [-s <out-filename>]

    Displays the firmware event/error logs for the specified P320/P420 drive.

msecli -Z -n <device-name> [-r] [-s <out-filename>]

    Prepares the specified P320/P420 drive for removal.

msecli -V [-a] [-r] [-s <out-filename>]

    Displays the Micron Storage Executive current Version.

msecli -P <zip file name> -n <device-name> [-r] [-s <out-filename>]

    Collects debug data from the system for each micron drive

msecli -U -i <fw-folder-path> [ -m <model-number> | -n <device-name> ] [-r] [-s <out-filename>]

    Performs firmware update with the firmware folder
    for the specified client drive


Copyright (C) 2015 Micron Technology, Inc.

Yeah ! Je tente d’afficher les informations SMART du SSD

apu:/# msecli -S

CMD_STATUS   : Device Error
STATUS_CODE  : 5

Copyright (C) 2015 Micron Technology, Inc.

Oops ? Après quelques minutes de réflexion, je me suis aperçu avoir encore fait le sauvage en omettant de monter /proc . Je corrige cela prestement puis affiche les informations sur le SSD à l’aide du cli :

apu:/# msecli -L

Drive Id             : 0
Device Name          : /dev/sda
Model No             : Crucial_CT128M550SSD3
Serial No            : xxxxxxxxxxxx
FW-Rev               : MU01
Total Size           : 128.04GB
Drive Status         : Drive is in good health
Sata Link Speed      : Gen3 (6.0 Gbps)
Temp(C)              : 61

Drive information is retrieved successfully
CMD_STATUS   : Success
STATUS_CODE  : 0

Copyright (C) 2015 Micron Technology, Inc.

So far so good. Je lance la mise à jour :

apu:/# msecli -U -i /opt/firmware -m M550
This will update all the M550 drives in the system
Are you sure you want to continue(Y|N):Y

Upgrading drive /dev/sda [Serial No. XXXXXXXXXXXX] from firmware MU01 to MU02
.............
Device Name  : /dev/sda
Firmware Update on /dev/sda Succeeded!
CMD_STATUS   : Success
STATUS_CODE  : 0

Copyright (C) 2015 Micron Technology, Inc.

Done ! La mise à jour est passé sans soucis. Je reboot sur le SSD pour le réinitialiser et repasser sur le système principal pour confirmer que tout s’est bien passer.

Parce qu’Un script vaut mille mots

Ci-dessous un résumé scripté des étapes que j’ai du suivre pour mettre à jour le SSD depuis Linux et sans booter sur l’iso :

#!/bin/sh

ROOT_CRUCIAL_ISO=/tmp/crucial/iso
ROOT_CRUCIAL=/tmp/crucial/root

mkdir -p "$ROOT_CRUCIAL"
gunzip "${ROOT_CRUCIAL_ISO}/boot/core.gz
pushd "$ROOT_CRUCIAL"
cpio -F "${ROOT_CRUCIAL_ISO}/boot/core" -i
popd

# mounting /proc is mandatory for the cli to properly work
mount -t proc none "${ROOT_CRUCIAL}/proc/"
mount --rbind /sys "${ROOT_CRUCIAL}/sys/"

# in case where the kernel has grsecurity enabled, we need to allow CAPS in the chroot
echo 0 > /proc/sys/kernel/grsecurity/chroot_caps 2> /dev/null

chroot "$ROOT_CRUCIAL" sh
msecli -U -i /opt/firmware -m M550