diff options
-rw-r--r-- | arch/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/Kconfig | 46 | ||||
-rw-r--r-- | arch/arm/dts/sama7g5-pinfunc.h | 2 | ||||
-rw-r--r-- | arch/arm/lib/semihosting.c | 181 | ||||
-rw-r--r-- | arch/riscv/include/asm/spl.h | 1 | ||||
-rw-r--r-- | arch/riscv/lib/Makefile | 2 | ||||
-rw-r--r-- | arch/riscv/lib/interrupts.c | 25 | ||||
-rw-r--r-- | arch/riscv/lib/semihosting.c | 24 | ||||
-rw-r--r-- | arch/sandbox/cpu/sdl.c | 11 | ||||
-rw-r--r-- | arch/sandbox/include/asm/test.h | 10 | ||||
-rw-r--r-- | cmd/sound.c | 2 | ||||
-rw-r--r-- | common/spl/Kconfig | 2 | ||||
-rw-r--r-- | doc/usage/cmd/sound.rst | 41 | ||||
-rw-r--r-- | doc/usage/index.rst | 1 | ||||
-rw-r--r-- | drivers/sound/sandbox.c | 9 | ||||
-rw-r--r-- | drivers/sound/sound.c | 5 | ||||
-rw-r--r-- | drivers/usb/gadget/f_dfu.c | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/rndis.c | 9 | ||||
-rw-r--r-- | include/dfu.h | 2 | ||||
-rw-r--r-- | include/semihosting.h | 11 | ||||
-rw-r--r-- | lib/Kconfig | 47 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/semihosting.c | 186 | ||||
-rw-r--r-- | test/dm/sound.c | 11 |
24 files changed, 395 insertions, 245 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index ae397166979..102956d24c6 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -111,7 +111,7 @@ config RISCV select SUPPORT_OF_CONTROL select OF_CONTROL select DM - select SPL_SEPARATE_BSS if SPL + imply SPL_SEPARATE_BSS if SPL imply DM_SERIAL imply DM_ETH imply DM_EVENT diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3f68d0988b7..cac4fa09fd3 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -413,52 +413,6 @@ config ARM_SMCCC This should be enabled if U-Boot needs to communicate with system firmware (for example, PSCI) according to SMCCC. -config SEMIHOSTING - bool "Support ARM semihosting" - help - Semihosting is a method for a target to communicate with a host - debugger. It uses special instructions which the debugger will trap - on and interpret. This allows U-Boot to read/write files, print to - the console, and execute arbitrary commands on the host system. - - Enabling this option will add support for reading and writing files - on the host system. If you don't have a debugger attached then trying - to do this will likely cause U-Boot to hang. Say 'n' if you are unsure. - -config SEMIHOSTING_FALLBACK - bool "Recover gracefully when semihosting fails" - depends on SEMIHOSTING && ARM64 - default y - help - Normally, if U-Boot makes a semihosting call and no debugger is - attached, then it will panic due to a synchronous abort - exception. This config adds an exception handler which will allow - U-Boot to recover. Say 'y' if unsure. - -config SPL_SEMIHOSTING - bool "Support ARM semihosting in SPL" - depends on SPL - help - Semihosting is a method for a target to communicate with a host - debugger. It uses special instructions which the debugger will trap - on and interpret. This allows U-Boot to read/write files, print to - the console, and execute arbitrary commands on the host system. - - Enabling this option will add support for reading and writing files - on the host system. If you don't have a debugger attached then trying - to do this will likely cause U-Boot to hang. Say 'n' if you are unsure. - -config SPL_SEMIHOSTING_FALLBACK - bool "Recover gracefully when semihosting fails in SPL" - depends on SPL_SEMIHOSTING && ARM64 - select ARMV8_SPL_EXCEPTION_VECTORS - default y - help - Normally, if U-Boot makes a semihosting call and no debugger is - attached, then it will panic due to a synchronous abort - exception. This config adds an exception handler which will allow - U-Boot to recover. Say 'y' if unsure. - config SYS_THUMB_BUILD bool "Build U-Boot using the Thumb instruction set" depends on !ARM64 diff --git a/arch/arm/dts/sama7g5-pinfunc.h b/arch/arm/dts/sama7g5-pinfunc.h index b77185f8eda..a17707ba60a 100644 --- a/arch/arm/dts/sama7g5-pinfunc.h +++ b/arch/arm/dts/sama7g5-pinfunc.h @@ -673,7 +673,7 @@ #define PIN_PD8__GPIO PINMUX_PIN(PIN_PD8, 0, 0) #define PIN_PD8__SDMMC2_DAT3 PINMUX_PIN(PIN_PD8, 1, 1) #define PIN_PD8__I2SMCC0_DIN0 PINMUX_PIN(PIN_PD8, 3, 1) -#define PIN_PD8__A11_NANDCLE PINMUX_PIN(PIN_PD8, 4, 2) +#define PIN_PD8__A22_NANDCLE PINMUX_PIN(PIN_PD8, 4, 2) #define PIN_PD8__TIOA2 PINMUX_PIN(PIN_PD8, 5, 2) #define PIN_PD8__FLEXCOM11_IO0 PINMUX_PIN(PIN_PD8, 6, 5) #define PIN_PD9 105 diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c index 939c0f75132..7b7669bed06 100644 --- a/arch/arm/lib/semihosting.c +++ b/arch/arm/lib/semihosting.c @@ -5,20 +5,6 @@ */ #include <common.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 SYSERRNO 0x13 /* * Macro to force the compiler to *populate* memory (for an array or struct) @@ -39,7 +25,7 @@ /* * Call the handler */ -static long smh_trap(unsigned int sysnum, void *addr) +long smh_trap(unsigned int sysnum, void *addr) { register long result asm("r0"); register void *_addr asm("r1") = addr; @@ -59,168 +45,3 @@ static long smh_trap(unsigned int sysnum, void *addr) return result; } - -#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 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; - struct smh_open_s { - const char *fname; - unsigned long mode; - size_t len; - } open; - - debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode); - - open.fname = fname; - open.len = strlen(fname); - open.mode = mode; - - /* Open the file on the host */ - fd = smh_trap(SYSOPEN, &open); - if (fd == -1) - return smh_errno(); - return fd; -} - -/** - * 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 - */ -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_rdwr_s read; - - debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); - - read.fd = fd; - read.memp = memp; - read.len = len; - - ret = smh_trap(SYSREAD, &read); - 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; -} - -long smh_close(long fd) -{ - long ret; - - debug("%s: fd %ld\n", __func__, fd); - - ret = smh_trap(SYSCLOSE, &fd); - if (ret == -1) - return smh_errno(); - return 0; -} - -long smh_flen(long fd) -{ - long ret; - - debug("%s: fd %ld\n", __func__, fd); - - ret = smh_trap(SYSFLEN, &fd); - if (ret == -1) - return smh_errno(); - return ret; -} - -long smh_seek(long fd, long pos) -{ - long ret; - struct smh_seek_s { - long fd; - long pos; - } seek; - - debug("%s: fd %ld pos %ld\n", __func__, fd, pos); - - seek.fd = fd; - seek.pos = pos; - - ret = smh_trap(SYSSEEK, &seek); - if (ret) - return smh_errno(); - return 0; -} - -int smh_getc(void) -{ - return smh_trap(SYSREADC, NULL); -} - -void smh_putc(char ch) -{ - smh_trap(SYSWRITEC, &ch); -} - -void smh_puts(const char *s) -{ - smh_trap(SYSWRITE0, (char *)s); -} diff --git a/arch/riscv/include/asm/spl.h b/arch/riscv/include/asm/spl.h index e8a94fcb1fe..2898a770ee2 100644 --- a/arch/riscv/include/asm/spl.h +++ b/arch/riscv/include/asm/spl.h @@ -25,6 +25,7 @@ enum { BOOT_DEVICE_DFU, BOOT_DEVICE_XIP, BOOT_DEVICE_BOOTROM, + BOOT_DEVICE_SMH, BOOT_DEVICE_NONE }; diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index d6a8ae97284..e5a81ba7223 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -42,3 +42,5 @@ extra-$(CONFIG_EFI) += $(EFI_CRT0) $(EFI_RELOC) obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMSET) += memset.o obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMMOVE) += memmove.o obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMCPY) += memcpy.o + +obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o diff --git a/arch/riscv/lib/interrupts.c b/arch/riscv/lib/interrupts.c index 100be2e9662..e966afa7e3e 100644 --- a/arch/riscv/lib/interrupts.c +++ b/arch/riscv/lib/interrupts.c @@ -9,6 +9,7 @@ * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com> */ +#include <linux/compat.h> #include <common.h> #include <efi_loader.h> #include <hang.h> @@ -17,6 +18,7 @@ #include <asm/ptrace.h> #include <asm/system.h> #include <asm/encoding.h> +#include <semihosting.h> DECLARE_GLOBAL_DATA_PTR; @@ -149,6 +151,29 @@ ulong handle_trap(ulong cause, ulong epc, ulong tval, struct pt_regs *regs) /* An UEFI application may have changed gd. Restore U-Boot's gd. */ efi_restore_gd(); + if (cause == CAUSE_BREAKPOINT && + CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)) { + ulong pre_addr = epc - 4, post_addr = epc + 4; + + /* Check for prior and post addresses to be in same page. */ + if ((pre_addr & ~(PAGE_SIZE - 1)) == + (post_addr & ~(PAGE_SIZE - 1))) { + u32 pre = *(u32 *)pre_addr; + u32 post = *(u32 *)post_addr; + + /* Check for semihosting, i.e.: + * slli zero,zero,0x1f + * ebreak + * srai zero,zero,0x7 + */ + if (pre == 0x01f01013 && post == 0x40705013) { + disable_semihosting(); + epc += 4; + return epc; + } + } + } + is_irq = (cause & MCAUSE_INT); irq = (cause & ~MCAUSE_INT); diff --git a/arch/riscv/lib/semihosting.c b/arch/riscv/lib/semihosting.c new file mode 100644 index 00000000000..d6593b02a6f --- /dev/null +++ b/arch/riscv/lib/semihosting.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Ventana Micro Systems Inc. + */ + +#include <common.h> + +long smh_trap(int sysnum, void *addr) +{ + register int ret asm ("a0") = sysnum; + register void *param0 asm ("a1") = addr; + + asm volatile (".align 4\n" + ".option push\n" + ".option norvc\n" + + "slli zero, zero, 0x1f\n" + "ebreak\n" + "srai zero, zero, 7\n" + ".option pop\n" + : "+r" (ret) : "r" (param0) : "memory"); + + return ret; +} diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c index f4ca36b35c8..2c570ed8d16 100644 --- a/arch/sandbox/cpu/sdl.c +++ b/arch/sandbox/cpu/sdl.c @@ -441,7 +441,6 @@ void sandbox_sdl_fill_audio(void *udata, Uint8 *stream, int len) { struct buf_info *buf; int avail; - bool have_data = false; int i; for (i = 0; i < 2; i++) { @@ -453,10 +452,9 @@ void sandbox_sdl_fill_audio(void *udata, Uint8 *stream, int len) } if (avail > len) avail = len; - have_data = true; - SDL_MixAudio(stream, buf->data + buf->pos, avail, - SDL_MIX_MAXVOLUME); + memcpy(stream, buf->data + buf->pos, avail); + stream += avail; buf->pos += avail; len -= avail; @@ -466,7 +464,8 @@ void sandbox_sdl_fill_audio(void *udata, Uint8 *stream, int len) else break; } - sdl.stopping = !have_data; + memset(stream, 0, len); + sdl.stopping = !!len; } int sandbox_sdl_sound_init(int rate, int channels) @@ -484,7 +483,7 @@ int sandbox_sdl_sound_init(int rate, int channels) wanted.freq = rate; wanted.format = AUDIO_S16; wanted.channels = channels; - wanted.samples = 1024; /* Good low-latency value for callback */ + wanted.samples = 960; /* Good low-latency value for callback */ wanted.callback = sandbox_sdl_fill_audio; wanted.userdata = NULL; diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index 0406085917f..568738c16d5 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -189,6 +189,16 @@ int sandbox_get_setup_called(struct udevice *dev); int sandbox_get_sound_active(struct udevice *dev); /** + * sandbox_get_sound_count() - Read back the count of the sound data so far + * + * This data is provided to the sandbox driver by the sound play() method. + * + * @dev: Device to check + * Return: count of audio data + */ +int sandbox_get_sound_count(struct udevice *dev); + +/** * sandbox_get_sound_sum() - Read back the sum of the sound data so far * * This data is provided to the sandbox driver by the sound play() method. diff --git a/cmd/sound.c b/cmd/sound.c index f82f2aa6708..20ac3f758e3 100644 --- a/cmd/sound.c +++ b/cmd/sound.c @@ -86,5 +86,5 @@ U_BOOT_CMD( sound, 4, 1, do_sound, "sound sub-system", "init - initialise the sound driver\n" - "sound play [len] [freq] - play a sound for len ms at freq hz\n" + "sound play [len [freq]] - play a sound for len ms at freq Hz\n" ); diff --git a/common/spl/Kconfig b/common/spl/Kconfig index fef01bdd7da..6c4848f3b9d 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -691,7 +691,7 @@ config SPL_FS_FAT config SPL_FS_LOAD_PAYLOAD_NAME string "File to load for U-Boot from the filesystem" - depends on SPL_FS_EXT4 || SPL_FS_FAT || SPL_FS_SQUASHFS + depends on SPL_FS_EXT4 || SPL_FS_FAT || SPL_FS_SQUASHFS || SPL_SEMIHOSTING default "tispl.bin" if SYS_K3_SPL_ATF default "u-boot.itb" if SPL_LOAD_FIT default "u-boot.img" diff --git a/doc/usage/cmd/sound.rst b/doc/usage/cmd/sound.rst new file mode 100644 index 00000000000..d3fac243b13 --- /dev/null +++ b/doc/usage/cmd/sound.rst @@ -0,0 +1,41 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright 2022, Heinrich Schuchardt <xypron.glpk@gmx.de> + +sound command +============= + +Synopsis +-------- + +:: + + sound init + sound play [len [freq]] + +Description +----------- + +The *sound* command is used to play a beep sound. + +sound init + initializes the sound driver. + +sound play + plays a square wave sound. It does not depend on previously calling + *sound init*. + +len + duration of the sound in ms, defaults to 1000 ms + +freq + frequency of the sound in Hz, defaults to 400 Hz + +Configuration +------------- + +The sound command is enabled by CONFIG_CMD_SOUND=y. + +Return value +------------ + +The return value $? is 0 (true) if the command succeeds, 1 (false) otherwise. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 0bc82887e9e..bbd40a6e189 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -73,6 +73,7 @@ Shell commands cmd/scp03 cmd/setexpr cmd/size + cmd/sound cmd/temperature cmd/tftpput cmd/true diff --git a/drivers/sound/sandbox.c b/drivers/sound/sandbox.c index 4a2c87a84c6..c6cbd81fdbc 100644 --- a/drivers/sound/sandbox.c +++ b/drivers/sound/sandbox.c @@ -29,6 +29,7 @@ struct sandbox_i2s_priv { struct sandbox_sound_priv { int setup_called; /* Incremented when setup() method is called */ bool active; /* TX data is being sent */ + int count; /* Use to count the provided audio data */ int sum; /* Use to sum the provided audio data */ bool allow_beep; /* true to allow the start_beep() interface */ int frequency_hz; /* Beep frequency if active, else 0 */ @@ -68,6 +69,13 @@ int sandbox_get_sound_active(struct udevice *dev) return priv->active; } +int sandbox_get_sound_count(struct udevice *dev) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + return priv->count; +} + int sandbox_get_sound_sum(struct udevice *dev) { struct sandbox_sound_priv *priv = dev_get_priv(dev); @@ -168,6 +176,7 @@ static int sandbox_sound_play(struct udevice *dev, void *data, uint data_size) for (i = 0; i < data_size; i++) priv->sum += ((uint8_t *)data)[i]; + priv->count += data_size; return i2s_tx_data(uc_priv->i2s, data, data_size); } diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c index 041dfdccfeb..c0fc50c99da 100644 --- a/drivers/sound/sound.c +++ b/drivers/sound/sound.c @@ -15,7 +15,10 @@ void sound_create_square_wave(uint sample_rate, unsigned short *data, int size, const int period = freq ? sample_rate / freq : 0; const int half = period / 2; - assert(freq); + if (!half) { + memset(data, 0, size); + return; + } /* Make sure we don't overflow our buffer */ if (size % 2) diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 33ef62f8bab..44877df4ec6 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -325,7 +325,7 @@ static int state_dfu_idle(struct f_dfu *f_dfu, switch (ctrl->bRequest) { case USB_REQ_DFU_DNLOAD: - if (ctrl->bRequestType == USB_DIR_OUT) { + if (!(ctrl->bRequestType & USB_DIR_IN)) { if (len == 0) { f_dfu->dfu_state = DFU_STATE_dfuERROR; value = RET_STALL; @@ -337,7 +337,7 @@ static int state_dfu_idle(struct f_dfu *f_dfu, } break; case USB_REQ_DFU_UPLOAD: - if (ctrl->bRequestType == USB_DIR_IN) { + if (ctrl->bRequestType & USB_DIR_IN) { f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; f_dfu->blk_seq_num = 0; value = handle_upload(req, len); @@ -436,7 +436,7 @@ static int state_dfu_dnload_idle(struct f_dfu *f_dfu, switch (ctrl->bRequest) { case USB_REQ_DFU_DNLOAD: - if (ctrl->bRequestType == USB_DIR_OUT) { + if (!(ctrl->bRequestType & USB_DIR_IN)) { f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; f_dfu->blk_seq_num = w_value; value = handle_dnload(gadget, len); @@ -527,7 +527,7 @@ static int state_dfu_upload_idle(struct f_dfu *f_dfu, switch (ctrl->bRequest) { case USB_REQ_DFU_UPLOAD: - if (ctrl->bRequestType == USB_DIR_IN) { + if (ctrl->bRequestType & USB_DIR_IN) { /* state transition if less data then requested */ f_dfu->blk_seq_num = w_value; value = handle_upload(req, len); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 13c327ea38a..3948f2cc9a4 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -855,14 +855,17 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) rndis_set_cmplt_type *resp; rndis_resp_t *r; + BufLength = get_unaligned_le32(&buf->InformationBufferLength); + BufOffset = get_unaligned_le32(&buf->InformationBufferOffset); + if ((BufOffset > RNDIS_MAX_TOTAL_SIZE - 8) || + (BufLength > RNDIS_MAX_TOTAL_SIZE - 8 - BufOffset)) + return -EINVAL; + r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_set_cmplt_type *) r->buf; - BufLength = get_unaligned_le32(&buf->InformationBufferLength); - BufOffset = get_unaligned_le32(&buf->InformationBufferOffset); - #ifdef VERBOSE debug("%s: Length: %d\n", __func__, BufLength); debug("%s: Offset: %d\n", __func__, BufOffset); diff --git a/include/dfu.h b/include/dfu.h index dcb9cd9d799..07922224ef1 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -495,7 +495,7 @@ static inline int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, } #endif -#ifdef CONFIG_DFU_VIRT +#if CONFIG_IS_ENABLED(DFU_VIRT) int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, char **argv, int argc); int dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset, diff --git a/include/semihosting.h b/include/semihosting.h index f1f73464e4f..4e844cbad87 100644 --- a/include/semihosting.h +++ b/include/semihosting.h @@ -17,6 +17,17 @@ #define SMH_T32_SVC 0xDFAB #define SMH_T32_HLT 0xBABC +/** + * smh_trap() - ARCH-specific semihosting call. + * + * Semihosting library/driver can use this function to do the + * actual semihosting calls. + * + * Return: Error code defined by semihosting spec. + */ + +long smh_trap(unsigned int sysnum, void *addr); + #if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) /** * semihosting_enabled() - Determine whether semihosting is supported diff --git a/lib/Kconfig b/lib/Kconfig index 6abe1d0a863..3c5a4ab3861 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -71,6 +71,53 @@ config HAVE_PRIVATE_LIBGCC config LIB_UUID bool +config SEMIHOSTING + bool "Support semihosting" + depends on ARM || RISCV + help + Semihosting is a method for a target to communicate with a host + debugger. It uses special instructions which the debugger will trap + on and interpret. This allows U-Boot to read/write files, print to + the console, and execute arbitrary commands on the host system. + + Enabling this option will add support for reading and writing files + on the host system. If you don't have a debugger attached then trying + to do this will likely cause U-Boot to hang. Say 'n' if you are unsure. + +config SEMIHOSTING_FALLBACK + bool "Recover gracefully when semihosting fails" + depends on SEMIHOSTING && (ARM64 || RISCV) + default y + help + Normally, if U-Boot makes a semihosting call and no debugger is + attached, then it will panic due to a synchronous abort + exception. This config adds an exception handler which will allow + U-Boot to recover. Say 'y' if unsure. + +config SPL_SEMIHOSTING + bool "Support semihosting in SPL" + depends on SPL && (ARM || RISCV) + help + Semihosting is a method for a target to communicate with a host + debugger. It uses special instructions which the debugger will trap + on and interpret. This allows U-Boot to read/write files, print to + the console, and execute arbitrary commands on the host system. + + Enabling this option will add support for reading and writing files + on the host system. If you don't have a debugger attached then trying + to do this will likely cause U-Boot to hang. Say 'n' if you are unsure. + +config SPL_SEMIHOSTING_FALLBACK + bool "Recover gracefully when semihosting fails in SPL" + depends on SPL_SEMIHOSTING && (ARM64 || RISCV) + select ARMV8_SPL_EXCEPTION_VECTORS if ARM64 + default y + help + Normally, if U-Boot makes a semihosting call and no debugger is + attached, then it will panic due to a synchronous abort + exception. This config adds an exception handler which will allow + U-Boot to recover. Say 'y' if unsure. + config PRINTF bool default y diff --git a/lib/Makefile b/lib/Makefile index f2cfd1e4289..d77b33e7f48 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -146,6 +146,8 @@ obj-y += date.o obj-y += rtc-lib.o obj-$(CONFIG_LIB_ELF) += elf.o +obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o + # # Build a fast OID lookup registry from include/linux/oid_registry.h # diff --git a/lib/semihosting.c b/lib/semihosting.c new file mode 100644 index 00000000000..831774e3566 --- /dev/null +++ b/lib/semihosting.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com> + * Copyright 2014 Broadcom Corporation + */ + +#include <common.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 SYSERRNO 0x13 + +#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 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; + struct smh_open_s { + const char *fname; + unsigned long mode; + size_t len; + } open; + + debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode); + + open.fname = fname; + open.len = strlen(fname); + open.mode = mode; + + /* Open the file on the host */ + fd = smh_trap(SYSOPEN, &open); + if (fd == -1) + return smh_errno(); + return fd; +} + +/** + * 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 + */ +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_rdwr_s read; + + debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); + + read.fd = fd; + read.memp = memp; + read.len = len; + + ret = smh_trap(SYSREAD, &read); + 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; +} + +long smh_close(long fd) +{ + long ret; + + debug("%s: fd %ld\n", __func__, fd); + + ret = smh_trap(SYSCLOSE, &fd); + if (ret == -1) + return smh_errno(); + return 0; +} + +long smh_flen(long fd) +{ + long ret; + + debug("%s: fd %ld\n", __func__, fd); + + ret = smh_trap(SYSFLEN, &fd); + if (ret == -1) + return smh_errno(); + return ret; +} + +long smh_seek(long fd, long pos) +{ + long ret; + struct smh_seek_s { + long fd; + long pos; + } seek; + + debug("%s: fd %ld pos %ld\n", __func__, fd, pos); + + seek.fd = fd; + seek.pos = pos; + + ret = smh_trap(SYSSEEK, &seek); + if (ret) + return smh_errno(); + return 0; +} + +int smh_getc(void) +{ + return smh_trap(SYSREADC, NULL); +} + +void smh_putc(char ch) +{ + smh_trap(SYSWRITEC, &ch); +} + +void smh_puts(const char *s) +{ + smh_trap(SYSWRITE0, (char *)s); +} diff --git a/test/dm/sound.c b/test/dm/sound.c index b73f6ab1113..15d545ab5a3 100644 --- a/test/dm/sound.c +++ b/test/dm/sound.c @@ -26,8 +26,19 @@ static int dm_test_sound(struct unit_test_state *uts) ut_asserteq(0, sandbox_get_setup_called(dev)); ut_assertok(sound_beep(dev, 1, 100)); + ut_asserteq(48, sandbox_get_sound_count(dev)); ut_asserteq(4560, sandbox_get_sound_sum(dev)); ut_assertok(sound_beep(dev, 1, 100)); + ut_asserteq(96, sandbox_get_sound_count(dev)); + ut_asserteq(9120, sandbox_get_sound_sum(dev)); + ut_assertok(sound_beep(dev, 1, -100)); + ut_asserteq(144, sandbox_get_sound_count(dev)); + ut_asserteq(9120, sandbox_get_sound_sum(dev)); + ut_assertok(sound_beep(dev, 1, 0)); + ut_asserteq(192, sandbox_get_sound_count(dev)); + ut_asserteq(9120, sandbox_get_sound_sum(dev)); + ut_assertok(sound_beep(dev, 1, INT_MAX)); + ut_asserteq(240, sandbox_get_sound_count(dev)); ut_asserteq(9120, sandbox_get_sound_sum(dev)); ut_asserteq(false, sandbox_get_sound_active(dev)); |