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