Recovery is no longer embedded in boot.img -- that forced recovery.img into the vendor image size check and overflowed the padded 2GB vendor partition. Instead: - common.mk gates recovery with PRODUCT_BUILD_RECOVERY_IMAGE (default false), so the normal build never touches it. Build it on demand with the new `precovery` helper (PAWLET_BUILD_RECOVERY=1 m recoveryimage) and flash the result to the recovery partition. - pawlet-recoveryboot.sh enters recovery via reboot,3 (separate partition) again, and gains a boot-loop failsafe: a /metadata counter (cleared on boot_completed via the .rc) drops the device into recovery after BOOT_MAX consecutive boots that reach early init but never complete. - BootControl comments restored to A/B-via-tryboot (a failed trial reverts to the active slot; recovery is a separate partition via reboot,3).
64 lines
2.5 KiB
C
64 lines
2.5 KiB
C
/*
|
||
* Copyright (C) The Android Open Source Project
|
||
* Copyright (C) 2025 oxmc / PawletOS
|
||
*
|
||
* SPDX-License-Identifier: Apache-2.0
|
||
*
|
||
* On-disk metadata format for PawletOS A/B boot control.
|
||
*
|
||
* Layout in the misc partition (4 MB, GPT label "misc"):
|
||
* 0x0000 – 0x07FF Android Bootloader Control Block (BCB) — do NOT touch
|
||
* 0x0800 – 0x0FFF Unused padding
|
||
* 0x1000 (4096) PawletBootControl — read/written by this HAL
|
||
*
|
||
* Slot numbering: 0 = slot A (boot_a, cmdline: androidboot.slot_suffix=_a)
|
||
* 1 = slot B (boot_b, cmdline: androidboot.slot_suffix=_b)
|
||
*
|
||
* A/B boot flow on RPi:
|
||
* - Normal boot always loads boot_a (GPT partition 1) via the RPi firmware.
|
||
* - To boot slot B once: `reboot tryboot` causes the firmware to use
|
||
* boot_partition=2 for one attempt (see [tryboot] in config.txt); a failed
|
||
* trial reverts to boot_a, so a bad update can't wedge the device.
|
||
* - On a confirmed-successful boot from slot B, markBootSuccessful() copies
|
||
* boot_b → boot_a so that subsequent normal reboots use the new content.
|
||
* - Recovery is a separate image on partition 3, entered via reboot,3
|
||
* (see vendor/pawlet/recoveryboot).
|
||
*/
|
||
#pragma once
|
||
|
||
#include <stdint.h>
|
||
#include <string.h>
|
||
|
||
// Magic value: "PAWL" in little-endian ASCII.
|
||
static constexpr uint32_t PAWLET_MAGIC = 0x4C574150u;
|
||
static constexpr uint32_t PAWLET_VERSION = 1u;
|
||
|
||
// Byte offset inside the misc partition where our struct lives.
|
||
static constexpr off_t PAWLET_MISC_OFFSET = 4096;
|
||
|
||
// How many boot attempts to grant a freshly-activated slot.
|
||
static constexpr int PAWLET_MAX_BOOT_ATTEMPTS = 3;
|
||
|
||
// Per-slot metadata (4 bytes).
|
||
struct PawletSlot {
|
||
uint8_t priority; // 0–15. Higher = preferred. 0 = unbootable.
|
||
uint8_t tries_remaining; // Remaining attempts before slot is unbootable.
|
||
uint8_t successful_boot; // 1 once markBootSuccessful() has been called.
|
||
uint8_t reserved;
|
||
};
|
||
|
||
// Complete on-disk structure placed at PAWLET_MISC_OFFSET.
|
||
// Total size: 24 bytes.
|
||
struct __attribute__((packed)) PawletBootControl {
|
||
uint32_t magic; // PAWLET_MAGIC
|
||
uint32_t version; // PAWLET_VERSION
|
||
uint8_t active_slot; // 0 = A, 1 = B
|
||
uint8_t merge_status; // android.hardware.boot.MergeStatus enum value
|
||
uint8_t reserved[2];
|
||
PawletSlot slots[2]; // [0] = slot A, [1] = slot B
|
||
uint32_t crc32; // CRC32 over all preceding bytes
|
||
};
|
||
|
||
static_assert(sizeof(PawletBootControl) == 24,
|
||
"PawletBootControl layout changed – update tests and partition docs");
|