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