]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
261d2760 DR |
2 | /* |
3 | * Copyright 2014 Broadcom Corporation | |
261d2760 DR |
4 | */ |
5 | ||
6 | /* | |
7 | * Minimal semihosting implementation for reading files into memory. If more | |
8 | * features like writing files or console output are required they can be | |
9 | * added later. This code has been tested on arm64/aarch64 fastmodel only. | |
10 | * An untested placeholder exists for armv7 architectures, but since they | |
11 | * are commonly available in silicon now, fastmodel usage makes less sense | |
12 | * for them. | |
13 | */ | |
14 | #include <common.h> | |
202a674b | 15 | #include <command.h> |
09140113 | 16 | #include <env.h> |
261d2760 DR |
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 | */ | |
e769f686 | 29 | static noinline long smh_trap(unsigned int sysnum, void *addr) |
261d2760 | 30 | { |
4e1ef150 | 31 | register long result asm("r0"); |
261d2760 DR |
32 | #if defined(CONFIG_ARM64) |
33 | asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr)); | |
432a6241 VD |
34 | #elif defined(CONFIG_CPU_V7M) |
35 | asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr)); | |
261d2760 DR |
36 | #else |
37 | /* Note - untested placeholder */ | |
38 | asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr)); | |
39 | #endif | |
40 | return result; | |
41 | } | |
42 | ||
43 | /* | |
9be5c661 LW |
44 | * Open a file on the host. Mode is "r" or "rb" currently. Returns a file |
45 | * descriptor or -1 on error. | |
261d2760 | 46 | */ |
9be5c661 | 47 | static long smh_open(const char *fname, char *modestr) |
261d2760 | 48 | { |
4e1ef150 | 49 | long fd; |
9be5c661 LW |
50 | unsigned long mode; |
51 | struct smh_open_s { | |
52 | const char *fname; | |
53 | unsigned long mode; | |
54 | size_t len; | |
55 | } open; | |
261d2760 | 56 | |
9be5c661 | 57 | debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr); |
261d2760 | 58 | |
9be5c661 LW |
59 | /* Check the file mode */ |
60 | if (!(strcmp(modestr, "r"))) { | |
61 | mode = MODE_READ; | |
62 | } else if (!(strcmp(modestr, "rb"))) { | |
63 | mode = MODE_READBIN; | |
64 | } else { | |
65 | printf("%s: ERROR mode \'%s\' not supported\n", __func__, | |
66 | modestr); | |
4e1ef150 | 67 | return -1; |
261d2760 DR |
68 | } |
69 | ||
9be5c661 LW |
70 | open.fname = fname; |
71 | open.len = strlen(fname); | |
72 | open.mode = mode; | |
261d2760 | 73 | |
9be5c661 LW |
74 | /* Open the file on the host */ |
75 | fd = smh_trap(SYSOPEN, &open); | |
76 | if (fd == -1) | |
77 | printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd, | |
78 | fname); | |
261d2760 | 79 | |
9be5c661 | 80 | return fd; |
261d2760 DR |
81 | } |
82 | ||
83 | /* | |
84 | * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure | |
85 | */ | |
4e1ef150 | 86 | static long smh_read(long fd, void *memp, size_t len) |
261d2760 | 87 | { |
4e1ef150 | 88 | long ret; |
261d2760 | 89 | struct smh_read_s { |
4e1ef150 | 90 | long fd; |
261d2760 | 91 | void *memp; |
4e1ef150 | 92 | size_t len; |
261d2760 DR |
93 | } read; |
94 | ||
7bdf75ca | 95 | debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); |
261d2760 DR |
96 | |
97 | read.fd = fd; | |
98 | read.memp = memp; | |
99 | read.len = len; | |
100 | ||
101 | ret = smh_trap(SYSREAD, &read); | |
4e1ef150 | 102 | if (ret < 0) { |
261d2760 DR |
103 | /* |
104 | * The ARM handler allows for returning partial lengths, | |
105 | * but in practice this never happens so rather than create | |
106 | * hard to maintain partial read loops and such, just fail | |
107 | * with an error message. | |
108 | */ | |
7bdf75ca | 109 | printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n", |
261d2760 | 110 | __func__, ret, fd, len, memp); |
4e1ef150 | 111 | return -1; |
261d2760 | 112 | } |
4e1ef150 LW |
113 | |
114 | return 0; | |
261d2760 DR |
115 | } |
116 | ||
261d2760 DR |
117 | /* |
118 | * Close the file using the file descriptor | |
119 | */ | |
4e1ef150 | 120 | static long smh_close(long fd) |
261d2760 | 121 | { |
4e1ef150 | 122 | long ret; |
261d2760 | 123 | |
4e1ef150 | 124 | debug("%s: fd %ld\n", __func__, fd); |
261d2760 | 125 | |
4e1ef150 | 126 | ret = smh_trap(SYSCLOSE, &fd); |
261d2760 | 127 | if (ret == -1) |
4e1ef150 | 128 | printf("%s: ERROR fd %ld\n", __func__, fd); |
261d2760 DR |
129 | |
130 | return ret; | |
131 | } | |
132 | ||
133 | /* | |
134 | * Get the file length from the file descriptor | |
135 | */ | |
4e1ef150 | 136 | static long smh_len_fd(long fd) |
261d2760 | 137 | { |
4e1ef150 | 138 | long ret; |
261d2760 | 139 | |
4e1ef150 | 140 | debug("%s: fd %ld\n", __func__, fd); |
261d2760 | 141 | |
4e1ef150 | 142 | ret = smh_trap(SYSFLEN, &fd); |
261d2760 | 143 | if (ret == -1) |
4e1ef150 | 144 | printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd); |
261d2760 DR |
145 | |
146 | return ret; | |
147 | } | |
148 | ||
202a674b LW |
149 | static int smh_load_file(const char * const name, ulong load_addr, |
150 | ulong *end_addr) | |
151 | { | |
152 | long fd; | |
153 | long len; | |
154 | long ret; | |
155 | ||
156 | fd = smh_open(name, "rb"); | |
157 | if (fd == -1) | |
158 | return -1; | |
159 | ||
160 | len = smh_len_fd(fd); | |
161 | if (len < 0) { | |
162 | smh_close(fd); | |
163 | return -1; | |
164 | } | |
165 | ||
166 | ret = smh_read(fd, (void *)load_addr, len); | |
167 | smh_close(fd); | |
168 | ||
169 | if (ret == 0) { | |
170 | *end_addr = load_addr + len - 1; | |
171 | printf("loaded file %s from %08lX to %08lX, %08lX bytes\n", | |
172 | name, | |
173 | load_addr, | |
174 | *end_addr, | |
175 | len); | |
176 | } else { | |
177 | printf("read failed\n"); | |
178 | return 0; | |
179 | } | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
09140113 SG |
184 | static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc, |
185 | char *const argv[]) | |
202a674b LW |
186 | { |
187 | if (argc == 3 || argc == 4) { | |
188 | ulong load_addr; | |
189 | ulong end_addr = 0; | |
072c8c4c | 190 | int ret; |
202a674b LW |
191 | char end_str[64]; |
192 | ||
193 | load_addr = simple_strtoul(argv[2], NULL, 16); | |
194 | if (!load_addr) | |
195 | return -1; | |
196 | ||
197 | ret = smh_load_file(argv[1], load_addr, &end_addr); | |
198 | if (ret < 0) | |
072c8c4c | 199 | return CMD_RET_FAILURE; |
202a674b LW |
200 | |
201 | /* Optionally save returned end to the environment */ | |
202 | if (argc == 4) { | |
203 | sprintf(end_str, "0x%08lx", end_addr); | |
382bee57 | 204 | env_set(argv[3], end_str); |
202a674b LW |
205 | } |
206 | } else { | |
207 | return CMD_RET_USAGE; | |
208 | } | |
209 | return 0; | |
210 | } | |
211 | ||
212 | U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting", | |
213 | "<file> 0x<address> [end var]\n" | |
214 | " - load a semihosted file to the address specified\n" | |
215 | " if the optional [end var] is specified, the end\n" | |
216 | " address of the file will be stored in this environment\n" | |
217 | " variable.\n"); |