1 /* $OpenBSD: ldc.c,v 1.3 2009/01/16 16:58:09 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 37 void ldc_send_rtr(struct ldc_conn *); 38 void ldc_send_rts(struct ldc_conn *); 39 void ldc_send_rdx(struct ldc_conn *); 40 41 void 42 ldc_rx_ctrl(struct ldc_conn *lc, struct ldc_pkt *lp) 43 { 44 switch (lp->ctrl) { 45 case LDC_VERS: 46 ldc_rx_ctrl_vers(lc, lp); 47 break; 48 49 case LDC_RTS: 50 ldc_rx_ctrl_rts(lc, lp); 51 break; 52 53 case LDC_RTR: 54 ldc_rx_ctrl_rtr(lc, lp); 55 break; 56 57 default: 58 DPRINTF(("CTRL/0x%02x/0x%02x\n", lp->stype, lp->ctrl)); 59 ldc_reset(lc); 60 break; 61 } 62 } 63 64 void 65 ldc_rx_ctrl_vers(struct ldc_conn *lc, struct ldc_pkt *lp) 66 { 67 switch (lp->stype) { 68 case LDC_INFO: 69 /* XXX do nothing for now. */ 70 break; 71 72 case LDC_ACK: 73 if (lc->lc_state != LDC_SND_VERS) { 74 DPRINTF(("Spurious CTRL/ACK/VERS: state %d\n", 75 lc->lc_state)); 76 ldc_reset(lc); 77 return; 78 } 79 DPRINTF(("CTRL/ACK/VERS\n")); 80 ldc_send_rts(lc); 81 break; 82 83 case LDC_NACK: 84 DPRINTF(("CTRL/NACK/VERS\n")); 85 ldc_reset(lc); 86 break; 87 88 default: 89 DPRINTF(("CTRL/0x%02x/VERS\n", lp->stype)); 90 ldc_reset(lc); 91 break; 92 } 93 } 94 95 void 96 ldc_rx_ctrl_rts(struct ldc_conn *lc, struct ldc_pkt *lp) 97 { 98 switch (lp->stype) { 99 case LDC_INFO: 100 if (lc->lc_state != LDC_RCV_VERS) { 101 DPRINTF(("Suprious CTRL/INFO/RTS: state %d\n", 102 lc->lc_state)); 103 ldc_reset(lc); 104 return; 105 } 106 DPRINTF(("CTRL/INFO/RTS\n")); 107 ldc_send_rtr(lc); 108 break; 109 110 case LDC_ACK: 111 DPRINTF(("CTRL/ACK/RTS\n")); 112 ldc_reset(lc); 113 break; 114 115 case LDC_NACK: 116 DPRINTF(("CTRL/NACK/RTS\n")); 117 ldc_reset(lc); 118 break; 119 120 default: 121 DPRINTF(("CTRL/0x%02x/RTS\n", lp->stype)); 122 ldc_reset(lc); 123 break; 124 } 125 } 126 127 void 128 ldc_rx_ctrl_rtr(struct ldc_conn *lc, struct ldc_pkt *lp) 129 { 130 switch (lp->stype) { 131 case LDC_INFO: 132 if (lc->lc_state != LDC_SND_RTS) { 133 DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n", 134 lc->lc_state)); 135 ldc_reset(lc); 136 return; 137 } 138 DPRINTF(("CTRL/INFO/RTR\n")); 139 ldc_send_rdx(lc); 140 lc->lc_start(lc); 141 break; 142 143 case LDC_ACK: 144 DPRINTF(("CTRL/ACK/RTR\n")); 145 ldc_reset(lc); 146 break; 147 148 case LDC_NACK: 149 DPRINTF(("CTRL/NACK/RTR\n")); 150 ldc_reset(lc); 151 break; 152 153 default: 154 DPRINTF(("CTRL/0x%02x/RTR\n", lp->stype)); 155 ldc_reset(lc); 156 break; 157 } 158 } 159 160 void 161 ldc_rx_data(struct ldc_conn *lc, struct ldc_pkt *lp) 162 { 163 if (lp->stype != LDC_INFO) { 164 DPRINTF(("DATA/0x%02x\n", lp->stype)); 165 ldc_reset(lc); 166 return; 167 } 168 169 if (lc->lc_state != LDC_SND_RTR && 170 lc->lc_state != LDC_SND_RDX) { 171 DPRINTF(("Spurious DATA/INFO: state %d\n", lc->lc_state)); 172 ldc_reset(lc); 173 return; 174 } 175 176 lc->lc_rx_data(lc, lp); 177 } 178 179 void 180 ldc_send_vers(struct ldc_conn *lc) 181 { 182 struct ldc_pkt *lp; 183 uint64_t tx_head, tx_tail, tx_state; 184 int err; 185 186 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 187 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 188 return; 189 190 lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail); 191 bzero(lp, sizeof(struct ldc_pkt)); 192 lp->type = LDC_CTRL; 193 lp->stype = LDC_INFO; 194 lp->ctrl = LDC_VERS; 195 lp->major = 1; 196 lp->minor = 0; 197 198 tx_tail += sizeof(*lp); 199 tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1); 200 err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); 201 if (err != H_EOK) { 202 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 203 return; 204 } 205 206 lc->lc_state = LDC_SND_VERS; 207 } 208 209 void 210 ldc_send_rts(struct ldc_conn *lc) 211 { 212 struct ldc_pkt *lp; 213 uint64_t tx_head, tx_tail, tx_state; 214 int err; 215 216 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 217 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 218 return; 219 220 lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail); 221 bzero(lp, sizeof(struct ldc_pkt)); 222 lp->type = LDC_CTRL; 223 lp->stype = LDC_INFO; 224 lp->ctrl = LDC_RTS; 225 lp->env = LDC_MODE_UNRELIABLE; 226 lp->seqid = lc->lc_tx_seqid++; 227 228 tx_tail += sizeof(*lp); 229 tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1); 230 err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); 231 if (err != H_EOK) { 232 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 233 return; 234 } 235 236 lc->lc_state = LDC_SND_RTS; 237 } 238 239 void 240 ldc_send_rtr(struct ldc_conn *lc) 241 { 242 struct ldc_pkt *lp; 243 uint64_t tx_head, tx_tail, tx_state; 244 int err; 245 246 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 247 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 248 return; 249 250 lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail); 251 bzero(lp, sizeof(struct ldc_pkt)); 252 lp->type = LDC_CTRL; 253 lp->stype = LDC_INFO; 254 lp->ctrl = LDC_RTR; 255 lp->env = LDC_MODE_UNRELIABLE; 256 lp->seqid = lc->lc_tx_seqid++; 257 258 tx_tail += sizeof(*lp); 259 tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1); 260 err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); 261 if (err != H_EOK) 262 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 263 264 lc->lc_state = LDC_SND_RTR; 265 } 266 267 void 268 ldc_send_rdx(struct ldc_conn *lc) 269 { 270 struct ldc_pkt *lp; 271 uint64_t tx_head, tx_tail, tx_state; 272 int err; 273 274 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 275 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 276 return; 277 278 lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail); 279 bzero(lp, sizeof(struct ldc_pkt)); 280 lp->type = LDC_CTRL; 281 lp->stype = LDC_INFO; 282 lp->ctrl = LDC_RDX; 283 lp->env = LDC_MODE_UNRELIABLE; 284 lp->seqid = lc->lc_tx_seqid++; 285 286 tx_tail += sizeof(*lp); 287 tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1); 288 err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); 289 if (err != H_EOK) 290 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 291 292 lc->lc_state = LDC_SND_RDX; 293 } 294 295 void 296 ldc_reset(struct ldc_conn *lc) 297 { 298 int err; 299 300 DPRINTF(("Resetting connection\n")); 301 hv_ldc_tx_qconf(lc->lc_id, 0, 0); 302 hv_ldc_rx_qconf(lc->lc_id, 0, 0); 303 lc->lc_tx_state = lc->lc_rx_state = LDC_CHANNEL_DOWN; 304 305 err = hv_ldc_tx_qconf(lc->lc_id, 306 lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries); 307 if (err != H_EOK) 308 printf("%s: hv_ldc_tx_qconf %d\n", __func__, err); 309 310 err = hv_ldc_rx_qconf(lc->lc_id, 311 lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries); 312 if (err != H_EOK) 313 printf("%s: hv_ldc_rx_qconf %d\n", __func__, err); 314 315 lc->lc_tx_seqid = 0; 316 lc->lc_state = 0; 317 lc->lc_reset(lc); 318 } 319 320 struct ldc_queue * 321 ldc_queue_alloc(bus_dma_tag_t t, int nentries) 322 { 323 struct ldc_queue *lq; 324 bus_size_t size; 325 caddr_t va; 326 int nsegs; 327 328 lq = malloc(sizeof(struct ldc_queue), M_DEVBUF, M_NOWAIT); 329 if (lq == NULL) 330 return NULL; 331 332 size = roundup(nentries * sizeof(struct ldc_pkt), PAGE_SIZE); 333 334 if (bus_dmamap_create(t, size, 1, size, 0, 335 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lq->lq_map) != 0) 336 return (NULL); 337 338 if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lq->lq_seg, 1, 339 &nsegs, BUS_DMA_NOWAIT) != 0) 340 goto destroy; 341 342 if (bus_dmamem_map(t, &lq->lq_seg, 1, size, &va, 343 BUS_DMA_NOWAIT) != 0) 344 goto free; 345 346 if (bus_dmamap_load(t, lq->lq_map, va, size, NULL, 347 BUS_DMA_NOWAIT) != 0) 348 goto unmap; 349 350 lq->lq_va = va; 351 lq->lq_nentries = nentries; 352 return (lq); 353 354 unmap: 355 bus_dmamem_unmap(t, va, size); 356 free: 357 bus_dmamem_free(t, &lq->lq_seg, 1); 358 destroy: 359 bus_dmamap_destroy(t, lq->lq_map); 360 361 return (NULL); 362 } 363 364 void 365 ldc_queue_free(bus_dma_tag_t t, struct ldc_queue *lq) 366 { 367 bus_size_t size; 368 369 size = roundup(lq->lq_nentries * sizeof(struct ldc_pkt), PAGE_SIZE); 370 371 bus_dmamap_unload(t, lq->lq_map); 372 bus_dmamem_unmap(t, lq->lq_va, size); 373 bus_dmamem_free(t, &lq->lq_seg, 1); 374 bus_dmamap_destroy(t, lq->lq_map); 375 free(lq, M_DEVBUF); 376 } 377 378 struct ldc_map * 379 ldc_map_alloc(bus_dma_tag_t t, int nentries) 380 { 381 struct ldc_map *lm; 382 bus_size_t size; 383 caddr_t va; 384 int nsegs; 385 386 lm = malloc(sizeof(struct ldc_map), M_DEVBUF, M_NOWAIT); 387 if (lm == NULL) 388 return NULL; 389 390 size = roundup(nentries * sizeof(struct ldc_map_slot), PAGE_SIZE); 391 392 if (bus_dmamap_create(t, size, 1, size, 0, 393 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lm->lm_map) != 0) 394 return (NULL); 395 396 if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lm->lm_seg, 1, 397 &nsegs, BUS_DMA_NOWAIT) != 0) 398 goto destroy; 399 400 if (bus_dmamem_map(t, &lm->lm_seg, 1, size, &va, 401 BUS_DMA_NOWAIT) != 0) 402 goto free; 403 404 if (bus_dmamap_load(t, lm->lm_map, va, size, NULL, 405 BUS_DMA_NOWAIT) != 0) 406 goto unmap; 407 408 lm->lm_slot = (struct ldc_map_slot *)va; 409 lm->lm_nentries = nentries; 410 bzero(lm->lm_slot, nentries * sizeof(struct ldc_map_slot)); 411 return (lm); 412 413 unmap: 414 bus_dmamem_unmap(t, va, size); 415 free: 416 bus_dmamem_free(t, &lm->lm_seg, 1); 417 destroy: 418 bus_dmamap_destroy(t, lm->lm_map); 419 420 return (NULL); 421 } 422 423 void 424 ldc_map_free(bus_dma_tag_t t, struct ldc_map *lm) 425 { 426 bus_size_t size; 427 428 size = lm->lm_nentries * sizeof(struct ldc_map_slot); 429 size = roundup(size, PAGE_SIZE); 430 431 bus_dmamap_unload(t, lm->lm_map); 432 bus_dmamem_unmap(t, (caddr_t)lm->lm_slot, size); 433 bus_dmamem_free(t, &lm->lm_seg, 1); 434 bus_dmamap_destroy(t, lm->lm_map); 435 free(lm, M_DEVBUF); 436 } 437