semihosting.c 4.94 KB
Newer Older
1
/*
2
 * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
3
 *
dp-arm's avatar
dp-arm committed
4
 * SPDX-License-Identifier: BSD-3-Clause
5
6
7
8
 */

#include <assert.h>
#include <errno.h>
9
#include <string.h>
10

11
12
#include <lib/semihosting.h>

13
14
15
16
#ifndef SEMIHOSTING_SUPPORTED
#define SEMIHOSTING_SUPPORTED  1
#endif

17
long semihosting_call(unsigned long operation, uintptr_t system_block_address);
18
19
20

typedef struct {
	const char *file_name;
21
22
	unsigned long mode;
	size_t name_length;
23
} smh_file_open_block_t;
24
25

typedef struct {
26
	long handle;
27
	uintptr_t buffer;
28
	size_t length;
29
} smh_file_read_write_block_t;
30
31

typedef struct {
32
33
	long handle;
	ssize_t location;
34
} smh_file_seek_block_t;
35
36
37

typedef struct {
	char *command_line;
38
	size_t command_length;
39
} smh_system_block_t;
40

41
long semihosting_connection_supported(void)
42
43
44
45
{
	return SEMIHOSTING_SUPPORTED;
}

46
long semihosting_file_open(const char *file_name, size_t mode)
47
{
48
	smh_file_open_block_t open_block;
49
50
51
52
53

	open_block.file_name = file_name;
	open_block.mode = mode;
	open_block.name_length = strlen(file_name);

54
	return semihosting_call(SEMIHOSTING_SYS_OPEN, (uintptr_t)&open_block);
55
56
}

57
long semihosting_file_seek(long file_handle, ssize_t offset)
58
{
59
	smh_file_seek_block_t seek_block;
60
	long result;
61
62
63
64

	seek_block.handle = file_handle;
	seek_block.location = offset;

65
	result = semihosting_call(SEMIHOSTING_SYS_SEEK, (uintptr_t)&seek_block);
66

67
	if (result != 0) {
68
		result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0);
69
	}
70
71
72
73

	return result;
}

74
long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer)
75
{
76
	smh_file_read_write_block_t read_block;
77
	long result = -EINVAL;
78

79
	if ((length == NULL) || (buffer == (uintptr_t)NULL)) {
80
		return result;
81
	}
82
83
84
85
86

	read_block.handle = file_handle;
	read_block.buffer = buffer;
	read_block.length = *length;

87
	result = semihosting_call(SEMIHOSTING_SYS_READ, (uintptr_t)&read_block);
88
89
90
91
92
93

	if (result == *length) {
		return -EINVAL;
	} else if (result < *length) {
		*length -= result;
		return 0;
94
	} else {
95
		return result;
96
	}
97
98
}

99
100
long semihosting_file_write(long file_handle, size_t *length,
				const uintptr_t buffer)
101
{
102
	smh_file_read_write_block_t write_block;
103
	long result = -EINVAL;
104

105
	if ((length == NULL) || (buffer == (uintptr_t)NULL)) {
106
		return -EINVAL;
107
	}
108
109

	write_block.handle = file_handle;
110
	write_block.buffer = (uintptr_t)buffer; /* cast away const */
111
112
	write_block.length = *length;

113
	result = semihosting_call(SEMIHOSTING_SYS_WRITE,
114
		(uintptr_t)&write_block);
115

116
117
118
	*length = result;

	return (result == 0) ? 0 : -EINVAL;
119
120
}

121
long semihosting_file_close(long file_handle)
122
{
123
	return semihosting_call(SEMIHOSTING_SYS_CLOSE, (uintptr_t)&file_handle);
124
125
}

126
long semihosting_file_length(long file_handle)
127
{
128
	return semihosting_call(SEMIHOSTING_SYS_FLEN, (uintptr_t)&file_handle);
129
130
131
132
}

char semihosting_read_char(void)
{
133
	return semihosting_call(SEMIHOSTING_SYS_READC, 0);
134
135
136
137
}

void semihosting_write_char(char character)
{
138
	semihosting_call(SEMIHOSTING_SYS_WRITEC, (uintptr_t)&character);
139
140
141
142
}

void semihosting_write_string(char *string)
{
143
	semihosting_call(SEMIHOSTING_SYS_WRITE0, (uintptr_t)string);
144
145
}

146
long semihosting_system(char *command_line)
147
{
148
	smh_system_block_t system_block;
149
150
151
152
153

	system_block.command_line = command_line;
	system_block.command_length = strlen(command_line);

	return semihosting_call(SEMIHOSTING_SYS_SYSTEM,
154
		(uintptr_t)&system_block);
155
156
}

157
long semihosting_get_flen(const char *file_name)
158
{
159
	long file_handle;
160
	long length;
161

162
	assert(semihosting_connection_supported() != 0);
163
164

	file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB);
165
	if (file_handle == -1) {
166
		return file_handle;
167
	}
168
169
170
171

	/* Find the length of the file */
	length = semihosting_file_length(file_handle);

172
	return (semihosting_file_close(file_handle) != 0) ? -1 : length;
173
174
}

175
176
long semihosting_download_file(const char *file_name,
			      size_t buf_size,
177
			      uintptr_t buf)
178
{
179
180
181
	long ret = -EINVAL;
	size_t length;
	long file_handle;
182
183

	/* Null pointer check */
184
	if (buf == 0U) {
185
		return ret;
186
	}
187

188
	assert(semihosting_connection_supported() != 0);
189
190

	file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB);
191
	if (file_handle == -1) {
192
		return ret;
193
	}
194
195
196

	/* Find the actual length of the file */
	length = semihosting_file_length(file_handle);
197
	if (length == (size_t)(-1)) {
198
		goto semihosting_fail;
199
	}
200
201

	/* Signal error if we do not have enough space for the file */
202
	if (length > buf_size) {
203
		goto semihosting_fail;
204
	}
205
206
207
208
209
210
211

	/*
	 * A successful read will return 0 in which case we pass back
	 * the actual number of bytes read. Else we pass a negative
	 * value indicating an error.
	 */
	ret = semihosting_file_read(file_handle, &length, buf);
212
	if (ret != 0) {
213
		goto semihosting_fail;
214
215
216
	} else {
		ret = (long)length;
	}
217
218
219
220
221

semihosting_fail:
	semihosting_file_close(file_handle);
	return ret;
}
222
223
224
225
226
227

void semihosting_exit(uint32_t reason, uint32_t subcode)
{
#ifdef __aarch64__
	uint64_t parameters[] = {reason, subcode};

228
	(void)semihosting_call(SEMIHOSTING_SYS_EXIT, (uintptr_t)&parameters);
229
230
#else
	/* The subcode is not supported on AArch32. */
231
	(void)semihosting_call(SEMIHOSTING_SYS_EXIT, reason);
232
233
#endif
}