1// See LICENSE for license details.
2
3#include "htif.h"
4#include "atomic.h"
5#include "mtrap.h"
6#include "fdt.h"
7#include "syscall.h"
8#include <string.h>
9
10extern uint64_t __htif_base;
11volatile uint64_t tohost __attribute__((section(".htif")));
12volatile uint64_t fromhost __attribute__((section(".htif")));
13volatile int htif_console_buf;
14static spinlock_t htif_lock = SPINLOCK_INIT;
15uintptr_t htif;
16
17#define TOHOST(base_int)	(uint64_t *)(base_int + TOHOST_OFFSET)
18#define FROMHOST(base_int)	(uint64_t *)(base_int + FROMHOST_OFFSET)
19
20#define TOHOST_OFFSET		((uintptr_t)tohost - (uintptr_t)__htif_base)
21#define FROMHOST_OFFSET		((uintptr_t)fromhost - (uintptr_t)__htif_base)
22
23static void __check_fromhost()
24{
25  uint64_t fh = fromhost;
26  if (!fh)
27    return;
28  fromhost = 0;
29
30  // this should be from the console
31  assert(FROMHOST_DEV(fh) == 1);
32  switch (FROMHOST_CMD(fh)) {
33    case 0:
34      htif_console_buf = 1 + (uint8_t)FROMHOST_DATA(fh);
35      break;
36    case 1:
37      break;
38    default:
39      assert(0);
40  }
41}
42
43static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
44{
45  while (tohost)
46    __check_fromhost();
47  tohost = TOHOST_CMD(dev, cmd, data);
48}
49
50int htif_console_getchar()
51{
52#if __riscv_xlen == 32
53  // HTIF devices are not supported on RV32
54  return -1;
55#endif
56
57  spinlock_lock(&htif_lock);
58    __check_fromhost();
59    int ch = htif_console_buf;
60    if (ch >= 0) {
61      htif_console_buf = -1;
62      __set_tohost(1, 0, 0);
63    }
64  spinlock_unlock(&htif_lock);
65
66  return ch - 1;
67}
68
69static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
70{
71  spinlock_lock(&htif_lock);
72    __set_tohost(dev, cmd, data);
73
74    while (1) {
75      uint64_t fh = fromhost;
76      if (fh) {
77        if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
78          fromhost = 0;
79          break;
80        }
81        __check_fromhost();
82      }
83    }
84  spinlock_unlock(&htif_lock);
85}
86
87void htif_syscall(uintptr_t arg)
88{
89  do_tohost_fromhost(0, 0, arg);
90}
91
92void htif_console_putchar(uint8_t ch)
93{
94#if __riscv_xlen == 32
95  // HTIF devices are not supported on RV32, so proxy a write system call
96  volatile uint64_t magic_mem[8];
97  magic_mem[0] = SYS_write;
98  magic_mem[1] = 1;
99  magic_mem[2] = (uintptr_t)&ch;
100  magic_mem[3] = 1;
101  do_tohost_fromhost(0, 0, (uintptr_t)magic_mem);
102#else
103  spinlock_lock(&htif_lock);
104    __set_tohost(1, 1, ch);
105  spinlock_unlock(&htif_lock);
106#endif
107}
108
109void htif_poweroff()
110{
111  while (1) {
112    fromhost = 0;
113    tohost = 1;
114  }
115}
116
117struct htif_scan
118{
119  int compat;
120};
121
122static void htif_open(const struct fdt_scan_node *node, void *extra)
123{
124  struct htif_scan *scan = (struct htif_scan *)extra;
125  memset(scan, 0, sizeof(*scan));
126}
127
128static void htif_prop(const struct fdt_scan_prop *prop, void *extra)
129{
130  struct htif_scan *scan = (struct htif_scan *)extra;
131  if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "ucb,htif0")) {
132    scan->compat = 1;
133  }
134}
135
136static void htif_done(const struct fdt_scan_node *node, void *extra)
137{
138  struct htif_scan *scan = (struct htif_scan *)extra;
139  if (!scan->compat) return;
140
141  htif = 1;
142}
143
144void query_htif(uintptr_t fdt)
145{
146  struct fdt_cb cb;
147  struct htif_scan scan;
148
149  memset(&cb, 0, sizeof(cb));
150  cb.open = htif_open;
151  cb.prop = htif_prop;
152  cb.done = htif_done;
153  cb.extra = &scan;
154
155  fdt_scan(fdt, &cb);
156}
157