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 */
69enum state {
70	DORMANT,
71	IDENT_WAIT,
72	IDENT_SEND,
73	REQ,
74	RESP
75} be_state = DORMANT;
76
77static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
78static u_int ident_idx;
79
80struct 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};
87static struct op_info *ops[OP_MAX+1];
88
89/* Return 0-padded uint32_t */
90static uint32_t
91fwctl_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*/
112static int errop_code;
113
114static void
115errop_set(int err)
116{
117
118	errop_code = err;
119}
120
121static int
122errop_start(uint32_t len)
123{
124	errop_code = ENOENT;
125
126	/* accept any length */
127	return (errop_code);
128}
129
130static void
131errop_data(uint32_t data, uint32_t len)
132{
133
134	/* ignore */
135}
136
137static int
138errop_result(struct iovec **data)
139{
140
141	/* no data to send back; always successful */
142	*data = NULL;
143	return (errop_code);
144}
145
146static void
147errop_done(struct iovec *data)
148{
149
150	/* assert data is NULL */
151}
152
153static 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 */
161SET_DECLARE(ctl_set, struct ctl);
162
163CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
164
165static struct ctl *
166ctl_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
181static struct iovec fget_biov[2];
182static char fget_str[FGET_STRSZ];
183static struct {
184	size_t f_sz;
185	uint32_t f_data[1024];
186} fget_buf;
187static int fget_cnt;
188static size_t fget_size;
189
190static int
191fget_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
202static void
203fget_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
210static int
211fget_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
246static void
247fget_done(struct iovec *data)
248{
249
250	/* nothing needs to be freed */
251}
252
253static int
254fget_len_result(struct iovec **data)
255{
256	return (fget_result(data, 0));
257}
258
259static int
260fget_val_result(struct iovec **data)
261{
262	return (fget_result(data, 1));
263}
264
265static 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
272static 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
279static 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
293static void
294fwctl_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
303static void
304fwctl_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
318static int
319fwctl_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
346static int
347fwctl_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
366static int
367fwctl_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
401static int
402fwctl_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 */
452static uint8_t
453fwctl_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
472static void
473fwctl_outw(uint16_t val)
474{
475	switch (be_state) {
476	case IDENT_WAIT:
477		if (val == 0) {
478			be_state = IDENT_SEND;
479			ident_idx = 0;
480		}
481		break;
482	default:
483		/* ignore */
484		break;
485	}
486}
487
488static uint32_t
489fwctl_inl(void)
490{
491	uint32_t retval;
492
493	switch (be_state) {
494	case RESP:
495		if (fwctl_response(&retval))
496			be_state = REQ;
497		break;
498	default:
499		retval = 0xffffffff;
500		break;
501	}
502
503	return (retval);
504}
505
506static void
507fwctl_outl(uint32_t val)
508{
509
510	switch (be_state) {
511	case REQ:
512		if (fwctl_request(val))
513			be_state = RESP;
514	default:
515		break;
516	}
517
518}
519
520static int
521fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
522    uint32_t *eax, void *arg)
523{
524
525	if (in) {
526		if (bytes == 1)
527			*eax = fwctl_inb();
528		else if (bytes == 4)
529			*eax = fwctl_inl();
530		else
531			*eax = 0xffff;
532	} else {
533		if (bytes == 2)
534			fwctl_outw(*eax);
535		else if (bytes == 4)
536			fwctl_outl(*eax);
537	}
538
539	return (0);
540}
541INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
542INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
543
544void
545fwctl_init(void)
546{
547
548	ops[OP_GET_LEN] = &fgetlen_info;
549	ops[OP_GET]     = &fgetval_info;
550
551	be_state = IDENT_WAIT;
552}
553