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