Page MenuHomeFreeBSD

D11768.diff
No OneTemporary

D11768.diff

Index: usr.sbin/bhyve/Makefile
===================================================================
--- usr.sbin/bhyve/Makefile
+++ usr.sbin/bhyve/Makefile
@@ -57,6 +57,7 @@
usb_mouse.c \
virtio.c \
vga.c \
+ vncserver.c \
xmsr.c \
spinup_ap.c
Index: usr.sbin/bhyve/bhyve.8
===================================================================
--- usr.sbin/bhyve/bhyve.8
+++ usr.sbin/bhyve/bhyve.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 2, 2017
+.Dd July 28, 2017
.Dt BHYVE 8
.Os
.Sh NAME
@@ -193,6 +193,9 @@
The LPC bridge emulation can only be configured on bus 0.
.It Li fbuf
Raw framebuffer device attached to VNC server.
+.It Li vncserver
+Raw framebuffer device attached to a third party library that provides
+the VNC server.
.It Li xhci
eXtensible Host Controller Interface (xHCI) USB controller.
.El
@@ -482,7 +485,7 @@
-s 3,ahci-cd,/path/to/uefi-OS-install.iso \\
-s 4,ahci-hd,disk.img \\
-s 5,virtio-net,tap0 \\
- -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \\
+ -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait,vncserver \\
-s 30,xhci,tablet \\
-s 31,lpc -l com1,stdio \\
-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \\
Index: usr.sbin/bhyve/pci_fbuf.c
===================================================================
--- usr.sbin/bhyve/pci_fbuf.c
+++ usr.sbin/bhyve/pci_fbuf.c
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2017 Marcelo Araujo <araujo@FreeBSD.org>.
* Copyright (c) 2015 Nahanni Systems, Inc.
* All rights reserved.
*
@@ -38,8 +39,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sysexits.h>
+#include <dlfcn.h>
#include <errno.h>
+#include <err.h>
#include <unistd.h>
#include "bhyvegc.h"
@@ -61,7 +65,7 @@
static int fbuf_debug = 1;
#define DEBUG_INFO 1
#define DEBUG_VERBOSE 4
-#define DPRINTF(level, params) if (level <= fbuf_debug) printf params
+#define DPRINTF(level, params) if (level <= fbuf_debug) printf params
#define KB (1024UL)
@@ -92,18 +96,19 @@
} __packed memregs;
/* rfb server */
- char *rfb_host;
- char *rfb_password;
- int rfb_port;
- int rfb_wait;
- int vga_enabled;
- int vga_full;
+ char *rfb_host;
+ char *rfb_password;
+ int rfb_port;
+ int rfb_wait;
+ int vga_enabled;
+ int vga_full;
+ int vncserver_enabled;
- uint32_t fbaddr;
- char *fb_base;
- uint16_t gc_width;
- uint16_t gc_height;
- void *vgasc;
+ uint32_t fbaddr;
+ char *fb_base;
+ uint16_t gc_width;
+ uint16_t gc_height;
+ void *vgasc;
struct bhyvegc_image *gc_image;
};
@@ -222,10 +227,13 @@
static int
pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
{
- char *uopts, *xopts, *config;
- char *tmpstr;
- int ret;
+ char *uopts, *xopts, *config;
+ char *tmpstr;
+ char *loader;
+ int ret;
+ void *shlib;
+ loader = NULL;
ret = 0;
uopts = strdup(opts);
for (xopts = strtok(uopts, ",");
@@ -236,6 +244,23 @@
continue;
}
+ if (strcmp(xopts, "vncserver") == 0) {
+ if (loader == NULL) {
+ loader = strdup("/usr/local/lib/libhyverem.so");
+ if (loader == NULL)
+ err(EX_OSERR, "malloc");
+ }
+ shlib = dlopen(loader, RTLD_LAZY);
+ if (!shlib) {
+ sc->vncserver_enabled = 0;
+ fprintf(stderr, "Using RFB bhyve implementation.\n");
+ } else
+ sc->vncserver_enabled = 1;
+ dlclose(shlib);
+ free(loader);
+ continue;
+ }
+
if ((config = strchr(xopts, '=')) == NULL) {
pci_fbuf_usage(xopts);
ret = -1;
@@ -247,16 +272,17 @@
DPRINTF(DEBUG_VERBOSE, ("pci_fbuf option %s = %s\r\n",
xopts, config));
- if (!strcmp(xopts, "tcp") || !strcmp(xopts, "rfb")) {
+ if (!strcmp(xopts, "tcp") || !strcmp(xopts, "rfb") ||
+ !strcmp(xopts, "vncserver")) {
/* parse host-ip:port */
- tmpstr = strsep(&config, ":");
+ tmpstr = strsep(&config, ":");
if (!config)
sc->rfb_port = atoi(tmpstr);
else {
sc->rfb_port = atoi(config);
sc->rfb_host = tmpstr;
}
- } else if (!strcmp(xopts, "vga")) {
+ } else if (!strcmp(xopts, "vga")) {
if (!strcmp(config, "off")) {
sc->vga_enabled = 0;
} else if (!strcmp(config, "io")) {
@@ -270,8 +296,8 @@
ret = -1;
goto done;
}
- } else if (!strcmp(xopts, "w")) {
- sc->memregs.width = atoi(config);
+ } else if (!strcmp(xopts, "w")) {
+ sc->memregs.width = atoi(config);
if (sc->memregs.width > COLS_MAX) {
pci_fbuf_usage(xopts);
ret = -1;
@@ -383,7 +409,7 @@
goto done;
}
DPRINTF(DEBUG_INFO, ("fbuf frame buffer base: %p [sz %lu]\r\n",
- sc->fb_base, FB_SIZE));
+ sc->fb_base, FB_SIZE));
/*
* Map the framebuffer into the guest address space.
@@ -409,7 +435,10 @@
memset((void *)sc->fb_base, 0, FB_SIZE);
- error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, sc->rfb_password);
+ if (sc->vncserver_enabled)
+ error = vncserver_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, sc->rfb_password);
+ else
+ error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, sc->rfb_password);
done:
if (error)
free(sc);
Index: usr.sbin/bhyve/rfb.h
===================================================================
--- usr.sbin/bhyve/rfb.h
+++ usr.sbin/bhyve/rfb.h
@@ -32,5 +32,6 @@
#define RFB_PORT 5900
int rfb_init(char *hostname, int port, int wait, char *password);
+int vncserver_init(char *hostname, int port, int wait, char *password);
#endif /* _RFB_H_ */
Index: usr.sbin/bhyve/vncserver.c
===================================================================
--- /dev/null
+++ usr.sbin/bhyve/vncserver.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 2017 Marcelo Araujo <araujo@FreeBSD.org>
+ * Copyright (c) 2016 Jakub Klama <jakub@ixsystems.com>
+ * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
+ * Copyright (c) 2015 Leon Dang
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <sys/param.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <cpuid.h>
+#include <zlib.h>
+
+#include "bhyvegc.h"
+#include "console.h"
+
+static int vncserver_debug = 1;
+#define DPRINTF(params) if (vncserver_debug) printf params
+#define WPRINTF(params) printf params
+
+struct vncserver_softc {
+ struct bhyvegc_image *vs_gc;
+ pthread_t vs_tid;
+ int vs_width;
+ int vs_height;
+ int vs_conn_wait;
+ int vs_sending;
+ pthread_mutex_t vs_mtx;
+ pthread_cond_t vs_cond;
+
+ int vs_hw_crc;
+ uint32_t * vs_crc; /* WxH crc cells */
+ uint32_t * vs_crc_tmp; /* buffer to store single crc row */
+ int vs_crc_width;
+ int vs_crc_height;
+
+ char *desktopName;
+ bool alwaysShared;
+ int redShift;
+ int greenShift;
+ int blueShift;
+ char *frameBuffer;
+ int bitsPerSample;
+ int samplesPerPixel;
+ int bytesPerPixel;
+ int bind_port;
+ void (*kdb_handler)(int down, uint32_t keysym);
+ void (*ptr_handler)(uint8_t button, int x, int y);
+};
+
+#define RFB_MAX_WIDTH 2000
+#define RFB_MAX_HEIGHT 1200
+#define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4
+
+#define PIX_PER_CELL 32
+#define PIXCELL_SHIFT 5
+#define PIXCELL_MASK 0x1F
+
+/* percentage changes to screen before sending the entire screen */
+#define RFB_SEND_ALL_THRESH 25
+
+/* path of libhyve-remote */
+#define LIB_HYVE_REMOTE "/usr/local/lib/libhyverem.so"
+
+/* prototype functions from shared library libhyve-remote */
+int (*vnc_init_server)(struct vncserver_softc *sc);
+int (*vnc_event_loop)(int time, bool b);
+int (*vnc_enable_http)(char *webdir, bool enable);
+int (*vnc_enable_password)(char *vnc_password);
+void (*vnc_mark_rect_modified)(struct vncserver_softc *sc, int x1, int y1,
+ int x2, int y2);
+
+/* Try to load libhyve-remote */
+static int
+load_shared_lib(void)
+{
+ char *loader = NULL;
+ void *shlib;
+
+ if (loader == NULL) {
+ loader = strdup(LIB_HYVE_REMOTE);
+ if (loader == NULL)
+ err(ENOMEM, "malloc error");
+ }
+
+ shlib = dlopen(loader, RTLD_GLOBAL);
+ if (!shlib) {
+ dlclose(shlib);
+ free(loader);
+ return (1);
+ } else {
+ vnc_init_server = dlsym(shlib, "vnc_init_server");
+ vnc_event_loop = dlsym(shlib, "vnc_event_loop");
+ vnc_enable_http = dlsym(shlib, "vnc_enable_http");
+ vnc_mark_rect_modified = dlsym(shlib, "vnc_mark_rect_modified");
+ vnc_enable_password = dlsym(shlib, "vnc_enable_password");
+ }
+ return (0);
+}
+
+/*
+ * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
+ */
+static __inline uint32_t
+fast_crc32(void *buf, int len, uint32_t crcval)
+{
+ uint32_t q = len / sizeof(uint32_t);
+ uint32_t *p = (uint32_t *)buf;
+
+ while (q--) {
+ asm volatile (
+ ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
+ :"=S" (crcval)
+ :"0" (crcval), "c" (*p)
+ );
+ p++;
+ }
+
+ return (crcval);
+}
+
+static int
+vncserver_send_screen(struct vncserver_softc *sc, int all)
+{
+ struct bhyvegc_image *gc_image;
+ int x, y;
+ int celly, cellwidth;
+ int xcells, ycells;
+ int w, h;
+ uint32_t *p;
+ int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */
+ int retval;
+ uint32_t *crc_p, *orig_crc;
+ int changes;
+
+ console_refresh();
+
+ pthread_mutex_lock(&sc->vs_mtx);
+ if (sc->vs_sending) {
+ pthread_mutex_unlock(&sc->vs_mtx);
+ return (1);
+ }
+ sc->vs_sending = 1;
+ pthread_mutex_unlock(&sc->vs_mtx);
+
+ retval = 0;
+
+ if (all) {
+ vnc_mark_rect_modified(sc, 0, 0, sc->vs_width, sc->vs_height);
+ goto done;
+ }
+
+ /*
+ * Calculate the checksum for each 32x32 cell. Send each that
+ * has changed since the last scan.
+ */
+
+ /* Resolution changed */
+
+ gc_image = sc->vs_gc;
+ sc->vs_crc_width = gc_image->width;
+ sc->vs_crc_height = gc_image->height;
+
+ w = sc->vs_crc_width;
+ h = sc->vs_crc_height;
+ xcells = howmany(sc->vs_crc_width, PIX_PER_CELL);
+ ycells = howmany(sc->vs_crc_height, PIX_PER_CELL);
+
+ rem_x = w & PIXCELL_MASK;
+
+ rem_y = h & PIXCELL_MASK;
+ if (!rem_y)
+ rem_y = PIX_PER_CELL;
+
+ p = gc_image->data;
+
+ /*
+ * Go through all cells and calculate crc. If significant number
+ * of changes, then send entire screen.
+ * crc_tmp is dual purpose: to store the new crc and to flag as
+ * a cell that has changed.
+ */
+ crc_p = sc->vs_crc_tmp - xcells;
+ orig_crc = sc->vs_crc - xcells;
+ changes = 0;
+ memset(sc->vs_crc_tmp, 0, sizeof(uint32_t) * xcells * ycells);
+ for (y = 0; y < h; y++) {
+ if ((y & PIXCELL_MASK) == 0) {
+ crc_p += xcells;
+ orig_crc += xcells;
+ }
+
+ for (x = 0; x < xcells; x++) {
+ if (sc->vs_hw_crc)
+ crc_p[x] = fast_crc32(p,
+ PIX_PER_CELL * sizeof(uint32_t),
+ crc_p[x]);
+ else
+ crc_p[x] = (uint32_t)crc32(crc_p[x],
+ (Bytef *)p,
+ PIX_PER_CELL * sizeof(uint32_t));
+
+ p += PIX_PER_CELL;
+
+ /* check for crc delta if last row in cell */
+ if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
+ if (orig_crc[x] != crc_p[x]) {
+ orig_crc[x] = crc_p[x];
+ crc_p[x] = 1;
+ changes++;
+ } else {
+ crc_p[x] = 0;
+ }
+ }
+ }
+
+ if (rem_x) {
+ if (sc->vs_hw_crc)
+ crc_p[x] = fast_crc32(p,
+ rem_x * sizeof(uint32_t),
+ crc_p[x]);
+ else
+ crc_p[x] = (uint32_t)crc32(crc_p[x],
+ (Bytef *)p,
+ rem_x * sizeof(uint32_t));
+ p += rem_x;
+
+ if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
+ if (orig_crc[x] != crc_p[x]) {
+ orig_crc[x] = crc_p[x];
+ crc_p[x] = 1;
+ changes++;
+ } else {
+ crc_p[x] = 0;
+ }
+ }
+ }
+ }
+
+ /* If number of changes is > THRESH percent, send the whole screen */
+ if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) {
+ vnc_mark_rect_modified(sc, 0, 0, sc->vs_width, sc->vs_height);
+ goto done;
+ }
+
+ /* Go through all cells, and send only changed ones */
+ crc_p = sc->vs_crc_tmp;
+ for (y = 0; y < h; y += PIX_PER_CELL) {
+ /* previous cell's row */
+ celly = (y >> PIXCELL_SHIFT);
+
+ /* Delta check crc to previous set */
+ for (x = 0; x < xcells; x++) {
+ if (*crc_p++ == 0)
+ continue;
+
+ if (x == (xcells - 1) && rem_x > 0)
+ cellwidth = rem_x;
+ else
+ cellwidth = PIX_PER_CELL;
+
+ vnc_mark_rect_modified(sc, x * PIX_PER_CELL,
+ celly * PIX_PER_CELL,
+ x * PIX_PER_CELL + cellwidth,
+ (celly * PIX_PER_CELL) + (y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL));
+ }
+ }
+
+ retval = 1;
+
+done:
+ pthread_mutex_lock(&sc->vs_mtx);
+ sc->vs_sending = 0;
+ pthread_mutex_unlock(&sc->vs_mtx);
+
+ return (retval);
+}
+
+static void
+handle_keyboard(int down, uint32_t keysym) {
+ console_key_event(down, keysym);
+}
+
+static void
+handle_ptr(uint8_t button, int x, int y) {
+ console_ptr_event(button, x, y);
+}
+
+static int64_t
+timeval_delta(struct timeval *prev, struct timeval *now)
+{
+ int64_t n1, n2;
+ n1 = now->tv_sec * 1000000 + now->tv_usec;
+ n2 = prev->tv_sec * 1000000 + prev->tv_usec;
+ return (n1 - n2);
+}
+
+static void *
+vncserver_thr(void *arg)
+{
+ struct vncserver_softc *sc;
+ struct kevent kev, ret;
+ int kq, err;
+
+ sc = arg;
+ kq = kqueue();
+
+ EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, 40, NULL);
+
+ err = kevent(kq, &kev, 1, NULL, 0, NULL);
+ if (err < 0)
+ goto out;
+
+ for (;;) {
+ err = kevent(kq, NULL, 0, &ret, 1, NULL);
+ if (err < 0)
+ goto out;
+
+ if (ret.filter == EVFILT_TIMER) {
+ if (vncserver_send_screen(sc, 0) < 0)
+ goto out;
+ }
+ }
+
+out:
+ close(kq);
+ return (NULL);
+}
+
+static int
+sse42_supported()
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+
+ return ((ecx & bit_SSE42) != 0);
+}
+
+int
+vncserver_init(char *hostname, int port, int wait, char *password)
+{
+ struct vncserver_softc *sc;
+
+ if(load_shared_lib() == 1)
+ errx(EX_SOFTWARE, "cannot load shared library libhyve-remote");
+
+ sc = calloc(1, sizeof(struct vncserver_softc));
+
+ sc->vs_crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
+ sizeof(uint32_t));
+ sc->vs_crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
+ sizeof(uint32_t));
+ sc->vs_crc_width = RFB_MAX_WIDTH;
+ sc->vs_crc_height = RFB_MAX_HEIGHT;
+
+ sc->vs_hw_crc = sse42_supported();
+
+ sc->vs_conn_wait = wait;
+
+ if (wait) {
+ pthread_mutex_init(&sc->vs_mtx, NULL);
+ pthread_cond_init(&sc->vs_cond, NULL);
+ }
+
+ sc->vs_gc = console_get_image();
+ sc->vs_height = sc->vs_gc->height;
+ sc->vs_width = sc->vs_gc->width;
+
+ sc->desktopName = "bhyve";
+ if (password) {
+ vnc_enable_password(password);
+ }
+ sc->alwaysShared = true;
+ sc->redShift = 16;
+ sc->greenShift = 8;
+ sc->blueShift = 0;
+ sc->bind_port = port ? port : 5900;
+ sc->frameBuffer = (char *)sc->vs_gc->data;
+
+ sc->kdb_handler = handle_keyboard;
+ sc->ptr_handler = handle_ptr;
+
+ vnc_init_server(sc);
+ vnc_event_loop(4000, true);
+
+ pthread_create(&sc->vs_tid, NULL, vncserver_thr, sc);
+ pthread_set_name_np(sc->vs_tid, "vncserver");
+
+ if (wait) {
+ DPRINTF(("Waiting for vnc client...\n"));
+ pthread_mutex_lock(&sc->vs_mtx);
+ pthread_cond_wait(&sc->vs_cond, &sc->vs_mtx);
+ pthread_mutex_unlock(&sc->vs_mtx);
+ }
+
+ return (0);
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Jan 18, 3:31 AM (18 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15849720
Default Alt Text
D11768.diff (16 KB)

Event Timeline