xref: /rk3399_ARM-atf/lib/semihosting/semihosting.c (revision 633fa4cd1fc8ef7f1b79bc4068b91c97fe4af4ef)
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)&parameters);
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