xref: /openbmc/u-boot/arch/arm/lib/semihosting.c (revision 5ff10aa7)
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 int smh_trap(unsigned int sysnum, void *addr)
30 {
31 	register int 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, load a file into memory, and close it. Check that the available space
43  * is sufficient to store the entire file. Return the bytes actually read from
44  * the file as seen by the read function. The verbose flag enables some extra
45  * printing of successful read status.
46  */
47 int smh_load(const char *fname, void *memp, int avail, int verbose)
48 {
49 	int ret, fd, len;
50 
51 	ret = -1;
52 
53 	debug("%s: fname \'%s\', avail %u, memp %p\n", __func__, fname,
54 	      avail, memp);
55 
56 	/* Open the file */
57 	fd = smh_open(fname, "rb");
58 	if (fd == -1)
59 		return ret;
60 
61 	/* Get the file length */
62 	ret = smh_len_fd(fd);
63 	if (ret == -1) {
64 		smh_close(fd);
65 		return ret;
66 	}
67 
68 	/* Check that the file will fit in the supplied buffer */
69 	if (ret > avail) {
70 		printf("%s: ERROR ret %d, avail %u\n", __func__, ret,
71 		       avail);
72 		smh_close(fd);
73 		return ret;
74 	}
75 
76 	len = ret;
77 
78 	/* Read the file into the buffer */
79 	ret = smh_read(fd, memp, len);
80 	if (ret == 0) {
81 		/* Print successful load information if requested */
82 		if (verbose) {
83 			printf("\n%s\n", fname);
84 			printf("    0x%8p dest\n", memp);
85 			printf("    0x%08x size\n", len);
86 			printf("    0x%08x avail\n", avail);
87 		}
88 	}
89 
90 	/* Close the file */
91 	smh_close(fd);
92 
93 	return ret;
94 }
95 
96 /*
97  * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
98  */
99 int smh_read(int fd, void *memp, int len)
100 {
101 	int ret;
102 	struct smh_read_s {
103 		int fd;
104 		void *memp;
105 		int len;
106 	} read;
107 
108 	debug("%s: fd %d, memp %p, len %d\n", __func__, fd, memp, len);
109 
110 	read.fd = fd;
111 	read.memp = memp;
112 	read.len = len;
113 
114 	ret = smh_trap(SYSREAD, &read);
115 	if (ret == 0) {
116 		return 0;
117 	} else {
118 		/*
119 		 * The ARM handler allows for returning partial lengths,
120 		 * but in practice this never happens so rather than create
121 		 * hard to maintain partial read loops and such, just fail
122 		 * with an error message.
123 		 */
124 		printf("%s: ERROR ret %d, fd %d, len %u memp %p\n",
125 		       __func__, ret, fd, len, memp);
126 	}
127 	return ret;
128 }
129 
130 /*
131  * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
132  * descriptor or -1 on error.
133  */
134 int smh_open(const char *fname, char *modestr)
135 {
136 	int ret, fd, mode;
137 	struct smh_open_s {
138 		const char *fname;
139 		unsigned int mode;
140 		unsigned int len;
141 	} open;
142 
143 	debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
144 
145 	ret = -1;
146 
147 	/* Check the file mode */
148 	if (!(strcmp(modestr, "r"))) {
149 		mode = MODE_READ;
150 	} else if (!(strcmp(modestr, "rb"))) {
151 		mode = MODE_READBIN;
152 	} else {
153 		printf("%s: ERROR mode \'%s\' not supported\n", __func__,
154 		       modestr);
155 		return ret;
156 	}
157 
158 	open.fname = fname;
159 	open.len = strlen(fname);
160 	open.mode = mode;
161 
162 	/* Open the file on the host */
163 	fd = smh_trap(SYSOPEN, &open);
164 	if (fd == -1)
165 		printf("%s: ERROR fd %d for file \'%s\'\n", __func__, fd,
166 		       fname);
167 
168 	return fd;
169 }
170 
171 /*
172  * Close the file using the file descriptor
173  */
174 int smh_close(int fd)
175 {
176 	int ret;
177 	long fdlong;
178 
179 	debug("%s: fd %d\n", __func__, fd);
180 
181 	fdlong = (long)fd;
182 	ret = smh_trap(SYSCLOSE, &fdlong);
183 	if (ret == -1)
184 		printf("%s: ERROR fd %d\n", __func__, fd);
185 
186 	return ret;
187 }
188 
189 /*
190  * Get the file length from the file descriptor
191  */
192 int smh_len_fd(int fd)
193 {
194 	int ret;
195 	long fdlong;
196 
197 	debug("%s: fd %d\n", __func__, fd);
198 
199 	fdlong = (long)fd;
200 	ret = smh_trap(SYSFLEN, &fdlong);
201 	if (ret == -1)
202 		printf("%s: ERROR ret %d\n", __func__, ret);
203 
204 	return ret;
205 }
206 
207 /*
208  * Get the file length from the filename
209  */
210 int smh_len(const char *fname)
211 {
212 	int ret, fd, len;
213 
214 	debug("%s: file \'%s\'\n", __func__, fname);
215 
216 	/* Open the file */
217 	fd = smh_open(fname, "rb");
218 	if (fd == -1)
219 		return fd;
220 
221 	/* Get the file length */
222 	len = smh_len_fd(fd);
223 
224 	/* Close the file */
225 	ret = smh_close(fd);
226 	if (ret == -1)
227 		return ret;
228 
229 	debug("%s: returning len %d\n", __func__, len);
230 
231 	/* Return the file length (or -1 error indication) */
232 	return len;
233 }
234