xref: /openbmc/u-boot/arch/arm/lib/semihosting.c (revision 5d6050fd)
1 /*
2  * Copyright 2014 Broadcom Corporation
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 /*
8  * Minimal semihosting implementation for reading files into memory. If more
9  * features like writing files or console output are required they can be
10  * added later. This code has been tested on arm64/aarch64 fastmodel only.
11  * An untested placeholder exists for armv7 architectures, but since they
12  * are commonly available in silicon now, fastmodel usage makes less sense
13  * for them.
14  */
15 #include <common.h>
16 #include <asm/semihosting.h>
17 
18 #define SYSOPEN		0x01
19 #define SYSCLOSE	0x02
20 #define SYSREAD		0x06
21 #define SYSFLEN		0x0C
22 
23 #define MODE_READ	0x0
24 #define MODE_READBIN	0x1
25 
26 /*
27  * Call the handler
28  */
29 static long smh_trap(unsigned int sysnum, void *addr)
30 {
31 	register long result asm("r0");
32 #if defined(CONFIG_ARM64)
33 	asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
34 #else
35 	/* Note - untested placeholder */
36 	asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
37 #endif
38 	return result;
39 }
40 
41 /*
42  * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
43  * descriptor or -1 on error.
44  */
45 static long smh_open(const char *fname, char *modestr)
46 {
47 	long fd;
48 	unsigned long mode;
49 	struct smh_open_s {
50 		const char *fname;
51 		unsigned long mode;
52 		size_t len;
53 	} open;
54 
55 	debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
56 
57 	/* Check the file mode */
58 	if (!(strcmp(modestr, "r"))) {
59 		mode = MODE_READ;
60 	} else if (!(strcmp(modestr, "rb"))) {
61 		mode = MODE_READBIN;
62 	} else {
63 		printf("%s: ERROR mode \'%s\' not supported\n", __func__,
64 		       modestr);
65 		return -1;
66 	}
67 
68 	open.fname = fname;
69 	open.len = strlen(fname);
70 	open.mode = mode;
71 
72 	/* Open the file on the host */
73 	fd = smh_trap(SYSOPEN, &open);
74 	if (fd == -1)
75 		printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
76 		       fname);
77 
78 	return fd;
79 }
80 
81 /*
82  * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
83  */
84 static long smh_read(long fd, void *memp, size_t len)
85 {
86 	long ret;
87 	struct smh_read_s {
88 		long fd;
89 		void *memp;
90 		size_t len;
91 	} read;
92 
93 	debug("%s: fd %ld, memp %p, len %lu\n", __func__, fd, memp, len);
94 
95 	read.fd = fd;
96 	read.memp = memp;
97 	read.len = len;
98 
99 	ret = smh_trap(SYSREAD, &read);
100 	if (ret < 0) {
101 		/*
102 		 * The ARM handler allows for returning partial lengths,
103 		 * but in practice this never happens so rather than create
104 		 * hard to maintain partial read loops and such, just fail
105 		 * with an error message.
106 		 */
107 		printf("%s: ERROR ret %ld, fd %ld, len %lu memp %p\n",
108 		       __func__, ret, fd, len, memp);
109 		return -1;
110 	}
111 
112 	return 0;
113 }
114 
115 /*
116  * Close the file using the file descriptor
117  */
118 static long smh_close(long fd)
119 {
120 	long ret;
121 
122 	debug("%s: fd %ld\n", __func__, fd);
123 
124 	ret = smh_trap(SYSCLOSE, &fd);
125 	if (ret == -1)
126 		printf("%s: ERROR fd %ld\n", __func__, fd);
127 
128 	return ret;
129 }
130 
131 /*
132  * Get the file length from the file descriptor
133  */
134 static long smh_len_fd(long fd)
135 {
136 	long ret;
137 
138 	debug("%s: fd %ld\n", __func__, fd);
139 
140 	ret = smh_trap(SYSFLEN, &fd);
141 	if (ret == -1)
142 		printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
143 
144 	return ret;
145 }
146 
147 /*
148  * Open, load a file into memory, and close it. Check that the available space
149  * is sufficient to store the entire file. Return the bytes actually read from
150  * the file as seen by the read function. The verbose flag enables some extra
151  * printing of successful read status.
152  */
153 int smh_load(const char *fname, void *memp, int avail, int verbose)
154 {
155 	long ret;
156 	long fd;
157 	size_t len;
158 
159 	ret = -1;
160 
161 	debug("%s: fname \'%s\', avail %u, memp %p\n", __func__, fname,
162 	      avail, memp);
163 
164 	/* Open the file */
165 	fd = smh_open(fname, "rb");
166 	if (fd == -1)
167 		return -1;
168 
169 	/* Get the file length */
170 	ret = smh_len_fd(fd);
171 	if (ret == -1) {
172 		smh_close(fd);
173 		return -1;
174 	}
175 
176 	/* Check that the file will fit in the supplied buffer */
177 	if (ret > avail) {
178 		printf("%s: ERROR ret %ld, avail %u\n", __func__, ret,
179 		       avail);
180 		smh_close(fd);
181 		return -1;
182 	}
183 
184 	len = ret;
185 
186 	/* Read the file into the buffer */
187 	ret = smh_read(fd, memp, len);
188 	if (ret == 0) {
189 		/* Print successful load information if requested */
190 		if (verbose) {
191 			printf("\n%s\n", fname);
192 			printf("    0x%8p dest\n", memp);
193 			printf("    0x%08lx size\n", len);
194 			printf("    0x%08x avail\n", avail);
195 		}
196 	}
197 
198 	/* Close the file */
199 	smh_close(fd);
200 
201 	return ret;
202 }
203 
204 /*
205  * Get the file length from the filename
206  */
207 long smh_len(const char *fname)
208 {
209 	long ret;
210 	long fd;
211 	long len;
212 
213 	debug("%s: file \'%s\'\n", __func__, fname);
214 
215 	/* Open the file */
216 	fd = smh_open(fname, "rb");
217 	if (fd < 0)
218 		return fd;
219 
220 	/* Get the file length */
221 	len = smh_len_fd(fd);
222 	if (len < 0) {
223 		smh_close(fd);
224 		return len;
225 	}
226 
227 	/* Close the file */
228 	ret = smh_close(fd);
229 	if (ret < 0)
230 		return ret;
231 
232 	debug("%s: returning len %ld\n", __func__, len);
233 
234 	/* Return the file length (or -1 error indication) */
235 	return len;
236 }
237