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