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