arm64 libc: hide .cerror, .curbrk, .minbrk for WITHOUT_SYMVER
[freebsd-src.git] / usr.sbin / bhyve / fwctl.c
blob4b6164b8156098d7380648bcde85374874306954
1 /*-
2 * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 * $FreeBSD$
30 * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
31 * but with a request/response messaging protocol.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/uio.h>
41 #include <assert.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
46 #include "bhyverun.h"
47 #include "inout.h"
48 #include "fwctl.h"
51 * Messaging protocol base operations
53 #define OP_NULL 1
54 #define OP_ECHO 2
55 #define OP_GET 3
56 #define OP_GET_LEN 4
57 #define OP_SET 5
58 #define OP_MAX OP_SET
60 /* I/O ports */
61 #define FWCTL_OUT 0x510
62 #define FWCTL_IN 0x511
65 * Back-end state-machine
67 enum state {
68 DORMANT,
69 IDENT_WAIT,
70 IDENT_SEND,
71 REQ,
72 RESP
73 } be_state = DORMANT;
75 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
76 static u_int ident_idx;
78 struct op_info {
79 int op;
80 int (*op_start)(int len);
81 void (*op_data)(uint32_t data, int len);
82 int (*op_result)(struct iovec **data);
83 void (*op_done)(struct iovec *data);
85 static struct op_info *ops[OP_MAX+1];
87 /* Return 0-padded uint32_t */
88 static uint32_t
89 fwctl_send_rest(uint32_t *data, size_t len)
91 union {
92 uint8_t c[4];
93 uint32_t w;
94 } u;
95 uint8_t *cdata;
96 int i;
98 cdata = (uint8_t *) data;
99 u.w = 0;
101 for (i = 0, u.w = 0; i < len; i++)
102 u.c[i] = *cdata++;
104 return (u.w);
108 * error op dummy proto - drop all data sent and return an error
110 static int errop_code;
112 static void
113 errop_set(int err)
116 errop_code = err;
119 static int
120 errop_start(int len)
122 errop_code = ENOENT;
124 /* accept any length */
125 return (errop_code);
128 static void
129 errop_data(uint32_t data, int len)
132 /* ignore */
135 static int
136 errop_result(struct iovec **data)
139 /* no data to send back; always successful */
140 *data = NULL;
141 return (errop_code);
144 static void
145 errop_done(struct iovec *data)
148 /* assert data is NULL */
151 static struct op_info errop_info = {
152 .op_start = errop_start,
153 .op_data = errop_data,
154 .op_result = errop_result,
155 .op_done = errop_done
158 /* OID search */
159 SET_DECLARE(ctl_set, struct ctl);
161 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
163 static struct ctl *
164 ctl_locate(const char *str, int maxlen)
166 struct ctl *cp, **cpp;
168 SET_FOREACH(cpp, ctl_set) {
169 cp = *cpp;
170 if (!strncmp(str, cp->c_oid, maxlen))
171 return (cp);
174 return (NULL);
177 /* uefi-sysctl get-len */
178 #define FGET_STRSZ 80
179 static struct iovec fget_biov[2];
180 static char fget_str[FGET_STRSZ];
181 static struct {
182 size_t f_sz;
183 uint32_t f_data[1024];
184 } fget_buf;
185 static int fget_cnt;
186 static size_t fget_size;
188 static int
189 fget_start(int len)
192 if (len > FGET_STRSZ)
193 return(E2BIG);
195 fget_cnt = 0;
197 return (0);
200 static void
201 fget_data(uint32_t data, int len)
204 *((uint32_t *) &fget_str[fget_cnt]) = data;
205 fget_cnt += sizeof(uint32_t);
208 static int
209 fget_result(struct iovec **data, int val)
211 struct ctl *cp;
212 int err;
214 err = 0;
216 /* Locate the OID */
217 cp = ctl_locate(fget_str, fget_cnt);
218 if (cp == NULL) {
219 *data = NULL;
220 err = ENOENT;
221 } else {
222 if (val) {
223 /* For now, copy the len/data into a buffer */
224 memset(&fget_buf, 0, sizeof(fget_buf));
225 fget_buf.f_sz = cp->c_len;
226 memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
227 fget_biov[0].iov_base = (char *)&fget_buf;
228 fget_biov[0].iov_len = sizeof(fget_buf.f_sz) +
229 cp->c_len;
230 } else {
231 fget_size = cp->c_len;
232 fget_biov[0].iov_base = (char *)&fget_size;
233 fget_biov[0].iov_len = sizeof(fget_size);
236 fget_biov[1].iov_base = NULL;
237 fget_biov[1].iov_len = 0;
238 *data = fget_biov;
241 return (err);
244 static void
245 fget_done(struct iovec *data)
248 /* nothing needs to be freed */
251 static int
252 fget_len_result(struct iovec **data)
254 return (fget_result(data, 0));
257 static int
258 fget_val_result(struct iovec **data)
260 return (fget_result(data, 1));
263 static struct op_info fgetlen_info = {
264 .op_start = fget_start,
265 .op_data = fget_data,
266 .op_result = fget_len_result,
267 .op_done = fget_done
270 static struct op_info fgetval_info = {
271 .op_start = fget_start,
272 .op_data = fget_data,
273 .op_result = fget_val_result,
274 .op_done = fget_done
277 static struct req_info {
278 int req_error;
279 u_int req_count;
280 uint32_t req_size;
281 uint32_t req_type;
282 uint32_t req_txid;
283 struct op_info *req_op;
284 int resp_error;
285 int resp_count;
286 int resp_size;
287 int resp_off;
288 struct iovec *resp_biov;
289 } rinfo;
291 static void
292 fwctl_response_done(void)
295 (*rinfo.req_op->op_done)(rinfo.resp_biov);
297 /* reinit the req data struct */
298 memset(&rinfo, 0, sizeof(rinfo));
301 static void
302 fwctl_request_done(void)
305 rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
307 /* XXX only a single vector supported at the moment */
308 rinfo.resp_off = 0;
309 if (rinfo.resp_biov == NULL) {
310 rinfo.resp_size = 0;
311 } else {
312 rinfo.resp_size = rinfo.resp_biov[0].iov_len;
316 static int
317 fwctl_request_start(void)
319 int err;
321 /* Data size doesn't include header */
322 rinfo.req_size -= 12;
324 rinfo.req_op = &errop_info;
325 if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
326 rinfo.req_op = ops[rinfo.req_type];
328 err = (*rinfo.req_op->op_start)(rinfo.req_size);
330 if (err) {
331 errop_set(err);
332 rinfo.req_op = &errop_info;
335 /* Catch case of zero-length message here */
336 if (rinfo.req_size == 0) {
337 fwctl_request_done();
338 return (1);
341 return (0);
344 static int
345 fwctl_request_data(uint32_t value)
347 int remlen;
349 /* Make sure remaining size is >= 0 */
350 rinfo.req_size -= sizeof(uint32_t);
351 remlen = (rinfo.req_size > 0) ? rinfo.req_size: 0;
353 (*rinfo.req_op->op_data)(value, remlen);
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(1);
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 uint32_t *dp;
402 int 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 = (uint32_t *)
426 ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
427 if (remlen >= sizeof(uint32_t)) {
428 *retval = *dp;
429 } else if (remlen > 0) {
430 *retval = fwctl_send_rest(dp, remlen);
432 rinfo.resp_off += sizeof(uint32_t);
433 break;
436 if (rinfo.resp_count > 3 &&
437 rinfo.resp_size - rinfo.resp_off <= 0) {
438 fwctl_response_done();
439 return (1);
442 return (0);
447 * i/o port handling.
449 static uint8_t
450 fwctl_inb(void)
452 uint8_t retval;
454 retval = 0xff;
456 switch (be_state) {
457 case IDENT_SEND:
458 retval = sig[ident_idx++];
459 if (ident_idx >= sizeof(sig))
460 be_state = REQ;
461 break;
462 default:
463 break;
466 return (retval);
469 static void
470 fwctl_outw(uint16_t val)
472 switch (be_state) {
473 case IDENT_WAIT:
474 if (val == 0) {
475 be_state = IDENT_SEND;
476 ident_idx = 0;
478 break;
479 default:
480 /* ignore */
481 break;
485 static uint32_t
486 fwctl_inl(void)
488 uint32_t retval;
490 switch (be_state) {
491 case RESP:
492 if (fwctl_response(&retval))
493 be_state = REQ;
494 break;
495 default:
496 retval = 0xffffffff;
497 break;
500 return (retval);
503 static void
504 fwctl_outl(uint32_t val)
507 switch (be_state) {
508 case REQ:
509 if (fwctl_request(val))
510 be_state = RESP;
511 default:
512 break;
517 static int
518 fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
519 uint32_t *eax, void *arg)
522 if (in) {
523 if (bytes == 1)
524 *eax = fwctl_inb();
525 else if (bytes == 4)
526 *eax = fwctl_inl();
527 else
528 *eax = 0xffff;
529 } else {
530 if (bytes == 2)
531 fwctl_outw(*eax);
532 else if (bytes == 4)
533 fwctl_outl(*eax);
536 return (0);
538 INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
539 INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler);
541 void
542 fwctl_init(void)
545 ops[OP_GET_LEN] = &fgetlen_info;
546 ops[OP_GET] = &fgetval_info;
548 be_state = IDENT_WAIT;