xref: /openbsd-src/sys/arch/sparc64/dev/ldc.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: ldc.c,v 1.9 2012/10/21 18:56:00 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2009 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/malloc.h>
20 #include <sys/systm.h>
21 
22 #include <machine/bus.h>
23 #include <machine/hypervisor.h>
24 
25 #include <sparc64/dev/ldcvar.h>
26 
27 #ifdef LDC_DEBUG
28 #define DPRINTF(x)	printf x
29 #else
30 #define DPRINTF(x)
31 #endif
32 
33 void	ldc_rx_ctrl_vers(struct ldc_conn *, struct ldc_pkt *);
34 void	ldc_rx_ctrl_rtr(struct ldc_conn *, struct ldc_pkt *);
35 void	ldc_rx_ctrl_rts(struct ldc_conn *, struct ldc_pkt *);
36 void	ldc_rx_ctrl_rdx(struct ldc_conn *, struct ldc_pkt *);
37 
38 void	ldc_send_ack(struct ldc_conn *);
39 void	ldc_send_rtr(struct ldc_conn *);
40 void	ldc_send_rts(struct ldc_conn *);
41 void	ldc_send_rdx(struct ldc_conn *);
42 
43 void
44 ldc_rx_ctrl(struct ldc_conn *lc, struct ldc_pkt *lp)
45 {
46 	switch (lp->ctrl) {
47 	case LDC_VERS:
48 		ldc_rx_ctrl_vers(lc, lp);
49 		break;
50 
51 	case LDC_RTS:
52 		ldc_rx_ctrl_rts(lc, lp);
53 		break;
54 
55 	case LDC_RTR:
56 		ldc_rx_ctrl_rtr(lc, lp);
57 		break;
58 
59 	case LDC_RDX:
60 		ldc_rx_ctrl_rdx(lc, lp);
61 		break;
62 
63 	default:
64 		DPRINTF(("CTRL/0x%02x/0x%02x\n", lp->stype, lp->ctrl));
65 		ldc_reset(lc);
66 		break;
67 	}
68 }
69 
70 void
71 ldc_rx_ctrl_vers(struct ldc_conn *lc, struct ldc_pkt *lp)
72 {
73 	switch (lp->stype) {
74 	case LDC_INFO:
75 		DPRINTF(("CTRL/INFO/VERS\n"));
76 		if (lp->major == LDC_VERSION_MAJOR &&
77 		    lp->minor == LDC_VERSION_MINOR)
78 			ldc_send_ack(lc);
79 		else
80 			/* XXX do nothing for now. */
81 			;
82 		break;
83 
84 	case LDC_ACK:
85 		if (lc->lc_state != LDC_SND_VERS) {
86 			DPRINTF(("Spurious CTRL/ACK/VERS: state %d\n",
87 			    lc->lc_state));
88 			ldc_reset(lc);
89 			return;
90 		}
91 		DPRINTF(("CTRL/ACK/VERS\n"));
92 		ldc_send_rts(lc);
93 		break;
94 
95 	case LDC_NACK:
96 		DPRINTF(("CTRL/NACK/VERS\n"));
97 		ldc_reset(lc);
98 		break;
99 
100 	default:
101 		DPRINTF(("CTRL/0x%02x/VERS\n", lp->stype));
102 		ldc_reset(lc);
103 		break;
104 	}
105 }
106 
107 void
108 ldc_rx_ctrl_rts(struct ldc_conn *lc, struct ldc_pkt *lp)
109 {
110 	switch (lp->stype) {
111 	case LDC_INFO:
112 		if (lc->lc_state != LDC_RCV_VERS) {
113 			DPRINTF(("Spurious CTRL/INFO/RTS: state %d\n",
114 			    lc->lc_state));
115 			ldc_reset(lc);
116 			return;
117 		}
118 		DPRINTF(("CTRL/INFO/RTS\n"));
119 		ldc_send_rtr(lc);
120 		break;
121 
122 	case LDC_ACK:
123 		DPRINTF(("CTRL/ACK/RTS\n"));
124 		ldc_reset(lc);
125 		break;
126 
127 	case LDC_NACK:
128 		DPRINTF(("CTRL/NACK/RTS\n"));
129 		ldc_reset(lc);
130 		break;
131 
132 	default:
133 		DPRINTF(("CTRL/0x%02x/RTS\n", lp->stype));
134 		ldc_reset(lc);
135 		break;
136 	}
137 }
138 
139 void
140 ldc_rx_ctrl_rtr(struct ldc_conn *lc, struct ldc_pkt *lp)
141 {
142 	switch (lp->stype) {
143 	case LDC_INFO:
144 		if (lc->lc_state != LDC_SND_RTS) {
145 			DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n",
146 			    lc->lc_state));
147 			ldc_reset(lc);
148 			return;
149 		}
150 		DPRINTF(("CTRL/INFO/RTR\n"));
151 		ldc_send_rdx(lc);
152 		lc->lc_start(lc);
153 		break;
154 
155 	case LDC_ACK:
156 		DPRINTF(("CTRL/ACK/RTR\n"));
157 		ldc_reset(lc);
158 		break;
159 
160 	case LDC_NACK:
161 		DPRINTF(("CTRL/NACK/RTR\n"));
162 		ldc_reset(lc);
163 		break;
164 
165 	default:
166 		DPRINTF(("CTRL/0x%02x/RTR\n", lp->stype));
167 		ldc_reset(lc);
168 		break;
169 	}
170 }
171 
172 void
173 ldc_rx_ctrl_rdx(struct ldc_conn *lc, struct ldc_pkt *lp)
174 {
175 	switch (lp->stype) {
176 	case LDC_INFO:
177 		if (lc->lc_state != LDC_SND_RTR) {
178 			DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n",
179 			    lc->lc_state));
180 			ldc_reset(lc);
181 			return;
182 		}
183 		DPRINTF(("CTRL/INFO/RDX\n"));
184 		lc->lc_start(lc);
185 		break;
186 
187 	case LDC_ACK:
188 		DPRINTF(("CTRL/ACK/RDX\n"));
189 		ldc_reset(lc);
190 		break;
191 
192 	case LDC_NACK:
193 		DPRINTF(("CTRL/NACK/RDX\n"));
194 		ldc_reset(lc);
195 		break;
196 
197 	default:
198 		DPRINTF(("CTRL/0x%02x/RDX\n", lp->stype));
199 		ldc_reset(lc);
200 		break;
201 	}
202 }
203 
204 void
205 ldc_rx_data(struct ldc_conn *lc, struct ldc_pkt *lp)
206 {
207 	size_t len;
208 
209 	if (lp->stype != LDC_INFO) {
210 		DPRINTF(("DATA/0x%02x\n", lp->stype));
211 		ldc_reset(lc);
212 		return;
213 	}
214 
215 	if (lc->lc_state != LDC_SND_RTR &&
216 	    lc->lc_state != LDC_SND_RDX) {
217 		DPRINTF(("Spurious DATA/INFO: state %d\n", lc->lc_state));
218 		ldc_reset(lc);
219 		return;
220 	}
221 
222 	if (lp->env & LDC_FRAG_START) {
223 		lc->lc_len = (lp->env & LDC_LEN_MASK) + 8;
224 		KASSERT(lc->lc_len <= sizeof(lc->lc_msg));
225 		memcpy((uint8_t *)lc->lc_msg, lp, lc->lc_len);
226 	} else {
227 		len = (lp->env & LDC_LEN_MASK);
228 		if (lc->lc_len + len > sizeof(lc->lc_msg)) {
229 			DPRINTF(("Buffer overrun\n"));
230 			ldc_reset(lc);
231 			return;
232 		}
233 		memcpy(((uint8_t *)lc->lc_msg) + lc->lc_len, &lp->major, len);
234 		lc->lc_len += len;
235 	}
236 
237 	if (lp->env & LDC_FRAG_STOP)
238 		lc->lc_rx_data(lc, (struct ldc_pkt *)lc->lc_msg);
239 }
240 
241 void
242 ldc_send_vers(struct ldc_conn *lc)
243 {
244 	struct ldc_pkt *lp;
245 	uint64_t tx_head, tx_tail, tx_state;
246 	int err;
247 
248 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
249 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
250 		return;
251 
252 	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
253 	bzero(lp, sizeof(struct ldc_pkt));
254 	lp->type = LDC_CTRL;
255 	lp->stype = LDC_INFO;
256 	lp->ctrl = LDC_VERS;
257 	lp->major = 1;
258 	lp->minor = 0;
259 
260 	tx_tail += sizeof(*lp);
261 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
262 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
263 	if (err != H_EOK) {
264 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
265 		return;
266 	}
267 
268 	lc->lc_state = LDC_SND_VERS;
269 }
270 
271 void
272 ldc_send_ack(struct ldc_conn *lc)
273 {
274 	struct ldc_pkt *lp;
275 	uint64_t tx_head, tx_tail, tx_state;
276 	int err;
277 
278 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
279 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
280 		return;
281 
282 	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
283 	bzero(lp, sizeof(struct ldc_pkt));
284 	lp->type = LDC_CTRL;
285 	lp->stype = LDC_ACK;
286 	lp->ctrl = LDC_VERS;
287 	lp->major = 1;
288 	lp->minor = 0;
289 
290 	tx_tail += sizeof(*lp);
291 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
292 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
293 	if (err != H_EOK) {
294 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
295 		return;
296 	}
297 
298 	lc->lc_state = LDC_RCV_VERS;
299 }
300 
301 void
302 ldc_send_rts(struct ldc_conn *lc)
303 {
304 	struct ldc_pkt *lp;
305 	uint64_t tx_head, tx_tail, tx_state;
306 	int err;
307 
308 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
309 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
310 		return;
311 
312 	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
313 	bzero(lp, sizeof(struct ldc_pkt));
314 	lp->type = LDC_CTRL;
315 	lp->stype = LDC_INFO;
316 	lp->ctrl = LDC_RTS;
317 	lp->env = LDC_MODE_UNRELIABLE;
318 	lp->seqid = lc->lc_tx_seqid++;
319 
320 	tx_tail += sizeof(*lp);
321 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
322 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
323 	if (err != H_EOK) {
324 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
325 		return;
326 	}
327 
328 	lc->lc_state = LDC_SND_RTS;
329 }
330 
331 void
332 ldc_send_rtr(struct ldc_conn *lc)
333 {
334 	struct ldc_pkt *lp;
335 	uint64_t tx_head, tx_tail, tx_state;
336 	int err;
337 
338 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
339 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
340 		return;
341 
342 	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
343 	bzero(lp, sizeof(struct ldc_pkt));
344 	lp->type = LDC_CTRL;
345 	lp->stype = LDC_INFO;
346 	lp->ctrl = LDC_RTR;
347 	lp->env = LDC_MODE_UNRELIABLE;
348 	lp->seqid = lc->lc_tx_seqid++;
349 
350 	tx_tail += sizeof(*lp);
351 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
352 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
353 	if (err != H_EOK)
354 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
355 
356 	lc->lc_state = LDC_SND_RTR;
357 }
358 
359 void
360 ldc_send_rdx(struct ldc_conn *lc)
361 {
362 	struct ldc_pkt *lp;
363 	uint64_t tx_head, tx_tail, tx_state;
364 	int err;
365 
366 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
367 	if (err != H_EOK || tx_state != LDC_CHANNEL_UP)
368 		return;
369 
370 	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
371 	bzero(lp, sizeof(struct ldc_pkt));
372 	lp->type = LDC_CTRL;
373 	lp->stype = LDC_INFO;
374 	lp->ctrl = LDC_RDX;
375 	lp->env = LDC_MODE_UNRELIABLE;
376 	lp->seqid = lc->lc_tx_seqid++;
377 
378 	tx_tail += sizeof(*lp);
379 	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
380 	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
381 	if (err != H_EOK)
382 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
383 
384 	lc->lc_state = LDC_SND_RDX;
385 }
386 
387 int
388 ldc_send_unreliable(struct ldc_conn *lc, void *msg, size_t len)
389 {
390 	struct ldc_pkt *lp;
391 	uint64_t tx_head, tx_tail, tx_state;
392 	uint64_t tx_avail;
393 	uint8_t *p = msg;
394 	int err;
395 
396 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
397 	if (err != H_EOK)
398 		return (EIO);
399 
400 	tx_avail = (tx_head - tx_tail) / sizeof(*lp) +
401 	    lc->lc_txq->lq_nentries - 1;
402 	tx_avail %= lc->lc_txq->lq_nentries;
403 	if (len > tx_avail * LDC_PKT_PAYLOAD)
404 		return (EWOULDBLOCK);
405 
406 	while (len > 0) {
407 		lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
408 		bzero(lp, sizeof(struct ldc_pkt));
409 		lp->type = LDC_DATA;
410 		lp->stype = LDC_INFO;
411 		lp->env = min(len, LDC_PKT_PAYLOAD);
412 		if (p == msg)
413 			lp->env |= LDC_FRAG_START;
414 		if (len <= LDC_PKT_PAYLOAD)
415 			lp->env |= LDC_FRAG_STOP;
416 		lp->seqid = lc->lc_tx_seqid++;
417 		bcopy(p, &lp->major, min(len, LDC_PKT_PAYLOAD));
418 
419 		tx_tail += sizeof(*lp);
420 		tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
421 		err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
422 		if (err != H_EOK)
423 			printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
424 		p += min(len, LDC_PKT_PAYLOAD);
425 		len -= min(len, LDC_PKT_PAYLOAD);
426 	}
427 
428 	return (0);
429 }
430 
431 void
432 ldc_reset(struct ldc_conn *lc)
433 {
434 	int err;
435 
436 	DPRINTF(("Resetting connection\n"));
437 	hv_ldc_tx_qconf(lc->lc_id, 0, 0);
438 	hv_ldc_rx_qconf(lc->lc_id, 0, 0);
439 	lc->lc_tx_state = lc->lc_rx_state = LDC_CHANNEL_DOWN;
440 
441 	err = hv_ldc_tx_qconf(lc->lc_id,
442 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
443 	if (err != H_EOK)
444 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
445 
446 	err = hv_ldc_rx_qconf(lc->lc_id,
447 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
448 	if (err != H_EOK)
449 		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
450 
451 	lc->lc_tx_seqid = 0;
452 	lc->lc_state = 0;
453 	lc->lc_reset(lc);
454 }
455 
456 struct ldc_queue *
457 ldc_queue_alloc(bus_dma_tag_t t, int nentries)
458 {
459 	struct ldc_queue *lq;
460 	bus_size_t size;
461 	caddr_t va;
462 	int nsegs;
463 
464 	lq = malloc(sizeof(struct ldc_queue), M_DEVBUF, M_NOWAIT);
465 	if (lq == NULL)
466 		return NULL;
467 
468 	size = roundup(nentries * sizeof(struct ldc_pkt), PAGE_SIZE);
469 
470 	if (bus_dmamap_create(t, size, 1, size, 0,
471 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lq->lq_map) != 0)
472 		return (NULL);
473 
474 	if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lq->lq_seg, 1,
475 	    &nsegs, BUS_DMA_NOWAIT) != 0)
476 		goto destroy;
477 
478 	if (bus_dmamem_map(t, &lq->lq_seg, 1, size, &va,
479 	    BUS_DMA_NOWAIT) != 0)
480 		goto free;
481 
482 	if (bus_dmamap_load(t, lq->lq_map, va, size, NULL,
483 	    BUS_DMA_NOWAIT) != 0)
484 		goto unmap;
485 
486 	lq->lq_va = va;
487 	lq->lq_nentries = nentries;
488 	return (lq);
489 
490 unmap:
491 	bus_dmamem_unmap(t, va, size);
492 free:
493 	bus_dmamem_free(t, &lq->lq_seg, 1);
494 destroy:
495 	bus_dmamap_destroy(t, lq->lq_map);
496 
497 	return (NULL);
498 }
499 
500 void
501 ldc_queue_free(bus_dma_tag_t t, struct ldc_queue *lq)
502 {
503 	bus_size_t size;
504 
505 	size = roundup(lq->lq_nentries * sizeof(struct ldc_pkt), PAGE_SIZE);
506 
507 	bus_dmamap_unload(t, lq->lq_map);
508 	bus_dmamem_unmap(t, lq->lq_va, size);
509 	bus_dmamem_free(t, &lq->lq_seg, 1);
510 	bus_dmamap_destroy(t, lq->lq_map);
511 	free(lq, M_DEVBUF);
512 }
513 
514 struct ldc_map *
515 ldc_map_alloc(bus_dma_tag_t t, int nentries)
516 {
517 	struct ldc_map *lm;
518 	bus_size_t size;
519 	caddr_t va;
520 	int nsegs;
521 
522 	lm = malloc(sizeof(struct ldc_map), M_DEVBUF, M_NOWAIT);
523 	if (lm == NULL)
524 		return NULL;
525 
526 	size = roundup(nentries * sizeof(struct ldc_map_slot), PAGE_SIZE);
527 
528 	if (bus_dmamap_create(t, size, 1, size, 0,
529 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lm->lm_map) != 0)
530 		return (NULL);
531 
532 	if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lm->lm_seg, 1,
533 	    &nsegs, BUS_DMA_NOWAIT) != 0)
534 		goto destroy;
535 
536 	if (bus_dmamem_map(t, &lm->lm_seg, 1, size, &va,
537 	    BUS_DMA_NOWAIT) != 0)
538 		goto free;
539 
540 	if (bus_dmamap_load(t, lm->lm_map, va, size, NULL,
541 	    BUS_DMA_NOWAIT) != 0)
542 		goto unmap;
543 
544 	lm->lm_slot = (struct ldc_map_slot *)va;
545 	lm->lm_nentries = nentries;
546 	bzero(lm->lm_slot, nentries * sizeof(struct ldc_map_slot));
547 	return (lm);
548 
549 unmap:
550 	bus_dmamem_unmap(t, va, size);
551 free:
552 	bus_dmamem_free(t, &lm->lm_seg, 1);
553 destroy:
554 	bus_dmamap_destroy(t, lm->lm_map);
555 
556 	return (NULL);
557 }
558 
559 void
560 ldc_map_free(bus_dma_tag_t t, struct ldc_map *lm)
561 {
562 	bus_size_t size;
563 
564 	size = lm->lm_nentries * sizeof(struct ldc_map_slot);
565 	size = roundup(size, PAGE_SIZE);
566 
567 	bus_dmamap_unload(t, lm->lm_map);
568 	bus_dmamem_unmap(t, (caddr_t)lm->lm_slot, size);
569 	bus_dmamem_free(t, &lm->lm_seg, 1);
570 	bus_dmamap_destroy(t, lm->lm_map);
571 	free(lm, M_DEVBUF);
572 }
573