diff --git a/thermosphere/src/semihosting.c b/thermosphere/src/semihosting.c deleted file mode 100644 index 7b8fe6e8d..000000000 --- a/thermosphere/src/semihosting.c +++ /dev/null @@ -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 -#include -#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; -} \ No newline at end of file diff --git a/thermosphere/src/semihosting.h b/thermosphere/src/semihosting.h deleted file mode 100644 index 7eac4e2ec..000000000 --- a/thermosphere/src/semihosting.h +++ /dev/null @@ -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 -#include /* 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); diff --git a/thermosphere/src/util_semihosting.hpp b/thermosphere/src/util_semihosting.hpp new file mode 100644 index 000000000..87f987d90 --- /dev/null +++ b/thermosphere/src/util_semihosting.hpp @@ -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 . + */ + +/* 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 + +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(impl::SemihostingCall(impl::SemihostingOperation::ReadC, nullptr)); + } + +} \ No newline at end of file