1300829Sgrehan/*- 2300829Sgrehan * Copyright (c) 2015 Nahanni Systems, Inc. 3300829Sgrehan * All rights reserved. 4300829Sgrehan * 5300829Sgrehan * Redistribution and use in source and binary forms, with or without 6300829Sgrehan * modification, are permitted provided that the following conditions 7300829Sgrehan * are met: 8300829Sgrehan * 1. Redistributions of source code must retain the above copyright 9300829Sgrehan * notice, this list of conditions and the following disclaimer. 10300829Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11300829Sgrehan * notice, this list of conditions and the following disclaimer in the 12300829Sgrehan * documentation and/or other materials provided with the distribution. 13300829Sgrehan * 14300829Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15300829Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16300829Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17300829Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18300829Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19300829Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20300829Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21300829Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22300829Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23300829Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24300829Sgrehan * SUCH DAMAGE. 25300829Sgrehan * 26300829Sgrehan * $FreeBSD: releng/11.0/usr.sbin/bhyve/pci_fbuf.c 302408 2016-07-08 00:04:57Z gjb $ 27300829Sgrehan */ 28300829Sgrehan 29300829Sgrehan#include <sys/cdefs.h> 30300829Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/pci_fbuf.c 302408 2016-07-08 00:04:57Z gjb $"); 31300829Sgrehan 32300829Sgrehan#include <sys/types.h> 33300829Sgrehan#include <sys/mman.h> 34300829Sgrehan 35300829Sgrehan#include <machine/vmm.h> 36300829Sgrehan#include <vmmapi.h> 37300829Sgrehan 38300829Sgrehan#include <stdio.h> 39300829Sgrehan#include <stdlib.h> 40300829Sgrehan#include <string.h> 41300829Sgrehan 42300829Sgrehan#include <errno.h> 43300829Sgrehan#include <unistd.h> 44300829Sgrehan 45300829Sgrehan#include "bhyvegc.h" 46300829Sgrehan#include "bhyverun.h" 47300829Sgrehan#include "console.h" 48300829Sgrehan#include "inout.h" 49300829Sgrehan#include "pci_emul.h" 50300829Sgrehan#include "rfb.h" 51300829Sgrehan#include "vga.h" 52300829Sgrehan 53300829Sgrehan/* 54300829Sgrehan * bhyve Framebuffer device emulation. 55300829Sgrehan * BAR0 points to the current mode information. 56300829Sgrehan * BAR1 is the 32-bit framebuffer address. 57300829Sgrehan * 58300829Sgrehan * -s <b>,fbuf,wait,tcp=<ip>:port,w=width,h=height 59300829Sgrehan */ 60300829Sgrehan 61300829Sgrehanstatic int fbuf_debug = 1; 62300829Sgrehan#define DEBUG_INFO 1 63300829Sgrehan#define DEBUG_VERBOSE 4 64300829Sgrehan#define DPRINTF(level, params) if (level <= fbuf_debug) printf params 65300829Sgrehan 66300829Sgrehan 67300829Sgrehan#define KB (1024UL) 68300829Sgrehan#define MB (1024 * 1024UL) 69300829Sgrehan 70300829Sgrehan#define DMEMSZ 128 71300829Sgrehan 72300829Sgrehan#define FB_SIZE (16*MB) 73300829Sgrehan 74300829Sgrehan#define COLS_MAX 1920 75300829Sgrehan#define ROWS_MAX 1200 76300829Sgrehan 77300829Sgrehan#define COLS_DEFAULT 1024 78300829Sgrehan#define ROWS_DEFAULT 768 79300829Sgrehan 80300829Sgrehan#define COLS_MIN 640 81300829Sgrehan#define ROWS_MIN 480 82300829Sgrehan 83300829Sgrehanstruct pci_fbuf_softc { 84300829Sgrehan struct pci_devinst *fsc_pi; 85300829Sgrehan struct { 86300829Sgrehan uint32_t fbsize; 87300829Sgrehan uint16_t width; 88300829Sgrehan uint16_t height; 89300829Sgrehan uint16_t depth; 90300829Sgrehan uint16_t refreshrate; 91300829Sgrehan uint8_t reserved[116]; 92300829Sgrehan } __packed memregs; 93300829Sgrehan 94300829Sgrehan /* rfb server */ 95300829Sgrehan char *rfb_host; 96300829Sgrehan int rfb_port; 97300829Sgrehan int rfb_wait; 98300829Sgrehan int use_vga; 99300829Sgrehan 100300829Sgrehan uint32_t fbaddr; 101300829Sgrehan char *fb_base; 102300829Sgrehan uint16_t gc_width; 103300829Sgrehan uint16_t gc_height; 104300829Sgrehan void *vgasc; 105300829Sgrehan struct bhyvegc_image *gc_image; 106300829Sgrehan}; 107300829Sgrehan 108300829Sgrehanstatic struct pci_fbuf_softc *fbuf_sc; 109300829Sgrehan 110300829Sgrehan#define PCI_FBUF_MSI_MSGS 4 111300829Sgrehan 112300829Sgrehanstatic void 113300829Sgrehanpci_fbuf_usage(char *opt) 114300829Sgrehan{ 115300829Sgrehan 116300829Sgrehan fprintf(stderr, "Invalid fbuf emulation \"%s\"\r\n", opt); 117300829Sgrehan fprintf(stderr, "fbuf: {wait,}tcp=<ip>:port\r\n"); 118300829Sgrehan} 119300829Sgrehan 120300829Sgrehanstatic void 121300829Sgrehanpci_fbuf_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 122300829Sgrehan int baridx, uint64_t offset, int size, uint64_t value) 123300829Sgrehan{ 124300829Sgrehan struct pci_fbuf_softc *sc; 125300829Sgrehan uint8_t *p; 126300829Sgrehan 127300829Sgrehan assert(baridx == 0); 128300829Sgrehan 129300829Sgrehan sc = pi->pi_arg; 130300829Sgrehan 131300829Sgrehan DPRINTF(DEBUG_VERBOSE, 132300829Sgrehan ("fbuf wr: offset 0x%lx, size: %d, value: 0x%lx\n", 133300829Sgrehan offset, size, value)); 134300829Sgrehan 135300829Sgrehan if (offset + size > DMEMSZ) { 136300829Sgrehan printf("fbuf: write too large, offset %ld size %d\n", 137300829Sgrehan offset, size); 138300829Sgrehan return; 139300829Sgrehan } 140300829Sgrehan 141300829Sgrehan p = (uint8_t *)&sc->memregs + offset; 142300829Sgrehan 143300829Sgrehan switch (size) { 144300829Sgrehan case 1: 145300829Sgrehan *p = value; 146300829Sgrehan break; 147300829Sgrehan case 2: 148300829Sgrehan *(uint16_t *)p = value; 149300829Sgrehan break; 150300829Sgrehan case 4: 151300829Sgrehan *(uint32_t *)p = value; 152300829Sgrehan break; 153300829Sgrehan case 8: 154300829Sgrehan *(uint64_t *)p = value; 155300829Sgrehan break; 156300829Sgrehan default: 157300829Sgrehan printf("fbuf: write unknown size %d\n", size); 158300829Sgrehan break; 159300829Sgrehan } 160300829Sgrehan 161300829Sgrehan if (!sc->gc_image->vgamode && sc->memregs.width == 0 && 162300829Sgrehan sc->memregs.height == 0) { 163300829Sgrehan DPRINTF(DEBUG_INFO, ("switching to VGA mode\r\n")); 164300829Sgrehan sc->gc_image->vgamode = 1; 165300829Sgrehan sc->gc_width = 0; 166300829Sgrehan sc->gc_height = 0; 167300829Sgrehan } else if (sc->gc_image->vgamode && sc->memregs.width != 0 && 168300829Sgrehan sc->memregs.height != 0) { 169300829Sgrehan DPRINTF(DEBUG_INFO, ("switching to VESA mode\r\n")); 170300829Sgrehan sc->gc_image->vgamode = 0; 171300829Sgrehan } 172300829Sgrehan} 173300829Sgrehan 174300829Sgrehanuint64_t 175300829Sgrehanpci_fbuf_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 176300829Sgrehan int baridx, uint64_t offset, int size) 177300829Sgrehan{ 178300829Sgrehan struct pci_fbuf_softc *sc; 179300829Sgrehan uint8_t *p; 180300829Sgrehan uint64_t value; 181300829Sgrehan 182300829Sgrehan assert(baridx == 0); 183300829Sgrehan 184300829Sgrehan sc = pi->pi_arg; 185300829Sgrehan 186300829Sgrehan 187300829Sgrehan if (offset + size > DMEMSZ) { 188300829Sgrehan printf("fbuf: read too large, offset %ld size %d\n", 189300829Sgrehan offset, size); 190300829Sgrehan return (0); 191300829Sgrehan } 192300829Sgrehan 193300829Sgrehan p = (uint8_t *)&sc->memregs + offset; 194300829Sgrehan value = 0; 195300829Sgrehan switch (size) { 196300829Sgrehan case 1: 197300829Sgrehan value = *p; 198300829Sgrehan break; 199300829Sgrehan case 2: 200300829Sgrehan value = *(uint16_t *)p; 201300829Sgrehan break; 202300829Sgrehan case 4: 203300829Sgrehan value = *(uint32_t *)p; 204300829Sgrehan break; 205300829Sgrehan case 8: 206300829Sgrehan value = *(uint64_t *)p; 207300829Sgrehan break; 208300829Sgrehan default: 209300829Sgrehan printf("fbuf: read unknown size %d\n", size); 210300829Sgrehan break; 211300829Sgrehan } 212300829Sgrehan 213300829Sgrehan DPRINTF(DEBUG_VERBOSE, 214300829Sgrehan ("fbuf rd: offset 0x%lx, size: %d, value: 0x%lx\n", 215300829Sgrehan offset, size, value)); 216300829Sgrehan 217300829Sgrehan return (value); 218300829Sgrehan} 219300829Sgrehan 220300829Sgrehanstatic int 221300829Sgrehanpci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts) 222300829Sgrehan{ 223300829Sgrehan char *uopts, *xopts, *config; 224300829Sgrehan char *tmpstr; 225300829Sgrehan int ret; 226300829Sgrehan 227300829Sgrehan ret = 0; 228300829Sgrehan uopts = strdup(opts); 229300829Sgrehan for (xopts = strtok(uopts, ","); 230300829Sgrehan xopts != NULL; 231300829Sgrehan xopts = strtok(NULL, ",")) { 232300829Sgrehan if (strcmp(xopts, "wait") == 0) { 233300829Sgrehan sc->rfb_wait = 1; 234300829Sgrehan continue; 235300829Sgrehan } 236300829Sgrehan 237300829Sgrehan#if 0 /* notyet */ 238300829Sgrehan if (strcmp(xopts, "vga") == 0) { 239300829Sgrehan sc->use_vga = 1; 240300829Sgrehan continue; 241300829Sgrehan } 242300829Sgrehan#endif 243300829Sgrehan 244300829Sgrehan if ((config = strchr(xopts, '=')) == NULL) { 245300829Sgrehan pci_fbuf_usage(xopts); 246300829Sgrehan ret = -1; 247300829Sgrehan goto done; 248300829Sgrehan } 249300829Sgrehan 250300829Sgrehan *config++ = '\0'; 251300829Sgrehan 252300829Sgrehan DPRINTF(DEBUG_VERBOSE, ("pci_fbuf option %s = %s\r\n", 253300829Sgrehan xopts, config)); 254300829Sgrehan 255300829Sgrehan if (!strcmp(xopts, "tcp")) { 256300829Sgrehan /* parse host-ip:port */ 257300829Sgrehan tmpstr = strsep(&config, ":"); 258300829Sgrehan if (!config) 259300829Sgrehan sc->rfb_port = atoi(tmpstr); 260300829Sgrehan else { 261300829Sgrehan sc->rfb_port = atoi(config); 262300829Sgrehan sc->rfb_host = tmpstr; 263300829Sgrehan } 264300829Sgrehan } else if (!strcmp(xopts, "w")) { 265300829Sgrehan sc->memregs.width = atoi(config); 266300829Sgrehan if (sc->memregs.width > COLS_MAX) { 267300829Sgrehan pci_fbuf_usage(xopts); 268300829Sgrehan ret = -1; 269300829Sgrehan goto done; 270300829Sgrehan } else if (sc->memregs.width == 0) 271300829Sgrehan sc->memregs.width = 1920; 272300829Sgrehan } else if (!strcmp(xopts, "h")) { 273300829Sgrehan sc->memregs.height = atoi(config); 274300829Sgrehan if (sc->memregs.height > ROWS_MAX) { 275300829Sgrehan pci_fbuf_usage(xopts); 276300829Sgrehan ret = -1; 277300829Sgrehan goto done; 278300829Sgrehan } else if (sc->memregs.height == 0) 279300829Sgrehan sc->memregs.height = 1080; 280300829Sgrehan 281300829Sgrehan } else { 282300829Sgrehan pci_fbuf_usage(xopts); 283300829Sgrehan ret = -1; 284300829Sgrehan goto done; 285300829Sgrehan } 286300829Sgrehan } 287300829Sgrehan 288300829Sgrehandone: 289300829Sgrehan return (ret); 290300829Sgrehan} 291300829Sgrehan 292300829Sgrehan 293300829Sgrehanextern void vga_render(struct bhyvegc *gc, void *arg); 294300829Sgrehan 295300829Sgrehanvoid 296300829Sgrehanpci_fbuf_render(struct bhyvegc *gc, void *arg) 297300829Sgrehan{ 298300829Sgrehan struct pci_fbuf_softc *sc; 299300829Sgrehan 300300829Sgrehan sc = arg; 301300829Sgrehan 302300829Sgrehan if (sc->use_vga && sc->gc_image->vgamode) { 303300829Sgrehan /* TODO: mode switching to vga and vesa should use the special 304300829Sgrehan * EFI-bhyve protocol port. 305300829Sgrehan */ 306300829Sgrehan vga_render(gc, sc->vgasc); 307300829Sgrehan return; 308300829Sgrehan } 309300829Sgrehan if (sc->gc_width != sc->memregs.width || 310300829Sgrehan sc->gc_height != sc->memregs.height) { 311300829Sgrehan bhyvegc_resize(gc, sc->memregs.width, sc->memregs.height); 312300829Sgrehan sc->gc_width = sc->memregs.width; 313300829Sgrehan sc->gc_height = sc->memregs.height; 314300829Sgrehan } 315300829Sgrehan 316300829Sgrehan return; 317300829Sgrehan} 318300829Sgrehan 319300829Sgrehanstatic int 320300829Sgrehanpci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 321300829Sgrehan{ 322300829Sgrehan int error, prot; 323300829Sgrehan struct pci_fbuf_softc *sc; 324300829Sgrehan 325300829Sgrehan if (fbuf_sc != NULL) { 326300829Sgrehan fprintf(stderr, "Only one frame buffer device is allowed.\n"); 327300829Sgrehan return (-1); 328300829Sgrehan } 329300829Sgrehan 330300829Sgrehan sc = calloc(1, sizeof(struct pci_fbuf_softc)); 331300829Sgrehan 332300829Sgrehan pi->pi_arg = sc; 333300829Sgrehan 334300829Sgrehan /* initialize config space */ 335300829Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x40FB); 336300829Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0xFB5D); 337300829Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_DISPLAY); 338300829Sgrehan pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA); 339300829Sgrehan 340300829Sgrehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, DMEMSZ); 341300829Sgrehan assert(error == 0); 342300829Sgrehan 343300829Sgrehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, FB_SIZE); 344300829Sgrehan assert(error == 0); 345300829Sgrehan 346300829Sgrehan error = pci_emul_add_msicap(pi, PCI_FBUF_MSI_MSGS); 347300829Sgrehan assert(error == 0); 348300829Sgrehan 349300829Sgrehan sc->fbaddr = pi->pi_bar[1].addr; 350300829Sgrehan sc->memregs.fbsize = FB_SIZE; 351300829Sgrehan sc->memregs.width = COLS_DEFAULT; 352300829Sgrehan sc->memregs.height = ROWS_DEFAULT; 353300829Sgrehan sc->memregs.depth = 32; 354300829Sgrehan 355300829Sgrehan sc->fsc_pi = pi; 356300829Sgrehan 357300829Sgrehan error = pci_fbuf_parse_opts(sc, opts); 358300829Sgrehan if (error != 0) 359300829Sgrehan goto done; 360300829Sgrehan 361300829Sgrehan sc->fb_base = vm_create_devmem(ctx, VM_FRAMEBUFFER, "framebuffer", FB_SIZE); 362300829Sgrehan if (sc->fb_base == MAP_FAILED) { 363300829Sgrehan error = -1; 364300829Sgrehan goto done; 365300829Sgrehan } 366300829Sgrehan DPRINTF(DEBUG_INFO, ("fbuf frame buffer base: %p [sz %lu]\r\n", 367300829Sgrehan sc->fb_base, FB_SIZE)); 368300829Sgrehan 369300829Sgrehan /* 370300829Sgrehan * Map the framebuffer into the guest address space. 371300829Sgrehan * XXX This may fail if the BAR is different than a prior 372300829Sgrehan * run. In this case flag the error. This will be fixed 373300829Sgrehan * when a change_memseg api is available. 374300829Sgrehan */ 375300829Sgrehan prot = PROT_READ | PROT_WRITE; 376300829Sgrehan if (vm_mmap_memseg(ctx, sc->fbaddr, VM_FRAMEBUFFER, 0, FB_SIZE, prot) != 0) { 377300829Sgrehan fprintf(stderr, "pci_fbuf: mapseg failed - try deleting VM and restarting\n"); 378300829Sgrehan error = -1; 379300829Sgrehan goto done; 380300829Sgrehan } 381300829Sgrehan 382300829Sgrehan console_init(sc->memregs.width, sc->memregs.height, sc->fb_base); 383300829Sgrehan console_fb_register(pci_fbuf_render, sc); 384300829Sgrehan 385300829Sgrehan sc->vgasc = vga_init(!sc->use_vga); 386300829Sgrehan sc->gc_image = console_get_image(); 387300829Sgrehan 388300829Sgrehan fbuf_sc = sc; 389300829Sgrehan 390300829Sgrehan memset((void *)sc->fb_base, 0, FB_SIZE); 391300829Sgrehan 392300829Sgrehan error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait); 393300829Sgrehandone: 394300829Sgrehan if (error) 395300829Sgrehan free(sc); 396300829Sgrehan 397300829Sgrehan return (error); 398300829Sgrehan} 399300829Sgrehan 400300829Sgrehanstruct pci_devemu pci_fbuf = { 401300829Sgrehan .pe_emu = "fbuf", 402300829Sgrehan .pe_init = pci_fbuf_init, 403300829Sgrehan .pe_barwrite = pci_fbuf_write, 404300829Sgrehan .pe_barread = pci_fbuf_read 405300829Sgrehan}; 406300829SgrehanPCI_EMUL_SET(pci_fbuf); 407