8725513c83a81fc06b61d17c197af7ea868d9c2a full viocon support diff --git a/share/man/man4/viocon.4 b/share/man/man4/viocon.4 index 26f2bd6c26d..12dee306859 100644 --- a/share/man/man4/viocon.4 +++ b/share/man/man4/viocon.4 @@ -58,9 +58,12 @@ by .An Stefan Fritsch Aq Mt sf@sfritsch.de . It was ported to .Nx 10.0 . -.Sh BUGS -Use as a kernel console for -.Nx -is not yet supported. .Pp -The multiport feature is not yet supported. +Kernel support was implemented by +.An Taylor R. Campbell Aq Mt riastradh@NetBSD.org . +.Pp +Early console support was implemented by +.An Emile So iMil Sc Heitor Aq Mt imil@NetBSD.org . +.Pp +Multiport support was implemented by +.An Jonathan A. Kollasch Aq Mt jakllsch@NetBSD.org . diff --git a/sys/arch/x86/x86/consinit.c b/sys/arch/x86/x86/consinit.c index 9ef384c2c07..55546095136 100644 --- a/sys/arch/x86/x86/consinit.c +++ b/sys/arch/x86/x86/consinit.c @@ -104,6 +104,14 @@ __KERNEL_RCSID(0, "$NetBSD: consinit.c,v 1.41 2025/04/30 05:15:08 imil Exp $"); #include #endif +#include "viocon.h" +#include "virtio_mmio.h" +/* XXX only on pvbus / VirtIO MMIO for now */ +#if (NVIOCON > 0) && (NVIRTIO_MMIO > 0) +#include +#include /* for kernel_map */ +#endif + #ifndef CONSDEVNAME #define CONSDEVNAME "pc" #endif @@ -263,6 +271,17 @@ dokbd: error); return; } +#if (NVIOCON > 0) && (NVIRTIO_MMIO > 0) + if (!strcmp(console_devname, "viocon")) { + /* We need uvm to be ready before we can map the MMIO region */ + if (!kernel_map) { + initted = 0; + return; + } + if (viocon_earlyinit() == 0) + return; + } +#endif #if (NCOM > 0) if (!strcmp(console_devname, "com")) { int addr = consinfo->addr; diff --git a/sys/dev/virtio/arch/x86/virtio_mmio_cmdline.c b/sys/dev/virtio/arch/x86/virtio_mmio_cmdline.c index 18a0ce9f8bc..b55c5a03ae4 100644 --- a/sys/dev/virtio/arch/x86/virtio_mmio_cmdline.c +++ b/sys/dev/virtio/arch/x86/virtio_mmio_cmdline.c @@ -63,21 +63,17 @@ #define VIRTIO_PRIVATE #include +#include #include #include +#include +#include #include #include "ioapic.h" #define VMMIOSTR "virtio_mmio.device=" -struct mmio_args { - uint64_t sz; - uint64_t baseaddr; - uint64_t irq; - uint64_t id; -}; - struct virtio_mmio_cmdline_softc { struct virtio_mmio_softc sc_msc; struct mmio_args margs; @@ -112,6 +108,8 @@ parsearg(struct mmio_args *margs, const char *arg) { char *p; + /* Bus space type */ + margs->bst = x86_bus_space_mem; /* */ margs->sz = strtoull(arg, (char **)&p, 0); if ((margs->sz == 0) || (margs->sz == UINT64_MAX)) @@ -186,50 +184,75 @@ bad: aprint_error("Error parsing virtio_mmio parameter: %s\n", arg); } +int +mmio_args_parse(struct mmio_args *margs) +{ + int keylen = strlen(VMMIOSTR); + char *next; + static char cmdline[LINE_MAX], *parg; + + /* first pass, or emptied parg from last pass */ + if (parg == NULL) { + strlcpy(cmdline, xen_start_info.cmd_line, sizeof(cmdline)); + parg = strstr(cmdline, VMMIOSTR); + } + + /* no args were found */ + if (parg == NULL) { + return MMIO_NO_ARG; + } + + /* useless, no parameters */ + if (strlen(parg) <= keylen) { + parg = NULL; + return MMIO_NO_ARG; + } + + parg += keylen; + + next = parg; + while (*next && *next != ' ') /* find end of argument */ + next++; + if (*next) { /* space */ + *next++ = '\0'; /* end the argument string */ + next = strstr(next,VMMIOSTR); + } + parsearg(margs, parg); + + if (next != NULL) + parg = next; + + if (!*parg) { + parg = NULL; + return MMIO_LAST_ARG; + } + + return MMIO_NEXT_ARG; +} + static void virtio_mmio_cmdline_attach(device_t parent, device_t self, void *aux) { struct virtio_mmio_cmdline_softc *sc = device_private(self); struct pv_attach_args *pvaa = aux; struct mmio_args *margs = &sc->margs; - int keylen = strlen(VMMIOSTR); - char *next; - static char cmdline[LINE_MAX], *parg = NULL; + int mmioarg; aprint_normal("\n"); aprint_naive("\n"); - if (parg == NULL) { /* first pass */ - strlcpy(cmdline, xen_start_info.cmd_line, sizeof(cmdline)); - aprint_verbose_dev(self, "kernel parameters: %s\n", - cmdline); - parg = strstr(cmdline, VMMIOSTR); - } + mmioarg = mmio_args_parse(margs); - if (parg != NULL) { - parg += keylen; - if (!*parg) - return; - - next = parg; - while (*next && *next != ' ') /* find end of argument */ - next++; - if (*next) { /* space */ - *next++ = '\0'; /* end the argument string */ - next = strstr(next, VMMIOSTR); - } - - aprint_normal_dev(self, "viommio: %s\n", parg); - parsearg(margs, parg); - - if (virtio_mmio_cmdline_do_attach(self, pvaa, margs)) - return; - - if (next) { - parg = next; - config_found(parent, pvaa, NULL, CFARGS_NONE); - } - } + if (mmioarg == MMIO_NO_ARG) + return; + + aprint_normal_dev(self, "viommio: @%#" PRIxPADDR "\n", margs->baseaddr); + + if (virtio_mmio_cmdline_do_attach(self, pvaa, margs)) + return; + + if (mmioarg == MMIO_NEXT_ARG) + config_found(parent, pvaa, NULL, CFARGS_NONE); } static int @@ -250,11 +273,10 @@ virtio_mmio_cmdline_do_attach(device_t self, error = bus_space_map(msc->sc_iot, margs->baseaddr, margs->sz, 0, &msc->sc_ioh); if (error) { - aprint_error_dev(self, "couldn't map %#" PRIx64 ": %d", + aprint_error_dev(self, "couldn't map %#" PRIxPADDR ": %d", margs->baseaddr, error); return error; } - msc->sc_alloc_interrupts = virtio_mmio_cmdline_alloc_interrupts; msc->sc_free_interrupts = virtio_mmio_cmdline_free_interrupts; diff --git a/sys/dev/virtio/files.virtio b/sys/dev/virtio/files.virtio index 4e399624ab0..8d3d8def6ea 100644 --- a/sys/dev/virtio/files.virtio +++ b/sys/dev/virtio/files.virtio @@ -3,8 +3,8 @@ # XXX the contents of the following included file should be moved here include "dev/pci/files.virtio" -file dev/virtio/virtio_mmio.c virtio_mmio +file dev/virtio/virtio_mmio.c virtio_mmio needs-flag device viocon attach viocon at virtio -file dev/virtio/viocon.c viocon +file dev/virtio/viocon.c viocon needs-flag diff --git a/sys/dev/virtio/viocon.c b/sys/dev/virtio/viocon.c index 73c938936be..d402440f9f7 100644 --- a/sys/dev/virtio/viocon.c +++ b/sys/dev/virtio/viocon.c @@ -32,11 +32,24 @@ __KERNEL_RCSID(0, "$NetBSD: viocon.c,v 1.10 2024/08/05 19:13:34 riastradh Exp $" #include #include #include +#include +#include + +#include +#include +#include +#include #include -#include + +#include #include "ioconf.h" +#include "virtio_mmio.h" +#include "pv.h" +#if NPV > 0 +#include +#endif /* OpenBSD compat shims */ #define ttymalloc(speed) tty_alloc() @@ -49,6 +62,8 @@ __KERNEL_RCSID(0, "$NetBSD: viocon.c,v 1.10 2024/08/05 19:13:34 riastradh Exp $" #define VIRTIO_CONSOLE_F_MULTIPORT (1ULL<<1) #define VIRTIO_CONSOLE_F_EMERG_WRITE (1ULL<<2) +#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) + /* config space */ #define VIRTIO_CONSOLE_COLS 0 /* 16 bits */ #define VIRTIO_CONSOLE_ROWS 2 /* 16 bits */ @@ -69,12 +84,15 @@ __KERNEL_RCSID(0, "$NetBSD: viocon.c,v 1.10 2024/08/05 19:13:34 riastradh Exp $" "b\x01" "MULTIPORT\0" \ "b\x02" "EMERG_WRITE\0" +/* The control virtqueues are after the first port */ +#define VIOCON_CTRL_RX_IDX 2 +#define VIOCON_CTRL_TX_IDX 3 struct virtio_console_control { uint32_t id; /* Port number */ #define VIRTIO_CONSOLE_DEVICE_READY 0 -#define VIRTIO_CONSOLE_PORT_ADD 1 -#define VIRTIO_CONSOLE_PORT_REMOVE 2 +#define VIRTIO_CONSOLE_DEVICE_ADD 1 +#define VIRTIO_CONSOLE_DEVICE_REMOVE 2 #define VIRTIO_CONSOLE_PORT_READY 3 #define VIRTIO_CONSOLE_CONSOLE_PORT 4 #define VIRTIO_CONSOLE_RESIZE 5 @@ -83,7 +101,7 @@ struct virtio_console_control { uint16_t event; uint16_t value; -}; +} __packed; struct virtio_console_control_resize { /* yes, the order is different than in config space */ @@ -93,10 +111,10 @@ struct virtio_console_control_resize { #define BUFSIZE 128 -#define VIOCONDEV(u,p) makedev(cdevsw_lookup_major(&viocon_cdevsw), \ - ((u) << 4) | (p)) -#define VIOCONUNIT(x) (minor(x) >> 4) -#define VIOCONPORT(x) (minor(x) & 0x0f) +#define VIOCONDEV(u,p) makedev(cdevsw_lookup_major(&viocon_cdevsw), \ + ((u & 0xff) << 4) | (p & 0xf)) +#define VIOCONUNIT(x) (TTUNIT(x) >> 4) +#define VIOCONPORT(x) (TTUNIT(x) & 0x0f) struct viocon_port { struct viocon_softc *vp_sc; @@ -104,19 +122,24 @@ struct viocon_port { struct virtqueue *vp_tx; void *vp_si; struct tty *vp_tty; - const char *vp_name; + char *vp_name; bus_dma_segment_t vp_dmaseg; bus_dmamap_t vp_dmamap; -#ifdef NOTYET - unsigned int vp_host_open:1; /* XXX needs F_MULTIPORT */ - unsigned int vp_guest_open:1; /* XXX needs F_MULTIPORT */ - unsigned int vp_is_console:1; /* XXX needs F_MULTIPORT */ -#endif + + unsigned int vp_host_open:1; + unsigned int vp_guest_open:1; + unsigned int vp_is_console:1; + unsigned int vp_iflow:1; /* rx flow control */ uint16_t vp_rows; uint16_t vp_cols; u_char *vp_rx_buf; u_char *vp_tx_buf; + + struct consdev vp_cntab; + unsigned int vp_pollpos; + unsigned int vp_polllen; + bool vp_polling; }; struct viocon_softc { @@ -132,10 +155,26 @@ struct viocon_softc { unsigned int sc_max_ports; struct viocon_port **sc_ports; + + bool has_multiport; + + bus_dma_segment_t sc_dmaseg; + bus_dmamap_t sc_dmamap; + struct { + struct virtio_console_control ctrl; + uint8_t buf[BUFSIZE-8]; + } *sc_ctrl_rx; + bus_dmamap_t sc_dmamap_tx; + union { + struct virtio_console_control sc_tx; + uint8_t sc_tx_buf[16]; + }; }; int viocon_match(struct device *, struct cfdata *, void *); void viocon_attach(struct device *, struct device *, void *); +int viocon_control_rx_intr(struct virtqueue *); +int viocon_control_tx_intr(struct virtqueue *); int viocon_tx_intr(struct virtqueue *); int viocon_tx_drain(struct viocon_port *, struct virtqueue *vq); int viocon_rx_intr(struct virtqueue *); @@ -151,7 +190,28 @@ int vioconread(dev_t, struct uio *, int); int vioconwrite(dev_t, struct uio *, int); void vioconstop(struct tty *, int); int vioconioctl(dev_t, u_long, void *, int, struct lwp *); -struct tty *viocontty(dev_t dev); +static int viocon_ports_vq_alloc(struct viocon_softc *, int); +struct tty *viocontty(dev_t dev); +static dev_type_poll(vioconpoll); +static void viocon_port_destroy(struct viocon_softc *, int); +static void viocon_control_rx_fill(struct viocon_softc *); +static void viocon_control_dmamap(struct viocon_softc *); +static int viocon_control_send(struct viocon_softc *, uint32_t, uint16_t, + uint16_t ); + +static void viocon_console(struct viocon_softc *, int); +static void viocon_cnpollc(dev_t, int); +static int viocon_cngetc(dev_t); +static void viocon_cnputc(dev_t, int); +#if NVIRTIO_MMIO > 0 +static void free_viocon_mmio_vaddr(void);; +static void viocon_early_putc(dev_t, int); + +static uint32_t *early_console; +static vaddr_t viocon_mmio_vaddr = 0; + +int (*enumerate_mmio_devices)(struct mmio_args *) = NULL; +#endif CFATTACH_DECL_NEW(viocon, sizeof(struct viocon_softc), viocon_match, viocon_attach, /*detach*/NULL, /*activate*/NULL); @@ -164,7 +224,7 @@ const struct cdevsw viocon_cdevsw = { .d_ioctl = vioconioctl, .d_stop = vioconstop, .d_tty = viocontty, - .d_poll = nopoll, /* XXX */ + .d_poll = vioconpoll, .d_mmap = nommap, .d_kqfilter = ttykqfilter, .d_discard = nodiscard, @@ -182,8 +242,110 @@ dev2port(dev_t dev) { return dev2sc(dev)->sc_ports[VIOCONPORT(dev)]; } +static inline int +viocon_vqidx2portidx(int vq) +{ + return (vq >= 4) ? (vq - VIOCON_PORT_NQS) / VIOCON_PORT_NQS : 0; +} +/* XXX only on pvbus / VirtIO MMIO for now */ +#if NVIRTIO_MMIO > 0 +static void +free_viocon_mmio_vaddr(void) +{ + if (!viocon_mmio_vaddr) + return; -int viocon_match(struct device *parent, struct cfdata *match, void *aux) + pmap_kremove(viocon_mmio_vaddr, PAGE_SIZE); + pmap_update(pmap_kernel()); + uvm_km_free(kernel_map, viocon_mmio_vaddr, PAGE_SIZE, UVM_KMF_VAONLY); + viocon_mmio_vaddr = 0; +} + +static void +viocon_early_putc(dev_t dev, int c) +{ + *early_console = c; +} + +int +viocon_earlyinit(void) +{ + struct mmio_args margs; + paddr_t mmio_baseaddr = 0; + struct virtio_mmio_softc sc; + static struct consdev viocon_early_consdev = { + .cn_putc = viocon_early_putc, + .cn_getc = NULL, + .cn_pollc = NULL, + .cn_dev = NODEV, + .cn_pri = CN_NORMAL + }; + +#if NPV > 0 + enumerate_mmio_devices = mmio_args_parse; +#endif + + if (enumerate_mmio_devices == NULL) { + aprint_error("%s: no MMIO device enumeration function\n", __func__); + return -1; + } + + while ((*enumerate_mmio_devices)(&margs)) { + if (!mmio_baseaddr) { + /* Fetch a page for early MMIO mapping */ + viocon_mmio_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, + UVM_KMF_VAONLY | UVM_KMF_NOWAIT); + if (!viocon_mmio_vaddr) { + aprint_error("%s: failed to allocate MMIO page\n", + __func__); + return -1; + } + /* map a page for early virtio console */ + mmio_baseaddr = margs.baseaddr & ~(PAGE_SIZE - 1); + pmap_kenter_pa(viocon_mmio_vaddr, mmio_baseaddr, + VM_PROT_READ|VM_PROT_WRITE, PMAP_NOCACHE); + pmap_update(pmap_kernel()); + } + + sc.sc_iot = margs.bst; + /* + * viocon_mmio_vaddr = reserved page + * margs.baseaddr = pa + * mmio_baseaddr = MMIO base address + */ + sc.sc_ioh = viocon_mmio_vaddr + (margs.baseaddr - mmio_baseaddr); + sc.sc_iosize = margs.sz; + sc.sc_le_regs = (BYTE_ORDER == LITTLE_ENDIAN); + + aprint_verbose("mmio addr:%#" PRIxPADDR "\n", margs.baseaddr); + + if (bus_space_read_4(sc.sc_iot, sc.sc_ioh, + VIRTIO_MMIO_MAGIC_VALUE) != VIRTIO_MMIO_MAGIC) + continue; + if (virtio_mmio_reg_read(&sc, VIRTIO_MMIO_DEVICE_ID) != + VIRTIO_DEVICE_ID_CONSOLE) + continue; + if (!(virtio_mmio_reg_read(&sc, VIRTIO_MMIO_DEVICE_FEATURES) & + VIRTIO_CONSOLE_F_EMERG_WRITE)) + continue; + + early_console = + (uint32_t *)(sc.sc_ioh + + VIRTIO_MMIO_CONFIG + VIRTIO_CONSOLE_EMERG_WR); + + cn_tab = &viocon_early_consdev; + + return 0; + } + + free_viocon_mmio_vaddr(); + aprint_error("%s: no valid virtio console device found", __func__); + return -1; +} +#endif + +int +viocon_match(struct device *parent, struct cfdata *match, void *aux) { struct virtio_attach_args *va = aux; if (va->sc_childdevid == VIRTIO_DEVICE_ID_CONSOLE) @@ -196,6 +358,8 @@ viocon_attach(struct device *parent, struct device *self, void *aux) { struct viocon_softc *sc = device_private(self); struct virtio_softc *vsc = device_private(parent); + prop_dictionary_t dict = device_properties(self); + prop_array_t namearray; int maxports = 1; size_t nvqs; @@ -205,29 +369,63 @@ viocon_attach(struct device *parent, struct device *self, void *aux) device_xname(parent)); return; } + + virtio_child_attach_start(vsc, self, IPL_TTY, + /*req_features*/VIRTIO_CONSOLE_F_SIZE | VIRTIO_CONSOLE_F_MULTIPORT + | VIRTIO_CONSOLE_F_EMERG_WRITE | VIRTIO_F_RING_EVENT_IDX, + VIRTIO_CONSOLE_FLAG_BITS); + + if (vsc->sc_active_features & VIRTIO_CONSOLE_F_MULTIPORT) { + maxports = virtio_read_device_config_4(vsc, + VIRTIO_CONSOLE_MAX_NR_PORTS); + aprint_verbose_dev(self, + "has multiport feature, max ports: %u\n", maxports); + sc->has_multiport = true; + } else + sc->has_multiport = false; + sc->sc_virtio = vsc; sc->sc_max_ports = maxports; nvqs = VIOCON_PORT_NQS * maxports; + if (sc->has_multiport) + nvqs += 2; /* rx and tx control */ + + prop_dictionary_set_uint32(dict, "max ports", sc->sc_max_ports); + namearray = prop_array_create_with_capacity(sc->sc_max_ports); + prop_dictionary_set(dict, "port names", namearray); sc->sc_vqs = kmem_zalloc(nvqs * sizeof(sc->sc_vqs[0]), KM_SLEEP); sc->sc_ports = kmem_zalloc(maxports * sizeof(sc->sc_ports[0]), KM_SLEEP); - virtio_child_attach_start(vsc, self, IPL_TTY, - /*req_features*/VIRTIO_CONSOLE_F_SIZE, VIRTIO_CONSOLE_FLAG_BITS); - - DPRINTF("%s: softc: %p\n", __func__, sc); - if (viocon_port_create(sc, 0) != 0) { - printf("\n%s: viocon_port_create failed\n", __func__); + if (viocon_ports_vq_alloc(sc, maxports) != 0) goto err; + + if (!sc->has_multiport) { + DPRINTF("%s: softc: %p\n", __func__, sc); + /* virtqueue index handled in port creation */ + if (viocon_port_create(sc, 0) != 0) { + printf("\n%s: viocon_port_create failed\n", __func__); + goto err; + } + viocon_console(sc, 0); } if (virtio_child_attach_finish(vsc, sc->sc_vqs, nvqs, /*config_change*/NULL, /*req_flags*/0) != 0) goto err; - viocon_rx_fill(sc->sc_ports[0]); + if (sc->has_multiport) { + viocon_control_rx_fill(sc); + + viocon_control_send(sc, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 1); + + virtio_start_vq_intr(vsc, sc->sc_c_vq_rx); + virtio_start_vq_intr(vsc, sc->sc_c_vq_tx); + } + return; err: @@ -236,48 +434,276 @@ err: virtio_child_attach_failed(vsc); } -int -viocon_port_create(struct viocon_softc *sc, int portidx) +static void +viocon_control_dmamap(struct viocon_softc *sc) { struct virtio_softc *vsc = sc->sc_virtio; - int rxidx, txidx, allocsize, nsegs; + int nsegs; + void *kva; + + if (bus_dmamap_create(virtio_dmat(vsc), BUFSIZE, 1, BUFSIZE, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) { + goto err; + } + + if (bus_dmamap_create(virtio_dmat(vsc), 16, 1, 16, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_dmamap_tx) != 0) { + goto err; + } + + if (bus_dmamem_alloc(virtio_dmat(vsc), BUFSIZE, + sizeof(struct virtio_console_control), 0, &sc->sc_dmaseg, + 1, &nsegs, BUS_DMA_NOWAIT) != 0) + goto err; + + if (bus_dmamem_map(virtio_dmat(vsc), &sc->sc_dmaseg, nsegs, + BUFSIZE, &kva, BUS_DMA_NOWAIT) != 0) + goto err; + + memset(kva, 0, BUFSIZE); + sc->sc_ctrl_rx = kva; + + if (bus_dmamap_load(virtio_dmat(vsc), sc->sc_dmamap, sc->sc_ctrl_rx, + BUFSIZE, NULL, BUS_DMA_NOWAIT|BUS_DMA_READ) != 0) + goto err; + + return; +err: + panic("%s failed", __func__); +} + +static int +viocon_ports_vq_alloc(struct viocon_softc *sc, int maxports) +{ + struct virtio_softc *vsc = sc->sc_virtio; + int rxidx, txidx, i; char name[6]; struct viocon_port *vp; - void *kva; - struct tty *tp; - vp = kmem_zalloc(sizeof(*vp), KM_SLEEP); - if (vp == NULL) - return ENOMEM; - sc->sc_ports[portidx] = vp; - vp->vp_sc = sc; - DPRINTF("%s: vp: %p\n", __func__, vp); - - rxidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_RX; - txidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_TX; - - snprintf(name, sizeof(name), "p%drx", portidx); - virtio_init_vq_vqdone(vsc, &sc->sc_vqs[rxidx], rxidx, - viocon_rx_intr); - if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], BUFSIZE, 1, - name) != 0) { - printf("\nCan't alloc %s virtqueue\n", name); + for (i = 0; i < maxports; i++) { + /* The control virtqueues are after the first port. */ + if (sc->has_multiport && i == 1) { + /* allocate control virtqueues */ + sc->sc_c_vq_rx = &sc->sc_vqs[VIOCON_CTRL_RX_IDX]; + sc->sc_c_vq_tx = &sc->sc_vqs[VIOCON_CTRL_TX_IDX]; + + virtio_init_vq_vqdone(vsc, sc->sc_c_vq_rx, VIOCON_CTRL_RX_IDX, + viocon_control_rx_intr); + virtio_init_vq_vqdone(vsc, sc->sc_c_vq_tx, VIOCON_CTRL_TX_IDX, + viocon_control_tx_intr); + + if (virtio_alloc_vq(vsc, &sc->sc_vqs[VIOCON_CTRL_RX_IDX], + BUFSIZE, 1, "control_rx")) + goto err; + if (virtio_alloc_vq(vsc, &sc->sc_vqs[VIOCON_CTRL_TX_IDX], + 16, 1, "control_tx")) + goto err; + + viocon_control_dmamap(sc); + } + + vp = kmem_zalloc(sizeof(*vp), KM_SLEEP); + if (vp == NULL) + return ENOMEM; + + sc->sc_ports[i] = vp; + vp->vp_sc = sc; + DPRINTF("%s: vp: %p\n", __func__, vp); + + rxidx = (i * VIOCON_PORT_NQS) + VIOCON_PORT_RX; + txidx = (i * VIOCON_PORT_NQS) + VIOCON_PORT_TX; + + if (i > 0) { + rxidx += VIOCON_CTRL_RX_IDX; + txidx += VIOCON_CTRL_RX_IDX; + } + + snprintf(name, sizeof(name), "p%drx", i); + virtio_init_vq_vqdone(vsc, &sc->sc_vqs[rxidx], rxidx, + viocon_rx_intr); + if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], BUFSIZE, 1, + name) != 0) { + printf("\nCan't alloc %s virtqueue\n", name); + goto err; + } + vp->vp_rx = &sc->sc_vqs[rxidx]; + vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp); + DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx); + + snprintf(name, sizeof(name), "p%dtx", i); + virtio_init_vq_vqdone(vsc, &sc->sc_vqs[txidx], txidx, + viocon_tx_intr); + if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], BUFSIZE, 1, + name) != 0) { + printf("\nCan't alloc %s virtqueue\n", name); + goto err; + } + vp->vp_tx = &sc->sc_vqs[txidx]; + DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx); + } + + return 0; +err: + panic("%s failed", __func__); + return -1; +} + +static void +viocon_control_rx_fill(struct viocon_softc *sc) +{ + struct virtio_softc *vsc = sc->sc_virtio; + struct virtqueue *vq = sc->sc_c_vq_rx; + int r, slot, ndone = 0; + + while ((r = virtio_enqueue_prep(vsc, vq, &slot)) == 0) { + if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0) + break; + bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dmamap, slot * BUFSIZE, + BUFSIZE, BUS_DMASYNC_PREREAD); + virtio_enqueue_p(vsc, vq, slot, sc->sc_dmamap, slot * BUFSIZE, + BUFSIZE, 0); + virtio_enqueue_commit(vsc, vq, slot, 0); + ndone++; + } + KASSERT(r == 0 || r == EAGAIN); + if (ndone > 0) + virtio_notify(vsc, vq); +} + +int +viocon_control_tx_intr(struct virtqueue *vq) +{ + struct virtio_softc *vsc = vq->vq_owner; + struct viocon_softc *sc = device_private(virtio_child(vsc)); + int ndone = 0, slot, len; + + while (virtio_dequeue(vsc, vq, &slot, &len) == 0) { + bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dmamap_tx, + 0, 8, BUS_DMASYNC_POSTWRITE); + virtio_dequeue_commit(vsc, vq, slot); + ndone++; + } + return ndone; +} + +int +viocon_control_rx_intr(struct virtqueue *vq) +{ + struct virtio_softc *vsc = vq->vq_owner; + struct viocon_softc *sc = device_private(virtio_child(vsc)); + struct viocon_port *vp; + prop_dictionary_t properties = device_properties(sc->sc_dev); + prop_array_t namearray; + int ndone = 0, slot, len, namelen; + uint32_t id; + uint16_t event, value __unused; + + while (virtio_dequeue(vsc, vq, &slot, &len) == 0) { + bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dmamap, + slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD); + id = virtio_rw32(vsc, sc->sc_ctrl_rx[slot].ctrl.id); + event = virtio_rw16(vsc, sc->sc_ctrl_rx[slot].ctrl.event); + value = virtio_rw16(vsc, sc->sc_ctrl_rx[slot].ctrl.value); + + if (id != VIRTIO_CONSOLE_BAD_ID && id >= sc->sc_max_ports) { + DPRINTF("%s: invalid port id %d\n", __func__, id); + return 0; + } + + switch (event) { + /* https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.html#x1-3290006 */ + case VIRTIO_CONSOLE_DEVICE_ADD: + aprint_verbose_dev(sc->sc_dev, "adding port %u\n", id); + if (viocon_port_create(sc, id) == 0) { + viocon_control_send(sc, id, + VIRTIO_CONSOLE_PORT_READY, 1); + /* Give time to catch CONSOLE_PORT and PORT_OPEN */ + DELAY(50); + } + break; + case VIRTIO_CONSOLE_CONSOLE_PORT: + aprint_verbose_dev(sc->sc_dev, "%u is a console port\n", id); + sc->sc_ports[id]->vp_is_console = 1; + viocon_console(sc, id); + break; + case VIRTIO_CONSOLE_PORT_OPEN: + aprint_verbose_dev(sc->sc_dev, "host opened port %u\n", id); + sc->sc_ports[id]->vp_host_open = 1; + break; + case VIRTIO_CONSOLE_PORT_NAME: + namelen = len - sizeof(sc->sc_ctrl_rx[slot].ctrl); + if (namelen > 0) { + vp = sc->sc_ports[id]; + if (vp->vp_name != NULL) + kmem_free(vp->vp_name, strlen(vp->vp_name) + 1); + vp->vp_name = kmem_zalloc(namelen, KM_SLEEP); + memcpy(vp->vp_name, sc->sc_ctrl_rx[slot].buf, namelen); + vp->vp_name[namelen] = '\0'; + aprint_normal_dev(sc->sc_dev, "port %u name: %s\n", + id, vp->vp_name); + namearray = prop_dictionary_get(properties, "port names"); + prop_array_set_string_nocopy(namearray, id, vp->vp_name); + } + break; + case VIRTIO_CONSOLE_DEVICE_REMOVE: + if (sc->sc_ports[id] != NULL) + viocon_port_destroy(sc, id); + break; + default: + aprint_verbose("unsupported event: %u\n", event); + } + + virtio_dequeue_commit(vsc, vq, slot); + ndone++; + } + + viocon_control_rx_fill(sc); + + return ndone; +} + +static int +viocon_control_send(struct viocon_softc *sc, uint32_t id, uint16_t event, + uint16_t value) +{ + struct virtio_softc *vsc = sc->sc_virtio; + int slot; + + sc->sc_tx.id = virtio_rw32(vsc, id); + sc->sc_tx.event = virtio_rw16(vsc, event); + sc->sc_tx.value = virtio_rw16(vsc, value); + + bus_dmamap_load(virtio_dmat(vsc), sc->sc_dmamap_tx, sc->sc_tx_buf, + 8, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE); + bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dmamap_tx, + 0, 8, BUS_DMASYNC_PREWRITE); + + if (virtio_enqueue_prep(vsc, sc->sc_c_vq_tx, &slot)) { goto err; } - vp->vp_rx = &sc->sc_vqs[rxidx]; - vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp); - DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx); - - snprintf(name, sizeof(name), "p%dtx", portidx); - virtio_init_vq_vqdone(vsc, &sc->sc_vqs[txidx], txidx, - viocon_tx_intr); - if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], BUFSIZE, 1, - name) != 0) { - printf("\nCan't alloc %s virtqueue\n", name); + if (virtio_enqueue_reserve(vsc, sc->sc_c_vq_tx, slot, 1)) { goto err; } - vp->vp_tx = &sc->sc_vqs[txidx]; - DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx); + + virtio_enqueue(vsc, sc->sc_c_vq_tx, slot, sc->sc_dmamap_tx, true); + virtio_enqueue_commit(vsc, sc->sc_c_vq_tx, slot, 1); + + return 0; +err: + return -1; +} + + +int +viocon_port_create(struct viocon_softc *sc, int portidx) +{ + struct virtio_softc *vsc = sc->sc_virtio; + struct viocon_port *vp = sc->sc_ports[portidx]; + struct tty *tp; + prop_dictionary_t properties = device_properties(sc->sc_dev); + prop_array_t namearray; + int allocsize, nsegs; + void *kva; allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE; @@ -314,16 +740,64 @@ viocon_port_create(struct viocon_softc *sc, int portidx) tp->t_dev = VIOCONDEV(device_unit(sc->sc_dev), portidx); vp->vp_tty = tp; DPRINTF("%s: tty: %p\n", __func__, tp); + vp->vp_name = kmem_zalloc(7, KM_SLEEP); + snprintf(vp->vp_name, 7, "port%02d", portidx); + + tty_attach(tp); virtio_start_vq_intr(vsc, vp->vp_rx); virtio_start_vq_intr(vsc, vp->vp_tx); + viocon_rx_fill(sc->sc_ports[portidx]); + + namearray = prop_dictionary_get(properties, "port names"); + prop_array_set_string_nocopy(namearray, portidx, vp->vp_name); + return 0; err: panic("%s failed", __func__); return -1; } +static void +viocon_port_destroy(struct viocon_softc *sc, int portidx) +{ + struct viocon_port *vp = sc->sc_ports[portidx]; + + if (vp == NULL) + return; + + viocon_control_send(sc, portidx, VIRTIO_CONSOLE_PORT_READY, 0); + + if (vp->vp_name != NULL) + kmem_free(vp->vp_name, strlen(vp->vp_name) + 1); + tty_free(vp->vp_tty); + vp = NULL; +} + +static void +viocon_console(struct viocon_softc *sc, int portidx) +{ + struct viocon_port *vp = sc->sc_ports[portidx]; + + if (cn_tab != NULL && cn_tab->cn_dev != NODEV) + return; + + sc->sc_ports[portidx]->vp_cntab = (struct consdev) { + .cn_pollc = viocon_cnpollc, + .cn_getc = viocon_cngetc, + .cn_putc = viocon_cnputc, + .cn_dev = vp->vp_tty->t_dev, + .cn_pri = CN_REMOTE, + }; + aprint_normal_dev(sc->sc_dev, "console\n"); + cn_tab = &sc->sc_ports[portidx]->vp_cntab; +#if NVIRTIO_MMIO > 0 + /* if a page was mapped for early console, free it */ + free_viocon_mmio_vaddr(); +#endif +} + int viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq) { @@ -346,17 +820,19 @@ viocon_tx_intr(struct virtqueue *vq) { struct virtio_softc *vsc = vq->vq_owner; struct viocon_softc *sc = device_private(virtio_child(vsc)); - int ndone = 0; - int portidx = (vq->vq_index - 1) / 2; + int portidx = viocon_vqidx2portidx(vq->vq_index); struct viocon_port *vp = sc->sc_ports[portidx]; struct tty *tp = vp->vp_tty; + int ndone = 0; splassert(IPL_TTY); ndone = viocon_tx_drain(vp, vq); + ttylock(tp); if (ndone && ISSET(tp->t_state, TS_BUSY)) { CLR(tp->t_state, TS_BUSY); (*tp->t_linesw->l_start)(tp); } + ttyunlock(tp); return 1; } @@ -388,7 +864,7 @@ viocon_rx_intr(struct virtqueue *vq) { struct virtio_softc *vsc = vq->vq_owner; struct viocon_softc *sc = device_private(virtio_child(vsc)); - int portidx = (vq->vq_index - 1) / 2; + int portidx = viocon_vqidx2portidx(vq->vq_index); struct viocon_port *vp = sc->sc_ports[portidx]; softint_schedule(vp->vp_si); @@ -414,6 +890,9 @@ viocon_rx_soft(void *arg) virtio_dequeue_commit(vsc, vq, slot); } + if (virtio_features(vsc) & VIRTIO_F_RING_EVENT_IDX) + virtio_start_vq_intr(vsc, vp->vp_rx); + viocon_rx_fill(vp); return; @@ -444,10 +923,16 @@ vioconstart(struct tty *tp) if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) goto out; + if (!ttypull(tp)) + goto out; + if (tp->t_outq.c_cc == 0) goto out; ndone = 0; + if (virtio_features(vsc) & VIRTIO_F_RING_EVENT_IDX) + virtio_start_vq_intr(vsc, vp->vp_tx); + while (tp->t_outq.c_cc > 0) { ret = virtio_enqueue_prep(vsc, vq, &slot); if (ret == EAGAIN) { @@ -466,6 +951,7 @@ vioconstart(struct tty *tp) vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1); virtio_enqueue_commit(vsc, vq, slot, 0); ndone++; + ttypull(tp); } if (ndone > 0) virtio_notify(vsc, vq); @@ -526,9 +1012,8 @@ vioconopen(dev_t dev, int flag, int mode, struct lwp *l) } vp = sc->sc_ports[port]; tp = vp->vp_tty; -#ifdef NOTYET vp->vp_guest_open = 1; -#endif + splx(s); if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) @@ -553,7 +1038,18 @@ vioconopen(dev_t dev, int flag, int mode, struct lwp *l) } splx(s); + error = ttyopen(tp, TTDIALOUT(dev), ISSET(flag, O_NONBLOCK)); + if (error) + goto bad; + error = (*tp->t_linesw->l_open)(dev, tp); + if (error) + goto bad; + + if ((virtio_features(sc->sc_virtio) & VIRTIO_CONSOLE_F_MULTIPORT) != 0) { + viocon_control_send(sc, port, VIRTIO_CONSOLE_PORT_OPEN, 1); + } +bad: return error; } @@ -569,13 +1065,18 @@ vioconclose(dev_t dev, int flag, int mode, struct lwp *l) (*tp->t_linesw->l_close)(tp, flag); s = spltty(); -#ifdef NOTYET vp->vp_guest_open = 0; -#endif + CLR(tp->t_state, TS_BUSY | TS_FLUSH); ttyclose(tp); splx(s); + if ((virtio_features(vp->vp_sc->sc_virtio) & + VIRTIO_CONSOLE_F_MULTIPORT) != 0) { + viocon_control_send(vp->vp_sc, VIOCONPORT(dev), + VIRTIO_CONSOLE_PORT_OPEN, 0); + } + return 0; } @@ -634,3 +1135,86 @@ vioconioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) return error2; return ENOTTY; } + +static void +viocon_cnpollc(dev_t dev, int on) +{ + struct viocon_port *vp = dev2port(dev); + int s; + + KASSERT((bool)on != vp->vp_polling); + + s = spltty(); + vp->vp_polling = on; + vioconhwiflow(vp->vp_tty, on); + splx(s); +} + +static int +viocon_cngetc(dev_t dev) +{ + struct viocon_softc *sc = dev2sc(dev); + struct viocon_port *vp = dev2port(dev); + struct virtqueue *vq = vp->vp_rx; + struct virtio_softc *vsc = sc->sc_virtio; + int slot, len; + + KASSERT(vp->vp_polling); + while (vp->vp_polllen == 0) { + if (virtio_dequeue(vsc, vq, &slot, &len) == 0) { + KASSERTMSG(slot >= 0, "slot=%d", slot); + KASSERTMSG(slot < vq->vq_num, "slot=%d", slot); + KASSERTMSG(len > 0, "len=%d", len); + KASSERTMSG(len <= BUFSIZE, "len=%d", len); + bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, + slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD); + vp->vp_polllen = len; + vp->vp_pollpos = slot * BUFSIZE; + } + } + KASSERT(vp->vp_pollpos <= vq->vq_num * BUFSIZE); + vp->vp_polllen--; + return vp->vp_rx_buf[vp->vp_pollpos++]; +} + +static void +viocon_cnputc(dev_t dev, int c) +{ + struct viocon_softc *sc = dev2sc(dev); + struct viocon_port *vp = dev2port(dev); + struct virtqueue *vq = vp->vp_tx; + struct virtio_softc *vsc = sc->sc_virtio; + int slot; + int s, error; + + s = spltty(); + KERNEL_LOCK(1, NULL); + (void)viocon_tx_drain(vp, vq); + error = virtio_enqueue_prep(vsc, vq, &slot); + if (error == 0) { + error = virtio_enqueue_reserve(vsc, vq, slot, 1); + KASSERTMSG(error == 0, "error=%d", error); + vp->vp_tx_buf[slot * BUFSIZE] = c; + bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, + vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, 1, + BUS_DMASYNC_PREWRITE); + virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, + vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, 1, 1); + virtio_enqueue_commit(vsc, vq, slot, 1); + } + KERNEL_UNLOCK_ONE(NULL); + splx(s); +} + +static int +vioconpoll(dev_t dev, int events, struct lwp *l) +{ + struct viocon_port *vp = dev2port(dev); + struct tty *tp = vp->vp_tty; + int revents; + + revents = tp->t_linesw->l_poll(tp, events, l); + + return revents; +} + diff --git a/sys/dev/virtio/virtio_mmio.c b/sys/dev/virtio/virtio_mmio.c index eb827f528f7..5c3f085577b 100644 --- a/sys/dev/virtio/virtio_mmio.c +++ b/sys/dev/virtio/virtio_mmio.c @@ -66,39 +66,8 @@ __KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.14 2024/03/09 11:55:59 isaki Exp $ #include #include -#define VIRTIO_PRIVATE #include -#define VIRTIO_MMIO_MAGIC ('v' | 'i' << 8 | 'r' << 16 | 't' << 24) - -#define VIRTIO_MMIO_MAGIC_VALUE 0x000 -#define VIRTIO_MMIO_VERSION 0x004 -#define VIRTIO_MMIO_DEVICE_ID 0x008 -#define VIRTIO_MMIO_VENDOR_ID 0x00c -#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 /* "HostFeatures" in v1 */ -#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 /* "HostFeaturesSel" in v1 */ -#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* "GuestFeatures" in v1 */ -#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 /* "GuestFeaturesSel" in v1 */ -#define VIRTIO_MMIO_V1_GUEST_PAGE_SIZE 0x028 -#define VIRTIO_MMIO_QUEUE_SEL 0x030 -#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 -#define VIRTIO_MMIO_QUEUE_NUM 0x038 -#define VIRTIO_MMIO_V1_QUEUE_ALIGN 0x03c -#define VIRTIO_MMIO_V1_QUEUE_PFN 0x040 -#define VIRTIO_MMIO_QUEUE_READY 0x044 -#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 -#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 -#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 -#define VIRTIO_MMIO_STATUS 0x070 -#define VIRTIO_MMIO_V2_QUEUE_DESC_LOW 0x080 -#define VIRTIO_MMIO_V2_QUEUE_DESC_HIGH 0x084 -#define VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW 0x090 -#define VIRTIO_MMIO_V2_QUEUE_AVAIL_HIGH 0x094 -#define VIRTIO_MMIO_V2_QUEUE_USED_LOW 0x0a0 -#define VIRTIO_MMIO_V2_QUEUE_USED_HIGH 0x0a4 -#define VIRTIO_MMIO_V2_CONFIG_GEN 0x0fc -#define VIRTIO_MMIO_CONFIG 0x100 - /* * MMIO configuration space for virtio-mmio v1 is in guest byte order. * @@ -128,7 +97,7 @@ static int virtio_mmio_alloc_interrupts(struct virtio_softc *); static void virtio_mmio_free_interrupts(struct virtio_softc *); static int virtio_mmio_setup_interrupts(struct virtio_softc *, int); -static uint32_t +uint32_t virtio_mmio_reg_read(struct virtio_mmio_softc *sc, bus_addr_t reg) { uint32_t val; diff --git a/sys/dev/virtio/virtio_mmiovar.h b/sys/dev/virtio/virtio_mmiovar.h index c95ec285287..bcd4066b756 100644 --- a/sys/dev/virtio/virtio_mmiovar.h +++ b/sys/dev/virtio/virtio_mmiovar.h @@ -28,8 +28,40 @@ #ifndef _VIRTIO_MMIOVAR_H_ #define _VIRTIO_MMIOVAR_H_ +#define VIRTIO_PRIVATE + #include /* XXX: move to non-pci */ +#define VIRTIO_MMIO_MAGIC ('v' | 'i' << 8 | 'r' << 16 | 't' << 24) + +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 +#define VIRTIO_MMIO_VERSION 0x004 +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 /* "HostFeatures" in v1 */ +#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 /* "HostFeaturesSel" in v1 */ +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* "GuestFeatures" in v1 */ +#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 /* "GuestFeaturesSel" in v1 */ +#define VIRTIO_MMIO_V1_GUEST_PAGE_SIZE 0x028 +#define VIRTIO_MMIO_QUEUE_SEL 0x030 +#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define VIRTIO_MMIO_QUEUE_NUM 0x038 +#define VIRTIO_MMIO_V1_QUEUE_ALIGN 0x03c +#define VIRTIO_MMIO_V1_QUEUE_PFN 0x040 +#define VIRTIO_MMIO_QUEUE_READY 0x044 +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_V2_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_V2_QUEUE_DESC_HIGH 0x084 +#define VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW 0x090 +#define VIRTIO_MMIO_V2_QUEUE_AVAIL_HIGH 0x094 +#define VIRTIO_MMIO_V2_QUEUE_USED_LOW 0x0a0 +#define VIRTIO_MMIO_V2_QUEUE_USED_HIGH 0x0a4 +#define VIRTIO_MMIO_V2_CONFIG_GEN 0x0fc +#define VIRTIO_MMIO_CONFIG 0x100 + struct virtio_mmio_softc { struct virtio_softc sc_sc; @@ -46,9 +78,20 @@ struct virtio_mmio_softc { }; +struct mmio_args { + bus_space_tag_t bst; + uint64_t sz; + paddr_t baseaddr; + uint64_t irq; + uint64_t id; +}; + +uint32_t virtio_mmio_reg_read(struct virtio_mmio_softc *, bus_addr_t); bool virtio_mmio_common_probe_present(struct virtio_mmio_softc *); void virtio_mmio_common_attach(struct virtio_mmio_softc *); int virtio_mmio_common_detach(struct virtio_mmio_softc *, int); int virtio_mmio_intr(void *); +extern int (*enumerate_mmio_devices)(struct mmio_args *); + #endif /* _VIRTIO_MMIOVAR_H_ */