blob: b8a6fd6638fc9e45b301183c35cb08fa586d88e2 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <crc32.h>
#include <lib/abr/data.h>
#include <lib/storage/gpt_utils.h>
#include <lib/storage/storage.h>
#include <lib/zircon_boot/zbi_utils.h>
#include <lib/zircon_boot/zircon_boot.h>
#include <zircon/hw/gpt.h>
// This file gives an example of using the firmware SDK to implement firmware for booting Zircon on
// a disk with GPT. The GPT disk should contain the following partitions:
//
// zircon_a -- Zircon A slot
// zircon_b -- Zircon B slot
// zircon_r -- Zircon R slot
// vbmeta_a -- verified boot metadata A slot
// vbmeta_b -- verified boot metadata B slot
// vbmeta_r -- verified boot metadata R slot
// durable_boot -- A/B/R metadata
//
// The code in this example is generic and designed to be used as reference. Unless otherwise
// stated, the implementation is recommended to be reused as it is, or as a basis for additioanl
// changes.
//
// A end-to-end runnable demo of this example is available in
// src/firmware/lib/zircon_boot/gpt_boot_demo.cc
// Dependency for libabr Crc32 calculation. We use the crc32 implementation from the firmware SDK
// Device can also choose to use its own implementation.
uint32_t AbrCrc32(const void* buf, size_t buf_size) {
return Crc32((const uint8_t*)buf, (uint32_t)buf_size);
}
// The first step is to construct a `FuchsiaFirmwareStorage` object. It is used as an abstraction of
// the main storage by the firmware SDK to access GPT disk. See
// src/firmware/lib/zircon_boot/include/lib/zircon_boot/storage_utils.h details of its definition.
// For the purpose of demonstration, the following implements a RAM buffer based
// `FuchsiaFirmwareStorage` for concept illustration. Device should replace them with its own
// implementation.
#define GPT_BOOT_STORAGE_BLOCK_SIZE 512
uint8_t ram_disk[GPT_BOOT_STORAGE_BLOCK_SIZE * 128]; // 64k
static bool Read(void* ctx, size_t block_offset, size_t blocks_count, void* dst) {
size_t offset = block_offset * GPT_BOOT_STORAGE_BLOCK_SIZE,
size = blocks_count * GPT_BOOT_STORAGE_BLOCK_SIZE;
memcpy(dst, ram_disk + offset, size);
return true;
}
static bool Write(void* ctx, size_t block_offset, size_t blocks_count, const void* src) {
size_t offset = block_offset * GPT_BOOT_STORAGE_BLOCK_SIZE,
size = blocks_count * GPT_BOOT_STORAGE_BLOCK_SIZE;
memcpy(ram_disk + offset, src, size);
return true;
}
// `FuchsiaFirmwareStorage` requires a scratch buffer to handle unaligned read/write. The buffer
// needs to have at least a capacity of the block size and be aligned according to
// FUCHSIA_FIRMWARE_STORAGE_BUFFER_ALIGNMENT. Below is an example definition of such a buffer.
static uint8_t scratch_buffer[GPT_BOOT_STORAGE_BLOCK_SIZE]
__attribute__((__aligned__(FUCHSIA_FIRMWARE_STORAGE_BUFFER_ALIGNMENT)));
// Defines a global `FuchsiaFirmwareStorage` as boot storage
FuchsiaFirmwareStorage gpt_boot_storage = {
.block_size = GPT_BOOT_STORAGE_BLOCK_SIZE,
.total_blocks = sizeof(ram_disk) / GPT_BOOT_STORAGE_BLOCK_SIZE,
.ctx = NULL,
.scratch_buffer = scratch_buffer,
.read = Read,
.write = Write,
};
// Next we construct a `ZirconBootOps` object. It is the main argument needed by the LoadAndBoot()
// API which we'll use to boot zircon. It contains a set of callbacks for device operations. See
// src/firmware/lib/zircon_boot/include/lib/zircon_boot/zircon_boot.h for details. Below is a
// reference implementation for each callback.
// Read data from the GPT partition.
//
// Note: The `offset`, `size` and `dst` arguments passed to this callback may not necessarily be
// aligned to block or DMA, thus we recommend using the reference implementation whenever possible,
// which internally handles unaligned read.
static bool ReadFromPartition(ZirconBootOps* ops, const char* part, size_t offset, size_t size,
void* dst, size_t* read_size) {
*read_size = size;
GptData* gpt_data = (GptData*)ops->context;
return FuchsiaFirmwareStorageGptRead(&gpt_boot_storage, gpt_data, part, offset, size, dst);
}
// Write data to the a GPT partition.
//
// Note: See note of `ReadFromPartition`
static bool WriteToPartition(ZirconBootOps* ops, const char* part, size_t offset, size_t size,
const void* src, size_t* write_size) {
*write_size = size;
GptData* gpt_data = (GptData*)ops->context;
return FuchsiaFirmwareStorageGptWriteConst(&gpt_boot_storage, gpt_data, part, offset, size, src);
}
// An example buffer for loading a kernel image. Device should replace it with its owned buffer.
static uint8_t kernel_load_buffer[8 * 1024];
// Callback to get a buffer for loading the kernel
static uint8_t* GetKernelLoadBuffer(ZirconBootOps* ops, size_t* size) {
if (*size > sizeof(kernel_load_buffer)) {
printf("kernel is too large\n");
return NULL;
}
*size = sizeof(kernel_load_buffer);
return kernel_load_buffer;
}
// An example buffer for booting a kernel image. Device should replace it with its owned buffer.
// The buffer needs to be aligned according to `ZIRCON_BOOT_KERNEL_ALIGN`
static uint8_t kernel_boot_buffer[8 * 1024] __attribute__((__aligned__(ZIRCON_BOOT_KERNEL_ALIGN)));
// Callback to boot the zircon kernel in `image`.
static void Boot(ZirconBootOps* ops, zbi_header_t* image, size_t capacity) {
// We need to relocate the kernel to a different buffer for booting because zircon requires
// additional scratch memory at the tail when booting. The following API can be used to extract
// the kernel from `image` and relocate it to a different address for booting. It also checks
// whether the buffer can provide sufficient space for scratch memory. Here we relocate the kernel
// to `kernel_boot_buffer`.
size_t boot_buffer_size = sizeof(kernel_boot_buffer);
void* kernel_entry_address = RelocateKernel(image, kernel_boot_buffer, &boot_buffer_size);
if (!kernel_entry_address) {
// In case the buffer is too small, `boot_buffer_size` holds the minimum required size.
printf("Failed to relocate kernel. Required capacity is %zu\n", boot_buffer_size);
ops->reboot(ops, false);
return;
}
printf("\nJumping to kernel @ %p with zbi @ %p...\n\n", kernel_entry_address, image);
// From here the device should jump to `kernel_entry_address` and pass the `image` pointer as the
// first argument.
}
// If device uses firmware A/B/R, that is, if firmware also participates in A/B/R slot switching,
// the following callback should be implemented to make sure that a firmware slot can only boot to
// the corresponding zircon slot, i.e. bootloader_a can only boot zircon_a. If device only has
// Zircon A/B/R, this callback can be ignored.
//
// Zircon A/B/R boot flow:
// first stage firmware --> main firmware (this example) --> zircon_a
// --> zircon_b
// --> zircon_r
//
// Firmware A/B/R boot flow:
// first stage firmware --> bootloader_a (this example) --> zircon_a
// --> bootloader_b (this example) --> zircon_b
// --> bootloader_r (this example) --> zircon_r
//
// Note: For firmware A/B/R, we strongly recommend that implementation of `first stage firmware`
// boots bootloader_a/b/r using the same libabr logic and according to the same A/B/R metadata on
// the `durable_boot` partition. A/B/R metadata should be in read-only mode. We suggest using the
// LoadAbrFirmware() API in the firmware SDK for loading firmware.
// A global variable for storing the current firmware slot. See GptBootMain() for how it should be
// set.
static AbrSlotIndex g_firmware_slot_index;
static bool FirmwareCanBootKernelSlot(ZirconBootOps* ops, AbrSlotIndex kernel_slot, bool* out) {
*out = g_firmware_slot_index == kernel_slot;
return true;
}
// A callback to reboot the device. If the device uses firmware A/B/R and `force_recovery` is true,
// the device should reboot to the bootloader_r slot. The recommended way to reboot when
// `force_recovery`=true is using the libabr AbrSetOneShotRecovery() API. i.e.
static void Reboot(ZirconBootOps* ops, bool force_recovery) {
if (force_recovery) {
AbrOps abr_ops = GetAbrOpsFromZirconBootOps(ops);
if (AbrSetOneShotRecovery(&abr_ops, true) != kAbrResultOk) {
printf("Failed to set one shot recovery\n");
while (1) {
}
}
}
printf("\nDevice reboots...\n\n");
// From here, trigger device reboot using device specific API.
}
// Some features in Fuchsia/Zircon requires certain zbi items to be appended by the firmware. The
// following callback is introduced for this purpose. It will be called by LoadAndBoot() before
// handing off to the kernel.
static bool AddZbiItems(ZirconBootOps* ops, zbi_header_t* image, size_t capacity,
const AbrSlotIndex* slot) {
// Appends the current slot index. This should almost always be appended as it is required to
// enable A/B/R support in Zircon, but in some cases such as RAM-boot there is no slot.
if (slot && AppendCurrentSlotZbiItem(image, capacity, *slot) != ZBI_RESULT_OK) {
return false;
}
// See zircon/kernel/target/arm64/board/pinecrest/boot-shim-config.h for examples of other zbi
// items that are commonly needed.
return true;
}
// The following are verified boot related callbacks. If all of them are implemented, verified boot
// is enabled. If any of them is missing, verified boot is not performed.
// Get the size of a partition.
static bool VerifiedBootGetPartitionSize(ZirconBootOps* ops, const char* part, size_t* out) {
GptData* gpt_data = (GptData*)ops->context;
return FuchsiaFirmwareStorageGetPartitionSize(&gpt_boot_storage, gpt_data, part, out);
}
// Read the anti-rollback index at location `rollback_index_location`. On production devices, this
// should be implemented using a secure storage such as RPMB. For demo purpose, we set it to be a
// no-op and always return 0.
static bool VerifiedBootReadRollbackIndex(ZirconBootOps* ops, size_t rollback_index_location,
uint64_t* out_rollback_index) {
*out_rollback_index = 0;
return true;
}
// Similar to VerifiedBootReadRollbackIndex, set to no-op for demo purpose.
static bool VerifiedBootWriteRollbackIndex(ZirconBootOps* ops, size_t rollback_index_location,
uint64_t rollback_index) {
return true;
}
// Returns whether the device is locked. If the device is unlocked, verified boot is attempted but
// failure is not considered fatal and it will still continue to boot. For this demo, we always
// return true. On production device the lock state should be stored in secure storage such as RPMB.
static bool VerifiedBootReadIsDeviceLocked(ZirconBootOps* ops, bool* out_is_locked) {
*out_is_locked = true;
return true;
}
// The callback returns the permanent attributes, which contains the avb root trust for performing
// verified boot. A common approach is to hardcode the permanent attributes into the firmware
// binary. For the purpose of demonstration, here we hardcode and return the libavb test permanent
// attributes third_party/android/platform/external/avb/test/data/atx_permanent_attributes.bin. It
// can be used to verify vbmeta images signed using libavb test key
// third_party/android/platform/external/avb/test/data/testkey_atx_psk.pem. For production devices
// using a different vbmeta signing key, this needs to be replaced with the corresponding permanent
// attributes associated with it.
static bool VerifiedBootReadPermanentAttributes(ZirconBootOps* ops,
AvbAtxPermanentAttributes* attribute) {
static const unsigned char kPermanentAttributes[] = {
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x9f, 0x35, 0xef, 0x65, 0xc3, 0x29, 0x4c,
0x23, 0x16, 0x10, 0xac, 0x32, 0xc1, 0x3c, 0xd5, 0xc5, 0xab, 0xa1, 0xd9, 0xe7, 0x13, 0x3f,
0x7e, 0xd1, 0xe6, 0x61, 0x5d, 0xa3, 0xa1, 0x60, 0xda, 0x57, 0x4b, 0xb2, 0xe6, 0xf, 0xe1,
0x50, 0xbf, 0x47, 0xff, 0x9, 0xaf, 0xcd, 0x49, 0x2d, 0x82, 0x33, 0x76, 0xa1, 0xfe, 0x28,
0x5f, 0x89, 0x62, 0xb3, 0xc0, 0xf1, 0x11, 0xaf, 0x15, 0x9, 0x27, 0xdb, 0xeb, 0x6, 0x1,
0xa2, 0xf8, 0xb7, 0xd7, 0x9c, 0xe4, 0x88, 0x3a, 0x86, 0x5, 0x2, 0x20, 0x69, 0xb2, 0x36,
0x4c, 0x3e, 0x25, 0x3, 0xed, 0xfc, 0xc, 0x6b, 0x1b, 0xa, 0x4, 0x9c, 0xce, 0x7f, 0x83,
0x82, 0x60, 0xd9, 0x52, 0x7e, 0xc4, 0x35, 0x7b, 0x1c, 0xe6, 0x64, 0x9c, 0x17, 0xec, 0x81,
0xe7, 0x9c, 0xc, 0x8b, 0x4b, 0x7e, 0x48, 0xbe, 0x0, 0x98, 0xa8, 0x20, 0x10, 0x4c, 0x9b,
0xd1, 0x16, 0x5b, 0x25, 0xe9, 0x4e, 0x61, 0xda, 0x7c, 0x63, 0x80, 0x8f, 0xa4, 0xac, 0x74,
0xee, 0xa8, 0x6, 0xac, 0x26, 0xd5, 0x71, 0x6f, 0xaa, 0x73, 0x20, 0x9c, 0x7f, 0xcd, 0x73,
0xd4, 0xa9, 0xa0, 0x7e, 0x5a, 0xb5, 0x61, 0xb0, 0x88, 0xb0, 0xdd, 0xdb, 0x6b, 0x79, 0xd1,
0x5a, 0x9e, 0x54, 0x49, 0x55, 0xc6, 0x89, 0x76, 0x7a, 0xc6, 0x78, 0x99, 0xdc, 0xc9, 0x0,
0x5d, 0x20, 0xf5, 0xfc, 0x8f, 0x39, 0x46, 0xf3, 0x2, 0x96, 0xd, 0x9b, 0xfb, 0xbc, 0xd5,
0xcf, 0x5a, 0x4f, 0xc4, 0xb8, 0xb, 0xd0, 0xf3, 0x19, 0x3c, 0x74, 0x4, 0xd5, 0x94, 0x2c,
0x19, 0x15, 0x64, 0xbf, 0x53, 0x67, 0x97, 0x7b, 0x9e, 0xc6, 0xe0, 0xfb, 0x29, 0x5b, 0x90,
0xad, 0x4, 0x8a, 0xd8, 0x5b, 0xdf, 0x69, 0x9, 0xe4, 0xa5, 0xe9, 0xd9, 0xf, 0xc4, 0xff,
0xae, 0xb7, 0x44, 0x12, 0xae, 0xad, 0x3, 0x97, 0xb8, 0xda, 0xd7, 0x60, 0x37, 0x15, 0xf2,
0xb9, 0xdb, 0x10, 0xf6, 0xe2, 0x26, 0x48, 0x7e, 0x3e, 0x3e, 0xc3, 0x67, 0xd3, 0xa6, 0x2,
0xf7, 0xbc, 0x60, 0xed, 0x45, 0xdf, 0x37, 0xef, 0xf9, 0xea, 0x97, 0x5f, 0x37, 0xb4, 0xeb,
0xb4, 0x91, 0x6c, 0x39, 0x4d, 0xed, 0x52, 0x15, 0x39, 0x47, 0x59, 0x62, 0xde, 0x32, 0x55,
0xe1, 0xd4, 0x15, 0x58, 0x7d, 0x52, 0x41, 0x12, 0x78, 0xee, 0x9f, 0xd, 0xc8, 0x5e, 0x34,
0x91, 0xf9, 0xe7, 0x4c, 0x1e, 0xe7, 0x2f, 0x90, 0x7f, 0xbb, 0xf8, 0x99, 0x3e, 0xc9, 0x79,
0xab, 0x1, 0xdb, 0x24, 0x39, 0xe3, 0xb4, 0xc9, 0x52, 0x73, 0xdb, 0x65, 0x42, 0xa5, 0x2e,
0x43, 0x56, 0xa0, 0x33, 0x8c, 0x1a, 0xb7, 0xa1, 0xed, 0x5c, 0xd0, 0x14, 0x93, 0x8d, 0x23,
0x78, 0x93, 0xcb, 0x3a, 0x3, 0x1f, 0xbb, 0xc6, 0x7b, 0xcd, 0x51, 0x4e, 0xaa, 0x14, 0x1,
0xe9, 0x3, 0x27, 0x13, 0xe2, 0xb2, 0xf8, 0x36, 0xc6, 0xe3, 0xc3, 0x7f, 0xb5, 0x74, 0x20,
0x5e, 0x17, 0xaa, 0x25, 0x7, 0x9b, 0x60, 0xda, 0x83, 0x98, 0xb5, 0x55, 0xae, 0x1b, 0x7a,
0xc1, 0x1f, 0x49, 0x72, 0xe2, 0xcb, 0x6a, 0x11, 0x77, 0xdf, 0x3f, 0xc0, 0x9f, 0x8f, 0x33,
0xc7, 0x10, 0x17, 0x8c, 0xfc, 0xd5, 0xb7, 0x5f, 0x5e, 0xb2, 0xe3, 0x7b, 0x2e, 0xdc, 0xc7,
0x34, 0xdb, 0x31, 0xb0, 0xdc, 0x5d, 0x14, 0x98, 0xb6, 0x1a, 0x2a, 0xd4, 0xb4, 0x4, 0x2c,
0xf0, 0x68, 0x1c, 0x91, 0x60, 0x28, 0xa5, 0x3b, 0x1, 0x98, 0xb6, 0x1e, 0x6e, 0xaa, 0x35,
0x89, 0xc7, 0x94, 0xaa, 0x9e, 0xf0, 0x11, 0x52, 0xf, 0x28, 0xa1, 0x3d, 0xd3, 0x17, 0xb5,
0x8, 0xd8, 0x7a, 0x41, 0xf9, 0x7, 0xe2, 0x87, 0x36, 0xcd, 0x86, 0x3e, 0x79, 0x99, 0x73,
0x50, 0x21, 0x30, 0x0, 0xd2, 0xf3, 0x88, 0x60, 0x32, 0x59, 0x58, 0x2f, 0x55, 0x93, 0x86,
0x56, 0x9a, 0x96, 0xb9, 0xf8, 0xbf, 0x24, 0xc4, 0xba, 0xea, 0xa4, 0x73, 0xb0, 0xc, 0xa6,
0xdb, 0x9, 0x2d, 0xa, 0x36, 0x3f, 0x80, 0xe6, 0x85, 0x7a, 0xf3, 0x1, 0x90, 0x3a, 0xc6,
0xee, 0x2d, 0xa8, 0xce, 0xb4, 0x3f, 0x3a, 0xa6, 0xa3, 0xaf, 0xb9, 0x21, 0xef, 0x40, 0x6f,
0xf4, 0x7f, 0x78, 0x25, 0x55, 0x39, 0x53, 0x67, 0x53, 0x56, 0x8d, 0x81, 0xaf, 0x63, 0x97,
0x68, 0x86, 0x75, 0x66, 0x14, 0x1e, 0xa6, 0x63, 0x1e, 0x2, 0xd0, 0x41, 0xd8, 0x78, 0x75,
0xd, 0x76, 0x77, 0xfa, 0x9c, 0xc5, 0xcc, 0x54, 0x6, 0x25, 0x53, 0x95, 0xeb, 0x4b, 0x7c,
0xb4, 0xc8, 0xbb, 0x5d, 0x6b, 0x6e, 0xf0, 0xd7, 0x8d, 0x3f, 0xdf, 0x93, 0x4c, 0x30, 0x5b,
0x2, 0xf5, 0xe, 0x49, 0x87, 0x60, 0x5f, 0x19, 0x6, 0x24, 0x3d, 0x5d, 0x97, 0x37, 0x61,
0xef, 0x3e, 0xb, 0x9e, 0x85, 0x1c, 0x1a, 0xa6, 0x53, 0x91, 0xd2, 0x2c, 0x18, 0x7c, 0x8f,
0x5b, 0x4a, 0xd5, 0xdd, 0xd9, 0x8a, 0xc3, 0x92, 0x19, 0x54, 0x39, 0xde, 0x33, 0xa1, 0xe1,
0x37, 0x60, 0x3c, 0x3b, 0x3b, 0xc5, 0xed, 0x1b, 0xef, 0x28, 0xf5, 0xdf, 0x44, 0x91, 0xa3,
0x1e, 0x69, 0x6a, 0x35, 0x85, 0x6e, 0x26, 0x46, 0x22, 0x4d, 0x87, 0x92, 0x44, 0x6b, 0x96,
0xdb, 0x75, 0xfe, 0x76, 0x3, 0x60, 0xf7, 0xfd, 0x90, 0x55, 0x7d, 0x6e, 0xd7, 0xaa, 0x44,
0x5, 0xc7, 0x23, 0x37, 0x12, 0xa8, 0xd4, 0xb2, 0x2b, 0xed, 0x41, 0x5f, 0x23, 0x38, 0x7c,
0x16, 0xe6, 0x16, 0xd3, 0x10, 0x19, 0x12, 0xcc, 0x8b, 0x6e, 0xcd, 0xd6, 0xa6, 0x39, 0x8a,
0x1b, 0x24, 0x3f, 0x4d, 0x6f, 0xa6, 0x0, 0x7c, 0xa0, 0xa1, 0x4a, 0xfd, 0xcd, 0x68, 0x50,
0x76, 0xc8, 0x68, 0x9d, 0xeb, 0xdf, 0x24, 0x39, 0xaf, 0x77, 0xb2, 0xb6, 0xaf, 0xb6, 0x34,
0x61, 0x37, 0x6a, 0xfd, 0xc7, 0x6d, 0x2, 0x9f, 0x29, 0xd5, 0x45, 0xf4, 0x89, 0xd8, 0x8c,
0x5c, 0xd3, 0x31, 0xa0, 0x58, 0x19, 0x54, 0x33, 0x46, 0x92, 0xbc, 0x1e, 0x4b, 0x14, 0xac,
0x73, 0xa5, 0x9, 0x9f, 0xb6, 0x2b, 0x2b, 0x73, 0x6b, 0x83, 0x86, 0x13, 0x6e, 0x3, 0xf7,
0xe0, 0x7d, 0x81, 0x47, 0x18, 0x8, 0xea, 0x9, 0x10, 0x24, 0x61, 0x6d, 0x9, 0x1d, 0xb8,
0x8e, 0xba, 0x4, 0x4d, 0xcc, 0xe6, 0xff, 0x28, 0x27, 0x86, 0x38, 0x1, 0x86, 0xbe, 0xf0,
0x5b, 0xf8, 0x1a, 0xd6, 0xde, 0xbe, 0xf9, 0x3b, 0x76, 0x3f, 0x85, 0x82, 0x22, 0x92, 0x4b,
0xe0, 0x76, 0x15, 0xb2, 0x57, 0x5a, 0xb0, 0x64, 0xde, 0xce, 0x93, 0xb8, 0x9f, 0x25, 0x53,
0x8c, 0x5e, 0xdf, 0x29, 0x4e, 0x50, 0x69, 0xfb, 0x7e, 0x33, 0xcb, 0xe, 0x28, 0x1, 0x6c,
0xab, 0xfa, 0xd8, 0x88, 0x2, 0xbc, 0xf2, 0xb1, 0xe, 0x2f, 0x6d, 0x1c, 0x8d, 0xe4, 0x11,
0x23, 0xcc, 0x67, 0x94, 0x7b, 0xf7, 0x8a, 0xf3, 0x68, 0x52, 0xe4, 0x82, 0x25, 0x86, 0xc6,
0x72, 0x19, 0x77, 0x80, 0x28, 0xe3, 0x86, 0xc8, 0x8a, 0xea, 0x3d, 0x54, 0x2f, 0xb, 0x64,
0xa, 0xc5, 0x12, 0x8c, 0xb2, 0x7, 0x72, 0x1b, 0x9, 0x9f, 0x32, 0xbd, 0xa3, 0xb0, 0xc,
0x95, 0xc8, 0x4d, 0xe5, 0xd7, 0x20, 0xdb, 0xf8, 0x34, 0x2a, 0x9d, 0x91, 0x58, 0x38, 0x7a,
0x9c, 0xe0, 0xa3, 0xf, 0x40, 0x9d, 0xff, 0xeb, 0x4b, 0xe2, 0x16, 0x94, 0x32, 0xce, 0xe8,
0x52, 0x75, 0x49, 0xf4, 0x71, 0x13, 0xbc, 0x59, 0x7d, 0x9a, 0xe8, 0x60, 0x29, 0x58, 0x1a,
0x14, 0x94, 0xe6, 0x37, 0x23, 0xad, 0xfe, 0xb, 0xf0, 0x63, 0x60, 0x4f, 0x5d, 0x10, 0x91,
0xf2, 0x50, 0x8e, 0xb, 0x4a, 0x47, 0xc9, 0xc, 0x1f, 0xdc, 0x94, 0x75, 0x25, 0x52, 0x99,
0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
};
memcpy(attribute, kPermanentAttributes, sizeof(AvbAtxPermanentAttributes));
return true;
}
// The callback returns the SHA256 hash of the permanent attributes
static bool VerifiedBootReadPermanentAttributesHash(ZirconBootOps* ops, uint8_t* hash) {
// Pre-computed SHA256 hash for `kPermanentAttributes`
static const uint8_t kPermanentAttributesHash[] = {
0x55, 0x41, 0x9e, 0x1a, 0xff, 0xff, 0x15, 0x3b, 0x58, 0xf6, 0x5c,
0xe8, 0xa5, 0x31, 0x3a, 0x71, 0xd2, 0xa8, 0x3a, 0x00, 0xd0, 0xab,
0xae, 0x10, 0xa2, 0x5b, 0x9a, 0x8e, 0x49, 0x3d, 0x04, 0xf7,
};
memcpy(hash, kPermanentAttributesHash, sizeof(kPermanentAttributesHash));
return true;
}
// See GptBootMain() for details.
ZirconBootFlags WaitForUserForceRecoveryInput(uint32_t timeout_seconds);
// Now integrate everything and start Zircon. The function should be called from the firmware main
// function
ZirconBootResult GptBootMain(void) {
// Opaque GptData that contains GPT partitions info.
GptData gpt_data;
// Checks, repairs GPT on storage, and initializes `gpt_data`.
if (!FuchsiaFirmwareStorageSyncGpt(&gpt_boot_storage, &gpt_data)) {
printf("Failed to process GPT\n");
// If GPT is corrupted, there's nothing we can do. Device can choose to enter a firmware
// recovery mode such as fastboot etc from here.
FuchsiaFirmwareStorageFreeGptData(&gpt_boot_storage, &gpt_data);
return kBootResultErrorGeneric;
}
// Construct a ZirconBootOps from the implemented callbacks.
ZirconBootOps zircon_boot_ops = {
.context = &gpt_data,
.read_from_partition = ReadFromPartition,
.write_to_partition = WriteToPartition,
.boot = Boot,
.firmware_can_boot_kernel_slot = FirmwareCanBootKernelSlot,
.reboot = Reboot,
.add_zbi_items = AddZbiItems,
.verified_boot_get_partition_size = VerifiedBootGetPartitionSize,
.verified_boot_read_rollback_index = VerifiedBootReadRollbackIndex,
.verified_boot_write_rollback_index = VerifiedBootWriteRollbackIndex,
.verified_boot_read_is_device_locked = VerifiedBootReadIsDeviceLocked,
.verified_boot_read_permanent_attributes = VerifiedBootReadPermanentAttributes,
.verified_boot_read_permanent_attributes_hash = VerifiedBootReadPermanentAttributesHash,
.get_kernel_load_buffer = GetKernelLoadBuffer,
};
// In this reference code, we assume firmware A/B/R is used and thus need to store current
// firmware slot in `g_firmware_slot_index` so that FirmwareCanBootKernelSlot() can work
// correctly. We assume and strongly recommend that firmware A/B/R uses the LoadAbrFirmware()
// API for ABR firmware loading. Given this, we can call the GetActiveBootSlot() API to know the
// firmware slot. Note that this needs to be done before any changes is made to the A/B/R
// metadata.
g_firmware_slot_index = GetActiveBootSlot(&zircon_boot_ops);
// Process force recovery request. This should typically be done by listening a key press from
// the serial, or ideally, via a physical mechanism such as button press etc. Device should
// implement similar logic using device-specific APIs.
uint32_t timeout_seconds = 2;
printf("Autoboot in %u seconds. Listening for force recovery input...\n", timeout_seconds);
ZirconBootFlags boot_flags = WaitForUserForceRecoveryInput(timeout_seconds);
// Finally after all the preparation, we can call LoadAndBoot to boot zircon.
ZirconBootResult res = LoadAndBoot(&zircon_boot_ops, boot_flags);
// Should not reach here if booted successfully. In this example, boot is simply a return and we
// need to release the gpt_data to avoid sanitizer build errors.
FuchsiaFirmwareStorageFreeGptData(&gpt_boot_storage, &gpt_data);
return res;
}