From 78e2971fc009e13f3b5f7fd3110ff9e038c7296e Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Fri, 9 May 2025 12:42:31 +0100 Subject: [PATCH] sys/dev/pci: New bochsfb driver Signed-off-by: Jiaxun Yang --- share/man/man4/Makefile | 2 +- share/man/man4/bochsfb.4 | 77 +++++ sys/arch/evbarm/conf/GENERIC | 1 + sys/arch/evbarm/conf/GENERIC64 | 1 + sys/dev/pci/bochsfb.c | 610 +++++++++++++++++++++++++++++++++ sys/dev/pci/bochsfbreg.h | 87 +++++ sys/dev/pci/bochsfbvar.h | 84 +++++ sys/dev/pci/files.pci | 4 + 8 files changed, 865 insertions(+), 1 deletion(-) create mode 100644 share/man/man4/bochsfb.4 create mode 100644 sys/dev/pci/bochsfb.c create mode 100644 sys/dev/pci/bochsfbreg.h create mode 100644 sys/dev/pci/bochsfbvar.h diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 0a87caf8dc0d2..843a4f5a87dc8 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -11,7 +11,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ auacer.4 audio.4 audiocs.4 auich.4 \ auixp.4 autri.4 auvia.4 awi.4 \ battery_pmu.4 bba.4 bce.4 bcsp.4 be.4 bge.4 bnx.4 bha.4 \ - bio.4 bktr.4 bluetooth.4 bmx280thp.4 bmtphy.4 bpf.4 bpfjit.4 \ + bio.4 bktr.4 bochsfb.4 bluetooth.4 bmx280thp.4 bmtphy.4 bpf.4 bpfjit.4 \ brgphy.4 bridge.4 bthidev.4 bthub.4 btkbd.4 \ btmagic.4 btms.4 btsco.4 btuart.4 \ bwfm.4 bwi.4 \ diff --git a/share/man/man4/bochsfb.4 b/share/man/man4/bochsfb.4 new file mode 100644 index 0000000000000..ebbce58deedbb --- /dev/null +++ b/share/man/man4/bochsfb.4 @@ -0,0 +1,77 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2025 +.\" The NetBSD Foundation, Inc. All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jiaxun Yang. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd March 2, 2025 +.Dt BOCHSFB 4 +.Os +.Sh NAME +.Nm bochsfb +.Nd Bochs Display Interface framebuffer +.Sh SYNOPSIS +.Cd "bochsfb* at pci?" +.Cd "wsdisplay* at bochsfb?" +.Sh DESCRIPTION +The +.Nm +driver provides support for graphics devices implementing the Bochs VBE +DISPI interface, including the Bochs reference VGA implementation, the QEMU +StdVGA device, and the VGA-compatible +.Xr virtio 4 +GPUs. +It programs the linear framebuffer through the DISPI registers and makes it +available through +.Xr wsdisplay 4 +as a console or +.Xr wsfb 4 +-compatible framebuffer. +.Pp +When a memory-mapped DISPI bar with an EDID block is present, +.Nm +uses the preferred mode reported by the display. +Devices without EDID support (such as virtio-vga) fall back to a 1024x768 +32-bit mode. +If no MMIO bar is available, the driver accesses the DISPI registers via the +legacy VGA I/O ports. +.Sh SEE ALSO +.Xr intro 4 , +.Xr pci 4 , +.Xr vga 4 , +.Xr wscons 4 , +.Xr wsdisplay 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Nx 11 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Jiaxun Yang Aq Mt jiaxun.yang@flygoat.com . diff --git a/sys/arch/evbarm/conf/GENERIC b/sys/arch/evbarm/conf/GENERIC index 7dbb4d2aa6ca9..5a704339c5634 100644 --- a/sys/arch/evbarm/conf/GENERIC +++ b/sys/arch/evbarm/conf/GENERIC @@ -522,6 +522,7 @@ hdmicec* at hdmicecbus? #tegradrm* at fdt? # NVIDIA Tegra Display #tegrafb* at tegrafbbus? dwhdmi* at fdt? # Designware HDMI TX +bochsfb* at pci? dev ? function ? # Bochs framebuffer (QEMU) genfb* at fdt? # Simple Framebuffer mesonfb* at fdt? # Amlogic Meson Framebuffer omapfb* at fdt? # TI OMAP3 Framebuffer diff --git a/sys/arch/evbarm/conf/GENERIC64 b/sys/arch/evbarm/conf/GENERIC64 index a15ed057dfb3d..af5e53be919fe 100644 --- a/sys/arch/evbarm/conf/GENERIC64 +++ b/sys/arch/evbarm/conf/GENERIC64 @@ -508,6 +508,7 @@ sunxilcdc* at fdt? # Allwinner DE2 timing controller sunximixer* at fdt? # Allwinner DE2 mixer #tegradrm* at fdt? # NVIDIA Tegra Display #tegrafb* at tegrafbbus? +bochsfb* at pci? dev ? function ? # Bochs framebuffer (QEMU) genfb* at fdt? # Simple Framebuffer wsdisplay* at wsemuldisplaydev? options VCONS_DRAW_INTR diff --git a/sys/dev/pci/bochsfb.c b/sys/dev/pci/bochsfb.c new file mode 100644 index 0000000000000..bfa45beb8bd28 --- /dev/null +++ b/sys/dev/pci/bochsfb.c @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Generic driver for Bochs Display Interface (DISPI) based devices: + * - Bochs VBE/VGA interface + * - QEMU standard VGA + * - QEMU virtio-vga series GPU + * + * This driver supports both MMIO and I/O port access methods. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "opt_wsemul.h" + +static int bochsfb_match(device_t, cfdata_t, void *); +static void bochsfb_attach(device_t, device_t, void *); + +static void bochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg, + uint16_t val); +static uint16_t bochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg); +static void bochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg, + uint8_t val); +static uint8_t bochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg); +static void bochsfb_set_blanking(struct bochsfb_softc *sc, int blank); + +static bool bochsfb_identify(struct bochsfb_softc *sc); +static int bochsfb_edid_mode(struct bochsfb_softc *sc); +static bool bochsfb_set_videomode(struct bochsfb_softc *sc); + +static paddr_t bochsfb_mmap(void *v, void *vs, off_t offset, int prot); +static int bochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, + struct lwp *l); +static void bochsfb_identify_screen(void *cookie, struct vcons_screen *scr, + int existing, long *defattr); + +CFATTACH_DECL_NEW(bochsfb, sizeof(struct bochsfb_softc), +bochsfb_match, bochsfb_attach, NULL, NULL); + +struct wsdisplay_accessops bochsfb_accessops = { + bochsfb_ioctl, + bochsfb_mmap, + NULL, /* alloc_screen */ + NULL, /* free_screen */ + NULL, /* show_screen */ + NULL, /* load_font */ + NULL, /* pollc */ + NULL /* scroll */ +}; + +static int +bochsfb_match(device_t parent, cfdata_t match, void *aux) +{ + const struct pci_attach_args *pa = (const struct pci_attach_args *)aux; + + /* This is a unauthorized PCI ID */ + if ((PCI_VENDOR(pa->pa_id) == 0x1234) && + (PCI_PRODUCT(pa->pa_id) == 0x1111)) + return 100; + + + /* Match QEMU VirtIO GPU */ + if ((PCI_VENDOR(pa->pa_id) == PCI_VENDOR_QUMRANET) && + (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_QUMRANET_VIRTIO_1050)) { + /* Only those comes with Legacy VGA support have BOCHS interface */ + if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA) { + /* XXX: Reduce once we have proper virtio driver */ + return 100; + } + } + + return 0; +} + +static void +bochsfb_attach(device_t parent, device_t self, void *aux) +{ + struct bochsfb_softc *sc = device_private(self); + prop_dictionary_t dict = device_properties(self); + struct wsemuldisplaydev_attach_args ws_aa; + struct rasops_info *ri; + const struct pci_attach_args *pa = aux; + pcireg_t screg; + bool is_console = false; + long defattr; + + sc->sc_pc = pa->pa_pc; + sc->sc_pcitag = pa->pa_tag; + sc->sc_dev = self; + sc->sc_pci_id = pa->pa_id; + + pci_aprint_devinfo(pa, NULL); + prop_dictionary_get_bool(dict, "is_console", &is_console); + + /* + * Map VGA I/O and memory space. + * First try to map framebuffer memory + */ + if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, + BUS_SPACE_MAP_LINEAR, &sc->sc_memt, + &sc->sc_fb_handle, &sc->sc_fb_addr, + &sc->sc_fb_size) != 0) { + aprint_error_dev(sc->sc_dev, "failed to map framebuffer memory\n"); + return; + } + + /* Try to map MMIO region for the DISPI interface */ + if (pci_mapreg_map(pa, PCI_MAPREG_START + 8, PCI_MAPREG_TYPE_MEM, + 0, &sc->sc_mmiot, &sc->sc_mmioh, &sc->sc_mmio_addr, + &sc->sc_mmio_size) != 0) { + + aprint_normal_dev(sc->sc_dev, "MMIO BAR not available, using I/O ports\n"); + sc->sc_has_mmio = false; + + /* I/O ports only exist if it's a VGA device*/ + if (!(PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)) { + aprint_error_dev(sc->sc_dev, + "DISPI I/O port not available\n"); + return; + } + + /* Map DISPI I/O ports as fallback */ + if (bus_space_map(pa->pa_iot, VBE_DISPI_IOPORT_INDEX, + 4, 0, &sc->sc_ioh_dispi) != 0) { + aprint_error_dev(sc->sc_dev, + "couldn't map DISPI I/O ports\n"); + return; + } + + /* Map I/O space for VGA and Bochs DISPI interface */ + if (bus_space_map(pa->pa_iot, VGA_IO_START, VGA_IO_SIZE, 0, + &sc->sc_ioh_vga) != 0) { + aprint_error_dev(sc->sc_dev, "couldn't map VGA I/O space\n"); + return; + } + sc->sc_iot = pa->pa_iot; + } else { + aprint_normal_dev(sc->sc_dev, "using MMIO for DISPI interface\n"); + sc->sc_has_mmio = true; + } + + /* Enable memory and I/O space */ + screg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, + PCI_COMMAND_STATUS_REG); + screg |= PCI_COMMAND_MEM_ENABLE; + /* Avoid mess with legacy IO on secondary display */ + if (!sc->sc_has_mmio) + screg |= PCI_COMMAND_IO_ENABLE; + pci_conf_write(sc->sc_pc, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, screg); + + + aprint_normal_dev(sc->sc_dev, "framebuffer at 0x%08lx, size %ld MB\n", + (long)sc->sc_fb_addr, (long)sc->sc_fb_size / (1024 * 1024)); + + /* Initialize the display */ + if (!bochsfb_identify(sc)) { + aprint_error_dev(sc->sc_dev, "initialization failed\n"); + return; + } + + if (bochsfb_edid_mode(sc)) { + /* No EDID data, use default resolution */ + sc->sc_width = 1024; + sc->sc_height = 768; + } + + sc->sc_bpp = 32; /* 32 bbp */ + sc->sc_linebytes = sc->sc_width * (sc->sc_bpp / 8); + + aprint_normal_dev(sc->sc_dev, "setting %dx%d %d bpp resolution\n", + sc->sc_width, sc->sc_height, sc->sc_bpp); + + if (!bochsfb_set_videomode(sc)) { + aprint_error_dev(sc->sc_dev, "couldn't set video mode\n"); + return; + } + bochsfb_set_blanking(sc, WSDISPLAYIO_VIDEO_ON); + + sc->sc_defaultscreen_descr = (struct wsscreen_descr){ + "default", + 0, 0, + NULL, + 8, 16, + WSSCREEN_WSCOLORS | WSSCREEN_HILIT, + NULL + }; + sc->sc_screens[0] = &sc->sc_defaultscreen_descr; + sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; + sc->sc_mode = WSDISPLAYIO_MODE_EMUL; + + vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, + &bochsfb_accessops); + sc->vd.init_screen = bochsfb_identify_screen; + + ri = &sc->sc_console_screen.scr_ri; + + if (is_console) { + vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, + &defattr); + + sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; + vcons_redraw_screen(&sc->sc_console_screen); + + sc->sc_defaultscreen_descr.textops = &ri->ri_ops; + sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; + sc->sc_defaultscreen_descr.nrows = ri->ri_rows; + sc->sc_defaultscreen_descr.ncols = ri->ri_cols; + + wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, + defattr); + vcons_replay_msgbuf(&sc->sc_console_screen); + } else { + if (sc->sc_console_screen.scr_ri.ri_rows == 0) { + vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, + &defattr); + } else + (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); + } + + ws_aa.console = is_console; + ws_aa.scrdata = &sc->sc_screenlist; + ws_aa.accessops = &bochsfb_accessops; + ws_aa.accesscookie = &sc->vd; + + config_found(sc->sc_dev, &ws_aa, wsemuldisplaydevprint, CFARGS_NONE); +} + +static void +bochsfb_identify_screen(void *cookie, struct vcons_screen *scr, int existing, + long *defattr) +{ + struct bochsfb_softc *sc = cookie; + struct rasops_info *ri = &scr->scr_ri; + + wsfont_init(); + + ri->ri_depth = sc->sc_bpp; + ri->ri_width = sc->sc_width; + ri->ri_height = sc->sc_height; + ri->ri_stride = sc->sc_linebytes; + ri->ri_flg = RI_CENTER; + + ri->ri_bits = bus_space_vaddr(sc->sc_memt, sc->sc_fb_handle); + + ri->ri_rnum = 8; + ri->ri_gnum = 8; + ri->ri_bnum = 8; + ri->ri_rpos = 16; + ri->ri_gpos = 8; + ri->ri_bpos = 0; + + scr->scr_flags |= VCONS_DONT_READ; + + rasops_init(ri, + ri->ri_height / 8, + ri->ri_width / 8); + + ri->ri_caps = WSSCREEN_WSCOLORS; + + rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, + ri->ri_width / ri->ri_font->fontwidth); + + ri->ri_hw = scr; +} + +/* +* Write to the VBE DISPI interface +*/ +static void +bochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg, uint16_t val) +{ + if (sc->sc_has_mmio) { + /* Use memory mapped I/O */ + bus_space_write_2(sc->sc_mmiot, sc->sc_mmioh, + BOCHSFB_MMIO_DISPI_OFFSET + reg * 2, val); + } else { + /* Use I/O ports */ + bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi, + VBE_DISPI_IOPORT_INDEX - VBE_DISPI_IOPORT_INDEX, reg); + bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi, + VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX, val); + } +} + +/* +* Read from the VBE DISPI interface +*/ +static uint16_t +bochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg) +{ + if (sc->sc_has_mmio) { + /* Use memory mapped I/O */ + return bus_space_read_2(sc->sc_mmiot, sc->sc_mmioh, + BOCHSFB_MMIO_DISPI_OFFSET + reg * 2); + } else { + /* Use I/O ports */ + bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi, + VBE_DISPI_IOPORT_INDEX - VBE_DISPI_IOPORT_INDEX, reg); + return bus_space_read_2(sc->sc_iot, sc->sc_ioh_dispi, + VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX); + } +} + +/* +* Write to the VGA IO Ports +*/ +static void +bochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg, uint8_t val) +{ + if (sc->sc_has_mmio) { + /* Use memory mapped I/O */ + bus_space_write_1(sc->sc_mmiot, sc->sc_mmioh, + reg - VGA_IO_START + BOCHSFB_MMIO_VGA_OFFSET, + val); + return; + } + + bus_space_write_1(sc->sc_iot, sc->sc_ioh_vga, reg, val); +} + +/* +* Read from the VGA IO Ports +*/ +static uint8_t +bochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg) +{ + if (sc->sc_has_mmio) { + /* Use memory mapped I/O */ + return bus_space_read_1(sc->sc_mmiot, sc->sc_mmioh, + reg - VGA_IO_START + BOCHSFB_MMIO_VGA_OFFSET); + } + + return bus_space_read_1(sc->sc_iot, sc->sc_ioh_vga, reg); +} + +/* +* Identify the Bochs/QEMU display +*/ +static bool +bochsfb_identify(struct bochsfb_softc *sc) +{ + /* Check for the Bochs display ID */ + sc->sc_id = bochsfb_read_dispi(sc, VBE_DISPI_INDEX_ID); + + if ((sc->sc_id & 0xFFF0) != VBE_DISPI_ID0) { + aprint_error_dev(sc->sc_dev, + "invalid display ID 0x%04x\n", sc->sc_id); + return false; + } + + aprint_normal_dev(sc->sc_dev, "Bochs display ID 0x%04x found\n", + sc->sc_id); + + return true; +} + +static int bochsfb_edid_mode(struct bochsfb_softc *sc) +{ + int ret; + + if (!sc->sc_has_mmio) + return -1; + + /* VirtIO VGA is not coming with EDID support */ + if (PCI_VENDOR(sc->sc_pci_id) == PCI_VENDOR_QUMRANET) + return -1; + + /* Read EDID data */ + bus_space_read_region_1(sc->sc_mmiot, sc->sc_mmioh, + BOCHSFB_MMIO_EDID_OFFSET, + sc->edid_buf, + BOCHSFB_MMIO_EDID_SIZE); + + /* Parse EDID data */ + ret = edid_parse(sc->edid_buf, &sc->sc_ei); + + if (ret != 0) { + aprint_normal_dev(sc->sc_dev, + "failed to parse EDID data\n"); + return ret; + } + + /* Get the preferred mode */ + if (!sc->sc_ei.edid_preferred_mode) { + aprint_normal_dev(sc->sc_dev, + "no preferred mode found in EDID data\n"); + return -1; + } + + /* Set the preferred mode */ + sc->sc_width = sc->sc_ei.edid_preferred_mode->hdisplay; + sc->sc_height = sc->sc_ei.edid_preferred_mode->vdisplay; + + return 0; +} + +/* +* Set video mode using the Bochs interface +*/ +static bool +bochsfb_set_videomode(struct bochsfb_softc *sc) +{ + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_ENABLE, 0); + + /* Set resolution and bit depth */ + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_BPP, sc->sc_bpp); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_XRES, sc->sc_width); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_YRES, sc->sc_height); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_BANK, 0); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_VIRT_WIDTH, sc->sc_width); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_VIRT_HEIGHT, sc->sc_height); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_X_OFFSET, 0); + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_Y_OFFSET, 0); + + /* Re-enable with linear frame buffer */ + bochsfb_write_dispi(sc, VBE_DISPI_INDEX_ENABLE, + VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); + + return true; +} + +static void +bochsfb_set_blanking(struct bochsfb_softc *sc, int blank) +{ + bochsfb_write_vga(sc, 0x3C2, 0x01); + (void)bochsfb_read_vga(sc, 0x3DA); + + if (blank == WSDISPLAYIO_VIDEO_OFF) { + bochsfb_write_vga(sc, 0x3C0, 0x00); + } else { + bochsfb_write_vga(sc, 0x3C0, 0x20); + } + sc->sc_blank = blank; +} + +static int +bochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) +{ + struct vcons_data *vd; + struct bochsfb_softc *sc; + struct wsdisplay_fbinfo *wsfbi; + struct vcons_screen *ms; + + vd = v; + sc = vd->cookie; + ms = vd->active; + + switch (cmd) { + case WSDISPLAYIO_GTYPE: + *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; + return 0; + + case PCI_IOC_CFGREAD: + case PCI_IOC_CFGWRITE: + return pci_devioctl(sc->sc_pc, sc->sc_pcitag, + cmd, data, flag, l); + + case WSDISPLAYIO_GET_BUSID: + return wsdisplayio_busid_pci(sc->sc_dev, sc->sc_pc, + sc->sc_pcitag, data); + + case WSDISPLAYIO_GINFO: + if (ms == NULL) + return ENODEV; + + wsfbi = (void *)data; + wsfbi->height = ms->scr_ri.ri_height; + wsfbi->width = ms->scr_ri.ri_width; + wsfbi->depth = ms->scr_ri.ri_depth; + wsfbi->cmsize = 0; /* No color map */ + return 0; + + case WSDISPLAYIO_LINEBYTES: + *(u_int *)data = sc->sc_linebytes; + return 0; + + case WSDISPLAYIO_SMODE: + { + int new_mode = *(int *)data; + if (new_mode != sc->sc_mode) { + sc->sc_mode = new_mode; + if (new_mode == WSDISPLAYIO_MODE_EMUL) { + vcons_redraw_screen(ms); + } + } + return 0; + } + case WSDISPLAYIO_GET_FBINFO: + { + struct wsdisplayio_fbinfo *fbi = data; + struct rasops_info *ri; + int ret; + + ri = &sc->vd.active->scr_ri; + ret = wsdisplayio_get_fbinfo(ri, fbi); + return ret; + } + case WSDISPLAYIO_GVIDEO: + *(int *)data = sc->sc_blank; + return 0; + case WSDISPLAYIO_SVIDEO: + bochsfb_set_blanking(sc, *(int *)data); + return 0; + case WSDISPLAYIO_GET_EDID: + { + struct wsdisplayio_edid_info *d = data; + return wsdisplayio_get_edid(sc->sc_dev, d); + } + } + + + return EPASSTHROUGH; +} + +static paddr_t +bochsfb_mmap(void *v, void *vs, off_t offset, int prot) +{ + struct vcons_data *vd; + struct bochsfb_softc *sc; + paddr_t pa; + + vd = v; + sc = vd->cookie; + + if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB) { + if (offset < sc->sc_fb_size) { + pa = bus_space_mmap(sc->sc_memt, sc->sc_fb_addr + offset, 0, + prot, BUS_SPACE_MAP_LINEAR); + return pa; + } + } else if (sc->sc_mode == WSDISPLAYIO_MODE_MAPPED) { + if (kauth_authorize_machdep(kauth_cred_get(), + KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) { + aprint_error_dev(sc->sc_dev, "mmap() rejected.\n"); + return -1; + } + + if ((offset >= sc->sc_fb_addr) && + (offset < sc->sc_fb_addr + sc->sc_fb_size)) { + pa = bus_space_mmap(sc->sc_memt, offset, 0, prot, + BUS_SPACE_MAP_LINEAR); + return pa; + } + + if (sc->sc_has_mmio && + (offset >= sc->sc_mmio_addr) && + (offset < sc->sc_mmio_addr + sc->sc_mmio_size)) { + pa = bus_space_mmap(sc->sc_mmiot, offset, 0, prot, + BUS_SPACE_MAP_LINEAR); + return pa; + } + +#ifdef PCI_MAGIC_IO_RANGE + /* allow mapping of IO space */ + if ((offset >= PCI_MAGIC_IO_RANGE) && + (offset < PCI_MAGIC_IO_RANGE + 0x10000)) { + pa = bus_space_mmap(sc->sc_iot, + offset - PCI_MAGIC_IO_RANGE, 0, prot, 0); + return pa; + } +#endif + } + + return -1; +} diff --git a/sys/dev/pci/bochsfbreg.h b/sys/dev/pci/bochsfbreg.h new file mode 100644 index 0000000000000..6286fb77236c0 --- /dev/null +++ b/sys/dev/pci/bochsfbreg.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Bochs DISPI interface register definitions + * See: + * https://wiki.osdev.org/Bochs_VBE_Extensions + * https://www.qemu.org/docs/master/specs/standard-vga.html + */ + +#ifndef BOCHSFBREG_H +#define BOCHSFBREG_H + +/* Standard VGA I/O ports */ +#define VGA_IO_START 0x3C0 +#define VGA_IO_SIZE 0x20 + +/* VGA registers we access */ +#define VGA_CRTC_INDEX 0x3D4 +#define VGA_CRTC_DATA 0x3D5 + +/* Bochs VBE DISPI interface I/O ports */ +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#if defined(__i386__) || defined(__x86_64__) +#define VBE_DISPI_IOPORT_DATA 0x01CF +#else +#define VBE_DISPI_IOPORT_DATA 0x01D0 +#endif + +/* Bochs VBE DISPI interface MMIO bar and offset */ +#define BOCHSFB_MMIO_BAR 0x14 +#define BOCHSFB_MMIO_EDID_OFFSET 0x000 +#define BOCHSFB_MMIO_EDID_SIZE 0x400 +#define BOCHSFB_MMIO_VGA_OFFSET 0x400 +#define BOCHSFB_MMIO_DISPI_OFFSET 0x500 + +/* VBE DISPI interface indices */ +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa + +/* VBE DISPI interface ID values */ +#define VBE_DISPI_ID0 0xB0C0 /* Magic value for first 12 bits */ +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +/* VBE DISPI interface enable values */ +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +#endif /* BOCHSFBREG_H */ diff --git a/sys/dev/pci/bochsfbvar.h b/sys/dev/pci/bochsfbvar.h new file mode 100644 index 0000000000000..fcda298ef81d8 --- /dev/null +++ b/sys/dev/pci/bochsfbvar.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BOCHSFBVAR_H +#define BOCHSFBVAR_H + +#include +#include +#include +#include +#include +#include + +/* Structure for the Bochs FB driver */ +struct bochsfb_softc { + device_t sc_dev; + + /* PCI attachment */ + pci_chipset_tag_t sc_pc; + pcitag_t sc_pcitag; + + /* Bus space tags and handles */ + bus_space_tag_t sc_iot; /* I/O space tag */ + bus_space_handle_t sc_ioh_vga; /* VGA I/O handle */ + bus_space_handle_t sc_ioh_dispi; /* DISPI I/O handle */ + + bus_space_tag_t sc_mmiot; /* MMIO space tag */ + bus_space_handle_t sc_mmioh; /* MMIO handle for DISPI interface */ + + bus_space_tag_t sc_memt; /* Memory space tag */ + bus_space_handle_t sc_fb_handle; /* Framebuffer handle */ + + bus_addr_t sc_mmio_addr; /* MMIO base address */ + bus_size_t sc_mmio_size; /* MMIO size */ + + bus_addr_t sc_fb_addr; /* Framebuffer physical address */ + bus_size_t sc_fb_size; /* Framebuffer size */ + + bool sc_has_mmio; /* Whether MMIO is available */ + + /* Device Info */ + uint16_t sc_id; /* Device ID */ + pcireg_t sc_pci_id; /* PCI ID */ + + /* Video mode parameters */ + int sc_width, sc_height, sc_linebytes, sc_bpp; + const struct videomode *sc_videomode; + + uint8_t edid_buf[0x400]; /* EDID data buffer */ + struct edid_info sc_ei; + + /* WS Display data */ + int sc_blank; + int sc_mode; + struct vcons_screen sc_console_screen; + struct vcons_data vd; + struct wsscreen_descr sc_defaultscreen_descr; + const struct wsscreen_descr *sc_screens[1]; + struct wsscreen_list sc_screenlist; +}; + + #endif /* BOCHSFBVAR_H */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 6957b63793669..dd70917fd3ee6 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1225,3 +1225,7 @@ device rge: ether, ifnet, arp, mii attach rge at pci file dev/pci/if_rge.c rge +# Bochs compatible framebuffer +device bochsfb: wsemuldisplaydev, rasops32, fb, vcons, videomode, edid +attach bochsfb at pci +file dev/pci/bochsfb.c bochsfb