blob: b983cc39352242d5b475a21bea0dda6a0b9b4042 [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
Darwin Rambod32d4112014-06-09 11:12:59 -070025/*
26 * Call the handler
27 */
Linus Walleij1c2e27e2015-03-23 11:06:10 +010028static noinline long smh_trap(unsigned int sysnum, void *addr)
Darwin Rambod32d4112014-06-09 11:12:59 -070029{
Linus Walleij6fda2652014-12-15 11:05:56 +010030 register long result asm("r0");
Darwin Rambod32d4112014-06-09 11:12:59 -070031#if defined(CONFIG_ARM64)
32 asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
Vadzim Dambrouski98d38942015-10-19 19:40:14 +030033#elif defined(CONFIG_CPU_V7M)
34 asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
Darwin Rambod32d4112014-06-09 11:12:59 -070035#else
36 /* Note - untested placeholder */
37 asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
38#endif
39 return result;
40}
41
42/*
Linus Walleij7cc8ca82014-12-15 11:06:05 +010043 * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
44 * descriptor or -1 on error.
Darwin Rambod32d4112014-06-09 11:12:59 -070045 */
Sean Andersona3a6c692022-03-22 16:59:15 -040046long smh_open(const char *fname, enum smh_open_mode mode)
Darwin Rambod32d4112014-06-09 11:12:59 -070047{
Linus Walleij6fda2652014-12-15 11:05:56 +010048 long fd;
Linus Walleij7cc8ca82014-12-15 11:06:05 +010049 struct smh_open_s {
50 const char *fname;
51 unsigned long mode;
52 size_t len;
53 } open;
Darwin Rambod32d4112014-06-09 11:12:59 -070054
Sean Andersona3a6c692022-03-22 16:59:15 -040055 debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
Darwin Rambod32d4112014-06-09 11:12:59 -070056
Linus Walleij7cc8ca82014-12-15 11:06:05 +010057 open.fname = fname;
58 open.len = strlen(fname);
59 open.mode = mode;
Darwin Rambod32d4112014-06-09 11:12:59 -070060
Linus Walleij7cc8ca82014-12-15 11:06:05 +010061 /* Open the file on the host */
62 fd = smh_trap(SYSOPEN, &open);
63 if (fd == -1)
64 printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
65 fname);
Darwin Rambod32d4112014-06-09 11:12:59 -070066
Linus Walleij7cc8ca82014-12-15 11:06:05 +010067 return fd;
Darwin Rambod32d4112014-06-09 11:12:59 -070068}
69
70/*
71 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
72 */
Sean Andersonadf073a2022-03-22 16:59:14 -040073long smh_read(long fd, void *memp, size_t len)
Darwin Rambod32d4112014-06-09 11:12:59 -070074{
Linus Walleij6fda2652014-12-15 11:05:56 +010075 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -070076 struct smh_read_s {
Linus Walleij6fda2652014-12-15 11:05:56 +010077 long fd;
Darwin Rambod32d4112014-06-09 11:12:59 -070078 void *memp;
Linus Walleij6fda2652014-12-15 11:05:56 +010079 size_t len;
Darwin Rambod32d4112014-06-09 11:12:59 -070080 } read;
81
Vadzim Dambrouski27382152015-10-19 19:40:15 +030082 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
Darwin Rambod32d4112014-06-09 11:12:59 -070083
84 read.fd = fd;
85 read.memp = memp;
86 read.len = len;
87
88 ret = smh_trap(SYSREAD, &read);
Linus Walleij6fda2652014-12-15 11:05:56 +010089 if (ret < 0) {
Darwin Rambod32d4112014-06-09 11:12:59 -070090 /*
91 * The ARM handler allows for returning partial lengths,
92 * but in practice this never happens so rather than create
93 * hard to maintain partial read loops and such, just fail
94 * with an error message.
95 */
Vadzim Dambrouski27382152015-10-19 19:40:15 +030096 printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n",
Darwin Rambod32d4112014-06-09 11:12:59 -070097 __func__, ret, fd, len, memp);
Linus Walleij6fda2652014-12-15 11:05:56 +010098 return -1;
Darwin Rambod32d4112014-06-09 11:12:59 -070099 }
Linus Walleij6fda2652014-12-15 11:05:56 +0100100
101 return 0;
Darwin Rambod32d4112014-06-09 11:12:59 -0700102}
103
104/*
Darwin Rambod32d4112014-06-09 11:12:59 -0700105 * Close the file using the file descriptor
106 */
Sean Andersonadf073a2022-03-22 16:59:14 -0400107long smh_close(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700108{
Linus Walleij6fda2652014-12-15 11:05:56 +0100109 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700110
Linus Walleij6fda2652014-12-15 11:05:56 +0100111 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700112
Linus Walleij6fda2652014-12-15 11:05:56 +0100113 ret = smh_trap(SYSCLOSE, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700114 if (ret == -1)
Linus Walleij6fda2652014-12-15 11:05:56 +0100115 printf("%s: ERROR fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700116
117 return ret;
118}
119
120/*
121 * Get the file length from the file descriptor
122 */
Sean Andersonadf073a2022-03-22 16:59:14 -0400123long smh_flen(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700124{
Linus Walleij6fda2652014-12-15 11:05:56 +0100125 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700126
Linus Walleij6fda2652014-12-15 11:05:56 +0100127 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700128
Linus Walleij6fda2652014-12-15 11:05:56 +0100129 ret = smh_trap(SYSFLEN, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700130 if (ret == -1)
Linus Walleij6fda2652014-12-15 11:05:56 +0100131 printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700132
133 return ret;
134}
135
Linus Walleij6cf16712015-03-23 11:06:11 +0100136static int smh_load_file(const char * const name, ulong load_addr,
137 ulong *end_addr)
138{
139 long fd;
140 long len;
141 long ret;
142
Sean Andersona3a6c692022-03-22 16:59:15 -0400143 fd = smh_open(name, MODE_READ | MODE_BINARY);
Linus Walleij6cf16712015-03-23 11:06:11 +0100144 if (fd == -1)
145 return -1;
146
Sean Andersonadf073a2022-03-22 16:59:14 -0400147 len = smh_flen(fd);
Linus Walleij6cf16712015-03-23 11:06:11 +0100148 if (len < 0) {
149 smh_close(fd);
150 return -1;
151 }
152
153 ret = smh_read(fd, (void *)load_addr, len);
154 smh_close(fd);
155
156 if (ret == 0) {
157 *end_addr = load_addr + len - 1;
158 printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
159 name,
160 load_addr,
161 *end_addr,
162 len);
163 } else {
164 printf("read failed\n");
165 return 0;
166 }
167
168 return 0;
169}
170
Simon Glassed38aef2020-05-10 11:40:03 -0600171static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
172 char *const argv[])
Linus Walleij6cf16712015-03-23 11:06:11 +0100173{
174 if (argc == 3 || argc == 4) {
175 ulong load_addr;
176 ulong end_addr = 0;
Ryan Harkinb564d522017-03-02 17:45:16 +0000177 int ret;
Linus Walleij6cf16712015-03-23 11:06:11 +0100178 char end_str[64];
179
Simon Glass3ff49ec2021-07-24 09:03:29 -0600180 load_addr = hextoul(argv[2], NULL);
Linus Walleij6cf16712015-03-23 11:06:11 +0100181 if (!load_addr)
182 return -1;
183
184 ret = smh_load_file(argv[1], load_addr, &end_addr);
185 if (ret < 0)
Ryan Harkinb564d522017-03-02 17:45:16 +0000186 return CMD_RET_FAILURE;
Linus Walleij6cf16712015-03-23 11:06:11 +0100187
188 /* Optionally save returned end to the environment */
189 if (argc == 4) {
190 sprintf(end_str, "0x%08lx", end_addr);
Simon Glass6a38e412017-08-03 12:22:09 -0600191 env_set(argv[3], end_str);
Linus Walleij6cf16712015-03-23 11:06:11 +0100192 }
193 } else {
194 return CMD_RET_USAGE;
195 }
196 return 0;
197}
198
199U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
200 "<file> 0x<address> [end var]\n"
201 " - load a semihosted file to the address specified\n"
202 " if the optional [end var] is specified, the end\n"
203 " address of the file will be stored in this environment\n"
204 " variable.\n");