blob: 2943f7b82fbae19144d775cd5a7603c637a66819 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Darwin Rambo261d2762014-06-09 11:12:59 -07002/*
3 * Copyright 2014 Broadcom Corporation
Darwin Rambo261d2762014-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 Walleij202a6742015-03-23 11:06:11 +010015#include <command.h>
Simon Glass09140112020-05-10 11:40:03 -060016#include <env.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060017#include <log.h>
Sean Andersonb10f7242022-03-22 16:59:14 -040018#include <semihosting.h>
Darwin Rambo261d2762014-06-09 11:12:59 -070019
20#define SYSOPEN 0x01
21#define SYSCLOSE 0x02
22#define SYSREAD 0x06
23#define SYSFLEN 0x0C
Sean Anderson80e62cc2022-03-22 16:59:16 -040024#define SYSERRNO 0x13
Darwin Rambo261d2762014-06-09 11:12:59 -070025
Darwin Rambo261d2762014-06-09 11:12:59 -070026/*
27 * Call the handler
28 */
Linus Walleije769f682015-03-23 11:06:10 +010029static noinline long smh_trap(unsigned int sysnum, void *addr)
Darwin Rambo261d2762014-06-09 11:12:59 -070030{
Linus Walleij4e1ef152014-12-15 11:05:56 +010031 register long result asm("r0");
Darwin Rambo261d2762014-06-09 11:12:59 -070032#if defined(CONFIG_ARM64)
33 asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
Vadzim Dambrouski432a6242015-10-19 19:40:14 +030034#elif defined(CONFIG_CPU_V7M)
35 asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
Darwin Rambo261d2762014-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 Anderson80e62cc2022-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
Linus Walleij9be5c662014-12-15 11:06:05 +010051 */
Sean Anderson80e62cc2022-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 Andersoneff77c32022-03-22 16:59:15 -040061long smh_open(const char *fname, enum smh_open_mode mode)
Linus Walleij9be5c662014-12-15 11:06:05 +010062{
63 long fd;
Linus Walleij9be5c662014-12-15 11:06:05 +010064 struct smh_open_s {
65 const char *fname;
66 unsigned long mode;
67 size_t len;
68 } open;
69
Sean Andersoneff77c32022-03-22 16:59:15 -040070 debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
Linus Walleij9be5c662014-12-15 11:06:05 +010071
72 open.fname = fname;
73 open.len = strlen(fname);
74 open.mode = mode;
75
76 /* Open the file on the host */
77 fd = smh_trap(SYSOPEN, &open);
78 if (fd == -1)
Sean Anderson80e62cc2022-03-22 16:59:16 -040079 return smh_errno();
Linus Walleij9be5c662014-12-15 11:06:05 +010080 return fd;
81}
82
Sean Andersonb10f7242022-03-22 16:59:14 -040083long smh_read(long fd, void *memp, size_t len)
Linus Walleij9be5c662014-12-15 11:06:05 +010084{
85 long ret;
86 struct smh_read_s {
87 long fd;
88 void *memp;
89 size_t len;
90 } read;
91
Vadzim Dambrouski7bdf75c2015-10-19 19:40:15 +030092 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
Linus Walleij9be5c662014-12-15 11:06:05 +010093
94 read.fd = fd;
95 read.memp = memp;
96 read.len = len;
97
98 ret = smh_trap(SYSREAD, &read);
Sean Anderson80e62cc2022-03-22 16:59:16 -040099 if (ret < 0)
100 return smh_errno();
101 return len - ret;
Linus Walleij9be5c662014-12-15 11:06:05 +0100102}
103
Sean Andersonb10f7242022-03-22 16:59:14 -0400104long smh_close(long fd)
Linus Walleij9be5c662014-12-15 11:06:05 +0100105{
106 long ret;
107
108 debug("%s: fd %ld\n", __func__, fd);
109
110 ret = smh_trap(SYSCLOSE, &fd);
111 if (ret == -1)
Sean Anderson80e62cc2022-03-22 16:59:16 -0400112 return smh_errno();
113 return 0;
Linus Walleij9be5c662014-12-15 11:06:05 +0100114}
115
Sean Andersonb10f7242022-03-22 16:59:14 -0400116long smh_flen(long fd)
Linus Walleij9be5c662014-12-15 11:06:05 +0100117{
118 long ret;
119
120 debug("%s: fd %ld\n", __func__, fd);
121
122 ret = smh_trap(SYSFLEN, &fd);
123 if (ret == -1)
Sean Anderson80e62cc2022-03-22 16:59:16 -0400124 return smh_errno();
Linus Walleij9be5c662014-12-15 11:06:05 +0100125 return ret;
126}
127
Linus Walleij202a6742015-03-23 11:06:11 +0100128static int smh_load_file(const char * const name, ulong load_addr,
129 ulong *end_addr)
130{
131 long fd;
132 long len;
133 long ret;
134
Sean Andersoneff77c32022-03-22 16:59:15 -0400135 fd = smh_open(name, MODE_READ | MODE_BINARY);
Sean Anderson80e62cc2022-03-22 16:59:16 -0400136 if (fd < 0)
137 return fd;
Linus Walleij202a6742015-03-23 11:06:11 +0100138
Sean Andersonb10f7242022-03-22 16:59:14 -0400139 len = smh_flen(fd);
Linus Walleij202a6742015-03-23 11:06:11 +0100140 if (len < 0) {
141 smh_close(fd);
Sean Anderson80e62cc2022-03-22 16:59:16 -0400142 return len;
Linus Walleij202a6742015-03-23 11:06:11 +0100143 }
144
145 ret = smh_read(fd, (void *)load_addr, len);
146 smh_close(fd);
147
Sean Anderson80e62cc2022-03-22 16:59:16 -0400148 if (ret == len) {
Linus Walleij202a6742015-03-23 11:06:11 +0100149 *end_addr = load_addr + len - 1;
150 printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
151 name,
152 load_addr,
153 *end_addr,
154 len);
Sean Anderson80e62cc2022-03-22 16:59:16 -0400155 } else if (ret >= 0) {
156 ret = -EAGAIN;
157 }
158
159 if (ret < 0) {
160 printf("read failed: %ld\n", ret);
161 return ret;
Linus Walleij202a6742015-03-23 11:06:11 +0100162 }
163
164 return 0;
165}
166
Simon Glass09140112020-05-10 11:40:03 -0600167static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
168 char *const argv[])
Linus Walleij202a6742015-03-23 11:06:11 +0100169{
170 if (argc == 3 || argc == 4) {
171 ulong load_addr;
172 ulong end_addr = 0;
Ryan Harkin072c8c42017-03-02 17:45:16 +0000173 int ret;
Linus Walleij202a6742015-03-23 11:06:11 +0100174 char end_str[64];
175
Simon Glass7e5f4602021-07-24 09:03:29 -0600176 load_addr = hextoul(argv[2], NULL);
Linus Walleij202a6742015-03-23 11:06:11 +0100177 if (!load_addr)
178 return -1;
179
180 ret = smh_load_file(argv[1], load_addr, &end_addr);
181 if (ret < 0)
Ryan Harkin072c8c42017-03-02 17:45:16 +0000182 return CMD_RET_FAILURE;
Linus Walleij202a6742015-03-23 11:06:11 +0100183
184 /* Optionally save returned end to the environment */
185 if (argc == 4) {
186 sprintf(end_str, "0x%08lx", end_addr);
Simon Glass382bee52017-08-03 12:22:09 -0600187 env_set(argv[3], end_str);
Linus Walleij202a6742015-03-23 11:06:11 +0100188 }
189 } else {
190 return CMD_RET_USAGE;
191 }
192 return 0;
193}
194
195U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
196 "<file> 0x<address> [end var]\n"
197 " - load a semihosted file to the address specified\n"
198 " if the optional [end var] is specified, the end\n"
199 " address of the file will be stored in this environment\n"
200 " variable.\n");