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