Skip to content

Commit

Permalink
core: kernel: Add semihosting functions
Browse files Browse the repository at this point in the history
Semihosting is a mechanism that enables target to communicate and use
I/O facilities on a host computer which is running a debugger, such as
GDB. The I/O facilities include character {read|write} {from|to} the
semihosting host side console or a file. In other words, OP-TEE OS can
output log to the host side console or the host side file, if there is a
semihosting host and OP-TEE OS requests the semihosting operations to
that host.

If CFG_SEMIHOSTING is enabled, some semihosting functions will be
compiled into OP-TEE kernel, including:
- semihosting_sys_readc()
- semihosting_sys_writec()
- semihosting_open()
- semihosting_read()
- semihosting_write()
- semihosting_close()

Note that the architectures which support semihosting should provide
their implementation of __do_semihosting(), which performs semihosting
instruction to raise the semihosting request.

Signed-off-by: Alvin Chang <[email protected]>
Reviewed-by: Etienne Carriere <[email protected]>
Reviewed-by: Jerome Forissier <[email protected]>
Reviewed-by: Jens Wiklander <[email protected]>
  • Loading branch information
gagachang authored and jforissier committed Mar 4, 2024
1 parent f459d3c commit 7e2a103
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 0 deletions.
23 changes: 23 additions & 0 deletions core/include/kernel/semihosting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2024 Andes Technology Corporation
*/
#ifndef __KERNEL_SEMIHOSTING_H
#define __KERNEL_SEMIHOSTING_H

#include <stddef.h>
#include <stdint.h>
#include <sys/fcntl.h>
#include <util.h>

/* Perform architecture-specific semihosting instructions. */
uintptr_t __do_semihosting(uintptr_t op, uintptr_t arg);

char semihosting_sys_readc(void);
void semihosting_sys_writec(char c);
int semihosting_open(const char *fname, int flags);
size_t semihosting_read(int fd, void *ptr, size_t len);
size_t semihosting_write(int fd, const void *ptr, size_t len);
int semihosting_close(int fd);

#endif /* __KERNEL_SEMIHOSTING_H */
169 changes: 169 additions & 0 deletions core/kernel/semihosting.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2024 Andes Technology Corporation
*/

#include <kernel/semihosting.h>
#include <string.h>

/*
* ARM and RISC-V have defined the standard way to perform
* the semihosting operations.
* - Operation codes and open modes are identical.
* - The implementation of the low-level __do_semihosting() call is
* architecture-specific.
* - Arm semihosting interface:
* https://developer.arm.com/documentation/dui0471/g/Semihosting/The-semihosting-interface
* - RISC-V semihosting interface:
* https://github.com/riscv-non-isa/riscv-semihosting/blob/main/binary-interface.adoc
*/

/* An integer that specifies the file open mode */
enum semihosting_open_mode {
SEMIHOSTING_OPEN_R = 0,
SEMIHOSTING_OPEN_RB = 1,
SEMIHOSTING_OPEN_RX = 2,
SEMIHOSTING_OPEN_RXB = 3,
SEMIHOSTING_OPEN_W = 4,
SEMIHOSTING_OPEN_WB = 5,
SEMIHOSTING_OPEN_WX = 6,
SEMIHOSTING_OPEN_WXB = 7,
SEMIHOSTING_OPEN_A = 8,
SEMIHOSTING_OPEN_AB = 9,
SEMIHOSTING_OPEN_AX = 10,
SEMIHOSTING_OPEN_AXB = 11,
};

enum semihosting_sys_ops {
/* Regular operations */
SEMIHOSTING_SYS_OPEN = 0x01,
SEMIHOSTING_SYS_CLOSE = 0x02,
SEMIHOSTING_SYS_WRITEC = 0x03,
SEMIHOSTING_SYS_WRITE = 0x05,
SEMIHOSTING_SYS_READ = 0x06,
SEMIHOSTING_SYS_READC = 0x07,
};

struct semihosting_param_t {
uintptr_t param0;
uintptr_t param1;
uintptr_t param2;
};

/**
* @brief Read one character byte from the semihosting host debug terminal
*
* @retval the character read from the semihosting host
*/
char semihosting_sys_readc(void)
{
return __do_semihosting(SEMIHOSTING_SYS_READC, 0);
}

/**
* @brief Write one character byte to the semihosting host debug terminal
* @param c: the character to be written
*/
void semihosting_sys_writec(char c)
{
__do_semihosting(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&c);
}

/**
* @brief Request the semihosting host to open a file on the host system
* @param fname: the path or name of the file
* @param flags: sys/fcntl.h standard flags to open the file with
*
* @retval nonzero if OK, or -1 if fails
*/
int semihosting_open(const char *fname, int flags)
{
int semi_open_flags = 0;
const int flags_mask = O_RDONLY | O_WRONLY | O_RDWR |
O_CREAT | O_TRUNC | O_APPEND;
struct semihosting_param_t arg = { };

/* Convert the flags to semihosting open. */
switch (flags & flags_mask) {
case O_RDONLY: /* 'r' */
semi_open_flags = SEMIHOSTING_OPEN_R;
break;
case O_WRONLY | O_CREAT | O_TRUNC: /* 'w' */
semi_open_flags = SEMIHOSTING_OPEN_W;
break;
case O_WRONLY | O_CREAT | O_APPEND: /* 'a' */
semi_open_flags = SEMIHOSTING_OPEN_A;
break;
case O_RDWR: /* 'r+' */
semi_open_flags = SEMIHOSTING_OPEN_RX;
break;
case O_RDWR | O_CREAT | O_TRUNC: /* 'w+' */
semi_open_flags = SEMIHOSTING_OPEN_WX;
break;
case O_RDWR | O_CREAT | O_APPEND: /* 'a+' */
semi_open_flags = SEMIHOSTING_OPEN_AX;
break;
default:
return -1;
}

arg.param0 = (uintptr_t)fname;
arg.param1 = semi_open_flags;
arg.param2 = strlen(fname);

return (int)__do_semihosting(SEMIHOSTING_SYS_OPEN, (uintptr_t)&arg);
}

/**
* @brief Read data from a file on the semihosting host system
* @param fd: a handle for a file previously opened
* @param ptr: pointer to a buffer
* @param len: the number of bytes to read to the buffer from the file
*
* @retval zero if OK, the same value as @len if fails, smaller value than @len
* for partial success
*/
size_t semihosting_read(int fd, void *ptr, size_t len)
{
struct semihosting_param_t arg = {
.param0 = fd,
.param1 = (uintptr_t)ptr,
.param2 = len
};

return __do_semihosting(SEMIHOSTING_SYS_READ, (uintptr_t)&arg);
}

/**
* @brief Write data into a file on the semihosting host system
* @param fd: a handle for a file previously opened
* @param ptr: pointer to a buffer
* @param len: the number of bytes to be written from the buffer to the file
*
* @retval zero if OK, otherwise the number of bytes that are not written
*/
size_t semihosting_write(int fd, const void *ptr, size_t len)
{
struct semihosting_param_t arg = {
.param0 = fd,
.param1 = (uintptr_t)ptr,
.param2 = len
};

return __do_semihosting(SEMIHOSTING_SYS_WRITE, (uintptr_t)&arg);
}

/**
* @brief Close a file on the semihosting host system
* @param fd: a handle for a file previously opened
*
* @retval zero if OK, -1 if fails
*/
int semihosting_close(int fd)
{
struct semihosting_param_t arg = {
.param0 = fd,
};

return (int)__do_semihosting(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&arg);
}
2 changes: 2 additions & 0 deletions core/kernel/sub.mk
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ srcs-y += pseudo_ta.c

srcs-$(CFG_TRANSFER_LIST) += transfer_list.c

srcs-$(CFG_SEMIHOSTING) += semihosting.c

ifeq ($(CFG_SYSCALL_FTRACE),y)
# We would not like to profile spin_lock_debug.c file as it provides
# common APIs that are needed for ftrace framework to trace syscalls.
Expand Down
5 changes: 5 additions & 0 deletions mk/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -1158,3 +1158,8 @@ $(eval $(call cfg-depends-all,CFG_WIDEVINE_HUK,CFG_DT))
# DT node "/options/op-tee/widevine" to some specific TAs.
CFG_WIDEVINE_PTA ?= n
$(eval $(call cfg-depends-all,CFG_WIDEVINE_PTA,CFG_DT CFG_WIDEVINE_HUK))

# Semihosting is a debugging mechanism that enables code running on an embedded
# system (also called the target) to communicate with and use the I/O of the
# host computer.
CFG_SEMIHOSTING ?= n

0 comments on commit 7e2a103

Please sign in to comment.