semihosting.c 4.6 KB

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