14f6ad66aSAchin Gupta /* 2*4c700c15SGovindraj Raja * Copyright (c) 2013-2022, 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 17633fa4cdSjohpow01 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 54633fa4cdSjohpow01 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 65633fa4cdSjohpow01 result = semihosting_call(SEMIHOSTING_SYS_SEEK, (uintptr_t)&seek_block); 664f6ad66aSAchin Gupta 677c494388SManish V Badarkhe if (result < 0) { 684f6ad66aSAchin Gupta result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0); 697c494388SManish V Badarkhe } else { 707c494388SManish V Badarkhe result = 0; 71633fa4cdSjohpow01 } 724f6ad66aSAchin Gupta 734f6ad66aSAchin Gupta return result; 744f6ad66aSAchin Gupta } 754f6ad66aSAchin Gupta 76625de1d4SDan Handley long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer) 774f6ad66aSAchin Gupta { 78fb037bfbSDan Handley smh_file_read_write_block_t read_block; 79cd529320SRyan Harkin long result = -EINVAL; 804f6ad66aSAchin Gupta 81633fa4cdSjohpow01 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 824f6ad66aSAchin Gupta return result; 83633fa4cdSjohpow01 } 844f6ad66aSAchin Gupta 854f6ad66aSAchin Gupta read_block.handle = file_handle; 864f6ad66aSAchin Gupta read_block.buffer = buffer; 874f6ad66aSAchin Gupta read_block.length = *length; 884f6ad66aSAchin Gupta 89633fa4cdSjohpow01 result = semihosting_call(SEMIHOSTING_SYS_READ, (uintptr_t)&read_block); 904f6ad66aSAchin Gupta 914f6ad66aSAchin Gupta if (result == *length) { 924f6ad66aSAchin Gupta return -EINVAL; 934f6ad66aSAchin Gupta } else if (result < *length) { 944f6ad66aSAchin Gupta *length -= result; 954f6ad66aSAchin Gupta return 0; 96633fa4cdSjohpow01 } else { 974f6ad66aSAchin Gupta return result; 984f6ad66aSAchin Gupta } 99633fa4cdSjohpow01 } 1004f6ad66aSAchin Gupta 101633fa4cdSjohpow01 long semihosting_file_write(long file_handle, size_t *length, 102625de1d4SDan Handley const uintptr_t buffer) 1034f6ad66aSAchin Gupta { 104fb037bfbSDan Handley smh_file_read_write_block_t write_block; 10531833affSJuan Castillo long result = -EINVAL; 1064f6ad66aSAchin Gupta 107633fa4cdSjohpow01 if ((length == NULL) || (buffer == (uintptr_t)NULL)) { 1084f6ad66aSAchin Gupta return -EINVAL; 109633fa4cdSjohpow01 } 1104f6ad66aSAchin Gupta 1114f6ad66aSAchin Gupta write_block.handle = file_handle; 112625de1d4SDan Handley write_block.buffer = (uintptr_t)buffer; /* cast away const */ 1134f6ad66aSAchin Gupta write_block.length = *length; 1144f6ad66aSAchin Gupta 11531833affSJuan Castillo result = semihosting_call(SEMIHOSTING_SYS_WRITE, 11661cbd41dSAndrew Walbran (uintptr_t)&write_block); 1174f6ad66aSAchin Gupta 11831833affSJuan Castillo *length = result; 11931833affSJuan Castillo 12031833affSJuan Castillo return (result == 0) ? 0 : -EINVAL; 1214f6ad66aSAchin Gupta } 1224f6ad66aSAchin Gupta 123cd529320SRyan Harkin long semihosting_file_close(long file_handle) 1244f6ad66aSAchin Gupta { 125633fa4cdSjohpow01 return semihosting_call(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&file_handle); 1264f6ad66aSAchin Gupta } 1274f6ad66aSAchin Gupta 128cd529320SRyan Harkin long semihosting_file_length(long file_handle) 1294f6ad66aSAchin Gupta { 130633fa4cdSjohpow01 return semihosting_call(SEMIHOSTING_SYS_FLEN, (uintptr_t)&file_handle); 1314f6ad66aSAchin Gupta } 1324f6ad66aSAchin Gupta 1334f6ad66aSAchin Gupta char semihosting_read_char(void) 1344f6ad66aSAchin Gupta { 13561cbd41dSAndrew Walbran return semihosting_call(SEMIHOSTING_SYS_READC, 0); 1364f6ad66aSAchin Gupta } 1374f6ad66aSAchin Gupta 1384f6ad66aSAchin Gupta void semihosting_write_char(char character) 1394f6ad66aSAchin Gupta { 14061cbd41dSAndrew Walbran semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character); 1414f6ad66aSAchin Gupta } 1424f6ad66aSAchin Gupta 1434f6ad66aSAchin Gupta void semihosting_write_string(char *string) 1444f6ad66aSAchin Gupta { 14561cbd41dSAndrew Walbran semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string); 1464f6ad66aSAchin Gupta } 1474f6ad66aSAchin Gupta 148cd529320SRyan Harkin long semihosting_system(char *command_line) 1494f6ad66aSAchin Gupta { 150fb037bfbSDan Handley smh_system_block_t system_block; 1514f6ad66aSAchin Gupta 1524f6ad66aSAchin Gupta system_block.command_line = command_line; 1534f6ad66aSAchin Gupta system_block.command_length = strlen(command_line); 1544f6ad66aSAchin Gupta 1554f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_SYSTEM, 15661cbd41dSAndrew Walbran (uintptr_t)&system_block); 1574f6ad66aSAchin Gupta } 1584f6ad66aSAchin Gupta 159cd529320SRyan Harkin long semihosting_get_flen(const char *file_name) 1604f6ad66aSAchin Gupta { 161cd529320SRyan Harkin long file_handle; 162bde2836fSAmbroise Vincent long length; 1634f6ad66aSAchin Gupta 164633fa4cdSjohpow01 assert(semihosting_connection_supported() != 0); 1654f6ad66aSAchin Gupta 1664f6ad66aSAchin Gupta file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 167633fa4cdSjohpow01 if (file_handle == -1) { 1684f6ad66aSAchin Gupta return file_handle; 169633fa4cdSjohpow01 } 1704f6ad66aSAchin Gupta 1714f6ad66aSAchin Gupta /* Find the length of the file */ 1724f6ad66aSAchin Gupta length = semihosting_file_length(file_handle); 1734f6ad66aSAchin Gupta 174633fa4cdSjohpow01 return (semihosting_file_close(file_handle) != 0) ? -1 : length; 1754f6ad66aSAchin Gupta } 1764f6ad66aSAchin Gupta 177cd529320SRyan Harkin long semihosting_download_file(const char *file_name, 178cd529320SRyan Harkin size_t buf_size, 179625de1d4SDan Handley uintptr_t buf) 1804f6ad66aSAchin Gupta { 181cd529320SRyan Harkin long ret = -EINVAL; 182cd529320SRyan Harkin size_t length; 183cd529320SRyan Harkin long file_handle; 1844f6ad66aSAchin Gupta 1854f6ad66aSAchin Gupta /* Null pointer check */ 186633fa4cdSjohpow01 if (buf == 0U) { 1874f6ad66aSAchin Gupta return ret; 188633fa4cdSjohpow01 } 1894f6ad66aSAchin Gupta 190633fa4cdSjohpow01 assert(semihosting_connection_supported() != 0); 1914f6ad66aSAchin Gupta 1924f6ad66aSAchin Gupta file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 193633fa4cdSjohpow01 if (file_handle == -1) { 1944f6ad66aSAchin Gupta return ret; 195633fa4cdSjohpow01 } 1964f6ad66aSAchin Gupta 1974f6ad66aSAchin Gupta /* Find the actual length of the file */ 1984f6ad66aSAchin Gupta length = semihosting_file_length(file_handle); 199633fa4cdSjohpow01 if (length == (size_t)(-1)) { 2004f6ad66aSAchin Gupta goto semihosting_fail; 201633fa4cdSjohpow01 } 2024f6ad66aSAchin Gupta 2034f6ad66aSAchin Gupta /* Signal error if we do not have enough space for the file */ 204633fa4cdSjohpow01 if (length > buf_size) { 2054f6ad66aSAchin Gupta goto semihosting_fail; 206633fa4cdSjohpow01 } 2074f6ad66aSAchin Gupta 2084f6ad66aSAchin Gupta /* 2094f6ad66aSAchin Gupta * A successful read will return 0 in which case we pass back 2104f6ad66aSAchin Gupta * the actual number of bytes read. Else we pass a negative 2114f6ad66aSAchin Gupta * value indicating an error. 2124f6ad66aSAchin Gupta */ 2134f6ad66aSAchin Gupta ret = semihosting_file_read(file_handle, &length, buf); 214633fa4cdSjohpow01 if (ret != 0) { 2154f6ad66aSAchin Gupta goto semihosting_fail; 216633fa4cdSjohpow01 } else { 217633fa4cdSjohpow01 ret = (long)length; 218633fa4cdSjohpow01 } 2194f6ad66aSAchin Gupta 2204f6ad66aSAchin Gupta semihosting_fail: 2214f6ad66aSAchin Gupta semihosting_file_close(file_handle); 2224f6ad66aSAchin Gupta return ret; 2234f6ad66aSAchin Gupta } 22461cbd41dSAndrew Walbran 22561cbd41dSAndrew Walbran void semihosting_exit(uint32_t reason, uint32_t subcode) 22661cbd41dSAndrew Walbran { 22761cbd41dSAndrew Walbran #ifdef __aarch64__ 22861cbd41dSAndrew Walbran uint64_t parameters[] = {reason, subcode}; 22961cbd41dSAndrew Walbran 23061cbd41dSAndrew Walbran (void)semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t)¶meters); 23161cbd41dSAndrew Walbran #else 23261cbd41dSAndrew Walbran /* The subcode is not supported on AArch32. */ 23361cbd41dSAndrew Walbran (void)semihosting_call(SEMIHOSTING_SYS_EXIT, reason); 23461cbd41dSAndrew Walbran #endif 23561cbd41dSAndrew Walbran } 236