blob: 2e45b0018666e139979efaacaa8dfb877f89bd2e [file] [log] [blame]
/*
* Copyright (c) 2018 The Fuchsia Authors
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common.h>
#include <dm/uclass.h>
#include <emmc_storage.h>
#include <fs.h>
#include <linux/mtd/partitions.h>
#include <part.h>
#include <version.h>
#include <wdt.h>
#include <zbi/zbi.h>
#include <zircon/boot_args.h>
#include <zircon/vboot.h>
#include <zircon/zircon.h>
#include <lib/zircon_boot/zbi_utils.h>
#include <asm/arch/reboot.h>
#include <asm/arch/secure_apb.h>
#include <asm/io.h>
#include "factory.h"
#include "override_reboot_mode.h"
#define PDEV_VID_GOOGLE 3
#define PDEV_PID_NELSON 10
// Elaine has 1 cluster of 4 cpus.
#define ELAINE_NUM_CPU 4
#define NVRAM_LENGTH (8 * 1024)
int append_zbi_item_or_log(void *zbi, size_t capacity, uint32_t type,
uint32_t extra, const void *payload, size_t size)
{
if (size > 0xFFFFFFFFU) {
printf("Error: ZBI item 0x%08X/0x%X is too large (%zu bytes)\n",
type, extra, size);
return -1;
}
zbi_result_t result = zbi_create_entry_with_payload(
zbi, capacity, type, extra, 0 /*flags*/, payload, size);
if (result != ZBI_RESULT_OK) {
printf("Error: Failed to add ZBI item 0x%08X/0x%X (code %d)\n",
type, extra, result);
return -1;
}
return 0;
}
static const zbi_mem_range_t mem_config[] = {
{
.type = ZBI_MEM_RANGE_RAM,
.length = 0x80000000, // 2 GB
},
{
.type = ZBI_MEM_RANGE_PERIPHERAL,
.paddr = 0xf5800000,
.length = 0x0a800000,
},
// secmon_reserved:linux,secmon
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x05000000,
.length = 0x2400000,
},
// logo_reserved:linux,meson-fb
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x5f800000,
.length = 0x800000,
},
};
static const dcfg_simple_t uart_driver = {
.mmio_phys = 0xff803000,
.irq = 225,
};
static const dcfg_arm_gicv2_driver_t gicv2_driver = {
.mmio_phys = 0xffc00000,
.gicd_offset = 0x1000,
.gicc_offset = 0x2000,
.gich_offset = 0x4000,
.gicv_offset = 0x6000,
.ipi_base = 5,
};
static const dcfg_arm_psci_driver_t psci_driver = {
.use_hvc = false,
.reboot_args = { 1, 0, 0 },
.reboot_bootloader_args = { 4, 0, 0 },
.reboot_recovery_args = { 2, 0, 0 },
};
static const dcfg_arm_generic_timer_driver_t timer_driver = {
.irq_phys = 30,
};
static const zbi_platform_id_t platform_id = {
.vid = PDEV_VID_GOOGLE,
.pid = PDEV_PID_NELSON,
.board_name = "nelson",
};
#define WDT_CTRL 0xffd0f0d0
#define WDT_PET 0xffd0f0dc
#define WATCHDOG_TIMEOUT_SECONDS 5
#define SECONDS_TO_NANOSECONDS 1000000000LL
static dcfg_generic_32bit_watchdog_t watchdog_driver = {
.pet_action = {
.addr = WDT_PET,
.clr_mask = 0xffffffff,
.set_mask = 0x00000000,
},
.enable_action = {
.addr = WDT_CTRL,
.clr_mask = 0x00000000,
.set_mask = 0x00040000,
},
.disable_action = {
.addr = WDT_CTRL,
.clr_mask = 0x00040000,
.set_mask = 0x00000000,
},
.watchdog_period_nsec =
WATCHDOG_TIMEOUT_SECONDS * SECONDS_TO_NANOSECONDS,
.flags = KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED,
};
/**
* disable_watchdog_petting() - disable petting of watchdog
*
* The function disables the petting function of watchdog driver by
* faking a pet_action.addr and setting the set_mask anc clr_mask
* field to be 0.
*
* It is mainly used for testing watchdog after booting into zircon.
* i.e., whether it triggers a reboot if zircon is not able to pet it in time
*/
void disable_watchdog_petting(void)
{
watchdog_driver.pet_action.addr = watchdog_driver.enable_action.addr;
watchdog_driver.pet_action.set_mask = 0; // no set
watchdog_driver.pet_action.clr_mask = 0; // no clear
}
static int add_reboot_reason(zbi_header_t *zbi, size_t capacity)
{
// See cmd/amlogic/cmd_reboot.c
const uint32_t reboot_mode_val = get_rebootmode_value();
zbi_hw_reboot_reason_t reboot_reason;
switch (reboot_mode_val) {
case AMLOGIC_COLD_BOOT:
reboot_reason = ZBI_HW_REBOOT_COLD;
break;
case AMLOGIC_NORMAL_BOOT:
case AMLOGIC_FACTORY_RESET_REBOOT:
case AMLOGIC_UPDATE_REBOOT:
case AMLOGIC_FASTBOOT_REBOOT:
case AMLOGIC_SUSPEND_REBOOT:
case AMLOGIC_HIBERNATE_REBOOT:
case AMLOGIC_BOOTLOADER_REBOOT:
case AMLOGIC_SHUTDOWN_REBOOT:
case AMLOGIC_RPMBP_REBOOT:
case AMLOGIC_QUIESCENT_REBOOT:
case AMLOGIC_CRASH_REBOOT:
case AMLOGIC_KERNEL_PANIC:
case AMLOGIC_RECOVERY_QUIESCENT_REBOOT:
reboot_reason = ZBI_HW_REBOOT_WARM;
break;
case AMLOGIC_WATCHDOG_REBOOT:
reboot_reason = ZBI_HW_REBOOT_WATCHDOG;
break;
default:
reboot_reason = ZBI_HW_REBOOT_UNDEFINED;
break;
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_HW_REBOOT_REASON,
0, &reboot_reason, sizeof(reboot_reason));
}
#define WATCHDOG_DEV_NAME "watchdog"
static void start_meson_watchdog(void)
{
struct udevice *wdt_dev;
int ret = uclass_get_device_by_name(UCLASS_WDT, WATCHDOG_DEV_NAME,
&wdt_dev);
if (ret < 0) {
printf("failed to get meson watchdog\n");
return;
}
printf("starting meson watchdog\n");
// Note: the watchdog uclass expects a timeout in milliseconds, but the
// Amlogic code uses seconds instead.
wdt_start(wdt_dev, WATCHDOG_TIMEOUT_SECONDS, 0);
wdt_reset(wdt_dev);
}
static int add_cpu_topology(zbi_header_t *zbi, size_t capacity)
{
zbi_topology_node_t nodes[ELAINE_NUM_CPU + 1];
zbi_topology_cluster_t cluster = {
.performance_class = 1,
};
zbi_topology_node_t cluster_node = {
.entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
.entity = { .cluster = cluster },
};
nodes[0] = cluster_node;
for (int cpu = 0; cpu < ELAINE_NUM_CPU; cpu++) {
zbi_topology_arm_info_t arm_info = {
.cluster_1_id = cpu,
.cpu_id = 0,
.gic_id = cpu,
};
zbi_topology_processor_t processor = {
.logical_ids = { cpu },
.logical_id_count = 1,
.flags =
(cpu == 0) ? ZBI_TOPOLOGY_PROCESSOR_PRIMARY : 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = { arm_info },
};
zbi_topology_node_t node = {
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = 0,
.entity = { .processor = processor },
};
nodes[cpu + 1] = node;
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_CPU_TOPOLOGY,
sizeof(zbi_topology_node_t), &nodes,
sizeof(nodes));
}
#define FACTORY_MAC_ADDR_BUFF_LEN 30
static int add_mac_addresses(zbi_header_t *zbi, size_t capacity)
{
char buffer[FACTORY_MAC_ADDR_BUFF_LEN];
u64 fullmac[2];
u8 mac_addr[6];
loff_t len_read;
int mac_num, i;
if (fs_set_blk_dev(ELAINE_FACTORY_IF, ELAINE_FACTORY_PART,
FS_TYPE_EXT)) {
printf("set_blk_dev %s-%s failed.\n", ELAINE_FACTORY_IF,
ELAINE_FACTORY_PART);
return -1;
}
if (fs_read(ELAINE_FACTORY_MAC_ADDR_FILE, (ulong)buffer, 0,
FACTORY_MAC_ADDR_BUFF_LEN, &len_read)) {
printf("Failed to read Mac Addresses from Factory partition\n");
return -1;
}
if (len_read != ELAINE_FACTORY_MAC_ADDR_FILE_LEN) {
printf("Factory MAC Addr File length (%lld) incorrect.\n",
len_read);
return -1;
}
buffer[len_read] = '\0';
/*
* "buffer" should now contain two hex strings separated by \n,
* for a total of 25 bytes. Separate into 2 C strings and convert.
*/
buffer[len_read / 2] = '\0';
fullmac[0] = simple_strtoull(buffer, NULL, 16);
fullmac[1] = simple_strtoull(&buffer[(len_read / 2) + 1], NULL, 16);
for (mac_num = 0; mac_num < ARRAY_SIZE(fullmac); mac_num++) {
for (i = ARRAY_SIZE(mac_addr) - 1; i >= 0; i--) {
mac_addr[i] = (u8)(fullmac[mac_num] & 0xff);
fullmac[mac_num] >>= 8;
}
if (append_zbi_item_or_log(zbi, capacity,
ZBI_TYPE_DRV_MAC_ADDRESS, mac_num,
mac_addr, sizeof(mac_addr))) {
return -1;
}
}
return 0;
}
static int add_serial_number(zbi_header_t *zbi, size_t capacity)
{
char *s;
if (!(s = env_get("serial#")) || (*s == '\0')) {
printf("%s: Failed to retrieve serial number.\n", __func__);
return -1;
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_SERIAL_NUMBER, 0,
s, strlen(s));
}
// Buffer to keep staged ZBI files.
// We store these in their own ZBI container, which takes up a little extra
// space due to ZBI headers, but makes copying them over to the actual ZBI
// trivial.
// The test team needs to be able to put up to 3 SSH keys on a single device
// so make sure it's at least big enough for that.
static uint8_t zbi_files[4096] __attribute__((aligned(ZBI_ALIGNMENT)));
static bool zbi_files_initialized = false;
int zircon_stage_zbi_file(const char *name, const uint8_t *data,
size_t data_len)
{
size_t name_len = strlen(name);
if (name_len > U8_MAX) {
printf("ZBI filename too long");
return -1;
}
// Payload = (name_length_byte + name + data), size must fit in a uint32_t.
size_t payload_length = 1 + name_len + data_len;
if (payload_length > U32_MAX || payload_length < data_len) {
printf("ZBI file data too large");
return -1;
}
if (!zbi_files_initialized) {
zbi_result_t result = zbi_init(zbi_files, sizeof(zbi_files));
if (result != ZBI_RESULT_OK) {
printf("Failed to initialize zbi_files: %d\n", result);
return -1;
}
zbi_files_initialized = true;
}
void *payload_as_void = NULL;
zbi_result_t result =
zbi_create_entry(zbi_files, sizeof(zbi_files),
ZBI_TYPE_BOOTLOADER_FILE, 0, 0, payload_length,
&payload_as_void);
if (result != ZBI_RESULT_OK) {
printf("Failed to create ZBI file entry: %d\n", result);
return -1;
}
uint8_t *payload = payload_as_void;
payload[0] = name_len;
memcpy(&payload[1], name, name_len);
memcpy(&payload[1 + name_len], data, data_len);
return 0;
}
static int add_staged_zbi_files(zbi_header_t *zbi, size_t capacity)
{
if (!zbi_files_initialized) {
return 0;
}
zbi_result_t result = zbi_extend(zbi, capacity, zbi_files);
if (result != ZBI_RESULT_OK) {
printf("Failed to add staged ZBI files: %d\n", result);
return -1;
}
printf("Added staged ZBI files with total ZBI size %u\n",
((zbi_header_t *)zbi_files)->length);
return 0;
}
static int add_current_slot(zbi_header_t *zbi, size_t capacity)
{
char buffer[32];
int length = snprintf(buffer, sizeof(buffer), "zvb.current_slot=%s",
g_tpl_slot);
if (length >= sizeof(buffer)) {
printf("current_slot string too large: '%s'\n", buffer);
return -1;
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_CMDLINE, 0,
buffer, length + 1);
}
// HW RNG is reseeded every 40 microseconds.
#define HW_RNG_RESEEDING_INTERVAL_MICROS 40
static const dcfg_amlogic_rng_driver_t rng_driver = {
.rng_data_phys = (uint64_t)P_RNG_USR_DATA,
.rng_status_phys = (uint64_t)P_RNG_USR_STS,
.rng_refresh_interval_usec = HW_RNG_RESEEDING_INTERVAL_MICROS,
};
// bitmask for determining whether the RNG_USR_DATA register is ready.
// // This mask should be applied to the RNG_USR_STS register.
// // 0: The RNG_USR_DATA register is not ready to be read from.
// // 1: The RNG_USR_DATA register is ready to be read from.
#define USR_RAND32_RDY 0x1
// Size of the CMDLINE entropy string.
#define CMDLINE_ENTROPY_SIZE 1024
// Random bits to pass to zircon.
#define CMDLINE_ENTROPY_BITS 256
// Deadline for time waiting for the RNG_USR_STS register to be ready.
// This is a very generous value, given that after reading from
// the hw rng, we should expect it to be available after
// HW_RNG_RESEEDING_INTERVAL_MICROS.
#define ENTROPY_COLLECTION_DEADLINE_MICROS 100000
#define ENTROPY_BITS_PER_CHAR 4
static const char zircon_entropy_arg[] = "kernel.entropy-mixin=";
static char entropy_cmdline[CMDLINE_ENTROPY_SIZE] = { 0 };
_Static_assert(CMDLINE_ENTROPY_BITS % 32 == 0,
"Requested entropy must be a multiple of 32");
_Static_assert((CMDLINE_ENTROPY_BITS / ENTROPY_BITS_PER_CHAR) +
sizeof(zircon_entropy_arg) <
CMDLINE_ENTROPY_SIZE,
"Requested entropy doesn't fit in cmdline.");
// fills an 8 char buffer with the lowercase hex representation of the given
// value.
// WARNING this does not add a '\0' to the end of the buffer.
static inline void uint32_to_hex(uint32_t val, char buf[static 8])
{
static const char hex[] = "0123456789abcdef";
int i = 0;
for (i = 7; i >= 0; i--) {
buf[i] = hex[val & 0xF];
val >>= 4;
}
}
// Reads a value from the userspace hardware random number generator.
//
// This assumes that the drng has been previously seeded. Callers should
// poll P_RNG_USR_STS beforehand to make sure that a reseed occurred.
static inline uint32_t read_hw_rng(void)
{
return readl(P_RNG_USR_DATA);
}
static void *mandatory_memset(void *dst, int c, size_t n)
{
volatile unsigned char *out = dst;
size_t i = 0;
for (i = 0; i < n; ++i) {
out[i] = (unsigned char)c;
}
return dst;
}
// Gathers CMDLINE_ENTROPY_BITS bits of entropy from the hardware random
// number generator and appends it as a ZBI_TYPE_CMDLINE for zircon to seed
// its cprng.
//
// This function will sleep a maximum of HW_RNG_RESEEDING_INTERVAL_MICROS *
// (CMDLINE_ENTROPY_BITS / 32) + ENTROPY_COLLECTION_DEADLINE_MICROS.
// If the function can't gather enough entropy after after exceeding
// the deadline, it will return -1 and the cmdline zbi will not be added.
static int add_cmdline_entropy(zbi_header_t *zbi, size_t capacity)
{
strcpy(entropy_cmdline, zircon_entropy_arg);
char *entropy = entropy_cmdline + strlen(zircon_entropy_arg);
uint32_t elapsed_time_us = 0;
int i = 0;
for (i = 0; i < CMDLINE_ENTROPY_BITS; i += 32) {
// Reading a 1 in the RNG_USR_STS means that the
// hw rng has been reseeded. Wait until we see a 1,
// without exceeding the global deadline.
while ((readl(P_RNG_USR_STS) & USR_RAND32_RDY) != 1) {
udelay(1);
elapsed_time_us++;
if (elapsed_time_us >
ENTROPY_COLLECTION_DEADLINE_MICROS) {
return -1;
}
}
uint32_to_hex(read_hw_rng(), entropy);
entropy += 8;
// According to the docs, this should guarantee a reseed.
udelay(HW_RNG_RESEEDING_INTERVAL_MICROS);
}
*entropy = '\0';
int ret = append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_CMDLINE, 0,
entropy_cmdline,
sizeof(entropy_cmdline));
mandatory_memset(entropy_cmdline, '\0', sizeof(entropy_cmdline));
return ret;
}
static int add_panel_type(zbi_header_t *zbi, size_t capacity)
{
enum {
// These values are shared with the Fuchsia GT6853 touch driver,
// and must be kept in sync.
kPanelTypeKdFiti9364 = 1,
kPanelTypeBoeFiti9364 = 2,
kPanelTypeInxFiti9364 = 3,
kPanelTypeKdFiti9365 = 4,
kPanelTypeBoeFiti9365 = 5,
kPanelTypeBoeSit7703 = 6,
};
const char *panel_type = env_get("panel_type");
if (!panel_type) {
return -1;
}
uint32_t panel_type_id = 0;
if (!strcmp(panel_type, "boe_fiti9364_7")) {
panel_type_id = kPanelTypeBoeFiti9364;
} else if (!strcmp(panel_type, "kd_fiti9364_7")) {
panel_type_id = kPanelTypeKdFiti9364;
} else if (!strcmp(panel_type, "inx_fiti9364_7")) {
panel_type_id = kPanelTypeInxFiti9364;
} else if (!strcmp(panel_type, "boe_fiti9365_7")) {
panel_type_id = kPanelTypeBoeFiti9365;
} else if (!strcmp(panel_type, "kd_fiti9365_7")) {
panel_type_id = kPanelTypeKdFiti9365;
} else if (!strcmp(panel_type, "boe_sit7703_7")) {
panel_type_id = kPanelTypeBoeSit7703;
} else {
// This is expected on P0/P1 boards, which use different
// panel_type strings.
return 0;
}
// The touch controller will be left with a default config if this
// fails, but it should still work.
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_DRV_BOARD_PRIVATE,
0, &panel_type_id, sizeof(panel_type_id));
}
const char *factory_file_list[] = {
// From vendor/google/app/factory_configs/nelson/fuchsia.factory.MiscFactoryStoreProvider.config
"hw.txt",
"locale_list.txt",
"mac_addr",
"serial.txt",
"rgb_b_led_cal.txt",
"rgb_b_off_cal.txt",
"rgb_b_on_cal.txt",
"rgb_c_led_cal.txt",
"rgb_c_off_cal.txt",
"rgb_c_on_cal.txt",
"rgb_g_led_cal.txt",
"rgb_g_off_cal.txt",
"rgb_g_on_cal.txt",
"rgb_r_led_cal.txt",
"rgb_r_off_cal.txt",
"rgb_r_on_cal.txt",
"sounds/unmute.opus",
"sounds/err_no_lang_pack.opus",
"sounds/error.opus",
"sounds/mic_muted_warning.opus",
"sounds/welcome.opus",
"sounds/mute.opus",
"sounds/fdr.opus",
// From vendor/google/app/factory_configs/nelson/fuchsia.factory.AlphaFactoryStoreProvider.config
"nf.key",
// From vendor/google/app/factory_configs/nelson/fuchsia.factory.CastCredentialsFactoryStoreProvider.config
"client.crt",
"client.key",
// From vendor/google/app/factory_configs/nelson/fuchsia.factory.PlayReadyFactoryStoreProvider.config
"pr3.crt",
"pr3.key",
// From vendor/google/app/factory_configs/nelson/fuchsia.factory.WidevineFactoryStoreProvider.config
"wv.key",
// From vendor/google/app/factory_configs/nelson/fuchsia.factory.WeaveFactoryStoreProvider.config
"weave.crt",
"weave_device_id",
"weave.key",
"weave_pairing_code",
};
bool read_factory(void *context, const char *name, size_t capacity,
void *output, size_t *out_len)
{
if (fs_set_blk_dev(ELAINE_FACTORY_IF, ELAINE_FACTORY_PART,
FS_TYPE_EXT)) {
printf("set_blk_dev %s-%s failed.\n", ELAINE_FACTORY_IF,
ELAINE_FACTORY_PART);
return false;
}
loff_t len_read;
if (fs_read(name, (ulong)output, 0, capacity, &len_read)) {
printf("Failed to read file %s from Factory partition\n", name);
return false;
}
*out_len = (size_t)len_read;
return true;
}
static int append_factory_files(zbi_header_t *zbi, size_t capacity)
{
return AppendBootfsFactoryFiles(zbi, capacity, factory_file_list,
ARRAY_SIZE(factory_file_list),
read_factory, NULL) == ZBI_RESULT_OK ?
0 :
1;
}
/* We do this a lot in zircon_fixup_zbi(), a macro helps with boilerplate.
* Generally it's best to fail loudly if we can't add a ZBI item; we want to
* know immediately if ZBI items are missing, not later when random things
* stop working in the OS. */
#define RETURN_IF_NONZERO(val) \
do { \
int ret = (val); \
if (ret != 0) { \
return ret; \
} \
} while (0)
int zircon_fixup_zbi_no_slot(zbi_header_t *zbi, size_t capacity)
{
// allocate crashlog save area before 0x5f800000-0x60000000 reserved area
zbi_nvram_t nvram;
nvram.base = 0x5f800000 - NVRAM_LENGTH;
nvram.length = NVRAM_LENGTH;
RETURN_IF_NONZERO(append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_NVRAM,
0, &nvram, sizeof(nvram)));
// add memory configuration
RETURN_IF_NONZERO(
append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_MEM_CONFIG, 0,
&mem_config, sizeof(mem_config)));
// add kernel drivers
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_UART,
&uart_driver, sizeof(uart_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2,
&gicv2_driver, sizeof(gicv2_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_PSCI,
&psci_driver, sizeof(psci_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GENERIC_TIMER,
&timer_driver, sizeof(timer_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER,
KDRV_GENERIC_32BIT_WATCHDOG, &watchdog_driver,
sizeof(watchdog_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_RNG,
&rng_driver, sizeof(rng_driver)));
char uboot_ver[] = "bootloader.name=" U_BOOT_VERSION_STRING;
// Zircon's cmdline parameters cannot contain spaces so
// convert spaces in autogenerated U-boot version string
// to underscores.
// See zircon/docs/kernel_cmdline.md
int i;
int len = strlen(uboot_ver);
for (i = 0; i < len; i++) {
if (uboot_ver[i] == ' ') {
uboot_ver[i] = '_';
}
}
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_CMDLINE, 0, uboot_ver, len + 1));
// add platform ID
RETURN_IF_NONZERO(
append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_PLATFORM_ID, 0,
&platform_id, sizeof(platform_id)));
RETURN_IF_NONZERO(add_cpu_topology(zbi, capacity));
RETURN_IF_NONZERO(add_serial_number(zbi, capacity));
RETURN_IF_NONZERO(add_mac_addresses(zbi, capacity));
RETURN_IF_NONZERO(add_cmdline_entropy(zbi, capacity));
add_panel_type(zbi, capacity);
// Only append extra cmdline args if unlocked
bool unlocked;
if (zircon_vboot_is_unlocked(&unlocked)) {
fprintf(stderr, "Error: unable to get unlock status\n");
} else if (unlocked) {
if (zircon_append_cmdline(zbi, capacity)) {
fprintf(stderr, "ERROR: unable to append boot_args.\n");
return -1;
}
}
RETURN_IF_NONZERO(add_reboot_reason(zbi, capacity));
if (watchdog_driver.flags & KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED) {
start_meson_watchdog();
}
RETURN_IF_NONZERO(add_staged_zbi_files(zbi, capacity));
RETURN_IF_NONZERO(append_factory_files(zbi, capacity));
return 0;
}
int zircon_fixup_zbi(zbi_header_t *zbi, size_t capacity)
{
RETURN_IF_NONZERO(add_current_slot(zbi, capacity));
return zircon_fixup_zbi_no_slot(zbi, capacity);
}