semihosting.c 4.6 KB

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