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: releng/11.0/usr.sbin/bhyve/fwctl.c 298454 2016-04-22 06:25:32Z araujo $
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: releng/11.0/usr.sbin/bhyve/fwctl.c 298454 2016-04-22 06:25:32Z araujo $");
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 */
67enum state {
68	DORMANT,
69	IDENT_WAIT,
70	IDENT_SEND,
71	REQ,
72	RESP
73} be_state = DORMANT;
74
75static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
76static u_int ident_idx;
77
78struct op_info {
79	int op;
80	int  (*op_start)(int len);
81	void (*op_data)(uint32_t data, int len);
82	int  (*op_result)(struct iovec **data);
83	void (*op_done)(struct iovec *data);
84};
85static struct op_info *ops[OP_MAX+1];
86
87/* Return 0-padded uint32_t */
88static uint32_t
89fwctl_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*/
110static int errop_code;
111
112static void
113errop_set(int err)
114{
115
116	errop_code = err;
117}
118
119static int
120errop_start(int len)
121{
122	errop_code = ENOENT;
123
124	/* accept any length */
125	return (errop_code);
126}
127
128static void
129errop_data(uint32_t data, int len)
130{
131
132	/* ignore */
133}
134
135static int
136errop_result(struct iovec **data)
137{
138
139	/* no data to send back; always successful */
140	*data = NULL;
141	return (errop_code);
142}
143
144static void
145errop_done(struct iovec *data)
146{
147
148	/* assert data is NULL */
149}
150
151static 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 */
159SET_DECLARE(ctl_set, struct ctl);
160
161CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
162
163static struct ctl *
164ctl_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
179static struct iovec fget_biov[2];
180static char fget_str[FGET_STRSZ];
181static struct {
182	size_t f_sz;
183	uint32_t f_data[1024];
184} fget_buf;
185static int fget_cnt;
186static size_t fget_size;
187
188static int
189fget_start(int len)
190{
191
192	if (len > FGET_STRSZ)
193		return(E2BIG);
194
195	fget_cnt = 0;
196
197	return (0);
198}
199
200static void
201fget_data(uint32_t data, int len)
202{
203
204	*((uint32_t *) &fget_str[fget_cnt]) = data;
205	fget_cnt += sizeof(uint32_t);
206}
207
208static int
209fget_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
244static void
245fget_done(struct iovec *data)
246{
247
248	/* nothing needs to be freed */
249}
250
251static int
252fget_len_result(struct iovec **data)
253{
254	return (fget_result(data, 0));
255}
256
257static int
258fget_val_result(struct iovec **data)
259{
260	return (fget_result(data, 1));
261}
262
263static 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
270static 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
277static 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	int	 resp_size;
287	int	 resp_off;
288	struct iovec *resp_biov;
289} rinfo;
290
291static void
292fwctl_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
301static void
302fwctl_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
316static int
317fwctl_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
344static int
345fwctl_request_data(uint32_t value)
346{
347	int remlen;
348
349	/* Make sure remaining size is >= 0 */
350	rinfo.req_size -= sizeof(uint32_t);
351	remlen = MAX(rinfo.req_size, 0);
352
353	(*rinfo.req_op->op_data)(value, remlen);
354
355	if (rinfo.req_size < sizeof(uint32_t)) {
356		fwctl_request_done();
357		return (1);
358	}
359
360	return (0);
361}
362
363static int
364fwctl_request(uint32_t value)
365{
366
367	int ret;
368
369	ret = 0;
370
371	switch (rinfo.req_count) {
372	case 0:
373		/* Verify size */
374		if (value < 12) {
375			printf("msg size error");
376			exit(1);
377		}
378		rinfo.req_size = value;
379		rinfo.req_count = 1;
380		break;
381	case 1:
382		rinfo.req_type = value;
383		rinfo.req_count++;
384		break;
385	case 2:
386		rinfo.req_txid = value;
387		rinfo.req_count++;
388		ret = fwctl_request_start();
389		break;
390	default:
391		ret = fwctl_request_data(value);
392		break;
393	}
394
395	return (ret);
396}
397
398static int
399fwctl_response(uint32_t *retval)
400{
401	uint32_t *dp;
402	int remlen;
403
404	switch(rinfo.resp_count) {
405	case 0:
406		/* 4 x u32 header len + data */
407		*retval = 4*sizeof(uint32_t) +
408		    roundup(rinfo.resp_size, sizeof(uint32_t));
409		rinfo.resp_count++;
410		break;
411	case 1:
412		*retval = rinfo.req_type;
413		rinfo.resp_count++;
414		break;
415	case 2:
416		*retval = rinfo.req_txid;
417		rinfo.resp_count++;
418		break;
419	case 3:
420		*retval = rinfo.resp_error;
421		rinfo.resp_count++;
422		break;
423	default:
424		remlen = rinfo.resp_size - rinfo.resp_off;
425		dp = (uint32_t *)
426		    ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
427		if (remlen >= sizeof(uint32_t)) {
428			*retval = *dp;
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_size - rinfo.resp_off <= 0) {
438		fwctl_response_done();
439		return (1);
440	}
441
442	return (0);
443}
444
445
446/*
447 * i/o port handling.
448 */
449static uint8_t
450fwctl_inb(void)
451{
452	uint8_t retval;
453
454	retval = 0xff;
455
456	switch (be_state) {
457	case IDENT_SEND:
458		retval = sig[ident_idx++];
459		if (ident_idx >= sizeof(sig))
460			be_state = REQ;
461		break;
462	default:
463		break;
464	}
465
466	return (retval);
467}
468
469static void
470fwctl_outw(uint16_t val)
471{
472	switch (be_state) {
473	case IDENT_WAIT:
474		if (val == 0) {
475			be_state = IDENT_SEND;
476			ident_idx = 0;
477		}
478		break;
479	default:
480		/* ignore */
481		break;
482	}
483}
484
485static uint32_t
486fwctl_inl(void)
487{
488	uint32_t retval;
489
490	switch (be_state) {
491	case RESP:
492		if (fwctl_response(&retval))
493			be_state = REQ;
494		break;
495	default:
496		retval = 0xffffffff;
497		break;
498	}
499
500	return (retval);
501}
502
503static void
504fwctl_outl(uint32_t val)
505{
506
507	switch (be_state) {
508	case REQ:
509		if (fwctl_request(val))
510			be_state = RESP;
511	default:
512		break;
513	}
514
515}
516
517static int
518fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
519    uint32_t *eax, void *arg)
520{
521
522	if (in) {
523		if (bytes == 1)
524			*eax = fwctl_inb();
525		else if (bytes == 4)
526			*eax = fwctl_inl();
527		else
528			*eax = 0xffff;
529	} else {
530		if (bytes == 2)
531			fwctl_outw(*eax);
532		else if (bytes == 4)
533			fwctl_outl(*eax);
534	}
535
536	return (0);
537}
538INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
539INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
540
541void
542fwctl_init(void)
543{
544
545	ops[OP_GET_LEN] = &fgetlen_info;
546	ops[OP_GET]     = &fgetval_info;
547
548	be_state = IDENT_WAIT;
549}
550