9
0
Fork 0

ARM: Add support for semihosting

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@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Andrey Smirnov 2015-10-28 11:34:36 -07:00 committed by Sascha Hauer
parent 611e86d581
commit 48d6151029
9 changed files with 529 additions and 0 deletions

View File

@ -0,0 +1,57 @@
.. index:: smhfs (filesystem)
.. _filesystems_smhfs:
File I/O over ARM semihosting support
=====================================
Target Side Setup
-----------------
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
Host Side Setup
---------------
FIXME: Currently OpenOCD does not work correctly if Barebox is built
with MMU enabled, so before using this featrue, please make sure that
MMU is disabled in your particular configuration
To make semihosting work host machine connected to the target via
JTAG/SWD must have semihosting capable debug software running. One
such tool would be OpenOCD. For ARM9 and ARM11 CPUs most recent
release of OpenOCD should suffice, however for ARMv7A based devices
patched version from here http://openocd.zylin.com/#/c/2908/ has to be
used.
The following steps are required to set up a operational semihosting
channel:
1. In a terminal start OpenOCD and specify your particular board
and debug adapter used.
2. In a separate terminal connect to OpenOCD via telnet
telnet localhost 4444
3. In resulting telnet session execute the following commands:
halt
arm semihosting on
resume
After that is done all of the semihosting related functions should be
ready to use.

View File

@ -325,6 +325,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

View File

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

View File

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

View File

@ -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@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)

227
arch/arm/lib/semihosting.c Normal file
View File

@ -0,0 +1,227 @@
/*
* semihosting.c -- ARM Semihoting API implementation
*
* Copyright (c) 2015 Zodiac Inflight Innovations
* Author: Andrey Smirnov <andrew.smirnov@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);

View File

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

View File

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

178
fs/smhfs.c Normal file
View File

@ -0,0 +1,178 @@
/*
* smhfs.c -- Driver implementing pseudo FS interface on top of ARM
* semihosting
*
* Copyright (c) 2015 Zodiac Inflight Innovations
* Author: Andrey Smirnov <andrew.smirnov@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_rm(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)
{
int fd;
/* Get rid of leading '/' */
filename = &filename[1];
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 {
f->pos = pos;
return f->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_rm,
.mkdir = smhfs_mkdir,
.rmdir = smhfs_rm,
.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);