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
semihosting_connection_supported(void)41cd529320SRyan Harkin long semihosting_connection_supported(void)
424f6ad66aSAchin Gupta {
434f6ad66aSAchin Gupta return SEMIHOSTING_SUPPORTED;
444f6ad66aSAchin Gupta }
454f6ad66aSAchin Gupta
semihosting_file_open(const char * file_name,size_t mode)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
semihosting_file_seek(long file_handle,ssize_t offset)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
semihosting_file_read(long file_handle,size_t * length,uintptr_t buffer)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
semihosting_file_write(long file_handle,size_t * length,const uintptr_t buffer)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
semihosting_file_close(long file_handle)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
semihosting_file_length(long file_handle)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
semihosting_read_char(void)1334f6ad66aSAchin Gupta char semihosting_read_char(void)
1344f6ad66aSAchin Gupta {
13561cbd41dSAndrew Walbran return semihosting_call(SEMIHOSTING_SYS_READC, 0);
1364f6ad66aSAchin Gupta }
1374f6ad66aSAchin Gupta
semihosting_write_char(char character)1384f6ad66aSAchin Gupta void semihosting_write_char(char character)
1394f6ad66aSAchin Gupta {
14061cbd41dSAndrew Walbran semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character);
1414f6ad66aSAchin Gupta }
1424f6ad66aSAchin Gupta
semihosting_write_string(char * string)1434f6ad66aSAchin Gupta void semihosting_write_string(char *string)
1444f6ad66aSAchin Gupta {
14561cbd41dSAndrew Walbran semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string);
1464f6ad66aSAchin Gupta }
1474f6ad66aSAchin Gupta
semihosting_system(char * command_line)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
semihosting_get_flen(const char * file_name)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
semihosting_download_file(const char * file_name,size_t buf_size,uintptr_t buf)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
semihosting_exit(uint32_t reason,uint32_t subcode)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