mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-17 08:43:07 +00:00
3145e0d0ff
All `sm efuseread/efusewrite` commands exit with an error, even if the fuse
have actually been dealt with correctly.
This is because the smc call return the size it actually processed but this
result is checked against 0.
Return failure in do_efuse_read/write if the return value of
meson_sm_read/write_efuse() is not the requested size.
Fixes: 52195ba5f5
("ARM: amlogic: add sm efuse write support and cmd for read/write efuse")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lore.kernel.org/r/20220804144138.33809-1-jbrunet@baylibre.com
283 lines
6.3 KiB
C
283 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2016 Beniamino Galvani <b.galvani@gmail.com>
|
|
*
|
|
* Secure monitor calls.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <env.h>
|
|
#include <log.h>
|
|
#include <asm/arch/sm.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/global_data.h>
|
|
#include <asm/ptrace.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <dm.h>
|
|
#include <linux/bitfield.h>
|
|
#include <regmap.h>
|
|
#include <syscon.h>
|
|
|
|
#define FN_GET_SHARE_MEM_INPUT_BASE 0x82000020
|
|
#define FN_GET_SHARE_MEM_OUTPUT_BASE 0x82000021
|
|
#define FN_EFUSE_READ 0x82000030
|
|
#define FN_EFUSE_WRITE 0x82000031
|
|
#define FN_CHIP_ID 0x82000044
|
|
|
|
static void *shmem_input;
|
|
static void *shmem_output;
|
|
|
|
static void meson_init_shmem(void)
|
|
{
|
|
struct pt_regs regs;
|
|
|
|
if (shmem_input && shmem_output)
|
|
return;
|
|
|
|
regs.regs[0] = FN_GET_SHARE_MEM_INPUT_BASE;
|
|
smc_call(®s);
|
|
shmem_input = (void *)regs.regs[0];
|
|
|
|
regs.regs[0] = FN_GET_SHARE_MEM_OUTPUT_BASE;
|
|
smc_call(®s);
|
|
shmem_output = (void *)regs.regs[0];
|
|
|
|
debug("Secure Monitor shmem: 0x%p 0x%p\n", shmem_input, shmem_output);
|
|
}
|
|
|
|
ssize_t meson_sm_read_efuse(uintptr_t offset, void *buffer, size_t size)
|
|
{
|
|
struct pt_regs regs;
|
|
|
|
meson_init_shmem();
|
|
|
|
regs.regs[0] = FN_EFUSE_READ;
|
|
regs.regs[1] = offset;
|
|
regs.regs[2] = size;
|
|
|
|
smc_call(®s);
|
|
|
|
if (regs.regs[0] == 0)
|
|
return -1;
|
|
|
|
memcpy(buffer, shmem_output, min(size, regs.regs[0]));
|
|
|
|
return regs.regs[0];
|
|
}
|
|
|
|
ssize_t meson_sm_write_efuse(uintptr_t offset, void *buffer, size_t size)
|
|
{
|
|
struct pt_regs regs;
|
|
|
|
meson_init_shmem();
|
|
|
|
memcpy(shmem_input, buffer, size);
|
|
|
|
regs.regs[0] = FN_EFUSE_WRITE;
|
|
regs.regs[1] = offset;
|
|
regs.regs[2] = size;
|
|
|
|
smc_call(®s);
|
|
|
|
return regs.regs[0];
|
|
}
|
|
|
|
#define SM_CHIP_ID_LENGTH 119
|
|
#define SM_CHIP_ID_OFFSET 4
|
|
#define SM_CHIP_ID_SIZE 12
|
|
|
|
int meson_sm_get_serial(void *buffer, size_t size)
|
|
{
|
|
struct pt_regs regs;
|
|
|
|
meson_init_shmem();
|
|
|
|
regs.regs[0] = FN_CHIP_ID;
|
|
regs.regs[1] = 0;
|
|
regs.regs[2] = 0;
|
|
|
|
smc_call(®s);
|
|
|
|
memcpy(buffer, shmem_output + SM_CHIP_ID_OFFSET,
|
|
min_t(size_t, size, SM_CHIP_ID_SIZE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define AO_SEC_SD_CFG15 0xfc
|
|
#define REBOOT_REASON_MASK GENMASK(15, 12)
|
|
|
|
int meson_sm_get_reboot_reason(void)
|
|
{
|
|
struct regmap *regmap;
|
|
int nodeoffset;
|
|
ofnode node;
|
|
unsigned int reason;
|
|
|
|
/* find the offset of compatible node */
|
|
nodeoffset = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
|
|
"amlogic,meson-gx-ao-secure");
|
|
if (nodeoffset < 0) {
|
|
printf("%s: failed to get amlogic,meson-gx-ao-secure\n",
|
|
__func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* get regmap from the syscon node */
|
|
node = offset_to_ofnode(nodeoffset);
|
|
regmap = syscon_node_to_regmap(node);
|
|
if (IS_ERR(regmap)) {
|
|
printf("%s: failed to get regmap\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_read(regmap, AO_SEC_SD_CFG15, &reason);
|
|
|
|
/* The SMC call is not used, we directly use AO_SEC_SD_CFG15 */
|
|
return FIELD_GET(REBOOT_REASON_MASK, reason);
|
|
}
|
|
|
|
static int do_sm_serial(struct cmd_tbl *cmdtp, int flag, int argc,
|
|
char *const argv[])
|
|
{
|
|
ulong address;
|
|
int ret;
|
|
|
|
if (argc < 2)
|
|
return CMD_RET_USAGE;
|
|
|
|
address = simple_strtoul(argv[1], NULL, 0);
|
|
|
|
ret = meson_sm_get_serial((void *)address, SM_CHIP_ID_SIZE);
|
|
if (ret)
|
|
return CMD_RET_FAILURE;
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
#define MAX_REBOOT_REASONS 14
|
|
|
|
static const char *reboot_reasons[MAX_REBOOT_REASONS] = {
|
|
[REBOOT_REASON_COLD] = "cold_boot",
|
|
[REBOOT_REASON_NORMAL] = "normal",
|
|
[REBOOT_REASON_RECOVERY] = "recovery",
|
|
[REBOOT_REASON_UPDATE] = "update",
|
|
[REBOOT_REASON_FASTBOOT] = "fastboot",
|
|
[REBOOT_REASON_SUSPEND_OFF] = "suspend_off",
|
|
[REBOOT_REASON_HIBERNATE] = "hibernate",
|
|
[REBOOT_REASON_BOOTLOADER] = "bootloader",
|
|
[REBOOT_REASON_SHUTDOWN_REBOOT] = "shutdown_reboot",
|
|
[REBOOT_REASON_RPMBP] = "rpmbp",
|
|
[REBOOT_REASON_CRASH_DUMP] = "crash_dump",
|
|
[REBOOT_REASON_KERNEL_PANIC] = "kernel_panic",
|
|
[REBOOT_REASON_WATCHDOG_REBOOT] = "watchdog_reboot",
|
|
};
|
|
|
|
static int do_sm_reboot_reason(struct cmd_tbl *cmdtp, int flag, int argc,
|
|
char *const argv[])
|
|
{
|
|
const char *reason_str;
|
|
char *destarg = NULL;
|
|
int reason;
|
|
|
|
if (argc > 1)
|
|
destarg = argv[1];
|
|
|
|
reason = meson_sm_get_reboot_reason();
|
|
if (reason < 0)
|
|
return CMD_RET_FAILURE;
|
|
|
|
if (reason >= MAX_REBOOT_REASONS ||
|
|
!reboot_reasons[reason])
|
|
reason_str = "unknown";
|
|
else
|
|
reason_str = reboot_reasons[reason];
|
|
|
|
if (destarg)
|
|
env_set(destarg, reason_str);
|
|
else
|
|
printf("reboot reason: %s (%x)\n", reason_str, reason);
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_efuse_read(struct cmd_tbl *cmdtp, int flag, int argc,
|
|
char *const argv[])
|
|
{
|
|
ulong address, offset, size;
|
|
int ret;
|
|
|
|
if (argc < 4)
|
|
return CMD_RET_USAGE;
|
|
|
|
offset = simple_strtoul(argv[1], NULL, 0);
|
|
size = simple_strtoul(argv[2], NULL, 0);
|
|
|
|
address = simple_strtoul(argv[3], NULL, 0);
|
|
|
|
ret = meson_sm_read_efuse(offset, (void *)address, size);
|
|
if (ret != size)
|
|
return CMD_RET_FAILURE;
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_efuse_write(struct cmd_tbl *cmdtp, int flag, int argc,
|
|
char *const argv[])
|
|
{
|
|
ulong address, offset, size;
|
|
int ret;
|
|
|
|
if (argc < 4)
|
|
return CMD_RET_USAGE;
|
|
|
|
offset = simple_strtoul(argv[1], NULL, 0);
|
|
size = simple_strtoul(argv[2], NULL, 0);
|
|
|
|
address = simple_strtoul(argv[3], NULL, 0);
|
|
|
|
ret = meson_sm_write_efuse(offset, (void *)address, size);
|
|
if (ret != size)
|
|
return CMD_RET_FAILURE;
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static struct cmd_tbl cmd_sm_sub[] = {
|
|
U_BOOT_CMD_MKENT(serial, 2, 1, do_sm_serial, "", ""),
|
|
U_BOOT_CMD_MKENT(reboot_reason, 1, 1, do_sm_reboot_reason, "", ""),
|
|
U_BOOT_CMD_MKENT(efuseread, 4, 1, do_efuse_read, "", ""),
|
|
U_BOOT_CMD_MKENT(efusewrite, 4, 0, do_efuse_write, "", ""),
|
|
};
|
|
|
|
static int do_sm(struct cmd_tbl *cmdtp, int flag, int argc,
|
|
char *const argv[])
|
|
{
|
|
struct cmd_tbl *c;
|
|
|
|
if (argc < 2)
|
|
return CMD_RET_USAGE;
|
|
|
|
/* Strip off leading 'sm' command argument */
|
|
argc--;
|
|
argv++;
|
|
|
|
c = find_cmd_tbl(argv[0], &cmd_sm_sub[0], ARRAY_SIZE(cmd_sm_sub));
|
|
|
|
if (c)
|
|
return c->cmd(cmdtp, flag, argc, argv);
|
|
else
|
|
return CMD_RET_USAGE;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
sm, 5, 0, do_sm,
|
|
"Secure Monitor Control",
|
|
"serial <address> - read chip unique id to memory address\n"
|
|
"sm reboot_reason [name] - get reboot reason and store to to environment\n"
|
|
"sm efuseread <offset> <size> <address> - read efuse to memory address\n"
|
|
"sm efusewrite <offset> <size> <address> - write into efuse from memory address"
|
|
);
|