xref: /illumos-gate/usr/src/cmd/bhyve/fwctl.c (revision 32640292)
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