1 /* $NetBSD: promdev.c,v 1.9 2001/01/18 12:50:10 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 <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; 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 (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_OBP_V3: { 500 char buf[64]; 501 sprintf(buf, "%lx mac-address drop swap 6 cmove", (u_long)ea); 502 prom_interpret(buf); 503 } 504 break; 505 } 506 } 507 508 509 /* 510 * A number of well-known devices on sun4s. 511 */ 512 static struct dtab { 513 char *name; 514 int type; 515 } dtab[] = { 516 { "sd", DT_BLOCK }, 517 { "st", DT_BLOCK }, 518 { "xd", DT_BLOCK }, 519 { "xy", DT_BLOCK }, 520 { "fd", DT_BLOCK }, 521 { "le", DT_NET }, 522 { "ie", DT_NET }, 523 { NULL, 0 } 524 }; 525 526 int 527 getdevtype(fd, name) 528 int fd; 529 char *name; 530 { 531 struct dtab *dp; 532 int node; 533 char *cp; 534 535 switch (prom_version()) { 536 case PROM_OLDMON: 537 case PROM_OBP_V0: 538 for (dp = dtab; dp->name; dp++) { 539 if (name[0] == dp->name[0] && 540 name[1] == dp->name[1]) 541 return (dp->type); 542 } 543 break; 544 545 case PROM_OBP_V2: 546 case PROM_OBP_V3: 547 node = (*obpvec->pv_v2devops.v2_fd_phandle)(fd); 548 cp = mygetpropstring(node, "device_type"); 549 if (strcmp(cp, "block") == 0) 550 return (DT_BLOCK); 551 else if (strcmp(cp, "network") == 0) 552 return (DT_NET); 553 else if (strcmp(cp, "byte") == 0) 554 return (DT_BYTE); 555 break; 556 } 557 return (0); 558 } 559 560 /* 561 * Return a string property. There is a (small) limit on the length; 562 * the string is fetched into a static buffer which is overwritten on 563 * subsequent calls. 564 */ 565 char * 566 mygetpropstring(node, name) 567 int node; 568 char *name; 569 { 570 int len; 571 static char buf[64]; 572 573 len = prom_proplen(node, name); 574 if (len > 0) 575 _prom_getprop(node, name, buf, len); 576 else 577 len = 0; 578 579 buf[len] = '\0'; /* usually unnecessary */ 580 return (buf); 581 } 582 #endif /* BOOTXX */ 583 584 /* 585 * Old monitor routines 586 */ 587 588 struct saioreq prom_si; 589 static int promdev_inuse; 590 591 int 592 oldmon_iopen(pd) 593 struct promdata *pd; 594 { 595 struct om_bootparam *bp; 596 struct om_boottable *ops; 597 struct devinfo *dip; 598 struct saioreq *si; 599 int error; 600 601 if (promdev_inuse) 602 return (EMFILE); 603 604 bp = *romVectorPtr->bootParam; 605 ops = bp->bootTable; 606 dip = ops->b_devinfo; 607 608 #ifdef DEBUG_PROM 609 printf("Boot device type: %s\n", ops->b_desc); 610 printf("d_devbytes=%d\n", dip->d_devbytes); 611 printf("d_dmabytes=%d\n", dip->d_dmabytes); 612 printf("d_localbytes=%d\n", dip->d_localbytes); 613 printf("d_stdcount=%d\n", dip->d_stdcount); 614 printf("d_stdaddrs[%d]=%x\n", bp->ctlrNum, dip->d_stdaddrs[bp->ctlrNum]); 615 printf("d_devtype=%d\n", dip->d_devtype); 616 printf("d_maxiobytes=%d\n", dip->d_maxiobytes); 617 #endif 618 619 dvma_init(); 620 621 si = &prom_si; 622 memset(si, 0, sizeof(*si)); 623 si->si_boottab = ops; 624 si->si_ctlr = bp->ctlrNum; 625 si->si_unit = bp->unitNum; 626 si->si_boff = bp->partNum; 627 628 if (si->si_ctlr > dip->d_stdcount) 629 return (ECTLR); 630 631 if (dip->d_devbytes) { 632 si->si_devaddr = oldmon_mapin(dip->d_stdaddrs[si->si_ctlr], 633 dip->d_devbytes, dip->d_devtype); 634 #ifdef DEBUG_PROM 635 printf("prom_iopen: devaddr=0x%x pte=0x%x\n", 636 si->si_devaddr, 637 getpte4((u_long)si->si_devaddr & ~PGOFSET)); 638 #endif 639 } 640 641 if (dip->d_dmabytes) { 642 si->si_dmaaddr = dvma_alloc(dip->d_dmabytes); 643 #ifdef DEBUG_PROM 644 printf("prom_iopen: dmaaddr=0x%x\n", si->si_dmaaddr); 645 #endif 646 } 647 648 if (dip->d_localbytes) { 649 si->si_devdata = alloc(dip->d_localbytes); 650 #ifdef DEBUG_PROM 651 printf("prom_iopen: devdata=0x%x\n", si->si_devdata); 652 #endif 653 } 654 655 /* OK, call the PROM device open routine. */ 656 error = (*ops->b_open)(si); 657 if (error != 0) { 658 printf("prom_iopen: \"%s\" error=%d\n", ops->b_desc, error); 659 return (ENXIO); 660 } 661 #ifdef DEBUG_PROM 662 printf("prom_iopen: succeeded, error=%d\n", error); 663 #endif 664 665 pd->si = si; 666 promdev_inuse++; 667 return (0); 668 } 669 670 void 671 oldmon_iclose(si) 672 struct saioreq *si; 673 { 674 struct om_boottable *ops; 675 struct devinfo *dip; 676 677 if (promdev_inuse == 0) 678 return; 679 680 ops = si->si_boottab; 681 dip = ops->b_devinfo; 682 683 (*ops->b_close)(si); 684 685 if (si->si_dmaaddr) { 686 dvma_free(si->si_dmaaddr, dip->d_dmabytes); 687 si->si_dmaaddr = NULL; 688 } 689 690 promdev_inuse = 0; 691 } 692 693 static struct mapinfo { 694 int maptype; 695 int pgtype; 696 int base; 697 } oldmon_mapinfo[] = { 698 #define PG_COMMON (PG_V|PG_W|PG_S|PG_NC) 699 { MAP_MAINMEM, PG_OBMEM | PG_COMMON, 0 }, 700 { MAP_OBIO, PG_OBIO | PG_COMMON, 0 }, 701 { MAP_MBMEM, PG_VME16 | PG_COMMON, 0xFF000000 }, 702 { MAP_MBIO, PG_VME16 | PG_COMMON, 0xFFFF0000 }, 703 { MAP_VME16A16D, PG_VME16 | PG_COMMON, 0xFFFF0000 }, 704 { MAP_VME16A32D, PG_VME32 | PG_COMMON, 0xFFFF0000 }, 705 { MAP_VME24A16D, PG_VME16 | PG_COMMON, 0xFF000000 }, 706 { MAP_VME24A32D, PG_VME32 | PG_COMMON, 0xFF000000 }, 707 { MAP_VME32A16D, PG_VME16 | PG_COMMON, 0 }, 708 { MAP_VME32A32D, PG_VME32 | PG_COMMON, 0 }, 709 }; 710 static int oldmon_mapinfo_cnt = 711 sizeof(oldmon_mapinfo) / sizeof(oldmon_mapinfo[0]); 712 713 /* The virtual address we will use for PROM device mappings. */ 714 static u_long prom_devmap = MONSHORTSEG; 715 716 static char * 717 oldmon_mapin(physaddr, length, maptype) 718 u_long physaddr; 719 int length, maptype; 720 { 721 int i, pa, pte, va; 722 723 if (length > (4*NBPG)) 724 panic("oldmon_mapin: length=%d\n", length); 725 726 for (i = 0; i < oldmon_mapinfo_cnt; i++) 727 if (oldmon_mapinfo[i].maptype == maptype) 728 goto found; 729 panic("oldmon_mapin: invalid maptype %d\n", maptype); 730 731 found: 732 pte = oldmon_mapinfo[i].pgtype; 733 pa = oldmon_mapinfo[i].base; 734 pa += physaddr; 735 pte |= ((pa >> SUN4_PGSHIFT) & PG_PFNUM); 736 737 va = prom_devmap; 738 do { 739 setpte4(va, pte); 740 va += NBPG; 741 pte += 1; 742 length -= NBPG; 743 } while (length > 0); 744 return ((char*)(prom_devmap | (pa & PGOFSET))); 745 } 746