semihosting.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright 2014 Broadcom Corporation
  4. */
  5. /*
  6. * Minimal semihosting implementation for reading files into memory. If more
  7. * features like writing files or console output are required they can be
  8. * added later. This code has been tested on arm64/aarch64 fastmodel only.
  9. * An untested placeholder exists for armv7 architectures, but since they
  10. * are commonly available in silicon now, fastmodel usage makes less sense
  11. * for them.
  12. */
  13. #include <common.h>
  14. #include <command.h>
  15. #include <env.h>
  16. #define SYSOPEN 0x01
  17. #define SYSCLOSE 0x02
  18. #define SYSREAD 0x06
  19. #define SYSFLEN 0x0C
  20. #define MODE_READ 0x0
  21. #define MODE_READBIN 0x1
  22. /*
  23. * Call the handler
  24. */
  25. static noinline long smh_trap(unsigned int sysnum, void *addr)
  26. {
  27. register long result asm("r0");
  28. #if defined(CONFIG_ARM64)
  29. asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
  30. #elif defined(CONFIG_CPU_V7M)
  31. asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
  32. #else
  33. /* Note - untested placeholder */
  34. asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
  35. #endif
  36. return result;
  37. }
  38. /*
  39. * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
  40. * descriptor or -1 on error.
  41. */
  42. static long smh_open(const char *fname, char *modestr)
  43. {
  44. long fd;
  45. unsigned long mode;
  46. struct smh_open_s {
  47. const char *fname;
  48. unsigned long mode;
  49. size_t len;
  50. } open;
  51. debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
  52. /* Check the file mode */
  53. if (!(strcmp(modestr, "r"))) {
  54. mode = MODE_READ;
  55. } else if (!(strcmp(modestr, "rb"))) {
  56. mode = MODE_READBIN;
  57. } else {
  58. printf("%s: ERROR mode \'%s\' not supported\n", __func__,
  59. modestr);
  60. return -1;
  61. }
  62. open.fname = fname;
  63. open.len = strlen(fname);
  64. open.mode = mode;
  65. /* Open the file on the host */
  66. fd = smh_trap(SYSOPEN, &open);
  67. if (fd == -1)
  68. printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
  69. fname);
  70. return fd;
  71. }
  72. /*
  73. * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
  74. */
  75. static long smh_read(long fd, void *memp, size_t len)
  76. {
  77. long ret;
  78. struct smh_read_s {
  79. long fd;
  80. void *memp;
  81. size_t len;
  82. } read;
  83. debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
  84. read.fd = fd;
  85. read.memp = memp;
  86. read.len = len;
  87. ret = smh_trap(SYSREAD, &read);
  88. if (ret < 0) {
  89. /*
  90. * The ARM handler allows for returning partial lengths,
  91. * but in practice this never happens so rather than create
  92. * hard to maintain partial read loops and such, just fail
  93. * with an error message.
  94. */
  95. printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n",
  96. __func__, ret, fd, len, memp);
  97. return -1;
  98. }
  99. return 0;
  100. }
  101. /*
  102. * Close the file using the file descriptor
  103. */
  104. static long smh_close(long fd)
  105. {
  106. long ret;
  107. debug("%s: fd %ld\n", __func__, fd);
  108. ret = smh_trap(SYSCLOSE, &fd);
  109. if (ret == -1)
  110. printf("%s: ERROR fd %ld\n", __func__, fd);
  111. return ret;
  112. }
  113. /*
  114. * Get the file length from the file descriptor
  115. */
  116. static long smh_len_fd(long fd)
  117. {
  118. long ret;
  119. debug("%s: fd %ld\n", __func__, fd);
  120. ret = smh_trap(SYSFLEN, &fd);
  121. if (ret == -1)
  122. printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
  123. return ret;
  124. }
  125. static int smh_load_file(const char * const name, ulong load_addr,
  126. ulong *end_addr)
  127. {
  128. long fd;
  129. long len;
  130. long ret;
  131. fd = smh_open(name, "rb");
  132. if (fd == -1)
  133. return -1;
  134. len = smh_len_fd(fd);
  135. if (len < 0) {
  136. smh_close(fd);
  137. return -1;
  138. }
  139. ret = smh_read(fd, (void *)load_addr, len);
  140. smh_close(fd);
  141. if (ret == 0) {
  142. *end_addr = load_addr + len - 1;
  143. printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
  144. name,
  145. load_addr,
  146. *end_addr,
  147. len);
  148. } else {
  149. printf("read failed\n");
  150. return 0;
  151. }
  152. return 0;
  153. }
  154. static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
  155. char *const argv[])
  156. {
  157. if (argc == 3 || argc == 4) {
  158. ulong load_addr;
  159. ulong end_addr = 0;
  160. int ret;
  161. char end_str[64];
  162. load_addr = simple_strtoul(argv[2], NULL, 16);
  163. if (!load_addr)
  164. return -1;
  165. ret = smh_load_file(argv[1], load_addr, &end_addr);
  166. if (ret < 0)
  167. return CMD_RET_FAILURE;
  168. /* Optionally save returned end to the environment */
  169. if (argc == 4) {
  170. sprintf(end_str, "0x%08lx", end_addr);
  171. env_set(argv[3], end_str);
  172. }
  173. } else {
  174. return CMD_RET_USAGE;
  175. }
  176. return 0;
  177. }
  178. U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
  179. "<file> 0x<address> [end var]\n"
  180. " - load a semihosted file to the address specified\n"
  181. " if the optional [end var] is specified, the end\n"
  182. " address of the file will be stored in this environment\n"
  183. " variable.\n");