arm-trusted-firmware/plat/rockchip/common/params_setup.c
Thomas Hebb e7b586987c rockchip: don't crash if we get an FDT we can't parse
When we parse the param from BL2, we try to parse it as a FDT and then,
if that fails, as aux params. However, we don't sufficiently distinguish
between failure modes in the first step: specifically, if we are given
an FDT with good magic that we can't parse for some other reason (e.g.
not enough space in our buffer), we still attempt to parse it as aux
params even though that's guaranteed to fatal. Instead, we should either
fail with a more descriptive message or continue to boot without parsing
the FDT.

This patch takes the latter approach, since all we currently get from
the FDT is non-critical UART params.

Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
Change-Id: I1e98f1fcda4f78e6b45e86956288bafe58b113e4
2020-07-04 15:37:25 -07:00

257 lines
5.3 KiB
C

/*
* Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <lib/bl_aux_params/bl_aux_params.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/console.h>
#include <drivers/gpio.h>
#include <libfdt.h>
#include <lib/coreboot.h>
#include <lib/mmio.h>
#include <plat/common/platform.h>
#include <plat_params.h>
#include <plat_private.h>
static struct bl_aux_gpio_info rst_gpio = { .index = UINT_MAX } ;
static struct bl_aux_gpio_info poweroff_gpio = { .index = UINT_MAX };
static struct bl_aux_gpio_info suspend_gpio[10];
uint32_t suspend_gpio_cnt;
static struct bl_aux_rk_apio_info suspend_apio;
#if COREBOOT
static int dt_process_fdt(u_register_t param_from_bl2)
{
return -ENODEV;
}
#else
static uint32_t rk_uart_base = PLAT_RK_UART_BASE;
static uint32_t rk_uart_baudrate = PLAT_RK_UART_BAUDRATE;
static uint32_t rk_uart_clock = PLAT_RK_UART_CLOCK;
#define FDT_BUFFER_SIZE 0x20000
static uint8_t fdt_buffer[FDT_BUFFER_SIZE];
void *plat_get_fdt(void)
{
return &fdt_buffer[0];
}
static void plat_rockchip_dt_process_fdt_uart(void *fdt)
{
const char *path_name = "/chosen";
const char *prop_name = "stdout-path";
int node_offset;
int stdout_path_len;
const char *stdout_path;
const char *separator;
const char *baud_start;
char serial_char;
int serial_no;
uint32_t uart_base;
uint32_t baud;
node_offset = fdt_path_offset(fdt, path_name);
if (node_offset < 0)
return;
stdout_path = fdt_getprop(fdt, node_offset, prop_name,
&stdout_path_len);
if (stdout_path == NULL)
return;
/*
* We expect something like:
* "serial0:baudrate"
*/
if (strncmp("serial", stdout_path, 6) != 0)
return;
serial_char = stdout_path[6];
serial_no = serial_char - '0';
switch (serial_no) {
case 0:
uart_base = UART0_BASE;
break;
case 1:
uart_base = UART1_BASE;
break;
case 2:
uart_base = UART2_BASE;
break;
#ifdef UART3_BASE
case 3:
uart_base = UART3_BASE;
break;
#endif
#ifdef UART4_BASE
case 4:
uart_base = UART4_BASE;
break;
#endif
#ifdef UART5_BASE
case 5:
uart_base = UART5_BASE;
break;
#endif
default:
return;
}
rk_uart_base = uart_base;
separator = strchr(stdout_path, ':');
if (!separator)
return;
baud = 0;
baud_start = separator + 1;
while (*baud_start != '\0') {
/*
* uart binding is <baud>{<parity>{<bits>{...}}}
* So the baudrate either is the whole string, or
* we end in the parity characters.
*/
if (*baud_start == 'n' || *baud_start == 'o' ||
*baud_start == 'e')
break;
baud = baud * 10 + (*baud_start - '0');
baud_start++;
}
rk_uart_baudrate = baud;
}
static int dt_process_fdt(u_register_t param_from_bl2)
{
void *fdt = plat_get_fdt();
int ret;
ret = fdt_open_into((void *)param_from_bl2, fdt, FDT_BUFFER_SIZE);
if (ret < 0)
return ret;
plat_rockchip_dt_process_fdt_uart(fdt);
return 0;
}
#endif
uint32_t rockchip_get_uart_base(void)
{
#if COREBOOT
return coreboot_serial.baseaddr;
#else
return rk_uart_base;
#endif
}
uint32_t rockchip_get_uart_baudrate(void)
{
#if COREBOOT
return coreboot_serial.baud;
#else
return rk_uart_baudrate;
#endif
}
uint32_t rockchip_get_uart_clock(void)
{
#if COREBOOT
return coreboot_serial.input_hertz;
#else
return rk_uart_clock;
#endif
}
struct bl_aux_gpio_info *plat_get_rockchip_gpio_reset(void)
{
if (rst_gpio.index == UINT_MAX)
return NULL;
return &rst_gpio;
}
struct bl_aux_gpio_info *plat_get_rockchip_gpio_poweroff(void)
{
if (poweroff_gpio.index == UINT_MAX)
return NULL;
return &poweroff_gpio;
}
struct bl_aux_gpio_info *plat_get_rockchip_suspend_gpio(uint32_t *count)
{
*count = suspend_gpio_cnt;
return &suspend_gpio[0];
}
struct bl_aux_rk_apio_info *plat_get_rockchip_suspend_apio(void)
{
return &suspend_apio;
}
static bool rk_aux_param_handler(struct bl_aux_param_header *param)
{
/* Store platform parameters for later processing if needed. */
switch (param->type) {
case BL_AUX_PARAM_RK_RESET_GPIO:
rst_gpio = ((struct bl_aux_param_gpio *)param)->gpio;
return true;
case BL_AUX_PARAM_RK_POWEROFF_GPIO:
poweroff_gpio = ((struct bl_aux_param_gpio *)param)->gpio;
return true;
case BL_AUX_PARAM_RK_SUSPEND_GPIO:
if (suspend_gpio_cnt >= ARRAY_SIZE(suspend_gpio)) {
ERROR("Exceeded the supported suspend GPIO number.\n");
return true;
}
suspend_gpio[suspend_gpio_cnt++] =
((struct bl_aux_param_gpio *)param)->gpio;
return true;
case BL_AUX_PARAM_RK_SUSPEND_APIO:
suspend_apio = ((struct bl_aux_param_rk_apio *)param)->apio;
return true;
}
return false;
}
void params_early_setup(u_register_t plat_param_from_bl2)
{
int ret;
/*
* Test if this is a FDT passed as a platform-specific parameter
* block.
*/
ret = dt_process_fdt(plat_param_from_bl2);
if (!ret) {
return;
} else if (ret != -FDT_ERR_BADMAGIC) {
/*
* If we found an FDT but couldn't parse it (e.g. corrupt, not
* enough space), return and don't attempt to parse the param
* as something else, since we know that will also fail. All
* we're doing is setting up UART, this doesn't need to be
* fatal.
*/
WARN("%s: found FDT but could not parse: error %d\n",
__func__, ret);
return;
}
bl_aux_params_parse(plat_param_from_bl2, rk_aux_param_handler);
}