NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/58681: [PATCH] Port virtio GPU driver from OpenBSD



>Number:         58681
>Category:       kern
>Synopsis:       [PATCH] Port virtio GPU driver from OpenBSD
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Sep 19 23:15:00 +0000 2024
>Originator:     gorg%gorgnet.net@localhost
>Release:        NetBSD 10.99.12
>Organization:
>Environment:
System: NetBSD 10.99.12: Wed Sep 18 15:14:20 MDT 2024 evbarm
Architecture: aarch64
Machine: evbarm
>Description:
This change adds a driver from OpenBSD for virtio GPU devices. Unlike the
driver in OpenBSD, this driver does not update the screen every 10
milliseconds, instead inserting wrappers into rasops to only update the screen
when necessary.

Thanks in advance for any feedback or replies. Please do not hesitate to let
me know if there are any changes I can make or pieces of information I can
provide.

>How-To-Repeat:
Boot NetBSD in qemu with the virtio-gpu device attached to observe that NetBSD
does not yet have a driver for such a device.

>Fix:
Index: share/man/man4/Makefile
===================================================================
RCS file: /cvsroot/src/share/man/man4/Makefile,v
retrieving revision 1.735
diff -u -r1.735 Makefile
--- share/man/man4/Makefile	26 Aug 2024 13:38:28 -0000	1.735
+++ share/man/man4/Makefile	19 Sep 2024 21:43:57 -0000
@@ -69,9 +69,9 @@
 	uark.4 ubsec.4 udp.4 uep.4 ug.4 uha.4 uk.4 ukphy.4 umb.4 \
 	unix.4 userconf.4 \
 	vald.4 valz.4 veriexec.4 vga.4 vge.4 viaide.4 video.4 \
-	vio9p.4 viocon.4 vioif.4 viomb.4 viornd.4 vioscsi.4 virt.4 virtio.4 \
-	vether.4 vlan.4 vmmon.4 vmnet.4 vmt.4 vmx.4 vnd.4 voodoofb.4 vr.4 \
-	vte.4 \
+	vio9p.4 viogpu.4 viocon.4 vioif.4 viomb.4 viornd.4 vioscsi.4 virt.4 \
+	virtio.4 vether.4 vlan.4 vmmon.4 vmnet.4 vmt.4 vmx.4 vnd.4 voodoofb.4 \
+	vr.4 vte.4 \
 	wapbl.4 wb.4 wbsio.4 wd.4 wdc.4 wg.4 wi.4 wm.4 wpi.4 \
 	wsbell.4 wscons.4 wsdisplay.4 wsfont.4 wskbd.4 wsmouse.4 wsmux.4 \
 	xbox.4 xge.4 \
Index: share/man/man4/viogpu.4
===================================================================
RCS file: share/man/man4/viogpu.4
diff -N share/man/man4/viogpu.4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ share/man/man4/viogpu.4	19 Sep 2024 21:43:57 -0000
@@ -0,0 +1,50 @@
+.\"     $OpenBSD: viogpu.4,v 1.1 2023/04/20 19:28:30 jcs Exp $
+.\"
+.\" Copyright (c) 2023 joshua stein <jcs%openbsd.org@localhost>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd April 20 2023
+.Dt VIOGPU 4
+.Os
+.Sh NAME
+.Nm viogpu
+.Nd VirtIO GPU device
+.Sh SYNOPSIS
+.Cd "viogpu* at virtio?"
+.Cd "wsdisplay* at viogpu?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Xr virtio 4
+GPU interface provided by QEMU and other virtual machines to create a
+.Xr wscons 4
+console.
+.El
+.Sh SEE ALSO
+.Xr intro 4 ,
+.Xr virtio 4
+.Xr wscons 4 ,
+.Xr wsdisplay 4 ,
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 7.4 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An joshua stein Aq Mt jcs%openbsd.org@localhost .
Index: sys/arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.614
diff -u -r1.614 GENERIC
--- sys/arch/amd64/conf/GENERIC	27 Aug 2024 00:56:16 -0000	1.614
+++ sys/arch/amd64/conf/GENERIC	19 Sep 2024 21:43:58 -0000
@@ -1130,6 +1130,7 @@
 viomb*	at virtio?			# Virtio memory balloon device
 ld*	at virtio?			# Virtio disk device
 viocon* at virtio?			# Virtio serial device
+viogpu* at virtio?			# Virtio GPU device
 vioif*	at virtio?			# Virtio network device
 viornd* at virtio?			# Virtio entropy device
 vioscsi* at virtio?			# Virtio SCSI device
Index: sys/arch/evbarm/conf/GENERIC64
===================================================================
RCS file: /cvsroot/src/sys/arch/evbarm/conf/GENERIC64,v
retrieving revision 1.219
diff -u -r1.219 GENERIC64
--- sys/arch/evbarm/conf/GENERIC64	27 Aug 2024 00:56:16 -0000	1.219
+++ sys/arch/evbarm/conf/GENERIC64	19 Sep 2024 21:44:06 -0000
@@ -566,6 +566,7 @@
 #viomb*		at virtio?		# Virtio memory balloon device
 ld*		at virtio?		# Virtio disk device
 viocon*		at virtio?		# Virtio serial device
+viogpu*		at virtio?		# Virtio GPU device
 vioif*		at virtio?		# Virtio network device
 viornd* 	at virtio?		# Virtio entropy device
 vioscsi*	at virtio?		# Virtio SCSI device
Index: sys/dev/pci/files.virtio
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/files.virtio,v
retrieving revision 1.2
diff -u -r1.2 files.virtio
--- sys/dev/pci/files.virtio	28 Oct 2019 02:56:40 -0000	1.2
+++ sys/dev/pci/files.virtio	19 Sep 2024 21:44:46 -0000
@@ -11,6 +11,10 @@
 attach	viomb at virtio
 file	dev/pci/viomb.c		viomb
 
+device	viogpu: wsemuldisplaydev, rasops32, vcons
+attach	viogpu at virtio
+file	dev/pci/viogpu.c	viogpu
+
 device	vioif: ether, ifnet, arp
 attach	vioif at virtio
 file	dev/pci/if_vioif.c	vioif
Index: sys/dev/pci/viogpu.c
===================================================================
RCS file: sys/dev/pci/viogpu.c
diff -N sys/dev/pci/viogpu.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/pci/viogpu.c	19 Sep 2024 21:44:50 -0000
@@ -0,0 +1,920 @@
+/*	$OpenBSD: viogpu.c,v 1.3 2023/05/29 08:13:35 sf Exp $ */
+
+/*
+ * Copyright (c) 2024 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 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.
+ */
+
+/*
+ * Copyright (c) 2021-2023 joshua stein <jcs%openbsd.org@localhost>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+#include <dev/pci/viogpu.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsdisplayvar.h>
+#include <dev/rasops/rasops.h>
+#include <dev/wscons/wsdisplay_vconsvar.h>
+
+struct viogpu_softc;
+
+static int	viogpu_match(device_t, cfdata_t, void *);
+static void	viogpu_attach(device_t, device_t, void *);
+static void	viogpu_attach_postintr(device_t);
+static int	viogpu_cmd_sync(struct viogpu_softc *, void *, size_t, void *,
+				size_t);
+static int	viogpu_cmd_req(struct viogpu_softc *, void *, size_t, size_t);
+static void	viogpu_screen_update(void *);
+static int	viogpu_vq_done(struct virtqueue *vq);
+
+static int	viogpu_get_display_info(struct viogpu_softc *);
+static int	viogpu_create_2d(struct viogpu_softc *, uint32_t, uint32_t,
+				 uint32_t);
+static int	viogpu_set_scanout(struct viogpu_softc *, uint32_t, uint32_t,
+				   uint32_t, uint32_t);
+static int	viogpu_attach_backing(struct viogpu_softc *, uint32_t,
+				      bus_dmamap_t);
+static int	viogpu_transfer_to_host_2d(struct viogpu_softc *sc, uint32_t,
+					   uint32_t, uint32_t, uint32_t,
+					   uint32_t);
+static int	viogpu_flush_resource(struct viogpu_softc *, uint32_t,
+				      uint32_t, uint32_t, uint32_t, uint32_t);
+
+static int	viogpu_wsioctl(void *, void *, u_long, void *, int,
+			       struct lwp *);
+
+static void 	viogpu_init_screen(void *, struct vcons_screen *, int, long *);
+
+static void	viogpu_cursor(void *, int, int, int);
+static void	viogpu_putchar(void *, int, int, u_int, long);
+static void	viogpu_copycols(void *, int, int, int, int);
+static void	viogpu_erasecols(void *, int, int, int, long);
+static void	viogpu_copyrows(void *, int, int, int);
+static void	viogpu_eraserows(void *, int, int, long);
+static void	viogpu_replaceattr(void *, long, long);
+
+struct virtio_gpu_resource_attach_backing_entries {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 nr_entries;
+	struct virtio_gpu_mem_entry entries[1];
+} __packed;
+
+#define VIOGPU_HEIGHT		160
+#define VIOGPU_WIDTH		160
+
+#define VIOGPU_CMD_DMA_SIZE \
+    MAX(sizeof(struct virtio_gpu_resp_display_info), \
+    MAX(sizeof(struct virtio_gpu_resource_create_2d), \
+    MAX(sizeof(struct virtio_gpu_set_scanout), \
+    MAX(sizeof(struct virtio_gpu_resource_attach_backing_entries), \
+    MAX(sizeof(struct virtio_gpu_transfer_to_host_2d), \
+    sizeof(struct virtio_gpu_resource_flush)))))) + \
+    sizeof(struct virtio_gpu_ctrl_hdr)
+
+struct viogpu_softc {
+	device_t		sc_dev;
+	struct virtio_softc	*sc_virtio;
+#define	VQCTRL	0
+#define	VQCURS	1
+	struct virtqueue	sc_vqs[2];
+
+	bus_dma_segment_t	sc_dma_seg;
+	bus_dmamap_t		sc_dma_map;
+	void			*sc_cmd;
+	int			sc_fence_id;
+
+	int			sc_fb_width;
+	int			sc_fb_height;
+	bus_dma_segment_t	sc_fb_dma_seg;
+	bus_dmamap_t		sc_fb_dma_map;
+	size_t			sc_fb_dma_size;
+	void			*sc_fb_dma_kva;
+
+	struct wsscreen_descr		sc_wsd;
+	const struct wsscreen_descr	*sc_scrlist[1];
+	struct wsscreen_list		sc_wsl;
+	struct vcons_data		sc_vd;
+	struct vcons_screen		sc_vcs;
+	int				console;
+
+	void	(*ri_cursor)(void *, int, int, int);
+	void	(*ri_putchar)(void *, int, int, u_int, long);
+	void	(*ri_copycols)(void *, int, int, int, int);
+	void	(*ri_erasecols)(void *, int, int, int, long);
+	void	(*ri_copyrows)(void *, int, int, int);
+	void	(*ri_eraserows)(void *, int, int, long);
+	void	(*ri_replaceattr)(void *, long, long);
+
+	kmutex_t		sc_mutex;
+	bool			needs_update;
+	bool			is_requesting;
+	kcondvar_t		req_wait;
+	void			*update_soft_ih;
+	size_t			cur_cmd_size;
+	size_t			cur_ret_size;
+};
+
+CFATTACH_DECL_NEW(viogpu, sizeof(struct viogpu_softc),
+		  viogpu_match, viogpu_attach, NULL, NULL);
+
+#if VIOGPU_DEBUG
+#define VIOGPU_FEATURES		(VIRTIO_GPU_F_VIRGL | VIRTIO_GPU_F_EDID)
+#else
+#define VIOGPU_FEATURES		0
+#endif
+
+static struct wsdisplay_accessops viogpu_accessops = {
+	.ioctl        = viogpu_wsioctl,
+	.mmap         = NULL, /* This would require signalling on write to
+	                       * update the screen. */
+	.alloc_screen = NULL,
+	.free_screen  = NULL,
+	.show_screen  = NULL,
+	.load_font    = NULL,
+	.pollc        = NULL,
+	.scroll       = NULL,
+};
+
+static int
+viogpu_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct virtio_attach_args *va = aux;
+
+	if (va->sc_childdevid == VIRTIO_DEVICE_ID_GPU)
+		return 1;
+
+	return 0;
+}
+
+static void
+viogpu_attach(device_t parent, device_t self, void *aux)
+{
+	struct viogpu_softc *sc = device_private(self);
+	struct virtio_softc *vsc = device_private(parent);
+	int error;
+
+	if (virtio_child(vsc) != NULL) {
+		aprint_error("child already attached for %s\n",
+		    device_xname(parent));
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_virtio = vsc;
+
+	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
+	cv_init(&sc->req_wait, "vgpu_req");
+	sc->update_soft_ih = softint_establish(SOFTINT_NET,
+	    viogpu_screen_update, sc);
+	sc->needs_update = false;
+	sc->is_requesting = false;
+	sc->sc_fence_id = 0;
+
+	virtio_child_attach_start(vsc, self, IPL_VM,
+	    VIOGPU_FEATURES, VIRTIO_COMMON_FLAG_BITS);
+
+	if (!virtio_version_1(vsc)) {
+		aprint_error_dev(sc->sc_dev, "requires virtio version 1\n");
+		goto err;
+	}
+
+	/* Allocate command and cursor virtqueues. */
+	virtio_init_vq_vqdone(vsc, &sc->sc_vqs[VQCTRL], 0, viogpu_vq_done);
+	error = virtio_alloc_vq(vsc, &sc->sc_vqs[VQCTRL], NBPG, 1, "control");
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "alloc_vq failed: %d\n", error);
+		goto err;
+	}
+
+	virtio_init_vq_vqdone(vsc, &sc->sc_vqs[VQCURS], 1, viogpu_vq_done);
+	error = virtio_alloc_vq(vsc, &sc->sc_vqs[VQCURS], NBPG, 1, "cursor");
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "alloc_vq failed: %d\n", error);
+		goto free_vq0;
+	}
+
+	if (virtio_child_attach_finish(vsc, sc->sc_vqs,
+	    __arraycount(sc->sc_vqs), NULL,
+	    VIRTIO_F_INTR_MPSAFE | VIRTIO_F_INTR_SOFTINT) != 0)
+		goto free_vqs;
+
+	/* Interrupts are required for synchronous commands in attachment. */
+	config_interrupts(self, viogpu_attach_postintr);
+
+	return;
+
+free_vqs:
+	virtio_free_vq(vsc, &sc->sc_vqs[VQCURS]);
+free_vq0:
+	virtio_free_vq(vsc, &sc->sc_vqs[VQCTRL]);
+err:
+	virtio_child_attach_failed(vsc);
+	cv_destroy(&sc->req_wait);
+	mutex_destroy(&sc->sc_mutex);
+	return;
+}
+
+static void
+viogpu_attach_postintr(device_t self)
+{
+	struct viogpu_softc *sc = device_private(self);
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct wsemuldisplaydev_attach_args waa;
+	struct rasops_info *ri;
+	long defattr;
+	int nsegs;
+	int error;
+
+	/* Set up DMA space for sending commands. */
+	error = bus_dmamap_create(virtio_dmat(vsc), VIOGPU_CMD_DMA_SIZE, 1,
+	    VIOGPU_CMD_DMA_SIZE, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+	    &sc->sc_dma_map);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamap_create failed: %d\n",
+		    error);
+		goto err;
+	}
+	error = bus_dmamem_alloc(virtio_dmat(vsc), VIOGPU_CMD_DMA_SIZE, 16, 0,
+	    &sc->sc_dma_seg, 1, &nsegs, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamem_alloc failed: %d\n",
+		    error);
+		goto destroy;
+	}
+	error = bus_dmamem_map(virtio_dmat(vsc), &sc->sc_dma_seg, nsegs,
+	    VIOGPU_CMD_DMA_SIZE, &sc->sc_cmd, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamem_map failed: %d\n",
+		    error);
+		goto free;
+	}
+	memset(sc->sc_cmd, 0, VIOGPU_CMD_DMA_SIZE);
+	error = bus_dmamap_load(virtio_dmat(vsc), sc->sc_dma_map, sc->sc_cmd,
+	    VIOGPU_CMD_DMA_SIZE, NULL, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamap_load failed: %d\n",
+		    error);
+		goto unmap;
+	}
+
+	if (viogpu_get_display_info(sc) != 0)
+		goto unmap;
+
+	/* Set up DMA space for actual framebuffer. */
+	sc->sc_fb_dma_size = sc->sc_fb_width * sc->sc_fb_height * 4;
+	error = bus_dmamap_create(virtio_dmat(vsc), sc->sc_fb_dma_size, 1,
+	    sc->sc_fb_dma_size, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+	    &sc->sc_fb_dma_map);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamap_create failed: %d\n",
+		    error);
+		goto unmap;
+	}
+	error = bus_dmamem_alloc(virtio_dmat(vsc), sc->sc_fb_dma_size, 1024, 0,
+	    &sc->sc_fb_dma_seg, 1, &nsegs, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamem_alloc failed: %d\n",
+		    error);
+		goto fb_destroy;
+	}
+	error = bus_dmamem_map(virtio_dmat(vsc), &sc->sc_fb_dma_seg, nsegs,
+	    sc->sc_fb_dma_size, &sc->sc_fb_dma_kva, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamem_map failed: %d\n",
+		    error);
+		goto fb_free;
+	}
+	memset(sc->sc_fb_dma_kva, 0, sc->sc_fb_dma_size);
+	error = bus_dmamap_load(virtio_dmat(vsc), sc->sc_fb_dma_map,
+	    sc->sc_fb_dma_kva, sc->sc_fb_dma_size, NULL, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamap_load failed: %d\n",
+		    error);
+		goto fb_unmap;
+	}
+
+	if (viogpu_create_2d(sc, 1, sc->sc_fb_width, sc->sc_fb_height) != 0)
+		goto fb_unmap;
+
+	if (viogpu_attach_backing(sc, 1, sc->sc_fb_dma_map) != 0)
+		goto fb_unmap;
+
+	if (viogpu_set_scanout(sc, 0, 1, sc->sc_fb_width,
+	    sc->sc_fb_height) != 0)
+		goto fb_unmap;
+
+	sc->console = 1;
+
+	sc->sc_wsd = (struct wsscreen_descr){
+		"std",
+		0, 0,
+		NULL,
+		8, 16,
+		0,
+		NULL
+	};
+
+	sc->sc_scrlist[0] = &sc->sc_wsd;
+	sc->sc_wsl.nscreens = __arraycount(sc->sc_scrlist);
+	sc->sc_wsl.screens = sc->sc_scrlist;
+
+	vcons_init(&sc->sc_vd, sc, &sc->sc_wsd, &viogpu_accessops);
+	sc->sc_vd.init_screen = viogpu_init_screen;
+
+	if (sc->console) {
+		vcons_init_screen(&sc->sc_vd, &sc->sc_vcs, 1, &defattr);
+		sc->sc_vcs.scr_flags |= VCONS_SCREEN_IS_STATIC;
+		ri = &sc->sc_vcs.scr_ri;
+
+		sc->sc_wsd.textops = &ri->ri_ops;
+		sc->sc_wsd.capabilities = ri->ri_caps;
+		sc->sc_wsd.nrows = ri->ri_rows;
+		sc->sc_wsd.ncols = ri->ri_cols;
+		wsdisplay_cnattach(&sc->sc_wsd, ri, 0, 0, defattr);
+
+		device_printf(sc->sc_dev, "%dx%d, %dbpp\n", ri->ri_width,
+		    ri->ri_height, ri->ri_depth);
+	}
+
+	waa.scrdata = &sc->sc_wsl;
+	waa.accessops = &viogpu_accessops;
+	waa.accesscookie = &sc->sc_vd;
+	waa.console = sc->console;
+
+	config_found(self, &waa, wsemuldisplaydevprint, CFARGS_NONE);
+
+	return;
+
+fb_unmap:
+	bus_dmamem_unmap(virtio_dmat(vsc), &sc->sc_fb_dma_kva,
+	    sc->sc_fb_dma_size);
+fb_free:
+	bus_dmamem_free(virtio_dmat(vsc), &sc->sc_fb_dma_seg, 1);
+fb_destroy:
+	bus_dmamap_destroy(virtio_dmat(vsc), sc->sc_fb_dma_map);
+unmap:
+	bus_dmamem_unmap(virtio_dmat(vsc), &sc->sc_cmd, VIOGPU_CMD_DMA_SIZE);
+free:
+	bus_dmamem_free(virtio_dmat(vsc), &sc->sc_dma_seg, 1);
+destroy:
+	bus_dmamap_destroy(virtio_dmat(vsc), sc->sc_dma_map);
+err:
+	aprint_error_dev(sc->sc_dev, "DMA setup failed\n");
+	virtio_free_vq(vsc, &sc->sc_vqs[VQCURS]);
+	virtio_free_vq(vsc, &sc->sc_vqs[VQCTRL]);
+	virtio_child_attach_failed(vsc);
+	cv_destroy(&sc->req_wait);
+	mutex_destroy(&sc->sc_mutex);
+	return;
+}
+
+/*
+ * This carries out a command synchronously, unlike the commands used to
+ * update the screen.
+ */
+static int
+viogpu_cmd_sync(struct viogpu_softc *sc, void *cmd, size_t cmd_size,
+		void *ret, size_t ret_size)
+{
+	int error;
+
+	mutex_enter(&sc->sc_mutex);
+
+	while (sc->is_requesting == true)
+		cv_wait(&sc->req_wait, &sc->sc_mutex);
+
+	error = viogpu_cmd_req(sc, cmd, cmd_size, ret_size);
+	if (error != 0)
+		goto out;
+
+	while (sc->is_requesting == true)
+		cv_wait(&sc->req_wait, &sc->sc_mutex);
+
+	if (ret != NULL)
+		memcpy(ret, (char *)sc->sc_cmd + cmd_size, ret_size);
+
+out:
+	mutex_exit(&sc->sc_mutex);
+
+	return error;
+}
+
+static void
+viogpu_screen_update(void *arg)
+{
+	struct viogpu_softc *sc = arg;
+
+	mutex_enter(&sc->sc_mutex);
+
+	if (sc->is_requesting == false)
+		viogpu_transfer_to_host_2d(sc, 1, 0, 0, sc->sc_fb_width,
+		    sc->sc_fb_height);
+	else
+		sc->needs_update = true;
+
+	mutex_exit(&sc->sc_mutex);
+}
+
+static int
+viogpu_cmd_req(struct viogpu_softc *sc, void *cmd, size_t cmd_size,
+	       size_t ret_size)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtqueue *vq = &sc->sc_vqs[VQCTRL];
+	struct virtio_gpu_ctrl_hdr *hdr =
+	    (struct virtio_gpu_ctrl_hdr *)sc->sc_cmd;
+	int slot, r;
+
+	memcpy(sc->sc_cmd, cmd, cmd_size);
+	memset((char *)sc->sc_cmd + cmd_size, 0, ret_size);
+
+#if VIOGPU_DEBUG
+	printf("%s: [%zu -> %zu]: ", __func__, cmd_size, ret_size);
+	for (int i = 0; i < cmd_size; i++) {
+		printf(" %02x", ((unsigned char *)sc->sc_cmd)[i]);
+	}
+	printf("\n");
+#endif
+
+	hdr->flags |= virtio_rw32(vsc, VIRTIO_GPU_FLAG_FENCE);
+	hdr->fence_id = virtio_rw64(vsc, ++sc->sc_fence_id);
+
+	r = virtio_enqueue_prep(vsc, vq, &slot);
+	if (r != 0)
+		panic("%s: control vq busy", device_xname(sc->sc_dev));
+
+	r = virtio_enqueue_reserve(vsc, vq, slot,
+	    sc->sc_dma_map->dm_nsegs + 1);
+	if (r != 0)
+		panic("%s: control vq busy", device_xname(sc->sc_dev));
+
+	bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, 0, cmd_size,
+	    BUS_DMASYNC_PREWRITE);
+	virtio_enqueue_p(vsc, vq, slot, sc->sc_dma_map, 0, cmd_size, true);
+
+	bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, cmd_size, ret_size,
+	    BUS_DMASYNC_PREREAD);
+	virtio_enqueue_p(vsc, vq, slot, sc->sc_dma_map, cmd_size, ret_size,
+	    false);
+
+	virtio_enqueue_commit(vsc, vq, slot, true);
+
+	sc->cur_cmd_size = cmd_size;
+	sc->cur_ret_size = ret_size;
+	sc->is_requesting = true;
+
+	return 0;
+}
+
+static int
+viogpu_vq_done(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct viogpu_softc *sc = device_private(virtio_child(vsc));
+	struct virtio_gpu_ctrl_hdr *resp;
+	int slot, len;
+	uint32_t cmd_type, resp_type;
+	uint64_t resp_fence, expect_fence;
+	bool next_req_sent = false;
+
+	mutex_enter(&sc->sc_mutex);
+
+	while (virtio_dequeue(vsc, vq, &slot, &len) != 0)
+		;
+
+	virtio_dequeue_commit(vsc, vq, slot);
+
+	bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, 0, sc->cur_cmd_size,
+	    BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, sc->cur_cmd_size,
+	    sc->cur_ret_size, BUS_DMASYNC_POSTREAD);
+
+	resp = (struct virtio_gpu_ctrl_hdr *)((char *)sc->sc_cmd +
+	    sc->cur_cmd_size);
+
+	cmd_type = virtio_rw32(vsc,
+	    ((struct virtio_gpu_ctrl_hdr *)sc->sc_cmd)->type);
+	resp_type = virtio_rw32(vsc, resp->type);
+	resp_fence = virtio_rw64(vsc, resp->fence_id);
+	expect_fence = sc->sc_fence_id;
+
+	switch (cmd_type) {
+	case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+		/* The second command for screen updating must be issued. */
+		if (resp_type == VIRTIO_GPU_RESP_OK_NODATA) {
+			viogpu_flush_resource(sc, 1, 0, 0, sc->sc_fb_width,
+			    sc->sc_fb_height);
+			next_req_sent = true;
+		}
+		break;
+	case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+		if (sc->needs_update == true) {
+			viogpu_transfer_to_host_2d(sc, 1, 0, 0,
+			    sc->sc_fb_width, sc->sc_fb_height);
+			sc->needs_update = false;
+			next_req_sent = true;
+		}
+		break;
+	default:
+		/* Other command types are called synchronously. */
+		break;
+	}
+
+	if (next_req_sent == false) {
+		sc->is_requesting = false;
+		cv_broadcast(&sc->req_wait);
+	}
+
+	mutex_exit(&sc->sc_mutex);
+
+	if (resp_type != VIRTIO_GPU_RESP_OK_NODATA) {
+		switch (cmd_type) {
+		case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+			device_printf(sc->sc_dev,
+			    "failed TRANSFER_TO_HOST: %d\n", resp_type);
+			break;
+		case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+			device_printf(sc->sc_dev,
+			    "failed RESOURCE_FLUSH: %d\n", resp_type);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (resp_fence != expect_fence)
+		printf("%s: return fence id not right (0x%" PRIx64 " != 0x%"
+		    PRIx64 ")\n", __func__, resp_fence, expect_fence);
+
+	return 0;
+}
+
+static int
+viogpu_get_display_info(struct viogpu_softc *sc)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtio_gpu_ctrl_hdr hdr = { 0 };
+	struct virtio_gpu_resp_display_info info = { 0 };
+
+	hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_GET_DISPLAY_INFO);
+
+	viogpu_cmd_sync(sc, &hdr, sizeof(hdr), &info, sizeof(info));
+
+	if (virtio_rw32(vsc, info.hdr.type) !=
+	    VIRTIO_GPU_RESP_OK_DISPLAY_INFO) {
+		device_printf(sc->sc_dev, "failed getting display info\n");
+		return 1;
+	}
+
+	if (!info.pmodes[0].enabled) {
+		device_printf(sc->sc_dev, "pmodes[0] is not enabled\n");
+		return 1;
+	}
+
+	sc->sc_fb_width = virtio_rw32(vsc, info.pmodes[0].r.width);
+	sc->sc_fb_height = virtio_rw32(vsc, info.pmodes[0].r.height);
+
+	return 0;
+}
+
+static int
+viogpu_create_2d(struct viogpu_softc *sc, uint32_t resource_id, uint32_t width,
+		 uint32_t height)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtio_gpu_resource_create_2d res = { 0 };
+	struct virtio_gpu_ctrl_hdr resp = { 0 };
+
+	res.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D);
+	res.resource_id = virtio_rw32(vsc, resource_id);
+	res.format = virtio_rw32(vsc, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
+	res.width = virtio_rw32(vsc, width);
+	res.height = virtio_rw32(vsc, height);
+
+	viogpu_cmd_sync(sc, &res, sizeof(res), &resp, sizeof(resp));
+
+	if (virtio_rw32(vsc, resp.type) != VIRTIO_GPU_RESP_OK_NODATA) {
+		device_printf(sc->sc_dev, "failed CREATE_2D: %d\n",
+		    virtio_rw32(vsc, resp.type));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+viogpu_set_scanout(struct viogpu_softc *sc, uint32_t scanout_id,
+		   uint32_t resource_id, uint32_t width, uint32_t height)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtio_gpu_set_scanout ss = { 0 };
+	struct virtio_gpu_ctrl_hdr resp = { 0 };
+
+	ss.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_SET_SCANOUT);
+	ss.scanout_id = virtio_rw32(vsc, scanout_id);
+	ss.resource_id = virtio_rw32(vsc, resource_id);
+	ss.r.width = virtio_rw32(vsc, width);
+	ss.r.height = virtio_rw32(vsc, height);
+
+	viogpu_cmd_sync(sc, &ss, sizeof(ss), &resp, sizeof(resp));
+
+	if (virtio_rw32(vsc, resp.type) != VIRTIO_GPU_RESP_OK_NODATA) {
+		device_printf(sc->sc_dev, "failed SET_SCANOUT: %d\n",
+		    virtio_rw32(vsc, resp.type));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+viogpu_attach_backing(struct viogpu_softc *sc, uint32_t resource_id,
+		      bus_dmamap_t dmamap)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtio_gpu_resource_attach_backing_entries backing = { 0 };
+	struct virtio_gpu_ctrl_hdr resp = { 0 };
+
+	backing.hdr.type = virtio_rw32(vsc,
+	    VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING);
+	backing.resource_id = virtio_rw32(vsc, resource_id);
+	backing.nr_entries = virtio_rw32(vsc, __arraycount(backing.entries));
+	backing.entries[0].addr = virtio_rw64(vsc, dmamap->dm_segs[0].ds_addr);
+	backing.entries[0].length = virtio_rw32(vsc,
+	    dmamap->dm_segs[0].ds_len);
+
+	if (dmamap->dm_nsegs > 1)
+		printf("%s: TODO: send all %d segs\n", __func__,
+		    dmamap->dm_nsegs);
+
+#if VIOGPU_DEBUG
+	printf("%s: backing addr 0x%" PRIx64 " length %d\n", __func__,
+	    backing.entries[0].addr, backing.entries[0].length);
+#endif
+
+	viogpu_cmd_sync(sc, &backing, sizeof(backing), &resp, sizeof(resp));
+
+	if (virtio_rw32(vsc, resp.type) != VIRTIO_GPU_RESP_OK_NODATA) {
+		device_printf(sc->sc_dev, "failed ATTACH_BACKING: %d\n",
+		    virtio_rw32(vsc, resp.type));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+viogpu_transfer_to_host_2d(struct viogpu_softc *sc, uint32_t resource_id,
+			   uint32_t x, uint32_t y, uint32_t width,
+			   uint32_t height)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtio_gpu_transfer_to_host_2d tth = { 0 };
+
+	tth.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D);
+	tth.resource_id = virtio_rw32(vsc, resource_id);
+	tth.r.x = virtio_rw32(vsc, x);
+	tth.r.y = virtio_rw32(vsc, y);
+	tth.r.width = virtio_rw32(vsc, width);
+	tth.r.height = virtio_rw32(vsc, height);
+	tth.offset = virtio_rw64(vsc, (y * sc->sc_fb_width + x) *
+	    4 /* bpp / 8 */);
+
+	viogpu_cmd_req(sc, &tth, sizeof(tth),
+	    sizeof(struct virtio_gpu_ctrl_hdr));
+
+	return 0;
+}
+
+static int
+viogpu_flush_resource(struct viogpu_softc *sc, uint32_t resource_id,
+		      uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	struct virtio_gpu_resource_flush flush = { 0 };
+
+	flush.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_RESOURCE_FLUSH);
+	flush.resource_id = virtio_rw32(vsc, resource_id);
+	flush.r.x = virtio_rw32(vsc, x);
+	flush.r.y = virtio_rw32(vsc, y);
+	flush.r.width = virtio_rw32(vsc, width);
+	flush.r.height = virtio_rw32(vsc, height);
+
+	viogpu_cmd_req(sc, &flush, sizeof(flush),
+	    sizeof(struct virtio_gpu_ctrl_hdr));
+
+	return 0;
+}
+
+static int
+viogpu_wsioctl(void *v, void *vs, u_long cmd, void *data, int flag,
+	       struct lwp *l)
+{
+	struct rasops_info *ri = v;
+	struct wsdisplayio_fbinfo *fbi;
+	struct wsdisplay_fbinfo *wdf;
+
+	switch (cmd) {
+	case WSDISPLAYIO_GTYPE:
+		*(u_int *)data = WSDISPLAY_TYPE_VIOGPU;
+		return 0;
+	case WSDISPLAYIO_GET_FBINFO:
+		fbi = (struct wsdisplayio_fbinfo *)data;
+		return wsdisplayio_get_fbinfo(ri, fbi);
+	case WSDISPLAYIO_GINFO:
+		wdf = (struct wsdisplay_fbinfo *)data;
+		wdf->height = ri->ri_height;
+		wdf->width = ri->ri_width;
+		wdf->depth = ri->ri_depth;
+		wdf->cmsize = 0;
+		return 0;
+	case WSDISPLAYIO_LINEBYTES:
+		*(u_int *)data = ri->ri_stride;
+		return 0;
+	case WSDISPLAYIO_SMODE:
+		return 0;
+	case WSDISPLAYIO_GVIDEO:
+	case WSDISPLAYIO_SVIDEO:
+		return 0;
+	}
+
+	return EPASSTHROUGH;
+}
+
+static void
+viogpu_init_screen(void *cookie, struct vcons_screen *scr, int existing,
+		   long *defattr)
+{
+	struct viogpu_softc *sc = cookie;
+	struct rasops_info *ri = &scr->scr_ri;
+
+	ri->ri_bits = sc->sc_fb_dma_kva;
+	ri->ri_flg = RI_CENTER | RI_CLEAR;
+	ri->ri_depth = 32;
+	ri->ri_width = sc->sc_fb_width;
+	ri->ri_height = sc->sc_fb_height;
+	ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
+	ri->ri_bpos = 0;	/* B8G8R8X8 */
+	ri->ri_bnum = 8;
+	ri->ri_gpos = 8;
+	ri->ri_gnum = 8;
+	ri->ri_rpos = 16;
+	ri->ri_rnum = 8;
+	rasops_init(ri, VIOGPU_HEIGHT, VIOGPU_WIDTH);
+
+	/*
+	 * Replace select text operations with wrappers that update the
+	 * correct part of the screen after the operation.
+	 */
+	sc->ri_cursor = ri->ri_ops.cursor;
+	sc->ri_putchar = ri->ri_ops.putchar;
+	sc->ri_copycols = ri->ri_ops.copycols;
+	sc->ri_erasecols = ri->ri_ops.erasecols;
+	sc->ri_copyrows = ri->ri_ops.copyrows;
+	sc->ri_eraserows = ri->ri_ops.eraserows;
+	sc->ri_replaceattr = ri->ri_ops.replaceattr;
+	ri->ri_ops.cursor = ri->ri_ops.cursor == NULL ? NULL : viogpu_cursor;
+	ri->ri_ops.putchar = ri->ri_ops.putchar == NULL ? NULL :
+	    viogpu_putchar;
+	ri->ri_ops.copycols = ri->ri_ops.copycols == NULL ? NULL :
+	    viogpu_copycols;
+	ri->ri_ops.erasecols = ri->ri_ops.erasecols == NULL ? NULL :
+	    viogpu_erasecols;
+	ri->ri_ops.copyrows = ri->ri_ops.copyrows == NULL ? NULL :
+	    viogpu_copyrows;
+	ri->ri_ops.eraserows = ri->ri_ops.eraserows == NULL ? NULL :
+	    viogpu_eraserows;
+	ri->ri_ops.replaceattr = ri->ri_ops.replaceattr == NULL ? NULL :
+	    viogpu_replaceattr;
+}
+
+static void
+viogpu_cursor(void *c, int on, int row, int col)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_cursor(c, on, row, col);
+
+	softint_schedule(sc->update_soft_ih);
+}
+
+static void
+viogpu_putchar(void *c, int row, int col, u_int uc, long attr)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_putchar(c, row, col, uc, attr);
+
+	softint_schedule(sc->update_soft_ih);
+}
+
+static void
+viogpu_copycols(void *c, int row, int srccol, int dstcol, int ncols)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_copycols(c, row, srccol, dstcol, ncols);
+
+	softint_schedule(sc->update_soft_ih);
+}
+
+static void
+viogpu_erasecols(void *c, int row, int startcol, int ncols, long attr)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_erasecols(c, row, startcol, ncols, attr);
+
+	softint_schedule(sc->update_soft_ih);
+}
+
+static void
+viogpu_copyrows(void *c, int srcrow, int dstrow, int nrows)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_copyrows(c, srcrow, dstrow, nrows);
+
+	softint_schedule(sc->update_soft_ih);
+}
+
+static void
+viogpu_eraserows(void *c, int row, int nrows, long attr)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_eraserows(c, row, nrows, attr);
+
+	softint_schedule(sc->update_soft_ih);
+}
+
+static void
+viogpu_replaceattr(void *c, long oldattr, long newattr)
+{
+	struct rasops_info *ri = c;
+	struct vcons_screen *vscr = ri->ri_hw;
+	struct viogpu_softc *sc = vscr->scr_vd->cookie;
+
+	sc->ri_replaceattr(c, oldattr, newattr);
+
+	softint_schedule(sc->update_soft_ih);
+}
Index: sys/dev/pci/viogpu.h
===================================================================
RCS file: sys/dev/pci/viogpu.h
diff -N sys/dev/pci/viogpu.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/pci/viogpu.h	19 Sep 2024 21:44:50 -0000
@@ -0,0 +1,450 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ *     Dave Airlie <airlied%redhat.com@localhost>
+ *     Gerd Hoffmann <kraxel%redhat.com@localhost>
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers:
+ *
+ * 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.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 IBM 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.
+ */
+
+#ifndef VIRTIO_GPU_HW_H
+#define VIRTIO_GPU_HW_H
+
+#include <sys/types.h>
+
+#define __u8 uint8_t
+#define __u32 uint32_t
+#define __le16 uint16_t
+#define __le32 uint32_t
+#define __le64 uint64_t
+
+/*
+ * VIRTIO_GPU_CMD_CTX_*
+ * VIRTIO_GPU_CMD_*_3D
+ */
+#define VIRTIO_GPU_F_VIRGL               (1ULL << 0)
+
+/*
+ * VIRTIO_GPU_CMD_GET_EDID
+ */
+#define VIRTIO_GPU_F_EDID                (1ULL << 1)
+/*
+ * VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID
+ */
+#define VIRTIO_GPU_F_RESOURCE_UUID       (1ULL << 2)
+
+/*
+ * VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB
+ */
+#define VIRTIO_GPU_F_RESOURCE_BLOB       (1ULL << 3)
+
+enum virtio_gpu_ctrl_type {
+	VIRTIO_GPU_UNDEFINED = 0,
+
+	/* 2d commands */
+	VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+	VIRTIO_GPU_CMD_RESOURCE_UNREF,
+	VIRTIO_GPU_CMD_SET_SCANOUT,
+	VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+	VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+	VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+	VIRTIO_GPU_CMD_GET_CAPSET_INFO,
+	VIRTIO_GPU_CMD_GET_CAPSET,
+	VIRTIO_GPU_CMD_GET_EDID,
+	VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
+	VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
+
+	/* 3d commands */
+	VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
+	VIRTIO_GPU_CMD_CTX_DESTROY,
+	VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
+	VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
+	VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
+	VIRTIO_GPU_CMD_SUBMIT_3D,
+	VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
+	VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
+
+	/* cursor commands */
+	VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+	VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+	/* success responses */
+	VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+	VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+	VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+	VIRTIO_GPU_RESP_OK_CAPSET,
+	VIRTIO_GPU_RESP_OK_EDID,
+	VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
+	VIRTIO_GPU_RESP_OK_MAP_INFO,
+
+	/* error responses */
+	VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+	VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+	VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+	VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+	VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+	VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+enum virtio_gpu_shm_id {
+	VIRTIO_GPU_SHM_ID_UNDEFINED = 0,
+	/*
+	 * VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB
+	 * VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB
+	 */
+	VIRTIO_GPU_SHM_ID_HOST_VISIBLE = 1
+};
+
+#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
+
+struct virtio_gpu_ctrl_hdr {
+	__le32 type;
+	__le32 flags;
+	__le64 fence_id;
+	__le32 ctx_id;
+	__le32 padding;
+} __packed;
+
+/* data passed in the cursor vq */
+
+struct virtio_gpu_cursor_pos {
+	__le32 scanout_id;
+	__le32 x;
+	__le32 y;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+struct virtio_gpu_update_cursor {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_cursor_pos pos;  /* update & move */
+	__le32 resource_id;           /* update only */
+	__le32 hot_x;                 /* update only */
+	__le32 hot_y;                 /* update only */
+	__le32 padding;
+} __packed;
+
+/* data passed in the control vq, 2d related */
+
+struct virtio_gpu_rect {
+	__le32 x;
+	__le32 y;
+	__le32 width;
+	__le32 height;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+struct virtio_gpu_resource_unref {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+struct virtio_gpu_resource_create_2d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 format;
+	__le32 width;
+	__le32 height;
+} __packed;
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+struct virtio_gpu_set_scanout {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le32 scanout_id;
+	__le32 resource_id;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+struct virtio_gpu_resource_flush {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+struct virtio_gpu_transfer_to_host_2d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le64 offset;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+struct virtio_gpu_mem_entry {
+	__le64 addr;
+	__le32 length;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+struct virtio_gpu_resource_attach_backing {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 nr_entries;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+struct virtio_gpu_resource_detach_backing {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+#define VIRTIO_GPU_MAX_SCANOUTS 16
+struct virtio_gpu_resp_display_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_display_one {
+		struct virtio_gpu_rect r;
+		__le32 enabled;
+		__le32 flags;
+	} pmodes[VIRTIO_GPU_MAX_SCANOUTS];
+} __packed;
+
+/* data passed in the control vq, 3d related */
+
+struct virtio_gpu_box {
+	__le32 x, y, z;
+	__le32 w, h, d;
+} __packed;
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
+struct virtio_gpu_transfer_host_3d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_box box;
+	__le64 offset;
+	__le32 resource_id;
+	__le32 level;
+	__le32 stride;
+	__le32 layer_stride;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
+#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
+struct virtio_gpu_resource_create_3d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 target;
+	__le32 format;
+	__le32 bind;
+	__le32 width;
+	__le32 height;
+	__le32 depth;
+	__le32 array_size;
+	__le32 last_level;
+	__le32 nr_samples;
+	__le32 flags;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_CTX_CREATE */
+struct virtio_gpu_ctx_create {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 nlen;
+	__le32 padding;
+	char debug_name[64];
+} __packed;
+
+/* VIRTIO_GPU_CMD_CTX_DESTROY */
+struct virtio_gpu_ctx_destroy {
+	struct virtio_gpu_ctrl_hdr hdr;
+} __packed;
+
+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
+struct virtio_gpu_ctx_resource {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_SUBMIT_3D */
+struct virtio_gpu_cmd_submit {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 size;
+	__le32 padding;
+} __packed;
+
+#define VIRTIO_GPU_CAPSET_VIRGL 1
+#define VIRTIO_GPU_CAPSET_VIRGL2 2
+
+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
+struct virtio_gpu_get_capset_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_index;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
+struct virtio_gpu_resp_capset_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_id;
+	__le32 capset_max_version;
+	__le32 capset_max_size;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_GET_CAPSET */
+struct virtio_gpu_get_capset {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_id;
+	__le32 capset_version;
+} __packed;
+
+/* VIRTIO_GPU_RESP_OK_CAPSET */
+struct virtio_gpu_resp_capset {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__u8 capset_data[];
+} __packed;
+
+/* VIRTIO_GPU_CMD_GET_EDID */
+struct virtio_gpu_cmd_get_edid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 scanout;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_RESP_OK_EDID */
+struct virtio_gpu_resp_edid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 size;
+	__le32 padding;
+	__u8 edid[1024];
+} __packed;
+
+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
+
+struct virtio_gpu_config {
+	__le32 events_read;
+	__le32 events_clear;
+	__le32 num_scanouts;
+	__le32 num_capsets;
+} __packed;
+
+/* simple formats for fbcon/X use */
+enum virtio_gpu_formats {
+	VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM  = 1,
+	VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM  = 2,
+	VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM  = 3,
+	VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM  = 4,
+
+	VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM  = 67,
+	VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM  = 68,
+
+	VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM  = 121,
+	VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM  = 134,
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID */
+struct virtio_gpu_resource_assign_uuid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+/* VIRTIO_GPU_RESP_OK_RESOURCE_UUID */
+struct virtio_gpu_resp_resource_uuid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__u8 uuid[16];
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB */
+struct virtio_gpu_resource_create_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+#define VIRTIO_GPU_BLOB_MEM_GUEST             0x0001
+#define VIRTIO_GPU_BLOB_MEM_HOST3D            0x0002
+#define VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST      0x0003
+
+#define VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE     0x0001
+#define VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE    0x0002
+#define VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+	/* zero is invalid blob mem */
+	__le32 blob_mem;
+	__le32 blob_flags;
+	__le32 nr_entries;
+	__le64 blob_id;
+	__le64 size;
+	/*
+	 * sizeof(nr_entries * virtio_gpu_mem_entry) bytes follow
+	 */
+} __packed;
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT_BLOB */
+struct virtio_gpu_set_scanout_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le32 scanout_id;
+	__le32 resource_id;
+	__le32 width;
+	__le32 height;
+	__le32 format;
+	__le32 padding;
+	__le32 strides[4];
+	__le32 offsets[4];
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB */
+struct virtio_gpu_resource_map_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+	__le64 offset;
+} __packed;
+
+/* VIRTIO_GPU_RESP_OK_MAP_INFO */
+#define VIRTIO_GPU_MAP_CACHE_MASK     0x0f
+#define VIRTIO_GPU_MAP_CACHE_NONE     0x00
+#define VIRTIO_GPU_MAP_CACHE_CACHED   0x01
+#define VIRTIO_GPU_MAP_CACHE_UNCACHED 0x02
+#define VIRTIO_GPU_MAP_CACHE_WC       0x03
+struct virtio_gpu_resp_map_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__u32 map_info;
+	__u32 padding;
+} __packed;
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB */
+struct virtio_gpu_resource_unmap_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+} __packed;
+
+#endif
Index: sys/dev/pci/virtio.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/virtio.c,v
retrieving revision 1.82
diff -u -r1.82 virtio.c
--- sys/dev/pci/virtio.c	5 Aug 2024 19:26:43 -0000	1.82
+++ sys/dev/pci/virtio.c	19 Sep 2024 21:44:50 -0000
@@ -65,6 +65,13 @@
 	"remote processor messaging",	/*  7 */
 	"SCSI",				/*  8 */
 	"9P transport",			/*  9 */
+	NULL,				/* 10 */
+	NULL,				/* 11 */
+	NULL,				/* 12 */
+	NULL,				/* 13 */
+	NULL,				/* 14 */
+	NULL,				/* 15 */
+	"GPU",				/* 16 */
 };
 #define NDEVNAMES	__arraycount(virtio_device_name)
 
@@ -1487,6 +1494,12 @@
 	return sc->sc_active_features;
 }
 
+bool
+virtio_version_1(struct virtio_softc *sc)
+{
+	return sc->sc_version_1;
+}
+
 int
 virtio_attach_failed(struct virtio_softc *sc)
 {
Index: sys/dev/pci/virtioreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/virtioreg.h,v
retrieving revision 1.13
diff -u -r1.13 virtioreg.h
--- sys/dev/pci/virtioreg.h	19 Nov 2023 19:47:03 -0000	1.13
+++ sys/dev/pci/virtioreg.h	19 Sep 2024 21:44:50 -0000
@@ -77,6 +77,7 @@
 #define VIRTIO_DEVICE_ID_RPMSG		 7
 #define VIRTIO_DEVICE_ID_SCSI		 8
 #define VIRTIO_DEVICE_ID_9P		 9
+#define VIRTIO_DEVICE_ID_GPU            16
 
 /* common device/guest features */
 #define  VIRTIO_F_NOTIFY_ON_EMPTY		__BIT(24)
Index: sys/dev/pci/virtiovar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/virtiovar.h,v
retrieving revision 1.29
diff -u -r1.29 virtiovar.h
--- sys/dev/pci/virtiovar.h	19 Apr 2023 00:23:45 -0000	1.29
+++ sys/dev/pci/virtiovar.h	19 Sep 2024 21:44:50 -0000
@@ -243,6 +243,7 @@
 device_t	virtio_child(struct virtio_softc *);
 int		virtio_intrhand(struct virtio_softc *);
 uint64_t	virtio_features(struct virtio_softc *);
+bool		virtio_version_1(struct virtio_softc *);
 
 /* autoconf(9) common */
 void virtio_set_status(struct virtio_softc *, int);
Index: sys/dev/virtio/virtio_mmio.c
===================================================================
RCS file: /cvsroot/src/sys/dev/virtio/virtio_mmio.c,v
retrieving revision 1.14
diff -u -r1.14 virtio_mmio.c
--- sys/dev/virtio/virtio_mmio.c	9 Mar 2024 11:55:59 -0000	1.14
+++ sys/dev/virtio/virtio_mmio.c	19 Sep 2024 21:44:56 -0000
@@ -446,6 +446,8 @@
 		 */
 		vsc->sc_bus_endian    = LITTLE_ENDIAN;
 		vsc->sc_struct_endian = LITTLE_ENDIAN;
+
+		vsc->sc_version_1 = true;
 	}
 
 	vsc->sc_active_features = negotiated;
Index: sys/dev/wscons/wsconsio.h
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wsconsio.h,v
retrieving revision 1.127
diff -u -r1.127 wsconsio.h
--- sys/dev/wscons/wsconsio.h	20 Jan 2024 00:23:12 -0000	1.127
+++ sys/dev/wscons/wsconsio.h	19 Sep 2024 21:44:56 -0000
@@ -367,6 +367,7 @@
 #define	WSDISPLAY_TYPE_PLFB	65	/* ARM PrimeCell PL11x */
 #define	WSDISPLAY_TYPE_SSDFB	66	/* ssdfb(4) */
 #define	WSDISPLAY_TYPE_HOLLYWOOD 67	/* Nintendo Wii "Hollywood" SoC */
+#define	WSDISPLAY_TYPE_VIOGPU	68	/* VirtIO GPU */
 
 /* Basic display information.  Not applicable to all display types. */
 struct wsdisplay_fbinfo {





Home | Main Index | Thread Index | Old Index