14f6ad66aSAchin Gupta /* 2*633fa4cdSjohpow01 * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. 34f6ad66aSAchin Gupta * 482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 54f6ad66aSAchin Gupta */ 64f6ad66aSAchin Gupta 74f6ad66aSAchin Gupta #include <assert.h> 84f6ad66aSAchin Gupta #include <errno.h> 997043ac9SDan Handley #include <string.h> 104f6ad66aSAchin Gupta 1109d40e0eSAntonio Nino Diaz #include <lib/semihosting.h> 1209d40e0eSAntonio Nino Diaz 134f6ad66aSAchin Gupta #ifndef SEMIHOSTING_SUPPORTED 144f6ad66aSAchin Gupta #define SEMIHOSTING_SUPPORTED 1 154f6ad66aSAchin Gupta #endif 164f6ad66aSAchin Gupta 17*633fa4cdSjohpow01 long semihosting_call(unsigned long operation, uintptr_t system_block_address); 184f6ad66aSAchin Gupta 194f6ad66aSAchin Gupta typedef struct { 204f6ad66aSAchin Gupta const char *file_name; 21cd529320SRyan Harkin unsigned long mode; 22cd529320SRyan Harkin size_t name_length; 23fb037bfbSDan Handley } smh_file_open_block_t; 244f6ad66aSAchin Gupta 254f6ad66aSAchin Gupta typedef struct { 26cd529320SRyan Harkin long handle; 27625de1d4SDan Handley uintptr_t buffer; 28cd529320SRyan Harkin size_t length; 29fb037bfbSDan Handley } smh_file_read_write_block_t; 304f6ad66aSAchin Gupta 314f6ad66aSAchin Gupta typedef struct { 32cd529320SRyan Harkin long handle; 33cd529320SRyan Harkin ssize_t location; 34fb037bfbSDan Handley } smh_file_seek_block_t; 354f6ad66aSAchin Gupta 364f6ad66aSAchin Gupta typedef struct { 374f6ad66aSAchin Gupta char *command_line; 38cd529320SRyan Harkin size_t command_length; 39fb037bfbSDan Handley } smh_system_block_t; 404f6ad66aSAchin Gupta 41cd529320SRyan Harkin long semihosting_connection_supported(void) 424f6ad66aSAchin Gupta { 434f6ad66aSAchin Gupta return SEMIHOSTING_SUPPORTED; 444f6ad66aSAchin Gupta } 454f6ad66aSAchin Gupta 46cd529320SRyan Harkin long semihosting_file_open(const char *file_name, size_t mode) 474f6ad66aSAchin Gupta { 48fb037bfbSDan Handley smh_file_open_block_t open_block; 494f6ad66aSAchin Gupta 504f6ad66aSAchin Gupta open_block.file_name = file_name; 514f6ad66aSAchin Gupta open_block.mode = mode; 524f6ad66aSAchin Gupta open_block.name_length = strlen(file_name); 534f6ad66aSAchin Gupta 54*633fa4cdSjohpow01 return semihosting_call(SEMIHOSTING_SYS_OPEN, (uintptr_t)&open_block); 554f6ad66aSAchin Gupta } 564f6ad66aSAchin Gupta 57cd529320SRyan Harkin long semihosting_file_seek(long file_handle, ssize_t offset) 584f6ad66aSAchin Gupta { 59fb037bfbSDan Handley smh_file_seek_block_t seek_block; 60cd529320SRyan Harkin long result; 614f6ad66aSAchin Gupta 624f6ad66aSAchin Gupta seek_block.handle = file_handle; 634f6ad66aSAchin Gupta seek_block.location = offset; 644f6ad66aSAchin Gupta 65*633fa4cdSjohpow01 result = semihosting_call(SEMIHOSTING_SYS_SEEK, (uintptr_t)&seek_block); 664f6ad66aSAchin Gupta 67*633fa4cdSjohpow01 if (result != 0) { 684f6ad66aSAchin Gupta result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0); 69*633fa4cdSjohpow01 } 704f6ad66aSAchin Gupta 714f6ad66aSAchin Gupta return result; 724f6ad66aSAchin Gupta } 734f6ad66aSAchin Gupta 74625de1d4SDan Handley long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer) 754f6ad66aSAchin Gupta { 76fb037bfbSDan Handley smh_file_read_write_block_t read_block; 77cd529320SRyan Harkin long result = -EINVAL; 784f6ad66aSAchin Gupta 79*633fa4cdSjohpow01 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 804f6ad66aSAchin Gupta return result; 81*633fa4cdSjohpow01 } 824f6ad66aSAchin Gupta 834f6ad66aSAchin Gupta read_block.handle = file_handle; 844f6ad66aSAchin Gupta read_block.buffer = buffer; 854f6ad66aSAchin Gupta read_block.length = *length; 864f6ad66aSAchin Gupta 87*633fa4cdSjohpow01 result = semihosting_call(SEMIHOSTING_SYS_READ, (uintptr_t)&read_block); 884f6ad66aSAchin Gupta 894f6ad66aSAchin Gupta if (result == *length) { 904f6ad66aSAchin Gupta return -EINVAL; 914f6ad66aSAchin Gupta } else if (result < *length) { 924f6ad66aSAchin Gupta *length -= result; 934f6ad66aSAchin Gupta return 0; 94*633fa4cdSjohpow01 } else { 954f6ad66aSAchin Gupta return result; 964f6ad66aSAchin Gupta } 97*633fa4cdSjohpow01 } 984f6ad66aSAchin Gupta 99*633fa4cdSjohpow01 long semihosting_file_write(long file_handle, size_t *length, 100625de1d4SDan Handley const uintptr_t buffer) 1014f6ad66aSAchin Gupta { 102fb037bfbSDan Handley smh_file_read_write_block_t write_block; 10331833affSJuan Castillo long result = -EINVAL; 1044f6ad66aSAchin Gupta 105*633fa4cdSjohpow01 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 1064f6ad66aSAchin Gupta return -EINVAL; 107*633fa4cdSjohpow01 } 1084f6ad66aSAchin Gupta 1094f6ad66aSAchin Gupta write_block.handle = file_handle; 110625de1d4SDan Handley write_block.buffer = (uintptr_t)buffer; /* cast away const */ 1114f6ad66aSAchin Gupta write_block.length = *length; 1124f6ad66aSAchin Gupta 11331833affSJuan Castillo result = semihosting_call(SEMIHOSTING_SYS_WRITE, 11461cbd41dSAndrew Walbran (uintptr_t)&write_block); 1154f6ad66aSAchin Gupta 11631833affSJuan Castillo *length = result; 11731833affSJuan Castillo 11831833affSJuan Castillo return (result == 0) ? 0 : -EINVAL; 1194f6ad66aSAchin Gupta } 1204f6ad66aSAchin Gupta 121cd529320SRyan Harkin long semihosting_file_close(long file_handle) 1224f6ad66aSAchin Gupta { 123*633fa4cdSjohpow01 return semihosting_call(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&file_handle); 1244f6ad66aSAchin Gupta } 1254f6ad66aSAchin Gupta 126cd529320SRyan Harkin long semihosting_file_length(long file_handle) 1274f6ad66aSAchin Gupta { 128*633fa4cdSjohpow01 return semihosting_call(SEMIHOSTING_SYS_FLEN, (uintptr_t)&file_handle); 1294f6ad66aSAchin Gupta } 1304f6ad66aSAchin Gupta 1314f6ad66aSAchin Gupta char semihosting_read_char(void) 1324f6ad66aSAchin Gupta { 13361cbd41dSAndrew Walbran return semihosting_call(SEMIHOSTING_SYS_READC, 0); 1344f6ad66aSAchin Gupta } 1354f6ad66aSAchin Gupta 1364f6ad66aSAchin Gupta void semihosting_write_char(char character) 1374f6ad66aSAchin Gupta { 13861cbd41dSAndrew Walbran semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character); 1394f6ad66aSAchin Gupta } 1404f6ad66aSAchin Gupta 1414f6ad66aSAchin Gupta void semihosting_write_string(char *string) 1424f6ad66aSAchin Gupta { 14361cbd41dSAndrew Walbran semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string); 1444f6ad66aSAchin Gupta } 1454f6ad66aSAchin Gupta 146cd529320SRyan Harkin long semihosting_system(char *command_line) 1474f6ad66aSAchin Gupta { 148fb037bfbSDan Handley smh_system_block_t system_block; 1494f6ad66aSAchin Gupta 1504f6ad66aSAchin Gupta system_block.command_line = command_line; 1514f6ad66aSAchin Gupta system_block.command_length = strlen(command_line); 1524f6ad66aSAchin Gupta 1534f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_SYSTEM, 15461cbd41dSAndrew Walbran (uintptr_t)&system_block); 1554f6ad66aSAchin Gupta } 1564f6ad66aSAchin Gupta 157cd529320SRyan Harkin long semihosting_get_flen(const char *file_name) 1584f6ad66aSAchin Gupta { 159cd529320SRyan Harkin long file_handle; 160bde2836fSAmbroise Vincent long length; 1614f6ad66aSAchin Gupta 162*633fa4cdSjohpow01 assert(semihosting_connection_supported() != 0); 1634f6ad66aSAchin Gupta 1644f6ad66aSAchin Gupta file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 165*633fa4cdSjohpow01 if (file_handle == -1) { 1664f6ad66aSAchin Gupta return file_handle; 167*633fa4cdSjohpow01 } 1684f6ad66aSAchin Gupta 1694f6ad66aSAchin Gupta /* Find the length of the file */ 1704f6ad66aSAchin Gupta length = semihosting_file_length(file_handle); 1714f6ad66aSAchin Gupta 172*633fa4cdSjohpow01 return (semihosting_file_close(file_handle) != 0) ? -1 : length; 1734f6ad66aSAchin Gupta } 1744f6ad66aSAchin Gupta 175cd529320SRyan Harkin long semihosting_download_file(const char *file_name, 176cd529320SRyan Harkin size_t buf_size, 177625de1d4SDan Handley uintptr_t buf) 1784f6ad66aSAchin Gupta { 179cd529320SRyan Harkin long ret = -EINVAL; 180cd529320SRyan Harkin size_t length; 181cd529320SRyan Harkin long file_handle; 1824f6ad66aSAchin Gupta 1834f6ad66aSAchin Gupta /* Null pointer check */ 184*633fa4cdSjohpow01 if (buf == 0U) { 1854f6ad66aSAchin Gupta return ret; 186*633fa4cdSjohpow01 } 1874f6ad66aSAchin Gupta 188*633fa4cdSjohpow01 assert(semihosting_connection_supported() != 0); 1894f6ad66aSAchin Gupta 1904f6ad66aSAchin Gupta file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 191*633fa4cdSjohpow01 if (file_handle == -1) { 1924f6ad66aSAchin Gupta return ret; 193*633fa4cdSjohpow01 } 1944f6ad66aSAchin Gupta 1954f6ad66aSAchin Gupta /* Find the actual length of the file */ 1964f6ad66aSAchin Gupta length = semihosting_file_length(file_handle); 197*633fa4cdSjohpow01 if (length == (size_t)(-1)) { 1984f6ad66aSAchin Gupta goto semihosting_fail; 199*633fa4cdSjohpow01 } 2004f6ad66aSAchin Gupta 2014f6ad66aSAchin Gupta /* Signal error if we do not have enough space for the file */ 202*633fa4cdSjohpow01 if (length > buf_size) { 2034f6ad66aSAchin Gupta goto semihosting_fail; 204*633fa4cdSjohpow01 } 2054f6ad66aSAchin Gupta 2064f6ad66aSAchin Gupta /* 2074f6ad66aSAchin Gupta * A successful read will return 0 in which case we pass back 2084f6ad66aSAchin Gupta * the actual number of bytes read. Else we pass a negative 2094f6ad66aSAchin Gupta * value indicating an error. 2104f6ad66aSAchin Gupta */ 2114f6ad66aSAchin Gupta ret = semihosting_file_read(file_handle, &length, buf); 212*633fa4cdSjohpow01 if (ret != 0) { 2134f6ad66aSAchin Gupta goto semihosting_fail; 214*633fa4cdSjohpow01 } else { 215*633fa4cdSjohpow01 ret = (long)length; 216*633fa4cdSjohpow01 } 2174f6ad66aSAchin Gupta 2184f6ad66aSAchin Gupta semihosting_fail: 2194f6ad66aSAchin Gupta semihosting_file_close(file_handle); 2204f6ad66aSAchin Gupta return ret; 2214f6ad66aSAchin Gupta } 22261cbd41dSAndrew Walbran 22361cbd41dSAndrew Walbran void semihosting_exit(uint32_t reason, uint32_t subcode) 22461cbd41dSAndrew Walbran { 22561cbd41dSAndrew Walbran #ifdef __aarch64__ 22661cbd41dSAndrew Walbran uint64_t parameters[] = {reason, subcode}; 22761cbd41dSAndrew Walbran 22861cbd41dSAndrew Walbran (void)semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t)¶meters); 22961cbd41dSAndrew Walbran #else 23061cbd41dSAndrew Walbran /* The subcode is not supported on AArch32. */ 23161cbd41dSAndrew Walbran (void)semihosting_call(SEMIHOSTING_SYS_EXIT, reason); 23261cbd41dSAndrew Walbran #endif 23361cbd41dSAndrew Walbran } 234