1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2014 Broadcom Corporation 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> 15 #include <command.h> 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 */ 28 static noinline long smh_trap(unsigned int sysnum, void *addr) 29 { 30 register long result asm("r0"); 31 #if defined(CONFIG_ARM64) 32 asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr)); 33 #elif defined(CONFIG_CPU_V7M) 34 asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr)); 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 /* 43 * Open a file on the host. Mode is "r" or "rb" currently. Returns a file 44 * descriptor or -1 on error. 45 */ 46 static long smh_open(const char *fname, char *modestr) 47 { 48 long fd; 49 unsigned long mode; 50 struct smh_open_s { 51 const char *fname; 52 unsigned long mode; 53 size_t len; 54 } open; 55 56 debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr); 57 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); 66 return -1; 67 } 68 69 open.fname = fname; 70 open.len = strlen(fname); 71 open.mode = mode; 72 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); 78 79 return fd; 80 } 81 82 /* 83 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure 84 */ 85 static long smh_read(long fd, void *memp, size_t len) 86 { 87 long ret; 88 struct smh_read_s { 89 long fd; 90 void *memp; 91 size_t len; 92 } read; 93 94 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); 95 96 read.fd = fd; 97 read.memp = memp; 98 read.len = len; 99 100 ret = smh_trap(SYSREAD, &read); 101 if (ret < 0) { 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 */ 108 printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n", 109 __func__, ret, fd, len, memp); 110 return -1; 111 } 112 113 return 0; 114 } 115 116 /* 117 * Close the file using the file descriptor 118 */ 119 static long smh_close(long fd) 120 { 121 long ret; 122 123 debug("%s: fd %ld\n", __func__, fd); 124 125 ret = smh_trap(SYSCLOSE, &fd); 126 if (ret == -1) 127 printf("%s: ERROR fd %ld\n", __func__, fd); 128 129 return ret; 130 } 131 132 /* 133 * Get the file length from the file descriptor 134 */ 135 static long smh_len_fd(long fd) 136 { 137 long ret; 138 139 debug("%s: fd %ld\n", __func__, fd); 140 141 ret = smh_trap(SYSFLEN, &fd); 142 if (ret == -1) 143 printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd); 144 145 return ret; 146 } 147 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; 188 int ret; 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) 197 return CMD_RET_FAILURE; 198 199 /* Optionally save returned end to the environment */ 200 if (argc == 4) { 201 sprintf(end_str, "0x%08lx", end_addr); 202 env_set(argv[3], end_str); 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"); 216