diff options
author | Tom Rini <trini@konsulko.com> | 2022-04-02 15:28:43 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-04-02 15:28:43 -0400 |
commit | 10d615f2fcc0d2ef1d611844eb6032fe0fca8afd (patch) | |
tree | fc64ed7fef66e55214c75b98a90531645a203294 /arch/arm/lib | |
parent | 5b160c3a13f80708b4a720119cd2a1ef161e57f0 (diff) | |
parent | 7a763471894feb58d5a1bdf78ea7014c7a952264 (diff) |
Merge branch '2022-04-01-arm-semihosting-cleanups-and-new-features' into next
This brings in two related series. The first from Andre:
This series is the continuation of last year's effort to support the new
Armv8-R64 application profile. This led to a significant rework of the
existing fastmodel (FVP) support, to both upgrade it to newest U-Boot
standards (OF_CONTROL and distro_boot support), but also to generalise
the code, so that plugging in the v8-R64 support in the last patch gets
much easier. This is because apart from the twisted memory map between
the two profiles there is actually little difference, when it comes to
U-Boot relevant parts of the hardware.
I kept the legacy semihosting support (which picks up magic files from
the current directory), but if that fails, we go and try virtio-blk
(.iso installer images work), then virtio-net.
Please have a look, and give it a try, if possible. Both the v8-R and
v8-A FVP models are available for free on the Arm website[1].
Patch 01/11 fixes a regression introduced in December, it should be
applied now. The rest of the patches are for the next merge window.
[1]
https://developer.arm.com/tools-and-software/simulation-models/fast-models
And the second from Sean (where we exclude 27, 28 and 29 for now):
This cleans up the semihosting code and adds the following new features:
- hostfs support (like sandbox)
- support for being used as a SPL boot device
- serial device support
- falling back to normal drivers if semihosting is disabled
The main device affected by these changes is vexpress64, so I'd
appreciate
if Andre (or anyone else) could try booting.
These changes are motivated by bringup for ls1046a. When forcing JTAG
boot, this device disables most communication peripherals, including
serial and ethernet devices. This appears to be fixed in later
generation devices, but we are stuck with it for now. Semihosting
provides an easy way to run a few console commands.
The patches in this series are organized as follows:
0-4: rST conversions and other documentation updates
5-9: Semihosting cleanups
10-14: Filesystem support (including SPL boot device)
15-16: Serial support
16: Documentation update
17: JTAG boot support for LS1046A
19-25: Semihosting fallback
26-29: DM puts support
The last two groups of patches are "bonus;" the first 17 patches stand
on their own. The last two groups could be broken out as separate
series, but I have kept them in this one to help with my sanity (and not
have to deal with too many outstanding series).
Patch 14 depends on [1] to apply cleanly. Patch 17 depends on [2] for
correctness. This series should be applied to u-boot/next (in
particular, the EROFS series must have been applied).
[1]
https://lore.kernel.org/u-boot/CACRpkdZ+9fmNjC_mvrbPa9-iuTQVd8UkJ7Zpe7cL0c5vZygsVw@mail.gmail.com/T/
[2]
https://lore.kernel.org/u-boot/20220222183840.1355337-2-sean.anderson@seco.com/
Diffstat (limited to 'arch/arm/lib')
-rw-r--r-- | arch/arm/lib/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/lib/interrupts_64.c | 80 | ||||
-rw-r--r-- | arch/arm/lib/semihosting.c | 232 |
3 files changed, 176 insertions, 138 deletions
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index c48e1f622d..594fc1228a 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -46,7 +46,7 @@ else obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMSET) += memset.o obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMCPY) += memcpy.o endif -obj-$(CONFIG_SEMIHOSTING) += semihosting.o +obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o obj-y += bdinfo.o obj-y += sections.o diff --git a/arch/arm/lib/interrupts_64.c b/arch/arm/lib/interrupts_64.c index c653e67db2..2e091415a4 100644 --- a/arch/arm/lib/interrupts_64.c +++ b/arch/arm/lib/interrupts_64.c @@ -5,11 +5,13 @@ */ #include <common.h> +#include <asm/esr.h> #include <asm/global_data.h> #include <asm/ptrace.h> #include <irq_func.h> #include <linux/compiler.h> #include <efi_loader.h> +#include <semihosting.h> DECLARE_GLOBAL_DATA_PTR; @@ -64,12 +66,55 @@ void show_regs(struct pt_regs *regs) } /* + * Try to "emulate" a semihosting call in the event that we don't have a + * debugger attached. + */ +static bool smh_emulate_trap(struct pt_regs *regs) +{ + int size; + + if (ESR_ELx_EC(regs->esr) != ESR_ELx_EC_UNKNOWN) + return false; + + if (regs->spsr & PSR_MODE32_BIT) { + if (regs->spsr & PSR_AA32_T_BIT) { + u16 *insn = (u16 *)ALIGN_DOWN(regs->elr, 2); + + if (*insn != SMH_T32_SVC && *insn != SMH_T32_HLT) + return false; + size = 2; + } else { + u32 *insn = (u32 *)ALIGN_DOWN(regs->elr, 4); + + if (*insn != SMH_A32_SVC && *insn != SMH_A32_HLT) + return false; + size = 4; + } + } else { + u32 *insn = (u32 *)ALIGN_DOWN(regs->elr, 4); + + if (*insn != SMH_A64_HLT) + return false; + size = 4; + } + + /* Avoid future semihosting calls */ + disable_semihosting(); + + /* Just pretend the call failed */ + regs->regs[0] = -1; + regs->elr += size; + return true; +} + +/* * do_bad_sync handles the impossible case in the Synchronous Abort vector. */ -void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr) +void do_bad_sync(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08x\n", esr); + printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08lx\n", + pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -78,10 +123,10 @@ void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr) /* * do_bad_irq handles the impossible case in the Irq vector. */ -void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr) +void do_bad_irq(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("Bad mode in \"Irq\" handler, esr 0x%08x\n", esr); + printf("Bad mode in \"Irq\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -90,10 +135,10 @@ void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr) /* * do_bad_fiq handles the impossible case in the Fiq vector. */ -void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr) +void do_bad_fiq(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("Bad mode in \"Fiq\" handler, esr 0x%08x\n", esr); + printf("Bad mode in \"Fiq\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -102,10 +147,10 @@ void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr) /* * do_bad_error handles the impossible case in the Error vector. */ -void do_bad_error(struct pt_regs *pt_regs, unsigned int esr) +void do_bad_error(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("Bad mode in \"Error\" handler, esr 0x%08x\n", esr); + printf("Bad mode in \"Error\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -114,10 +159,13 @@ void do_bad_error(struct pt_regs *pt_regs, unsigned int esr) /* * do_sync handles the Synchronous Abort exception. */ -void do_sync(struct pt_regs *pt_regs, unsigned int esr) +void do_sync(struct pt_regs *pt_regs) { + if (CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) && + smh_emulate_trap(pt_regs)) + return; efi_restore_gd(); - printf("\"Synchronous Abort\" handler, esr 0x%08x\n", esr); + printf("\"Synchronous Abort\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -126,10 +174,10 @@ void do_sync(struct pt_regs *pt_regs, unsigned int esr) /* * do_irq handles the Irq exception. */ -void do_irq(struct pt_regs *pt_regs, unsigned int esr) +void do_irq(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("\"Irq\" handler, esr 0x%08x\n", esr); + printf("\"Irq\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -138,10 +186,10 @@ void do_irq(struct pt_regs *pt_regs, unsigned int esr) /* * do_fiq handles the Fiq exception. */ -void do_fiq(struct pt_regs *pt_regs, unsigned int esr) +void do_fiq(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("\"Fiq\" handler, esr 0x%08x\n", esr); + printf("\"Fiq\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); @@ -153,10 +201,10 @@ void do_fiq(struct pt_regs *pt_regs, unsigned int esr) * it is defined with weak attribute and can be redefined * in processor specific code. */ -void __weak do_error(struct pt_regs *pt_regs, unsigned int esr) +void __weak do_error(struct pt_regs *pt_regs) { efi_restore_gd(); - printf("\"Error\" handler, esr 0x%08x\n", esr); + printf("\"Error\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); show_efi_loaded_images(pt_regs); panic("Resetting CPU ...\n"); diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c index 9fd82459b2..dbea2b06fb 100644 --- a/arch/arm/lib/semihosting.c +++ b/arch/arm/lib/semihosting.c @@ -1,28 +1,29 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com> * Copyright 2014 Broadcom Corporation */ /* - * Minimal semihosting implementation for reading files into memory. If more - * features like writing files or console output are required they can be - * added later. This code has been tested on arm64/aarch64 fastmodel only. - * An untested placeholder exists for armv7 architectures, but since they - * are commonly available in silicon now, fastmodel usage makes less sense - * for them. + * This code has been tested on arm64/aarch64 fastmodel only. An untested + * placeholder exists for armv7 architectures, but since they are commonly + * available in silicon now, fastmodel usage makes less sense for them. */ #include <common.h> -#include <command.h> -#include <env.h> #include <log.h> +#include <semihosting.h> #define SYSOPEN 0x01 #define SYSCLOSE 0x02 +#define SYSWRITEC 0x03 +#define SYSWRITE0 0x04 +#define SYSWRITE 0x05 #define SYSREAD 0x06 +#define SYSREADC 0x07 +#define SYSISERROR 0x08 +#define SYSSEEK 0x0A #define SYSFLEN 0x0C - -#define MODE_READ 0x0 -#define MODE_READBIN 0x1 +#define SYSERRNO 0x13 /* * Call the handler @@ -41,32 +42,54 @@ static noinline long smh_trap(unsigned int sysnum, void *addr) return result; } -/* - * Open a file on the host. Mode is "r" or "rb" currently. Returns a file - * descriptor or -1 on error. +#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) +static bool _semihosting_enabled = true; +static bool try_semihosting = true; + +bool semihosting_enabled(void) +{ + if (try_semihosting) { + smh_trap(SYSERRNO, NULL); + try_semihosting = false; + } + + return _semihosting_enabled; +} + +void disable_semihosting(void) +{ + _semihosting_enabled = false; +} +#endif + +/** + * smh_errno() - Read the host's errno + * + * This gets the value of the host's errno and negates it. The host's errno may + * or may not be set, so only call this function if a previous semihosting call + * has failed. + * + * Return: a negative error value */ -static long smh_open(const char *fname, char *modestr) +static int smh_errno(void) +{ + long ret = smh_trap(SYSERRNO, NULL); + + if (ret > 0 && ret < INT_MAX) + return -ret; + return -EIO; +} + +long smh_open(const char *fname, enum smh_open_mode mode) { long fd; - unsigned long mode; struct smh_open_s { const char *fname; unsigned long mode; size_t len; } open; - debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr); - - /* Check the file mode */ - if (!(strcmp(modestr, "r"))) { - mode = MODE_READ; - } else if (!(strcmp(modestr, "rb"))) { - mode = MODE_READBIN; - } else { - printf("%s: ERROR mode \'%s\' not supported\n", __func__, - modestr); - return -1; - } + debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode); open.fname = fname; open.len = strlen(fname); @@ -75,23 +98,26 @@ static long smh_open(const char *fname, char *modestr) /* Open the file on the host */ fd = smh_trap(SYSOPEN, &open); if (fd == -1) - printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd, - fname); - + return smh_errno(); return fd; } -/* - * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure +/** + * struct smg_rdwr_s - Arguments for read and write + * @fd: A file descriptor returned from smh_open() + * @memp: Pointer to a buffer of memory of at least @len bytes + * @len: The number of bytes to read or write */ -static long smh_read(long fd, void *memp, size_t len) +struct smh_rdwr_s { + long fd; + void *memp; + size_t len; +}; + +long smh_read(long fd, void *memp, size_t len) { long ret; - struct smh_read_s { - long fd; - void *memp; - size_t len; - } read; + struct smh_rdwr_s read; debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); @@ -100,25 +126,30 @@ static long smh_read(long fd, void *memp, size_t len) read.len = len; ret = smh_trap(SYSREAD, &read); - if (ret < 0) { - /* - * The ARM handler allows for returning partial lengths, - * but in practice this never happens so rather than create - * hard to maintain partial read loops and such, just fail - * with an error message. - */ - printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n", - __func__, ret, fd, len, memp); - return -1; - } + if (ret < 0) + return smh_errno(); + return len - ret; +} + +long smh_write(long fd, const void *memp, size_t len, ulong *written) +{ + long ret; + struct smh_rdwr_s write; + + debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); + write.fd = fd; + write.memp = (void *)memp; + write.len = len; + + ret = smh_trap(SYSWRITE, &write); + *written = len - ret; + if (ret) + return smh_errno(); return 0; } -/* - * Close the file using the file descriptor - */ -static long smh_close(long fd) +long smh_close(long fd) { long ret; @@ -126,15 +157,11 @@ static long smh_close(long fd) ret = smh_trap(SYSCLOSE, &fd); if (ret == -1) - printf("%s: ERROR fd %ld\n", __func__, fd); - - return ret; + return smh_errno(); + return 0; } -/* - * Get the file length from the file descriptor - */ -static long smh_len_fd(long fd) +long smh_flen(long fd) { long ret; @@ -142,77 +169,40 @@ static long smh_len_fd(long fd) ret = smh_trap(SYSFLEN, &fd); if (ret == -1) - printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd); - + return smh_errno(); return ret; } -static int smh_load_file(const char * const name, ulong load_addr, - ulong *end_addr) +long smh_seek(long fd, long pos) { - long fd; - long len; long ret; + struct smh_seek_s { + long fd; + long pos; + } seek; - fd = smh_open(name, "rb"); - if (fd == -1) - return -1; + debug("%s: fd %ld pos %ld\n", __func__, fd, pos); - len = smh_len_fd(fd); - if (len < 0) { - smh_close(fd); - return -1; - } - - ret = smh_read(fd, (void *)load_addr, len); - smh_close(fd); - - if (ret == 0) { - *end_addr = load_addr + len - 1; - printf("loaded file %s from %08lX to %08lX, %08lX bytes\n", - name, - load_addr, - *end_addr, - len); - } else { - printf("read failed\n"); - return 0; - } + seek.fd = fd; + seek.pos = pos; + ret = smh_trap(SYSSEEK, &seek); + if (ret) + return smh_errno(); return 0; } -static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc, - char *const argv[]) +int smh_getc(void) { - if (argc == 3 || argc == 4) { - ulong load_addr; - ulong end_addr = 0; - int ret; - char end_str[64]; - - load_addr = hextoul(argv[2], NULL); - if (!load_addr) - return -1; - - ret = smh_load_file(argv[1], load_addr, &end_addr); - if (ret < 0) - return CMD_RET_FAILURE; - - /* Optionally save returned end to the environment */ - if (argc == 4) { - sprintf(end_str, "0x%08lx", end_addr); - env_set(argv[3], end_str); - } - } else { - return CMD_RET_USAGE; - } - return 0; + return smh_trap(SYSREADC, NULL); +} + +void smh_putc(char ch) +{ + smh_trap(SYSWRITEC, &ch); } -U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting", - "<file> 0x<address> [end var]\n" - " - load a semihosted file to the address specified\n" - " if the optional [end var] is specified, the end\n" - " address of the file will be stored in this environment\n" - " variable.\n"); +void smh_puts(const char *s) +{ + smh_trap(SYSWRITE0, (char *)s); +} |