1 /* 2 * Copyright (c) 2013-2020, 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 } 70 71 return result; 72 } 73 74 long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer) 75 { 76 smh_file_read_write_block_t read_block; 77 long result = -EINVAL; 78 79 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 80 return result; 81 } 82 83 read_block.handle = file_handle; 84 read_block.buffer = buffer; 85 read_block.length = *length; 86 87 result = semihosting_call(SEMIHOSTING_SYS_READ, (uintptr_t)&read_block); 88 89 if (result == *length) { 90 return -EINVAL; 91 } else if (result < *length) { 92 *length -= result; 93 return 0; 94 } else { 95 return result; 96 } 97 } 98 99 long semihosting_file_write(long file_handle, size_t *length, 100 const uintptr_t buffer) 101 { 102 smh_file_read_write_block_t write_block; 103 long result = -EINVAL; 104 105 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 106 return -EINVAL; 107 } 108 109 write_block.handle = file_handle; 110 write_block.buffer = (uintptr_t)buffer; /* cast away const */ 111 write_block.length = *length; 112 113 result = semihosting_call(SEMIHOSTING_SYS_WRITE, 114 (uintptr_t)&write_block); 115 116 *length = result; 117 118 return (result == 0) ? 0 : -EINVAL; 119 } 120 121 long semihosting_file_close(long file_handle) 122 { 123 return semihosting_call(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&file_handle); 124 } 125 126 long semihosting_file_length(long file_handle) 127 { 128 return semihosting_call(SEMIHOSTING_SYS_FLEN, (uintptr_t)&file_handle); 129 } 130 131 char semihosting_read_char(void) 132 { 133 return semihosting_call(SEMIHOSTING_SYS_READC, 0); 134 } 135 136 void semihosting_write_char(char character) 137 { 138 semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character); 139 } 140 141 void semihosting_write_string(char *string) 142 { 143 semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string); 144 } 145 146 long semihosting_system(char *command_line) 147 { 148 smh_system_block_t system_block; 149 150 system_block.command_line = command_line; 151 system_block.command_length = strlen(command_line); 152 153 return semihosting_call(SEMIHOSTING_SYS_SYSTEM, 154 (uintptr_t)&system_block); 155 } 156 157 long semihosting_get_flen(const char *file_name) 158 { 159 long file_handle; 160 long length; 161 162 assert(semihosting_connection_supported() != 0); 163 164 file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 165 if (file_handle == -1) { 166 return file_handle; 167 } 168 169 /* Find the length of the file */ 170 length = semihosting_file_length(file_handle); 171 172 return (semihosting_file_close(file_handle) != 0) ? -1 : length; 173 } 174 175 long semihosting_download_file(const char *file_name, 176 size_t buf_size, 177 uintptr_t buf) 178 { 179 long ret = -EINVAL; 180 size_t length; 181 long file_handle; 182 183 /* Null pointer check */ 184 if (buf == 0U) { 185 return ret; 186 } 187 188 assert(semihosting_connection_supported() != 0); 189 190 file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 191 if (file_handle == -1) { 192 return ret; 193 } 194 195 /* Find the actual length of the file */ 196 length = semihosting_file_length(file_handle); 197 if (length == (size_t)(-1)) { 198 goto semihosting_fail; 199 } 200 201 /* Signal error if we do not have enough space for the file */ 202 if (length > buf_size) { 203 goto semihosting_fail; 204 } 205 206 /* 207 * A successful read will return 0 in which case we pass back 208 * the actual number of bytes read. Else we pass a negative 209 * value indicating an error. 210 */ 211 ret = semihosting_file_read(file_handle, &length, buf); 212 if (ret != 0) { 213 goto semihosting_fail; 214 } else { 215 ret = (long)length; 216 } 217 218 semihosting_fail: 219 semihosting_file_close(file_handle); 220 return ret; 221 } 222 223 void semihosting_exit(uint32_t reason, uint32_t subcode) 224 { 225 #ifdef __aarch64__ 226 uint64_t parameters[] = {reason, subcode}; 227 228 (void)semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t)¶meters); 229 #else 230 /* The subcode is not supported on AArch32. */ 231 (void)semihosting_call(SEMIHOSTING_SYS_EXIT, reason); 232 #endif 233 } 234