| /* |
| * Copyright (c) 2018 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <aml_i2c.h> |
| #include <asm/arch/secure_apb.h> |
| #include <asm/global_data.h> |
| #include <common.h> |
| #include <emmc_partitions.h> |
| #include <g_dnl.h> |
| #include <inttypes.h> |
| #include <lib/zbi/zbi.h> |
| #include <lib/zbi-format/board.h> |
| #include <lib/zbi-format/cpu.h> |
| #include <lib/zbi-format/memory.h> |
| #include <libfdt.h> |
| #include <part.h> |
| #include <zircon.h> |
| |
| #define PDEV_VID_KHADAS 4 |
| #define PDEV_PID_VIM2 2 |
| #define PDEV_PID_VIM3 3 |
| |
| #define NVRAM_LENGTH (16 * 1024) |
| |
| #define CMDLINE_ENTROPY_SIZE 1024 |
| #define CMDLINE_ENTROPY_BITS 256 // random bits to pass to zircon. |
| |
| #define ENTROPY_BITS_PER_CHAR 4 |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| static char entropy_cmdline[CMDLINE_ENTROPY_SIZE] = { 0 }; |
| static const char zircon_entropy_arg[] = "kernel.entropy-mixin="; |
| |
| #define static_assert _Static_assert |
| 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 char BOOTLOADER_VERSION[] = "zircon-bootloader=0.12"; |
| |
| static zbi_mem_range_t mem_config[] = { |
| { |
| .type = ZBI_MEM_TYPE_RAM, |
| .length = 0x80000000, |
| }, |
| { |
| .type = ZBI_MEM_TYPE_PERIPHERAL, |
| .paddr = 0xfe000000, |
| .length = 0x02000000, |
| }, |
| { |
| .type = ZBI_MEM_TYPE_RESERVED, |
| .paddr = 0x07400000, |
| .length = 0x00100000, |
| }, |
| { |
| .type = ZBI_MEM_TYPE_RESERVED, |
| .paddr = 0x05000000, |
| .length = 0x02300000, |
| }, |
| }; |
| |
| static const zbi_dcfg_simple_t uart_driver = { |
| .mmio_phys = 0xff803000, |
| .irq = 225, |
| }; |
| |
| static const zbi_dcfg_arm_gic_v2_driver_t gicv2_driver = { |
| .mmio_phys = 0xffc00000, |
| .gicd_offset = 0x1000, |
| .gicc_offset = 0x2000, |
| .gich_offset = 0x4000, |
| .gicv_offset = 0x6000, |
| .ipi_base = 0, |
| }; |
| |
| static const zbi_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 zbi_dcfg_arm_generic_timer_driver_t timer_driver = { |
| .irq_phys = 30, |
| }; |
| |
| static const zbi_platform_id_t platform_id = { |
| .vid = PDEV_VID_KHADAS, |
| .pid = PDEV_PID_VIM3, |
| .board_name = "vim3", |
| }; |
| |
| enum { |
| PART_BOOTLOADER, |
| PART_ZIRCON_A, |
| PART_ZIRCON_B, |
| PART_ZIRCON_R, |
| PART_SYS_CONFIG, |
| PART_FACTORY_CONFIG, |
| PART_FVM, |
| PART_COUNT, |
| }; |
| |
| extern int dtb_read(void *addr); |
| |
| zbi_result_t add_zbi_item_or_log(void *zbi, size_t capacity, uint32_t type, |
| uint32_t extra, const void *payload, |
| size_t payload_length, const char *format, ...) |
| { |
| zbi_result_t result = zbi_create_entry_with_payload( |
| zbi, capacity, type, extra, 0, payload, payload_length); |
| if (result != ZBI_RESULT_OK) { |
| va_list args; |
| va_start(args, format); |
| vprintf(format, args); |
| va_end(args); |
| } |
| return result; |
| } |
| |
| static zbi_result_t add_serial_number(zbi_header_t *zbi, size_t capacity) |
| { |
| // Serial number from secure storage takes priority, if it exists. |
| // Otherwise use the "serial" env var. |
| // TODO: we may want to unify this logic with the fastboot logic to |
| // make sure they stay in sync. |
| const char *s = get_usid_string(); |
| if (!s) { |
| s = getenv("serial"); |
| } |
| if (!s || (*s == '\0')) { |
| printf("Failed to retrieve serial number\n"); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| return add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_SERIAL_NUMBER, 0, s, strlen(s), |
| "Failed to add ZBI entry for serial number\n"); |
| } |
| |
| static zbi_result_t add_board_info(zbi_header_t *zbi, size_t capacity) |
| { |
| zbi_board_info_t board_info = {}; |
| char *s; |
| if (((s = getenv("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"); |
| } |
| |
| return add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_DRV_BOARD_INFO, 0, |
| &board_info, sizeof(board_info), |
| "Failed to add ZBI entry for board info\n"); |
| } |
| |
| 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; |
| } |
| |
| // 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 zbi_result_t add_cpu_topology(zbi_header_t *zbi, size_t capacity) |
| { |
| 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){ .parent_index = ZBI_TOPOLOGY_NO_PARENT, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_CLUSTER, |
| .cluster = { |
| .performance_class = |
| 0, // low performance cluster |
| } } }; |
| nodes[index++] = |
| (zbi_topology_node_t){ .parent_index = little_cluster, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .processor = { |
| .logical_ids = { logical_processor++ }, |
| .logical_id_count = 1, |
| .flags = |
| ZBI_TOPOLOGY_PROCESSOR_FLAGS_PRIMARY, |
| .architecture_info = { |
| .discriminant = |
| ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64, |
| .arm64 = { |
| .cluster_1_id = |
| 0, |
| .cpu_id = |
| 0, |
| .gic_id = |
| 0, |
| } } } } }; |
| nodes[index++] = |
| (zbi_topology_node_t){ .parent_index = little_cluster, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .processor = { |
| .logical_ids = { logical_processor++ }, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture_info = { |
| .discriminant = |
| ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64, |
| .arm64 = { |
| .cluster_1_id = |
| 0, |
| .cpu_id = |
| 1, |
| .gic_id = |
| 1, |
| } } } } }; |
| big_cluster = index++; |
| nodes[big_cluster] = |
| (zbi_topology_node_t){ .parent_index = ZBI_TOPOLOGY_NO_PARENT, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_CLUSTER, |
| .cluster = { |
| .performance_class = |
| 1, // high performance cluster |
| } } }; |
| nodes[index++] = |
| (zbi_topology_node_t){ .parent_index = big_cluster, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .processor = { |
| .logical_ids = { logical_processor++ }, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture_info = { |
| .discriminant = |
| ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64, |
| .arm64 = { |
| .cluster_1_id = |
| 1, |
| .cpu_id = |
| 0, |
| .gic_id = |
| 4, |
| } } } } }; |
| nodes[index++] = |
| (zbi_topology_node_t){ .parent_index = big_cluster, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .processor = { |
| .logical_ids = { logical_processor++ }, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture_info = { |
| .discriminant = |
| ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64, |
| .arm64 = { |
| .cluster_1_id = |
| 1, |
| .cpu_id = |
| 1, |
| .gic_id = |
| 5, |
| } } } } }; |
| nodes[index++] = |
| (zbi_topology_node_t){ .parent_index = big_cluster, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .processor = { |
| .logical_ids = { logical_processor++ }, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture_info = { |
| .discriminant = |
| ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64, |
| .arm64 = { |
| .cluster_1_id = |
| 1, |
| .cpu_id = |
| 2, |
| .gic_id = |
| 6, |
| } } } } }; |
| nodes[index++] = |
| (zbi_topology_node_t){ .parent_index = big_cluster, |
| .entity = { |
| .discriminant = |
| ZBI_TOPOLOGY_ENTITY_PROCESSOR, |
| .processor = { |
| .logical_ids = { logical_processor++ }, |
| .logical_id_count = 1, |
| .flags = 0, |
| .architecture_info = { |
| .discriminant = |
| ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64, |
| .arm64 = { |
| .cluster_1_id = |
| 1, |
| .cpu_id = |
| 3, |
| .gic_id = |
| 7, |
| } } } } }; |
| |
| return add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_CPU_TOPOLOGY, |
| sizeof(zbi_topology_node_t), &nodes, |
| sizeof(zbi_topology_node_t) * index, |
| "Failed to add ZBI entry for CPU topology\n"); |
| } |
| |
| static int hex_digit(char ch) |
| { |
| if (ch >= '0' && ch <= '9') { |
| return ch - '0'; |
| } else if (ch >= 'a' && ch <= 'f') { |
| return ch - 'a' + 10; |
| } else if (ch >= 'A' && ch <= 'F') { |
| return ch - 'A' + 10; |
| } else { |
| return -1; |
| } |
| } |
| |
| static zbi_result_t add_eth_mac_address(zbi_header_t *zbi, size_t capacity) |
| { |
| char *str = getenv("eth_mac"); |
| uint8_t addr[6]; |
| |
| // this would be easier with sscanf |
| int i; |
| for (i = 0; i < 6; i++) { |
| unsigned left, right; |
| if (str[0] && str[1] && (left = hex_digit(*str++)) >= 0 && |
| (right = hex_digit(*str++)) >= 0) { |
| addr[i] = (left << 4) | right; |
| } else { |
| goto failed; |
| } |
| if (i < 5 && *str++ != ':') { |
| goto failed; |
| } |
| } |
| |
| return add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_DRV_MAC_ADDRESS, 0, |
| addr, sizeof(addr), |
| "Failed to add ZBI entry for MAC address\n"); |
| |
| failed: |
| printf("MAC address parsing failed for \"%s\"\n", getenv("eth_mac")); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| static zbi_result_t add_device_tree_from_fdt(zbi_header_t *zbi, size_t capacity) |
| { |
| const void *fdt = gd->fdt_blob; |
| if (!fdt) { |
| printf("No FDT found in global data\n"); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| int res = fdt_check_header(fdt); |
| if (res != 0) { |
| printf("FDT check failed: %i\n", res); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| return add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_DEVICETREE, 0, fdt, |
| fdt_totalsize(fdt), |
| "Failed to add ZBI entry for device tree\n"); |
| } |
| |
| static zbi_result_t add_device_tree_from_mmc(zbi_header_t *zbi, size_t capacity) |
| { |
| void *dtb_blob = NULL; |
| int rc; |
| zbi_result_t zbi_res; |
| uint32_t length; |
| |
| struct virtual_partition *vpart = |
| aml_get_virtual_partition_by_name(MMC_DTB_NAME); |
| if (!vpart) { |
| printf("DTB partition not found\n"); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| zbi_res = zbi_get_next_entry_payload( |
| zbi, zircon_image_get_capacity(zbi), &dtb_blob, &length); |
| if (zbi_res != ZBI_RESULT_OK || !dtb_blob || length < vpart->size) { |
| printf("Failed to get next entry payload for DTB of length %llu." |
| "(next entry payload length = %u)\n", |
| vpart->size, length); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| rc = dtb_read(dtb_blob); |
| if (rc != 0) { |
| printf("Failed to read DTB partition: %i\n", rc); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| rc = fdt_check_header(dtb_blob); |
| if (rc != 0) { |
| printf("FDT check failed: %i\n", rc); |
| return ZBI_RESULT_ERROR; |
| } |
| |
| printf("DTB partition found (size:%llu), adding FDT (size:%u) to ZBI\n", |
| vpart->size, fdt_totalsize(dtb_blob)); |
| |
| zbi_res = zbi_create_entry(zbi, zircon_image_get_capacity(zbi), |
| ZBI_TYPE_DEVICETREE, 0, ZBI_FLAGS_VERSION, |
| fdt_totalsize(dtb_blob), NULL); |
| if (zbi_res != ZBI_RESULT_OK) { |
| printf("Failed to create entry for DTB %llu\n", vpart->size); |
| } |
| |
| return zbi_res; |
| } |
| |
| static zbi_result_t add_device_tree(zbi_header_t *zbi, size_t capacity) |
| { |
| zbi_result_t res = add_device_tree_from_fdt(zbi, capacity); |
| if (res != ZBI_RESULT_OK) { |
| res = add_device_tree_from_mmc(zbi, capacity); |
| } |
| return res; |
| } |
| |
| // 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. |
| // NOTE that there is no guarantee of the quality of this rng. |
| static inline uint32_t read_hw_rng(void) |
| { |
| return readl(RNG_USR_DATA); |
| } |
| |
| static zbi_result_t add_cmdline_has_lcd(zbi_header_t *zbi, size_t capacity) |
| { |
| static const char zircon_vim3_has_lcd_arg[] = "driver.vim3.has_lcd="; |
| ulong lcd_exist = |
| getenv_ulong("lcd_exist", /*base=*/10, /*default_val=*/0); |
| |
| char vim3_has_lcd_cmdline[64]; |
| snprintf(vim3_has_lcd_cmdline, sizeof(vim3_has_lcd_cmdline), "%s%s", |
| zircon_vim3_has_lcd_arg, lcd_exist ? "true" : "false"); |
| |
| return add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_CMDLINE, 0, |
| vim3_has_lcd_cmdline, |
| strlen(vim3_has_lcd_cmdline), |
| "Failed to add ZBI entry for cmdline\n"); |
| } |
| |
| static zbi_result_t add_cmdline_entropy(zbi_header_t *zbi, size_t capacity) |
| { |
| strcpy(entropy_cmdline, zircon_entropy_arg); |
| char *entropy = entropy_cmdline + strlen(zircon_entropy_arg); |
| int i = 0; |
| zbi_result_t res; |
| |
| for (i = 0; i < CMDLINE_ENTROPY_BITS; i += 32) { |
| uint32_to_hex(read_hw_rng(), entropy); |
| entropy += 8; |
| } |
| *entropy = '\0'; |
| |
| res = add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_CMDLINE, 0, entropy_cmdline, |
| sizeof(entropy_cmdline), |
| "Failed to add ZBI entry for cmdline entropy\n"); |
| |
| mandatory_memset(entropy_cmdline, '\0', sizeof(entropy_cmdline)); |
| return res; |
| } |
| |
| int zircon_preboot(zbi_header_t *zbi, size_t capacity) |
| { |
| #if 0 //Deprecated cpu topology descripiton |
| add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_CPU_CONFIG, 0, &cpu_config, |
| sizeof(zbi_cpu_config_t) + |
| sizeof(zbi_cpu_cluster_t) * cpu_config.cluster_count, |
| "Failed to add ZBI entry for CPU config\n"); |
| printf("Appended ZBI_TYPE_CPU_CONFIG - %x\n",zbi->length); |
| #endif |
| |
| #if 0 // Old method of overriding vim3 memory suze. |
| //TODO - vim3 does not define a ddr_size env variable. Since there are two |
| // variants of vim3, need to figure out how to figure this out from u-boot |
| const char* ddr_size = getenv("ddr_size"); |
| if (!strcmp(ddr_size, "3")) { |
| mem_config[0].length = 0xc0000000; |
| } |
| #endif |
| |
| // Compute the available memory. |
| uint64_t mem_bytes = CONFIG_SYS_MEM_TOP_HIDE; // Add back hidden ram. |
| for (int i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { |
| printf("Dram bank %d has 0x%lx\n", i, gd->bd->bi_dram[i].size); |
| mem_bytes += gd->bd->bi_dram[i].size; |
| } |
| mem_config[0].length = mem_bytes; |
| |
| // A little math to print the memory without (ab)using floats. |
| const uint32_t mem_mbytes = mem_bytes / (1024 * 1024); |
| const uint32_t mem_gbytes = mem_mbytes / 1024; |
| const uint32_t mem_remaining_mbytes = mem_mbytes % 1024; |
| printf("ZBI_MEM_RANGE_RAM is 0x%" PRIx64 |
| " (%d.%03d GiB) in %d bank(s)\n", |
| mem_config[0].length, mem_gbytes, |
| mem_remaining_mbytes * 1000 / 1024, CONFIG_NR_DRAM_BANKS); |
| |
| // add memory configuration |
| add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_MEM_CONFIG, 0, &mem_config, |
| sizeof(mem_config), |
| "Failed to add ZBI entry for memory config\n"); |
| |
| // Reserve crashlog save area at very end of RAM. |
| // This space is currently left untouched via CONFIG_SYS_MEM_TOP_HIDE, |
| // but we don't actually need to hide it so we reclaim some of it for |
| // NVRAM (http://b/302699144#comment4). |
| _Static_assert(NVRAM_LENGTH <= CONFIG_SYS_MEM_TOP_HIDE); |
| zbi_nvram_t nvram; |
| nvram.base = mem_config[0].length - NVRAM_LENGTH; |
| nvram.length = NVRAM_LENGTH; |
| add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_NVRAM, 0, &nvram, |
| sizeof(nvram), |
| "Failed to add ZBI entry for NVRAM\n"); |
| |
| // add kernel drivers |
| add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, |
| ZBI_KERNEL_DRIVER_AMLOGIC_UART, &uart_driver, |
| sizeof(uart_driver), |
| "Failed to add ZBI entry for kernel driver AMLOGIC_UART\n"); |
| add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, |
| ZBI_KERNEL_DRIVER_ARM_GIC_V2, &gicv2_driver, |
| sizeof(gicv2_driver), |
| "Failed to add ZBI entry for kernel driver ARM_GIC_V2\n"); |
| add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, |
| ZBI_KERNEL_DRIVER_ARM_PSCI, &psci_driver, sizeof(psci_driver), |
| "Failed to add ZBI entry for kernel driver ARM_PSCI\n"); |
| add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, |
| ZBI_KERNEL_DRIVER_ARM_GENERIC_TIMER, &timer_driver, |
| sizeof(timer_driver), |
| "Failed to add ZBI entry for kernel driver ARM_GENERIC_TIMER\n"); |
| // add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_HDCP, &hdcp_driver, |
| // sizeof(hdcp_driver), |
| // "Failed to add ZBI entry for kernel driver AMLOGIC_HDCP\n"); |
| |
| add_zbi_item_or_log( |
| zbi, capacity, ZBI_TYPE_CMDLINE, 0, BOOTLOADER_VERSION, |
| strlen(BOOTLOADER_VERSION) + 1, |
| "Failed to add ZBI entry for cmdline bootloader version\n"); |
| |
| // add platform ID |
| add_zbi_item_or_log(zbi, capacity, ZBI_TYPE_PLATFORM_ID, 0, |
| &platform_id, sizeof(platform_id), |
| "Failed to add ZBI entry for platform ID\n"); |
| |
| add_serial_number(zbi, capacity); |
| add_board_info(zbi, capacity); |
| add_cmdline_has_lcd(zbi, capacity); |
| add_cmdline_entropy(zbi, capacity); |
| add_cpu_topology(zbi, capacity); |
| add_eth_mac_address(zbi, capacity); |
| add_device_tree(zbi, capacity); |
| return 0; |
| } |