blob: c38892fdd88822df46d99711e73802919b5cccc3 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Darwin Rambod32d4112014-06-09 11:12:59 -07002/*
3 * Copyright 2014 Broadcom Corporation
Darwin Rambod32d4112014-06-09 11:12:59 -07004 */
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>
Linus Walleij6cf16712015-03-23 11:06:11 +010015#include <command.h>
Simon Glassed38aef2020-05-10 11:40:03 -060016#include <env.h>
Simon Glass0f2af882020-05-10 11:40:05 -060017#include <log.h>
Sean Andersonadf073a2022-03-22 16:59:14 -040018#include <semihosting.h>
Darwin Rambod32d4112014-06-09 11:12:59 -070019
20#define SYSOPEN 0x01
21#define SYSCLOSE 0x02
22#define SYSREAD 0x06
23#define SYSFLEN 0x0C
24
25#define MODE_READ 0x0
26#define MODE_READBIN 0x1
27
28/*
29 * Call the handler
30 */
Linus Walleij1c2e27e2015-03-23 11:06:10 +010031static noinline long smh_trap(unsigned int sysnum, void *addr)
Darwin Rambod32d4112014-06-09 11:12:59 -070032{
Linus Walleij6fda2652014-12-15 11:05:56 +010033 register long result asm("r0");
Darwin Rambod32d4112014-06-09 11:12:59 -070034#if defined(CONFIG_ARM64)
35 asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
Vadzim Dambrouski98d38942015-10-19 19:40:14 +030036#elif defined(CONFIG_CPU_V7M)
37 asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
Darwin Rambod32d4112014-06-09 11:12:59 -070038#else
39 /* Note - untested placeholder */
40 asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
41#endif
42 return result;
43}
44
45/*
Linus Walleij7cc8ca82014-12-15 11:06:05 +010046 * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
47 * descriptor or -1 on error.
Darwin Rambod32d4112014-06-09 11:12:59 -070048 */
Sean Andersonadf073a2022-03-22 16:59:14 -040049long smh_open(const char *fname, char *modestr)
Darwin Rambod32d4112014-06-09 11:12:59 -070050{
Linus Walleij6fda2652014-12-15 11:05:56 +010051 long fd;
Linus Walleij7cc8ca82014-12-15 11:06:05 +010052 unsigned long mode;
53 struct smh_open_s {
54 const char *fname;
55 unsigned long mode;
56 size_t len;
57 } open;
Darwin Rambod32d4112014-06-09 11:12:59 -070058
Linus Walleij7cc8ca82014-12-15 11:06:05 +010059 debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
Darwin Rambod32d4112014-06-09 11:12:59 -070060
Linus Walleij7cc8ca82014-12-15 11:06:05 +010061 /* Check the file mode */
62 if (!(strcmp(modestr, "r"))) {
63 mode = MODE_READ;
64 } else if (!(strcmp(modestr, "rb"))) {
65 mode = MODE_READBIN;
66 } else {
67 printf("%s: ERROR mode \'%s\' not supported\n", __func__,
68 modestr);
Linus Walleij6fda2652014-12-15 11:05:56 +010069 return -1;
Darwin Rambod32d4112014-06-09 11:12:59 -070070 }
71
Linus Walleij7cc8ca82014-12-15 11:06:05 +010072 open.fname = fname;
73 open.len = strlen(fname);
74 open.mode = mode;
Darwin Rambod32d4112014-06-09 11:12:59 -070075
Linus Walleij7cc8ca82014-12-15 11:06:05 +010076 /* Open the file on the host */
77 fd = smh_trap(SYSOPEN, &open);
78 if (fd == -1)
79 printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
80 fname);
Darwin Rambod32d4112014-06-09 11:12:59 -070081
Linus Walleij7cc8ca82014-12-15 11:06:05 +010082 return fd;
Darwin Rambod32d4112014-06-09 11:12:59 -070083}
84
85/*
86 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
87 */
Sean Andersonadf073a2022-03-22 16:59:14 -040088long smh_read(long fd, void *memp, size_t len)
Darwin Rambod32d4112014-06-09 11:12:59 -070089{
Linus Walleij6fda2652014-12-15 11:05:56 +010090 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -070091 struct smh_read_s {
Linus Walleij6fda2652014-12-15 11:05:56 +010092 long fd;
Darwin Rambod32d4112014-06-09 11:12:59 -070093 void *memp;
Linus Walleij6fda2652014-12-15 11:05:56 +010094 size_t len;
Darwin Rambod32d4112014-06-09 11:12:59 -070095 } read;
96
Vadzim Dambrouski27382152015-10-19 19:40:15 +030097 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
Darwin Rambod32d4112014-06-09 11:12:59 -070098
99 read.fd = fd;
100 read.memp = memp;
101 read.len = len;
102
103 ret = smh_trap(SYSREAD, &read);
Linus Walleij6fda2652014-12-15 11:05:56 +0100104 if (ret < 0) {
Darwin Rambod32d4112014-06-09 11:12:59 -0700105 /*
106 * The ARM handler allows for returning partial lengths,
107 * but in practice this never happens so rather than create
108 * hard to maintain partial read loops and such, just fail
109 * with an error message.
110 */
Vadzim Dambrouski27382152015-10-19 19:40:15 +0300111 printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n",
Darwin Rambod32d4112014-06-09 11:12:59 -0700112 __func__, ret, fd, len, memp);
Linus Walleij6fda2652014-12-15 11:05:56 +0100113 return -1;
Darwin Rambod32d4112014-06-09 11:12:59 -0700114 }
Linus Walleij6fda2652014-12-15 11:05:56 +0100115
116 return 0;
Darwin Rambod32d4112014-06-09 11:12:59 -0700117}
118
119/*
Darwin Rambod32d4112014-06-09 11:12:59 -0700120 * Close the file using the file descriptor
121 */
Sean Andersonadf073a2022-03-22 16:59:14 -0400122long smh_close(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700123{
Linus Walleij6fda2652014-12-15 11:05:56 +0100124 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700125
Linus Walleij6fda2652014-12-15 11:05:56 +0100126 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700127
Linus Walleij6fda2652014-12-15 11:05:56 +0100128 ret = smh_trap(SYSCLOSE, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700129 if (ret == -1)
Linus Walleij6fda2652014-12-15 11:05:56 +0100130 printf("%s: ERROR fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700131
132 return ret;
133}
134
135/*
136 * Get the file length from the file descriptor
137 */
Sean Andersonadf073a2022-03-22 16:59:14 -0400138long smh_flen(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700139{
Linus Walleij6fda2652014-12-15 11:05:56 +0100140 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700141
Linus Walleij6fda2652014-12-15 11:05:56 +0100142 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700143
Linus Walleij6fda2652014-12-15 11:05:56 +0100144 ret = smh_trap(SYSFLEN, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700145 if (ret == -1)
Linus Walleij6fda2652014-12-15 11:05:56 +0100146 printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700147
148 return ret;
149}
150
Linus Walleij6cf16712015-03-23 11:06:11 +0100151static int smh_load_file(const char * const name, ulong load_addr,
152 ulong *end_addr)
153{
154 long fd;
155 long len;
156 long ret;
157
158 fd = smh_open(name, "rb");
159 if (fd == -1)
160 return -1;
161
Sean Andersonadf073a2022-03-22 16:59:14 -0400162 len = smh_flen(fd);
Linus Walleij6cf16712015-03-23 11:06:11 +0100163 if (len < 0) {
164 smh_close(fd);
165 return -1;
166 }
167
168 ret = smh_read(fd, (void *)load_addr, len);
169 smh_close(fd);
170
171 if (ret == 0) {
172 *end_addr = load_addr + len - 1;
173 printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
174 name,
175 load_addr,
176 *end_addr,
177 len);
178 } else {
179 printf("read failed\n");
180 return 0;
181 }
182
183 return 0;
184}
185
Simon Glassed38aef2020-05-10 11:40:03 -0600186static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
187 char *const argv[])
Linus Walleij6cf16712015-03-23 11:06:11 +0100188{
189 if (argc == 3 || argc == 4) {
190 ulong load_addr;
191 ulong end_addr = 0;
Ryan Harkinb564d522017-03-02 17:45:16 +0000192 int ret;
Linus Walleij6cf16712015-03-23 11:06:11 +0100193 char end_str[64];
194
Simon Glass3ff49ec2021-07-24 09:03:29 -0600195 load_addr = hextoul(argv[2], NULL);
Linus Walleij6cf16712015-03-23 11:06:11 +0100196 if (!load_addr)
197 return -1;
198
199 ret = smh_load_file(argv[1], load_addr, &end_addr);
200 if (ret < 0)
Ryan Harkinb564d522017-03-02 17:45:16 +0000201 return CMD_RET_FAILURE;
Linus Walleij6cf16712015-03-23 11:06:11 +0100202
203 /* Optionally save returned end to the environment */
204 if (argc == 4) {
205 sprintf(end_str, "0x%08lx", end_addr);
Simon Glass6a38e412017-08-03 12:22:09 -0600206 env_set(argv[3], end_str);
Linus Walleij6cf16712015-03-23 11:06:11 +0100207 }
208 } else {
209 return CMD_RET_USAGE;
210 }
211 return 0;
212}
213
214U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
215 "<file> 0x<address> [end var]\n"
216 " - load a semihosted file to the address specified\n"
217 " if the optional [end var] is specified, the end\n"
218 " address of the file will be stored in this environment\n"
219 " variable.\n");