| /* |
| * Copyright (c) 2018 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <asm/arch/secure_apb.h> |
| #include <asm/arch/timer.h> |
| #include <command.h> |
| #include <asm/arch/reboot.h> |
| #include <asm/arch/secure_apb.h> |
| #include <asm/io.h> |
| #include <common.h> |
| #include <dm/uclass.h> |
| #include <emmc_storage.h> |
| #include <fs.h> |
| #include <mmc.h> |
| #include <u-boot/sha256.h> |
| #include <version.h> |
| #include <wdt.h> |
| #include <zircon_uboot/boot_args.h> |
| #include <zircon_uboot/zircon.h> |
| |
| #ifndef CONFIG_FACTORY_BOOT_KVS |
| #error "Factory_boot KVS has to be enabled for this board!!!" |
| #endif |
| |
| #include <factory_boot_kvs.h> |
| #include <tee/ta_vx_helper.h> |
| |
| // 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 |
| |
| #define PDEV_VID_GOOGLE 3 |
| #define PDEV_PID_LUIS 12 |
| |
| #define NVRAM_LENGTH (8 * 1024) |
| |
| // 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 |
| |
| // HW RNG is reseeded every 40 microseconds. |
| #define HW_RNG_RESEEDING_INTERVAL_MICROS 40 |
| |
| #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."); |
| |
| 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 = 0x76800000, |
| .length = 0x800000, |
| }, |
| /* linux,usable-memory */ |
| { |
| .type = ZBI_MEM_RANGE_RESERVED, |
| .paddr = 0x00000000, |
| .length = 0x100000, |
| }, |
| }; |
| |
| 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 = 0, |
| }; |
| |
| 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 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, |
| }; |
| |
| #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 const zbi_platform_id_t platform_id = { |
| .vid = PDEV_VID_GOOGLE, |
| .pid = PDEV_PID_LUIS, |
| .board_name = "luis", |
| }; |
| |
| enum { |
| PART_TPL, |
| PART_FTS, |
| PART_FACTORY, |
| PART_ZIRCON_B, |
| PART_ZIRCON_A, |
| PART_ZIRCON_R, |
| PART_FVM, |
| PART_SYS_CONFIG, |
| PART_MIGRATION, |
| PART_COUNT, |
| }; |
| |
| 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; |
| } |
| |
| static int default_mac_address(int n, u64* fullmac) |
| { |
| // add default MAC addresses like we make default serial numbers, |
| // from a hash of the eMMC CID |
| static u64 default_mac; |
| if (!default_mac) { |
| struct mmc* mmc = find_mmc_device(STORAGE_DEV_EMMC); |
| if (!mmc) { |
| printf("Cannot find eMMC dev.\n"); |
| return -1; |
| } |
| |
| u8 hash[SHA256_DIGEST_SIZE]; |
| sha256_csum_wd((void *)mmc->cid, sizeof mmc->cid, hash, CHUNKSZ_SHA256); |
| memcpy(&default_mac, hash, 5); |
| default_mac |= 0x02ull << 40; |
| } |
| |
| if (!fullmac) { |
| return -2; |
| } |
| |
| *fullmac = default_mac; |
| return 0; |
| } |
| |
| #define MAC_ADDR_KEY_1 "mac_addr1" |
| #define MAC_ADDR_KEY_2 "mac_addr2" |
| // 0 = WiFi MAC, 1 = BT/enet MAC. |
| #define NUM_MAC_ADDRESSES 2 |
| static int add_mac_addresses(zbi_header_t *zbi) |
| { |
| const char *mac_addr_keys[] = { MAC_ADDR_KEY_1, MAC_ADDR_KEY_2 }; |
| u8 mac_addr[6]; |
| int mac_num, i; |
| for (mac_num = 0; mac_num < NUM_MAC_ADDRESSES; mac_num++) { |
| u64 fullmac; |
| if (FbKvsGetULong(mac_addr_keys[mac_num], &fullmac) != |
| kFbKvsResultOk) { |
| printf("%s: ERROR: FbKvsGetULong() failed to read MAC ADDR %d\n", |
| __func__, mac_num); |
| if (default_mac_address(mac_num, &fullmac) != 0) |
| continue; |
| } |
| |
| for (i = ARRAY_SIZE(mac_addr) - 1; i >= 0; i--) { |
| mac_addr[i] = (u8)(fullmac & 0xff); |
| fullmac >>= 8; |
| } |
| zircon_append_boot_item(zbi, ZBI_TYPE_DRV_MAC_ADDRESS, mac_num, |
| mac_addr, sizeof(mac_addr)); |
| } |
| |
| return 0; |
| } |
| |
| static void add_serial_number(zbi_header_t* zbi) { |
| char* s; |
| if (!(s = env_get("serial#")) || (*s == '\0')) { |
| printf("%s: Failed to retrieve serial number.\n", __func__); |
| return; |
| } |
| zircon_append_boot_item(zbi, ZBI_TYPE_SERIAL_NUMBER, 0, s, strlen(s)); |
| } |
| |
| static void add_board_info(zbi_header_t* zbi) |
| { |
| zbi_board_info_t board_info = {}; |
| char* s; |
| if (((s = env_get("hw_id")) != NULL) && (*s != '\0')) { |
| uint32_t hw_id = simple_strtoul(s, NULL, 16); |
| board_info.revision = hw_id; |
| } else { |
| printf("Failed to retrieve Board Revision\n"); |
| } |
| |
| zircon_append_boot_item(zbi, ZBI_TYPE_DRV_BOARD_INFO, 0, &board_info, |
| sizeof(board_info)); |
| } |
| |
| // Define a cpu topology for the system that captures how all of the cores are |
| // connected and grouped. Since this is a GICv2 system we include the ID for the |
| // cores, especially since it is a tricky one with a gap between the little and |
| // big clusters. |
| static void add_cpu_topology(zbi_header_t* zbi) |
| { |
| int index = 0; |
| int logical_processor = 0; |
| int big_cluster = 0, little_cluster = 0; |
| |
| zbi_topology_node_t nodes[8]; |
| |
| little_cluster = index++; |
| nodes[little_cluster] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER, |
| .parent_index = ZBI_TOPOLOGY_NO_PARENT, |
| .entity = { |
| .cluster = { |
| .performance_class = 0, // low performance cluster |
| } |
| } |
| }; |
| |
| nodes[index++] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .parent_index = little_cluster, |
| .entity = { |
| .processor = { |
| .logical_ids = {logical_processor++}, |
| .logical_id_count = 1, |
| .flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY, |
| .architecture = ZBI_TOPOLOGY_ARCH_ARM, |
| .architecture_info = { |
| .arm = { |
| .cluster_1_id = 0, |
| .cpu_id = 0, |
| .gic_id = 0, |
| } |
| } |
| } |
| } |
| }; |
| nodes[index++] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .parent_index = little_cluster, |
| .entity = { |
| .processor = { |
| .logical_ids = {logical_processor++}, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture = ZBI_TOPOLOGY_ARCH_ARM, |
| .architecture_info = { |
| .arm = { |
| .cluster_1_id = 0, |
| .cpu_id = 1, |
| .gic_id = 1, |
| } |
| } |
| } |
| } |
| }; |
| |
| big_cluster = index++; |
| nodes[big_cluster] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER, |
| .parent_index = ZBI_TOPOLOGY_NO_PARENT, |
| .entity = { |
| .cluster = { |
| .performance_class = 1, // high performance cluster |
| } |
| } |
| }; |
| |
| nodes[index++] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .parent_index = big_cluster, |
| .entity = { |
| .processor = { |
| .logical_ids = {logical_processor++}, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture = ZBI_TOPOLOGY_ARCH_ARM, |
| .architecture_info = { |
| .arm = { |
| .cluster_1_id = 1, |
| .cpu_id = 0, |
| .gic_id = 4, |
| } |
| } |
| } |
| } |
| }; |
| nodes[index++] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .parent_index = big_cluster, |
| .entity = { |
| .processor = { |
| .logical_ids = {logical_processor++}, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture = ZBI_TOPOLOGY_ARCH_ARM, |
| .architecture_info = { |
| .arm = { |
| .cluster_1_id = 1, |
| .cpu_id = 1, |
| .gic_id = 5, |
| } |
| } |
| } |
| } |
| }; |
| nodes[index++] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .parent_index = big_cluster, |
| .entity = { |
| .processor = { |
| .logical_ids = {logical_processor++}, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture = ZBI_TOPOLOGY_ARCH_ARM, |
| .architecture_info = { |
| .arm = { |
| .cluster_1_id = 1, |
| .cpu_id = 2, |
| .gic_id = 6, |
| } |
| } |
| } |
| } |
| }; |
| nodes[index++] = (zbi_topology_node_t){ |
| .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .parent_index = big_cluster, |
| .entity = { |
| .processor = { |
| .logical_ids = {logical_processor++}, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture = ZBI_TOPOLOGY_ARCH_ARM, |
| .architecture_info = { |
| .arm = { |
| .cluster_1_id = 1, |
| .cpu_id = 3, |
| .gic_id = 7, |
| } |
| } |
| } |
| } |
| }; |
| |
| zircon_append_boot_item(zbi, ZBI_TYPE_CPU_TOPOLOGY, sizeof(zbi_topology_node_t), |
| &nodes, sizeof(zbi_topology_node_t) * index); |
| } |
| |
| static void add_reboot_reason(zbi_header_t* zbi) { |
| // See cmd/amlogic/cmd_reboot.c |
| const uint32_t reboot_mode_val = ((readl(AO_SEC_SD_CFG15) >> 12) & 0xf); |
| |
| 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; |
| } |
| |
| zircon_append_boot_item(zbi, 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); |
| } |
| |
| // 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); |
| } |
| |
| // 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) { |
| 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'; |
| |
| zircon_append_boot_item(zbi, ZBI_TYPE_CMDLINE, 0, entropy_cmdline, |
| sizeof(entropy_cmdline)); |
| |
| mandatory_memset(entropy_cmdline, '\0', sizeof(entropy_cmdline)); |
| return 0; |
| } |
| |
| #define FB_KVS_SEAL_NAME "factory_verity_seal" |
| #define FB_KVS_SEAL_SIZE 32 |
| #define KERNEL_ARG_SEAL_NAME "factory_verity_seal=" |
| static int add_factory_verity_seal(zbi_header_t *zbi) { |
| uint8_t seal_data[FB_KVS_SEAL_SIZE] = {0}; |
| char seal_hex_str[sizeof(KERNEL_ARG_SEAL_NAME) + FB_KVS_SEAL_SIZE * 2]; |
| size_t off = strlen(KERNEL_ARG_SEAL_NAME); |
| static const char hex[] = "0123456789abcdef"; |
| size_t size = sizeof(seal_data); |
| size_t i; |
| |
| if (FbKvsGetData(FB_KVS_SEAL_NAME, seal_data, &size) != kFbKvsResultOk) { |
| printf("%s: ERROR: FbKvsGetData() failed to read '" |
| FB_KVS_SEAL_NAME"' value\n", __func__); |
| return -1; |
| } |
| |
| memcpy(seal_hex_str, KERNEL_ARG_SEAL_NAME, strlen(KERNEL_ARG_SEAL_NAME)); |
| |
| /* convert to hex string */ |
| for (i = 0; i < size; ++i) { |
| seal_hex_str[off + 2 * i] = hex[seal_data[i] >> 4]; |
| seal_hex_str[off + 2 * i + 1] = hex[seal_data[i] & 0xf]; |
| } |
| seal_hex_str[off + i*2] = '\0'; |
| |
| zircon_append_boot_item(zbi, ZBI_TYPE_CMDLINE, 0, seal_hex_str, |
| sizeof(seal_hex_str)); |
| return 0; |
| } |
| |
| int zircon_preboot(zbi_header_t *zbi) |
| { |
| /* |
| * allocate crashlog save area before 0x5f800000-0x60000000 |
| * reserved area |
| */ |
| zbi_nvram_t nvram; |
| |
| nvram.base = 0x5f800000 - NVRAM_LENGTH; |
| nvram.length = NVRAM_LENGTH; |
| zircon_append_boot_item(zbi, ZBI_TYPE_NVRAM, 0, &nvram, sizeof(nvram)); |
| |
| /* add memory configuration */ |
| zircon_append_boot_item(zbi, ZBI_TYPE_MEM_CONFIG, 0, &mem_config, |
| sizeof(mem_config)); |
| |
| /* add kernel drivers */ |
| zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_UART, |
| &uart_driver, sizeof(uart_driver)); |
| zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2, |
| &gicv2_driver, sizeof(gicv2_driver)); |
| zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_PSCI, |
| &psci_driver, sizeof(psci_driver)); |
| zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, |
| KDRV_ARM_GENERIC_TIMER, |
| &timer_driver, sizeof(timer_driver)); |
| zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, |
| KDRV_GENERIC_32BIT_WATCHDOG, |
| &watchdog_driver, sizeof(watchdog_driver)); |
| zircon_append_boot_item(zbi, 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] = '_'; |
| } |
| } |
| |
| zircon_append_boot_item(zbi, ZBI_TYPE_CMDLINE, 0, uboot_ver, len + 1); |
| |
| add_serial_number(zbi); |
| |
| /* Add board-specific information */ |
| add_board_info(zbi); |
| |
| /* add platform ID */ |
| zircon_append_boot_item(zbi, ZBI_TYPE_PLATFORM_ID, 0, &platform_id, |
| sizeof(platform_id)); |
| |
| add_cpu_topology(zbi); |
| |
| int ret = add_cmdline_entropy(zbi); |
| if (ret < 0) { |
| panic("ERROR: unable to gather enough entropy to pass via cmdline!\n"); |
| } |
| |
| ret = add_mac_addresses(zbi); |
| if (ret < 0) { |
| printf("ERROR: unable to read MAC addresses from the" |
| " factory_boot partition!\n"); |
| } |
| |
| ret = add_factory_verity_seal(zbi); |
| if (ret < 0) { |
| printf("ERROR: unable to read Factory verity seal value from the" |
| " factory_boot partition!\n"); |
| } |
| |
| // 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)) { |
| fprintf(stderr, "ERROR: unable to append boot_args.\n"); |
| } |
| } |
| |
| add_reboot_reason(zbi); |
| |
| if (watchdog_driver.flags & KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED) { |
| start_meson_watchdog(); |
| } |
| |
| return 0; |
| } |