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