1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
31 * but with a request/response messaging protocol.
32 */
33
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/errno.h>
37 #include <sys/uio.h>
38
39 #include <assert.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "bhyverun.h"
45 #include "inout.h"
46 #include "fwctl.h"
47
48 /*
49 * Messaging protocol base operations
50 */
51 #define OP_NULL 1
52 #define OP_ECHO 2
53 #define OP_GET 3
54 #define OP_GET_LEN 4
55 #define OP_SET 5
56 #define OP_MAX OP_SET
57
58 /* I/O ports */
59 #define FWCTL_OUT 0x510
60 #define FWCTL_IN 0x511
61
62 /*
63 * Back-end state-machine
64 */
65 static enum state {
66 IDENT,
67 REQ,
68 RESP
69 } be_state;
70
71 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
72 static u_int ident_idx;
73
74 struct op_info {
75 int op;
76 int (*op_start)(uint32_t len);
77 void (*op_data)(uint32_t data);
78 int (*op_result)(struct iovec **data);
79 void (*op_done)(struct iovec *data);
80 };
81 static struct op_info *ops[OP_MAX+1];
82
83 /* Return 0-padded uint32_t */
84 static uint32_t
fwctl_send_rest(uint8_t * data,size_t len)85 fwctl_send_rest(uint8_t *data, size_t len)
86 {
87 union {
88 uint8_t c[4];
89 uint32_t w;
90 } u;
91 size_t i;
92
93 u.w = 0;
94 for (i = 0; i < len; i++)
95 u.c[i] = *data++;
96
97 return (u.w);
98 }
99
100 /*
101 * error op dummy proto - drop all data sent and return an error
102 */
103 static int errop_code;
104
105 static void
errop_set(int err)106 errop_set(int err)
107 {
108
109 errop_code = err;
110 }
111
112 static int
errop_start(uint32_t len __unused)113 errop_start(uint32_t len __unused)
114 {
115 errop_code = ENOENT;
116
117 /* accept any length */
118 return (errop_code);
119 }
120
121 static void
errop_data(uint32_t data __unused)122 errop_data(uint32_t data __unused)
123 {
124
125 /* ignore */
126 }
127
128 static int
errop_result(struct iovec ** data)129 errop_result(struct iovec **data)
130 {
131
132 /* no data to send back; always successful */
133 *data = NULL;
134 return (errop_code);
135 }
136
137 static void
errop_done(struct iovec * data __unused)138 errop_done(struct iovec *data __unused)
139 {
140
141 /* assert data is NULL */
142 }
143
144 static struct op_info errop_info = {
145 .op_start = errop_start,
146 .op_data = errop_data,
147 .op_result = errop_result,
148 .op_done = errop_done
149 };
150
151 /* OID search */
152 SET_DECLARE(ctl_set, struct ctl);
153
154 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
155
156 static struct ctl *
ctl_locate(const char * str,int maxlen)157 ctl_locate(const char *str, int maxlen)
158 {
159 struct ctl *cp, **cpp;
160
161 SET_FOREACH(cpp, ctl_set) {
162 cp = *cpp;
163 if (!strncmp(str, cp->c_oid, maxlen))
164 return (cp);
165 }
166
167 return (NULL);
168 }
169
170 /* uefi-sysctl get-len */
171 #define FGET_STRSZ 80
172 static struct iovec fget_biov[2];
173 static char fget_str[FGET_STRSZ];
174 static struct {
175 size_t f_sz;
176 uint32_t f_data[1024];
177 } fget_buf;
178 static int fget_cnt;
179 static size_t fget_size;
180
181 static int
fget_start(uint32_t len)182 fget_start(uint32_t len)
183 {
184
185 if (len > FGET_STRSZ)
186 return(E2BIG);
187
188 fget_cnt = 0;
189
190 return (0);
191 }
192
193 static void
fget_data(uint32_t data)194 fget_data(uint32_t data)
195 {
196
197 assert(fget_cnt + sizeof(uint32_t) <= sizeof(fget_str));
198 memcpy(&fget_str[fget_cnt], &data, sizeof(data));
199 fget_cnt += sizeof(uint32_t);
200 }
201
202 static int
fget_result(struct iovec ** data,int val)203 fget_result(struct iovec **data, int val)
204 {
205 struct ctl *cp;
206 int err;
207
208 err = 0;
209
210 /* Locate the OID */
211 cp = ctl_locate(fget_str, fget_cnt);
212 if (cp == NULL) {
213 *data = NULL;
214 err = ENOENT;
215 } else {
216 if (val) {
217 /* For now, copy the len/data into a buffer */
218 memset(&fget_buf, 0, sizeof(fget_buf));
219 fget_buf.f_sz = cp->c_len;
220 memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
221 fget_biov[0].iov_base = (char *)&fget_buf;
222 fget_biov[0].iov_len = sizeof(fget_buf.f_sz) +
223 cp->c_len;
224 } else {
225 fget_size = cp->c_len;
226 fget_biov[0].iov_base = (char *)&fget_size;
227 fget_biov[0].iov_len = sizeof(fget_size);
228 }
229
230 fget_biov[1].iov_base = NULL;
231 fget_biov[1].iov_len = 0;
232 *data = fget_biov;
233 }
234
235 return (err);
236 }
237
238 static void
fget_done(struct iovec * data __unused)239 fget_done(struct iovec *data __unused)
240 {
241
242 /* nothing needs to be freed */
243 }
244
245 static int
fget_len_result(struct iovec ** data)246 fget_len_result(struct iovec **data)
247 {
248 return (fget_result(data, 0));
249 }
250
251 static int
fget_val_result(struct iovec ** data)252 fget_val_result(struct iovec **data)
253 {
254 return (fget_result(data, 1));
255 }
256
257 static struct op_info fgetlen_info = {
258 .op_start = fget_start,
259 .op_data = fget_data,
260 .op_result = fget_len_result,
261 .op_done = fget_done
262 };
263
264 static struct op_info fgetval_info = {
265 .op_start = fget_start,
266 .op_data = fget_data,
267 .op_result = fget_val_result,
268 .op_done = fget_done
269 };
270
271 static struct req_info {
272 int req_error;
273 u_int req_count;
274 uint32_t req_size;
275 uint32_t req_type;
276 uint32_t req_txid;
277 struct op_info *req_op;
278 int resp_error;
279 int resp_count;
280 size_t resp_size;
281 size_t resp_off;
282 struct iovec *resp_biov;
283 } rinfo;
284
285 static void
fwctl_response_done(void)286 fwctl_response_done(void)
287 {
288
289 (*rinfo.req_op->op_done)(rinfo.resp_biov);
290
291 /* reinit the req data struct */
292 memset(&rinfo, 0, sizeof(rinfo));
293 }
294
295 static void
fwctl_request_done(void)296 fwctl_request_done(void)
297 {
298
299 rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
300
301 /* XXX only a single vector supported at the moment */
302 rinfo.resp_off = 0;
303 if (rinfo.resp_biov == NULL) {
304 rinfo.resp_size = 0;
305 } else {
306 rinfo.resp_size = rinfo.resp_biov[0].iov_len;
307 }
308 }
309
310 static int
fwctl_request_start(void)311 fwctl_request_start(void)
312 {
313 int err;
314
315 /* Data size doesn't include header */
316 rinfo.req_size -= 12;
317
318 rinfo.req_op = &errop_info;
319 if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
320 rinfo.req_op = ops[rinfo.req_type];
321
322 err = (*rinfo.req_op->op_start)(rinfo.req_size);
323
324 if (err) {
325 errop_set(err);
326 rinfo.req_op = &errop_info;
327 }
328
329 /* Catch case of zero-length message here */
330 if (rinfo.req_size == 0) {
331 fwctl_request_done();
332 return (1);
333 }
334
335 return (0);
336 }
337
338 static int
fwctl_request_data(uint32_t value)339 fwctl_request_data(uint32_t value)
340 {
341
342 /* Make sure remaining size is > 0 */
343 assert(rinfo.req_size > 0);
344 if (rinfo.req_size <= sizeof(uint32_t))
345 rinfo.req_size = 0;
346 else
347 rinfo.req_size -= sizeof(uint32_t);
348
349 (*rinfo.req_op->op_data)(value);
350
351 if (rinfo.req_size < sizeof(uint32_t)) {
352 fwctl_request_done();
353 return (1);
354 }
355
356 return (0);
357 }
358
359 static int
fwctl_request(uint32_t value)360 fwctl_request(uint32_t value)
361 {
362 int ret;
363
364 ret = 0;
365
366 switch (rinfo.req_count) {
367 case 0:
368 /* Verify size */
369 if (value < 12) {
370 printf("msg size error");
371 exit(4);
372 }
373 rinfo.req_size = value;
374 rinfo.req_count = 1;
375 break;
376 case 1:
377 rinfo.req_type = value;
378 rinfo.req_count++;
379 break;
380 case 2:
381 rinfo.req_txid = value;
382 rinfo.req_count++;
383 ret = fwctl_request_start();
384 break;
385 default:
386 ret = fwctl_request_data(value);
387 break;
388 }
389
390 return (ret);
391 }
392
393 static int
fwctl_response(uint32_t * retval)394 fwctl_response(uint32_t *retval)
395 {
396 uint8_t *dp;
397 ssize_t remlen;
398
399 switch(rinfo.resp_count) {
400 case 0:
401 /* 4 x u32 header len + data */
402 *retval = 4*sizeof(uint32_t) +
403 roundup(rinfo.resp_size, sizeof(uint32_t));
404 rinfo.resp_count++;
405 break;
406 case 1:
407 *retval = rinfo.req_type;
408 rinfo.resp_count++;
409 break;
410 case 2:
411 *retval = rinfo.req_txid;
412 rinfo.resp_count++;
413 break;
414 case 3:
415 *retval = rinfo.resp_error;
416 rinfo.resp_count++;
417 break;
418 default:
419 remlen = rinfo.resp_size - rinfo.resp_off;
420 dp = (uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off;
421 if (remlen >= (ssize_t)sizeof(uint32_t)) {
422 memcpy(retval, dp, sizeof(uint32_t));
423 } else if (remlen > 0) {
424 *retval = fwctl_send_rest(dp, remlen);
425 }
426 rinfo.resp_off += sizeof(uint32_t);
427 break;
428 }
429
430 if (rinfo.resp_count > 3 &&
431 rinfo.resp_off >= rinfo.resp_size) {
432 fwctl_response_done();
433 return (1);
434 }
435
436 return (0);
437 }
438
439 static void
fwctl_reset(void)440 fwctl_reset(void)
441 {
442
443 switch (be_state) {
444 case RESP:
445 /* If a response was generated but not fully read, discard it. */
446 fwctl_response_done();
447 break;
448 case REQ:
449 /* Discard partially-received request. */
450 memset(&rinfo, 0, sizeof(rinfo));
451 break;
452 case IDENT:
453 break;
454 }
455
456 be_state = IDENT;
457 ident_idx = 0;
458 }
459
460
461 /*
462 * i/o port handling.
463 */
464 static uint8_t
fwctl_inb(void)465 fwctl_inb(void)
466 {
467 uint8_t retval;
468
469 retval = 0xff;
470
471 switch (be_state) {
472 case IDENT:
473 retval = sig[ident_idx++];
474 if (ident_idx >= sizeof(sig))
475 be_state = REQ;
476 break;
477 default:
478 break;
479 }
480
481 return (retval);
482 }
483
484 static void
fwctl_outw(uint16_t val)485 fwctl_outw(uint16_t val)
486 {
487 if (val == 0) {
488 /*
489 * The guest wants to read the signature. It's possible that the
490 * guest is unaware of the fwctl state at this moment. For that
491 * reason, reset the state machine unconditionally.
492 */
493 fwctl_reset();
494 }
495 }
496
497 static uint32_t
fwctl_inl(void)498 fwctl_inl(void)
499 {
500 uint32_t retval;
501
502 switch (be_state) {
503 case RESP:
504 if (fwctl_response(&retval))
505 be_state = REQ;
506 break;
507 default:
508 retval = 0xffffffff;
509 break;
510 }
511
512 return (retval);
513 }
514
515 static void
fwctl_outl(uint32_t val)516 fwctl_outl(uint32_t val)
517 {
518
519 switch (be_state) {
520 case REQ:
521 if (fwctl_request(val))
522 be_state = RESP;
523 default:
524 break;
525 }
526
527 }
528
529 static int
fwctl_handler(struct vmctx * ctx __unused,int in,int port __unused,int bytes,uint32_t * eax,void * arg __unused)530 fwctl_handler(struct vmctx *ctx __unused, int in,
531 int port __unused, int bytes, uint32_t *eax, void *arg __unused)
532 {
533
534 if (in) {
535 if (bytes == 1)
536 *eax = fwctl_inb();
537 else if (bytes == 4)
538 *eax = fwctl_inl();
539 else
540 *eax = 0xffff;
541 } else {
542 if (bytes == 2)
543 fwctl_outw(*eax);
544 else if (bytes == 4)
545 fwctl_outl(*eax);
546 }
547
548 return (0);
549 }
550
551 void
fwctl_init(void)552 fwctl_init(void)
553 {
554 struct inout_port iop;
555 int error;
556
557 bzero(&iop, sizeof(iop));
558 iop.name = "fwctl_wreg";
559 iop.port = FWCTL_OUT;
560 iop.size = 1;
561 iop.flags = IOPORT_F_INOUT;
562 iop.handler = fwctl_handler;
563
564 error = register_inout(&iop);
565 assert(error == 0);
566
567 bzero(&iop, sizeof(iop));
568 iop.name = "fwctl_rreg";
569 iop.port = FWCTL_IN;
570 iop.size = 1;
571 iop.flags = IOPORT_F_IN;
572 iop.handler = fwctl_handler;
573
574 error = register_inout(&iop);
575 assert(error == 0);
576
577 ops[OP_GET_LEN] = &fgetlen_info;
578 ops[OP_GET] = &fgetval_info;
579
580 be_state = IDENT;
581 }
582