Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 2 Oct 2015 21:09:49 +0000 (UTC)
From:      Peter Grehan <grehan@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r288522 - head/usr.sbin/bhyve
Message-ID:  <201510022109.t92L9nvM001571@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: grehan
Date: Fri Oct  2 21:09:49 2015
New Revision: 288522
URL: https://svnweb.freebsd.org/changeset/base/288522

Log:
  Simple sysctl-like firmware query interface. Similar in operation
  to the qemu one, and uses the same i/o ports but with different
  messaging. Requires the 'bootrom' option to be enabled.
  
  This is used by UEFI (and potentially other BIOSs/firmware) to
  request information from bhyve. Currently, only the number of
  vCPUs is made available, with more to follow.
  
  A very large thankyou to Ben Perrault who helped out testing
  an earlier version of this, and bhyve/Windows in general.
  
  Reviewed by:	tychon
  Discussed with:	neel
  Sponsored by:	Nahanni Systems

Added:
  head/usr.sbin/bhyve/fwctl.c   (contents, props changed)
  head/usr.sbin/bhyve/fwctl.h   (contents, props changed)
Modified:
  head/usr.sbin/bhyve/Makefile
  head/usr.sbin/bhyve/bhyverun.c

Modified: head/usr.sbin/bhyve/Makefile
==============================================================================
--- head/usr.sbin/bhyve/Makefile	Fri Oct  2 20:13:56 2015	(r288521)
+++ head/usr.sbin/bhyve/Makefile	Fri Oct  2 21:09:49 2015	(r288522)
@@ -16,6 +16,7 @@ SRCS=	\
 	bootrom.c		\
 	consport.c		\
 	dbgport.c		\
+	fwctl.c			\
 	inout.c			\
 	ioapic.c		\
 	mem.c			\

Modified: head/usr.sbin/bhyve/bhyverun.c
==============================================================================
--- head/usr.sbin/bhyve/bhyverun.c	Fri Oct  2 20:13:56 2015	(r288521)
+++ head/usr.sbin/bhyve/bhyverun.c	Fri Oct  2 21:09:49 2015	(r288522)
@@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$");
 #include "acpi.h"
 #include "inout.h"
 #include "dbgport.h"
+#include "fwctl.h"
 #include "ioapic.h"
 #include "mem.h"
 #include "mevent.h"
@@ -950,6 +951,9 @@ main(int argc, char *argv[])
 		assert(error == 0);
 	}
 
+	if (lpc_bootrom())
+		fwctl_init();
+
 	/*
 	 * Change the proc title to include the VM name.
 	 */

Added: head/usr.sbin/bhyve/fwctl.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/bhyve/fwctl.c	Fri Oct  2 21:09:49 2015	(r288522)
@@ -0,0 +1,549 @@
+/*-
+ * Copyright (c) 2015  Peter Grehan <grehan@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
+ * but with a request/response messaging protocol.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bhyverun.h"
+#include "inout.h"
+#include "fwctl.h"
+
+/*
+ * Messaging protocol base operations
+ */
+#define	OP_NULL		1
+#define	OP_ECHO		2
+#define	OP_GET		3
+#define	OP_GET_LEN	4
+#define	OP_SET		5
+#define	OP_MAX		OP_SET
+
+/* I/O ports */
+#define	FWCTL_OUT	0x510
+#define	FWCTL_IN	0x511
+
+/*
+ * Back-end state-machine
+ */
+enum state {
+	DORMANT,
+	IDENT_WAIT,
+	IDENT_SEND,
+	REQ,
+	RESP
+} be_state = DORMANT;
+
+static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
+static u_int ident_idx;
+
+struct op_info {
+	int op;
+	int  (*op_start)(int len);
+	void (*op_data)(uint32_t data, int len);
+	int  (*op_result)(struct iovec **data);
+	void (*op_done)(struct iovec *data);
+};
+static struct op_info *ops[OP_MAX+1];
+
+/* Return 0-padded uint32_t */
+static uint32_t
+fwctl_send_rest(uint32_t *data, size_t len)
+{
+	union {
+		uint8_t c[4];
+		uint32_t w;
+	} u;
+	uint8_t *cdata;
+	int i;
+
+	cdata = (uint8_t *) data;
+	u.w = 0;	
+
+	for (i = 0, u.w = 0; i < len; i++)
+		u.c[i] = *cdata++;
+
+	return (u.w);
+}
+
+/*
+ * error op dummy proto - drop all data sent and return an error
+*/
+static int errop_code;
+
+static void
+errop_set(int err)
+{
+
+	errop_code = err;
+}
+
+static int
+errop_start(int len)
+{
+	errop_code = ENOENT;
+
+	/* accept any length */
+	return (errop_code);
+}
+
+static void
+errop_data(uint32_t data, int len)
+{
+
+	/* ignore */
+}
+
+static int
+errop_result(struct iovec **data)
+{
+
+	/* no data to send back; always successful */
+	*data = NULL;
+	return (errop_code);
+}
+
+static void
+errop_done(struct iovec *data)
+{
+
+	/* assert data is NULL */
+}
+
+static struct op_info errop_info = {
+	.op_start  = errop_start,
+	.op_data   = errop_data,
+	.op_result = errop_result,
+	.op_done   = errop_done
+};
+
+/* OID search */
+SET_DECLARE(ctl_set, struct ctl);
+
+CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
+
+static struct ctl *
+ctl_locate(const char *str, int maxlen)
+{
+	struct ctl *cp, **cpp;
+
+	SET_FOREACH(cpp, ctl_set)  {
+		cp = *cpp;
+		if (!strncmp(str, cp->c_oid, maxlen))
+			return (cp);
+	}
+
+	return (NULL);
+}
+
+/* uefi-sysctl get-len */
+#define FGET_STRSZ	80
+static struct iovec fget_biov[2];
+static char fget_str[FGET_STRSZ];
+static struct {
+	size_t f_sz;
+	uint32_t f_data[1024];
+} fget_buf;
+static int fget_cnt;
+static size_t fget_size;
+
+static int
+fget_start(int len)
+{
+
+	if (len > FGET_STRSZ)
+		return(E2BIG);
+
+	fget_cnt = 0;
+
+	return (0);
+}
+
+static void
+fget_data(uint32_t data, int len)
+{
+
+	*((uint32_t *) &fget_str[fget_cnt]) = data;
+	fget_cnt += sizeof(uint32_t);
+}
+
+static int
+fget_result(struct iovec **data, int val)
+{
+	struct ctl *cp;
+	int err;
+
+	err = 0;
+
+	/* Locate the OID */
+	cp = ctl_locate(fget_str, fget_cnt);
+	if (cp == NULL) {
+		*data = NULL;
+		err = ENOENT;
+	} else {
+		if (val) {
+			/* For now, copy the len/data into a buffer */
+			memset(&fget_buf, 0, sizeof(fget_buf));
+			fget_buf.f_sz = cp->c_len;
+			memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
+			fget_biov[0].iov_base = (char *)&fget_buf;
+			fget_biov[0].iov_len  = sizeof(fget_buf.f_sz) +
+				cp->c_len;
+		} else {
+			fget_size = cp->c_len;
+			fget_biov[0].iov_base = (char *)&fget_size;
+			fget_biov[0].iov_len  = sizeof(fget_size);
+		}
+
+		fget_biov[1].iov_base = NULL;
+		fget_biov[1].iov_len  = 0;
+		*data = fget_biov;
+	}
+
+	return (err);
+}
+
+static void
+fget_done(struct iovec *data)
+{
+
+	/* nothing needs to be freed */
+}
+
+static int
+fget_len_result(struct iovec **data)
+{
+	return (fget_result(data, 0));
+}
+
+static int
+fget_val_result(struct iovec **data)
+{
+	return (fget_result(data, 1));
+}
+
+static struct op_info fgetlen_info = {
+	.op_start  = fget_start,
+	.op_data   = fget_data,
+	.op_result = fget_len_result,
+	.op_done   = fget_done
+};
+
+static struct op_info fgetval_info = {
+	.op_start  = fget_start,
+	.op_data   = fget_data,
+	.op_result = fget_val_result,
+	.op_done   = fget_done
+};
+
+static struct req_info {
+	int      req_error;
+	u_int    req_count;
+	uint32_t req_size;
+	uint32_t req_type;
+	uint32_t req_txid;
+	struct op_info *req_op;
+	int	 resp_error;
+	int	 resp_count;
+	int	 resp_size;
+	int	 resp_off;
+	struct iovec *resp_biov;
+} rinfo;
+
+static void
+fwctl_response_done(void)
+{
+
+	(*rinfo.req_op->op_done)(rinfo.resp_biov);
+
+	/* reinit the req data struct */
+	memset(&rinfo, 0, sizeof(rinfo));
+}
+
+static void
+fwctl_request_done(void)
+{
+
+	rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
+
+	/* XXX only a single vector supported at the moment */
+	rinfo.resp_off = 0;
+	if (rinfo.resp_biov == NULL) {
+		rinfo.resp_size = 0;
+	} else {
+		rinfo.resp_size = rinfo.resp_biov[0].iov_len;
+	}
+}
+
+static int
+fwctl_request_start(void)
+{
+	int err;
+
+	/* Data size doesn't include header */
+	rinfo.req_size -= 12;
+
+	rinfo.req_op = &errop_info;
+	if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
+		rinfo.req_op = ops[rinfo.req_type];
+
+	err = (*rinfo.req_op->op_start)(rinfo.req_size);
+
+	if (err) {
+		errop_set(err);
+		rinfo.req_op = &errop_info;
+	}
+
+	/* Catch case of zero-length message here */
+	if (rinfo.req_size == 0) {
+		fwctl_request_done();
+		return (1);
+	}
+
+	return (0);
+}
+
+static int
+fwctl_request_data(uint32_t value)
+{
+	int remlen;
+
+	/* Make sure remaining size is >= 0 */
+	rinfo.req_size -= sizeof(uint32_t);
+	remlen = (rinfo.req_size > 0) ? rinfo.req_size: 0;
+
+	(*rinfo.req_op->op_data)(value, remlen);
+
+	if (rinfo.req_size < sizeof(uint32_t)) {
+		fwctl_request_done();
+		return (1);
+	}
+
+	return (0);
+}
+
+static int
+fwctl_request(uint32_t value)
+{
+
+	int ret;
+
+	ret = 0;
+
+	switch (rinfo.req_count) {
+	case 0:
+		/* Verify size */
+		if (value < 12) {
+			printf("msg size error");
+			exit(1);
+		}
+		rinfo.req_size = value;
+		rinfo.req_count = 1;
+		break;
+	case 1:
+		rinfo.req_type = value;
+		rinfo.req_count++;
+		break;
+	case 2:
+		rinfo.req_txid = value;
+		rinfo.req_count++;
+		ret = fwctl_request_start();
+		break;
+	default:
+		ret = fwctl_request_data(value);
+		break;
+	}
+
+	return (ret);
+}
+
+static int
+fwctl_response(uint32_t *retval)
+{
+	uint32_t *dp;
+	int remlen;
+
+	switch(rinfo.resp_count) {
+	case 0:
+		/* 4 x u32 header len + data */
+		*retval = 4*sizeof(uint32_t) +
+		    roundup(rinfo.resp_size, sizeof(uint32_t));
+		rinfo.resp_count++;
+		break;
+	case 1:
+		*retval = rinfo.req_type;
+		rinfo.resp_count++;
+		break;
+	case 2:
+		*retval = rinfo.req_txid;
+		rinfo.resp_count++;
+		break;
+	case 3:
+		*retval = rinfo.resp_error;
+		rinfo.resp_count++;
+		break;
+	default:
+		remlen = rinfo.resp_size - rinfo.resp_off;
+		dp = (uint32_t *)
+		    ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
+		if (remlen >= sizeof(uint32_t)) {
+			*retval = *dp;
+		} else if (remlen > 0) {
+			*retval = fwctl_send_rest(dp, remlen);
+		}
+		rinfo.resp_off += sizeof(uint32_t);
+		break;
+	}
+
+	if (rinfo.resp_count > 3 &&
+	    rinfo.resp_size - rinfo.resp_off <= 0) {
+		fwctl_response_done();
+		return (1);
+	}
+
+	return (0);
+}
+
+
+/*
+ * i/o port handling.
+ */
+static uint8_t
+fwctl_inb(void)
+{
+	uint8_t retval;
+
+	retval = 0xff;
+
+	switch (be_state) {
+	case IDENT_SEND:
+		retval = sig[ident_idx++];
+		if (ident_idx >= sizeof(sig))
+			be_state = REQ;
+		break;
+	default:
+		break;
+	}
+
+	return (retval);
+}
+
+static void
+fwctl_outw(uint16_t val)
+{
+	switch (be_state) {
+	case IDENT_WAIT:
+		if (val == 0) {
+			be_state = IDENT_SEND;
+			ident_idx = 0;
+		}
+		break;
+	default:
+		/* ignore */
+		break;
+	}
+}
+
+static uint32_t
+fwctl_inl(void)
+{
+	uint32_t retval;
+
+	switch (be_state) {
+	case RESP:
+		if (fwctl_response(&retval))
+			be_state = REQ;
+		break;
+	default:
+		retval = 0xffffffff;
+		break;
+	}
+
+	return (retval);
+}
+
+static void
+fwctl_outl(uint32_t val)
+{
+
+	switch (be_state) {
+	case REQ:
+		if (fwctl_request(val))
+			be_state = RESP;
+	default:
+		break;
+	}
+
+}
+
+static int
+fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
+    uint32_t *eax, void *arg)
+{
+
+	if (in) {
+		if (bytes == 1)
+			*eax = fwctl_inb();
+		else if (bytes == 4)
+			*eax = fwctl_inl();
+		else
+			*eax = 0xffff;
+	} else {
+		if (bytes == 2)
+			fwctl_outw(*eax);
+		else if (bytes == 4)
+			fwctl_outl(*eax);
+	}
+
+	return (0);
+}
+INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
+INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_OUT,   fwctl_handler);
+
+void
+fwctl_init(void)
+{
+
+	ops[OP_GET_LEN] = &fgetlen_info;
+	ops[OP_GET]     = &fgetval_info;
+
+	be_state = IDENT_WAIT;
+}

Added: head/usr.sbin/bhyve/fwctl.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/bhyve/fwctl.h	Fri Oct  2 21:09:49 2015	(r288522)
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2015  Peter Grehan <grehan@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FWCTL_H_
+#define _FWCTL_H_
+
+#include <sys/linker_set.h>
+
+/*
+ * Linker set api for export of information to guest firmware via
+ * a sysctl-like OID interface
+ */
+struct ctl {
+	const char *c_oid;
+	const void *c_data;
+	const int c_len;
+};
+
+#define CTL_NODE(oid, data, len)				\
+	static struct ctl __CONCAT(__ctl, __LINE__) = {		\
+		oid,						\
+		(data),						\
+		(len),						\
+	};							\
+	DATA_SET(ctl_set, __CONCAT(__ctl, __LINE__))
+
+void	fwctl_init(void);
+
+#endif /* _FWCTL_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201510022109.t92L9nvM001571>