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