xref: /openbsd/sys/arch/sparc64/dev/vldcp.c (revision 264ca280)
1 /*	$OpenBSD: vldcp.c,v 1.12 2015/02/10 22:04:00 miod 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 	uint64_t	sc_tx_ino;
61 	uint64_t	sc_rx_ino;
62 	void		*sc_tx_ih;
63 	void		*sc_rx_ih;
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 	sc->sc_tx_ino = ca->ca_tx_ino;
137 	sc->sc_rx_ino = ca->ca_rx_ino;
138 
139 	printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_ino, sc->sc_rx_ino);
140 
141 	/*
142 	 * Un-configure queues before registering interrupt handlers,
143 	 * such that we dont get any stale LDC packets or events.
144 	 */
145 	hv_ldc_tx_qconf(ca->ca_id, 0, 0);
146 	hv_ldc_rx_qconf(ca->ca_id, 0, 0);
147 
148 	sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_ino,
149 	    IPL_TTY, 0, vldcp_tx_intr, sc, sc->sc_dv.dv_xname);
150 	sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_ino,
151 	    IPL_TTY, 0, vldcp_rx_intr, sc, sc->sc_dv.dv_xname);
152 	if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
153 		printf(", can't establish interrupt\n");
154 		return;
155 	}
156 
157 	lc = &sc->sc_lc;
158 	lc->lc_id = ca->ca_id;
159 	lc->lc_sc = sc;
160 
161 	lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_TX_ENTRIES);
162 	if (lc->lc_txq == NULL) {
163 		printf(", can't allocate tx queue\n");
164 		return;
165 	}
166 
167 	lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_RX_ENTRIES);
168 	if (lc->lc_rxq == NULL) {
169 		printf(", can't allocate rx queue\n");
170 		goto free_txqueue;
171 	}
172 
173 	for (svc = vldc_svc; svc->vs_name != NULL; svc++) {
174 		if (strcmp(ca->ca_name, svc->vs_name) == 0) {
175 			svc->vs_sc = sc;
176 			break;
177 		}
178 	}
179 
180 	if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
181 	    strcmp(ca->ca_name, "ldom-primary") != 0) {
182 		int minor = VLDC_LDOM_OFFSET + vldc_num_ldoms++;
183 		if (minor < nitems(vldc_svc))
184 			vldc_svc[minor].vs_sc = sc;
185 	}
186 
187 	printf(" channel \"%s\"\n", ca->ca_name);
188 	return;
189 
190 #if 0
191 free_rxqueue:
192 	ldc_queue_free(sc->sc_dmatag, lc->lc_rxq);
193 #endif
194 free_txqueue:
195 	ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
196 }
197 
198 int
199 vldcp_tx_intr(void *arg)
200 {
201 	struct vldcp_softc *sc = arg;
202 	struct ldc_conn *lc = &sc->sc_lc;
203 	uint64_t tx_head, tx_tail, tx_state;
204 	int err;
205 
206 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
207 	if (err != H_EOK) {
208 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
209 		return (0);
210 	}
211 
212 	if (tx_state != lc->lc_tx_state) {
213 		switch (tx_state) {
214 		case LDC_CHANNEL_DOWN:
215 			DPRINTF(("Tx link down\n"));
216 			break;
217 		case LDC_CHANNEL_UP:
218 			DPRINTF(("Tx link up\n"));
219 			break;
220 		case LDC_CHANNEL_RESET:
221 			DPRINTF(("Tx link reset\n"));
222 			break;
223 		}
224 		lc->lc_tx_state = tx_state;
225 	}
226 
227 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
228 	selwakeup(&sc->sc_wsel);
229 	wakeup(lc->lc_txq);
230 	return (1);
231 }
232 
233 int
234 vldcp_rx_intr(void *arg)
235 {
236 	struct vldcp_softc *sc = arg;
237 	struct ldc_conn *lc = &sc->sc_lc;
238 	uint64_t rx_head, rx_tail, rx_state;
239 	int err;
240 
241 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
242 	if (err != H_EOK) {
243 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
244 		return (0);
245 	}
246 
247 	if (rx_state != lc->lc_rx_state) {
248 		switch (rx_state) {
249 		case LDC_CHANNEL_DOWN:
250 			DPRINTF(("Rx link down\n"));
251 			break;
252 		case LDC_CHANNEL_UP:
253 			DPRINTF(("Rx link up\n"));
254 			break;
255 		case LDC_CHANNEL_RESET:
256 			DPRINTF(("Rx link reset\n"));
257 			break;
258 		}
259 		lc->lc_rx_state = rx_state;
260 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
261 		    INTR_DISABLED);
262 		selwakeup(&sc->sc_rsel);
263 		wakeup(lc->lc_rxq);
264 		return (1);
265 	}
266 
267 	if (rx_head == rx_tail)
268 		return (0);
269 
270 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
271 	selwakeup(&sc->sc_rsel);
272 	wakeup(lc->lc_rxq);
273 	return (1);
274 }
275 
276 cdev_decl(vldcp);
277 struct vldcp_softc *vldcp_lookup(dev_t);
278 
279 struct vldcp_softc *
280 vldcp_lookup(dev_t dev)
281 {
282 	struct vldcp_softc *sc = NULL;
283 
284 	if (minor(dev) < nitems(vldc_svc))
285 		sc = vldc_svc[minor(dev)].vs_sc;
286 
287 	if (sc)
288 		device_ref(&sc->sc_dv);
289 
290 	return (sc);
291 }
292 
293 int
294 vldcpopen(dev_t dev, int flag, int mode, struct proc *p)
295 {
296 	struct vldcp_softc *sc;
297 	struct ldc_conn *lc;
298 	uint64_t rx_head, rx_tail, rx_state;
299 	int err;
300 
301 	sc = vldcp_lookup(dev);
302 	if (sc == NULL)
303 		return (ENXIO);
304 	lc = &sc->sc_lc;
305 
306 	err = hv_ldc_tx_qconf(lc->lc_id,
307 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
308 	if (err != H_EOK)
309 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
310 
311 	err = hv_ldc_rx_qconf(lc->lc_id,
312 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
313 	if (err != H_EOK)
314 		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
315 
316 	/* Clear a pending channel reset.  */
317 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
318 	if (err != H_EOK)
319 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
320 
321 	device_unref(&sc->sc_dv);
322 	return (0);
323 }
324 
325 int
326 vldcpclose(dev_t dev, int flag, int mode, struct proc *p)
327 {
328 	struct vldcp_softc *sc;
329 
330 	sc = vldcp_lookup(dev);
331 	if (sc == NULL)
332 		return (ENXIO);
333 
334 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
335 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
336 
337 	hv_ldc_tx_qconf(sc->sc_lc.lc_id, 0, 0);
338 	hv_ldc_rx_qconf(sc->sc_lc.lc_id, 0, 0);
339 
340 	device_unref(&sc->sc_dv);
341 	return (0);
342 }
343 
344 int
345 vldcpread(dev_t dev, struct uio *uio, int ioflag)
346 {
347 	struct vldcp_softc *sc;
348 	struct ldc_conn *lc;
349 	uint64_t rx_head, rx_tail, rx_state;
350 	int err, ret;
351 	int s;
352 
353 	sc = vldcp_lookup(dev);
354 	if (sc == NULL)
355 		return (ENXIO);
356 	lc = &sc->sc_lc;
357 
358 	if (uio->uio_resid != 64) {
359 		device_unref(&sc->sc_dv);
360 		return (EINVAL);
361 	}
362 
363 	s = spltty();
364 retry:
365 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
366 	if (err != H_EOK) {
367 		splx(s);
368 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
369 		device_unref(&sc->sc_dv);
370 		return (EIO);
371 	}
372 
373 	if (rx_state != LDC_CHANNEL_UP) {
374 		splx(s);
375 		device_unref(&sc->sc_dv);
376 		return (EIO);
377 	}
378 
379 	DPRINTF(("rx head %llx, rx tail %llx\n", rx_head, rx_tail));
380 
381 	if (rx_head == rx_tail) {
382 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
383 		    INTR_ENABLED);
384 		ret = tsleep(lc->lc_rxq, PWAIT | PCATCH, "hvrd", 0);
385 		if (ret) {
386 			splx(s);
387 			device_unref(&sc->sc_dv);
388 			return (ret);
389 		}
390 		goto retry;
391 	}
392 	splx(s);
393 
394 	ret = uiomove(lc->lc_rxq->lq_va + rx_head, 64, uio);
395 
396 	rx_head += 64;
397 	rx_head &= ((lc->lc_rxq->lq_nentries * 64) - 1);
398 	err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
399 	if (err != H_EOK)
400 		printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
401 
402 	device_unref(&sc->sc_dv);
403 	return (ret);
404 }
405 
406 int
407 vldcpwrite(dev_t dev, struct uio *uio, int ioflag)
408 {
409 	struct vldcp_softc *sc;
410 	struct ldc_conn *lc;
411 	uint64_t tx_head, tx_tail, tx_state;
412 	uint64_t next_tx_tail;
413 	int err, ret;
414 	int s;
415 
416 	sc = vldcp_lookup(dev);
417 	if (sc == NULL)
418 		return (ENXIO);
419 	lc = &sc->sc_lc;
420 
421 	if (uio->uio_resid != 64) {
422 		device_unref(&sc->sc_dv);
423 		return (EINVAL);
424 	}
425 
426 	s = spltty();
427 retry:
428 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
429 	if (err != H_EOK) {
430 		splx(s);
431 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
432 		device_unref(&sc->sc_dv);
433 		return (EIO);
434 	}
435 
436 	if (tx_state != LDC_CHANNEL_UP) {
437 		splx(s);
438 		device_unref(&sc->sc_dv);
439 		return (EIO);
440 	}
441 
442 	DPRINTF(("tx head %llx, tx tail %llx\n", tx_head, tx_tail));
443 
444 	next_tx_tail = tx_tail + 64;
445 	next_tx_tail &= ((lc->lc_txq->lq_nentries * 64) - 1);
446 
447 	if (tx_head == next_tx_tail) {
448 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
449 		    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_bustag, sc->sc_rx_ino,
589 			    INTR_ENABLED);
590 			selrecord(p, &sc->sc_rsel);
591 		}
592 		if (events & (POLLOUT | POLLWRNORM)) {
593 			cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
594 			    INTR_ENABLED);
595 			selrecord(p, &sc->sc_wsel);
596 		}
597 	}
598 	splx(s);
599 	return revents;
600 }
601