blob: 16864576853f7c1ec0cb016b667a29fa60d9e7c4 [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
Sean Anderson003eaa82022-03-22 16:59:16 -040024#define SYSERRNO 0x13
Darwin Rambod32d4112014-06-09 11:12:59 -070025
Darwin Rambod32d4112014-06-09 11:12:59 -070026/*
27 * Call the handler
28 */
Linus Walleij1c2e27e2015-03-23 11:06:10 +010029static noinline long smh_trap(unsigned int sysnum, void *addr)
Darwin Rambod32d4112014-06-09 11:12:59 -070030{
Linus Walleij6fda2652014-12-15 11:05:56 +010031 register long result asm("r0");
Darwin Rambod32d4112014-06-09 11:12:59 -070032#if defined(CONFIG_ARM64)
33 asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
Vadzim Dambrouski98d38942015-10-19 19:40:14 +030034#elif defined(CONFIG_CPU_V7M)
35 asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
Darwin Rambod32d4112014-06-09 11:12:59 -070036#else
37 /* Note - untested placeholder */
38 asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
39#endif
40 return result;
41}
42
Sean Anderson003eaa82022-03-22 16:59:16 -040043/**
44 * smh_errno() - Read the host's errno
45 *
46 * This gets the value of the host's errno and negates it. The host's errno may
47 * or may not be set, so only call this function if a previous semihosting call
48 * has failed.
49 *
50 * Return: a negative error value
Darwin Rambod32d4112014-06-09 11:12:59 -070051 */
Sean Anderson003eaa82022-03-22 16:59:16 -040052static int smh_errno(void)
53{
54 long ret = smh_trap(SYSERRNO, NULL);
55
56 if (ret > 0 && ret < INT_MAX)
57 return -ret;
58 return -EIO;
59}
60
Sean Andersona3a6c692022-03-22 16:59:15 -040061long smh_open(const char *fname, enum smh_open_mode mode)
Darwin Rambod32d4112014-06-09 11:12:59 -070062{
Linus Walleij6fda2652014-12-15 11:05:56 +010063 long fd;
Linus Walleij7cc8ca82014-12-15 11:06:05 +010064 struct smh_open_s {
65 const char *fname;
66 unsigned long mode;
67 size_t len;
68 } open;
Darwin Rambod32d4112014-06-09 11:12:59 -070069
Sean Andersona3a6c692022-03-22 16:59:15 -040070 debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
Darwin Rambod32d4112014-06-09 11:12:59 -070071
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)
Sean Anderson003eaa82022-03-22 16:59:16 -040079 return smh_errno();
Linus Walleij7cc8ca82014-12-15 11:06:05 +010080 return fd;
Darwin Rambod32d4112014-06-09 11:12:59 -070081}
82
83/*
84 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
85 */
Sean Andersonadf073a2022-03-22 16:59:14 -040086long smh_read(long fd, void *memp, size_t len)
Darwin Rambod32d4112014-06-09 11:12:59 -070087{
Linus Walleij6fda2652014-12-15 11:05:56 +010088 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -070089 struct smh_read_s {
Linus Walleij6fda2652014-12-15 11:05:56 +010090 long fd;
Darwin Rambod32d4112014-06-09 11:12:59 -070091 void *memp;
Linus Walleij6fda2652014-12-15 11:05:56 +010092 size_t len;
Darwin Rambod32d4112014-06-09 11:12:59 -070093 } read;
94
Vadzim Dambrouski27382152015-10-19 19:40:15 +030095 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
Darwin Rambod32d4112014-06-09 11:12:59 -070096
97 read.fd = fd;
98 read.memp = memp;
99 read.len = len;
100
101 ret = smh_trap(SYSREAD, &read);
Sean Anderson003eaa82022-03-22 16:59:16 -0400102 if (ret < 0)
103 return smh_errno();
104 return len - ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700105}
106
107/*
Darwin Rambod32d4112014-06-09 11:12:59 -0700108 * Close the file using the file descriptor
109 */
Sean Andersonadf073a2022-03-22 16:59:14 -0400110long smh_close(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700111{
Linus Walleij6fda2652014-12-15 11:05:56 +0100112 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700113
Linus Walleij6fda2652014-12-15 11:05:56 +0100114 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700115
Linus Walleij6fda2652014-12-15 11:05:56 +0100116 ret = smh_trap(SYSCLOSE, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700117 if (ret == -1)
Sean Anderson003eaa82022-03-22 16:59:16 -0400118 return smh_errno();
119 return 0;
Darwin Rambod32d4112014-06-09 11:12:59 -0700120}
121
122/*
123 * Get the file length from the file descriptor
124 */
Sean Andersonadf073a2022-03-22 16:59:14 -0400125long smh_flen(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700126{
Linus Walleij6fda2652014-12-15 11:05:56 +0100127 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700128
Linus Walleij6fda2652014-12-15 11:05:56 +0100129 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700130
Linus Walleij6fda2652014-12-15 11:05:56 +0100131 ret = smh_trap(SYSFLEN, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700132 if (ret == -1)
Sean Anderson003eaa82022-03-22 16:59:16 -0400133 return smh_errno();
Darwin Rambod32d4112014-06-09 11:12:59 -0700134 return ret;
135}
136
Linus Walleij6cf16712015-03-23 11:06:11 +0100137static int smh_load_file(const char * const name, ulong load_addr,
138 ulong *end_addr)
139{
140 long fd;
141 long len;
142 long ret;
143
Sean Andersona3a6c692022-03-22 16:59:15 -0400144 fd = smh_open(name, MODE_READ | MODE_BINARY);
Sean Anderson003eaa82022-03-22 16:59:16 -0400145 if (fd < 0)
146 return fd;
Linus Walleij6cf16712015-03-23 11:06:11 +0100147
Sean Andersonadf073a2022-03-22 16:59:14 -0400148 len = smh_flen(fd);
Linus Walleij6cf16712015-03-23 11:06:11 +0100149 if (len < 0) {
150 smh_close(fd);
Sean Anderson003eaa82022-03-22 16:59:16 -0400151 return len;
Linus Walleij6cf16712015-03-23 11:06:11 +0100152 }
153
154 ret = smh_read(fd, (void *)load_addr, len);
155 smh_close(fd);
156
Sean Anderson003eaa82022-03-22 16:59:16 -0400157 if (ret == len) {
Linus Walleij6cf16712015-03-23 11:06:11 +0100158 *end_addr = load_addr + len - 1;
159 printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
160 name,
161 load_addr,
162 *end_addr,
163 len);
Sean Anderson003eaa82022-03-22 16:59:16 -0400164 } else if (ret >= 0) {
165 ret = -EAGAIN;
166 }
167
168 if (ret < 0) {
169 printf("read failed: %ld\n", ret);
170 return ret;
Linus Walleij6cf16712015-03-23 11:06:11 +0100171 }
172
173 return 0;
174}
175
Simon Glassed38aef2020-05-10 11:40:03 -0600176static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
177 char *const argv[])
Linus Walleij6cf16712015-03-23 11:06:11 +0100178{
179 if (argc == 3 || argc == 4) {
180 ulong load_addr;
181 ulong end_addr = 0;
Ryan Harkinb564d522017-03-02 17:45:16 +0000182 int ret;
Linus Walleij6cf16712015-03-23 11:06:11 +0100183 char end_str[64];
184
Simon Glass3ff49ec2021-07-24 09:03:29 -0600185 load_addr = hextoul(argv[2], NULL);
Linus Walleij6cf16712015-03-23 11:06:11 +0100186 if (!load_addr)
187 return -1;
188
189 ret = smh_load_file(argv[1], load_addr, &end_addr);
190 if (ret < 0)
Ryan Harkinb564d522017-03-02 17:45:16 +0000191 return CMD_RET_FAILURE;
Linus Walleij6cf16712015-03-23 11:06:11 +0100192
193 /* Optionally save returned end to the environment */
194 if (argc == 4) {
195 sprintf(end_str, "0x%08lx", end_addr);
Simon Glass6a38e412017-08-03 12:22:09 -0600196 env_set(argv[3], end_str);
Linus Walleij6cf16712015-03-23 11:06:11 +0100197 }
198 } else {
199 return CMD_RET_USAGE;
200 }
201 return 0;
202}
203
204U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
205 "<file> 0x<address> [end var]\n"
206 " - load a semihosted file to the address specified\n"
207 " if the optional [end var] is specified, the end\n"
208 " address of the file will be stored in this environment\n"
209 " variable.\n");