semihosting.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2022 Ventana Micro Systems Inc.
  5. *
  6. * Authors:
  7. * Anup Patel <apatel@ventanamicro.com>
  8. * Kautuk Consul <kconsul@ventanamicro.com>
  9. */
  10. #include <sbi/sbi_console.h>
  11. #include <sbi/sbi_string.h>
  12. #include <sbi/sbi_error.h>
  13. #include <sbi_utils/serial/semihosting.h>
  14. #define SYSOPEN 0x01
  15. #define SYSWRITEC 0x03
  16. #define SYSWRITE 0x05
  17. #define SYSREAD 0x06
  18. #define SYSREADC 0x07
  19. #define SYSERRNO 0x13
  20. static long semihosting_trap(int sysnum, void *addr)
  21. {
  22. register int ret asm ("a0") = sysnum;
  23. register void *param0 asm ("a1") = addr;
  24. asm volatile (
  25. " .align 4\n"
  26. " .option push\n"
  27. " .option norvc\n"
  28. " slli zero, zero, 0x1f\n"
  29. " ebreak\n"
  30. " srai zero, zero, 7\n"
  31. " .option pop\n"
  32. : "+r" (ret) : "r" (param0) : "memory");
  33. return ret;
  34. }
  35. static bool _semihosting_enabled = true;
  36. static bool try_semihosting = true;
  37. bool semihosting_enabled(void)
  38. {
  39. register int ret asm ("a0") = SYSERRNO;
  40. register void *param0 asm ("a1") = NULL;
  41. unsigned long tmp = 0;
  42. if (!try_semihosting)
  43. return _semihosting_enabled;
  44. asm volatile (
  45. " .align 4\n"
  46. " .option push\n"
  47. " .option norvc\n"
  48. " j _semihost_test_vector_next\n"
  49. " .align 4\n"
  50. "_semihost_test_vector:\n"
  51. " csrr %[en], mepc\n"
  52. " addi %[en], %[en], 4\n"
  53. " csrw mepc, %[en]\n"
  54. " add %[en], zero, zero\n"
  55. " mret\n"
  56. "_semihost_test_vector_next:\n"
  57. " la %[tmp], _semihost_test_vector\n"
  58. " csrrw %[tmp], mtvec, %[tmp]\n"
  59. " .align 4\n"
  60. " slli zero, zero, 0x1f\n"
  61. " ebreak\n"
  62. " srai zero, zero, 7\n"
  63. " csrw mtvec, %[tmp]\n"
  64. " .option pop\n"
  65. : [tmp] "+r" (tmp), [en] "+r" (_semihosting_enabled),
  66. [ret] "+r" (ret)
  67. : "r" (param0) : "memory");
  68. try_semihosting = false;
  69. return _semihosting_enabled;
  70. }
  71. static int semihosting_errno(void)
  72. {
  73. long ret = semihosting_trap(SYSERRNO, NULL);
  74. if (ret > 0)
  75. return -ret;
  76. return SBI_EIO;
  77. }
  78. static int semihosting_infd = SBI_ENODEV;
  79. static int semihosting_outfd = SBI_ENODEV;
  80. static long semihosting_open(const char *fname, enum semihosting_open_mode mode)
  81. {
  82. long fd;
  83. struct semihosting_open_s {
  84. const char *fname;
  85. unsigned long mode;
  86. size_t len;
  87. } open;
  88. open.fname = fname;
  89. open.len = sbi_strlen(fname);
  90. open.mode = mode;
  91. /* Open the file on the host */
  92. fd = semihosting_trap(SYSOPEN, &open);
  93. if (fd == -1)
  94. return semihosting_errno();
  95. return fd;
  96. }
  97. /**
  98. * struct semihosting_rdwr_s - Arguments for read and write
  99. * @fd: A file descriptor returned from semihosting_open()
  100. * @memp: Pointer to a buffer of memory of at least @len bytes
  101. * @len: The number of bytes to read or write
  102. */
  103. struct semihosting_rdwr_s {
  104. long fd;
  105. void *memp;
  106. size_t len;
  107. };
  108. static long semihosting_read(long fd, void *memp, size_t len)
  109. {
  110. long ret;
  111. struct semihosting_rdwr_s read;
  112. read.fd = fd;
  113. read.memp = memp;
  114. read.len = len;
  115. ret = semihosting_trap(SYSREAD, &read);
  116. if (ret < 0)
  117. return semihosting_errno();
  118. return len - ret;
  119. }
  120. static long semihosting_write(long fd, const void *memp, size_t len)
  121. {
  122. long ret;
  123. struct semihosting_rdwr_s write;
  124. write.fd = fd;
  125. write.memp = (void *)memp;
  126. write.len = len;
  127. ret = semihosting_trap(SYSWRITE, &write);
  128. if (ret < 0)
  129. return semihosting_errno();
  130. return len - ret;
  131. }
  132. /* clang-format on */
  133. static void semihosting_putc(char ch)
  134. {
  135. semihosting_trap(SYSWRITEC, &ch);
  136. }
  137. static unsigned long semihosting_puts(const char *str, unsigned long len)
  138. {
  139. char ch;
  140. long ret;
  141. unsigned long i;
  142. if (semihosting_outfd < 0) {
  143. for (i = 0; i < len; i++) {
  144. ch = str[i];
  145. semihosting_trap(SYSWRITEC, &ch);
  146. }
  147. ret = len;
  148. } else
  149. ret = semihosting_write(semihosting_outfd, str, len);
  150. return (ret < 0) ? 0 : ret;
  151. }
  152. static int semihosting_getc(void)
  153. {
  154. char ch = 0;
  155. int ret;
  156. if (semihosting_infd < 0) {
  157. ret = semihosting_trap(SYSREADC, NULL);
  158. ret = ret < 0 ? -1 : ret;
  159. } else
  160. ret = semihosting_read(semihosting_infd, &ch, 1) > 0 ? ch : -1;
  161. return ret;
  162. }
  163. static struct sbi_console_device semihosting_console = {
  164. .name = "semihosting",
  165. .console_putc = semihosting_putc,
  166. .console_puts = semihosting_puts,
  167. .console_getc = semihosting_getc
  168. };
  169. int semihosting_init(void)
  170. {
  171. semihosting_infd = semihosting_open(":tt", MODE_READ);
  172. semihosting_outfd = semihosting_open(":tt", MODE_WRITE);
  173. sbi_console_set_device(&semihosting_console);
  174. return 0;
  175. }