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