xref: /illumos-gate/usr/src/cmd/bhyve/fwctl.c (revision 4f3f3e9a1dee62c031fa67cfe64e11d6dd3fab1b)
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 	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
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
115 errop_set(int err)
116 {
117 
118 	errop_code = err;
119 }
120 
121 static int
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
131 errop_data(uint32_t data, uint32_t len)
132 {
133 
134 	/* ignore */
135 }
136 
137 static int
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
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 *
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
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
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
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
247 fget_done(struct iovec *data)
248 {
249 
250 	/* nothing needs to be freed */
251 }
252 
253 static int
254 fget_len_result(struct iovec **data)
255 {
256 	return (fget_result(data, 0));
257 }
258 
259 static int
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
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
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
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
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
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
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
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
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
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
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
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
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