[PATCH] ARM: Add support for semihosting
Antony Pavlov
antonynpavlov at gmail.com
Mon Oct 19 00:26:26 PDT 2015
On Sun, 18 Oct 2015 22:33:57 -0700
Andrey Smirnov <andrew.smirnov at gmail.com> wrote:
> Add semihosting API implementation and implement a filesystem driver
> to access debugging host filesystem using it.
>
> Tested on Freescale SabreSD board (i.MX6Q) using OpenOCD
>
> Signed-off-by: Andrey Smirnov <andrew.smirnov at gmail.com>
> ---
> Documentation/filesystems/smhfs.rst | 20 ++++
> arch/arm/Kconfig | 9 ++
> arch/arm/include/asm/semihosting.h | 19 +++
> arch/arm/lib/Makefile | 1 +
> arch/arm/lib/semihosting-trap.S | 28 +++++
> arch/arm/lib/semihosting.c | 227 ++++++++++++++++++++++++++++++++++++
> fs/Kconfig | 9 ++
> fs/Makefile | 1 +
> fs/smhfs.c | 177 ++++++++++++++++++++++++++++
> 9 files changed, 491 insertions(+)
> create mode 100644 Documentation/filesystems/smhfs.rst
> create mode 100644 arch/arm/include/asm/semihosting.h
> create mode 100644 arch/arm/lib/semihosting-trap.S
> create mode 100644 arch/arm/lib/semihosting.c
> create mode 100644 fs/smhfs.c
>
> diff --git a/Documentation/filesystems/smhfs.rst b/Documentation/filesystems/smhfs.rst
> new file mode 100644
> index 0000000..06b24b7
> --- /dev/null
> +++ b/Documentation/filesystems/smhfs.rst
> @@ -0,0 +1,20 @@
> +.. index:: smhfs (filesystem)
> +
> +.. _filesystems_smhfs:
> +
> +File I/O over ARM semihosting support
> +=====================================
> +
> +barebox can communicate with debug programms attached via SWD/JTAG by
^^^^^^^^^ programs?
> +means of ARM semihosting protocol.
> +
> +Not all of the I/O primitives neccessary to implement a full
> +filesystem are exposed in ARM semihosting API and because of that some
> +aspects of filesystem funcionality are missing. Implementation does
> +not have support for listing directories. This means a
> +:ref:`command_ls` to a SMHFS-mounted path will show an empty
> +directory. Nevertheless, the files are there.
> +
> +Example::
> +
> + mount -t smhfs /dev/null /mnt/smhfs
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 304b6e6..1bccca3 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -326,6 +326,15 @@ config ARM_UNWIND
> the performance is not affected. Currently, this feature
> only works with EABI compilers. If unsure say Y.
>
> +config ARM_SEMIHOSTING
> + bool "enable ARM semihosting support"
> + help
> + This option enables ARM semihosting support in barebox. ARM
> + semihosting is a communication discipline that allows code
> + running on target ARM cpu perform system calls and access
> + the data on the host computer connected to the target via
> + debugging channel (JTAG, SWD). If unsure say N
> +
> endmenu
>
> source common/Kconfig
> diff --git a/arch/arm/include/asm/semihosting.h b/arch/arm/include/asm/semihosting.h
> new file mode 100644
> index 0000000..b478dad
> --- /dev/null
> +++ b/arch/arm/include/asm/semihosting.h
> @@ -0,0 +1,19 @@
> +#ifndef __ASM_ARM_SEMIHOSTING_H
> +#define __ASM_ARM_SEMIHOSTING_H
> +
> +int semihosting_open(const char *fname, int flags);
> +int semihosting_close(int fd);
> +int semihosting_writec(char c);
> +int semihosting_write0(const char *str);
> +ssize_t semihosting_write(int fd, const void *buf, size_t count);
> +ssize_t semihosting_read(int fd, void *buf, size_t count);
> +int semihosting_readc(void);
> +int semihosting_isatty(int fd);
> +int semihosting_seek(int fd, loff_t pos);
> +int semihosting_flen(int fd);
> +int semihosting_remove(const char *fname);
> +int semihosting_rename(const char *fname1, const char *fname2);
> +int semihosting_errno(void);
> +int semihosting_system(const char *command);
> +
> +#endif
> diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
> index a328795..e1c6f5b 100644
> --- a/arch/arm/lib/Makefile
> +++ b/arch/arm/lib/Makefile
> @@ -20,6 +20,7 @@ pbl-y += runtime-offset.o
> obj-$(CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS) += memcpy.o
> obj-$(CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS) += memset.o
> obj-$(CONFIG_ARM_UNWIND) += unwind.o
> +obj-$(CONFIG_ARM_SEMIHOSTING) += semihosting-trap.o semihosting.o
> obj-$(CONFIG_MODULES) += module.o
> extra-y += barebox.lds
>
> diff --git a/arch/arm/lib/semihosting-trap.S b/arch/arm/lib/semihosting-trap.S
> new file mode 100644
> index 0000000..9e40ebf
> --- /dev/null
> +++ b/arch/arm/lib/semihosting-trap.S
> @@ -0,0 +1,28 @@
> +/*
> + * semihosting-trap.S -- Assembly code needed to make a semihosting call
> + *
> + * Copyright (c) 2015 Zodiac Inflight Innovations
> + * Author: Andrey Smirnov <andrew.smirnov at gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/linkage.h>
> +#include <asm/unified.h>
> +
> +.section .text.semihosting_trap
> +ENTRY(semihosting_trap)
> + @ In supervisor mode SVC would clobber LR
> + push {lr}
> + ARM( svc #0x123456 )
> + THUMB( svc #0xAB )
> + pop {pc}
> +ENDPROC(semihosting_trap)
> diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c
> new file mode 100644
> index 0000000..a735196
> --- /dev/null
> +++ b/arch/arm/lib/semihosting.c
> @@ -0,0 +1,227 @@
> +/*
> + * semihosting.c -- ARM Semihoting API implementation
> + *
> + * Copyright (c) 2015 Zodiac Inflight Innovations
> + * Author: Andrey Smirnov <andrew.smirnov at gmail.com>
> + *
> + * based on a smiliar code from U-Boot
> + * Copyright (c) 2014 Broadcom Corporation
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <common.h>
> +#include <command.h>
> +#include <fcntl.h>
> +
> +#ifndef O_BINARY
> +#define O_BINARY 0
> +#endif
> +
> +
> +enum {
> + SEMIHOSTING_SYS_OPEN = 0x01,
> + SEMIHOSTING_SYS_CLOSE = 0x02,
> + SEMIHOSTING_SYS_WRITEC = 0x03,
> + SEMIHOSTING_SYS_WRITE0 = 0x04,
> + SEMIHOSTING_SYS_WRITE = 0x05,
> + SEMIHOSTING_SYS_READ = 0x06,
> + SEMIHOSTING_SYS_READC = 0x07,
> + /* SYS_ISERROR is not implemented */
> + SEMIHOSTING_SYS_ISATTY = 0x09,
> + SEMIHOSTING_SYS_SEEK = 0x0a,
> + SEMIHOSTING_SYS_FLEN = 0x0c,
> + SEMIHOSTING_SYS_REMOVE = 0x0e,
> + SEMIHOSTING_SYS_RENAME = 0x0f,
> + SEMIHOSTING_SYS_TIME = 0x11,
> + SEMIHOSTING_SYS_ERRNO = 0x13,
> + /* SYS_GET_CMDLINE is not implemented */
> + /* SYS_HEAPINFO is not implemented */
> + /* angel_SWIreason_ReportException is not implemented */
> + SEMIHOSTING_SYS_SYSTEM = 0x12,
> +};
> +
> +uint32_t semihosting_trap(uint32_t sysnum, void *addr);
> +
> +static uint32_t semihosting_flags_to_mode(int flags)
> +{
> + static const int semihosting_open_modeflags[12] = {
> + O_RDONLY,
> + O_RDONLY | O_BINARY,
> + O_RDWR,
> + O_RDWR | O_BINARY,
> + O_WRONLY | O_CREAT | O_TRUNC,
> + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
> + O_RDWR | O_CREAT | O_TRUNC,
> + O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
> + O_WRONLY | O_CREAT | O_APPEND,
> + O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
> + O_RDWR | O_CREAT | O_APPEND,
> + O_RDWR | O_CREAT | O_APPEND | O_BINARY
> + };
> +
> + int i;
> + for (i = 0; i < ARRAY_SIZE(semihosting_open_modeflags); i++) {
> + if (semihosting_open_modeflags[i] == flags)
> + return i;
> + }
> +
> + return 0;
> +}
> +
> +int semihosting_open(const char *fname, int flags)
> +{
> + struct __packed {
> + uint32_t fname;
> + uint32_t mode;
> + uint32_t len;
> + } open = {
> + .fname = (uint32_t)fname,
> + .len = strlen(fname),
> + .mode = semihosting_flags_to_mode(flags),
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_OPEN, &open);
> +}
> +EXPORT_SYMBOL(semihosting_open);
> +
> +int semihosting_close(int fd)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_CLOSE, &fd);
> +}
> +EXPORT_SYMBOL(semihosting_close);
> +
> +int semihosting_writec(char c)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_WRITEC, &c);
> +}
> +EXPORT_SYMBOL(semihosting_writec);
> +
> +int semihosting_write0(const char *str)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_WRITE0, (void *)str);
> +}
> +EXPORT_SYMBOL(semihosting_write0);
> +
> +struct __packed semihosting_file_io {
> + uint32_t fd;
> + uint32_t memp;
> + uint32_t len;
> +};
> +
> +ssize_t semihosting_write(int fd, const void *buf, size_t count)
> +{
> + struct semihosting_file_io write = {
> + .fd = fd,
> + .memp = (uint32_t)buf,
> + .len = count,
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_WRITE, &write);
> +}
> +EXPORT_SYMBOL(semihosting_write);
> +
> +ssize_t semihosting_read(int fd, void *buf, size_t count)
> +{
> + struct semihosting_file_io read = {
> + .fd = fd,
> + .memp = (uint32_t)buf,
> + .len = count,
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_READ, &read);
> +}
> +EXPORT_SYMBOL(semihosting_read);
> +
> +int semihosting_readc(void)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_READC, NULL);
> +}
> +EXPORT_SYMBOL(semihosting_readc);
> +
> +int semihosting_isatty(int fd)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_ISATTY, &fd);
> +}
> +EXPORT_SYMBOL(semihosting_isatty);
> +
> +int semihosting_seek(int fd, off_t pos)
> +{
> + struct __packed {
> + uint32_t fd;
> + uint32_t pos;
> + } seek = {
> + .fd = fd,
> + .pos = pos,
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_SEEK, &seek);
> +}
> +EXPORT_SYMBOL(semihosting_seek);
> +
> +int semihosting_flen(int fd)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_FLEN, &fd);
> +}
> +EXPORT_SYMBOL(semihosting_flen);
> +
> +int semihosting_remove(const char *fname)
> +{
> + struct __packed {
> + uint32_t fname;
> + uint32_t fname_length;
> + } remove = {
> + .fname = (uint32_t)fname,
> + .fname_length = strlen(fname),
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_REMOVE, &remove);
> +}
> +EXPORT_SYMBOL(semihosting_remove);
> +
> +int semihosting_rename(const char *fname1, const char *fname2)
> +{
> + struct __packed {
> + uint32_t fname1;
> + uint32_t fname1_length;
> + uint32_t fname2;
> + uint32_t fname2_length;
> + } rename = {
> + .fname1 = (uint32_t)fname1,
> + .fname1_length = strlen(fname1),
> + .fname2 = (uint32_t)fname2,
> + .fname2_length = strlen(fname2),
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_RENAME, &rename);
> +}
> +EXPORT_SYMBOL(semihosting_rename);
> +
> +int semihosting_errno(void)
> +{
> + return semihosting_trap(SEMIHOSTING_SYS_ERRNO, NULL);
> +}
> +EXPORT_SYMBOL(semihosting_errno);
> +
> +
> +int semihosting_system(const char *command)
> +{
> + struct __packed {
> + uint32_t cmd;
> + uint32_t cmd_len;
> + } system = {
> + .cmd = (uint32_t)command,
> + .cmd_len = strlen(command),
> + };
> +
> + return semihosting_trap(SEMIHOSTING_SYS_SYSTEM, &system);
> +}
> +EXPORT_SYMBOL(semihosting_system);
> diff --git a/fs/Kconfig b/fs/Kconfig
> index feab537..9217bc8 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -80,4 +80,13 @@ config FS_UIMAGEFS
> select CRC32
> prompt "uImage FS support"
>
> +config FS_SMHFS
> + depends on ARM_SEMIHOSTING
> + bool
> + prompt "Semihosting FS support"
> + help
> + If enabled this filesystem provides access to the files
> + located on a debugging host connected to the target running
> + Barebox
> +
> endmenu
> diff --git a/fs/Makefile b/fs/Makefile
> index f5aae91..4693205 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -13,3 +13,4 @@ obj-$(CONFIG_FS_BPKFS) += bpkfs.o
> obj-$(CONFIG_FS_UIMAGEFS) += uimagefs.o
> obj-$(CONFIG_FS_EFI) += efi.o
> obj-$(CONFIG_FS_EFIVARFS) += efivarfs.o
> +obj-$(CONFIG_FS_SMHFS) += smhfs.o
> diff --git a/fs/smhfs.c b/fs/smhfs.c
> new file mode 100644
> index 0000000..d38afe6
> --- /dev/null
> +++ b/fs/smhfs.c
> @@ -0,0 +1,177 @@
> +/*
> + * smhfs.c -- Driver implementing pseudo FS interface on top of ARM
> + * semihosting
> + *
> + * Copyright (c) 2015 Zodiac Inflight Innovations
> + * Author: Andrey Smirnov <andrew.smirnov at gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <common.h>
> +#include <command.h>
> +#include <init.h>
> +#include <fs.h>
> +#include <errno.h>
> +#include <linux/stat.h>
> +#include <asm/semihosting.h>
> +
> +static int file_to_fd(const FILE *f)
> +{
> + return (int)f->priv;
> +}
> +
> +static int smhfs_create(struct device_d __always_unused *dev,
> + const char __always_unused *pathname,
> + mode_t __always_unused mode)
> +{
> + return 0;
> +}
> +
> +static int smhfs_mkdir(struct device_d __always_unused *dev,
> + const char __always_unused *pathname)
> +{
> + return -ENOSYS;
> +}
> +
> +static int smhfs_remove(struct device_d __always_unused *dev,
> + const char *pathname)
> +{
> + /* Get rid of leading '/' */
> + pathname = &pathname[1];
> +
> + if (semihosting_remove(pathname) != 0)
> + return semihosting_errno();
> + else
> + return 0
> +}
> +
> +static int smhfs_truncate(struct device_d __always_unused *dev,
> + FILE __always_unused *f,
> + ulong __always_unused size)
> +{
> + return -ENOSYS;
> +}
> +
> +static int smhfs_open(struct device_d __always_unused *dev,
> + FILE *file, const char *filename)
> +{
> + /* Get rid of leading '/' */
> + filename = &filename[1];
> +
> + const int fd = semihosting_open(filename, file->flags);
> + if (fd < 0)
> + goto error;
> +
> + file->priv = (void *)fd;
> + file->size = semihosting_flen(fd);
> + if (file->size < 0)
> + goto error;
> +
> + return 0;
> +error:
> + return semihosting_errno();
> +}
> +
> +static int smhfs_close(struct device_d __always_unused *dev,
> + FILE *f)
> +{
> + if (semihosting_close(file_to_fd(f)))
> + return semihosting_errno();
> + else
> + return 0;
> +}
> +
> +static int smhfs_write(struct device_d __always_unused *dev,
> + FILE *f, const void *inbuf, size_t insize)
> +{
> + if (semihosting_write(file_to_fd(f), inbuf, insize))
> + return semihosting_errno();
> + else
> + return insize;
> +}
> +
> +static int smhfs_read(struct device_d __always_unused *dev,
> + FILE *f, void *buf, size_t insize)
> +{
> + if (!semihosting_read(file_to_fd(f), buf, insize))
> + return insize;
> + else
> + return semihosting_errno();
> +}
> +
> +static loff_t smhfs_lseek(struct device_d __always_unused *dev,
> + FILE *f, loff_t pos)
> +{
> + if (semihosting_seek(file_to_fd(f), pos)) {
> + return semihosting_errno();
> + } else {
> + file->pos = pos;
> + return file->pos;
> + }
> +}
> +
> +static DIR* smhfs_opendir(struct device_d __always_unused *dev,
> + const char __always_unused *pathname)
> +{
> + return NULL;
> +}
> +
> +static int smhfs_stat(struct device_d __always_unused *dev,
> + const char *filename, struct stat *s)
> +{
> + FILE file;
> +
> + if (smhfs_open(NULL, &file, filename) == 0) {
> + s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
> + s->st_size = file.size;
> + }
> + smhfs_close(NULL, &file);
> +
> + return 0;
> +}
> +
> +static int smhfs_probe(struct device_d __always_unused *dev)
> +{
> + /* TODO: Add provisions to detect if debugger is connected */
> + return 0;
> +}
> +
> +static void smhfs_remove(struct device_d __always_unused *dev)
> +{
> +}
> +
> +static struct fs_driver_d smhfs_driver = {
> + .open = smhfs_open,
> + .close = smhfs_close,
> + .read = smhfs_read,
> + .lseek = smhfs_lseek,
> + .opendir = smhfs_opendir,
> + .stat = smhfs_stat,
> + .create = smhfs_create,
> + .unlink = smhfs_remove,
> + .mkdir = smhfs_mkdir,
> + .rmdir = smhfs_remove,
> + .write = smhfs_write,
> + .truncate = smhfs_truncate,
> + .flags = FS_DRIVER_NO_DEV,
> + .drv = {
> + .probe = smhfs_probe,
> + .remove = smhfs_remove,
> + .name = "smhfs",
> + }
> +};
> +
> +static int smhfs_init(void)
> +{
> + return register_fs_driver(&smhfs_driver);
> +}
> +coredevice_initcall(smhfs_init);
> --
> 2.1.4
>
> _______________________________________________
> barebox mailing list
> barebox at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
--
--
Best regards,
Antony Pavlov
More information about the barebox
mailing list