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:
parent
611e86d581
commit
48d6151029
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue