[PATCH] ARM: Add support for semihosting

Andrey Smirnov andrew.smirnov at gmail.com
Sun Oct 18 22:33:57 PDT 2015


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
+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



More information about the barebox mailing list