xref: /openbsd-src/sys/arch/sparc64/dev/ldc.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: ldc.c,v 1.7 2009/12/26 21:21:10 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(("Suprious 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 void
388 ldc_reset(struct ldc_conn *lc)
389 {
390 	int err;
391 
392 	DPRINTF(("Resetting connection\n"));
393 	hv_ldc_tx_qconf(lc->lc_id, 0, 0);
394 	hv_ldc_rx_qconf(lc->lc_id, 0, 0);
395 	lc->lc_tx_state = lc->lc_rx_state = LDC_CHANNEL_DOWN;
396 
397 	err = hv_ldc_tx_qconf(lc->lc_id,
398 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
399 	if (err != H_EOK)
400 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
401 
402 	err = hv_ldc_rx_qconf(lc->lc_id,
403 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
404 	if (err != H_EOK)
405 		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
406 
407 	lc->lc_tx_seqid = 0;
408 	lc->lc_state = 0;
409 	lc->lc_reset(lc);
410 }
411 
412 struct ldc_queue *
413 ldc_queue_alloc(bus_dma_tag_t t, int nentries)
414 {
415 	struct ldc_queue *lq;
416 	bus_size_t size;
417 	caddr_t va;
418 	int nsegs;
419 
420 	lq = malloc(sizeof(struct ldc_queue), M_DEVBUF, M_NOWAIT);
421 	if (lq == NULL)
422 		return NULL;
423 
424 	size = roundup(nentries * sizeof(struct ldc_pkt), PAGE_SIZE);
425 
426 	if (bus_dmamap_create(t, size, 1, size, 0,
427 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lq->lq_map) != 0)
428 		return (NULL);
429 
430 	if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lq->lq_seg, 1,
431 	    &nsegs, BUS_DMA_NOWAIT) != 0)
432 		goto destroy;
433 
434 	if (bus_dmamem_map(t, &lq->lq_seg, 1, size, &va,
435 	    BUS_DMA_NOWAIT) != 0)
436 		goto free;
437 
438 	if (bus_dmamap_load(t, lq->lq_map, va, size, NULL,
439 	    BUS_DMA_NOWAIT) != 0)
440 		goto unmap;
441 
442 	lq->lq_va = va;
443 	lq->lq_nentries = nentries;
444 	return (lq);
445 
446 unmap:
447 	bus_dmamem_unmap(t, va, size);
448 free:
449 	bus_dmamem_free(t, &lq->lq_seg, 1);
450 destroy:
451 	bus_dmamap_destroy(t, lq->lq_map);
452 
453 	return (NULL);
454 }
455 
456 void
457 ldc_queue_free(bus_dma_tag_t t, struct ldc_queue *lq)
458 {
459 	bus_size_t size;
460 
461 	size = roundup(lq->lq_nentries * sizeof(struct ldc_pkt), PAGE_SIZE);
462 
463 	bus_dmamap_unload(t, lq->lq_map);
464 	bus_dmamem_unmap(t, lq->lq_va, size);
465 	bus_dmamem_free(t, &lq->lq_seg, 1);
466 	bus_dmamap_destroy(t, lq->lq_map);
467 	free(lq, M_DEVBUF);
468 }
469 
470 struct ldc_map *
471 ldc_map_alloc(bus_dma_tag_t t, int nentries)
472 {
473 	struct ldc_map *lm;
474 	bus_size_t size;
475 	caddr_t va;
476 	int nsegs;
477 
478 	lm = malloc(sizeof(struct ldc_map), M_DEVBUF, M_NOWAIT);
479 	if (lm == NULL)
480 		return NULL;
481 
482 	size = roundup(nentries * sizeof(struct ldc_map_slot), PAGE_SIZE);
483 
484 	if (bus_dmamap_create(t, size, 1, size, 0,
485 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lm->lm_map) != 0)
486 		return (NULL);
487 
488 	if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lm->lm_seg, 1,
489 	    &nsegs, BUS_DMA_NOWAIT) != 0)
490 		goto destroy;
491 
492 	if (bus_dmamem_map(t, &lm->lm_seg, 1, size, &va,
493 	    BUS_DMA_NOWAIT) != 0)
494 		goto free;
495 
496 	if (bus_dmamap_load(t, lm->lm_map, va, size, NULL,
497 	    BUS_DMA_NOWAIT) != 0)
498 		goto unmap;
499 
500 	lm->lm_slot = (struct ldc_map_slot *)va;
501 	lm->lm_nentries = nentries;
502 	bzero(lm->lm_slot, nentries * sizeof(struct ldc_map_slot));
503 	return (lm);
504 
505 unmap:
506 	bus_dmamem_unmap(t, va, size);
507 free:
508 	bus_dmamem_free(t, &lm->lm_seg, 1);
509 destroy:
510 	bus_dmamap_destroy(t, lm->lm_map);
511 
512 	return (NULL);
513 }
514 
515 void
516 ldc_map_free(bus_dma_tag_t t, struct ldc_map *lm)
517 {
518 	bus_size_t size;
519 
520 	size = lm->lm_nentries * sizeof(struct ldc_map_slot);
521 	size = roundup(size, PAGE_SIZE);
522 
523 	bus_dmamap_unload(t, lm->lm_map);
524 	bus_dmamem_unmap(t, (caddr_t)lm->lm_slot, size);
525 	bus_dmamem_free(t, &lm->lm_seg, 1);
526 	bus_dmamap_destroy(t, lm->lm_map);
527 	free(lm, M_DEVBUF);
528 }
529