15325 bhyve upstream sync 2023 January
[illumos-gate.git] / usr / src / cmd / bhyve / fwctl.c
blob047a1050e7fdab333eefb3ee99682a752d83e0be
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
5 * All rights reserved.
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.
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.
28 * $FreeBSD$
32 * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
33 * but with a request/response messaging protocol.
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/errno.h>
41 #include <sys/uio.h>
43 #include <assert.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
48 #include "bhyverun.h"
49 #include "inout.h"
50 #include "fwctl.h"
53 * Messaging protocol base operations
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
62 /* I/O ports */
63 #define FWCTL_OUT 0x510
64 #define FWCTL_IN 0x511
67 * Back-end state-machine
69 static enum state {
70 DORMANT,
71 IDENT_WAIT,
72 IDENT_SEND,
73 REQ,
74 RESP
75 } be_state = DORMANT;
77 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
78 static u_int ident_idx;
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);
87 static struct op_info *ops[OP_MAX+1];
89 /* Return 0-padded uint32_t */
90 static uint32_t
91 fwctl_send_rest(uint8_t *data, size_t len)
93 union {
94 uint8_t c[4];
95 uint32_t w;
96 } u;
97 size_t i;
99 u.w = 0;
100 for (i = 0; i < len; i++)
101 u.c[i] = *data++;
103 return (u.w);
107 * error op dummy proto - drop all data sent and return an error
109 static int errop_code;
111 static void
112 errop_set(int err)
115 errop_code = err;
118 static int
119 errop_start(uint32_t len __unused)
121 errop_code = ENOENT;
123 /* accept any length */
124 return (errop_code);
127 static void
128 errop_data(uint32_t data __unused, uint32_t len __unused)
131 /* ignore */
134 static int
135 errop_result(struct iovec **data)
138 /* no data to send back; always successful */
139 *data = NULL;
140 return (errop_code);
143 static void
144 errop_done(struct iovec *data __unused)
147 /* assert data is NULL */
150 static struct op_info errop_info = {
151 .op_start = errop_start,
152 .op_data = errop_data,
153 .op_result = errop_result,
154 .op_done = errop_done
157 /* OID search */
158 SET_DECLARE(ctl_set, struct ctl);
160 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
162 static struct ctl *
163 ctl_locate(const char *str, int maxlen)
165 struct ctl *cp, **cpp;
167 SET_FOREACH(cpp, ctl_set) {
168 cp = *cpp;
169 if (!strncmp(str, cp->c_oid, maxlen))
170 return (cp);
173 return (NULL);
176 /* uefi-sysctl get-len */
177 #define FGET_STRSZ 80
178 static struct iovec fget_biov[2];
179 static char fget_str[FGET_STRSZ];
180 static struct {
181 size_t f_sz;
182 uint32_t f_data[1024];
183 } fget_buf;
184 static int fget_cnt;
185 static size_t fget_size;
187 static int
188 fget_start(uint32_t len)
191 if (len > FGET_STRSZ)
192 return(E2BIG);
194 fget_cnt = 0;
196 return (0);
199 static void
200 fget_data(uint32_t data, uint32_t len __unused)
203 memcpy(&fget_str[fget_cnt], &data, sizeof(data));
204 fget_cnt += sizeof(uint32_t);
207 static int
208 fget_result(struct iovec **data, int val)
210 struct ctl *cp;
211 int err;
213 err = 0;
215 /* Locate the OID */
216 cp = ctl_locate(fget_str, fget_cnt);
217 if (cp == NULL) {
218 *data = NULL;
219 err = ENOENT;
220 } else {
221 if (val) {
222 /* For now, copy the len/data into a buffer */
223 memset(&fget_buf, 0, sizeof(fget_buf));
224 fget_buf.f_sz = cp->c_len;
225 memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
226 fget_biov[0].iov_base = (char *)&fget_buf;
227 fget_biov[0].iov_len = sizeof(fget_buf.f_sz) +
228 cp->c_len;
229 } else {
230 fget_size = cp->c_len;
231 fget_biov[0].iov_base = (char *)&fget_size;
232 fget_biov[0].iov_len = sizeof(fget_size);
235 fget_biov[1].iov_base = NULL;
236 fget_biov[1].iov_len = 0;
237 *data = fget_biov;
240 return (err);
243 static void
244 fget_done(struct iovec *data __unused)
247 /* nothing needs to be freed */
250 static int
251 fget_len_result(struct iovec **data)
253 return (fget_result(data, 0));
256 static int
257 fget_val_result(struct iovec **data)
259 return (fget_result(data, 1));
262 static struct op_info fgetlen_info = {
263 .op_start = fget_start,
264 .op_data = fget_data,
265 .op_result = fget_len_result,
266 .op_done = fget_done
269 static struct op_info fgetval_info = {
270 .op_start = fget_start,
271 .op_data = fget_data,
272 .op_result = fget_val_result,
273 .op_done = fget_done
276 static struct req_info {
277 int req_error;
278 u_int req_count;
279 uint32_t req_size;
280 uint32_t req_type;
281 uint32_t req_txid;
282 struct op_info *req_op;
283 int resp_error;
284 int resp_count;
285 size_t resp_size;
286 size_t resp_off;
287 struct iovec *resp_biov;
288 } rinfo;
290 static void
291 fwctl_response_done(void)
294 (*rinfo.req_op->op_done)(rinfo.resp_biov);
296 /* reinit the req data struct */
297 memset(&rinfo, 0, sizeof(rinfo));
300 static void
301 fwctl_request_done(void)
304 rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
306 /* XXX only a single vector supported at the moment */
307 rinfo.resp_off = 0;
308 if (rinfo.resp_biov == NULL) {
309 rinfo.resp_size = 0;
310 } else {
311 rinfo.resp_size = rinfo.resp_biov[0].iov_len;
315 static int
316 fwctl_request_start(void)
318 int err;
320 /* Data size doesn't include header */
321 rinfo.req_size -= 12;
323 rinfo.req_op = &errop_info;
324 if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
325 rinfo.req_op = ops[rinfo.req_type];
327 err = (*rinfo.req_op->op_start)(rinfo.req_size);
329 if (err) {
330 errop_set(err);
331 rinfo.req_op = &errop_info;
334 /* Catch case of zero-length message here */
335 if (rinfo.req_size == 0) {
336 fwctl_request_done();
337 return (1);
340 return (0);
343 static int
344 fwctl_request_data(uint32_t value)
347 /* Make sure remaining size is >= 0 */
348 if (rinfo.req_size <= sizeof(uint32_t))
349 rinfo.req_size = 0;
350 else
351 rinfo.req_size -= sizeof(uint32_t);
353 (*rinfo.req_op->op_data)(value, rinfo.req_size);
355 if (rinfo.req_size < sizeof(uint32_t)) {
356 fwctl_request_done();
357 return (1);
360 return (0);
363 static int
364 fwctl_request(uint32_t value)
367 int ret;
369 ret = 0;
371 switch (rinfo.req_count) {
372 case 0:
373 /* Verify size */
374 if (value < 12) {
375 printf("msg size error");
376 exit(4);
378 rinfo.req_size = value;
379 rinfo.req_count = 1;
380 break;
381 case 1:
382 rinfo.req_type = value;
383 rinfo.req_count++;
384 break;
385 case 2:
386 rinfo.req_txid = value;
387 rinfo.req_count++;
388 ret = fwctl_request_start();
389 break;
390 default:
391 ret = fwctl_request_data(value);
392 break;
395 return (ret);
398 static int
399 fwctl_response(uint32_t *retval)
401 uint8_t *dp;
402 ssize_t remlen;
404 switch(rinfo.resp_count) {
405 case 0:
406 /* 4 x u32 header len + data */
407 *retval = 4*sizeof(uint32_t) +
408 roundup(rinfo.resp_size, sizeof(uint32_t));
409 rinfo.resp_count++;
410 break;
411 case 1:
412 *retval = rinfo.req_type;
413 rinfo.resp_count++;
414 break;
415 case 2:
416 *retval = rinfo.req_txid;
417 rinfo.resp_count++;
418 break;
419 case 3:
420 *retval = rinfo.resp_error;
421 rinfo.resp_count++;
422 break;
423 default:
424 remlen = rinfo.resp_size - rinfo.resp_off;
425 dp = (uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off;
426 if (remlen >= (ssize_t)sizeof(uint32_t)) {
427 memcpy(retval, dp, sizeof(uint32_t));
428 } else if (remlen > 0) {
429 *retval = fwctl_send_rest(dp, remlen);
431 rinfo.resp_off += sizeof(uint32_t);
432 break;
435 if (rinfo.resp_count > 3 &&
436 rinfo.resp_off >= rinfo.resp_size) {
437 fwctl_response_done();
438 return (1);
441 return (0);
446 * i/o port handling.
448 static uint8_t
449 fwctl_inb(void)
451 uint8_t retval;
453 retval = 0xff;
455 switch (be_state) {
456 case IDENT_SEND:
457 retval = sig[ident_idx++];
458 if (ident_idx >= sizeof(sig))
459 be_state = REQ;
460 break;
461 default:
462 break;
465 return (retval);
468 static void
469 fwctl_outw(uint16_t val)
471 if (be_state == DORMANT) {
472 return;
475 if (val == 0) {
477 * The guest wants to read the signature. It's possible that the
478 * guest is unaware of the fwctl state at this moment. For that
479 * reason, reset the state machine unconditionally.
481 be_state = IDENT_SEND;
482 ident_idx = 0;
486 static uint32_t
487 fwctl_inl(void)
489 uint32_t retval;
491 switch (be_state) {
492 case RESP:
493 if (fwctl_response(&retval))
494 be_state = REQ;
495 break;
496 default:
497 retval = 0xffffffff;
498 break;
501 return (retval);
504 static void
505 fwctl_outl(uint32_t val)
508 switch (be_state) {
509 case REQ:
510 if (fwctl_request(val))
511 be_state = RESP;
512 default:
513 break;
518 static int
519 fwctl_handler(struct vmctx *ctx __unused, int in,
520 int port __unused, int bytes, uint32_t *eax, void *arg __unused)
523 if (in) {
524 if (bytes == 1)
525 *eax = fwctl_inb();
526 else if (bytes == 4)
527 *eax = fwctl_inl();
528 else
529 *eax = 0xffff;
530 } else {
531 if (bytes == 2)
532 fwctl_outw(*eax);
533 else if (bytes == 4)
534 fwctl_outl(*eax);
537 return (0);
540 void
541 fwctl_init(void)
543 struct inout_port iop;
544 int error;
546 bzero(&iop, sizeof(iop));
547 iop.name = "fwctl_wreg";
548 iop.port = FWCTL_OUT;
549 iop.size = 1;
550 iop.flags = IOPORT_F_INOUT;
551 iop.handler = fwctl_handler;
553 error = register_inout(&iop);
554 assert(error == 0);
556 bzero(&iop, sizeof(iop));
557 iop.name = "fwctl_rreg";
558 iop.port = FWCTL_IN;
559 iop.size = 1;
560 iop.flags = IOPORT_F_IN;
561 iop.handler = fwctl_handler;
563 error = register_inout(&iop);
564 assert(error == 0);
566 ops[OP_GET_LEN] = &fgetlen_info;
567 ops[OP_GET] = &fgetval_info;
569 be_state = IDENT_WAIT;