1 /*-
2  * Copyright (c) 2015  Peter Grehan <grehan@freebsd.org>
3  * All rights reserved.
4  *
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.
13  *
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.
25  *
26  * $FreeBSD: stable/10/usr.sbin/bhyve/fwctl.c 341607 2018-12-05 21:51:39Z emaste $
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 __FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/fwctl.c 341607 2018-12-05 21:51:39Z emaste $");
35 
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/uio.h>
40 
41 #include <assert.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 #include "bhyverun.h"
47 #include "inout.h"
48 #include "fwctl.h"
49 
50 /*
51  * Messaging protocol base operations
52  */
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
59 
60 /* I/O ports */
61 #define	FWCTL_OUT	0x510
62 #define	FWCTL_IN	0x511
63 
64 /*
65  * Back-end state-machine
66  */
67 enum state {
68 	DORMANT,
69 	IDENT_WAIT,
70 	IDENT_SEND,
71 	REQ,
72 	RESP
73 } be_state = DORMANT;
74 
75 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
76 static u_int ident_idx;
77 
78 struct op_info {
79 	int op;
80 	int  (*op_start)(uint32_t len);
81 	void (*op_data)(uint32_t data, uint32_t len);
82 	int  (*op_result)(struct iovec **data);
83 	void (*op_done)(struct iovec *data);
84 };
85 static struct op_info *ops[OP_MAX+1];
86 
87 /* Return 0-padded uint32_t */
88 static uint32_t
fwctl_send_rest(uint32_t * data,size_t len)89 fwctl_send_rest(uint32_t *data, size_t len)
90 {
91 	union {
92 		uint8_t c[4];
93 		uint32_t w;
94 	} u;
95 	uint8_t *cdata;
96 	int i;
97 
98 	cdata = (uint8_t *) data;
99 	u.w = 0;
100 
101 	for (i = 0, u.w = 0; i < len; i++)
102 		u.c[i] = *cdata++;
103 
104 	return (u.w);
105 }
106 
107 /*
108  * error op dummy proto - drop all data sent and return an error
109 */
110 static int errop_code;
111 
112 static void
errop_set(int err)113 errop_set(int err)
114 {
115 
116 	errop_code = err;
117 }
118 
119 static int
errop_start(uint32_t len)120 errop_start(uint32_t len)
121 {
122 	errop_code = ENOENT;
123 
124 	/* accept any length */
125 	return (errop_code);
126 }
127 
128 static void
errop_data(uint32_t data,uint32_t len)129 errop_data(uint32_t data, uint32_t len)
130 {
131 
132 	/* ignore */
133 }
134 
135 static int
errop_result(struct iovec ** data)136 errop_result(struct iovec **data)
137 {
138 
139 	/* no data to send back; always successful */
140 	*data = NULL;
141 	return (errop_code);
142 }
143 
144 static void
errop_done(struct iovec * data)145 errop_done(struct iovec *data)
146 {
147 
148 	/* assert data is NULL */
149 }
150 
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
156 };
157 
158 /* OID search */
159 SET_DECLARE(ctl_set, struct ctl);
160 
161 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
162 
163 static struct ctl *
ctl_locate(const char * str,int maxlen)164 ctl_locate(const char *str, int maxlen)
165 {
166 	struct ctl *cp, **cpp;
167 
168 	SET_FOREACH(cpp, ctl_set)  {
169 		cp = *cpp;
170 		if (!strncmp(str, cp->c_oid, maxlen))
171 			return (cp);
172 	}
173 
174 	return (NULL);
175 }
176 
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;
187 
188 static int
fget_start(uint32_t len)189 fget_start(uint32_t len)
190 {
191 
192 	if (len > FGET_STRSZ)
193 		return(E2BIG);
194 
195 	fget_cnt = 0;
196 
197 	return (0);
198 }
199 
200 static void
fget_data(uint32_t data,uint32_t len)201 fget_data(uint32_t data, uint32_t len)
202 {
203 
204 	*((uint32_t *) &fget_str[fget_cnt]) = data;
205 	fget_cnt += sizeof(uint32_t);
206 }
207 
208 static int
fget_result(struct iovec ** data,int val)209 fget_result(struct iovec **data, int val)
210 {
211 	struct ctl *cp;
212 	int err;
213 
214 	err = 0;
215 
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);
234 		}
235 
236 		fget_biov[1].iov_base = NULL;
237 		fget_biov[1].iov_len  = 0;
238 		*data = fget_biov;
239 	}
240 
241 	return (err);
242 }
243 
244 static void
fget_done(struct iovec * data)245 fget_done(struct iovec *data)
246 {
247 
248 	/* nothing needs to be freed */
249 }
250 
251 static int
fget_len_result(struct iovec ** data)252 fget_len_result(struct iovec **data)
253 {
254 	return (fget_result(data, 0));
255 }
256 
257 static int
fget_val_result(struct iovec ** data)258 fget_val_result(struct iovec **data)
259 {
260 	return (fget_result(data, 1));
261 }
262 
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
268 };
269 
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
275 };
276 
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 	size_t	 resp_size;
287 	size_t	 resp_off;
288 	struct iovec *resp_biov;
289 } rinfo;
290 
291 static void
fwctl_response_done(void)292 fwctl_response_done(void)
293 {
294 
295 	(*rinfo.req_op->op_done)(rinfo.resp_biov);
296 
297 	/* reinit the req data struct */
298 	memset(&rinfo, 0, sizeof(rinfo));
299 }
300 
301 static void
fwctl_request_done(void)302 fwctl_request_done(void)
303 {
304 
305 	rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
306 
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;
313 	}
314 }
315 
316 static int
fwctl_request_start(void)317 fwctl_request_start(void)
318 {
319 	int err;
320 
321 	/* Data size doesn't include header */
322 	rinfo.req_size -= 12;
323 
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];
327 
328 	err = (*rinfo.req_op->op_start)(rinfo.req_size);
329 
330 	if (err) {
331 		errop_set(err);
332 		rinfo.req_op = &errop_info;
333 	}
334 
335 	/* Catch case of zero-length message here */
336 	if (rinfo.req_size == 0) {
337 		fwctl_request_done();
338 		return (1);
339 	}
340 
341 	return (0);
342 }
343 
344 static int
fwctl_request_data(uint32_t value)345 fwctl_request_data(uint32_t value)
346 {
347 
348 	/* Make sure remaining size is >= 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
fwctl_request(uint32_t value)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(1);
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
fwctl_response(uint32_t * retval)400 fwctl_response(uint32_t *retval)
401 {
402 	uint32_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 = (uint32_t *)
427 		    ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
428 		if (remlen >= sizeof(uint32_t)) {
429 			*retval = *dp;
430 		} else if (remlen > 0) {
431 			*retval = fwctl_send_rest(dp, remlen);
432 		}
433 		rinfo.resp_off += sizeof(uint32_t);
434 		break;
435 	}
436 
437 	if (rinfo.resp_count > 3 &&
438 	    rinfo.resp_off >= rinfo.resp_size) {
439 		fwctl_response_done();
440 		return (1);
441 	}
442 
443 	return (0);
444 }
445 
446 
447 /*
448  * i/o port handling.
449  */
450 static uint8_t
fwctl_inb(void)451 fwctl_inb(void)
452 {
453 	uint8_t retval;
454 
455 	retval = 0xff;
456 
457 	switch (be_state) {
458 	case IDENT_SEND:
459 		retval = sig[ident_idx++];
460 		if (ident_idx >= sizeof(sig))
461 			be_state = REQ;
462 		break;
463 	default:
464 		break;
465 	}
466 
467 	return (retval);
468 }
469 
470 static void
fwctl_outw(uint16_t val)471 fwctl_outw(uint16_t val)
472 {
473 	switch (be_state) {
474 	case IDENT_WAIT:
475 		if (val == 0) {
476 			be_state = IDENT_SEND;
477 			ident_idx = 0;
478 		}
479 		break;
480 	default:
481 		/* ignore */
482 		break;
483 	}
484 }
485 
486 static uint32_t
fwctl_inl(void)487 fwctl_inl(void)
488 {
489 	uint32_t retval;
490 
491 	switch (be_state) {
492 	case RESP:
493 		if (fwctl_response(&retval))
494 			be_state = REQ;
495 		break;
496 	default:
497 		retval = 0xffffffff;
498 		break;
499 	}
500 
501 	return (retval);
502 }
503 
504 static void
fwctl_outl(uint32_t val)505 fwctl_outl(uint32_t val)
506 {
507 
508 	switch (be_state) {
509 	case REQ:
510 		if (fwctl_request(val))
511 			be_state = RESP;
512 	default:
513 		break;
514 	}
515 
516 }
517 
518 static int
fwctl_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)519 fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
520     uint32_t *eax, void *arg)
521 {
522 
523 	if (in) {
524 		if (bytes == 1)
525 			*eax = fwctl_inb();
526 		else if (bytes == 4)
527 			*eax = fwctl_inl();
528 		else
529 			*eax = 0xffff;
530 	} else {
531 		if (bytes == 2)
532 			fwctl_outw(*eax);
533 		else if (bytes == 4)
534 			fwctl_outl(*eax);
535 	}
536 
537 	return (0);
538 }
539 INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
540 INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
541 
542 void
fwctl_init(void)543 fwctl_init(void)
544 {
545 
546 	ops[OP_GET_LEN] = &fgetlen_info;
547 	ops[OP_GET]     = &fgetval_info;
548 
549 	be_state = IDENT_WAIT;
550 }
551