]>
Commit | Line | Data |
---|---|---|
50336067 MC |
1 | /* |
2 | * QEMU RISC-V Host Target Interface (HTIF) Emulation | |
3 | * | |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, [email protected] | |
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" | |
50336067 MC |
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; | |
17b9751e | 43 | static int address_symbol_set; |
50336067 MC |
44 | |
45 | void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, | |
17b9751e | 46 | uint64_t st_size) |
50336067 MC |
47 | { |
48 | if (strcmp("fromhost", st_name) == 0) { | |
17b9751e | 49 | address_symbol_set |= 1; |
50336067 MC |
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) { | |
17b9751e | 56 | address_symbol_set |= 2; |
50336067 MC |
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); | |
17b9751e | 253 | if (address_symbol_set == 3) { |
50336067 | 254 | memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, |
6fad7d18 KF |
255 | TYPE_HTIF_UART, size); |
256 | memory_region_add_subregion_overlap(address_space, base, | |
257 | &s->mmio, 1); | |
50336067 MC |
258 | } |
259 | ||
260 | return s; | |
261 | } |