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