1 /* 2 * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <errno.h> 9 #include <string.h> 10 11 #include <lib/semihosting.h> 12 13 #ifndef SEMIHOSTING_SUPPORTED 14 #define SEMIHOSTING_SUPPORTED 1 15 #endif 16 17 long semihosting_call(unsigned long operation, uintptr_t system_block_address); 18 19 typedef struct { 20 const char *file_name; 21 unsigned long mode; 22 size_t name_length; 23 } smh_file_open_block_t; 24 25 typedef struct { 26 long handle; 27 uintptr_t buffer; 28 size_t length; 29 } smh_file_read_write_block_t; 30 31 typedef struct { 32 long handle; 33 ssize_t location; 34 } smh_file_seek_block_t; 35 36 typedef struct { 37 char *command_line; 38 size_t command_length; 39 } smh_system_block_t; 40 41 long semihosting_connection_supported(void) 42 { 43 return SEMIHOSTING_SUPPORTED; 44 } 45 46 long semihosting_file_open(const char *file_name, size_t mode) 47 { 48 smh_file_open_block_t open_block; 49 50 open_block.file_name = file_name; 51 open_block.mode = mode; 52 open_block.name_length = strlen(file_name); 53 54 return semihosting_call(SEMIHOSTING_SYS_OPEN, (uintptr_t)&open_block); 55 } 56 57 long semihosting_file_seek(long file_handle, ssize_t offset) 58 { 59 smh_file_seek_block_t seek_block; 60 long result; 61 62 seek_block.handle = file_handle; 63 seek_block.location = offset; 64 65 result = semihosting_call(SEMIHOSTING_SYS_SEEK, (uintptr_t)&seek_block); 66 67 if (result < 0) { 68 result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0); 69 } else { 70 result = 0; 71 } 72 73 return result; 74 } 75 76 long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer) 77 { 78 smh_file_read_write_block_t read_block; 79 long result = -EINVAL; 80 81 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 82 return result; 83 } 84 85 read_block.handle = file_handle; 86 read_block.buffer = buffer; 87 read_block.length = *length; 88 89 result = semihosting_call(SEMIHOSTING_SYS_READ, (uintptr_t)&read_block); 90 91 if (result == *length) { 92 return -EINVAL; 93 } else if (result < *length) { 94 *length -= result; 95 return 0; 96 } else { 97 return result; 98 } 99 } 100 101 long semihosting_file_write(long file_handle, size_t *length, 102 const uintptr_t buffer) 103 { 104 smh_file_read_write_block_t write_block; 105 long result = -EINVAL; 106 107 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 108 return -EINVAL; 109 } 110 111 write_block.handle = file_handle; 112 write_block.buffer = (uintptr_t)buffer; /* cast away const */ 113 write_block.length = *length; 114 115 result = semihosting_call(SEMIHOSTING_SYS_WRITE, 116 (uintptr_t)&write_block); 117 118 *length = result; 119 120 return (result == 0) ? 0 : -EINVAL; 121 } 122 123 long semihosting_file_close(long file_handle) 124 { 125 return semihosting_call(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&file_handle); 126 } 127 128 long semihosting_file_length(long file_handle) 129 { 130 return semihosting_call(SEMIHOSTING_SYS_FLEN, (uintptr_t)&file_handle); 131 } 132 133 char semihosting_read_char(void) 134 { 135 return semihosting_call(SEMIHOSTING_SYS_READC, 0); 136 } 137 138 void semihosting_write_char(char character) 139 { 140 semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character); 141 } 142 143 void semihosting_write_string(char *string) 144 { 145 semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string); 146 } 147 148 long semihosting_system(char *command_line) 149 { 150 smh_system_block_t system_block; 151 152 system_block.command_line = command_line; 153 system_block.command_length = strlen(command_line); 154 155 return semihosting_call(SEMIHOSTING_SYS_SYSTEM, 156 (uintptr_t)&system_block); 157 } 158 159 long semihosting_get_flen(const char *file_name) 160 { 161 long file_handle; 162 long length; 163 164 assert(semihosting_connection_supported() != 0); 165 166 file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 167 if (file_handle == -1) { 168 return file_handle; 169 } 170 171 /* Find the length of the file */ 172 length = semihosting_file_length(file_handle); 173 174 return (semihosting_file_close(file_handle) != 0) ? -1 : length; 175 } 176 177 long semihosting_download_file(const char *file_name, 178 size_t buf_size, 179 uintptr_t buf) 180 { 181 long ret = -EINVAL; 182 size_t length; 183 long file_handle; 184 185 /* Null pointer check */ 186 if (buf == 0U) { 187 return ret; 188 } 189 190 assert(semihosting_connection_supported() != 0); 191 192 file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 193 if (file_handle == -1) { 194 return ret; 195 } 196 197 /* Find the actual length of the file */ 198 length = semihosting_file_length(file_handle); 199 if (length == (size_t)(-1)) { 200 goto semihosting_fail; 201 } 202 203 /* Signal error if we do not have enough space for the file */ 204 if (length > buf_size) { 205 goto semihosting_fail; 206 } 207 208 /* 209 * A successful read will return 0 in which case we pass back 210 * the actual number of bytes read. Else we pass a negative 211 * value indicating an error. 212 */ 213 ret = semihosting_file_read(file_handle, &length, buf); 214 if (ret != 0) { 215 goto semihosting_fail; 216 } else { 217 ret = (long)length; 218 } 219 220 semihosting_fail: 221 semihosting_file_close(file_handle); 222 return ret; 223 } 224 225 void semihosting_exit(uint32_t reason, uint32_t subcode) 226 { 227 #ifdef __aarch64__ 228 uint64_t parameters[] = {reason, subcode}; 229 230 (void)semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t)¶meters); 231 #else 232 /* The subcode is not supported on AArch32. */ 233 (void)semihosting_call(SEMIHOSTING_SYS_EXIT, reason); 234 #endif 235 } 236