Home | History | Annotate | Line # | Download | only in pci
      1 /*
      2  * Copyright (c) 2025 The NetBSD Foundation, 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 ANY EXPRESS OR
     15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 /*
     27  * Generic driver for Bochs Display Interface (DISPI) based devices:
     28  * - Bochs VBE/VGA interface
     29  * - QEMU standard VGA
     30  * - QEMU virtio-vga series GPU
     31  *
     32  * This driver supports both MMIO and I/O port access methods.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: bochsfb.c,v 1.1 2026/01/31 12:12:58 nia Exp $");
     37 
     38 #include <sys/param.h>
     39 #include <sys/systm.h>
     40 #include <sys/kernel.h>
     41 #include <sys/device.h>
     42 #include <sys/bus.h>
     43 #include <sys/endian.h>
     44 #include <sys/kauth.h>
     45 
     46 #include <dev/pci/pcivar.h>
     47 #include <dev/pci/pcireg.h>
     48 #include <dev/pci/pcidevs.h>
     49 #include <dev/pci/pciio.h>
     50 
     51 #include <dev/pci/bochsfbreg.h>
     52 #include <dev/pci/bochsfbvar.h>
     53 
     54 #include <dev/videomode/videomode.h>
     55 #include <dev/pci/wsdisplay_pci.h>
     56 
     57 #include "opt_wsemul.h"
     58 
     59 static int	bochsfb_match(device_t, cfdata_t, void *);
     60 static void	bochsfb_attach(device_t, device_t, void *);
     61 
     62 static void	bochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg,
     63 		uint16_t val);
     64 static uint16_t	bochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg);
     65 static void	bochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg,
     66 		uint8_t val);
     67 static uint8_t	bochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg);
     68 static void 	bochsfb_set_blanking(struct bochsfb_softc *sc, int blank);
     69 
     70 static bool	bochsfb_identify(struct bochsfb_softc *sc);
     71 static int      bochsfb_edid_mode(struct bochsfb_softc *sc);
     72 static bool	bochsfb_set_videomode(struct bochsfb_softc *sc);
     73 
     74 static paddr_t	bochsfb_mmap(void *v, void *vs, off_t offset, int prot);
     75 static int	bochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
     76 		struct lwp *l);
     77 static void	bochsfb_identify_screen(void *cookie, struct vcons_screen *scr,
     78 		int existing, long *defattr);
     79 
     80 CFATTACH_DECL_NEW(bochsfb, sizeof(struct bochsfb_softc),
     81 bochsfb_match, bochsfb_attach, NULL, NULL);
     82 
     83 struct wsdisplay_accessops bochsfb_accessops = {
     84 	bochsfb_ioctl,
     85 	bochsfb_mmap,
     86 	NULL,	/* alloc_screen */
     87 	NULL,	/* free_screen */
     88 	NULL,	/* show_screen */
     89 	NULL, 	/* load_font */
     90 	NULL,	/* pollc */
     91 	NULL	/* scroll */
     92 };
     93 
     94 static int
     95 bochsfb_match(device_t parent, cfdata_t match, void *aux)
     96 {
     97 	const struct pci_attach_args *pa = (const struct pci_attach_args *)aux;
     98 
     99 	/* This is a unauthorized PCI ID */
    100 	if ((PCI_VENDOR(pa->pa_id) == 0x1234) &&
    101 	    (PCI_PRODUCT(pa->pa_id) == 0x1111))
    102 		return 100;
    103 
    104 	return 0;
    105 }
    106 
    107 static void
    108 bochsfb_attach(device_t parent, device_t self, void *aux)
    109 {
    110 	struct bochsfb_softc *sc = device_private(self);
    111 	prop_dictionary_t dict = device_properties(self);
    112 	struct wsemuldisplaydev_attach_args ws_aa;
    113 	struct rasops_info *ri;
    114 	const struct pci_attach_args *pa = aux;
    115 	pcireg_t screg;
    116 	bool is_console = false;
    117 	long defattr;
    118 
    119 	sc->sc_pc = pa->pa_pc;
    120 	sc->sc_pcitag = pa->pa_tag;
    121 	sc->sc_dev = self;
    122 	sc->sc_pci_id = pa->pa_id;
    123 
    124 	pci_aprint_devinfo(pa, NULL);
    125 	prop_dictionary_get_bool(dict, "is_console", &is_console);
    126 
    127 	/*
    128 	 * Map VGA I/O and memory space.
    129 	 * First try to map framebuffer memory
    130 	 */
    131 	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM,
    132 			   BUS_SPACE_MAP_LINEAR, &sc->sc_memt,
    133 			   &sc->sc_fb_handle, &sc->sc_fb_addr,
    134 			   &sc->sc_fb_size) != 0) {
    135 		aprint_error_dev(sc->sc_dev, "failed to map framebuffer memory\n");
    136 		return;
    137 	}
    138 
    139 	/* Try to map MMIO region for the DISPI interface */
    140 	if (pci_mapreg_map(pa, PCI_MAPREG_START + 8, PCI_MAPREG_TYPE_MEM,
    141 			0, &sc->sc_mmiot, &sc->sc_mmioh, &sc->sc_mmio_addr,
    142 			&sc->sc_mmio_size) != 0) {
    143 
    144 		aprint_normal_dev(sc->sc_dev, "MMIO BAR not available, using I/O ports\n");
    145 		sc->sc_has_mmio = false;
    146 
    147 		/* I/O ports only exist if it's a VGA device*/
    148 		if (!(PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
    149 	     	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)) {
    150 			aprint_error_dev(sc->sc_dev,
    151 				"DISPI I/O port not available\n");
    152 			return;
    153 		}
    154 
    155 		/* Map DISPI I/O ports as fallback */
    156 		if (bus_space_map(pa->pa_iot, VBE_DISPI_IOPORT_INDEX,
    157 			4, 0, &sc->sc_ioh_dispi) != 0) {
    158 			aprint_error_dev(sc->sc_dev,
    159 				"couldn't map DISPI I/O ports\n");
    160 			return;
    161 		}
    162 
    163 		/* Map I/O space for VGA and Bochs DISPI interface */
    164 		if (bus_space_map(pa->pa_iot, VGA_IO_START, VGA_IO_SIZE, 0,
    165 			&sc->sc_ioh_vga) != 0) {
    166 			aprint_error_dev(sc->sc_dev, "couldn't map VGA I/O space\n");
    167 			return;
    168 		}
    169 		sc->sc_iot = pa->pa_iot;
    170 	} else {
    171 		aprint_normal_dev(sc->sc_dev, "using MMIO for DISPI interface\n");
    172 		sc->sc_has_mmio = true;
    173 	}
    174 
    175 	/* Enable memory and I/O space */
    176 	screg = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
    177 			      PCI_COMMAND_STATUS_REG);
    178 	screg |= PCI_COMMAND_MEM_ENABLE;
    179 	/* Avoid mess with legacy IO on secondary display */
    180 	if (!sc->sc_has_mmio)
    181 		screg |= PCI_COMMAND_IO_ENABLE;
    182 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, screg);
    183 
    184 
    185 	aprint_normal_dev(sc->sc_dev, "framebuffer at 0x%08lx, size %ld MB\n",
    186 			(long)sc->sc_fb_addr, (long)sc->sc_fb_size / (1024 * 1024));
    187 
    188 	/* Initialize the display */
    189 	if (!bochsfb_identify(sc)) {
    190 		aprint_error_dev(sc->sc_dev, "initialization failed\n");
    191 		return;
    192 	}
    193 
    194 	if (bochsfb_edid_mode(sc)) {
    195 		/* No EDID data, use default resolution */
    196 		sc->sc_width = BOCHSFB_DEFAULT_WIDTH;
    197 		sc->sc_height = BOCHSFB_DEFAULT_HEIGHT;
    198 	}
    199 
    200 	sc->sc_bpp = 32; /* 32 bbp */
    201 	sc->sc_linebytes = sc->sc_width * (sc->sc_bpp / 8);
    202 
    203 	aprint_normal_dev(sc->sc_dev, "setting %dx%d %d bpp resolution\n",
    204 				sc->sc_width, sc->sc_height, sc->sc_bpp);
    205 
    206 	if (!bochsfb_set_videomode(sc)) {
    207 		aprint_error_dev(sc->sc_dev, "couldn't set video mode\n");
    208 		return;
    209 	}
    210 	bochsfb_set_blanking(sc, WSDISPLAYIO_VIDEO_ON);
    211 
    212 	sc->sc_defaultscreen_descr = (struct wsscreen_descr){
    213 		"default",
    214 		0, 0,
    215 		NULL,
    216 		8, 16,
    217 		WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
    218 		NULL
    219 	};
    220 	sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
    221 	sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
    222 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
    223 
    224 	vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr,
    225 		  &bochsfb_accessops);
    226 	sc->vd.init_screen = bochsfb_identify_screen;
    227 
    228 	ri = &sc->sc_console_screen.scr_ri;
    229 
    230 	if (is_console) {
    231 		vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
    232 				  &defattr);
    233 
    234 		sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
    235 		vcons_redraw_screen(&sc->sc_console_screen);
    236 
    237 		sc->sc_defaultscreen_descr.textops = &ri->ri_ops;
    238 		sc->sc_defaultscreen_descr.capabilities = ri->ri_caps;
    239 		sc->sc_defaultscreen_descr.nrows = ri->ri_rows;
    240 		sc->sc_defaultscreen_descr.ncols = ri->ri_cols;
    241 
    242 		wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0,
    243 				   defattr);
    244 		vcons_replay_msgbuf(&sc->sc_console_screen);
    245 	} else {
    246 		if (sc->sc_console_screen.scr_ri.ri_rows == 0) {
    247 			vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
    248 					  &defattr);
    249 		} else
    250 			(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
    251 	}
    252 
    253 	ws_aa.console = is_console;
    254 	ws_aa.scrdata = &sc->sc_screenlist;
    255 	ws_aa.accessops = &bochsfb_accessops;
    256 	ws_aa.accesscookie = &sc->vd;
    257 
    258 	config_found(sc->sc_dev, &ws_aa, wsemuldisplaydevprint, CFARGS_NONE);
    259 }
    260 
    261 static void
    262 bochsfb_identify_screen(void *cookie, struct vcons_screen *scr, int existing,
    263 		    long *defattr)
    264 {
    265 	struct bochsfb_softc *sc = cookie;
    266 	struct rasops_info *ri = &scr->scr_ri;
    267 
    268 	wsfont_init();
    269 
    270 	ri->ri_depth = sc->sc_bpp;
    271 	ri->ri_width = sc->sc_width;
    272 	ri->ri_height = sc->sc_height;
    273 	ri->ri_stride = sc->sc_linebytes;
    274 	ri->ri_flg = RI_CENTER;
    275 
    276 	ri->ri_bits = bus_space_vaddr(sc->sc_memt, sc->sc_fb_handle);
    277 
    278 	ri->ri_rnum = 8;
    279 	ri->ri_gnum = 8;
    280 	ri->ri_bnum = 8;
    281 	ri->ri_rpos = 16;
    282 	ri->ri_gpos = 8;
    283 	ri->ri_bpos = 0;
    284 
    285 	scr->scr_flags |= VCONS_DONT_READ;
    286 
    287 	rasops_init(ri,
    288 		    ri->ri_height / 8,
    289 		    ri->ri_width / 8);
    290 
    291 	ri->ri_caps = WSSCREEN_WSCOLORS;
    292 
    293 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
    294 			ri->ri_width / ri->ri_font->fontwidth);
    295 
    296 	ri->ri_hw = scr;
    297 }
    298 
    299 /*
    300  * Write to the VBE DISPI interface
    301  */
    302 static void
    303 bochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg, uint16_t val)
    304 {
    305 	if (sc->sc_has_mmio) {
    306 		/* Use memory mapped I/O */
    307 		bus_space_write_2(sc->sc_mmiot, sc->sc_mmioh,
    308 				  BOCHSFB_MMIO_DISPI_OFFSET + reg * 2, val);
    309 	} else {
    310 		/* Use I/O ports */
    311 		bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi,
    312 			VBE_DISPI_IOPORT_INDEX - VBE_DISPI_IOPORT_INDEX, reg);
    313 		bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi,
    314 			VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX, val);
    315 	}
    316 }
    317 
    318 /*
    319 * Read from the VBE DISPI interface
    320 */
    321 static uint16_t
    322 bochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg)
    323 {
    324 	if (sc->sc_has_mmio) {
    325 		/* Use memory mapped I/O */
    326 		return bus_space_read_2(sc->sc_mmiot, sc->sc_mmioh,
    327 					BOCHSFB_MMIO_DISPI_OFFSET + reg * 2);
    328 	} else {
    329 		/* Use I/O ports */
    330 		bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi,
    331 				  VBE_DISPI_IOPORT_INDEX - VBE_DISPI_IOPORT_INDEX, reg);
    332 		return bus_space_read_2(sc->sc_iot, sc->sc_ioh_dispi,
    333 					VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX);
    334 	}
    335 }
    336 
    337 /*
    338 * Write to the VGA IO Ports
    339 */
    340 static void
    341 bochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg, uint8_t val)
    342 {
    343 	if (sc->sc_has_mmio) {
    344 		/* Use memory mapped I/O */
    345 		bus_space_write_1(sc->sc_mmiot, sc->sc_mmioh,
    346 				  reg - VGA_IO_START + BOCHSFB_MMIO_VGA_OFFSET,
    347 				  val);
    348 		return;
    349 	}
    350 
    351 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_vga, reg, val);
    352 }
    353 
    354 /*
    355 * Read from the VGA IO Ports
    356 */
    357 static uint8_t
    358 bochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg)
    359 {
    360 	if (sc->sc_has_mmio) {
    361 		/* Use memory mapped I/O */
    362 		return bus_space_read_1(sc->sc_mmiot, sc->sc_mmioh,
    363 					reg - VGA_IO_START + BOCHSFB_MMIO_VGA_OFFSET);
    364 	}
    365 
    366 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh_vga, reg);
    367 }
    368 
    369 /*
    370 * Identify the Bochs/QEMU display
    371 */
    372 static bool
    373 bochsfb_identify(struct bochsfb_softc *sc)
    374 {
    375 	/* Check for the Bochs display ID */
    376 	sc->sc_id = bochsfb_read_dispi(sc, VBE_DISPI_INDEX_ID);
    377 
    378 	if ((sc->sc_id & 0xFFF0) != VBE_DISPI_ID0) {
    379 		aprint_error_dev(sc->sc_dev,
    380 				 "invalid display ID 0x%04x\n", sc->sc_id);
    381 		return false;
    382 	}
    383 
    384 	aprint_normal_dev(sc->sc_dev, "Bochs display ID 0x%04x found\n",
    385 			  sc->sc_id);
    386 
    387 	return true;
    388 }
    389 
    390 static int bochsfb_edid_mode(struct bochsfb_softc *sc)
    391 {
    392 	int ret;
    393 
    394 	if (!sc->sc_has_mmio)
    395 		return -1;
    396 
    397 	/* VirtIO VGA is not coming with EDID support */
    398 	if (PCI_VENDOR(sc->sc_pci_id) == PCI_VENDOR_QUMRANET)
    399 		return -1;
    400 
    401 	/* Read EDID data */
    402 	bus_space_read_region_1(sc->sc_mmiot, sc->sc_mmioh,
    403 				BOCHSFB_MMIO_EDID_OFFSET,
    404 				sc->edid_buf,
    405 				BOCHSFB_MMIO_EDID_SIZE);
    406 
    407 	/* Parse EDID data */
    408 	ret = edid_parse(sc->edid_buf, &sc->sc_ei);
    409 
    410 	if (ret != 0) {
    411 		aprint_normal_dev(sc->sc_dev,
    412 			"failed to parse EDID data\n");
    413 		return ret;
    414 	}
    415 
    416 	/* Get the preferred mode */
    417 	if (!sc->sc_ei.edid_preferred_mode) {
    418 		aprint_normal_dev(sc->sc_dev,
    419 			"no preferred mode found in EDID data\n");
    420 		return -1;
    421 	}
    422 
    423 	/* Set the preferred mode */
    424 	sc->sc_width = sc->sc_ei.edid_preferred_mode->hdisplay;
    425 	sc->sc_height = sc->sc_ei.edid_preferred_mode->vdisplay;
    426 
    427 	return 0;
    428 }
    429 
    430 /*
    431 * Set video mode using the Bochs interface
    432 */
    433 static bool
    434 bochsfb_set_videomode(struct bochsfb_softc *sc)
    435 {
    436 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_ENABLE, 0);
    437 
    438 	/* Set resolution and bit depth */
    439 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_BPP, sc->sc_bpp);
    440 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_XRES, sc->sc_width);
    441 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_YRES, sc->sc_height);
    442 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_BANK, 0);
    443 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_VIRT_WIDTH, sc->sc_width);
    444 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_VIRT_HEIGHT, sc->sc_height);
    445 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_X_OFFSET, 0);
    446 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_Y_OFFSET, 0);
    447 
    448 	/* Re-enable with linear frame buffer */
    449 	bochsfb_write_dispi(sc, VBE_DISPI_INDEX_ENABLE,
    450 			    	VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
    451 
    452 	return true;
    453 }
    454 
    455 static void
    456 bochsfb_set_blanking(struct bochsfb_softc *sc, int blank)
    457 {
    458 	bochsfb_write_vga(sc, 0x3C2, 0x01);
    459 	(void)bochsfb_read_vga(sc, 0x3DA);
    460 
    461 	if (blank == WSDISPLAYIO_VIDEO_OFF) {
    462 		bochsfb_write_vga(sc, 0x3C0, 0x00);
    463 	} else {
    464 		bochsfb_write_vga(sc, 0x3C0, 0x20);
    465 	}
    466 	sc->sc_blank = blank;
    467 }
    468 
    469 static int
    470 bochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
    471 {
    472 	struct vcons_data *vd;
    473 	struct bochsfb_softc *sc;
    474 	struct wsdisplay_fbinfo *wsfbi;
    475 	struct vcons_screen *ms;
    476 
    477 	vd = v;
    478 	sc = vd->cookie;
    479 	ms = vd->active;
    480 
    481 	switch (cmd) {
    482 	case WSDISPLAYIO_GTYPE:
    483 		*(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
    484 		return 0;
    485 
    486 	case PCI_IOC_CFGREAD:
    487 	case PCI_IOC_CFGWRITE:
    488 		return pci_devioctl(sc->sc_pc, sc->sc_pcitag,
    489 				    cmd, data, flag, l);
    490 
    491 	case WSDISPLAYIO_GET_BUSID:
    492 		return wsdisplayio_busid_pci(sc->sc_dev, sc->sc_pc,
    493 					     sc->sc_pcitag, data);
    494 
    495 	case WSDISPLAYIO_GINFO:
    496 		if (ms == NULL)
    497 			return ENODEV;
    498 
    499 		wsfbi = (void *)data;
    500 		wsfbi->height = ms->scr_ri.ri_height;
    501 		wsfbi->width = ms->scr_ri.ri_width;
    502 		wsfbi->depth = ms->scr_ri.ri_depth;
    503 		wsfbi->cmsize = 0; /* No color map */
    504 		return 0;
    505 
    506 	case WSDISPLAYIO_LINEBYTES:
    507 		*(u_int *)data = sc->sc_linebytes;
    508 		return 0;
    509 
    510 	case WSDISPLAYIO_SMODE:
    511 		{
    512 			int new_mode = *(int *)data;
    513 			if (new_mode != sc->sc_mode) {
    514 				sc->sc_mode = new_mode;
    515 				if (new_mode == WSDISPLAYIO_MODE_EMUL) {
    516 					vcons_redraw_screen(ms);
    517 				}
    518 			}
    519 			return 0;
    520 		}
    521 	case WSDISPLAYIO_GET_FBINFO:
    522 		{
    523 			struct wsdisplayio_fbinfo *fbi = data;
    524 			struct rasops_info *ri;
    525 			int ret;
    526 
    527 			ri = &sc->vd.active->scr_ri;
    528 			ret = wsdisplayio_get_fbinfo(ri, fbi);
    529 			return ret;
    530 		}
    531 	case WSDISPLAYIO_GVIDEO:
    532 		*(int *)data = sc->sc_blank;
    533 		return 0;
    534 	case WSDISPLAYIO_SVIDEO:
    535 		bochsfb_set_blanking(sc, *(int *)data);
    536 		return 0;
    537 	case WSDISPLAYIO_GET_EDID:
    538 		{
    539 			struct wsdisplayio_edid_info *d = data;
    540 			return wsdisplayio_get_edid(sc->sc_dev, d);
    541 		}
    542 	}
    543 
    544 
    545 	return EPASSTHROUGH;
    546 }
    547 
    548 static paddr_t
    549 bochsfb_mmap(void *v, void *vs, off_t offset, int prot)
    550 {
    551 	struct vcons_data *vd;
    552 	struct bochsfb_softc *sc;
    553 	paddr_t pa;
    554 
    555 	vd = v;
    556 	sc = vd->cookie;
    557 
    558 	if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB) {
    559 		if (offset < sc->sc_fb_size) {
    560 			pa = bus_space_mmap(sc->sc_memt, sc->sc_fb_addr + offset, 0,
    561 					prot, BUS_SPACE_MAP_LINEAR);
    562 			return pa;
    563 		}
    564 	} else if (sc->sc_mode == WSDISPLAYIO_MODE_MAPPED) {
    565 		if (kauth_authorize_machdep(kauth_cred_get(),
    566 		    KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) {
    567 			aprint_error_dev(sc->sc_dev, "mmap() rejected.\n");
    568 			return -1;
    569 		}
    570 
    571 		if ((offset >= sc->sc_fb_addr) &&
    572 		    (offset < sc->sc_fb_addr + sc->sc_fb_size)) {
    573 			pa = bus_space_mmap(sc->sc_memt, offset, 0, prot,
    574 					    BUS_SPACE_MAP_LINEAR);
    575 			return pa;
    576 		}
    577 
    578 		if (sc->sc_has_mmio &&
    579 		    (offset >= sc->sc_mmio_addr) &&
    580 		    (offset < sc->sc_mmio_addr + sc->sc_mmio_size)) {
    581 			pa = bus_space_mmap(sc->sc_mmiot, offset, 0, prot,
    582 					    BUS_SPACE_MAP_LINEAR);
    583 			return pa;
    584 		}
    585 
    586 #ifdef PCI_MAGIC_IO_RANGE
    587 		/* allow mapping of IO space */
    588 		if ((offset >= PCI_MAGIC_IO_RANGE) &&
    589 		    (offset < PCI_MAGIC_IO_RANGE + 0x10000)) {
    590 			pa = bus_space_mmap(sc->sc_iot,
    591 			    offset - PCI_MAGIC_IO_RANGE, 0, prot, 0);
    592 			return pa;
    593 		}
    594 #endif
    595 	}
    596 
    597 	return -1;
    598 }
    599