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