Arsitektur Dual-Rootfs (A/B Partitioning)

Pola pembaruan A/B membagi media penyimpanan (eMMC, NAND, atau SSD) menjadi dua set sistem independen: slot A dan slot B. Saat perangkat aktif menjalankan slot A, image firmware baru ditulis langsung ke partisi pasif (slot B). User space tidak pernah memodifikasi partisi yang sedang aktif sebagai rootfs mounted read-only.

Struktur partisi tipikal pada embedded Linux:

  • /dev/mmcblk0p1 (Bootloader & Env): Berisi U-Boot SPL, U-Boot proper, dan environment redundan.
  • /dev/mmcblk0p2 (Kernel/Rootfs A): Image sistem aktif (status: good).
  • /dev/mmcblk0p3 (Kernel/Rootfs B): Image target instalasi OTA (status: trial).
  • /dev/mmcblk0p4 (Data): Partisi persisten read-write, dipisah dari siklus OS.

Keuntungan utama model ini adalah sifat atomik: jika proses flashing terputus oleh mati daya, partisi pasif yang korup diabaikan oleh bootloader. Perangkat tetap boot ke partisi aktif sebelumnya.

Automasi Fallback Bootloader via U-Boot dan RAUC

Bootloader mengontrol slot mana yang dimuat via state machine berbasis counter. RAUC (Robust Auto-Update Controller) berinteraksi langsung dengan U-Boot Environment untuk mencatat status boot.

Logika transisi state U-Boot:

  1. RAUC menandai slot target (misal: Slot B) dengan nilai percobaan: BOOT_B_LEFT=3.
  2. Sistem me-reboot perangkat. U-Boot membaca counter. Jika BOOT_B_LEFT > 0, U-Boot mengurangi counter (BOOT_B_LEFT=2) dan mem-boot Slot B.
  3. Jika kernel panic, watchdog hardware reset, atau systemd hang sebelum validasi berhasil, siklus berulang hingga counter bernilai 0.
  4. Ketika counter habis, U-Boot otomatis fallback mem-boot slot alternatif yang stabil (Slot A).

Contoh skrip environment U-Boot (boot.scr atau compile-time script):

test -n "${BOOT_ORDER}" || setenv BOOT_ORDER "A B"
test -n "${BOOT_A_LEFT}" || setenv BOOT_A_LEFT 3
test -n "${BOOT_B_LEFT}" || setenv BOOT_B_LEFT 3

setenv bootargs_base "console=ttyS0,115200 rootwait rw"

for slot in ${BOOT_ORDER}; do
    if test "${slot}" = "A" && test ${BOOT_A_LEFT} -gt 0; then
        setexpr BOOT_A_LEFT ${BOOT_A_LEFT} - 1
        saveenv
        setenv bootargs "${bootargs_base} root=/dev/mmcblk0p2"
        load mmc 0:2 ${kernel_addr_r} /boot/fitImage
        bootm ${kernel_addr_r}
    fi
    if test "${slot}" = "B" && test ${BOOT_B_LEFT} -gt 0; then
        setexpr BOOT_B_LEFT ${BOOT_B_LEFT} - 1
        saveenv
        setenv bootargs "${bootargs_base} root=/dev/mmcblk0p3"
        load mmc 0:3 ${kernel_addr_r} /boot/fitImage
        bootm ${kernel_addr_r}
    fi
done

# Fallback darurat jika kedua slot gagal
echo "Semua slot rusak. Masuk recovery mode."
run boot_recovery

Konfigurasi RAUC (/etc/rauc/system.conf) untuk sinkronisasi state machine:

[system]
compatible=company-device-v1
bootloader=uboot
mountprefix=/mnt/rauc

[keyring]
path=/etc/rauc/keyring.pem

[slot.rootfs.0]
device=/dev/mmcblk0p2
type=raw
bootname=A

[slot.rootfs.1]
device=/dev/mmcblk0p3
type=raw
bootname=B

Health Check Runtime via systemd Watchdog

Bootloader hanya menjamin kernel dan init system berhasil dijalankan, bukan fungsionalitas aplikasi. Konfirmasi bahwa boot benar-benar sukses (mark as good) harus didelegasikan ke user space melalui health-check service.

Gunakan unit systemd yang bergantung pada hardware watchdog daemon dan status critical service.

File: /etc/systemd/system/firmware-healthcheck.service

[Unit]
Description=Firmware Update Health Check
After=multi-user.target network.target application.service
Requires=application.service
OnFailure=firmware-rollback.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/firmware-validate.sh
TimeoutSec=60

[Install]
WantedBy=multi-user.target

File: /usr/bin/firmware-validate.sh

#!/bin/sh
set -e

# 1. Verifikasi konektivitas bus & IPC aplikasi
systemctl is-active --quiet application.service || exit 1

# 2. Verifikasi socket/API internal merespons
curl --fail --silent http://127.0.0.1:8080/healthz > /dev/null || exit 1

# 3. Tandai slot aktif sebagai sistem yang valid ke RAUC/U-Boot
rauc status mark-good

echo "Slot boot berhasil divalidasi dan ditandai GOOD."
exit 0

Jika script firmware-validate.sh menghasilkan exit status non-zero atau freeze sebelum watchdog ter-refresh, systemd memicu target reboot atau watchdog driver me-reset CPU. Boot counter di U-Boot berkurang satu hingga threshold rollback tercapai.

Observasi dan Deteksi Metrik Rilis

Untuk mendeteksi regresi sebelum seluruh armada perangkat mengalami rollback massal, pantau dua kanal telemetri utama:

1. Early-Boot via Serial Console

Serial UART memberikan visibilitas saat kernel panic sebelum userspace logging aktif. Simpan circular ring-buffer boot serial menggunakan log collector hardware atau pstore driver:

# Verifikasi driver pstore aktif di kernel
mount -t pstore pstore /sys/fs/pstore
ls -la /sys/fs/pstore/console-ramoops-*

2. Userspace Metrics via journalctl

Ekstrak error bootloader and update service:

# Periksa status update terakhir pada runtime
rauc status --detailed

# Tampilkan kegagalan service spesifik pada boot saat ini
journalctl -b 0 -p err..emerg -u firmware-healthcheck.service

Checklist Postmortem Bootloop

Saat terjadi insiden rollback otomatis di perangkat target, lakukan investigasi dengan urutan berikut:

  1. Inspeksi Kernel Panic / OOM: Periksa apakah driver tree (DTS) baru mencoba mengalokasikan resource yang tidak tersedia di hardware rev tertentu.
  2. Validasi U-Boot Env Integrity: Pastikan partisi environment redundan tidak korup akibat crash concurrent writing. Cek via fw_printenv.
  3. Status Mount Partisi Data: Kegagalan automount pada partisi RW data (misal: korupsi SQLite atau permission schema) sering kali menyebabkan systemd crashloop dan menggagalkan health check, memicu rollback palsu.
  4. Cek Shared Lib ABI Mismatch: Pastikan image slot baru tidak mengandalkan symlink ke partisi RW yang masih membawa binary versi lama.