pci_fbuf.c revision 300829
128257Smsmith/*-
255939Snsouch * Copyright (c) 2015 Nahanni Systems, Inc.
328257Smsmith * All rights reserved.
428257Smsmith *
528257Smsmith * Redistribution and use in source and binary forms, with or without
628257Smsmith * modification, are permitted provided that the following conditions
728257Smsmith * are met:
828257Smsmith * 1. Redistributions of source code must retain the above copyright
928257Smsmith *    notice, this list of conditions and the following disclaimer.
1028257Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1128257Smsmith *    notice, this list of conditions and the following disclaimer in the
1228257Smsmith *    documentation and/or other materials provided with the distribution.
1328257Smsmith *
1428257Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
1528257Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1628257Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1728257Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1828257Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1928257Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2028257Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2128257Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2228257Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2328257Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2428257Smsmith * SUCH DAMAGE.
2528257Smsmith *
26119418Sobrien * $FreeBSD: projects/bhyve_graphics/pci_fbuf.c 300829 2016-05-27 06:30:35Z grehan $
27119418Sobrien */
28119418Sobrien
29119418Sobrien#include <sys/cdefs.h>
3028257Smsmith__FBSDID("$FreeBSD: projects/bhyve_graphics/pci_fbuf.c 300829 2016-05-27 06:30:35Z grehan $");
31187576Sjhb
3228257Smsmith#include <sys/types.h>
3355939Snsouch#include <sys/mman.h>
34187576Sjhb
35187576Sjhb#include <machine/vmm.h>
3655939Snsouch#include <vmmapi.h>
3755939Snsouch
3828257Smsmith#include <stdio.h>
39185003Sjhb#include <stdlib.h>
4055939Snsouch#include <string.h>
4128257Smsmith
4255939Snsouch#include <errno.h>
43185003Sjhb#include <unistd.h>
44119284Simp
45119284Simp#include "bhyvegc.h"
4655939Snsouch#include "bhyverun.h"
47185003Sjhb#include "console.h"
4828257Smsmith#include "inout.h"
4955939Snsouch#include "pci_emul.h"
5028257Smsmith#include "rfb.h"
5155939Snsouch#include "vga.h"
5228257Smsmith
5328257Smsmith/*
5428257Smsmith * bhyve Framebuffer device emulation.
5528257Smsmith * BAR0 points to the current mode information.
5655939Snsouch * BAR1 is the 32-bit framebuffer address.
5755939Snsouch *
5828257Smsmith *  -s <b>,fbuf,wait,tcp=<ip>:port,w=width,h=height
59187576Sjhb */
6042475Snsouch
6142475Snsouchstatic int fbuf_debug = 1;
6228257Smsmith#define	DEBUG_INFO	1
63227814Sattilio#define	DEBUG_VERBOSE	4
64187576Sjhb#define	DPRINTF(level, params)  if (level <= fbuf_debug) printf params
6542475Snsouch
6642475Snsouch
6742475Snsouch#define	KB	(1024UL)
6855939Snsouch#define	MB	(1024 * 1024UL)
6942475Snsouch
7042475Snsouch#define	DMEMSZ	128
7142475Snsouch
7242475Snsouch#define	FB_SIZE		(16*MB)
7342475Snsouch
7442475Snsouch#define COLS_MAX	1920
7542475Snsouch#define	ROWS_MAX	1200
7642475Snsouch
7755939Snsouch#define COLS_DEFAULT	1024
7828257Smsmith#define ROWS_DEFAULT	768
7928257Smsmith
80187576Sjhb#define COLS_MIN	640
81187576Sjhb#define ROWS_MIN	480
82187576Sjhb
83187576Sjhbstruct pci_fbuf_softc {
84187576Sjhb	struct pci_devinst *fsc_pi;
8542475Snsouch	struct {
8628257Smsmith		uint32_t fbsize;
8728257Smsmith		uint16_t width;
8828257Smsmith		uint16_t height;
8928257Smsmith		uint16_t depth;
9028257Smsmith		uint16_t refreshrate;
9128257Smsmith		uint8_t  reserved[116];
9255939Snsouch	} __packed memregs;
9338061Smsmith
9455939Snsouch	/* rfb server */
9538061Smsmith	char      *rfb_host;
9638061Smsmith	int       rfb_port;
9755939Snsouch	int       rfb_wait;
9838061Smsmith	int       use_vga;
9955939Snsouch
10038061Smsmith	uint32_t  fbaddr;
101227814Sattilio	char      *fb_base;
10255939Snsouch	uint16_t  gc_width;
10338061Smsmith	uint16_t  gc_height;
10455939Snsouch	void      *vgasc;
10555939Snsouch	struct bhyvegc_image *gc_image;
10655939Snsouch};
10755939Snsouch
10855939Snsouchstatic struct pci_fbuf_softc *fbuf_sc;
10955939Snsouch
11055939Snsouch#define	PCI_FBUF_MSI_MSGS	 4
11155939Snsouch
11255939Snsouchstatic void
11355939Snsouchpci_fbuf_usage(char *opt)
11455939Snsouch{
11555939Snsouch
11638061Smsmith	fprintf(stderr, "Invalid fbuf emulation \"%s\"\r\n", opt);
117227814Sattilio	fprintf(stderr, "fbuf: {wait,}tcp=<ip>:port\r\n");
11855939Snsouch}
11955939Snsouch
12038061Smsmithstatic void
12155939Snsouchpci_fbuf_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
12255939Snsouch	       int baridx, uint64_t offset, int size, uint64_t value)
12355939Snsouch{
12455939Snsouch	struct pci_fbuf_softc *sc;
12555939Snsouch	uint8_t *p;
12655939Snsouch
12755939Snsouch	assert(baridx == 0);
12855939Snsouch
12955939Snsouch	sc = pi->pi_arg;
13055939Snsouch
13155939Snsouch	DPRINTF(DEBUG_VERBOSE,
132227814Sattilio	    ("fbuf wr: offset 0x%lx, size: %d, value: 0x%lx\n",
13363458Sn_hibma	    offset, size, value));
134185003Sjhb
13555939Snsouch	if (offset + size > DMEMSZ) {
13663458Sn_hibma		printf("fbuf: write too large, offset %ld size %d\n",
13763458Sn_hibma		       offset, size);
13863458Sn_hibma		return;
13938061Smsmith	}
14038061Smsmith
14138061Smsmith	p = (uint8_t *)&sc->memregs + offset;
14238061Smsmith
14342475Snsouch	switch (size) {
14442475Snsouch	case 1:
14542475Snsouch		*p = value;
14642475Snsouch		break;
14742475Snsouch	case 2:
14855939Snsouch		*(uint16_t *)p = value;
14942475Snsouch		break;
150187576Sjhb	case 4:
151227814Sattilio		*(uint32_t *)p = value;
15255939Snsouch		break;
15342475Snsouch	case 8:
15442475Snsouch		*(uint64_t *)p = value;
15542475Snsouch		break;
15628257Smsmith	default:
15728257Smsmith		printf("fbuf: write unknown size %d\n", size);
15838061Smsmith		break;
15928257Smsmith	}
16028257Smsmith
16155939Snsouch	if (!sc->gc_image->vgamode && sc->memregs.width == 0 &&
16228257Smsmith	    sc->memregs.height == 0) {
163187576Sjhb		DPRINTF(DEBUG_INFO, ("switching to VGA mode\r\n"));
164227814Sattilio		sc->gc_image->vgamode = 1;
16555939Snsouch		sc->gc_width = 0;
16628257Smsmith		sc->gc_height = 0;
16728257Smsmith	} else if (sc->gc_image->vgamode && sc->memregs.width != 0 &&
16828257Smsmith	    sc->memregs.height != 0) {
16928257Smsmith		DPRINTF(DEBUG_INFO, ("switching to VESA mode\r\n"));
17028257Smsmith		sc->gc_image->vgamode = 0;
17138061Smsmith	}
17228257Smsmith}
17328257Smsmith
17455939Snsouchuint64_t
17528257Smsmithpci_fbuf_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
176187576Sjhb	      int baridx, uint64_t offset, int size)
177227814Sattilio{
17855939Snsouch	struct pci_fbuf_softc *sc;
17928257Smsmith	uint8_t *p;
18028257Smsmith	uint64_t value;
18128257Smsmith
18228257Smsmith	assert(baridx == 0);
18328257Smsmith
18438061Smsmith	sc = pi->pi_arg;
18528257Smsmith
18628257Smsmith
18755939Snsouch	if (offset + size > DMEMSZ) {
18828257Smsmith		printf("fbuf: read too large, offset %ld size %d\n",
18928257Smsmith		       offset, size);
19028257Smsmith		return (0);
191227814Sattilio	}
192187576Sjhb
19355939Snsouch	p = (uint8_t *)&sc->memregs + offset;
19428257Smsmith	value = 0;
19528257Smsmith	switch (size) {
19628257Smsmith	case 1:
19728257Smsmith		value = *p;
19839134Snsouch		break;
19928257Smsmith	case 2:
20028257Smsmith		value = *(uint16_t *)p;
20128257Smsmith		break;
20228257Smsmith	case 4:
20328257Smsmith		value = *(uint32_t *)p;
204187576Sjhb		break;
205187576Sjhb	case 8:
206187576Sjhb		value = *(uint64_t *)p;
207187576Sjhb		break;
208187576Sjhb	default:
209187576Sjhb		printf("fbuf: read unknown size %d\n", size);
210187576Sjhb		break;
211187576Sjhb	}
212187576Sjhb
213187576Sjhb	DPRINTF(DEBUG_VERBOSE,
214187576Sjhb	    ("fbuf rd: offset 0x%lx, size: %d, value: 0x%lx\n",
215187576Sjhb	     offset, size, value));
216187576Sjhb
217187576Sjhb	return (value);
218187576Sjhb}
219187576Sjhb
220187576Sjhbstatic int
221187576Sjhbpci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
222187576Sjhb{
223187576Sjhb	char	*uopts, *xopts, *config;
224187576Sjhb	char	*tmpstr;
225227758Sattilio	int	ret;
226187576Sjhb
227187576Sjhb	ret = 0;
228187576Sjhb	uopts = strdup(opts);
229187576Sjhb	for (xopts = strtok(uopts, ",");
230187576Sjhb	     xopts != NULL;
231187576Sjhb	     xopts = strtok(NULL, ",")) {
232187576Sjhb		if (strcmp(xopts, "wait") == 0) {
233187576Sjhb			sc->rfb_wait = 1;
234187576Sjhb			continue;
235187576Sjhb		}
236187576Sjhb
237187576Sjhb#if 0 /* notyet */
238187576Sjhb		if (strcmp(xopts, "vga") == 0) {
239187576Sjhb			sc->use_vga = 1;
240187576Sjhb			continue;
241187576Sjhb		}
242187576Sjhb#endif
243
244		if ((config = strchr(xopts, '=')) == NULL) {
245			pci_fbuf_usage(xopts);
246			ret = -1;
247			goto done;
248		}
249
250		*config++ = '\0';
251
252		DPRINTF(DEBUG_VERBOSE, ("pci_fbuf option %s = %s\r\n",
253		   xopts, config));
254
255		if (!strcmp(xopts, "tcp")) {
256			/* parse host-ip:port */
257			tmpstr = strsep(&config, ":");
258			if (!config)
259				sc->rfb_port = atoi(tmpstr);
260			else {
261				sc->rfb_port = atoi(config);
262				sc->rfb_host = tmpstr;
263			}
264		} else if (!strcmp(xopts, "w")) {
265			sc->memregs.width = atoi(config);
266			if (sc->memregs.width > COLS_MAX) {
267				pci_fbuf_usage(xopts);
268				ret = -1;
269				goto done;
270			} else if (sc->memregs.width == 0)
271				sc->memregs.width = 1920;
272		} else if (!strcmp(xopts, "h")) {
273			sc->memregs.height = atoi(config);
274			if (sc->memregs.height > ROWS_MAX) {
275				pci_fbuf_usage(xopts);
276				ret = -1;
277				goto done;
278			} else if (sc->memregs.height == 0)
279				sc->memregs.height = 1080;
280
281		} else {
282			pci_fbuf_usage(xopts);
283			ret = -1;
284			goto done;
285		}
286	}
287
288done:
289	return (ret);
290}
291
292
293extern void vga_render(struct bhyvegc *gc, void *arg);
294
295void
296pci_fbuf_render(struct bhyvegc *gc, void *arg)
297{
298	struct pci_fbuf_softc *sc;
299
300	sc = arg;
301
302	if (sc->use_vga && sc->gc_image->vgamode) {
303		/* TODO: mode switching to vga and vesa should use the special
304		 *      EFI-bhyve protocol port.
305		 */
306		vga_render(gc, sc->vgasc);
307		return;
308	}
309	if (sc->gc_width != sc->memregs.width ||
310	    sc->gc_height != sc->memregs.height) {
311		bhyvegc_resize(gc, sc->memregs.width, sc->memregs.height);
312		sc->gc_width = sc->memregs.width;
313		sc->gc_height = sc->memregs.height;
314	}
315
316	return;
317}
318
319static int
320pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
321{
322	int error, prot;
323	struct pci_fbuf_softc *sc;
324
325	if (fbuf_sc != NULL) {
326		fprintf(stderr, "Only one frame buffer device is allowed.\n");
327		return (-1);
328	}
329
330	sc = calloc(1, sizeof(struct pci_fbuf_softc));
331
332	pi->pi_arg = sc;
333
334	/* initialize config space */
335	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x40FB);
336	pci_set_cfgdata16(pi, PCIR_VENDOR, 0xFB5D);
337	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_DISPLAY);
338	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA);
339
340	error = pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, DMEMSZ);
341	assert(error == 0);
342
343	error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, FB_SIZE);
344	assert(error == 0);
345
346	error = pci_emul_add_msicap(pi, PCI_FBUF_MSI_MSGS);
347	assert(error == 0);
348
349	sc->fbaddr = pi->pi_bar[1].addr;
350	sc->memregs.fbsize = FB_SIZE;
351	sc->memregs.width  = COLS_DEFAULT;
352	sc->memregs.height = ROWS_DEFAULT;
353	sc->memregs.depth  = 32;
354
355	sc->fsc_pi = pi;
356
357	error = pci_fbuf_parse_opts(sc, opts);
358	if (error != 0)
359		goto done;
360
361	sc->fb_base = vm_create_devmem(ctx, VM_FRAMEBUFFER, "framebuffer", FB_SIZE);
362	if (sc->fb_base == MAP_FAILED) {
363		error = -1;
364		goto done;
365	}
366	DPRINTF(DEBUG_INFO, ("fbuf frame buffer base: %p [sz %lu]\r\n",
367	        sc->fb_base, FB_SIZE));
368
369	/*
370	 * Map the framebuffer into the guest address space.
371	 * XXX This may fail if the BAR is different than a prior
372	 * run. In this case flag the error. This will be fixed
373	 * when a change_memseg api is available.
374	 */
375	prot = PROT_READ | PROT_WRITE;
376	if (vm_mmap_memseg(ctx, sc->fbaddr, VM_FRAMEBUFFER, 0, FB_SIZE, prot) != 0) {
377		fprintf(stderr, "pci_fbuf: mapseg failed - try deleting VM and restarting\n");
378		error = -1;
379		goto done;
380	}
381
382	console_init(sc->memregs.width, sc->memregs.height, sc->fb_base);
383	console_fb_register(pci_fbuf_render, sc);
384
385	sc->vgasc = vga_init(!sc->use_vga);
386	sc->gc_image = console_get_image();
387
388	fbuf_sc = sc;
389
390	memset((void *)sc->fb_base, 0, FB_SIZE);
391
392	error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait);
393done:
394	if (error)
395		free(sc);
396
397	return (error);
398}
399
400struct pci_devemu pci_fbuf = {
401	.pe_emu =	"fbuf",
402	.pe_init =	pci_fbuf_init,
403	.pe_barwrite =	pci_fbuf_write,
404	.pe_barread =	pci_fbuf_read
405};
406PCI_EMUL_SET(pci_fbuf);
407