mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-27 13:33:40 +00:00
185f812c41
Sphinx expects Return: and not @return to indicate a return value. find . -name '*.c' -exec \ sed -i 's/^\(\s\)\*\(\s*\)@return\(\s\)/\1*\2Return:\3/' {} \; find . -name '*.h' -exec \ sed -i 's/^\(\s\)\*\(\s*\)@return\(\s\)/\1*\2Return:\3/' {} \; Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
399 lines
8.9 KiB
C
399 lines
8.9 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2015-2016 Freescale Semiconductor, Inc.
|
|
* Copyright 2017,2021 NXP
|
|
*/
|
|
|
|
/*
|
|
* @file
|
|
* Contains all the functions to handle parsing and loading of PE firmware
|
|
* files.
|
|
*/
|
|
|
|
#include <dm.h>
|
|
#include <dm/device-internal.h>
|
|
#include <image.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <linux/bitops.h>
|
|
#include <net/pfe_eth/pfe_eth.h>
|
|
#include <net/pfe_eth/pfe_firmware.h>
|
|
#include <spi_flash.h>
|
|
#ifdef CONFIG_CHAIN_OF_TRUST
|
|
#include <fsl_validate.h>
|
|
#endif
|
|
|
|
#define PFE_FIRMWARE_FIT_CNF_NAME "config@1"
|
|
|
|
static const void *pfe_fit_addr;
|
|
#ifdef CONFIG_CHAIN_OF_TRUST
|
|
static const void *pfe_esbc_hdr_addr;
|
|
#endif
|
|
|
|
/*
|
|
* PFE elf firmware loader.
|
|
* Loads an elf firmware image into a list of PE's (specified using a bitmask)
|
|
*
|
|
* @param pe_mask Mask of PE id's to load firmware to
|
|
* @param pfe_firmware Pointer to the firmware image
|
|
*
|
|
* Return: 0 on success, a negative value on error
|
|
*/
|
|
static int pfe_load_elf(int pe_mask, uint8_t *pfe_firmware)
|
|
{
|
|
Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *)pfe_firmware;
|
|
Elf32_Half sections = be16_to_cpu(elf_hdr->e_shnum);
|
|
Elf32_Shdr *shdr = (Elf32_Shdr *)(pfe_firmware +
|
|
be32_to_cpu(elf_hdr->e_shoff));
|
|
int id, section;
|
|
int ret;
|
|
|
|
debug("%s: no of sections: %d\n", __func__, sections);
|
|
|
|
/* Some sanity checks */
|
|
if (strncmp((char *)&elf_hdr->e_ident[EI_MAG0], ELFMAG, SELFMAG)) {
|
|
printf("%s: incorrect elf magic number\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) {
|
|
printf("%s: incorrect elf class(%x)\n", __func__,
|
|
elf_hdr->e_ident[EI_CLASS]);
|
|
return -1;
|
|
}
|
|
|
|
if (elf_hdr->e_ident[EI_DATA] != ELFDATA2MSB) {
|
|
printf("%s: incorrect elf data(%x)\n", __func__,
|
|
elf_hdr->e_ident[EI_DATA]);
|
|
return -1;
|
|
}
|
|
|
|
if (be16_to_cpu(elf_hdr->e_type) != ET_EXEC) {
|
|
printf("%s: incorrect elf file type(%x)\n", __func__,
|
|
be16_to_cpu(elf_hdr->e_type));
|
|
return -1;
|
|
}
|
|
|
|
for (section = 0; section < sections; section++, shdr++) {
|
|
if (!(be32_to_cpu(shdr->sh_flags) & (SHF_WRITE | SHF_ALLOC |
|
|
SHF_EXECINSTR)))
|
|
continue;
|
|
for (id = 0; id < MAX_PE; id++)
|
|
if (pe_mask & BIT(id)) {
|
|
ret = pe_load_elf_section(id,
|
|
pfe_firmware, shdr);
|
|
if (ret < 0)
|
|
goto err;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Get PFE firmware from FIT image
|
|
*
|
|
* @param data pointer to PFE firmware
|
|
* @param size pointer to size of the firmware
|
|
* @param fw_name pfe firmware name, either class or tmu
|
|
*
|
|
* Return: 0 on success, a negative value on error
|
|
*/
|
|
static int pfe_get_fw(const void **data,
|
|
size_t *size, char *fw_name)
|
|
{
|
|
int conf_node_off, fw_node_off;
|
|
char *conf_node_name = NULL;
|
|
char *desc;
|
|
int ret = 0;
|
|
|
|
conf_node_name = PFE_FIRMWARE_FIT_CNF_NAME;
|
|
|
|
conf_node_off = fit_conf_get_node(pfe_fit_addr, conf_node_name);
|
|
if (conf_node_off < 0) {
|
|
printf("PFE Firmware: %s: no such config\n", conf_node_name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
fw_node_off = fit_conf_get_prop_node(pfe_fit_addr, conf_node_off,
|
|
fw_name);
|
|
if (fw_node_off < 0) {
|
|
printf("PFE Firmware: No '%s' in config\n",
|
|
fw_name);
|
|
return -ENOLINK;
|
|
}
|
|
|
|
if (!(fit_image_verify(pfe_fit_addr, fw_node_off))) {
|
|
printf("PFE Firmware: Bad firmware image (bad CRC)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (fit_image_get_data(pfe_fit_addr, fw_node_off, data, size)) {
|
|
printf("PFE Firmware: Can't get %s subimage data/size",
|
|
fw_name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
ret = fit_get_desc(pfe_fit_addr, fw_node_off, &desc);
|
|
if (ret)
|
|
printf("PFE Firmware: Can't get description\n");
|
|
else
|
|
printf("%s\n", desc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Check PFE FIT image
|
|
*
|
|
* Return: 0 on success, a negative value on error
|
|
*/
|
|
static int pfe_fit_check(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = fdt_check_header(pfe_fit_addr);
|
|
if (ret) {
|
|
printf("PFE Firmware: Bad firmware image (not a FIT image)\n");
|
|
return ret;
|
|
}
|
|
|
|
if (fit_check_format(pfe_fit_addr, IMAGE_SIZE_INVAL)) {
|
|
printf("PFE Firmware: Bad firmware image (bad FIT header)\n");
|
|
ret = -1;
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int pfe_spi_flash_init(void)
|
|
{
|
|
struct spi_flash *pfe_flash;
|
|
struct udevice *new;
|
|
int ret = 0;
|
|
void *addr = malloc(CONFIG_SYS_LS_PFE_FW_LENGTH);
|
|
|
|
if (!addr)
|
|
return -ENOMEM;
|
|
|
|
ret = spi_flash_probe_bus_cs(CONFIG_SYS_FSL_PFE_SPI_BUS,
|
|
CONFIG_SYS_FSL_PFE_SPI_CS,
|
|
CONFIG_SYS_FSL_PFE_SPI_MAX_HZ,
|
|
CONFIG_SYS_FSL_PFE_SPI_MODE,
|
|
&new);
|
|
if (ret) {
|
|
printf("SF: failed to probe spi\n");
|
|
free(addr);
|
|
device_remove(new, DM_REMOVE_NORMAL);
|
|
return ret;
|
|
}
|
|
|
|
|
|
pfe_flash = dev_get_uclass_priv(new);
|
|
if (!pfe_flash) {
|
|
printf("SF: probe for pfe failed\n");
|
|
free(addr);
|
|
device_remove(new, DM_REMOVE_NORMAL);
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = spi_flash_read(pfe_flash,
|
|
CONFIG_SYS_LS_PFE_FW_ADDR,
|
|
CONFIG_SYS_LS_PFE_FW_LENGTH,
|
|
addr);
|
|
if (ret) {
|
|
printf("SF: read for pfe failed\n");
|
|
free(addr);
|
|
spi_flash_free(pfe_flash);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_CHAIN_OF_TRUST
|
|
void *hdr_addr = malloc(CONFIG_SYS_LS_PFE_ESBC_LENGTH);
|
|
|
|
if (!hdr_addr) {
|
|
free(addr);
|
|
spi_flash_free(pfe_flash);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = spi_flash_read(pfe_flash,
|
|
CONFIG_SYS_LS_PFE_ESBC_ADDR,
|
|
CONFIG_SYS_LS_PFE_ESBC_LENGTH,
|
|
hdr_addr);
|
|
if (ret) {
|
|
printf("SF: failed to read pfe esbc header\n");
|
|
free(addr);
|
|
free(hdr_addr);
|
|
spi_flash_free(pfe_flash);
|
|
return ret;
|
|
}
|
|
|
|
pfe_esbc_hdr_addr = hdr_addr;
|
|
#endif
|
|
pfe_fit_addr = addr;
|
|
spi_flash_free(pfe_flash);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* PFE firmware initialization.
|
|
* Loads different firmware files from FIT image.
|
|
* Initializes PE IMEM/DMEM and UTIL-PE DDR
|
|
* Initializes control path symbol addresses (by looking them up in the elf
|
|
* firmware files
|
|
* Takes PE's out of reset
|
|
*
|
|
* Return: 0 on success, a negative value on error
|
|
*/
|
|
int pfe_firmware_init(void)
|
|
{
|
|
#define PFE_KEY_HASH NULL
|
|
char *pfe_firmware_name;
|
|
const void *raw_image_addr;
|
|
size_t raw_image_size = 0;
|
|
u8 *pfe_firmware;
|
|
#ifdef CONFIG_CHAIN_OF_TRUST
|
|
uintptr_t pfe_esbc_hdr = 0;
|
|
uintptr_t pfe_img_addr = 0;
|
|
#endif
|
|
int ret = 0;
|
|
int fw_count, max_fw_count;
|
|
const char *p;
|
|
|
|
ret = pfe_spi_flash_init();
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = pfe_fit_check();
|
|
if (ret)
|
|
goto err;
|
|
|
|
#ifdef CONFIG_CHAIN_OF_TRUST
|
|
pfe_esbc_hdr = (uintptr_t)pfe_esbc_hdr_addr;
|
|
pfe_img_addr = (uintptr_t)pfe_fit_addr;
|
|
if (fsl_check_boot_mode_secure() != 0) {
|
|
/*
|
|
* In case of failure in validation, fsl_secboot_validate
|
|
* would not return back in case of Production environment
|
|
* with ITS=1. In Development environment (ITS=0 and
|
|
* SB_EN=1), the function may return back in case of
|
|
* non-fatal failures.
|
|
*/
|
|
ret = fsl_secboot_validate(pfe_esbc_hdr,
|
|
PFE_KEY_HASH,
|
|
&pfe_img_addr);
|
|
if (ret != 0)
|
|
printf("PFE firmware(s) validation failed\n");
|
|
else
|
|
printf("PFE firmware(s) validation Successful\n");
|
|
}
|
|
#endif
|
|
|
|
p = env_get("load_util");
|
|
if (!p) {
|
|
max_fw_count = 2;
|
|
} else {
|
|
max_fw_count = dectoul(p, NULL);
|
|
if (max_fw_count)
|
|
max_fw_count = 3;
|
|
else
|
|
max_fw_count = 2;
|
|
}
|
|
|
|
for (fw_count = 0; fw_count < max_fw_count; fw_count++) {
|
|
switch (fw_count) {
|
|
case 0:
|
|
pfe_firmware_name = "class_slowpath";
|
|
break;
|
|
case 1:
|
|
pfe_firmware_name = "tmu_slowpath";
|
|
break;
|
|
case 2:
|
|
pfe_firmware_name = "util_slowpath";
|
|
break;
|
|
}
|
|
|
|
if (pfe_get_fw(&raw_image_addr, &raw_image_size,
|
|
pfe_firmware_name)) {
|
|
printf("%s firmware couldn't be found in FIT image\n",
|
|
pfe_firmware_name);
|
|
break;
|
|
}
|
|
pfe_firmware = malloc(raw_image_size);
|
|
if (!pfe_firmware)
|
|
return -ENOMEM;
|
|
memcpy((void *)pfe_firmware, (void *)raw_image_addr,
|
|
raw_image_size);
|
|
|
|
switch (fw_count) {
|
|
case 0:
|
|
env_set_addr("class_elf_firmware", pfe_firmware);
|
|
env_set_addr("class_elf_size", (void *)raw_image_size);
|
|
break;
|
|
case 1:
|
|
env_set_addr("tmu_elf_firmware", pfe_firmware);
|
|
env_set_addr("tmu_elf_size", (void *)raw_image_size);
|
|
break;
|
|
case 2:
|
|
env_set_addr("util_elf_firmware", pfe_firmware);
|
|
env_set_addr("util_elf_size", (void *)raw_image_size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
raw_image_addr = NULL;
|
|
pfe_firmware = NULL;
|
|
raw_image_size = 0;
|
|
for (fw_count = 0; fw_count < 2; fw_count++) {
|
|
if (fw_count == 0)
|
|
pfe_firmware_name = "class";
|
|
else if (fw_count == 1)
|
|
pfe_firmware_name = "tmu";
|
|
|
|
pfe_get_fw(&raw_image_addr, &raw_image_size, pfe_firmware_name);
|
|
pfe_firmware = malloc(raw_image_size);
|
|
if (!pfe_firmware)
|
|
return -ENOMEM;
|
|
memcpy((void *)pfe_firmware, (void *)raw_image_addr,
|
|
raw_image_size);
|
|
|
|
if (fw_count == 0)
|
|
ret = pfe_load_elf(CLASS_MASK, pfe_firmware);
|
|
else if (fw_count == 1)
|
|
ret = pfe_load_elf(TMU_MASK, pfe_firmware);
|
|
|
|
if (ret < 0) {
|
|
printf("%s: %s firmware load failed\n", __func__,
|
|
pfe_firmware_name);
|
|
goto err;
|
|
}
|
|
debug("%s: %s firmware loaded\n", __func__, pfe_firmware_name);
|
|
free(pfe_firmware);
|
|
}
|
|
|
|
tmu_enable(0xb);
|
|
class_enable();
|
|
gpi_enable(HGPI_BASE_ADDR);
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* PFE firmware cleanup
|
|
* Puts PE's in reset
|
|
*/
|
|
void pfe_firmware_exit(void)
|
|
{
|
|
debug("%s\n", __func__);
|
|
|
|
class_disable();
|
|
tmu_disable(0xf);
|
|
hif_tx_disable();
|
|
hif_rx_disable();
|
|
}
|