xref: /aosp_15_r20/external/arm-trusted-firmware/lib/semihosting/semihosting.c (revision 54fd6939e177f8ff529b10183254802c76df6d08)
1*54fd6939SJiyong Park /*
2*54fd6939SJiyong Park  * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
3*54fd6939SJiyong Park  *
4*54fd6939SJiyong Park  * SPDX-License-Identifier: BSD-3-Clause
5*54fd6939SJiyong Park  */
6*54fd6939SJiyong Park 
7*54fd6939SJiyong Park #include <assert.h>
8*54fd6939SJiyong Park #include <errno.h>
9*54fd6939SJiyong Park #include <string.h>
10*54fd6939SJiyong Park 
11*54fd6939SJiyong Park #include <lib/semihosting.h>
12*54fd6939SJiyong Park 
13*54fd6939SJiyong Park #ifndef SEMIHOSTING_SUPPORTED
14*54fd6939SJiyong Park #define SEMIHOSTING_SUPPORTED  1
15*54fd6939SJiyong Park #endif
16*54fd6939SJiyong Park 
17*54fd6939SJiyong Park long semihosting_call(unsigned long operation, uintptr_t system_block_address);
18*54fd6939SJiyong Park 
19*54fd6939SJiyong Park typedef struct {
20*54fd6939SJiyong Park 	const char *file_name;
21*54fd6939SJiyong Park 	unsigned long mode;
22*54fd6939SJiyong Park 	size_t name_length;
23*54fd6939SJiyong Park } smh_file_open_block_t;
24*54fd6939SJiyong Park 
25*54fd6939SJiyong Park typedef struct {
26*54fd6939SJiyong Park 	long handle;
27*54fd6939SJiyong Park 	uintptr_t buffer;
28*54fd6939SJiyong Park 	size_t length;
29*54fd6939SJiyong Park } smh_file_read_write_block_t;
30*54fd6939SJiyong Park 
31*54fd6939SJiyong Park typedef struct {
32*54fd6939SJiyong Park 	long handle;
33*54fd6939SJiyong Park 	ssize_t location;
34*54fd6939SJiyong Park } smh_file_seek_block_t;
35*54fd6939SJiyong Park 
36*54fd6939SJiyong Park typedef struct {
37*54fd6939SJiyong Park 	char *command_line;
38*54fd6939SJiyong Park 	size_t command_length;
39*54fd6939SJiyong Park } smh_system_block_t;
40*54fd6939SJiyong Park 
semihosting_connection_supported(void)41*54fd6939SJiyong Park long semihosting_connection_supported(void)
42*54fd6939SJiyong Park {
43*54fd6939SJiyong Park 	return SEMIHOSTING_SUPPORTED;
44*54fd6939SJiyong Park }
45*54fd6939SJiyong Park 
semihosting_file_open(const char * file_name,size_t mode)46*54fd6939SJiyong Park long semihosting_file_open(const char *file_name, size_t mode)
47*54fd6939SJiyong Park {
48*54fd6939SJiyong Park 	smh_file_open_block_t open_block;
49*54fd6939SJiyong Park 
50*54fd6939SJiyong Park 	open_block.file_name = file_name;
51*54fd6939SJiyong Park 	open_block.mode = mode;
52*54fd6939SJiyong Park 	open_block.name_length = strlen(file_name);
53*54fd6939SJiyong Park 
54*54fd6939SJiyong Park 	return semihosting_call(SEMIHOSTING_SYS_OPEN, (uintptr_t)&open_block);
55*54fd6939SJiyong Park }
56*54fd6939SJiyong Park 
semihosting_file_seek(long file_handle,ssize_t offset)57*54fd6939SJiyong Park long semihosting_file_seek(long file_handle, ssize_t offset)
58*54fd6939SJiyong Park {
59*54fd6939SJiyong Park 	smh_file_seek_block_t seek_block;
60*54fd6939SJiyong Park 	long result;
61*54fd6939SJiyong Park 
62*54fd6939SJiyong Park 	seek_block.handle = file_handle;
63*54fd6939SJiyong Park 	seek_block.location = offset;
64*54fd6939SJiyong Park 
65*54fd6939SJiyong Park 	result = semihosting_call(SEMIHOSTING_SYS_SEEK, (uintptr_t)&seek_block);
66*54fd6939SJiyong Park 
67*54fd6939SJiyong Park 	if (result != 0) {
68*54fd6939SJiyong Park 		result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0);
69*54fd6939SJiyong Park 	}
70*54fd6939SJiyong Park 
71*54fd6939SJiyong Park 	return result;
72*54fd6939SJiyong Park }
73*54fd6939SJiyong Park 
semihosting_file_read(long file_handle,size_t * length,uintptr_t buffer)74*54fd6939SJiyong Park long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer)
75*54fd6939SJiyong Park {
76*54fd6939SJiyong Park 	smh_file_read_write_block_t read_block;
77*54fd6939SJiyong Park 	long result = -EINVAL;
78*54fd6939SJiyong Park 
79*54fd6939SJiyong Park 	if ((length == NULL) || (buffer == (uintptr_t)NULL)) {
80*54fd6939SJiyong Park 		return result;
81*54fd6939SJiyong Park 	}
82*54fd6939SJiyong Park 
83*54fd6939SJiyong Park 	read_block.handle = file_handle;
84*54fd6939SJiyong Park 	read_block.buffer = buffer;
85*54fd6939SJiyong Park 	read_block.length = *length;
86*54fd6939SJiyong Park 
87*54fd6939SJiyong Park 	result = semihosting_call(SEMIHOSTING_SYS_READ, (uintptr_t)&read_block);
88*54fd6939SJiyong Park 
89*54fd6939SJiyong Park 	if (result == *length) {
90*54fd6939SJiyong Park 		return -EINVAL;
91*54fd6939SJiyong Park 	} else if (result < *length) {
92*54fd6939SJiyong Park 		*length -= result;
93*54fd6939SJiyong Park 		return 0;
94*54fd6939SJiyong Park 	} else {
95*54fd6939SJiyong Park 		return result;
96*54fd6939SJiyong Park 	}
97*54fd6939SJiyong Park }
98*54fd6939SJiyong Park 
semihosting_file_write(long file_handle,size_t * length,const uintptr_t buffer)99*54fd6939SJiyong Park long semihosting_file_write(long file_handle, size_t *length,
100*54fd6939SJiyong Park 				const uintptr_t buffer)
101*54fd6939SJiyong Park {
102*54fd6939SJiyong Park 	smh_file_read_write_block_t write_block;
103*54fd6939SJiyong Park 	long result = -EINVAL;
104*54fd6939SJiyong Park 
105*54fd6939SJiyong Park 	if ((length == NULL) || (buffer == (uintptr_t)NULL)) {
106*54fd6939SJiyong Park 		return -EINVAL;
107*54fd6939SJiyong Park 	}
108*54fd6939SJiyong Park 
109*54fd6939SJiyong Park 	write_block.handle = file_handle;
110*54fd6939SJiyong Park 	write_block.buffer = (uintptr_t)buffer; /* cast away const */
111*54fd6939SJiyong Park 	write_block.length = *length;
112*54fd6939SJiyong Park 
113*54fd6939SJiyong Park 	result = semihosting_call(SEMIHOSTING_SYS_WRITE,
114*54fd6939SJiyong Park 		(uintptr_t)&write_block);
115*54fd6939SJiyong Park 
116*54fd6939SJiyong Park 	*length = result;
117*54fd6939SJiyong Park 
118*54fd6939SJiyong Park 	return (result == 0) ? 0 : -EINVAL;
119*54fd6939SJiyong Park }
120*54fd6939SJiyong Park 
semihosting_file_close(long file_handle)121*54fd6939SJiyong Park long semihosting_file_close(long file_handle)
122*54fd6939SJiyong Park {
123*54fd6939SJiyong Park 	return semihosting_call(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&file_handle);
124*54fd6939SJiyong Park }
125*54fd6939SJiyong Park 
semihosting_file_length(long file_handle)126*54fd6939SJiyong Park long semihosting_file_length(long file_handle)
127*54fd6939SJiyong Park {
128*54fd6939SJiyong Park 	return semihosting_call(SEMIHOSTING_SYS_FLEN, (uintptr_t)&file_handle);
129*54fd6939SJiyong Park }
130*54fd6939SJiyong Park 
semihosting_read_char(void)131*54fd6939SJiyong Park char semihosting_read_char(void)
132*54fd6939SJiyong Park {
133*54fd6939SJiyong Park 	return semihosting_call(SEMIHOSTING_SYS_READC, 0);
134*54fd6939SJiyong Park }
135*54fd6939SJiyong Park 
semihosting_write_char(char character)136*54fd6939SJiyong Park void semihosting_write_char(char character)
137*54fd6939SJiyong Park {
138*54fd6939SJiyong Park 	semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character);
139*54fd6939SJiyong Park }
140*54fd6939SJiyong Park 
semihosting_write_string(char * string)141*54fd6939SJiyong Park void semihosting_write_string(char *string)
142*54fd6939SJiyong Park {
143*54fd6939SJiyong Park 	semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string);
144*54fd6939SJiyong Park }
145*54fd6939SJiyong Park 
semihosting_system(char * command_line)146*54fd6939SJiyong Park long semihosting_system(char *command_line)
147*54fd6939SJiyong Park {
148*54fd6939SJiyong Park 	smh_system_block_t system_block;
149*54fd6939SJiyong Park 
150*54fd6939SJiyong Park 	system_block.command_line = command_line;
151*54fd6939SJiyong Park 	system_block.command_length = strlen(command_line);
152*54fd6939SJiyong Park 
153*54fd6939SJiyong Park 	return semihosting_call(SEMIHOSTING_SYS_SYSTEM,
154*54fd6939SJiyong Park 		(uintptr_t)&system_block);
155*54fd6939SJiyong Park }
156*54fd6939SJiyong Park 
semihosting_get_flen(const char * file_name)157*54fd6939SJiyong Park long semihosting_get_flen(const char *file_name)
158*54fd6939SJiyong Park {
159*54fd6939SJiyong Park 	long file_handle;
160*54fd6939SJiyong Park 	long length;
161*54fd6939SJiyong Park 
162*54fd6939SJiyong Park 	assert(semihosting_connection_supported() != 0);
163*54fd6939SJiyong Park 
164*54fd6939SJiyong Park 	file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB);
165*54fd6939SJiyong Park 	if (file_handle == -1) {
166*54fd6939SJiyong Park 		return file_handle;
167*54fd6939SJiyong Park 	}
168*54fd6939SJiyong Park 
169*54fd6939SJiyong Park 	/* Find the length of the file */
170*54fd6939SJiyong Park 	length = semihosting_file_length(file_handle);
171*54fd6939SJiyong Park 
172*54fd6939SJiyong Park 	return (semihosting_file_close(file_handle) != 0) ? -1 : length;
173*54fd6939SJiyong Park }
174*54fd6939SJiyong Park 
semihosting_download_file(const char * file_name,size_t buf_size,uintptr_t buf)175*54fd6939SJiyong Park long semihosting_download_file(const char *file_name,
176*54fd6939SJiyong Park 			      size_t buf_size,
177*54fd6939SJiyong Park 			      uintptr_t buf)
178*54fd6939SJiyong Park {
179*54fd6939SJiyong Park 	long ret = -EINVAL;
180*54fd6939SJiyong Park 	size_t length;
181*54fd6939SJiyong Park 	long file_handle;
182*54fd6939SJiyong Park 
183*54fd6939SJiyong Park 	/* Null pointer check */
184*54fd6939SJiyong Park 	if (buf == 0U) {
185*54fd6939SJiyong Park 		return ret;
186*54fd6939SJiyong Park 	}
187*54fd6939SJiyong Park 
188*54fd6939SJiyong Park 	assert(semihosting_connection_supported() != 0);
189*54fd6939SJiyong Park 
190*54fd6939SJiyong Park 	file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB);
191*54fd6939SJiyong Park 	if (file_handle == -1) {
192*54fd6939SJiyong Park 		return ret;
193*54fd6939SJiyong Park 	}
194*54fd6939SJiyong Park 
195*54fd6939SJiyong Park 	/* Find the actual length of the file */
196*54fd6939SJiyong Park 	length = semihosting_file_length(file_handle);
197*54fd6939SJiyong Park 	if (length == (size_t)(-1)) {
198*54fd6939SJiyong Park 		goto semihosting_fail;
199*54fd6939SJiyong Park 	}
200*54fd6939SJiyong Park 
201*54fd6939SJiyong Park 	/* Signal error if we do not have enough space for the file */
202*54fd6939SJiyong Park 	if (length > buf_size) {
203*54fd6939SJiyong Park 		goto semihosting_fail;
204*54fd6939SJiyong Park 	}
205*54fd6939SJiyong Park 
206*54fd6939SJiyong Park 	/*
207*54fd6939SJiyong Park 	 * A successful read will return 0 in which case we pass back
208*54fd6939SJiyong Park 	 * the actual number of bytes read. Else we pass a negative
209*54fd6939SJiyong Park 	 * value indicating an error.
210*54fd6939SJiyong Park 	 */
211*54fd6939SJiyong Park 	ret = semihosting_file_read(file_handle, &length, buf);
212*54fd6939SJiyong Park 	if (ret != 0) {
213*54fd6939SJiyong Park 		goto semihosting_fail;
214*54fd6939SJiyong Park 	} else {
215*54fd6939SJiyong Park 		ret = (long)length;
216*54fd6939SJiyong Park 	}
217*54fd6939SJiyong Park 
218*54fd6939SJiyong Park semihosting_fail:
219*54fd6939SJiyong Park 	semihosting_file_close(file_handle);
220*54fd6939SJiyong Park 	return ret;
221*54fd6939SJiyong Park }
222*54fd6939SJiyong Park 
semihosting_exit(uint32_t reason,uint32_t subcode)223*54fd6939SJiyong Park void semihosting_exit(uint32_t reason, uint32_t subcode)
224*54fd6939SJiyong Park {
225*54fd6939SJiyong Park #ifdef __aarch64__
226*54fd6939SJiyong Park 	uint64_t parameters[] = {reason, subcode};
227*54fd6939SJiyong Park 
228*54fd6939SJiyong Park 	(void)semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t)&parameters);
229*54fd6939SJiyong Park #else
230*54fd6939SJiyong Park 	/* The subcode is not supported on AArch32. */
231*54fd6939SJiyong Park 	(void)semihosting_call(SEMIHOSTING_SYS_EXIT, reason);
232*54fd6939SJiyong Park #endif
233*54fd6939SJiyong Park }
234