thermosphere: rewrite semihosting

This commit is contained in:
TuxSH 2020-02-28 21:44:18 +00:00
parent 0cb5eab933
commit 56d764d09c
3 changed files with 201 additions and 274 deletions

View file

@ -1,209 +0,0 @@
// Adapted from Arm TF
/*
* Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <errno.h>
#include <string.h>
#include "semihosting.h"
long semihosting_call(unsigned long operation,
void *system_block_address);
typedef struct {
const char *file_name;
unsigned long mode;
size_t name_length;
} smh_file_open_block_t;
typedef struct {
long handle;
uintptr_t buffer;
size_t length;
} smh_file_read_write_block_t;
typedef struct {
long handle;
ssize_t location;
} smh_file_seek_block_t;
typedef struct {
char *command_line;
size_t command_length;
} smh_system_block_t;
long semihosting_file_open(const char *file_name, size_t mode)
{
smh_file_open_block_t open_block;
open_block.file_name = file_name;
open_block.mode = mode;
open_block.name_length = strlen(file_name);
return semihosting_call(SEMIHOSTING_SYS_OPEN,
(void *) &open_block);
}
long semihosting_file_seek(long file_handle, ssize_t offset)
{
smh_file_seek_block_t seek_block;
long result;
seek_block.handle = file_handle;
seek_block.location = offset;
result = semihosting_call(SEMIHOSTING_SYS_SEEK,
(void *) &seek_block);
if (result)
result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0);
return result;
}
long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer)
{
smh_file_read_write_block_t read_block;
long result = -EINVAL;
if ((length == NULL) || (buffer == (uintptr_t)NULL))
return result;
read_block.handle = file_handle;
read_block.buffer = buffer;
read_block.length = *length;
result = semihosting_call(SEMIHOSTING_SYS_READ,
(void *) &read_block);
if (result == *length) {
return -EINVAL;
} else if (result < *length) {
*length -= result;
return 0;
} else
return result;
}
long semihosting_file_write(long file_handle,
size_t *length,
const uintptr_t buffer)
{
smh_file_read_write_block_t write_block;
long result = -EINVAL;
if ((length == NULL) || (buffer == (uintptr_t)NULL))
return -EINVAL;
write_block.handle = file_handle;
write_block.buffer = (uintptr_t)buffer; /* cast away const */
write_block.length = *length;
result = semihosting_call(SEMIHOSTING_SYS_WRITE,
(void *) &write_block);
*length = result;
return (result == 0) ? 0 : -EINVAL;
}
long semihosting_file_close(long file_handle)
{
return semihosting_call(SEMIHOSTING_SYS_CLOSE,
(void *) &file_handle);
}
long semihosting_file_length(long file_handle)
{
return semihosting_call(SEMIHOSTING_SYS_FLEN,
(void *) &file_handle);
}
char semihosting_read_char(void)
{
return semihosting_call(SEMIHOSTING_SYS_READC, NULL);
}
void semihosting_write_char(char character)
{
semihosting_call(SEMIHOSTING_SYS_WRITEC, (void *) &character);
}
void semihosting_write_string(const char *string)
{
semihosting_call(SEMIHOSTING_SYS_WRITE0, (void *) string);
}
long semihosting_system(char *command_line)
{
smh_system_block_t system_block;
system_block.command_line = command_line;
system_block.command_length = strlen(command_line);
return semihosting_call(SEMIHOSTING_SYS_SYSTEM,
(void *) &system_block);
}
long semihosting_get_flen(const char *file_name)
{
long file_handle;
size_t length;
//assert(semihosting_connection_supported());
file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB);
if (file_handle == -1)
return file_handle;
/* Find the length of the file */
length = semihosting_file_length(file_handle);
return semihosting_file_close(file_handle) ? -1 : length;
}
long semihosting_download_file(const char *file_name,
size_t buf_size,
uintptr_t buf)
{
long ret = -EINVAL;
size_t length;
long file_handle;
/* Null pointer check */
if (!buf)
return ret;
//assert(semihosting_connection_supported());
file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB);
if (file_handle == -1)
return ret;
/* Find the actual length of the file */
length = semihosting_file_length(file_handle);
if (length == -1)
goto semihosting_fail;
/* Signal error if we do not have enough space for the file */
if (length > buf_size)
goto semihosting_fail;
/*
* A successful read will return 0 in which case we pass back
* the actual number of bytes read. Else we pass a negative
* value indicating an error.
*/
ret = semihosting_file_read(file_handle, &length, buf);
if (ret)
goto semihosting_fail;
else
ret = length;
semihosting_fail:
semihosting_file_close(file_handle);
return ret;
}

View file

@ -1,65 +0,0 @@
#pragma once
// Adapted from Arm TF
/*
* Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdint.h>
#include <stdio.h> /* For ssize_t */
#define SEMIHOSTING_SYS_OPEN 0x01
#define SEMIHOSTING_SYS_CLOSE 0x02
#define SEMIHOSTING_SYS_WRITE0 0x04
#define SEMIHOSTING_SYS_WRITEC 0x03
#define SEMIHOSTING_SYS_WRITE 0x05
#define SEMIHOSTING_SYS_READ 0x06
#define SEMIHOSTING_SYS_READC 0x07
#define SEMIHOSTING_SYS_SEEK 0x0A
#define SEMIHOSTING_SYS_FLEN 0x0C
#define SEMIHOSTING_SYS_REMOVE 0x0E
#define SEMIHOSTING_SYS_SYSTEM 0x12
#define SEMIHOSTING_SYS_ERRNO 0x13
#define FOPEN_MODE_R 0x0
#define FOPEN_MODE_RB 0x1
#define FOPEN_MODE_RPLUS 0x2
#define FOPEN_MODE_RPLUSB 0x3
#define FOPEN_MODE_W 0x4
#define FOPEN_MODE_WB 0x5
#define FOPEN_MODE_WPLUS 0x6
#define FOPEN_MODE_WPLUSB 0x7
#define FOPEN_MODE_A 0x8
#define FOPEN_MODE_AB 0x9
#define FOPEN_MODE_APLUS 0xa
#define FOPEN_MODE_APLUSB 0xb
static inline long semihosting_connection_supported(void)
{
#ifdef PLATFORM_QEMU
return 1;
#else
return 0;
#endif
}
long semihosting_file_open(const char *file_name, size_t mode);
long semihosting_file_seek(long file_handle, ssize_t offset);
long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer);
long semihosting_file_write(long file_handle,
size_t *length,
const uintptr_t buffer);
long semihosting_file_close(long file_handle);
long semihosting_file_length(long file_handle);
long semihosting_system(char *command_line);
long semihosting_get_flen(const char *file_name);
long semihosting_download_file(const char *file_name,
size_t buf_size,
uintptr_t buf);
void semihosting_write_char(char character);
void semihosting_write_string(const char *string);
char semihosting_read_char(void);

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Initially from Arm TF */
/*
* Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#pragma once
#include "defines.hpp"
#include <string_view>
namespace ams::util {
namespace impl {
enum class SemihostingOperation : unsigned int {
/* Only the operations we implement below are listed. */
Open = 1,
Close = 2,
Write0 = 3,
WriteC = 4,
Write = 5,
Read = 6,
ReadC = 7,
Seek = 0xA,
FLen = 0xC,
Remove = 0xE,
System = 0x12,
Errno = 0x13
};
ALWAYS_INLINE long SemihostingCall(impl::SemihostingOperation op, const void *blk)
{
register long ret asm("x0");
register impl::SemihostingOperation arg1 asm("w0") = op;
register const void *arg2 asm("x1") = blk;
__asm__ __volatile__("hlt 0xF000" : "=r" (ret) : "0" (arg1), "r" (arg2) : "memory");
return ret;
}
}
class SemihostingFile {
NON_COPYABLE(SemihostingFile);
private:
/* A call to open can't return a 0 handle */
long m_handle = 0;
public:
enum class OpenMode : unsigned long {
R = 0,
RB = 1,
RPlus = 2,
RPlusB = 3,
W = 4,
WB = 5,
WPlus = 6,
WPlusB = 7,
A = 8,
AB = 9,
APlus = 10,
APlusB = 11
};
constexpr bool IsOpen() const { return m_handle > 0; }
long Open(std::string_view name, OpenMode mode)
{
/* std::string_view is implicitely constructible from const char* */
struct {
const char *name;
OpenMode mode;
size_t nameSize;
} blk = { name.data(), mode, name.size() };
int ret = impl::SemihostingCall(impl::SemihostingOperation::Open, &blk);
if (ret > 0) {
m_handle = ret;
return 0;
} else {
m_handle = 0;
return -1;
}
}
long Seek(ssize_t offset) const
{
struct {
long handle;
ssize_t offset;
} blk = { m_handle, offset };
return impl::SemihostingCall(impl::SemihostingOperation::Seek, &blk);
}
size_t Read(void *buf, size_t len) const
{
struct {
long handle;
void *buf;
size_t len;
} blk = { m_handle, buf, len };
long nrem = impl::SemihostingCall(impl::SemihostingOperation::Read, &blk);
return len - nrem;
}
size_t Write(const void *buf, size_t len) const
{
struct {
long handle;
const void *buf;
size_t len;
} blk = { m_handle, buf, len };
long nrem = impl::SemihostingCall(impl::SemihostingOperation::Write, &blk);
return len - nrem;
}
ssize_t GetSize() const
{
return impl::SemihostingCall(impl::SemihostingOperation::FLen, &m_handle);
}
long Close()
{
long ret = impl::SemihostingCall(impl::SemihostingOperation::Close, &m_handle);
m_handle = 0;
return ret;
}
public:
constexpr SemihostingFile() = default;
SemihostingFile(const char *name, OpenMode mode = OpenMode::RB)
{
Open(name, mode);
}
SemihostingFile(SemihostingFile &&) = default;
SemihostingFile &operator=(SemihostingFile &&) = default;
~SemihostingFile()
{
Close();
}
public:
static ssize_t GetFileSize(const char *name)
{
SemihostingFile file{name};
return file.IsOpen() ? file.GetSize() : -1;
}
static ssize_t DownloadFile(void *buf, const char *name, size_t maxLen)
{
SemihostingFile file{name};
return file.IsOpen() ? file.Read(buf, maxLen) : -1;
}
};
long SemihostingSystem(std::string_view cmdLine)
{
/* std::string_view is implicitely constructible from const char* */
struct {
const char *str;
size_t len;
} blk = { cmdLine.data(), cmdLine.size() };
return impl::SemihostingCall(impl::SemihostingOperation::System, &blk);
}
void SemihostingWriteChar(char c)
{
impl::SemihostingCall(impl::SemihostingOperation::WriteC, &c);
}
void SemihostingWriteString(const char *str)
{
/* Has to be null-terminated, sadly. */
impl::SemihostingCall(impl::SemihostingOperation::Write0, str);
}
char SemihostingReadChar(void)
{
return static_cast<char>(impl::SemihostingCall(impl::SemihostingOperation::ReadC, nullptr));
}
}