Home | History | Annotate | Download | only in lib
      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