Sam Protsenko 4d8ccb938d board: samsung: e850-96: Load LDFW from EFI partition
In case when EFI System Partition is present it can be used to store
firmware binaries, instead of keeping those on separate dedicated
partitions. That simplifies the partition table and makes it more
standard. Rework the firmware loader code to look for LDFW binary at
/EFI/firmware/ldfw.bin on ESP first, and if either the partition or the
file doesn't exist -- fallback to reading it from 'ldfw' partition. This
way backward compatibility can be kept, and Android partition tables
without ESP partition can be handled too.

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
2025-02-05 19:15:12 +09:00

163 lines
3.9 KiB
C

// 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 <fs.h>
#include <linux/arm-smccc.h>
#include "fw.h"
#define EMMC_IFACE "mmc"
#define EMMC_DEV_NUM 0
#define LDFW_RAW_PART "ldfw"
#define LDFW_FAT_PART "esp"
#define LDFW_FAT_PATH "/EFI/firmware/ldfw.bin"
#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];
};
/* Load LDFW binary as a file from FAT partition */
static int read_fw_from_fat(const char *part_name, const char *path, void *buf)
{
char dev_part_str[8];
loff_t len_read;
int err;
snprintf(dev_part_str, sizeof(dev_part_str), "%d#%s", EMMC_DEV_NUM,
LDFW_FAT_PART);
err = fs_set_blk_dev(EMMC_IFACE, dev_part_str, FS_TYPE_FAT);
if (err) {
debug("%s: Can't set block device\n", __func__);
return -ENODEV;
}
err = fs_read(path, (ulong)buf, 0, 0, &len_read);
if (err) {
debug("%s: Can't read LDFW file\n", __func__);
return -EIO;
}
return 0;
}
/* Load LDFW binary from raw partition on block device into RAM buffer */
static int read_fw_from_raw(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;
/* First try to read LDFW from EFI partition, then from the raw one */
err = read_fw_from_fat(LDFW_FAT_PART, LDFW_FAT_PATH, buf);
if (err) {
err = read_fw_from_raw(LDFW_RAW_PART, 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;
}