1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 11 unchanged lines hidden (view full) --- 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> |
28__FBSDID("$FreeBSD: head/sys/boot/pc98/libpc98/biosdisk.c 146011 2005-05-08 14:17:28Z nyan $"); |
29 30/* 31 * BIOS disk device handling. 32 * 33 * Ideas and algorithms from: 34 * 35 * - NetBSD libi386/biosdisk.c 36 * - FreeBSD biosboot/disk.c --- 39 unchanged lines hidden (view full) --- 76 int od_flags; 77#define BD_MODEINT13 0x0000 78#define BD_MODEEDD1 0x0001 79#define BD_MODEEDD3 0x0002 80#define BD_MODEMASK 0x0003 81#define BD_FLOPPY 0x0004 82#define BD_LABELOK 0x0008 83#define BD_PARTTABOK 0x0010 |
84#define BD_OPTICAL 0x0020 |
85 struct disklabel od_disklabel; 86 int od_nslices; /* slice count */ 87 struct pc98_partition od_slicetab[NDOSPART]; 88}; 89 90/* 91 * List of BIOS devices, translation from disk unit number to 92 * BIOS unit number. 93 */ 94static struct bdinfo 95{ 96 int bd_unit; /* BIOS unit number */ 97 int bd_flags; 98 int bd_type; /* BIOS 'drive type' (floppy only) */ |
99 int bd_da_unit; /* kernel unit number for da */ |
100} bdinfo [MAXBDDEV]; 101static int nbdinfo = 0; 102 103static int bd_getgeom(struct open_disk *od); 104static int bd_read(struct open_disk *od, daddr_t dblk, int blks, 105 caddr_t dest); 106static int bd_write(struct open_disk *od, daddr_t dblk, int blks, 107 caddr_t dest); --- 57 unchanged lines hidden (view full) --- 165} 166 167/* 168 * Quiz the BIOS for disk devices, save a little info about them. 169 */ 170static int 171bd_init(void) 172{ |
173 int base, unit; 174 int da_drive=0, n=-0x10; 175 176 /* sequence 0x90, 0x80, 0xa0 */ 177 for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 178 for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { 179 bdinfo[nbdinfo].bd_unit = unit; 180 bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; --- 19 unchanged lines hidden (view full) --- 200 bdinfo[nbdinfo].bd_da_unit = da_drive++; 201 } 202 /* XXX we need "disk aliases" to make this simpler */ 203 printf("BIOS drive %c: is disk%d\n", 204 'A' + nbdinfo, nbdinfo); 205 nbdinfo++; 206 } 207 } |
208 return(0); 209} 210 211/* 212 * Try to detect a device supported by the legacy int13 BIOS 213 */ 214static int 215bd_int13probe(struct bdinfo *bd) 216{ |
217 int addr; 218 219 if (bd->bd_flags & BD_FLOPPY) { 220 addr = 0xa155c; 221 } else { 222 if ((bd->bd_unit & 0xf0) == 0x80) 223 addr = 0xa155d; 224 else --- 7 unchanged lines hidden (view full) --- 232 int media = ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F; 233 234 if (media == 7) { /* MO */ 235 bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL; 236 return(1); 237 } 238 } 239 return(0); |
240} 241 242/* 243 * Print information about disks 244 */ 245static void 246bd_print(int verbose) 247{ 248 int i, j; 249 char line[80]; 250 struct i386_devdesc dev; 251 struct open_disk *od; 252 struct pc98_partition *dptr; 253 254 for (i = 0; i < nbdinfo; i++) { |
255 sprintf(line, " disk%d: BIOS drive %c:\n", i, 'A' + i); |
256 pager_output(line); 257 258 /* try to open the whole disk */ 259 dev.d_kind.biosdisk.unit = i; 260 dev.d_kind.biosdisk.slice = -1; 261 dev.d_kind.biosdisk.partition = -1; 262 263 if (!bd_opendisk(&od, &dev)) { 264 265 /* Do we have a partition table? */ 266 if (od->od_flags & BD_PARTTABOK) { 267 dptr = &od->od_slicetab[0]; 268 269 /* Check for a "dedicated" disk */ |
270 for (j = 0; j < od->od_nslices; j++) { 271 switch(dptr[j].dp_mid) { 272 case DOSMID_386BSD: 273 sprintf(line, " disk%ds%d", i, j + 1); 274 bd_printbsdslice(od, 275 dptr[j].dp_scyl * od->od_hds * od->od_sec + 276 dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect, 277 line, verbose); 278 break; 279 default: 280 break; 281 } 282 } |
283 } 284 bd_closedisk(od); 285 } 286 } 287} 288 |
289/* |
290 * Print out each valid partition in the disklabel of a FreeBSD slice. 291 * For size calculations, we assume a 512 byte sector size. 292 */ 293static void 294bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, 295 int verbose) 296{ 297 char line[80]; --- 120 unchanged lines hidden (view full) --- 418 * Following calculations attempt to determine the correct value 419 * for d->od_boff by looking for the slice and partition specified, 420 * or searching for reasonable defaults. 421 */ 422 423 /* 424 * Find the slice in the DOS slice table. 425 */ |
426 if (od->od_flags & BD_FLOPPY) { 427 sector = 0; 428 goto unsliced; 429 } |
430 if (bd_read(od, 0, 1, buf)) { 431 DEBUG("error reading MBR"); 432 error = EIO; 433 goto out; 434 } 435 436 /* 437 * Check the slice table magic. 438 */ 439 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 440 /* If a slice number was explicitly supplied, this is an error */ 441 if (dev->d_kind.biosdisk.slice > 0) { 442 DEBUG("no slice table/MBR (no magic)"); 443 error = ENOENT; 444 goto out; 445 } 446 sector = 0; 447 goto unsliced; /* may be a floppy */ 448 } |
449 if (bd_read(od, 1, 1, buf)) { 450 DEBUG("error reading MBR"); 451 error = EIO; 452 goto out; 453 } |
454 455 /* 456 * copy the partition table, then pick up any extended partitions. 457 */ 458 bcopy(buf + DOSPARTOFF, &od->od_slicetab, 459 sizeof(struct pc98_partition) * NDOSPART); |
460 od->od_nslices = NDOSPART; /* extended slices start here */ |
461 od->od_flags |= BD_PARTTABOK; 462 dptr = &od->od_slicetab[0]; 463 464 /* Is this a request for the whole disk? */ 465 if (dev->d_kind.biosdisk.slice == -1) { 466 sector = 0; 467 goto unsliced; 468 } --- 5 unchanged lines hidden (view full) --- 474 slice = dev->d_kind.biosdisk.slice - 1; 475 if (slice >= od->od_nslices) { 476 DEBUG("slice %d not found", slice); 477 error = ENOENT; 478 goto out; 479 } 480 } 481 |
482 /* Try to auto-detect the best slice; this should always give a slice number */ 483 if (dev->d_kind.biosdisk.slice == 0) { 484 slice = bd_bestslice(od); 485 if (slice == -1) { 486 error = ENOENT; 487 goto out; 488 } 489 dev->d_kind.biosdisk.slice = slice; 490 } 491 492 dptr = &od->od_slicetab[0]; 493 /* 494 * Accept the supplied slice number unequivocally (we may be looking 495 * at a DOS partition). 496 */ 497 dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ |
498 sector = dptr->dp_scyl * od->od_hds * od->od_sec + 499 dptr->dp_shd * od->od_sec + dptr->dp_ssect; 500 { 501 int end = dptr->dp_ecyl * od->od_hds * od->od_sec + 502 dptr->dp_ehd * od->od_sec + dptr->dp_esect; 503 DEBUG("slice entry %d at %d, %d sectors", 504 dev->d_kind.biosdisk.slice - 1, sector, end-sector); 505 } |
506 507 /* 508 * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 509 */ |
510 if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) |
511 dev->d_kind.biosdisk.partition = 0; 512 513 unsliced: 514 /* 515 * Now we have the slice offset, look for the partition in the disklabel if we have 516 * a partition to start with. 517 * 518 * XXX we might want to check the label checksum. --- 43 unchanged lines hidden (view full) --- 562 if (error) { 563 free(od); 564 } else { 565 *odp = od; /* return the open disk */ 566 } 567 return(error); 568} 569 |
570/* 571 * Search for a slice with the following preferences: 572 * 573 * 1: Active FreeBSD slice 574 * 2: Non-active FreeBSD slice 575 * 3: Active Linux slice 576 * 4: non-active Linux slice 577 * 5: Active FAT/FAT32 slice --- 18 unchanged lines hidden (view full) --- 596 int pref, preflevel; 597 int i, prefslice; 598 599 prefslice = 0; 600 preflevel = PREF_NONE; 601 602 dp = &od->od_slicetab[0]; 603 for (i = 0; i < od->od_nslices; i++, dp++) { |
604 switch(dp->dp_mid & 0x7f) { 605 case DOSMID_386BSD & 0x7f: /* FreeBSD */ 606 if ((dp->dp_mid & 0x80) && 607 (preflevel > PREF_FBSD_ACT)) { 608 pref = i; 609 preflevel = PREF_FBSD_ACT; 610 } else if (preflevel > PREF_FBSD) { 611 pref = i; --- 12 unchanged lines hidden (view full) --- 624 pref = i; 625 preflevel = PREF_DOS_ACT; 626 } else if (preflevel > PREF_DOS) { 627 pref = i; 628 preflevel = PREF_DOS; 629 } 630 break; 631 } |
632 } 633 return (prefslice); 634} 635 636static int 637bd_close(struct open_file *f) 638{ 639 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); --- 109 unchanged lines hidden (view full) --- 749 if (blks < 0) 750 return (-1); 751 752 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 753 resid = blks; 754 p = dest; 755 756 /* Decide whether we have to bounce */ |
757 if (((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { |
758 759 /* 760 * There is a 64k physical boundary somewhere in the destination buffer, so we have 761 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 762 * need to. Use the bottom half unless there is a break there, in which case we 763 * use the top half. 764 */ |
765 x = min(od->od_sec, (unsigned)blks); |
766 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 767 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 768 breg = bbuf; 769 } else { 770 breg = bbuf + x * BIOSDISK_SECSIZE; 771 } 772 maxfer = x; /* limit transfers to bounce region size */ 773 } else { --- 12 unchanged lines hidden (view full) --- 786 x = min(od->od_sec - sec, resid); 787 if (maxfer > 0) 788 x = min(x, maxfer); /* fit bounce buffer */ 789 790 /* where do we transfer to? */ 791 xp = bbuf == NULL ? p : breg; 792 793 /* correct sector number for 1-based BIOS numbering */ |
794 if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 795 sec++; |
796 797 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 798 for (retry = 0; retry < 3; retry++) { 799 /* if retrying, reset the drive */ 800 if (retry > 0) { |
801 v86.ctl = V86_FLAGS; 802 v86.addr = 0x1b; 803 v86.eax = 0x0300 | od->od_unit; |
804 v86int(); 805 } 806 |
807 v86.ctl = V86_FLAGS; 808 v86.addr = 0x1b; 809 if (od->od_flags & BD_FLOPPY) { 810 v86.eax = 0xd600 | od->od_unit; 811 v86.ecx = 0x0200 | (cyl & 0xff); 812 } 813 else { 814 v86.eax = 0x0600 | od->od_unit; --- 8 unchanged lines hidden (view full) --- 823 } 824 v86.ebx = x * BIOSDISK_SECSIZE; 825 v86.es = VTOPSEG(xp); 826 v86.ebp = VTOPOFF(xp); 827 v86int(); 828 result = (v86.efl & 0x1); 829 if (result == 0) 830 break; |
831 } 832 |
833 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), result ? "failed" : "ok"); 834 /* BUG here, cannot use v86 in printf because putchar uses it too */ 835 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 836 od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 837 od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 838 (v86.eax >> 8) & 0xff); |
839 if (result) { 840 if (bbuf != NULL) 841 free(bbuf); 842 return(-1); 843 } 844 if (bbuf != NULL) 845 bcopy(breg, p, x * BIOSDISK_SECSIZE); 846 p += (x * BIOSDISK_SECSIZE); --- 18 unchanged lines hidden (view full) --- 865 if (blks < 0) 866 return (-1); 867 868 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 869 resid = blks; 870 p = dest; 871 872 /* Decide whether we have to bounce */ |
873 if (((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { |
874 875 /* 876 * There is a 64k physical boundary somewhere in the destination buffer, so we have 877 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 878 * need to. Use the bottom half unless there is a break there, in which case we 879 * use the top half. 880 */ 881 |
882 x = min(od->od_sec, (unsigned)blks); |
883 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 884 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 885 breg = bbuf; 886 } else { 887 breg = bbuf + x * BIOSDISK_SECSIZE; 888 } 889 maxfer = x; /* limit transfers to bounce region size */ 890 } else { --- 12 unchanged lines hidden (view full) --- 903 x = min(od->od_sec - sec, resid); 904 if (maxfer > 0) 905 x = min(x, maxfer); /* fit bounce buffer */ 906 907 /* where do we transfer to? */ 908 xp = bbuf == NULL ? p : breg; 909 910 /* correct sector number for 1-based BIOS numbering */ |
911 if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 912 sec++; |
913 |
914 /* Put your Data In, Put your Data out, 915 Put your Data In, and shake it all about 916 */ 917 if (bbuf != NULL) 918 bcopy(p, breg, x * BIOSDISK_SECSIZE); 919 p += (x * BIOSDISK_SECSIZE); 920 dblk += x; 921 resid -= x; 922 923 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 924 for (retry = 0; retry < 3; retry++) { 925 /* if retrying, reset the drive */ 926 if (retry > 0) { |
927 v86.ctl = V86_FLAGS; 928 v86.addr = 0x1b; 929 v86.eax = 0x0300 | od->od_unit; |
930 v86int(); 931 } 932 |
933 v86.ctl = V86_FLAGS; 934 v86.addr = 0x1b; 935 if (od->od_flags & BD_FLOPPY) { 936 v86.eax = 0xd500 | od->od_unit; 937 v86.ecx = 0x0200 | (cyl & 0xff); 938 } else { 939 v86.eax = 0x0500 | od->od_unit; 940 v86.ecx = cyl; 941 } 942 v86.edx = (hd << 8) | sec; 943 v86.ebx = x * BIOSDISK_SECSIZE; 944 v86.es = VTOPSEG(xp); 945 v86.ebp = VTOPOFF(xp); 946 v86int(); 947 result = (v86.efl & 0x1); 948 if (result == 0) 949 break; |
950 } 951 |
952 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, 953 od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), 954 result ? "failed" : "ok"); 955 /* BUG here, cannot use v86 in printf because putchar uses it too */ 956 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 957 od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 958 od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 959 (v86.eax >> 8) & 0xff); |
960 |
961 if (result) { 962 if (bbuf != NULL) 963 free(bbuf); 964 return(-1); 965 } 966 } 967 968/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 969 if (bbuf != NULL) 970 free(bbuf); 971 return(0); 972} 973static int 974bd_getgeom(struct open_disk *od) 975{ 976 |
977 if (od->od_flags & BD_FLOPPY) { 978 od->od_cyl = 79; 979 od->od_hds = 2; 980 od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 981 } else if (od->od_flags & BD_OPTICAL) { 982 od->od_cyl = 0xFFFE; 983 od->od_hds = 8; 984 od->od_sec = 32; --- 4 unchanged lines hidden (view full) --- 989 v86int(); 990 991 od->od_cyl = v86.ecx; 992 od->od_hds = (v86.edx >> 8) & 0xff; 993 od->od_sec = v86.edx & 0xff; 994 if (v86.efl & 0x1) 995 return(1); 996 } |
997 |
998 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 999 return(0); 1000} 1001 1002/* 1003 * Return the BIOS geometry of a given "fixed drive" in a format 1004 * suitable for the legacy bootinfo structure. Since the kernel is 1005 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we --- 5 unchanged lines hidden (view full) --- 1011 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 1012 * indicated by returning the geometry of a "1.2M" PC-format floppy 1013 * disk. And, incidentally, what is returned is not the geometry as 1014 * such but the highest valid cylinder, head, and sector numbers. 1015 */ 1016u_int32_t 1017bd_getbigeom(int bunit) 1018{ |
1019 int hds = 0; 1020 int unit = 0x80; /* IDE HDD */ 1021 u_int addr = 0xA155d; 1022 1023 while (unit < 0xa7) { 1024 if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 1025 if (hds++ == bunit) 1026 break; --- 13 unchanged lines hidden (view full) --- 1040 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 1041 v86.ctl = V86_FLAGS; 1042 v86.addr = 0x1b; 1043 v86.eax = 0x8400 | unit; 1044 v86int(); 1045 if (v86.efl & 0x1) 1046 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 1047 return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); |
1048} 1049 1050/* 1051 * Return a suitable dev_t value for (dev). 1052 * 1053 * In the case where it looks like (dev) is a SCSI disk, we allow the number of 1054 * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 1055 */ --- 9 unchanged lines hidden (view full) --- 1065 1066 biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); 1067 DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); 1068 if (biosdev == -1) /* not a BIOS device */ 1069 return(-1); 1070 if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 1071 return(-1); 1072 |
1073 if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { |
1074 /* floppy (or emulated floppy) or ATAPI device */ 1075 if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { 1076 /* is an ATAPI disk */ 1077 major = WFDMAJOR; 1078 } else { 1079 /* is a floppy disk */ 1080 major = FDMAJOR; 1081 } --- 10 unchanged lines hidden (view full) --- 1092 unitofs = i; 1093 } 1094 } else { 1095 /* assume an IDE disk */ 1096 major = WDMAJOR; 1097 } 1098 } 1099 /* default root disk unit number */ |
1100 if ((biosdev & 0xf0) == 0xa0) 1101 unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit; 1102 else 1103 unit = biosdev & 0xf; |
1104 1105 /* XXX a better kludge to set the root disk unit number */ 1106 if ((nip = getenv("root_disk_unit")) != NULL) { 1107 i = strtol(nip, &cp, 0); 1108 /* check for parse error */ 1109 if ((cp != nip) && (*cp == 0)) 1110 unit = i; 1111 } 1112 1113 rootdev = MAKEBOOTDEV(major, 1114 (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ 1115 (dev->d_kind.biosdisk.slice + 1) & 0xf, 1116 unit, 1117 dev->d_kind.biosdisk.partition); 1118 DEBUG("dev is 0x%x\n", rootdev); 1119 return(rootdev); 1120} |