[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