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