1 /* $NetBSD: promdev.c,v 1.7 1999/02/15 18:59:36 pk Exp $ */ 2 3 /* 4 * Copyright (c) 1993 Paul Kranenburg 5 * Copyright (c) 1995 Gordon W. Ross 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Paul Kranenburg. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Note: the `#ifndef BOOTXX' in here serve to queeze the code size 36 * of the 1st-stage boot program. 37 */ 38 #include <sys/param.h> 39 #include <sys/reboot.h> 40 #include <machine/idprom.h> 41 #include <machine/oldmon.h> 42 #include <machine/promlib.h> 43 #include <machine/ctlreg.h> 44 #include <sparc/sparc/asm.h> 45 #include <machine/pte.h> 46 47 #include <lib/libsa/stand.h> 48 49 #include <sparc/stand/common/promdev.h> 50 51 /* OBP V0-3 PROM vector */ 52 #define obpvec ((struct promvec *)romp) 53 54 int obp_close __P((struct open_file *)); 55 int obp_strategy __P((void *, int, daddr_t, size_t, void *, size_t *)); 56 int obp_v0_strategy __P((void *, int, daddr_t, size_t, void *, size_t *)); 57 ssize_t obp_v0_xmit __P((struct promdata *, void *, size_t)); 58 ssize_t obp_v0_recv __P((struct promdata *, void *, size_t)); 59 int obp_v2_strategy __P((void *, int, daddr_t, size_t, void *, size_t *)); 60 ssize_t obp_v2_xmit __P((struct promdata *, void *, size_t)); 61 ssize_t obp_v2_recv __P((struct promdata *, void *, size_t)); 62 int oldmon_close __P((struct open_file *)); 63 int oldmon_strategy __P((void *, int, daddr_t, size_t, void *, size_t *)); 64 void oldmon_iclose __P((struct saioreq *)); 65 int oldmon_iopen __P((struct promdata *)); 66 ssize_t oldmon_xmit __P((struct promdata *, void *, size_t)); 67 ssize_t oldmon_recv __P((struct promdata *, void *, size_t)); 68 69 static char *oldmon_mapin __P((u_long, int, int)); 70 #ifndef BOOTXX 71 static char *mygetpropstring __P((int, char *)); 72 static int getdevtype __P((int, char *)); 73 #endif 74 75 extern struct filesystem file_system_nfs[]; 76 extern struct filesystem file_system_ufs[]; 77 78 #define null_devopen (void *)sparc_noop 79 #define null_devioctl (void *)sparc_noop 80 81 #if 0 82 struct devsw devsw[]; 83 int ndevs = (sizeof(devsw)/sizeof(devsw[0])); 84 #endif 85 86 struct devsw oldmon_devsw = 87 { "oldmon", oldmon_strategy, null_devopen, oldmon_close, null_devioctl }; 88 struct devsw obp_v0_devsw = 89 { "obp v0", obp_v0_strategy, null_devopen, obp_close, null_devioctl }; 90 struct devsw obp_v2_devsw = 91 { "obp v2", obp_v2_strategy, null_devopen, obp_close, null_devioctl }; 92 93 94 char *prom_bootdevice; 95 static int saveecho; 96 97 98 void 99 putchar(c) 100 int c; 101 { 102 103 if (c == '\n') 104 prom_putchar('\r'); 105 prom_putchar(c); 106 } 107 108 void 109 _rtt() 110 { 111 prom_halt(); 112 } 113 114 int 115 devopen(f, fname, file) 116 struct open_file *f; 117 const char *fname; 118 char **file; 119 { 120 int error = 0, fd; 121 struct promdata *pd; 122 123 pd = (struct promdata *)alloc(sizeof *pd); 124 f->f_devdata = (void *)pd; 125 126 switch (prom_version()) { 127 case PROM_OLDMON: 128 error = oldmon_iopen(pd); 129 #ifndef BOOTXX 130 pd->xmit = oldmon_xmit; 131 pd->recv = oldmon_recv; 132 #endif 133 f->f_dev = &oldmon_devsw; 134 saveecho = *romVectorPtr->echo; 135 *romVectorPtr->echo = 0; 136 break; 137 138 case PROM_OBP_V0: 139 case PROM_OBP_V2: 140 case PROM_OBP_V3: 141 case PROM_OPENFIRM: 142 if (prom_bootdevice == NULL) { 143 error = ENXIO; 144 break; 145 } 146 fd = prom_open(prom_bootdevice); 147 if (fd == 0) { 148 error = ENXIO; 149 break; 150 } 151 pd->fd = fd; 152 switch (prom_version()) { 153 case PROM_OBP_V0: 154 #ifndef BOOTXX 155 pd->xmit = obp_v0_xmit; 156 pd->recv = obp_v0_recv; 157 #endif 158 f->f_dev = &obp_v0_devsw; 159 break; 160 case PROM_OBP_V2: 161 case PROM_OBP_V3: 162 case PROM_OPENFIRM: 163 #ifndef BOOTXX 164 pd->xmit = obp_v2_xmit; 165 pd->recv = obp_v2_recv; 166 #endif 167 f->f_dev = &obp_v2_devsw; 168 } 169 } 170 171 if (error) { 172 printf("Can't open device `%s'\n", prom_bootdevice); 173 return (error); 174 } 175 176 #ifdef BOOTXX 177 pd->devtype = DT_BLOCK; 178 #else /* BOOTXX */ 179 pd->devtype = getdevtype(fd, prom_bootdevice); 180 /* Assume type BYTE is a raw device */ 181 if (pd->devtype != DT_BYTE) 182 *file = (char *)fname; 183 184 if (pd->devtype == DT_NET) { 185 bcopy(file_system_nfs, file_system, sizeof(struct fs_ops)); 186 if ((error = net_open(pd)) != 0) { 187 printf("Can't open NFS network connection on `%s'\n", 188 prom_bootdevice); 189 return (error); 190 } 191 } else 192 bcopy(file_system_ufs, file_system, sizeof(struct fs_ops)); 193 #endif /* BOOTXX */ 194 return (0); 195 } 196 197 198 int 199 obp_v0_strategy(devdata, flag, dblk, size, buf, rsize) 200 void *devdata; 201 int flag; 202 daddr_t dblk; 203 size_t size; 204 void *buf; 205 size_t *rsize; 206 { 207 int n, error = 0; 208 struct promdata *pd = (struct promdata *)devdata; 209 int fd = pd->fd; 210 211 #ifdef DEBUG_PROM 212 printf("promstrategy: size=%d dblk=%d\n", size, dblk); 213 #endif 214 215 #define prom_bread(fd, nblk, dblk, buf) \ 216 (*obpvec->pv_v0devops.v0_rbdev)(fd, nblk, dblk, buf) 217 #define prom_bwrite(fd, nblk, dblk, buf) \ 218 (*obpvec->pv_v0devops.v0_wbdev)(fd, nblk, dblk, buf) 219 220 #ifndef BOOTXX /* We know it's a block device, so save some space */ 221 if (pd->devtype != DT_BLOCK) { 222 printf("promstrategy: non-block device not supported\n"); 223 error = EINVAL; 224 } 225 #endif 226 227 n = (flag == F_READ) 228 ? prom_bread(fd, btodb(size), dblk, buf) 229 : prom_bwrite(fd, btodb(size), dblk, buf); 230 231 *rsize = dbtob(n); 232 233 #ifdef DEBUG_PROM 234 printf("rsize = %x\n", *rsize); 235 #endif 236 return (error); 237 } 238 239 int 240 obp_v2_strategy(devdata, flag, dblk, size, buf, rsize) 241 void *devdata; 242 int flag; 243 daddr_t dblk; 244 size_t size; 245 void *buf; 246 size_t *rsize; 247 { 248 int error = 0; 249 struct promdata *pd = (struct promdata *)devdata; 250 int fd = pd->fd; 251 252 #ifdef DEBUG_PROM 253 printf("promstrategy: size=%d dblk=%d\n", size, dblk); 254 #endif 255 256 #ifndef BOOTXX /* We know it's a block device, so save some space */ 257 if (pd->devtype == DT_BLOCK) 258 #endif 259 prom_seek(fd, dbtob(dblk)); 260 261 *rsize = (flag == F_READ) 262 ? prom_read(fd, buf, size) 263 : prom_write(fd, buf, size); 264 265 #ifdef DEBUG_PROM 266 printf("rsize = %x\n", *rsize); 267 #endif 268 return (error); 269 } 270 271 /* 272 * On old-monitor machines, things work differently. 273 */ 274 int 275 oldmon_strategy(devdata, flag, dblk, size, buf, rsize) 276 void *devdata; 277 int flag; 278 daddr_t dblk; 279 size_t size; 280 void *buf; 281 size_t *rsize; 282 { 283 struct promdata *pd = devdata; 284 struct saioreq *si; 285 struct om_boottable *ops; 286 char *dmabuf; 287 int si_flag; 288 size_t xcnt; 289 290 si = pd->si; 291 ops = si->si_boottab; 292 293 #ifdef DEBUG_PROM 294 printf("prom_strategy: size=%d dblk=%d\n", size, dblk); 295 #endif 296 297 dmabuf = dvma_mapin(buf, size); 298 299 si->si_bn = dblk; 300 si->si_ma = dmabuf; 301 si->si_cc = size; 302 303 si_flag = (flag == F_READ) ? SAIO_F_READ : SAIO_F_WRITE; 304 xcnt = (*ops->b_strategy)(si, si_flag); 305 dvma_mapout(dmabuf, size); 306 307 #ifdef DEBUG_PROM 308 printf("disk_strategy: xcnt = %x\n", xcnt); 309 #endif 310 311 if (xcnt <= 0) 312 return (EIO); 313 314 *rsize = xcnt; 315 return (0); 316 } 317 318 int 319 obp_close(f) 320 struct open_file *f; 321 { 322 struct promdata *pd = f->f_devdata; 323 register int fd = pd->fd; 324 325 #ifndef BOOTXX 326 if (pd->devtype == DT_NET) 327 net_close(pd); 328 #endif 329 prom_close(fd); 330 return 0; 331 } 332 333 int 334 oldmon_close(f) 335 struct open_file *f; 336 { 337 struct promdata *pd = f->f_devdata; 338 339 #ifndef BOOTXX 340 if (pd->devtype == DT_NET) 341 net_close(pd); 342 #endif 343 oldmon_iclose(pd->si); 344 pd->si = NULL; 345 *romVectorPtr->echo = saveecho; /* Hmm, probably must go somewhere else */ 346 return 0; 347 } 348 349 #ifndef BOOTXX 350 ssize_t 351 obp_v0_xmit(pd, buf, len) 352 struct promdata *pd; 353 void *buf; 354 size_t len; 355 { 356 357 return ((*obpvec->pv_v0devops.v0_wnet)(pd->fd, len, buf)); 358 } 359 360 ssize_t 361 obp_v2_xmit(pd, buf, len) 362 struct promdata *pd; 363 void *buf; 364 size_t len; 365 { 366 367 return (prom_write(pd->fd, buf, len)); 368 } 369 370 ssize_t 371 obp_v0_recv(pd, buf, len) 372 struct promdata *pd; 373 void *buf; 374 size_t len; 375 { 376 377 return ((*obpvec->pv_v0devops.v0_rnet)(pd->fd, len, buf)); 378 } 379 380 ssize_t 381 obp_v2_recv(pd, buf, len) 382 struct promdata *pd; 383 void *buf; 384 size_t len; 385 { 386 int n; 387 388 n = prom_read(pd->fd, buf, len); 389 390 /* OBP V2 & V3 may return -2 */ 391 return (n == -2 ? 0 : n); 392 } 393 394 ssize_t 395 oldmon_xmit(pd, buf, len) 396 struct promdata *pd; 397 void *buf; 398 size_t len; 399 { 400 struct saioreq *si; 401 struct saif *sif; 402 char *dmabuf; 403 int rv; 404 405 si = pd->si; 406 sif = si->si_sif; 407 if (sif == NULL) { 408 printf("xmit: not a network device\n"); 409 return (-1); 410 } 411 dmabuf = dvma_mapin(buf, len); 412 rv = sif->sif_xmit(si->si_devdata, dmabuf, len); 413 dvma_mapout(dmabuf, len); 414 415 return (ssize_t)(rv ? -1 : len); 416 } 417 418 ssize_t 419 oldmon_recv(pd, buf, len) 420 struct promdata *pd; 421 void *buf; 422 size_t len; 423 { 424 struct saioreq *si; 425 struct saif *sif; 426 char *dmabuf; 427 int rv; 428 429 si = pd->si; 430 sif = si->si_sif; 431 dmabuf = dvma_mapin(buf, len); 432 rv = sif->sif_poll(si->si_devdata, dmabuf); 433 dvma_mapout(dmabuf, len); 434 435 return (ssize_t)rv; 436 } 437 438 int 439 getchar() 440 { 441 return (prom_getchar()); 442 } 443 444 time_t 445 getsecs() 446 { 447 (void)prom_peekchar(); 448 return (prom_ticks() / 1000); 449 } 450 451 452 453 void 454 prom_getether(fd, ea) 455 int fd; 456 u_char *ea; 457 { 458 static struct idprom idprom; 459 char buf[64]; 460 u_char *src, *dst; 461 int len, x; 462 463 switch (prom_version()) { 464 case PROM_OLDMON: 465 if (idprom.id_format == 0) { 466 dst = (char*)&idprom; 467 src = (char*)AC_IDPROM; 468 len = sizeof(struct idprom); 469 do { 470 x = lduba(src++, ASI_CONTROL); 471 *dst++ = x; 472 } while (--len > 0); 473 } 474 bcopy(idprom.id_ether, ea, 6); 475 break; 476 case PROM_OBP_V0: 477 case PROM_OBP_V2: 478 (void)(*obpvec->pv_enaddr)(fd, (char *)ea); 479 break; 480 case PROM_OBP_V3: 481 sprintf(buf, "%lx mac-address drop swap 6 cmove", (u_long)ea); 482 prom_interpret(buf); 483 break; 484 } 485 } 486 487 488 /* 489 * A number of well-known devices on sun4s. 490 */ 491 static struct dtab { 492 char *name; 493 int type; 494 } dtab[] = { 495 { "sd", DT_BLOCK }, 496 { "st", DT_BLOCK }, 497 { "xd", DT_BLOCK }, 498 { "xy", DT_BLOCK }, 499 { "fd", DT_BLOCK }, 500 { "le", DT_NET }, 501 { "ie", DT_NET }, 502 { NULL, 0 } 503 }; 504 505 int 506 getdevtype(fd, name) 507 int fd; 508 char *name; 509 { 510 struct dtab *dp; 511 int node; 512 char *cp; 513 514 switch (prom_version()) { 515 case PROM_OLDMON: 516 case PROM_OBP_V0: 517 for (dp = dtab; dp->name; dp++) { 518 if (name[0] == dp->name[0] && 519 name[1] == dp->name[1]) 520 return (dp->type); 521 } 522 break; 523 524 case PROM_OBP_V2: 525 case PROM_OBP_V3: 526 node = (*obpvec->pv_v2devops.v2_fd_phandle)(fd); 527 cp = mygetpropstring(node, "device_type"); 528 if (strcmp(cp, "block") == 0) 529 return (DT_BLOCK); 530 else if (strcmp(cp, "network") == 0) 531 return (DT_NET); 532 else if (strcmp(cp, "byte") == 0) 533 return (DT_BYTE); 534 break; 535 } 536 return (0); 537 } 538 539 /* 540 * Return a string property. There is a (small) limit on the length; 541 * the string is fetched into a static buffer which is overwritten on 542 * subsequent calls. 543 */ 544 char * 545 mygetpropstring(node, name) 546 int node; 547 char *name; 548 { 549 int len; 550 static char buf[64]; 551 552 len = prom_proplen(node, name); 553 if (len > 0) 554 _prom_getprop(node, name, buf, len); 555 else 556 len = 0; 557 558 buf[len] = '\0'; /* usually unnecessary */ 559 return (buf); 560 } 561 #endif /* BOOTXX */ 562 563 /* 564 * Old monitor routines 565 */ 566 567 struct saioreq prom_si; 568 static int promdev_inuse; 569 570 int 571 oldmon_iopen(pd) 572 struct promdata *pd; 573 { 574 struct om_bootparam *bp; 575 struct om_boottable *ops; 576 struct devinfo *dip; 577 struct saioreq *si; 578 int error; 579 580 if (promdev_inuse) 581 return (EMFILE); 582 583 bp = *romVectorPtr->bootParam; 584 ops = bp->bootTable; 585 dip = ops->b_devinfo; 586 587 #ifdef DEBUG_PROM 588 printf("Boot device type: %s\n", ops->b_desc); 589 printf("d_devbytes=%d\n", dip->d_devbytes); 590 printf("d_dmabytes=%d\n", dip->d_dmabytes); 591 printf("d_localbytes=%d\n", dip->d_localbytes); 592 printf("d_stdcount=%d\n", dip->d_stdcount); 593 printf("d_stdaddrs[%d]=%x\n", bp->ctlrNum, dip->d_stdaddrs[bp->ctlrNum]); 594 printf("d_devtype=%d\n", dip->d_devtype); 595 printf("d_maxiobytes=%d\n", dip->d_maxiobytes); 596 #endif 597 598 dvma_init(); 599 600 si = &prom_si; 601 memset(si, 0, sizeof(*si)); 602 si->si_boottab = ops; 603 si->si_ctlr = bp->ctlrNum; 604 si->si_unit = bp->unitNum; 605 si->si_boff = bp->partNum; 606 607 if (si->si_ctlr > dip->d_stdcount) 608 return (ECTLR); 609 610 if (dip->d_devbytes) { 611 si->si_devaddr = oldmon_mapin(dip->d_stdaddrs[si->si_ctlr], 612 dip->d_devbytes, dip->d_devtype); 613 #ifdef DEBUG_PROM 614 printf("prom_iopen: devaddr=0x%x pte=0x%x\n", 615 si->si_devaddr, 616 getpte4((u_long)si->si_devaddr & ~PGOFSET)); 617 #endif 618 } 619 620 if (dip->d_dmabytes) { 621 si->si_dmaaddr = dvma_alloc(dip->d_dmabytes); 622 #ifdef DEBUG_PROM 623 printf("prom_iopen: dmaaddr=0x%x\n", si->si_dmaaddr); 624 #endif 625 } 626 627 if (dip->d_localbytes) { 628 si->si_devdata = alloc(dip->d_localbytes); 629 #ifdef DEBUG_PROM 630 printf("prom_iopen: devdata=0x%x\n", si->si_devdata); 631 #endif 632 } 633 634 /* OK, call the PROM device open routine. */ 635 error = (*ops->b_open)(si); 636 if (error != 0) { 637 printf("prom_iopen: \"%s\" error=%d\n", ops->b_desc, error); 638 return (ENXIO); 639 } 640 #ifdef DEBUG_PROM 641 printf("prom_iopen: succeeded, error=%d\n", error); 642 #endif 643 644 pd->si = si; 645 promdev_inuse++; 646 return (0); 647 } 648 649 void 650 oldmon_iclose(si) 651 struct saioreq *si; 652 { 653 struct om_boottable *ops; 654 struct devinfo *dip; 655 656 if (promdev_inuse == 0) 657 return; 658 659 ops = si->si_boottab; 660 dip = ops->b_devinfo; 661 662 (*ops->b_close)(si); 663 664 if (si->si_dmaaddr) { 665 dvma_free(si->si_dmaaddr, dip->d_dmabytes); 666 si->si_dmaaddr = NULL; 667 } 668 669 promdev_inuse = 0; 670 } 671 672 static struct mapinfo { 673 int maptype; 674 int pgtype; 675 int base; 676 } oldmon_mapinfo[] = { 677 #define PG_COMMON (PG_V|PG_W|PG_S|PG_NC) 678 { MAP_MAINMEM, PG_OBMEM | PG_COMMON, 0 }, 679 { MAP_OBIO, PG_OBIO | PG_COMMON, 0 }, 680 { MAP_MBMEM, PG_VME16 | PG_COMMON, 0xFF000000 }, 681 { MAP_MBIO, PG_VME16 | PG_COMMON, 0xFFFF0000 }, 682 { MAP_VME16A16D, PG_VME16 | PG_COMMON, 0xFFFF0000 }, 683 { MAP_VME16A32D, PG_VME32 | PG_COMMON, 0xFFFF0000 }, 684 { MAP_VME24A16D, PG_VME16 | PG_COMMON, 0xFF000000 }, 685 { MAP_VME24A32D, PG_VME32 | PG_COMMON, 0xFF000000 }, 686 { MAP_VME32A16D, PG_VME16 | PG_COMMON, 0 }, 687 { MAP_VME32A32D, PG_VME32 | PG_COMMON, 0 }, 688 }; 689 static int oldmon_mapinfo_cnt = 690 sizeof(oldmon_mapinfo) / sizeof(oldmon_mapinfo[0]); 691 692 /* The virtual address we will use for PROM device mappings. */ 693 static u_long prom_devmap = MONSHORTSEG; 694 695 static char * 696 oldmon_mapin(physaddr, length, maptype) 697 u_long physaddr; 698 int length, maptype; 699 { 700 int i, pa, pte, va; 701 702 if (length > (4*NBPG)) 703 panic("oldmon_mapin: length=%d\n", length); 704 705 for (i = 0; i < oldmon_mapinfo_cnt; i++) 706 if (oldmon_mapinfo[i].maptype == maptype) 707 goto found; 708 panic("oldmon_mapin: invalid maptype %d\n", maptype); 709 710 found: 711 pte = oldmon_mapinfo[i].pgtype; 712 pa = oldmon_mapinfo[i].base; 713 pa += physaddr; 714 pte |= ((pa >> SUN4_PGSHIFT) & PG_PFNUM); 715 716 va = prom_devmap; 717 do { 718 setpte4(va, pte); 719 va += NBPG; 720 pte += 1; 721 length -= NBPG; 722 } while (length > 0); 723 return ((char*)(prom_devmap | (pa & PGOFSET))); 724 } 725