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)¶meters);
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