bochsfb.c revision 1.1
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 59static int bochsfb_match(device_t, cfdata_t, void *); 60static void bochsfb_attach(device_t, device_t, void *); 61 62static void bochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg, 63 uint16_t val); 64static uint16_t bochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg); 65static void bochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg, 66 uint8_t val); 67static uint8_t bochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg); 68static void bochsfb_set_blanking(struct bochsfb_softc *sc, int blank); 69 70static bool bochsfb_identify(struct bochsfb_softc *sc); 71static int bochsfb_edid_mode(struct bochsfb_softc *sc); 72static bool bochsfb_set_videomode(struct bochsfb_softc *sc); 73 74static paddr_t bochsfb_mmap(void *v, void *vs, off_t offset, int prot); 75static int bochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 76 struct lwp *l); 77static void bochsfb_identify_screen(void *cookie, struct vcons_screen *scr, 78 int existing, long *defattr); 79 80CFATTACH_DECL_NEW(bochsfb, sizeof(struct bochsfb_softc), 81bochsfb_match, bochsfb_attach, NULL, NULL); 82 83struct 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 94static int 95bochsfb_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 107static void 108bochsfb_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 261static void 262bochsfb_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 */ 302static void 303bochsfb_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*/ 321static uint16_t 322bochsfb_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*/ 340static void 341bochsfb_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*/ 357static uint8_t 358bochsfb_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*/ 372static bool 373bochsfb_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 390static 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*/ 433static bool 434bochsfb_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 455static void 456bochsfb_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 469static int 470bochsfb_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 548static paddr_t 549bochsfb_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