pci_fbuf.c revision 302408
1/*-
2 * Copyright (c) 2015 Nahanni Systems, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/usr.sbin/bhyve/pci_fbuf.c 302408 2016-07-08 00:04:57Z gjb $
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/usr.sbin/bhyve/pci_fbuf.c 302408 2016-07-08 00:04:57Z gjb $");
31
32#include <sys/types.h>
33#include <sys/mman.h>
34
35#include <machine/vmm.h>
36#include <vmmapi.h>
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41
42#include <errno.h>
43#include <unistd.h>
44
45#include "bhyvegc.h"
46#include "bhyverun.h"
47#include "console.h"
48#include "inout.h"
49#include "pci_emul.h"
50#include "rfb.h"
51#include "vga.h"
52
53/*
54 * bhyve Framebuffer device emulation.
55 * BAR0 points to the current mode information.
56 * BAR1 is the 32-bit framebuffer address.
57 *
58 *  -s <b>,fbuf,wait,tcp=<ip>:port,w=width,h=height
59 */
60
61static int fbuf_debug = 1;
62#define	DEBUG_INFO	1
63#define	DEBUG_VERBOSE	4
64#define	DPRINTF(level, params)  if (level <= fbuf_debug) printf params
65
66
67#define	KB	(1024UL)
68#define	MB	(1024 * 1024UL)
69
70#define	DMEMSZ	128
71
72#define	FB_SIZE		(16*MB)
73
74#define COLS_MAX	1920
75#define	ROWS_MAX	1200
76
77#define COLS_DEFAULT	1024
78#define ROWS_DEFAULT	768
79
80#define COLS_MIN	640
81#define ROWS_MIN	480
82
83struct pci_fbuf_softc {
84	struct pci_devinst *fsc_pi;
85	struct {
86		uint32_t fbsize;
87		uint16_t width;
88		uint16_t height;
89		uint16_t depth;
90		uint16_t refreshrate;
91		uint8_t  reserved[116];
92	} __packed memregs;
93
94	/* rfb server */
95	char      *rfb_host;
96	int       rfb_port;
97	int       rfb_wait;
98	int       use_vga;
99
100	uint32_t  fbaddr;
101	char      *fb_base;
102	uint16_t  gc_width;
103	uint16_t  gc_height;
104	void      *vgasc;
105	struct bhyvegc_image *gc_image;
106};
107
108static struct pci_fbuf_softc *fbuf_sc;
109
110#define	PCI_FBUF_MSI_MSGS	 4
111
112static void
113pci_fbuf_usage(char *opt)
114{
115
116	fprintf(stderr, "Invalid fbuf emulation \"%s\"\r\n", opt);
117	fprintf(stderr, "fbuf: {wait,}tcp=<ip>:port\r\n");
118}
119
120static void
121pci_fbuf_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
122	       int baridx, uint64_t offset, int size, uint64_t value)
123{
124	struct pci_fbuf_softc *sc;
125	uint8_t *p;
126
127	assert(baridx == 0);
128
129	sc = pi->pi_arg;
130
131	DPRINTF(DEBUG_VERBOSE,
132	    ("fbuf wr: offset 0x%lx, size: %d, value: 0x%lx\n",
133	    offset, size, value));
134
135	if (offset + size > DMEMSZ) {
136		printf("fbuf: write too large, offset %ld size %d\n",
137		       offset, size);
138		return;
139	}
140
141	p = (uint8_t *)&sc->memregs + offset;
142
143	switch (size) {
144	case 1:
145		*p = value;
146		break;
147	case 2:
148		*(uint16_t *)p = value;
149		break;
150	case 4:
151		*(uint32_t *)p = value;
152		break;
153	case 8:
154		*(uint64_t *)p = value;
155		break;
156	default:
157		printf("fbuf: write unknown size %d\n", size);
158		break;
159	}
160
161	if (!sc->gc_image->vgamode && sc->memregs.width == 0 &&
162	    sc->memregs.height == 0) {
163		DPRINTF(DEBUG_INFO, ("switching to VGA mode\r\n"));
164		sc->gc_image->vgamode = 1;
165		sc->gc_width = 0;
166		sc->gc_height = 0;
167	} else if (sc->gc_image->vgamode && sc->memregs.width != 0 &&
168	    sc->memregs.height != 0) {
169		DPRINTF(DEBUG_INFO, ("switching to VESA mode\r\n"));
170		sc->gc_image->vgamode = 0;
171	}
172}
173
174uint64_t
175pci_fbuf_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
176	      int baridx, uint64_t offset, int size)
177{
178	struct pci_fbuf_softc *sc;
179	uint8_t *p;
180	uint64_t value;
181
182	assert(baridx == 0);
183
184	sc = pi->pi_arg;
185
186
187	if (offset + size > DMEMSZ) {
188		printf("fbuf: read too large, offset %ld size %d\n",
189		       offset, size);
190		return (0);
191	}
192
193	p = (uint8_t *)&sc->memregs + offset;
194	value = 0;
195	switch (size) {
196	case 1:
197		value = *p;
198		break;
199	case 2:
200		value = *(uint16_t *)p;
201		break;
202	case 4:
203		value = *(uint32_t *)p;
204		break;
205	case 8:
206		value = *(uint64_t *)p;
207		break;
208	default:
209		printf("fbuf: read unknown size %d\n", size);
210		break;
211	}
212
213	DPRINTF(DEBUG_VERBOSE,
214	    ("fbuf rd: offset 0x%lx, size: %d, value: 0x%lx\n",
215	     offset, size, value));
216
217	return (value);
218}
219
220static int
221pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
222{
223	char	*uopts, *xopts, *config;
224	char	*tmpstr;
225	int	ret;
226
227	ret = 0;
228	uopts = strdup(opts);
229	for (xopts = strtok(uopts, ",");
230	     xopts != NULL;
231	     xopts = strtok(NULL, ",")) {
232		if (strcmp(xopts, "wait") == 0) {
233			sc->rfb_wait = 1;
234			continue;
235		}
236
237#if 0 /* notyet */
238		if (strcmp(xopts, "vga") == 0) {
239			sc->use_vga = 1;
240			continue;
241		}
242#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