1 | /* |
2 | * QEMU RISC-V Host Target Interface (HTIF) Emulation |
3 | * |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu |
5 | * Copyright (c) 2017-2018 SiFive, Inc. |
6 | * |
7 | * This provides HTIF device emulation for QEMU. At the moment this allows |
8 | * for identical copies of bbl/linux to run on both spike and QEMU. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify it |
11 | * under the terms and conditions of the GNU General Public License, |
12 | * version 2 or later, as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope it will be useful, but WITHOUT |
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
17 | * more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License along with |
20 | * this program. If not, see <http://www.gnu.org/licenses/>. |
21 | */ |
22 | |
23 | #include "qemu/osdep.h" |
24 | #include "qapi/error.h" |
25 | #include "qemu/log.h" |
26 | #include "hw/sysbus.h" |
27 | #include "hw/char/serial.h" |
28 | #include "chardev/char.h" |
29 | #include "chardev/char-fe.h" |
30 | #include "hw/riscv/riscv_htif.h" |
31 | #include "qemu/timer.h" |
32 | #include "qemu/error-report.h" |
33 | |
34 | #define RISCV_DEBUG_HTIF 0 |
35 | #define HTIF_DEBUG(fmt, ...) \ |
36 | do { \ |
37 | if (RISCV_DEBUG_HTIF) { \ |
38 | qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ |
39 | } \ |
40 | } while (0) |
41 | |
42 | static uint64_t fromhost_addr, tohost_addr; |
43 | static int address_symbol_set; |
44 | |
45 | void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, |
46 | uint64_t st_size) |
47 | { |
48 | if (strcmp("fromhost" , st_name) == 0) { |
49 | address_symbol_set |= 1; |
50 | fromhost_addr = st_value; |
51 | if (st_size != 8) { |
52 | error_report("HTIF fromhost must be 8 bytes" ); |
53 | exit(1); |
54 | } |
55 | } else if (strcmp("tohost" , st_name) == 0) { |
56 | address_symbol_set |= 2; |
57 | tohost_addr = st_value; |
58 | if (st_size != 8) { |
59 | error_report("HTIF tohost must be 8 bytes" ); |
60 | exit(1); |
61 | } |
62 | } |
63 | } |
64 | |
65 | /* |
66 | * Called by the char dev to see if HTIF is ready to accept input. |
67 | */ |
68 | static int htif_can_recv(void *opaque) |
69 | { |
70 | return 1; |
71 | } |
72 | |
73 | /* |
74 | * Called by the char dev to supply input to HTIF console. |
75 | * We assume that we will receive one character at a time. |
76 | */ |
77 | static void htif_recv(void *opaque, const uint8_t *buf, int size) |
78 | { |
79 | HTIFState *htifstate = opaque; |
80 | |
81 | if (size != 1) { |
82 | return; |
83 | } |
84 | |
85 | /* TODO - we need to check whether mfromhost is zero which indicates |
86 | the device is ready to receive. The current implementation |
87 | will drop characters */ |
88 | |
89 | uint64_t val_written = htifstate->pending_read; |
90 | uint64_t resp = 0x100 | *buf; |
91 | |
92 | htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); |
93 | } |
94 | |
95 | /* |
96 | * Called by the char dev to supply special events to the HTIF console. |
97 | * Not used for HTIF. |
98 | */ |
99 | static void htif_event(void *opaque, int event) |
100 | { |
101 | |
102 | } |
103 | |
104 | static int htif_be_change(void *opaque) |
105 | { |
106 | HTIFState *s = opaque; |
107 | |
108 | qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, |
109 | htif_be_change, s, NULL, true); |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) |
115 | { |
116 | uint8_t device = val_written >> 56; |
117 | uint8_t cmd = val_written >> 48; |
118 | uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; |
119 | int resp = 0; |
120 | |
121 | HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 |
122 | " -payload: %016" PRIx64 "\n" , device, cmd, payload & 0xFF, payload); |
123 | |
124 | /* |
125 | * Currently, there is a fixed mapping of devices: |
126 | * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) |
127 | * 1: Console |
128 | */ |
129 | if (unlikely(device == 0x0)) { |
130 | /* frontend syscall handler, shutdown and exit code support */ |
131 | if (cmd == 0x0) { |
132 | if (payload & 0x1) { |
133 | /* exit code */ |
134 | int exit_code = payload >> 1; |
135 | exit(exit_code); |
136 | } else { |
137 | qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n" ); |
138 | } |
139 | } else { |
140 | qemu_log("HTIF device %d: unknown command\n" , device); |
141 | } |
142 | } else if (likely(device == 0x1)) { |
143 | /* HTIF Console */ |
144 | if (cmd == 0x0) { |
145 | /* this should be a queue, but not yet implemented as such */ |
146 | htifstate->pending_read = val_written; |
147 | htifstate->env->mtohost = 0; /* clear to indicate we read */ |
148 | return; |
149 | } else if (cmd == 0x1) { |
150 | qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1); |
151 | resp = 0x100 | (uint8_t)payload; |
152 | } else { |
153 | qemu_log("HTIF device %d: unknown command\n" , device); |
154 | } |
155 | } else { |
156 | qemu_log("HTIF unknown device or command\n" ); |
157 | HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 |
158 | " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); |
159 | } |
160 | /* |
161 | * - latest bbl does not set fromhost to 0 if there is a value in tohost |
162 | * - with this code enabled, qemu hangs waiting for fromhost to go to 0 |
163 | * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10 |
164 | * - HTIF needs protocol documentation and a more complete state machine |
165 | |
166 | while (!htifstate->fromhost_inprogress && |
167 | htifstate->env->mfromhost != 0x0) { |
168 | } |
169 | */ |
170 | htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); |
171 | htifstate->env->mtohost = 0; /* clear to indicate we read */ |
172 | } |
173 | |
174 | #define TOHOST_OFFSET1 (htifstate->tohost_offset) |
175 | #define TOHOST_OFFSET2 (htifstate->tohost_offset + 4) |
176 | #define FROMHOST_OFFSET1 (htifstate->fromhost_offset) |
177 | #define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4) |
178 | |
179 | /* CPU wants to read an HTIF register */ |
180 | static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) |
181 | { |
182 | HTIFState *htifstate = opaque; |
183 | if (addr == TOHOST_OFFSET1) { |
184 | return htifstate->env->mtohost & 0xFFFFFFFF; |
185 | } else if (addr == TOHOST_OFFSET2) { |
186 | return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF; |
187 | } else if (addr == FROMHOST_OFFSET1) { |
188 | return htifstate->env->mfromhost & 0xFFFFFFFF; |
189 | } else if (addr == FROMHOST_OFFSET2) { |
190 | return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF; |
191 | } else { |
192 | qemu_log("Invalid htif read: address %016" PRIx64 "\n" , |
193 | (uint64_t)addr); |
194 | return 0; |
195 | } |
196 | } |
197 | |
198 | /* CPU wrote to an HTIF register */ |
199 | static void htif_mm_write(void *opaque, hwaddr addr, |
200 | uint64_t value, unsigned size) |
201 | { |
202 | HTIFState *htifstate = opaque; |
203 | if (addr == TOHOST_OFFSET1) { |
204 | if (htifstate->env->mtohost == 0x0) { |
205 | htifstate->allow_tohost = 1; |
206 | htifstate->env->mtohost = value & 0xFFFFFFFF; |
207 | } else { |
208 | htifstate->allow_tohost = 0; |
209 | } |
210 | } else if (addr == TOHOST_OFFSET2) { |
211 | if (htifstate->allow_tohost) { |
212 | htifstate->env->mtohost |= value << 32; |
213 | htif_handle_tohost_write(htifstate, htifstate->env->mtohost); |
214 | } |
215 | } else if (addr == FROMHOST_OFFSET1) { |
216 | htifstate->fromhost_inprogress = 1; |
217 | htifstate->env->mfromhost = value & 0xFFFFFFFF; |
218 | } else if (addr == FROMHOST_OFFSET2) { |
219 | htifstate->env->mfromhost |= value << 32; |
220 | htifstate->fromhost_inprogress = 0; |
221 | } else { |
222 | qemu_log("Invalid htif write: address %016" PRIx64 "\n" , |
223 | (uint64_t)addr); |
224 | } |
225 | } |
226 | |
227 | static const MemoryRegionOps htif_mm_ops = { |
228 | .read = htif_mm_read, |
229 | .write = htif_mm_write, |
230 | }; |
231 | |
232 | HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, |
233 | CPURISCVState *env, Chardev *chr) |
234 | { |
235 | uint64_t base = MIN(tohost_addr, fromhost_addr); |
236 | uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base; |
237 | uint64_t tohost_offset = tohost_addr - base; |
238 | uint64_t fromhost_offset = fromhost_addr - base; |
239 | |
240 | HTIFState *s = g_malloc0(sizeof(HTIFState)); |
241 | s->address_space = address_space; |
242 | s->main_mem = main_mem; |
243 | s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); |
244 | s->env = env; |
245 | s->tohost_offset = tohost_offset; |
246 | s->fromhost_offset = fromhost_offset; |
247 | s->pending_read = 0; |
248 | s->allow_tohost = 0; |
249 | s->fromhost_inprogress = 0; |
250 | qemu_chr_fe_init(&s->chr, chr, &error_abort); |
251 | qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, |
252 | htif_be_change, s, NULL, true); |
253 | if (address_symbol_set == 3) { |
254 | memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, |
255 | TYPE_HTIF_UART, size); |
256 | memory_region_add_subregion_overlap(address_space, base, |
257 | &s->mmio, 1); |
258 | } |
259 | |
260 | return s; |
261 | } |
262 | |