Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ SOC_INFO := soc_info.c soc_info.h
FEL_LIB := fel_lib.c fel_lib.h
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h

sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-smc-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LIBFDT_CFLAGS) $(LDFLAGS) -o $@ \
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) $(LIBFDT_LIBS)

Expand Down
121 changes: 100 additions & 21 deletions fel.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ static uint32_t fel_to_spl_thunk[] = {
#include "thunks/fel-to-spl-thunk.h"
};

static uint32_t fel_to_secure_svc_smc_thunk[] = {
#include "thunks/fel-to-secure-svc-smc-thunk.h"
};

#define DRAM_BASE 0x40000000
#define DRAM_SIZE 0x80000000

Expand Down Expand Up @@ -564,36 +568,111 @@ void aw_set_sctlr(feldev_handle *dev, soc_info_t *soc_info,
aw_write_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0, sctlr);
}

/*
* Issue a "smc #0" instruction. This brings a SoC booted in "secure boot"
* state from the default non-secure FEL into secure FEL.
* This crashes on devices using "non-secure boot", as the BROM does not
* provide a handler address in MVBAR. So we have a runtime check.
*/
void aw_apply_smc_workaround(feldev_handle *dev)
static bool aw_fel_needs_smc_workaround(feldev_handle *dev)
{
soc_info_t *soc_info = dev->soc_info;
uint32_t val;

if (soc_info->secure_boot_fuse_offset) {
if (!soc_info->sid_base)
return false;
aw_fel_read(dev, soc_info->sid_base +
soc_info->secure_boot_fuse_offset,
&val, sizeof(val));
if (!le32toh(val))
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you ever seen a value other than 0 or 1 in 0x030060a0? I don't think this needs a mask; any nonzero value is secure.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The mask is gone; any nonzero value in the secure boot status word is treated as secure boot.

}

if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
return false;
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
&val, sizeof(val));

return le32toh(val) == 0;
}

static void aw_fel_execute_secure_svc_smc_thunk(feldev_handle *dev)
{
soc_info_t *soc_info = dev->soc_info;
const monitor_smc_handler *handler = soc_info->monitor_smc_handler;
const sram_swap_buffers *swap_buffers = soc_info->swap_buffers;
struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */
uint32_t arm_code[] = {
htole32(0xe1600070), /* smc #0 */
htole32(0xe12fff1e), /* bx lr */
htole32(0xe12fff1e), /* bx lr */
};
uint32_t *thunk_buf, *p;
size_t thunk_size;
size_t i;

/* Return if the SoC does not need this workaround */
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
return;
if (!handler)
pr_fatal("FEL thunk: missing monitor SMC handler info\n");

/* This has less overhead than fel_readl_n() and may be good enough */
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
&val, sizeof(val));
for (i = 0; swap_buffers[i].size; i++)
;

thunk_size = sizeof(fel_to_secure_svc_smc_thunk) +
4 * sizeof(uint32_t) +
(i + 1) * sizeof(*swap_buffers);

/* Return if the workaround is not needed or has been already applied */
if (val != 0)
if (thunk_size > soc_info->thunk_size)
pr_fatal("FEL thunk: bad size (need %zu, have %u)\n",
thunk_size, soc_info->thunk_size);

thunk_buf = malloc(thunk_size);
if (!thunk_buf)
pr_fatal("FEL thunk: failed to allocate buffer\n");
memcpy(thunk_buf, fel_to_secure_svc_smc_thunk,
sizeof(fel_to_secure_svc_smc_thunk));

p = thunk_buf + ARRAY_SIZE(fel_to_secure_svc_smc_thunk);
*p++ = soc_info->spl_addr;
*p++ = handler->vector_addr;
*p++ = handler->gicc_base;
*p++ = handler->gicd_base;
memcpy(p, swap_buffers, (i + 1) * sizeof(*swap_buffers));

for (i = 0; i < thunk_size / sizeof(uint32_t); i++)
thunk_buf[i] = htole32(thunk_buf[i]);

aw_fel_write(dev, arm_code, soc_info->spl_addr, sizeof(arm_code));
aw_fel_write(dev, thunk_buf, soc_info->thunk_addr, thunk_size);
aw_fel_execute(dev, soc_info->thunk_addr);
free(thunk_buf);

/* TODO: Try to find and fix the bug, which needs this workaround */
nanosleep(&req, NULL);
}

/*
* Apply the "smc #0" workaround. This moves a secure-boot FEL session from
* the default non-secure state into secure state.
* This crashes on devices using "non-secure boot", as the BROM does not
* provide a handler address in MVBAR. So we have a runtime check.
* Some newer SoCs need to perform the SMC and return to FEL via a thunk,
* which handles the monitor-to-SVC transition details.
*/
static void aw_apply_smc_workaround(feldev_handle *dev)
{
soc_info_t *soc_info = dev->soc_info;

/* Return if the workaround is not needed */
if (!aw_fel_needs_smc_workaround(dev))
return;

pr_info("Applying SMC workaround... ");
aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code));
aw_fel_execute(dev, soc_info->scratch_addr);
if (soc_info->smc_workaround == SMC_WORKAROUND_SECURE_SVC_SMC_THUNK) {
pr_info("Applying SMC workaround via secure-SVC SMC thunk... ");
aw_fel_execute_secure_svc_smc_thunk(dev);
} else {
uint32_t arm_code[] = {
htole32(0xe1600070), /* smc #0 */
htole32(0xe12fff1e), /* bx lr */
};

pr_info("Applying SMC workaround... ");
aw_fel_write(dev, arm_code, soc_info->scratch_addr,
sizeof(arm_code));
aw_fel_execute(dev, soc_info->scratch_addr);
}
pr_info(" done.\n");
}

Expand Down Expand Up @@ -1381,7 +1460,7 @@ int main(int argc, char **argv)
*/
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);

/* Some SoCs need the SMC workaround to enter the secure boot mode */
/* Some SoCs need the SMC workaround to enter secure state */
aw_apply_smc_workaround(handle);

/* Handle command-style arguments, in order of appearance */
Expand Down
19 changes: 18 additions & 1 deletion soc_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ sram_swap_buffers a133_sram_swap_buffers[] = {
{ .size = 0 } /* End of the table */
};

static const monitor_smc_handler h616_monitor_smc_handler = {
.vector_addr = 0x000300c0,
.gicc_base = 0x03022000,
.gicd_base = 0x03021000,
};

/*
* R329 has no SRAM A1, but a huge SRAM A2 at 0x100000. SPL and BROM uses
* this SRAM A2's first part like how other SoCs use SRAM A1. The sp and
Expand Down Expand Up @@ -397,6 +403,7 @@ soc_info_t soc_info_table[] = {
.rvbar_reg = 0x017000A0,
/* Check L.NOP in the OpenRISC reset vector */
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
.watchdog = &wd_h3_compat,
},{
.soc_id = 0x1639, /* Allwinner A80 */
Expand Down Expand Up @@ -445,6 +452,7 @@ soc_info_t soc_info_table[] = {
.sid_sections = h3_sid_maps,
/* Check L.NOP in the OpenRISC reset vector */
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
.watchdog = &wd_h3_compat,
},{
.soc_id = 0x1681, /* Allwinner V3s */
Expand Down Expand Up @@ -483,6 +491,7 @@ soc_info_t soc_info_table[] = {
.rvbar_reg = 0x017000A0,
/* Check L.NOP in the OpenRISC reset vector */
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
.watchdog = &wd_h3_compat,
},{
.soc_id = 0x1701, /* Allwinner R40 */
Expand Down Expand Up @@ -522,6 +531,7 @@ soc_info_t soc_info_table[] = {
.rvbar_reg = 0x09010040,
/* Check L.NOP in the OpenRISC reset vector */
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
.watchdog = &wd_h6_compat,
},{
.soc_id = 0x1816, /* Allwinner V536 */
Expand Down Expand Up @@ -561,6 +571,10 @@ soc_info_t soc_info_table[] = {
.rvbar_reg = 0x09010040,
.rvbar_reg_alt= 0x08100040,
.ver_reg = 0x03000024,
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
.secure_boot_fuse_offset = 0xa0,
.smc_workaround = SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
.monitor_smc_handler = &h616_monitor_smc_handler,
.watchdog = &wd_h6_compat,
},{
.soc_id = 0x1851, /* Allwinner R329 */
Expand Down Expand Up @@ -640,7 +654,10 @@ soc_info_t soc_info_table[] = {
.sid_offset = 0x200,
.sid_sections = generic_2k_sid_maps,
.rvbar_reg = 0x08100040,
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
.secure_boot_fuse_offset = 0xa0,
.smc_workaround = SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
.monitor_smc_handler = &h616_monitor_smc_handler,
.watchdog = &wd_h6_compat,
},{
.swap_buffers = NULL /* End of the table */
Expand Down
29 changes: 26 additions & 3 deletions soc_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,31 @@ typedef struct {
* - No access to the secure side of the GIC, so it can't be configured to
* be accessible from non-secure world.
* - No RMR trigger on ARMv8 cores to bring the core into AArch64.
* However it has been found out that a simple "smc" call will immediately
* return from monitor mode, but with the NS bit cleared, so access to all
* secure peripherals is suddenly possible.
* On older SoCs, a simple "smc" call returns with the NS bit cleared,
* so access to all secure peripherals is suddenly possible. Newer SoCs
* may need a secure-SVC thunk to handle the monitor-to-SVC transition
* after the SMC call before returning to FEL.
* The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to
* have a check for this condition (reading from restricted addresses
* typically returns zero) and then activate the SMC workaround if needed.
* The 'secure_boot_fuse_offset' field can be used when the SoC has an
* explicit readable secure boot status word at sid_base + offset. It is a
* gate only; a separate runtime state probe is still required to avoid
* applying the workaround on every invocation.
* The 'smc_workaround' field selects how to apply the workaround once the
* runtime checks say that it is needed.
*/
typedef enum {
SMC_WORKAROUND_DIRECT_SMC,
SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
} smc_workaround_t;

typedef struct {
uint32_t vector_addr;
uint32_t gicc_base;
uint32_t gicd_base;
} monitor_smc_handler;

typedef struct {
uint32_t soc_id; /* ID of the SoC */
const char *name; /* human-readable SoC name string */
Expand All @@ -135,6 +153,11 @@ typedef struct {
bool icache_fix;
/* Use SMC workaround (enter secure mode) if can't read from this address */
uint32_t needs_smc_workaround_if_zero_word_at_addr;
/* Require non-zero sid_base + offset before applying SMC workaround */
uint32_t secure_boot_fuse_offset;
/* How to apply the SMC workaround */
smc_workaround_t smc_workaround;
const monitor_smc_handler *monitor_smc_handler;
uint32_t sram_size; /* Usable contiguous SRAM at spl_addr */
sram_swap_buffers *swap_buffers;
} soc_info_t;
Expand Down
5 changes: 3 additions & 2 deletions thunks/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

SPL_THUNK := fel-to-spl-thunk.h
SPL_THUNK += fel-to-secure-svc-smc-thunk.h
THUNKS := clrsetbits.h
THUNKS += memcpy.h
THUNKS += readl_writel.h
Expand All @@ -24,9 +25,9 @@ OBJDUMP := $(CROSS_COMPILE)objdump

AWK_O_TO_H := LC_ALL=C awk -f objdump_to_h.awk

# The SPL thunk requires a different output format. The "style" variable for
# These thunks require the old output format. The "style" variable for
# awk controls this, and causes the htole32() conversion to be omitted.
fel-to-spl-thunk.h: fel-to-spl-thunk.S FORCE
$(SPL_THUNK): %.h: %.S FORCE
$(AS) -o $(subst .S,.o,$<) -march=armv5te $<
$(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@

Expand Down
8 changes: 4 additions & 4 deletions thunks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ usually via `sunxi-fel`.

Normally you don't need to change or (re)build anything within this folder.
Currently our main build process (via the parent directory's _Makefile_)
only includes `fel-to-spl-thunk.h` directly. Other _.h_ files are provided
**just for reference**. The main purpose of this folder is simply keeping
track of _.S_ sources, to help with possible future maintenance of the
various code snippets.
includes `fel-to-spl-thunk.h` and `fel-to-secure-svc-smc-thunk.h` directly.
Other _.h_ files are provided **just for reference**. The main purpose of this
folder is simply keeping track of _.S_ sources, to help with possible future
maintenance of the various code snippets.

Please note that any files lacking explicit license information are intended
to be covered by the project's [overall license](../LICENSE.md) (GPLv2).
Expand Down
Loading
Loading