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