| /* |
| * 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); |
| } |