1 /* 2 * Copyright (c) 2013-2019, 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, 18 uintptr_t system_block_address); 19 20 typedef struct { 21 const char *file_name; 22 unsigned long mode; 23 size_t name_length; 24 } smh_file_open_block_t; 25 26 typedef struct { 27 long handle; 28 uintptr_t buffer; 29 size_t length; 30 } smh_file_read_write_block_t; 31 32 typedef struct { 33 long handle; 34 ssize_t location; 35 } smh_file_seek_block_t; 36 37 typedef struct { 38 char *command_line; 39 size_t command_length; 40 } smh_system_block_t; 41 42 long semihosting_connection_supported(void) 43 { 44 return SEMIHOSTING_SUPPORTED; 45 } 46 47 long semihosting_file_open(const char *file_name, size_t mode) 48 { 49 smh_file_open_block_t open_block; 50 51 open_block.file_name = file_name; 52 open_block.mode = mode; 53 open_block.name_length = strlen(file_name); 54 55 return semihosting_call(SEMIHOSTING_SYS_OPEN, 56 (uintptr_t) &open_block); 57 } 58 59 long semihosting_file_seek(long file_handle, ssize_t offset) 60 { 61 smh_file_seek_block_t seek_block; 62 long result; 63 64 seek_block.handle = file_handle; 65 seek_block.location = offset; 66 67 result = semihosting_call(SEMIHOSTING_SYS_SEEK, 68 (uintptr_t) &seek_block); 69 70 if (result) 71 result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0); 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 read_block.handle = file_handle; 85 read_block.buffer = buffer; 86 read_block.length = *length; 87 88 result = semihosting_call(SEMIHOSTING_SYS_READ, 89 (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 long semihosting_file_write(long file_handle, 101 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 write_block.handle = file_handle; 111 write_block.buffer = (uintptr_t)buffer; /* cast away const */ 112 write_block.length = *length; 113 114 result = semihosting_call(SEMIHOSTING_SYS_WRITE, 115 (uintptr_t) &write_block); 116 117 *length = result; 118 119 return (result == 0) ? 0 : -EINVAL; 120 } 121 122 long semihosting_file_close(long file_handle) 123 { 124 return semihosting_call(SEMIHOSTING_SYS_CLOSE, 125 (uintptr_t) &file_handle); 126 } 127 128 long semihosting_file_length(long file_handle) 129 { 130 return semihosting_call(SEMIHOSTING_SYS_FLEN, 131 (uintptr_t) &file_handle); 132 } 133 134 char semihosting_read_char(void) 135 { 136 return semihosting_call(SEMIHOSTING_SYS_READC, 0); 137 } 138 139 void semihosting_write_char(char character) 140 { 141 semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t) &character); 142 } 143 144 void semihosting_write_string(char *string) 145 { 146 semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t) string); 147 } 148 149 long semihosting_system(char *command_line) 150 { 151 smh_system_block_t system_block; 152 153 system_block.command_line = command_line; 154 system_block.command_length = strlen(command_line); 155 156 return semihosting_call(SEMIHOSTING_SYS_SYSTEM, 157 (uintptr_t) &system_block); 158 } 159 160 long semihosting_get_flen(const char *file_name) 161 { 162 long file_handle; 163 long length; 164 165 assert(semihosting_connection_supported()); 166 167 file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 168 if (file_handle == -1) 169 return file_handle; 170 171 /* Find the length of the file */ 172 length = semihosting_file_length(file_handle); 173 174 return semihosting_file_close(file_handle) ? -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) 187 return ret; 188 189 assert(semihosting_connection_supported()); 190 191 file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 192 if (file_handle == -1) 193 return ret; 194 195 /* Find the actual length of the file */ 196 length = semihosting_file_length(file_handle); 197 if (length == -1) 198 goto semihosting_fail; 199 200 /* Signal error if we do not have enough space for the file */ 201 if (length > buf_size) 202 goto semihosting_fail; 203 204 /* 205 * A successful read will return 0 in which case we pass back 206 * the actual number of bytes read. Else we pass a negative 207 * value indicating an error. 208 */ 209 ret = semihosting_file_read(file_handle, &length, buf); 210 if (ret) 211 goto semihosting_fail; 212 else 213 ret = length; 214 215 semihosting_fail: 216 semihosting_file_close(file_handle); 217 return ret; 218 } 219 220 void semihosting_exit(uint32_t reason, uint32_t subcode) 221 { 222 #ifdef __aarch64__ 223 uint64_t parameters[] = {reason, subcode}; 224 225 (void) semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t) ¶meters); 226 #else 227 /* The subcode is not supported on AArch32. */ 228 (void) semihosting_call(SEMIHOSTING_SYS_EXIT, reason); 229 #endif 230 } 231