xref: /openbsd-src/sys/arch/sparc64/dev/vldcp.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: vldcp.c,v 1.9 2014/07/12 18:44:43 tedu Exp $	*/
2 /*
3  * Copyright (c) 2009, 2012 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/poll.h>
23 #include <sys/proc.h>
24 #include <sys/systm.h>
25 
26 #include <machine/autoconf.h>
27 #include <machine/hypervisor.h>
28 #include <machine/mdesc.h>
29 
30 #include <uvm/uvm_extern.h>
31 
32 #include <sparc64/dev/cbusvar.h>
33 #include <sparc64/dev/ldcvar.h>
34 
35 #ifdef VLDCP_DEBUG
36 #define DPRINTF(x)	printf x
37 #else
38 #define DPRINTF(x)
39 #endif
40 
41 #include <sys/ioccom.h>
42 
43 struct hv_io {
44 	uint64_t	hi_cookie;
45 	void		*hi_addr;
46 	size_t		hi_len;
47 };
48 
49 #define HVIOCREAD	_IOW('h', 0, struct hv_io)
50 #define HVIOCWRITE	_IOW('h', 1, struct hv_io)
51 
52 #define VLDCP_TX_ENTRIES	128
53 #define VLDCP_RX_ENTRIES	128
54 
55 struct vldcp_softc {
56 	struct device	sc_dv;
57 	bus_space_tag_t	sc_bustag;
58 	bus_dma_tag_t	sc_dmatag;
59 
60 	void		*sc_tx_ih;
61 	void		*sc_rx_ih;
62 	uint64_t	sc_tx_sysino;
63 	uint64_t	sc_rx_sysino;
64 
65 	struct ldc_conn	sc_lc;
66 
67 	struct selinfo	sc_rsel;
68 	struct selinfo	sc_wsel;
69 };
70 
71 int	vldcp_match(struct device *, void *, void *);
72 void	vldcp_attach(struct device *, struct device *, void *);
73 
74 struct cfattach vldcp_ca = {
75 	sizeof(struct vldcp_softc), vldcp_match, vldcp_attach
76 };
77 
78 struct cfdriver vldcp_cd = {
79 	NULL, "vldcp", DV_DULL
80 };
81 
82 int	vldcp_tx_intr(void *);
83 int	vldcp_rx_intr(void *);
84 
85 /*
86  * We attach to certain well-known channels.  These are assigned fixed
87  * device minor device numbers through their index in the table below.
88  * So "hvctl" gets minor 0, "spds" gets minor 1, etc. etc.
89  *
90  * We also attach to the domain services channels.  These are named
91  * "ldom-<guestname>" and get assigned a device minor starting at
92  * VLDC_LDOM_OFFSET.
93  */
94 #define VLDC_NUM_SERVICES	64
95 #define VLDC_LDOM_OFFSET	32
96 int vldc_num_ldoms;
97 
98 struct vldc_svc {
99 	const char *vs_name;
100 	struct vldcp_softc *vs_sc;
101 };
102 
103 struct vldc_svc vldc_svc[VLDC_NUM_SERVICES] = {
104 	{ "hvctl" },
105 	{ "spds" },
106 	{ NULL }
107 };
108 
109 int
110 vldcp_match(struct device *parent, void *match, void *aux)
111 {
112 	struct cbus_attach_args *ca = aux;
113 	struct vldc_svc *svc;
114 
115 	for (svc = vldc_svc; svc->vs_name != NULL; svc++)
116 		if (strcmp(ca->ca_name, svc->vs_name) == 0)
117 			return (1);
118 
119 	if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
120 	    strcmp(ca->ca_name, "ldom-primary") != 0)
121 		return (1);
122 
123 	return (0);
124 }
125 
126 void
127 vldcp_attach(struct device *parent, struct device *self, void *aux)
128 {
129 	struct vldcp_softc *sc = (struct vldcp_softc *)self;
130 	struct cbus_attach_args *ca = aux;
131 	struct vldc_svc *svc;
132 	struct ldc_conn *lc;
133 
134 	sc->sc_bustag = ca->ca_bustag;
135 	sc->sc_dmatag = ca->ca_dmatag;
136 
137 	if (cbus_intr_map(ca->ca_node, ca->ca_tx_ino, &sc->sc_tx_sysino) ||
138 	    cbus_intr_map(ca->ca_node, ca->ca_rx_ino, &sc->sc_rx_sysino)) {
139 		printf(": can't map interrupt\n");
140 		return;
141 	}
142 	printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_sysino, sc->sc_rx_sysino);
143 
144 	/*
145 	 * Un-configure queues before registering interrupt handlers,
146 	 * such that we dont get any stale LDC packets or events.
147 	 */
148 	hv_ldc_tx_qconf(ca->ca_id, 0, 0);
149 	hv_ldc_rx_qconf(ca->ca_id, 0, 0);
150 
151 	sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_sysino,
152 	    IPL_TTY, 0, vldcp_tx_intr, sc, sc->sc_dv.dv_xname);
153 	sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_sysino,
154 	    IPL_TTY, 0, vldcp_rx_intr, sc, sc->sc_dv.dv_xname);
155 	if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
156 		printf(", can't establish interrupt\n");
157 		return;
158 	}
159 
160 	lc = &sc->sc_lc;
161 	lc->lc_id = ca->ca_id;
162 	lc->lc_sc = sc;
163 
164 	lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_TX_ENTRIES);
165 	if (lc->lc_txq == NULL) {
166 		printf(", can't allocate tx queue\n");
167 		return;
168 	}
169 
170 	lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_RX_ENTRIES);
171 	if (lc->lc_rxq == NULL) {
172 		printf(", can't allocate rx queue\n");
173 		goto free_txqueue;
174 	}
175 
176 	for (svc = vldc_svc; svc->vs_name != NULL; svc++) {
177 		if (strcmp(ca->ca_name, svc->vs_name) == 0) {
178 			svc->vs_sc = sc;
179 			break;
180 		}
181 	}
182 
183 	if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
184 	    strcmp(ca->ca_name, "ldom-primary") != 0) {
185 		int minor = VLDC_LDOM_OFFSET + vldc_num_ldoms++;
186 		if (minor < nitems(vldc_svc))
187 			vldc_svc[minor].vs_sc = sc;
188 	}
189 
190 	printf(" channel \"%s\"\n", ca->ca_name);
191 	return;
192 
193 #if 0
194 free_rxqueue:
195 	ldc_queue_free(sc->sc_dmatag, lc->lc_rxq);
196 #endif
197 free_txqueue:
198 	ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
199 }
200 
201 int
202 vldcp_tx_intr(void *arg)
203 {
204 	struct vldcp_softc *sc = arg;
205 	struct ldc_conn *lc = &sc->sc_lc;
206 	uint64_t tx_head, tx_tail, tx_state;
207 	int err;
208 
209 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
210 	if (err != H_EOK) {
211 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
212 		return (0);
213 	}
214 
215 	if (tx_state != lc->lc_tx_state) {
216 		switch (tx_state) {
217 		case LDC_CHANNEL_DOWN:
218 			DPRINTF(("Tx link down\n"));
219 			break;
220 		case LDC_CHANNEL_UP:
221 			DPRINTF(("Tx link up\n"));
222 			break;
223 		case LDC_CHANNEL_RESET:
224 			DPRINTF(("Tx link reset\n"));
225 			break;
226 		}
227 		lc->lc_tx_state = tx_state;
228 	}
229 
230 	cbus_intr_setenabled(sc->sc_tx_sysino, INTR_DISABLED);
231 	selwakeup(&sc->sc_wsel);
232 	wakeup(lc->lc_txq);
233 	return (1);
234 }
235 
236 int
237 vldcp_rx_intr(void *arg)
238 {
239 	struct vldcp_softc *sc = arg;
240 	struct ldc_conn *lc = &sc->sc_lc;
241 	uint64_t rx_head, rx_tail, rx_state;
242 	int err;
243 
244 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
245 	if (err != H_EOK) {
246 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
247 		return (0);
248 	}
249 
250 	if (rx_state != lc->lc_rx_state) {
251 		switch (rx_state) {
252 		case LDC_CHANNEL_DOWN:
253 			DPRINTF(("Rx link down\n"));
254 			break;
255 		case LDC_CHANNEL_UP:
256 			DPRINTF(("Rx link up\n"));
257 			break;
258 		case LDC_CHANNEL_RESET:
259 			DPRINTF(("Rx link reset\n"));
260 			break;
261 		}
262 		lc->lc_rx_state = rx_state;
263 		cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED);
264 		selwakeup(&sc->sc_rsel);
265 		wakeup(lc->lc_rxq);
266 		return (1);
267 	}
268 
269 	if (rx_head == rx_tail)
270 		return (0);
271 
272 	cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED);
273 	selwakeup(&sc->sc_rsel);
274 	wakeup(lc->lc_rxq);
275 	return (1);
276 }
277 
278 cdev_decl(vldcp);
279 struct vldcp_softc *vldcp_lookup(dev_t);
280 
281 struct vldcp_softc *
282 vldcp_lookup(dev_t dev)
283 {
284 	struct vldcp_softc *sc = NULL;
285 
286 	if (minor(dev) < nitems(vldc_svc))
287 		sc = vldc_svc[minor(dev)].vs_sc;
288 
289 	if (sc)
290 		device_ref(&sc->sc_dv);
291 
292 	return (sc);
293 }
294 
295 int
296 vldcpopen(dev_t dev, int flag, int mode, struct proc *p)
297 {
298 	struct vldcp_softc *sc;
299 	struct ldc_conn *lc;
300 	uint64_t rx_head, rx_tail, rx_state;
301 	int err;
302 
303 	sc = vldcp_lookup(dev);
304 	if (sc == NULL)
305 		return (ENXIO);
306 	lc = &sc->sc_lc;
307 
308 	err = hv_ldc_tx_qconf(lc->lc_id,
309 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
310 	if (err != H_EOK)
311 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
312 
313 	err = hv_ldc_rx_qconf(lc->lc_id,
314 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
315 	if (err != H_EOK)
316 		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
317 
318 	/* Clear a pending channel reset.  */
319 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
320 	if (err != H_EOK)
321 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
322 
323 	device_unref(&sc->sc_dv);
324 	return (0);
325 }
326 
327 int
328 vldcpclose(dev_t dev, int flag, int mode, struct proc *p)
329 {
330 	struct vldcp_softc *sc;
331 
332 	sc = vldcp_lookup(dev);
333 	if (sc == NULL)
334 		return (ENXIO);
335 
336 	cbus_intr_setenabled(sc->sc_tx_sysino, INTR_DISABLED);
337 	cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED);
338 
339 	hv_ldc_tx_qconf(sc->sc_lc.lc_id, 0, 0);
340 	hv_ldc_rx_qconf(sc->sc_lc.lc_id, 0, 0);
341 
342 	device_unref(&sc->sc_dv);
343 	return (0);
344 }
345 
346 int
347 vldcpread(dev_t dev, struct uio *uio, int ioflag)
348 {
349 	struct vldcp_softc *sc;
350 	struct ldc_conn *lc;
351 	uint64_t rx_head, rx_tail, rx_state;
352 	int err, ret;
353 	int s;
354 
355 	sc = vldcp_lookup(dev);
356 	if (sc == NULL)
357 		return (ENXIO);
358 	lc = &sc->sc_lc;
359 
360 	if (uio->uio_resid != 64) {
361 		device_unref(&sc->sc_dv);
362 		return (EINVAL);
363 	}
364 
365 	s = spltty();
366 retry:
367 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
368 	if (err != H_EOK) {
369 		splx(s);
370 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
371 		device_unref(&sc->sc_dv);
372 		return (EIO);
373 	}
374 
375 	if (rx_state != LDC_CHANNEL_UP) {
376 		splx(s);
377 		device_unref(&sc->sc_dv);
378 		return (EIO);
379 	}
380 
381 	DPRINTF(("rx head %llx, rx tail %llx\n", rx_head, rx_tail));
382 
383 	if (rx_head == rx_tail) {
384 		cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED);
385 		ret = tsleep(lc->lc_rxq, PWAIT | PCATCH, "hvrd", 0);
386 		if (ret) {
387 			splx(s);
388 			device_unref(&sc->sc_dv);
389 			return (ret);
390 		}
391 		goto retry;
392 	}
393 	splx(s);
394 
395 	ret = uiomove(lc->lc_rxq->lq_va + rx_head, 64, uio);
396 
397 	rx_head += 64;
398 	rx_head &= ((lc->lc_rxq->lq_nentries * 64) - 1);
399 	err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
400 	if (err != H_EOK)
401 		printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
402 
403 	device_unref(&sc->sc_dv);
404 	return (ret);
405 }
406 
407 int
408 vldcpwrite(dev_t dev, struct uio *uio, int ioflag)
409 {
410 	struct vldcp_softc *sc;
411 	struct ldc_conn *lc;
412 	uint64_t tx_head, tx_tail, tx_state;
413 	uint64_t next_tx_tail;
414 	int err, ret;
415 	int s;
416 
417 	sc = vldcp_lookup(dev);
418 	if (sc == NULL)
419 		return (ENXIO);
420 	lc = &sc->sc_lc;
421 
422 	if (uio->uio_resid != 64) {
423 		device_unref(&sc->sc_dv);
424 		return (EINVAL);
425 	}
426 
427 	s = spltty();
428 retry:
429 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
430 	if (err != H_EOK) {
431 		splx(s);
432 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
433 		device_unref(&sc->sc_dv);
434 		return (EIO);
435 	}
436 
437 	if (tx_state != LDC_CHANNEL_UP) {
438 		splx(s);
439 		device_unref(&sc->sc_dv);
440 		return (EIO);
441 	}
442 
443 	DPRINTF(("tx head %llx, tx tail %llx\n", tx_head, tx_tail));
444 
445 	next_tx_tail = tx_tail + 64;
446 	next_tx_tail &= ((lc->lc_txq->lq_nentries * 64) - 1);
447 
448 	if (tx_head == next_tx_tail) {
449 		cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED);
450 		ret = tsleep(lc->lc_txq, PWAIT | PCATCH, "hvwr", 0);
451 		if (ret) {
452 			splx(s);
453 			device_unref(&sc->sc_dv);
454 			return (ret);
455 		}
456 		goto retry;
457 	}
458 	splx(s);
459 
460 	ret = uiomove(lc->lc_txq->lq_va + tx_tail, 64, uio);
461 
462 	err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail);
463 	if (err != H_EOK) {
464 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
465 		device_unref(&sc->sc_dv);
466 		return (EIO);
467 	}
468 
469 	device_unref(&sc->sc_dv);
470 	return (ret);
471 }
472 
473 int
474 vldcpioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
475 {
476 	struct vldcp_softc *sc;
477 	struct ldc_conn *lc;
478 	struct hv_io *hi = (struct hv_io *)data;
479 	paddr_t pa, offset;
480 	psize_t nbytes;
481 	caddr_t buf;
482 	size_t size;
483 	int err;
484 
485 	sc = vldcp_lookup(dev);
486 	if (sc == NULL)
487 		return (ENXIO);
488 	lc = &sc->sc_lc;
489 
490 	switch (cmd) {
491 	case HVIOCREAD:
492 	case HVIOCWRITE:
493 		break;
494 	default:
495 		device_unref(&sc->sc_dv);
496 		return (ENOTTY);
497 	}
498 
499 	buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
500 
501 	switch(cmd) {
502 	case HVIOCREAD:
503 		size = hi->hi_len;
504 		offset = 0;
505 		while (size > 0) {
506 			pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
507 			nbytes = min(PAGE_SIZE, size);
508 			err = hv_ldc_copy(lc->lc_id, LDC_COPY_IN,
509 			    hi->hi_cookie + offset, pa, nbytes, &nbytes);
510 			if (err != H_EOK) {
511 				printf("hv_ldc_copy %d\n", err);
512 				free(buf, M_DEVBUF, 0);
513 				device_unref(&sc->sc_dv);
514 				return (EINVAL);
515 			}
516 			err = copyout(buf, (caddr_t)hi->hi_addr + offset, nbytes);
517 			if (err) {
518 				free(buf, M_DEVBUF, 0);
519 				device_unref(&sc->sc_dv);
520 				return (err);
521 			}
522 			size -= nbytes;
523 			offset += nbytes;
524 		}
525 		break;
526 	case HVIOCWRITE:
527 		size = hi->hi_len;
528 		offset = 0;
529 		while (size > 0) {
530 			pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
531 			nbytes = min(PAGE_SIZE, size);
532 			err = copyin((caddr_t)hi->hi_addr + offset, buf, nbytes);
533 			if (err) {
534 				free(buf, M_DEVBUF, 0);
535 				device_unref(&sc->sc_dv);
536 				return (err);
537 			}
538 			err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT,
539 			    hi->hi_cookie + offset, pa, nbytes, &nbytes);
540 			if (err != H_EOK) {
541 				printf("hv_ldc_copy %d\n", err);
542 				free(buf, M_DEVBUF, 0);
543 				device_unref(&sc->sc_dv);
544 				return (EINVAL);
545 			}
546 			size -= nbytes;
547 			offset += nbytes;
548 		}
549 		break;
550 
551 	}
552 
553 	free(buf, M_DEVBUF, 0);
554 
555 	device_unref(&sc->sc_dv);
556 	return (0);
557 }
558 
559 int
560 vldcppoll(dev_t dev, int events, struct proc *p)
561 {
562 	struct vldcp_softc *sc;
563 	struct ldc_conn *lc;
564 	uint64_t head, tail, state;
565 	int revents = 0;
566 	int s, err;
567 
568 	sc = vldcp_lookup(dev);
569 	if (sc == NULL)
570 		return (ENXIO);
571 	lc = &sc->sc_lc;
572 
573 	s = spltty();
574 	if (events & (POLLIN | POLLRDNORM)) {
575 		err = hv_ldc_rx_get_state(lc->lc_id, &head, &tail, &state);
576 
577 		if (err == 0 && state == LDC_CHANNEL_UP && head != tail)
578 			revents |= events & (POLLIN | POLLRDNORM);
579 	}
580 	if (events & (POLLOUT | POLLWRNORM)) {
581 		err = hv_ldc_tx_get_state(lc->lc_id, &head, &tail, &state);
582 
583 		if (err == 0 && state == LDC_CHANNEL_UP && head != tail)
584 			revents |= events & (POLLOUT | POLLWRNORM);
585 	}
586 	if (revents == 0) {
587 		if (events & (POLLIN | POLLRDNORM)) {
588 			cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED);
589 			selrecord(p, &sc->sc_rsel);
590 		}
591 		if (events & (POLLOUT | POLLWRNORM)) {
592 			cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED);
593 			selrecord(p, &sc->sc_wsel);
594 		}
595 	}
596 	splx(s);
597 	return revents;
598 }
599