1*3b372c34Sjsg /* $OpenBSD: vldcp.c,v 1.26 2024/05/14 08:26:13 jsg Exp $ */
21e3aa971Skettenis /*
31e3aa971Skettenis * Copyright (c) 2009, 2012 Mark Kettenis
41e3aa971Skettenis *
51e3aa971Skettenis * Permission to use, copy, modify, and distribute this software for any
61e3aa971Skettenis * purpose with or without fee is hereby granted, provided that the above
71e3aa971Skettenis * copyright notice and this permission notice appear in all copies.
81e3aa971Skettenis *
91e3aa971Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
101e3aa971Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
111e3aa971Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
121e3aa971Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
131e3aa971Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
141e3aa971Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
151e3aa971Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
161e3aa971Skettenis */
171e3aa971Skettenis
181e3aa971Skettenis #include <sys/param.h>
191e3aa971Skettenis #include <sys/conf.h>
201e3aa971Skettenis #include <sys/device.h>
211e3aa971Skettenis #include <sys/malloc.h>
221e3aa971Skettenis #include <sys/proc.h>
231e3aa971Skettenis #include <sys/systm.h>
241e3aa971Skettenis
251e3aa971Skettenis #include <machine/autoconf.h>
261e3aa971Skettenis #include <machine/hypervisor.h>
271e3aa971Skettenis #include <machine/mdesc.h>
281e3aa971Skettenis
298716a13cSmpi #include <uvm/uvm_extern.h>
301e3aa971Skettenis
311e3aa971Skettenis #include <sparc64/dev/cbusvar.h>
321e3aa971Skettenis #include <sparc64/dev/ldcvar.h>
331e3aa971Skettenis
341e3aa971Skettenis #ifdef VLDCP_DEBUG
351e3aa971Skettenis #define DPRINTF(x) printf x
361e3aa971Skettenis #else
371e3aa971Skettenis #define DPRINTF(x)
381e3aa971Skettenis #endif
391e3aa971Skettenis
401e3aa971Skettenis #include <sys/ioccom.h>
411e3aa971Skettenis
421e3aa971Skettenis struct hv_io {
431e3aa971Skettenis uint64_t hi_cookie;
441e3aa971Skettenis void *hi_addr;
451e3aa971Skettenis size_t hi_len;
461e3aa971Skettenis };
471e3aa971Skettenis
481e3aa971Skettenis #define HVIOCREAD _IOW('h', 0, struct hv_io)
491e3aa971Skettenis #define HVIOCWRITE _IOW('h', 1, struct hv_io)
501e3aa971Skettenis
511e3aa971Skettenis #define VLDCP_TX_ENTRIES 128
521e3aa971Skettenis #define VLDCP_RX_ENTRIES 128
531e3aa971Skettenis
541e3aa971Skettenis struct vldcp_softc {
551e3aa971Skettenis struct device sc_dv;
561e3aa971Skettenis bus_space_tag_t sc_bustag;
571e3aa971Skettenis bus_dma_tag_t sc_dmatag;
581e3aa971Skettenis
59b3a497edSkettenis uint64_t sc_tx_ino;
60b3a497edSkettenis uint64_t sc_rx_ino;
611e3aa971Skettenis void *sc_tx_ih;
621e3aa971Skettenis void *sc_rx_ih;
631e3aa971Skettenis
641e3aa971Skettenis struct ldc_conn sc_lc;
65a9a1af2cSkettenis
66a9a1af2cSkettenis struct selinfo sc_rsel;
67a9a1af2cSkettenis struct selinfo sc_wsel;
681e3aa971Skettenis };
691e3aa971Skettenis
701e3aa971Skettenis int vldcp_match(struct device *, void *, void *);
711e3aa971Skettenis void vldcp_attach(struct device *, struct device *, void *);
72b37ca7acSmpi void filt_vldcprdetach(struct knote *);
73b37ca7acSmpi void filt_vldcpwdetach(struct knote *);
74b37ca7acSmpi int filt_vldcpread(struct knote *, long);
75b37ca7acSmpi int vldcpkqfilter(dev_t, struct knote *);
761e3aa971Skettenis
77eb7eaf8dSmpi const struct cfattach vldcp_ca = {
781e3aa971Skettenis sizeof(struct vldcp_softc), vldcp_match, vldcp_attach
791e3aa971Skettenis };
801e3aa971Skettenis
811e3aa971Skettenis struct cfdriver vldcp_cd = {
821e3aa971Skettenis NULL, "vldcp", DV_DULL
831e3aa971Skettenis };
841e3aa971Skettenis
851e3aa971Skettenis int vldcp_tx_intr(void *);
861e3aa971Skettenis int vldcp_rx_intr(void *);
871e3aa971Skettenis
88d2a8e8c8Skettenis /*
89d2a8e8c8Skettenis * We attach to certain well-known channels. These are assigned fixed
90d2a8e8c8Skettenis * device minor device numbers through their index in the table below.
91d2a8e8c8Skettenis * So "hvctl" gets minor 0, "spds" gets minor 1, etc. etc.
92d2a8e8c8Skettenis *
93d2a8e8c8Skettenis * We also attach to the domain services channels. These are named
94d2a8e8c8Skettenis * "ldom-<guestname>" and get assigned a device minor starting at
95d2a8e8c8Skettenis * VLDC_LDOM_OFFSET.
96d2a8e8c8Skettenis */
97d2a8e8c8Skettenis #define VLDC_NUM_SERVICES 64
98d2a8e8c8Skettenis #define VLDC_LDOM_OFFSET 32
99d2a8e8c8Skettenis int vldc_num_ldoms;
100d2a8e8c8Skettenis
1011e3aa971Skettenis struct vldc_svc {
1021e3aa971Skettenis const char *vs_name;
1031e3aa971Skettenis struct vldcp_softc *vs_sc;
1041e3aa971Skettenis };
1051e3aa971Skettenis
106d2a8e8c8Skettenis struct vldc_svc vldc_svc[VLDC_NUM_SERVICES] = {
1071e3aa971Skettenis { "hvctl" },
1081e3aa971Skettenis { "spds" },
1091e3aa971Skettenis { NULL }
1101e3aa971Skettenis };
1111e3aa971Skettenis
1121e3aa971Skettenis int
vldcp_match(struct device * parent,void * match,void * aux)1131e3aa971Skettenis vldcp_match(struct device *parent, void *match, void *aux)
1141e3aa971Skettenis {
1151e3aa971Skettenis struct cbus_attach_args *ca = aux;
1161e3aa971Skettenis struct vldc_svc *svc;
1171e3aa971Skettenis
1181e3aa971Skettenis for (svc = vldc_svc; svc->vs_name != NULL; svc++)
1191e3aa971Skettenis if (strcmp(ca->ca_name, svc->vs_name) == 0)
1201e3aa971Skettenis return (1);
1211e3aa971Skettenis
122d2a8e8c8Skettenis if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
123d2a8e8c8Skettenis strcmp(ca->ca_name, "ldom-primary") != 0)
124d2a8e8c8Skettenis return (1);
125d2a8e8c8Skettenis
1261e3aa971Skettenis return (0);
1271e3aa971Skettenis }
1281e3aa971Skettenis
1291e3aa971Skettenis void
vldcp_attach(struct device * parent,struct device * self,void * aux)1301e3aa971Skettenis vldcp_attach(struct device *parent, struct device *self, void *aux)
1311e3aa971Skettenis {
1321e3aa971Skettenis struct vldcp_softc *sc = (struct vldcp_softc *)self;
1331e3aa971Skettenis struct cbus_attach_args *ca = aux;
1341e3aa971Skettenis struct vldc_svc *svc;
1351e3aa971Skettenis struct ldc_conn *lc;
1361e3aa971Skettenis
1371e3aa971Skettenis sc->sc_bustag = ca->ca_bustag;
1381e3aa971Skettenis sc->sc_dmatag = ca->ca_dmatag;
139b3a497edSkettenis sc->sc_tx_ino = ca->ca_tx_ino;
140b3a497edSkettenis sc->sc_rx_ino = ca->ca_rx_ino;
1411e3aa971Skettenis
142b3a497edSkettenis printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_ino, sc->sc_rx_ino);
1431e3aa971Skettenis
1441e3aa971Skettenis /*
1451e3aa971Skettenis * Un-configure queues before registering interrupt handlers,
1461e3aa971Skettenis * such that we dont get any stale LDC packets or events.
1471e3aa971Skettenis */
1481e3aa971Skettenis hv_ldc_tx_qconf(ca->ca_id, 0, 0);
1491e3aa971Skettenis hv_ldc_rx_qconf(ca->ca_id, 0, 0);
1501e3aa971Skettenis
151b3a497edSkettenis sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_ino,
1521e3aa971Skettenis IPL_TTY, 0, vldcp_tx_intr, sc, sc->sc_dv.dv_xname);
153b3a497edSkettenis sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_ino,
1541e3aa971Skettenis IPL_TTY, 0, vldcp_rx_intr, sc, sc->sc_dv.dv_xname);
1551e3aa971Skettenis if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
1561e3aa971Skettenis printf(", can't establish interrupt\n");
1571e3aa971Skettenis return;
1581e3aa971Skettenis }
1591e3aa971Skettenis
1601e3aa971Skettenis lc = &sc->sc_lc;
1611e3aa971Skettenis lc->lc_id = ca->ca_id;
1621e3aa971Skettenis lc->lc_sc = sc;
1631e3aa971Skettenis
1641e3aa971Skettenis lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_TX_ENTRIES);
1651e3aa971Skettenis if (lc->lc_txq == NULL) {
1661e3aa971Skettenis printf(", can't allocate tx queue\n");
1671e3aa971Skettenis return;
1681e3aa971Skettenis }
1691e3aa971Skettenis
1701e3aa971Skettenis lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_RX_ENTRIES);
1711e3aa971Skettenis if (lc->lc_rxq == NULL) {
1721e3aa971Skettenis printf(", can't allocate rx queue\n");
1731e3aa971Skettenis goto free_txqueue;
1741e3aa971Skettenis }
175a6d5d44bSstsp lc->lc_rx_state = LDC_CHANNEL_INIT;
1761e3aa971Skettenis
177d2a8e8c8Skettenis for (svc = vldc_svc; svc->vs_name != NULL; svc++) {
178d2a8e8c8Skettenis if (strcmp(ca->ca_name, svc->vs_name) == 0) {
1791e3aa971Skettenis svc->vs_sc = sc;
180d2a8e8c8Skettenis break;
181d2a8e8c8Skettenis }
182d2a8e8c8Skettenis }
183d2a8e8c8Skettenis
184d2a8e8c8Skettenis if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
185d2a8e8c8Skettenis strcmp(ca->ca_name, "ldom-primary") != 0) {
186d2a8e8c8Skettenis int minor = VLDC_LDOM_OFFSET + vldc_num_ldoms++;
187d2a8e8c8Skettenis if (minor < nitems(vldc_svc))
188d2a8e8c8Skettenis vldc_svc[minor].vs_sc = sc;
189d2a8e8c8Skettenis }
1901e3aa971Skettenis
1911e3aa971Skettenis printf(" channel \"%s\"\n", ca->ca_name);
1921e3aa971Skettenis return;
1931e3aa971Skettenis
1941e3aa971Skettenis #if 0
1951e3aa971Skettenis free_rxqueue:
1961e3aa971Skettenis ldc_queue_free(sc->sc_dmatag, lc->lc_rxq);
1971e3aa971Skettenis #endif
1981e3aa971Skettenis free_txqueue:
1991e3aa971Skettenis ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
2001e3aa971Skettenis }
2011e3aa971Skettenis
2021e3aa971Skettenis int
vldcp_tx_intr(void * arg)2031e3aa971Skettenis vldcp_tx_intr(void *arg)
2041e3aa971Skettenis {
2051e3aa971Skettenis struct vldcp_softc *sc = arg;
2061e3aa971Skettenis struct ldc_conn *lc = &sc->sc_lc;
2071e3aa971Skettenis uint64_t tx_head, tx_tail, tx_state;
2081e3aa971Skettenis int err;
2091e3aa971Skettenis
2101e3aa971Skettenis err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
2111e3aa971Skettenis if (err != H_EOK) {
2121e3aa971Skettenis printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
2131e3aa971Skettenis return (0);
2141e3aa971Skettenis }
2151e3aa971Skettenis
2161e3aa971Skettenis if (tx_state != lc->lc_tx_state) {
2171e3aa971Skettenis switch (tx_state) {
2181e3aa971Skettenis case LDC_CHANNEL_DOWN:
2198f296756Sstsp DPRINTF(("%s: Tx link down\n", __func__));
2201e3aa971Skettenis break;
2211e3aa971Skettenis case LDC_CHANNEL_UP:
2228f296756Sstsp DPRINTF(("%s: Tx link up\n", __func__));
2231e3aa971Skettenis break;
2241e3aa971Skettenis case LDC_CHANNEL_RESET:
2258f296756Sstsp DPRINTF(("%s: Tx link reset\n", __func__));
2261e3aa971Skettenis break;
2271e3aa971Skettenis }
2281e3aa971Skettenis lc->lc_tx_state = tx_state;
2291e3aa971Skettenis }
2301e3aa971Skettenis
231b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
232a9a1af2cSkettenis selwakeup(&sc->sc_wsel);
2331e3aa971Skettenis wakeup(lc->lc_txq);
2341e3aa971Skettenis return (1);
2351e3aa971Skettenis }
2361e3aa971Skettenis
2371e3aa971Skettenis int
vldcp_rx_intr(void * arg)2381e3aa971Skettenis vldcp_rx_intr(void *arg)
2391e3aa971Skettenis {
2401e3aa971Skettenis struct vldcp_softc *sc = arg;
2411e3aa971Skettenis struct ldc_conn *lc = &sc->sc_lc;
2421e3aa971Skettenis uint64_t rx_head, rx_tail, rx_state;
2431e3aa971Skettenis int err;
2441e3aa971Skettenis
2451e3aa971Skettenis err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
2461e3aa971Skettenis if (err != H_EOK) {
2471e3aa971Skettenis printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
2481e3aa971Skettenis return (0);
2491e3aa971Skettenis }
2501e3aa971Skettenis
2511e3aa971Skettenis if (rx_state != lc->lc_rx_state) {
2521e3aa971Skettenis switch (rx_state) {
2531e3aa971Skettenis case LDC_CHANNEL_DOWN:
2548f296756Sstsp DPRINTF(("%s: Rx link down\n", __func__));
255e36e7d99Sstsp if (rx_head == rx_tail)
256e36e7d99Sstsp break;
257e36e7d99Sstsp /* Discard and ack pending I/O. */
258566caca0Sstsp DPRINTF(("setting rx qhead to %llx\n", rx_tail));
259e36e7d99Sstsp err = hv_ldc_rx_set_qhead(lc->lc_id, rx_tail);
260e36e7d99Sstsp if (err == H_EOK)
261e36e7d99Sstsp break;
262e36e7d99Sstsp printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
2631e3aa971Skettenis break;
2641e3aa971Skettenis case LDC_CHANNEL_UP:
2658f296756Sstsp DPRINTF(("%s: Rx link up\n", __func__));
2661e3aa971Skettenis break;
2671e3aa971Skettenis case LDC_CHANNEL_RESET:
2688f296756Sstsp DPRINTF(("%s: Rx link reset\n", __func__));
269566caca0Sstsp if (rx_head == rx_tail)
270566caca0Sstsp break;
271566caca0Sstsp /* Discard and ack pending I/O. */
272566caca0Sstsp DPRINTF(("setting rx qhead to %llx\n", rx_tail));
273566caca0Sstsp err = hv_ldc_rx_set_qhead(lc->lc_id, rx_tail);
274566caca0Sstsp if (err == H_EOK)
275566caca0Sstsp break;
276566caca0Sstsp printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
2771e3aa971Skettenis break;
2781e3aa971Skettenis }
2791e3aa971Skettenis lc->lc_rx_state = rx_state;
280b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
281b3a497edSkettenis INTR_DISABLED);
282a9a1af2cSkettenis selwakeup(&sc->sc_rsel);
283a9a1af2cSkettenis wakeup(lc->lc_rxq);
2841e3aa971Skettenis return (1);
2851e3aa971Skettenis }
2861e3aa971Skettenis
2871e3aa971Skettenis if (rx_head == rx_tail)
2881e3aa971Skettenis return (0);
2891e3aa971Skettenis
290b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
291a9a1af2cSkettenis selwakeup(&sc->sc_rsel);
2921e3aa971Skettenis wakeup(lc->lc_rxq);
2931e3aa971Skettenis return (1);
2941e3aa971Skettenis }
2951e3aa971Skettenis
2961e3aa971Skettenis cdev_decl(vldcp);
2971e3aa971Skettenis struct vldcp_softc *vldcp_lookup(dev_t);
2981e3aa971Skettenis
2991e3aa971Skettenis struct vldcp_softc *
vldcp_lookup(dev_t dev)3001e3aa971Skettenis vldcp_lookup(dev_t dev)
3011e3aa971Skettenis {
3021e3aa971Skettenis struct vldcp_softc *sc = NULL;
3031e3aa971Skettenis
3041e3aa971Skettenis if (minor(dev) < nitems(vldc_svc))
3051e3aa971Skettenis sc = vldc_svc[minor(dev)].vs_sc;
3061e3aa971Skettenis
3071e3aa971Skettenis if (sc)
3081e3aa971Skettenis device_ref(&sc->sc_dv);
3091e3aa971Skettenis
3101e3aa971Skettenis return (sc);
3111e3aa971Skettenis }
3121e3aa971Skettenis
3131e3aa971Skettenis int
vldcpopen(dev_t dev,int flag,int mode,struct proc * p)3141e3aa971Skettenis vldcpopen(dev_t dev, int flag, int mode, struct proc *p)
3151e3aa971Skettenis {
3161e3aa971Skettenis struct vldcp_softc *sc;
3171e3aa971Skettenis struct ldc_conn *lc;
3187495348fSkettenis uint64_t rx_head, rx_tail, rx_state;
3191e3aa971Skettenis int err;
3201e3aa971Skettenis
3211e3aa971Skettenis sc = vldcp_lookup(dev);
3221e3aa971Skettenis if (sc == NULL)
3231e3aa971Skettenis return (ENXIO);
3241e3aa971Skettenis lc = &sc->sc_lc;
3251e3aa971Skettenis
3261e3aa971Skettenis err = hv_ldc_tx_qconf(lc->lc_id,
3271e3aa971Skettenis lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
3281e3aa971Skettenis if (err != H_EOK)
3291e3aa971Skettenis printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
3301e3aa971Skettenis
3311e3aa971Skettenis err = hv_ldc_rx_qconf(lc->lc_id,
3321e3aa971Skettenis lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
3331e3aa971Skettenis if (err != H_EOK)
33402aa388cSkettenis printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
3351e3aa971Skettenis
3367495348fSkettenis /* Clear a pending channel reset. */
3377495348fSkettenis err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
3387495348fSkettenis if (err != H_EOK)
3397495348fSkettenis printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
3407495348fSkettenis
3411e3aa971Skettenis device_unref(&sc->sc_dv);
3421e3aa971Skettenis return (0);
3431e3aa971Skettenis }
3441e3aa971Skettenis
3451e3aa971Skettenis int
vldcpclose(dev_t dev,int flag,int mode,struct proc * p)3461e3aa971Skettenis vldcpclose(dev_t dev, int flag, int mode, struct proc *p)
3471e3aa971Skettenis {
3481e3aa971Skettenis struct vldcp_softc *sc;
3491e3aa971Skettenis
3501e3aa971Skettenis sc = vldcp_lookup(dev);
3511e3aa971Skettenis if (sc == NULL)
3521e3aa971Skettenis return (ENXIO);
3531e3aa971Skettenis
354b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
355b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
3561e3aa971Skettenis
3571e3aa971Skettenis hv_ldc_tx_qconf(sc->sc_lc.lc_id, 0, 0);
3581e3aa971Skettenis hv_ldc_rx_qconf(sc->sc_lc.lc_id, 0, 0);
3591e3aa971Skettenis
3601e3aa971Skettenis device_unref(&sc->sc_dv);
3611e3aa971Skettenis return (0);
3621e3aa971Skettenis }
3631e3aa971Skettenis
3641e3aa971Skettenis int
vldcpread(dev_t dev,struct uio * uio,int ioflag)3651e3aa971Skettenis vldcpread(dev_t dev, struct uio *uio, int ioflag)
3661e3aa971Skettenis {
3671e3aa971Skettenis struct vldcp_softc *sc;
3681e3aa971Skettenis struct ldc_conn *lc;
3691e3aa971Skettenis uint64_t rx_head, rx_tail, rx_state;
3701e3aa971Skettenis int err, ret;
3711e3aa971Skettenis int s;
3721e3aa971Skettenis
3731e3aa971Skettenis sc = vldcp_lookup(dev);
3741e3aa971Skettenis if (sc == NULL)
3751e3aa971Skettenis return (ENXIO);
3761e3aa971Skettenis lc = &sc->sc_lc;
3771e3aa971Skettenis
3781e3aa971Skettenis if (uio->uio_resid != 64) {
3791e3aa971Skettenis device_unref(&sc->sc_dv);
3801e3aa971Skettenis return (EINVAL);
3811e3aa971Skettenis }
3821e3aa971Skettenis
3831e3aa971Skettenis s = spltty();
3841e3aa971Skettenis retry:
3851e3aa971Skettenis err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
3861e3aa971Skettenis if (err != H_EOK) {
3871e3aa971Skettenis splx(s);
3881e3aa971Skettenis printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
3891e3aa971Skettenis device_unref(&sc->sc_dv);
3901e3aa971Skettenis return (EIO);
3911e3aa971Skettenis }
3921e3aa971Skettenis
393566caca0Sstsp DPRINTF(("rx head %llx, rx tail %llx, state %lld\n", rx_head, rx_tail, rx_state));
394566caca0Sstsp
3951e3aa971Skettenis if (rx_state != LDC_CHANNEL_UP) {
3961e3aa971Skettenis splx(s);
3971e3aa971Skettenis device_unref(&sc->sc_dv);
3981e3aa971Skettenis return (EIO);
3991e3aa971Skettenis }
4001e3aa971Skettenis
4011e3aa971Skettenis if (rx_head == rx_tail) {
402b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
403b3a497edSkettenis INTR_ENABLED);
404d6fc7890Scheloha ret = tsleep_nsec(lc->lc_rxq, PWAIT | PCATCH, "hvrd", INFSLP);
4051e3aa971Skettenis if (ret) {
4061e3aa971Skettenis splx(s);
4071e3aa971Skettenis device_unref(&sc->sc_dv);
4081e3aa971Skettenis return (ret);
4091e3aa971Skettenis }
4101e3aa971Skettenis goto retry;
4111e3aa971Skettenis }
4121e3aa971Skettenis
41368343589Smiod ret = uiomove(lc->lc_rxq->lq_va + rx_head, 64, uio);
4141e3aa971Skettenis
4151e3aa971Skettenis rx_head += 64;
4161e3aa971Skettenis rx_head &= ((lc->lc_rxq->lq_nentries * 64) - 1);
4171e3aa971Skettenis err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
4181e3aa971Skettenis if (err != H_EOK)
4191e3aa971Skettenis printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
4201e3aa971Skettenis
4212624aec2Sstsp splx(s);
4221e3aa971Skettenis device_unref(&sc->sc_dv);
4231e3aa971Skettenis return (ret);
4241e3aa971Skettenis }
4251e3aa971Skettenis
4261e3aa971Skettenis int
vldcpwrite(dev_t dev,struct uio * uio,int ioflag)4271e3aa971Skettenis vldcpwrite(dev_t dev, struct uio *uio, int ioflag)
4281e3aa971Skettenis {
4291e3aa971Skettenis struct vldcp_softc *sc;
4301e3aa971Skettenis struct ldc_conn *lc;
4311e3aa971Skettenis uint64_t tx_head, tx_tail, tx_state;
4321e3aa971Skettenis uint64_t next_tx_tail;
4331e3aa971Skettenis int err, ret;
4341e3aa971Skettenis int s;
4351e3aa971Skettenis
4361e3aa971Skettenis sc = vldcp_lookup(dev);
4371e3aa971Skettenis if (sc == NULL)
4381e3aa971Skettenis return (ENXIO);
4391e3aa971Skettenis lc = &sc->sc_lc;
4401e3aa971Skettenis
4411e3aa971Skettenis if (uio->uio_resid != 64) {
4421e3aa971Skettenis device_unref(&sc->sc_dv);
4431e3aa971Skettenis return (EINVAL);
4441e3aa971Skettenis }
4451e3aa971Skettenis
4461e3aa971Skettenis s = spltty();
4471e3aa971Skettenis retry:
4481e3aa971Skettenis err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
4491e3aa971Skettenis if (err != H_EOK) {
4501e3aa971Skettenis splx(s);
4511e3aa971Skettenis printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
4521e3aa971Skettenis device_unref(&sc->sc_dv);
4531e3aa971Skettenis return (EIO);
4541e3aa971Skettenis }
4551e3aa971Skettenis
4561e3aa971Skettenis if (tx_state != LDC_CHANNEL_UP) {
4571e3aa971Skettenis splx(s);
4581e3aa971Skettenis device_unref(&sc->sc_dv);
4591e3aa971Skettenis return (EIO);
4601e3aa971Skettenis }
4611e3aa971Skettenis
4621e3aa971Skettenis DPRINTF(("tx head %llx, tx tail %llx\n", tx_head, tx_tail));
4631e3aa971Skettenis
4641e3aa971Skettenis next_tx_tail = tx_tail + 64;
4651e3aa971Skettenis next_tx_tail &= ((lc->lc_txq->lq_nentries * 64) - 1);
4661e3aa971Skettenis
4671e3aa971Skettenis if (tx_head == next_tx_tail) {
468b3a497edSkettenis cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
469b3a497edSkettenis INTR_ENABLED);
470d6fc7890Scheloha ret = tsleep_nsec(lc->lc_txq, PWAIT | PCATCH, "hvwr", INFSLP);
4711e3aa971Skettenis if (ret) {
4721e3aa971Skettenis splx(s);
4731e3aa971Skettenis device_unref(&sc->sc_dv);
4741e3aa971Skettenis return (ret);
4751e3aa971Skettenis }
4761e3aa971Skettenis goto retry;
4771e3aa971Skettenis }
4781e3aa971Skettenis splx(s);
4791e3aa971Skettenis
48068343589Smiod ret = uiomove(lc->lc_txq->lq_va + tx_tail, 64, uio);
4811e3aa971Skettenis
4821e3aa971Skettenis err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail);
4831e3aa971Skettenis if (err != H_EOK) {
4841e3aa971Skettenis printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
4851e3aa971Skettenis device_unref(&sc->sc_dv);
4861e3aa971Skettenis return (EIO);
4871e3aa971Skettenis }
4881e3aa971Skettenis
4891e3aa971Skettenis device_unref(&sc->sc_dv);
4901e3aa971Skettenis return (ret);
4911e3aa971Skettenis }
4921e3aa971Skettenis
4931e3aa971Skettenis int
vldcpioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)4941e3aa971Skettenis vldcpioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
4951e3aa971Skettenis {
4961e3aa971Skettenis struct vldcp_softc *sc;
4971e3aa971Skettenis struct ldc_conn *lc;
4981e3aa971Skettenis struct hv_io *hi = (struct hv_io *)data;
4991e3aa971Skettenis paddr_t pa, offset;
5001e3aa971Skettenis psize_t nbytes;
5011e3aa971Skettenis caddr_t buf;
5021e3aa971Skettenis size_t size;
5031e3aa971Skettenis int err;
5041e3aa971Skettenis
5051e3aa971Skettenis sc = vldcp_lookup(dev);
5061e3aa971Skettenis if (sc == NULL)
5071e3aa971Skettenis return (ENXIO);
5081e3aa971Skettenis lc = &sc->sc_lc;
5091e3aa971Skettenis
5101e3aa971Skettenis switch (cmd) {
5111e3aa971Skettenis case HVIOCREAD:
5121e3aa971Skettenis case HVIOCWRITE:
5131e3aa971Skettenis break;
5141e3aa971Skettenis default:
5151e3aa971Skettenis device_unref(&sc->sc_dv);
5161e3aa971Skettenis return (ENOTTY);
5171e3aa971Skettenis }
5181e3aa971Skettenis
5191e3aa971Skettenis buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
5201e3aa971Skettenis
5211e3aa971Skettenis switch(cmd) {
5221e3aa971Skettenis case HVIOCREAD:
5231e3aa971Skettenis size = hi->hi_len;
5241e3aa971Skettenis offset = 0;
5251e3aa971Skettenis while (size > 0) {
5261e3aa971Skettenis pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
5271e3aa971Skettenis nbytes = min(PAGE_SIZE, size);
5281e3aa971Skettenis err = hv_ldc_copy(lc->lc_id, LDC_COPY_IN,
5291e3aa971Skettenis hi->hi_cookie + offset, pa, nbytes, &nbytes);
5301e3aa971Skettenis if (err != H_EOK) {
5311e3aa971Skettenis printf("hv_ldc_copy %d\n", err);
532f8e6c425Stedu free(buf, M_DEVBUF, 0);
5331e3aa971Skettenis device_unref(&sc->sc_dv);
5341e3aa971Skettenis return (EINVAL);
5351e3aa971Skettenis }
5361e3aa971Skettenis err = copyout(buf, (caddr_t)hi->hi_addr + offset, nbytes);
5371e3aa971Skettenis if (err) {
538f8e6c425Stedu free(buf, M_DEVBUF, 0);
5391e3aa971Skettenis device_unref(&sc->sc_dv);
5401e3aa971Skettenis return (err);
5411e3aa971Skettenis }
5421e3aa971Skettenis size -= nbytes;
5431e3aa971Skettenis offset += nbytes;
5441e3aa971Skettenis }
5451e3aa971Skettenis break;
5461e3aa971Skettenis case HVIOCWRITE:
5471e3aa971Skettenis size = hi->hi_len;
5481e3aa971Skettenis offset = 0;
5491e3aa971Skettenis while (size > 0) {
5501e3aa971Skettenis pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
5511e3aa971Skettenis nbytes = min(PAGE_SIZE, size);
5521e3aa971Skettenis err = copyin((caddr_t)hi->hi_addr + offset, buf, nbytes);
5531e3aa971Skettenis if (err) {
554f8e6c425Stedu free(buf, M_DEVBUF, 0);
5551e3aa971Skettenis device_unref(&sc->sc_dv);
5561e3aa971Skettenis return (err);
5571e3aa971Skettenis }
5581e3aa971Skettenis err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT,
5591e3aa971Skettenis hi->hi_cookie + offset, pa, nbytes, &nbytes);
5601e3aa971Skettenis if (err != H_EOK) {
5611e3aa971Skettenis printf("hv_ldc_copy %d\n", err);
562f8e6c425Stedu free(buf, M_DEVBUF, 0);
5631e3aa971Skettenis device_unref(&sc->sc_dv);
5641e3aa971Skettenis return (EINVAL);
5651e3aa971Skettenis }
5661e3aa971Skettenis size -= nbytes;
5671e3aa971Skettenis offset += nbytes;
5681e3aa971Skettenis }
5691e3aa971Skettenis break;
5701e3aa971Skettenis
5711e3aa971Skettenis }
5721e3aa971Skettenis
573f8e6c425Stedu free(buf, M_DEVBUF, 0);
5741e3aa971Skettenis
5751e3aa971Skettenis device_unref(&sc->sc_dv);
5761e3aa971Skettenis return (0);
5771e3aa971Skettenis }
5781e3aa971Skettenis
579b37ca7acSmpi void
filt_vldcprdetach(struct knote * kn)580b37ca7acSmpi filt_vldcprdetach(struct knote *kn)
581b37ca7acSmpi {
582b37ca7acSmpi struct vldcp_softc *sc = (void *)kn->kn_hook;
583b37ca7acSmpi int s;
584b37ca7acSmpi
585b37ca7acSmpi s = spltty();
5869b0cf67bSvisa klist_remove_locked(&sc->sc_rsel.si_note, kn);
587b37ca7acSmpi splx(s);
588b37ca7acSmpi }
589b37ca7acSmpi
590b37ca7acSmpi void
filt_vldcpwdetach(struct knote * kn)591b37ca7acSmpi filt_vldcpwdetach(struct knote *kn)
592b37ca7acSmpi {
593b37ca7acSmpi struct vldcp_softc *sc = (void *)kn->kn_hook;
594b37ca7acSmpi int s;
595b37ca7acSmpi
596b37ca7acSmpi s = spltty();
5979b0cf67bSvisa klist_remove_locked(&sc->sc_wsel.si_note, kn);
598b37ca7acSmpi splx(s);
599b37ca7acSmpi }
600b37ca7acSmpi
601b37ca7acSmpi int
filt_vldcpread(struct knote * kn,long hint)602b37ca7acSmpi filt_vldcpread(struct knote *kn, long hint)
603b37ca7acSmpi {
604b37ca7acSmpi struct vldcp_softc *sc = (void *)kn->kn_hook;
605b37ca7acSmpi struct ldc_conn *lc = &sc->sc_lc;
606b37ca7acSmpi uint64_t head, tail, avail, state;
607b37ca7acSmpi int s, err;
608b37ca7acSmpi
609b37ca7acSmpi s = spltty();
610b37ca7acSmpi err = hv_ldc_rx_get_state(lc->lc_id, &head, &tail, &state);
611b37ca7acSmpi if (err == 0 && state == LDC_CHANNEL_UP && head != tail) {
612b37ca7acSmpi avail = (tail - head) / sizeof(struct ldc_pkt) +
613b37ca7acSmpi lc->lc_rxq->lq_nentries;
614b37ca7acSmpi avail %= lc->lc_rxq->lq_nentries;
615b37ca7acSmpi kn->kn_data = avail;
616b37ca7acSmpi } else {
617b37ca7acSmpi cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
618b37ca7acSmpi INTR_ENABLED);
619ed188d6eSvisa kn->kn_data = 0;
620b37ca7acSmpi }
621b37ca7acSmpi splx(s);
622b37ca7acSmpi
623b37ca7acSmpi return (kn->kn_data > 0);
624b37ca7acSmpi }
625b37ca7acSmpi
626b37ca7acSmpi int
filt_vldcwrite(struct knote * kn,long hint)627b37ca7acSmpi filt_vldcwrite(struct knote *kn, long hint)
628b37ca7acSmpi {
629b37ca7acSmpi struct vldcp_softc *sc = (void *)kn->kn_hook;
630b37ca7acSmpi struct ldc_conn *lc = &sc->sc_lc;
631b37ca7acSmpi uint64_t head, tail, avail, state;
632b37ca7acSmpi int s, err;
633b37ca7acSmpi
634b37ca7acSmpi s = spltty();
635b37ca7acSmpi err = hv_ldc_tx_get_state(lc->lc_id, &head, &tail, &state);
636b37ca7acSmpi if (err == 0 && state == LDC_CHANNEL_UP && head != tail) {
637b37ca7acSmpi avail = (head - tail) / sizeof(struct ldc_pkt) +
638b37ca7acSmpi lc->lc_txq->lq_nentries - 1;
639b37ca7acSmpi avail %= lc->lc_txq->lq_nentries;
640b37ca7acSmpi kn->kn_data = avail;
641b37ca7acSmpi } else {
642b37ca7acSmpi cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
643b37ca7acSmpi INTR_ENABLED);
644ed188d6eSvisa kn->kn_data = 0;
645b37ca7acSmpi }
646b37ca7acSmpi splx(s);
647b37ca7acSmpi
648b37ca7acSmpi return (kn->kn_data > 0);
649b37ca7acSmpi }
650b37ca7acSmpi
651b37ca7acSmpi const struct filterops vldcpread_filtops = {
652b37ca7acSmpi .f_flags = FILTEROP_ISFD,
653b37ca7acSmpi .f_attach = NULL,
654b37ca7acSmpi .f_detach = filt_vldcprdetach,
655b37ca7acSmpi .f_event = filt_vldcpread,
656b37ca7acSmpi };
657b37ca7acSmpi
658b37ca7acSmpi const struct filterops vldcpwrite_filtops = {
659b37ca7acSmpi .f_flags = FILTEROP_ISFD,
660b37ca7acSmpi .f_attach = NULL,
661b37ca7acSmpi .f_detach = filt_vldcpwdetach,
662b37ca7acSmpi .f_event = filt_vldcwrite,
663b37ca7acSmpi };
664b37ca7acSmpi
665b37ca7acSmpi int
vldcpkqfilter(dev_t dev,struct knote * kn)666b37ca7acSmpi vldcpkqfilter(dev_t dev, struct knote *kn)
667b37ca7acSmpi {
668b37ca7acSmpi struct vldcp_softc *sc;
669b37ca7acSmpi struct klist *klist;
670b37ca7acSmpi int s;
671b37ca7acSmpi
672b37ca7acSmpi sc = vldcp_lookup(dev);
673b37ca7acSmpi if (sc == NULL)
674b37ca7acSmpi return (ENXIO);
675b37ca7acSmpi
676b37ca7acSmpi switch (kn->kn_filter) {
677b37ca7acSmpi case EVFILT_READ:
678b37ca7acSmpi klist = &sc->sc_rsel.si_note;
679b37ca7acSmpi kn->kn_fop = &vldcpread_filtops;
680b37ca7acSmpi break;
681b37ca7acSmpi case EVFILT_WRITE:
682b37ca7acSmpi klist = &sc->sc_wsel.si_note;
683b37ca7acSmpi kn->kn_fop = &vldcpwrite_filtops;
684b37ca7acSmpi break;
685b37ca7acSmpi
686b37ca7acSmpi default:
6878b51d7d5Svisa device_unref(&sc->sc_dv);
688b37ca7acSmpi return (EINVAL);
689b37ca7acSmpi }
690b37ca7acSmpi
691b37ca7acSmpi kn->kn_hook = sc;
692b37ca7acSmpi
693b37ca7acSmpi s = spltty();
6949b0cf67bSvisa klist_insert_locked(klist, kn);
695b37ca7acSmpi splx(s);
696b37ca7acSmpi
6978b51d7d5Svisa device_unref(&sc->sc_dv);
698b37ca7acSmpi return (0);
699b37ca7acSmpi }
700