blob: 4f2b3b02a763e76443a4656089c7a325d333351d [file] [log] [blame]
/*
* 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 = 5,
};
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;
}