1261d2760SDarwin Rambo /* 2261d2760SDarwin Rambo * Copyright 2014 Broadcom Corporation 3261d2760SDarwin Rambo * 4261d2760SDarwin Rambo * SPDX-License-Identifier: GPL-2.0+ 5261d2760SDarwin Rambo */ 6261d2760SDarwin Rambo 7261d2760SDarwin Rambo /* 8261d2760SDarwin Rambo * Minimal semihosting implementation for reading files into memory. If more 9261d2760SDarwin Rambo * features like writing files or console output are required they can be 10261d2760SDarwin Rambo * added later. This code has been tested on arm64/aarch64 fastmodel only. 11261d2760SDarwin Rambo * An untested placeholder exists for armv7 architectures, but since they 12261d2760SDarwin Rambo * are commonly available in silicon now, fastmodel usage makes less sense 13261d2760SDarwin Rambo * for them. 14261d2760SDarwin Rambo */ 15261d2760SDarwin Rambo #include <common.h> 16202a674bSLinus Walleij #include <command.h> 17261d2760SDarwin Rambo 18261d2760SDarwin Rambo #define SYSOPEN 0x01 19261d2760SDarwin Rambo #define SYSCLOSE 0x02 20261d2760SDarwin Rambo #define SYSREAD 0x06 21261d2760SDarwin Rambo #define SYSFLEN 0x0C 22261d2760SDarwin Rambo 23261d2760SDarwin Rambo #define MODE_READ 0x0 24261d2760SDarwin Rambo #define MODE_READBIN 0x1 25261d2760SDarwin Rambo 26261d2760SDarwin Rambo /* 27261d2760SDarwin Rambo * Call the handler 28261d2760SDarwin Rambo */ 29e769f686SLinus Walleij static noinline long smh_trap(unsigned int sysnum, void *addr) 30261d2760SDarwin Rambo { 314e1ef150SLinus Walleij register long result asm("r0"); 32261d2760SDarwin Rambo #if defined(CONFIG_ARM64) 33261d2760SDarwin Rambo asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr)); 34432a6241SVadzim Dambrouski #elif defined(CONFIG_CPU_V7M) 35432a6241SVadzim Dambrouski asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr)); 36261d2760SDarwin Rambo #else 37261d2760SDarwin Rambo /* Note - untested placeholder */ 38261d2760SDarwin Rambo asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr)); 39261d2760SDarwin Rambo #endif 40261d2760SDarwin Rambo return result; 41261d2760SDarwin Rambo } 42261d2760SDarwin Rambo 43261d2760SDarwin Rambo /* 449be5c661SLinus Walleij * Open a file on the host. Mode is "r" or "rb" currently. Returns a file 459be5c661SLinus Walleij * descriptor or -1 on error. 469be5c661SLinus Walleij */ 479be5c661SLinus Walleij static long smh_open(const char *fname, char *modestr) 489be5c661SLinus Walleij { 499be5c661SLinus Walleij long fd; 509be5c661SLinus Walleij unsigned long mode; 519be5c661SLinus Walleij struct smh_open_s { 529be5c661SLinus Walleij const char *fname; 539be5c661SLinus Walleij unsigned long mode; 549be5c661SLinus Walleij size_t len; 559be5c661SLinus Walleij } open; 569be5c661SLinus Walleij 579be5c661SLinus Walleij debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr); 589be5c661SLinus Walleij 599be5c661SLinus Walleij /* Check the file mode */ 609be5c661SLinus Walleij if (!(strcmp(modestr, "r"))) { 619be5c661SLinus Walleij mode = MODE_READ; 629be5c661SLinus Walleij } else if (!(strcmp(modestr, "rb"))) { 639be5c661SLinus Walleij mode = MODE_READBIN; 649be5c661SLinus Walleij } else { 659be5c661SLinus Walleij printf("%s: ERROR mode \'%s\' not supported\n", __func__, 669be5c661SLinus Walleij modestr); 679be5c661SLinus Walleij return -1; 689be5c661SLinus Walleij } 699be5c661SLinus Walleij 709be5c661SLinus Walleij open.fname = fname; 719be5c661SLinus Walleij open.len = strlen(fname); 729be5c661SLinus Walleij open.mode = mode; 739be5c661SLinus Walleij 749be5c661SLinus Walleij /* Open the file on the host */ 759be5c661SLinus Walleij fd = smh_trap(SYSOPEN, &open); 769be5c661SLinus Walleij if (fd == -1) 779be5c661SLinus Walleij printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd, 789be5c661SLinus Walleij fname); 799be5c661SLinus Walleij 809be5c661SLinus Walleij return fd; 819be5c661SLinus Walleij } 829be5c661SLinus Walleij 839be5c661SLinus Walleij /* 849be5c661SLinus Walleij * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure 859be5c661SLinus Walleij */ 869be5c661SLinus Walleij static long smh_read(long fd, void *memp, size_t len) 879be5c661SLinus Walleij { 889be5c661SLinus Walleij long ret; 899be5c661SLinus Walleij struct smh_read_s { 909be5c661SLinus Walleij long fd; 919be5c661SLinus Walleij void *memp; 929be5c661SLinus Walleij size_t len; 939be5c661SLinus Walleij } read; 949be5c661SLinus Walleij 957bdf75caSVadzim Dambrouski debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); 969be5c661SLinus Walleij 979be5c661SLinus Walleij read.fd = fd; 989be5c661SLinus Walleij read.memp = memp; 999be5c661SLinus Walleij read.len = len; 1009be5c661SLinus Walleij 1019be5c661SLinus Walleij ret = smh_trap(SYSREAD, &read); 1029be5c661SLinus Walleij if (ret < 0) { 1039be5c661SLinus Walleij /* 1049be5c661SLinus Walleij * The ARM handler allows for returning partial lengths, 1059be5c661SLinus Walleij * but in practice this never happens so rather than create 1069be5c661SLinus Walleij * hard to maintain partial read loops and such, just fail 1079be5c661SLinus Walleij * with an error message. 1089be5c661SLinus Walleij */ 1097bdf75caSVadzim Dambrouski printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n", 1109be5c661SLinus Walleij __func__, ret, fd, len, memp); 1119be5c661SLinus Walleij return -1; 1129be5c661SLinus Walleij } 1139be5c661SLinus Walleij 1149be5c661SLinus Walleij return 0; 1159be5c661SLinus Walleij } 1169be5c661SLinus Walleij 1179be5c661SLinus Walleij /* 1189be5c661SLinus Walleij * Close the file using the file descriptor 1199be5c661SLinus Walleij */ 1209be5c661SLinus Walleij static long smh_close(long fd) 1219be5c661SLinus Walleij { 1229be5c661SLinus Walleij long ret; 1239be5c661SLinus Walleij 1249be5c661SLinus Walleij debug("%s: fd %ld\n", __func__, fd); 1259be5c661SLinus Walleij 1269be5c661SLinus Walleij ret = smh_trap(SYSCLOSE, &fd); 1279be5c661SLinus Walleij if (ret == -1) 1289be5c661SLinus Walleij printf("%s: ERROR fd %ld\n", __func__, fd); 1299be5c661SLinus Walleij 1309be5c661SLinus Walleij return ret; 1319be5c661SLinus Walleij } 1329be5c661SLinus Walleij 1339be5c661SLinus Walleij /* 1349be5c661SLinus Walleij * Get the file length from the file descriptor 1359be5c661SLinus Walleij */ 1369be5c661SLinus Walleij static long smh_len_fd(long fd) 1379be5c661SLinus Walleij { 1389be5c661SLinus Walleij long ret; 1399be5c661SLinus Walleij 1409be5c661SLinus Walleij debug("%s: fd %ld\n", __func__, fd); 1419be5c661SLinus Walleij 1429be5c661SLinus Walleij ret = smh_trap(SYSFLEN, &fd); 1439be5c661SLinus Walleij if (ret == -1) 1449be5c661SLinus Walleij printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd); 1459be5c661SLinus Walleij 1469be5c661SLinus Walleij return ret; 1479be5c661SLinus Walleij } 1489be5c661SLinus Walleij 149202a674bSLinus Walleij static int smh_load_file(const char * const name, ulong load_addr, 150202a674bSLinus Walleij ulong *end_addr) 151202a674bSLinus Walleij { 152202a674bSLinus Walleij long fd; 153202a674bSLinus Walleij long len; 154202a674bSLinus Walleij long ret; 155202a674bSLinus Walleij 156202a674bSLinus Walleij fd = smh_open(name, "rb"); 157202a674bSLinus Walleij if (fd == -1) 158202a674bSLinus Walleij return -1; 159202a674bSLinus Walleij 160202a674bSLinus Walleij len = smh_len_fd(fd); 161202a674bSLinus Walleij if (len < 0) { 162202a674bSLinus Walleij smh_close(fd); 163202a674bSLinus Walleij return -1; 164202a674bSLinus Walleij } 165202a674bSLinus Walleij 166202a674bSLinus Walleij ret = smh_read(fd, (void *)load_addr, len); 167202a674bSLinus Walleij smh_close(fd); 168202a674bSLinus Walleij 169202a674bSLinus Walleij if (ret == 0) { 170202a674bSLinus Walleij *end_addr = load_addr + len - 1; 171202a674bSLinus Walleij printf("loaded file %s from %08lX to %08lX, %08lX bytes\n", 172202a674bSLinus Walleij name, 173202a674bSLinus Walleij load_addr, 174202a674bSLinus Walleij *end_addr, 175202a674bSLinus Walleij len); 176202a674bSLinus Walleij } else { 177202a674bSLinus Walleij printf("read failed\n"); 178202a674bSLinus Walleij return 0; 179202a674bSLinus Walleij } 180202a674bSLinus Walleij 181202a674bSLinus Walleij return 0; 182202a674bSLinus Walleij } 183202a674bSLinus Walleij 184202a674bSLinus Walleij static int do_smhload(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 185202a674bSLinus Walleij { 186202a674bSLinus Walleij if (argc == 3 || argc == 4) { 187202a674bSLinus Walleij ulong load_addr; 188202a674bSLinus Walleij ulong end_addr = 0; 189*072c8c4cSRyan Harkin int ret; 190202a674bSLinus Walleij char end_str[64]; 191202a674bSLinus Walleij 192202a674bSLinus Walleij load_addr = simple_strtoul(argv[2], NULL, 16); 193202a674bSLinus Walleij if (!load_addr) 194202a674bSLinus Walleij return -1; 195202a674bSLinus Walleij 196202a674bSLinus Walleij ret = smh_load_file(argv[1], load_addr, &end_addr); 197202a674bSLinus Walleij if (ret < 0) 198*072c8c4cSRyan Harkin return CMD_RET_FAILURE; 199202a674bSLinus Walleij 200202a674bSLinus Walleij /* Optionally save returned end to the environment */ 201202a674bSLinus Walleij if (argc == 4) { 202202a674bSLinus Walleij sprintf(end_str, "0x%08lx", end_addr); 203202a674bSLinus Walleij setenv(argv[3], end_str); 204202a674bSLinus Walleij } 205202a674bSLinus Walleij } else { 206202a674bSLinus Walleij return CMD_RET_USAGE; 207202a674bSLinus Walleij } 208202a674bSLinus Walleij return 0; 209202a674bSLinus Walleij } 210202a674bSLinus Walleij 211202a674bSLinus Walleij U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting", 212202a674bSLinus Walleij "<file> 0x<address> [end var]\n" 213202a674bSLinus Walleij " - load a semihosted file to the address specified\n" 214202a674bSLinus Walleij " if the optional [end var] is specified, the end\n" 215202a674bSLinus Walleij " address of the file will be stored in this environment\n" 216202a674bSLinus Walleij " variable.\n"); 217