board: samsung: e850-96: Load LDFW firmware on board init

LDFW is a Loadable Firmware which provides additional security
capabilities in EL3 monitor. For example, True Random Number Generator
(TRNG) block registers can't be accessed from EL1 (where U-Boot and
Linux kernel are running), but it's possible to access TRNG capabilities
via corresponding SMC calls, which in turn are handled by LDFW. To do
so, LDFW firmware has to be loaded first. It's stored on a raw eMMC
partition, so it has to be read into NWD (Normal World) RAM buffer, and
then loaded to SWD (Secure World) memory using the special SMC call to
EL3 monitor program. EL3_MON will load LDFW to SWD memory, more
specifically to the area starting at 0xbf700000 (with size of 7.5 MiB).
That memory area is reserved in device tree, so there shouldn't be any
collisions. After that LDFW becomes functional.

Implement LDFW firmware loading on board init. While at it, fix the
copyright date in header comments, as this board support was actually
added in 2024, not in 2020: it was probably a copy-paste mistake.

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
This commit is contained in:
Sam Protsenko 2024-07-23 13:14:36 -05:00 committed by Minkyu Kang
parent ac1a474b38
commit f04e58cc97
4 changed files with 149 additions and 4 deletions

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2020, Linaro Limited
# Copyright (C) 2024, Linaro Limited
# Sam Protsenko <semen.protsenko@linaro.org>
obj-y := e850-96.o
obj-y := e850-96.o fw.o

View File

@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020, Linaro Limited
* Sam Protsenko <semen.protsenko@linaro.org>
* Copyright (c) 2024, Linaro Ltd.
* Author: Sam Protsenko <semen.protsenko@linaro.org>
*/
#include <init.h>
#include "fw.h"
int dram_init(void)
{
@ -18,5 +19,6 @@ int dram_init_banksize(void)
int board_init(void)
{
load_ldfw();
return 0;
}

131
board/samsung/e850-96/fw.c Normal file
View File

@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2024 Linaro Ltd.
* Author: Sam Protsenko <semen.protsenko@linaro.org>
*
* Firmware loading code.
*/
#include <part.h>
#include <linux/arm-smccc.h>
#include "fw.h"
#define EMMC_IFACE "mmc"
#define EMMC_DEV_NUM 0
/* LDFW constants */
#define LDFW_PART_NAME "ldfw"
#define LDFW_NWD_ADDR 0x88000000
#define LDFW_MAGIC 0x10adab1e
#define SMC_CMD_LOAD_LDFW -0x500
#define SDM_HW_RESET_STATUS 0x1230
#define SDM_SW_RESET_STATUS 0x1231
#define SB_ERROR_PREFIX 0xfdaa0000
struct ldfw_header {
u32 magic;
u32 size;
u32 init_entry;
u32 entry_point;
u32 suspend_entry;
u32 resume_entry;
u32 start_smc_id;
u32 version;
u32 set_runtime_entry;
u32 reserved[3];
char fw_name[16];
};
static int read_fw(const char *part_name, void *buf)
{
struct blk_desc *blk_desc;
struct disk_partition part;
unsigned long cnt;
int part_num;
blk_desc = blk_get_dev(EMMC_IFACE, EMMC_DEV_NUM);
if (!blk_desc) {
debug("%s: Can't get eMMC device\n", __func__);
return -ENODEV;
}
part_num = part_get_info_by_name(blk_desc, part_name, &part);
if (part_num < 0) {
debug("%s: Can't get LDWF partition\n", __func__);
return -ENOENT;
}
cnt = blk_dread(blk_desc, part.start, part.size, buf);
if (cnt != part.size) {
debug("%s: Can't read LDFW partition\n", __func__);
return -EIO;
}
return 0;
}
int load_ldfw(void)
{
const phys_addr_t addr = (phys_addr_t)LDFW_NWD_ADDR;
struct ldfw_header *hdr;
struct arm_smccc_res res;
void *buf = (void *)addr;
u64 size = 0;
int err, i;
/* Load LDFW from the block device partition into RAM buffer */
err = read_fw(LDFW_PART_NAME, buf);
if (err)
return err;
/* Validate LDFW by magic number in its header */
hdr = buf;
if (hdr->magic != LDFW_MAGIC) {
debug("%s: Wrong LDFW magic; is LDFW flashed?\n", __func__);
return -EINVAL;
}
/* Calculate actual total size of all LDFW blobs */
for (i = 0; hdr->magic == LDFW_MAGIC; ++i) {
#ifdef DEBUG
char name[17] = { 0 };
strncpy(name, hdr->fw_name, 16);
debug("%s: ldfw #%d: version = 0x%x, name = %s\n", __func__, i,
hdr->version, name);
#endif
size += (u64)hdr->size;
hdr = (struct ldfw_header *)((u64)hdr + (u64)hdr->size);
}
debug("%s: The whole size of all LDFWs: 0x%llx\n", __func__, size);
/* Load LDFW firmware to SWD (Secure World) memory via EL3 monitor */
arm_smccc_smc(SMC_CMD_LOAD_LDFW, addr, size, 0, 0, 0, 0, 0, &res);
err = (int)res.a0;
if (err == -1 || err == SDM_HW_RESET_STATUS) {
debug("%s: Can't load LDFW in dump_gpr state\n", __func__);
return -EIO;
} else if (err == SDM_SW_RESET_STATUS) {
debug("%s: Can't load LDFW in kernel panic (SW RESET) state\n",
__func__);
return -EIO;
} else if (err < 0 && (err & 0xffff0000) == SB_ERROR_PREFIX) {
debug("%s: LDFW signature is corrupted! ret=0x%x\n", __func__,
(u32)err);
return -EIO;
} else if (err == 0) {
debug("%s: No LDFW is inited\n", __func__);
return -EIO;
}
#ifdef DEBUG
u32 tried = res.a0 & 0xffff;
u32 failed = (res.a0 >> 16) & 0xffff;
debug("%s: %d/%d LDFWs have been loaded successfully\n", __func__,
tried - failed, tried);
#endif
return 0;
}

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2024 Linaro Ltd.
* Sam Protsenko <semen.protsenko@linaro.org>
*/
#ifndef __E850_96_FW_H
#define __E850_96_FW_H
int load_ldfw(void);
#endif /* __E850_96_FW_H */