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