xref: /openbsd-src/sys/arch/sparc64/dev/vldcp.c (revision 3b372c3481e8650fbe12d5cb2d27ca2fb82bc7d2)
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