initial commit
[freebsd-arm:freebsd-arm.git] / boot / pc98 / libpc98 / biosdisk.c
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
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
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$");
29
30 /*
31  * BIOS disk device handling.
32  * 
33  * Ideas and algorithms from:
34  *
35  * - NetBSD libi386/biosdisk.c
36  * - FreeBSD biosboot/disk.c
37  *
38  */
39
40 #include <stand.h>
41
42 #include <sys/disklabel.h>
43 #include <sys/diskpc98.h>
44 #include <machine/bootinfo.h>
45
46 #include <stdarg.h>
47
48 #include <bootstrap.h>
49 #include <btxv86.h>
50 #include "libi386.h"
51
52 #define BIOS_NUMDRIVES          0x475
53 #define BIOSDISK_SECSIZE        512
54 #define BUFSIZE                 (1 * BIOSDISK_SECSIZE)
55 #define MAXBDDEV                MAXDEV
56
57 #define DT_ATAPI                0x10            /* disk type for ATAPI floppies */
58 #define WDMAJOR                 0               /* major numbers for devices we frontend for */
59 #define WFDMAJOR                1
60 #define FDMAJOR                 2
61 #define DAMAJOR                 4
62
63 #ifdef DISK_DEBUG
64 # define DEBUG(fmt, args...)    printf("%s: " fmt "\n" , __func__ , ## args)
65 #else
66 # define DEBUG(fmt, args...)
67 #endif
68
69 struct open_disk {
70     int                 od_dkunit;              /* disk unit number */
71     int                 od_unit;                /* BIOS unit number */
72     int                 od_cyl;                 /* BIOS geometry */
73     int                 od_hds;
74     int                 od_sec;
75     int                 od_boff;                /* block offset from beginning of BIOS disk */
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  */
94 static 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];
101 static int nbdinfo = 0;
102
103 static int      bd_getgeom(struct open_disk *od);
104 static int      bd_read(struct open_disk *od, daddr_t dblk, int blks,
105                     caddr_t dest);
106 static int      bd_write(struct open_disk *od, daddr_t dblk, int blks,
107                     caddr_t dest);
108
109 static int      bd_int13probe(struct bdinfo *bd);
110
111 static void     bd_printslice(struct open_disk *od, struct pc98_partition *dp,
112                     char *prefix, int verbose);
113 static void     bd_printbsdslice(struct open_disk *od, daddr_t offset,
114                     char *prefix, int verbose);
115
116 static int      bd_init(void);
117 static int      bd_strategy(void *devdata, int flag, daddr_t dblk,
118                     size_t size, char *buf, size_t *rsize);
119 static int      bd_realstrategy(void *devdata, int flag, daddr_t dblk,
120                     size_t size, char *buf, size_t *rsize);
121 static int      bd_open(struct open_file *f, ...);
122 static int      bd_close(struct open_file *f);
123 static void     bd_print(int verbose);
124
125 struct devsw biosdisk = {
126     "disk", 
127     DEVT_DISK, 
128     bd_init,
129     bd_strategy, 
130     bd_open, 
131     bd_close, 
132     noioctl,
133     bd_print,
134     NULL
135 };
136
137 static int      bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
138 static void     bd_closedisk(struct open_disk *od);
139 static int      bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev);
140 static int      bd_bestslice(struct open_disk *od);
141 static void     bd_checkextended(struct open_disk *od, int slicenum);
142
143 /*
144  * Translate between BIOS device numbers and our private unit numbers.
145  */
146 int
147 bd_bios2unit(int biosdev)
148 {
149     int         i;
150     
151     DEBUG("looking for bios device 0x%x", biosdev);
152     for (i = 0; i < nbdinfo; i++) {
153         DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
154         if (bdinfo[i].bd_unit == biosdev)
155             return(i);
156     }
157     return(-1);
158 }
159
160 int
161 bd_unit2bios(int unit)
162 {
163     if ((unit >= 0) && (unit < nbdinfo))
164         return(bdinfo[unit].bd_unit);
165     return(-1);
166 }
167
168 /*    
169  * Quiz the BIOS for disk devices, save a little info about them.
170  */
171 static int
172 bd_init(void) 
173 {
174     int         base, unit;
175     int         da_drive=0, n=-0x10;
176
177     /* sequence 0x90, 0x80, 0xa0 */
178     for (base = 0x90; base <= 0xa0; base += n, n += 0x30) {
179         for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) {
180             bdinfo[nbdinfo].bd_unit = unit;
181             bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0;
182
183             if (!bd_int13probe(&bdinfo[nbdinfo])){
184                 if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) ||
185                     ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6))
186                     continue;   /* Target IDs are not contiguous. */
187                 else
188                     break;
189             }
190
191             if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){
192                 /* available 1.44MB access? */
193                 if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) {
194                     /* boot media 1.2MB FD? */
195                     if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90)
196                         bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf);
197                 }
198             }
199             else {
200                 if ((unit & 0xF0) == 0xA0)      /* SCSI HD or MO */
201                     bdinfo[nbdinfo].bd_da_unit = da_drive++;
202             }
203             /* XXX we need "disk aliases" to make this simpler */
204             printf("BIOS drive %c: is disk%d\n", 
205                    'A' + nbdinfo, nbdinfo);
206             nbdinfo++;
207         }
208     }
209     return(0);
210 }
211
212 /*
213  * Try to detect a device supported by the legacy int13 BIOS
214  */
215 static int
216 bd_int13probe(struct bdinfo *bd)
217 {
218     int addr;
219
220     if (bd->bd_flags & BD_FLOPPY) {
221         addr = 0xa155c;
222     } else {
223         if ((bd->bd_unit & 0xf0) == 0x80)
224             addr = 0xa155d;
225         else
226             addr = 0xa1482;
227     }
228     if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) {
229         bd->bd_flags |= BD_MODEINT13;
230         return(1);
231     }
232     if ((bd->bd_unit & 0xF0) == 0xA0) {
233         int media = ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F;
234
235         if (media == 7) { /* MO */
236             bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL;
237             return(1);
238         }
239     }
240     return(0);
241 }
242
243 /*
244  * Print information about disks
245  */
246 static void
247 bd_print(int verbose)
248 {
249     int                         i, j;
250     char                        line[80];
251     struct i386_devdesc         dev;
252     struct open_disk            *od;
253     struct pc98_partition       *dptr;
254     
255     for (i = 0; i < nbdinfo; i++) {
256         sprintf(line, "    disk%d:   BIOS drive %c:\n", i, 'A' + i);
257         pager_output(line);
258
259         /* try to open the whole disk */
260         dev.d_unit = i;
261         dev.d_kind.biosdisk.slice = -1;
262         dev.d_kind.biosdisk.partition = -1;
263         
264         if (!bd_opendisk(&od, &dev)) {
265
266             /* Do we have a partition table? */
267             if (od->od_flags & BD_PARTTABOK) {
268                 dptr = &od->od_slicetab[0];
269
270                 /* Check for a "dedicated" disk */
271                 for (j = 0; j < od->od_nslices; j++) {
272                     sprintf(line, "      disk%ds%d", i, j + 1);
273                     bd_printslice(od, &dptr[j], line, verbose);
274                 }
275             }
276             bd_closedisk(od);
277         }
278     }
279 }
280
281 /* Given a size in 512 byte sectors, convert it to a human-readable number. */
282 static char *
283 display_size(uint64_t size)
284 {
285     static char buf[80];
286     char unit;
287
288     size /= 2;
289     unit = 'K';
290     if (size >= 10485760000LL) {
291         size /= 1073741824;
292         unit = 'T';
293     } else if (size >= 10240000) {
294         size /= 1048576;
295         unit = 'G';
296     } else if (size >= 10000) {
297         size /= 1024;
298         unit = 'M';
299     }
300     sprintf(buf, "%.6ld%cB", (long)size, unit);
301     return (buf);
302 }
303
304 /*
305  * Print information about slices on a disk.  For the size calculations we
306  * assume a 512 byte sector.
307  */
308 static void
309 bd_printslice(struct open_disk *od, struct pc98_partition *dp, char *prefix,
310         int verbose)
311 {
312         int cylsecs, start, size;
313         char stats[80];
314         char line[80];
315
316         cylsecs = od->od_hds * od->od_sec;
317         start = dp->dp_scyl * cylsecs + dp->dp_shd * od->od_sec + dp->dp_ssect;
318         size = (dp->dp_ecyl - dp->dp_scyl + 1) * cylsecs;
319
320         if (verbose)
321                 sprintf(stats, " %s (%d - %d)", display_size(size),
322                     start, start + size);
323         else
324                 stats[0] = '\0';
325
326         switch(dp->dp_mid & PC98_MID_MASK) {
327         case PC98_MID_386BSD:
328                 bd_printbsdslice(od, start, prefix, verbose);
329                 return;
330         case 0x00:                              /* unused partition */
331                 return;
332         case 0x01:
333                 sprintf(line, "%s: FAT-12%s\n", prefix, stats);
334                 break;
335         case 0x11:
336         case 0x20:
337         case 0x21:
338         case 0x22:
339         case 0x23:
340         case 0x24:
341                 sprintf(line, "%s: FAT-16%s\n", prefix, stats);
342                 break;
343         default:
344                 sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_mid,
345                     stats);
346         }
347         pager_output(line);
348 }
349
350 /*
351  * Print out each valid partition in the disklabel of a FreeBSD slice.
352  * For size calculations, we assume a 512 byte sector size.
353  */
354 static void
355 bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix,
356     int verbose)
357 {
358     char                line[80];
359     char                buf[BIOSDISK_SECSIZE];
360     struct disklabel    *lp;
361     int                 i;
362
363     /* read disklabel */
364     if (bd_read(od, offset + LABELSECTOR, 1, buf))
365         return;
366     lp =(struct disklabel *)(&buf[0]);
367     if (lp->d_magic != DISKMAGIC) {
368         sprintf(line, "%s: FFS  bad disklabel\n", prefix);
369         pager_output(line);
370         return;
371     }
372     
373     /* Print partitions */
374     for (i = 0; i < lp->d_npartitions; i++) {
375         /*
376          * For each partition, make sure we know what type of fs it is.  If
377          * not, then skip it.  However, since floppies often have bogus
378          * fstypes, print the 'a' partition on a floppy even if it is marked
379          * unused.
380          */
381         if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) ||
382             (lp->d_partitions[i].p_fstype == FS_SWAP) ||
383             (lp->d_partitions[i].p_fstype == FS_VINUM) ||
384             ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 
385              (od->od_flags & BD_FLOPPY) && (i == 0))) {
386
387             /* Only print out statistics in verbose mode */
388             if (verbose)
389                 sprintf(line, "  %s%c: %s %s (%d - %d)\n", prefix, 'a' + i,
390                     (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap " : 
391                     (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
392                     "FFS  ",
393                     display_size(lp->d_partitions[i].p_size),
394                     lp->d_partitions[i].p_offset,
395                     lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size);
396             else
397                 sprintf(line, "  %s%c: %s\n", prefix, 'a' + i,
398                     (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 
399                     (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
400                     "FFS");
401             pager_output(line);
402         }
403     }
404 }
405
406
407 /*
408  * Attempt to open the disk described by (dev) for use by (f).
409  *
410  * Note that the philosophy here is "give them exactly what
411  * they ask for".  This is necessary because being too "smart"
412  * about what the user might want leads to complications.
413  * (eg. given no slice or partition value, with a disk that is
414  *  sliced - are they after the first BSD slice, or the DOS
415  *  slice before it?)
416  */
417 static int 
418 bd_open(struct open_file *f, ...)
419 {
420     va_list                     ap;
421     struct i386_devdesc         *dev;
422     struct open_disk            *od;
423     int                         error;
424
425     va_start(ap, f);
426     dev = va_arg(ap, struct i386_devdesc *);
427     va_end(ap);
428     if ((error = bd_opendisk(&od, dev)))
429         return(error);
430     
431     /*
432      * Save our context
433      */
434     ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
435     DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
436     return(0);
437 }
438
439 static int
440 bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
441 {
442     struct open_disk            *od;
443     int                         error;
444
445     if (dev->d_unit >= nbdinfo) {
446         DEBUG("attempt to open nonexistent disk");
447         return(ENXIO);
448     }
449     
450     od = (struct open_disk *)malloc(sizeof(struct open_disk));
451     if (!od) {
452         DEBUG("no memory");
453         return (ENOMEM);
454     }
455
456     /* Look up BIOS unit number, intialise open_disk structure */
457     od->od_dkunit = dev->d_unit;
458     od->od_unit = bdinfo[od->od_dkunit].bd_unit;
459     od->od_flags = bdinfo[od->od_dkunit].bd_flags;
460     od->od_boff = 0;
461     error = 0;
462     DEBUG("open '%s', unit 0x%x slice %d partition %d",
463              i386_fmtdev(dev), dev->d_unit, 
464              dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition);
465
466     /* Get geometry for this open (removable device may have changed) */
467     if (bd_getgeom(od)) {
468         DEBUG("can't get geometry");
469         error = ENXIO;
470         goto out;
471     }
472
473     /* Determine disk layout. */
474     error = bd_open_pc98(od, dev);
475     
476  out:
477     if (error) {
478         free(od);
479     } else {
480         *odp = od;      /* return the open disk */
481     }
482     return(error);
483 }
484
485 static int
486 bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev)
487 {
488     struct pc98_partition       *dptr;
489     struct disklabel            *lp;
490     int                         sector, slice, i;
491     char                        buf[BUFSIZE];
492
493     /*
494      * Following calculations attempt to determine the correct value
495      * for d->od_boff by looking for the slice and partition specified,
496      * or searching for reasonable defaults.
497      */
498
499     /*
500      * Find the slice in the DOS slice table.
501      */
502     od->od_nslices = 0;
503     if (od->od_flags & BD_FLOPPY) {
504         sector = 0;
505         goto unsliced;
506     }
507     if (bd_read(od, 0, 1, buf)) {
508         DEBUG("error reading MBR");
509         return (EIO);
510     }
511
512     /* 
513      * Check the slice table magic.
514      */
515     if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
516         /* If a slice number was explicitly supplied, this is an error */
517         if (dev->d_kind.biosdisk.slice > 0) {
518             DEBUG("no slice table/MBR (no magic)");
519             return (ENOENT);
520         }
521         sector = 0;
522         goto unsliced;          /* may be a floppy */
523     }
524     if (bd_read(od, 1, 1, buf)) {
525         DEBUG("error reading MBR");
526         return (EIO);
527     }
528
529     /*
530      * copy the partition table, then pick up any extended partitions.
531      */
532     bcopy(buf + DOSPARTOFF, &od->od_slicetab,
533       sizeof(struct pc98_partition) * NDOSPART);
534     od->od_nslices = NDOSPART;          /* extended slices start here */
535     od->od_flags |= BD_PARTTABOK;
536     dptr = &od->od_slicetab[0];
537
538     /* Is this a request for the whole disk? */
539     if (dev->d_kind.biosdisk.slice == -1) {
540         sector = 0;
541         goto unsliced;
542     }
543
544     /*
545      * if a slice number was supplied but not found, this is an error.
546      */
547     if (dev->d_kind.biosdisk.slice > 0) {
548         slice = dev->d_kind.biosdisk.slice - 1;
549         if (slice >= od->od_nslices) {
550             DEBUG("slice %d not found", slice);
551             return (ENOENT);
552         }
553     }
554
555     /* Try to auto-detect the best slice; this should always give a slice number */
556     if (dev->d_kind.biosdisk.slice == 0) {
557         slice = bd_bestslice(od);
558         if (slice == -1) {
559             return (ENOENT);
560         }
561         dev->d_kind.biosdisk.slice = slice;
562     }
563
564     dptr = &od->od_slicetab[0];
565     /*
566      * Accept the supplied slice number unequivocally (we may be looking
567      * at a DOS partition).
568      */
569     dptr += (dev->d_kind.biosdisk.slice - 1);   /* we number 1-4, offsets are 0-3 */
570     sector = dptr->dp_scyl * od->od_hds * od->od_sec +
571         dptr->dp_shd * od->od_sec + dptr->dp_ssect;
572     {
573         int end = dptr->dp_ecyl * od->od_hds * od->od_sec +
574             dptr->dp_ehd * od->od_sec + dptr->dp_esect;
575         DEBUG("slice entry %d at %d, %d sectors",
576               dev->d_kind.biosdisk.slice - 1, sector, end-sector);
577     }
578
579     /*
580      * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
581      */
582     if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0))
583         dev->d_kind.biosdisk.partition = 0;
584
585  unsliced:
586     /* 
587      * Now we have the slice offset, look for the partition in the disklabel if we have
588      * a partition to start with.
589      *
590      * XXX we might want to check the label checksum.
591      */
592     if (dev->d_kind.biosdisk.partition < 0) {
593         od->od_boff = sector;           /* no partition, must be after the slice */
594         DEBUG("opening raw slice");
595     } else {
596         
597         if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
598             DEBUG("error reading disklabel");
599             return (EIO);
600         }
601         DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
602         bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
603         lp = &od->od_disklabel;
604         od->od_flags |= BD_LABELOK;
605
606         if (lp->d_magic != DISKMAGIC) {
607             DEBUG("no disklabel");
608             return (ENOENT);
609         }
610         if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
611             DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
612                   'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
613             return (EPART);
614         }
615
616 #ifdef DISK_DEBUG
617         /* Complain if the partition is unused unless this is a floppy. */
618         if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
619             !(od->od_flags & BD_FLOPPY))
620             DEBUG("warning, partition marked as unused");
621 #endif
622         
623         od->od_boff = 
624                 lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
625                 lp->d_partitions[RAW_PART].p_offset +
626                 sector;
627     }
628     return (0);
629 }
630
631 /*
632  * Search for a slice with the following preferences:
633  *
634  * 1: Active FreeBSD slice
635  * 2: Non-active FreeBSD slice
636  * 3: Active Linux slice
637  * 4: non-active Linux slice
638  * 5: Active FAT/FAT32 slice
639  * 6: non-active FAT/FAT32 slice
640  */
641 #define PREF_RAWDISK    0
642 #define PREF_FBSD_ACT   1
643 #define PREF_FBSD       2
644 #define PREF_LINUX_ACT  3
645 #define PREF_LINUX      4
646 #define PREF_DOS_ACT    5
647 #define PREF_DOS        6
648 #define PREF_NONE       7
649
650 /*
651  * slicelimit is in the range 0 .. NDOSPART
652  */
653 static int
654 bd_bestslice(struct open_disk *od)
655 {
656         struct pc98_partition *dp;
657         int pref, preflevel;
658         int i, prefslice;
659         
660         prefslice = 0;
661         preflevel = PREF_NONE;
662
663         dp = &od->od_slicetab[0];
664         for (i = 0; i < od->od_nslices; i++, dp++) {
665                 switch(dp->dp_mid & PC98_MID_MASK) {
666                 case PC98_MID_386BSD:           /* FreeBSD */
667                         if ((dp->dp_mid & PC98_MID_BOOTABLE) &&
668                             (preflevel > PREF_FBSD_ACT)) {
669                                 pref = i;
670                                 preflevel = PREF_FBSD_ACT;
671                         } else if (preflevel > PREF_FBSD) {
672                                 pref = i;
673                                 preflevel = PREF_FBSD;
674                         }
675                         break;
676
677                 case 0x11:                              /* DOS/Windows */
678                 case 0x20:
679                 case 0x21:
680                 case 0x22:
681                 case 0x23:
682                 case 0x63:
683                         if ((dp->dp_mid & PC98_MID_BOOTABLE) &&
684                             (preflevel > PREF_DOS_ACT)) {
685                                 pref = i;
686                                 preflevel = PREF_DOS_ACT;
687                         } else if (preflevel > PREF_DOS) {
688                                 pref = i;
689                                 preflevel = PREF_DOS;
690                         }
691                         break;
692                 }
693         }
694         return (prefslice);
695 }
696  
697 static int 
698 bd_close(struct open_file *f)
699 {
700     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
701
702     bd_closedisk(od);
703     return(0);
704 }
705
706 static void
707 bd_closedisk(struct open_disk *od)
708 {
709     DEBUG("open_disk %p", od);
710 #if 0
711     /* XXX is this required? (especially if disk already open...) */
712     if (od->od_flags & BD_FLOPPY)
713         delay(3000000);
714 #endif
715     free(od);
716 }
717
718 static int 
719 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
720 {
721     struct bcache_devdata       bcd;
722     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
723
724     bcd.dv_strategy = bd_realstrategy;
725     bcd.dv_devdata = devdata;
726     return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize));
727 }
728
729 static int 
730 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
731 {
732     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
733     int                 blks;
734 #ifdef BD_SUPPORT_FRAGS
735     char                fragbuf[BIOSDISK_SECSIZE];
736     size_t              fragsize;
737
738     fragsize = size % BIOSDISK_SECSIZE;
739 #else
740     if (size % BIOSDISK_SECSIZE)
741         panic("bd_strategy: %d bytes I/O not multiple of block size", size);
742 #endif
743
744     DEBUG("open_disk %p", od);
745     blks = size / BIOSDISK_SECSIZE;
746     if (rsize)
747         *rsize = 0;
748
749     switch(rw){
750     case F_READ:
751         DEBUG("read %d from %d to %p", blks, dblk, buf);
752
753         if (blks && bd_read(od, dblk, blks, buf)) {
754             DEBUG("read error");
755             return (EIO);
756         }
757 #ifdef BD_SUPPORT_FRAGS
758         DEBUG("bd_strategy: frag read %d from %d+%d to %p",
759             fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
760         if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
761             DEBUG("frag read error");
762             return(EIO);
763         }
764         bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
765 #endif
766         break;
767     case F_WRITE :
768         DEBUG("write %d from %d to %p", blks, dblk, buf);
769
770         if (blks && bd_write(od, dblk, blks, buf)) {
771             DEBUG("write error");
772             return (EIO);
773         }
774 #ifdef BD_SUPPORT_FRAGS
775         if(fragsize) {
776             DEBUG("Attempted to write a frag");
777             return (EIO);
778         }
779 #endif
780         break;
781     default:
782         /* DO NOTHING */
783         return (EROFS);
784     }
785
786     if (rsize)
787         *rsize = size;
788     return (0);
789 }
790
791 /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
792 #define FLOPPY_BOUNCEBUF        18
793
794 static int
795 bd_chs_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
796 {
797     u_int       x, bpc, cyl, hd, sec;
798
799     bpc = (od->od_sec * od->od_hds);    /* blocks per cylinder */
800     x = dblk;
801     cyl = x / bpc;                      /* block # / blocks per cylinder */
802     x %= bpc;                           /* block offset into cylinder */
803     hd = x / od->od_sec;                /* offset / blocks per track */
804     sec = x % od->od_sec;               /* offset into track */
805
806     v86.ctl = V86_FLAGS;
807     v86.addr = 0x1b;
808     if (write)
809         v86.eax = 0x0500 | od->od_unit;
810     else
811         v86.eax = 0x0600 | od->od_unit;
812     if (od->od_flags & BD_FLOPPY) {
813         v86.eax |= 0xd000;
814         v86.ecx = 0x0200 | (cyl & 0xff);
815         v86.edx = (hd << 8) | (sec + 1);
816     } else if (od->od_flags & BD_OPTICAL) {
817         v86.eax &= 0xFF7F;
818         v86.ecx = dblk & 0xFFFF;
819         v86.edx = dblk >> 16;
820     } else {
821         v86.ecx = cyl;
822         v86.edx = (hd << 8) | sec;
823     }
824     v86.ebx = blks * BIOSDISK_SECSIZE;
825     v86.es = VTOPSEG(dest);
826     v86.ebp = VTOPOFF(dest);
827     v86int();
828     return (v86.efl & 0x1);
829 }
830
831 static int
832 bd_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
833 {
834     u_int       x, sec, result, resid, retry, maxfer;
835     caddr_t     p, xp, bbuf, breg;
836     
837     /* Just in case some idiot actually tries to read/write -1 blocks... */
838     if (blks < 0)
839         return (-1);
840
841     resid = blks;
842     p = dest;
843
844     /* Decide whether we have to bounce */
845     if (VTOP(dest) >> 20 != 0 ||
846         ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
847
848         /* 
849          * There is a 64k physical boundary somewhere in the
850          * destination buffer, or the destination buffer is above
851          * first 1MB of physical memory so we have to arrange a
852          * suitable bounce buffer.  Allocate a buffer twice as large
853          * as we need to.  Use the bottom half unless there is a break
854          * there, in which case we use the top half.
855          */
856         x = min(od->od_sec, (unsigned)blks);
857         bbuf = alloca(x * 2 * BIOSDISK_SECSIZE);
858         if (((u_int32_t)VTOP(bbuf) & 0xffff0000) ==
859             ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
860             breg = bbuf;
861         } else {
862             breg = bbuf + x * BIOSDISK_SECSIZE;
863         }
864         maxfer = x;             /* limit transfers to bounce region size */
865     } else {
866         breg = bbuf = NULL;
867         maxfer = 0;
868     }
869     
870     while (resid > 0) {
871         /*
872          * Play it safe and don't cross track boundaries.
873          * (XXX this is probably unnecessary)
874          */
875         sec = dblk % od->od_sec;        /* offset into track */
876         x = min(od->od_sec - sec, resid);
877         if (maxfer > 0)
878             x = min(x, maxfer);         /* fit bounce buffer */
879
880         /* where do we transfer to? */
881         xp = bbuf == NULL ? p : breg;
882
883         /*
884          * Put your Data In, Put your Data out,
885          * Put your Data In, and shake it all about 
886          */
887         if (write && bbuf != NULL)
888             bcopy(p, breg, x * BIOSDISK_SECSIZE);
889
890         /*
891          * Loop retrying the operation a couple of times.  The BIOS
892          * may also retry.
893          */
894         for (retry = 0; retry < 3; retry++) {
895             /* if retrying, reset the drive */
896             if (retry > 0) {
897                 v86.ctl = V86_FLAGS;
898                 v86.addr = 0x1b;
899                 v86.eax = 0x0300 | od->od_unit;
900                 v86int();
901             }
902
903             result = bd_chs_io(od, dblk, x, xp, write);
904             if (result == 0)
905                 break;
906         }
907
908         if (write)
909             DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
910                 p, VTOP(p), dblk, result ? "failed" : "ok");
911         else
912             DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
913                 dblk, p, VTOP(p), result ? "failed" : "ok");
914         if (result) {
915             return(-1);
916         }
917         if (!write && bbuf != NULL)
918             bcopy(breg, p, x * BIOSDISK_SECSIZE);
919         p += (x * BIOSDISK_SECSIZE);
920         dblk += x;
921         resid -= x;
922     }
923
924 /*    hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
925     return(0);
926 }
927
928 static int
929 bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
930 {
931
932     return (bd_io(od, dblk, blks, dest, 0));
933 }
934
935 static int
936 bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
937 {
938
939     return (bd_io(od, dblk, blks, dest, 1));
940 }
941
942 static int
943 bd_getgeom(struct open_disk *od)
944 {
945
946     if (od->od_flags & BD_FLOPPY) {
947         od->od_cyl = 79;
948         od->od_hds = 2;
949         od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
950     } else if (od->od_flags & BD_OPTICAL) {
951         od->od_cyl = 0xFFFE;
952         od->od_hds = 8;
953         od->od_sec = 32;
954     } else {
955         v86.ctl = V86_FLAGS;
956         v86.addr = 0x1b;
957         v86.eax = 0x8400 | od->od_unit;
958         v86int();
959       
960         od->od_cyl = v86.ecx;
961         od->od_hds = (v86.edx >> 8) & 0xff;
962         od->od_sec = v86.edx & 0xff;
963         if (v86.efl & 0x1)
964             return(1);
965     }
966
967     DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
968     return(0);
969 }
970
971 /*
972  * Return the BIOS geometry of a given "fixed drive" in a format
973  * suitable for the legacy bootinfo structure.  Since the kernel is
974  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
975  * prefer to get the information directly, rather than rely on being
976  * able to put it together from information already maintained for
977  * different purposes and for a probably different number of drives.
978  *
979  * For valid drives, the geometry is expected in the format (31..0)
980  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
981  * indicated by returning the geometry of a "1.2M" PC-format floppy
982  * disk.  And, incidentally, what is returned is not the geometry as
983  * such but the highest valid cylinder, head, and sector numbers.
984  */
985 u_int32_t
986 bd_getbigeom(int bunit)
987 {
988     int hds = 0;
989     int unit = 0x80;            /* IDE HDD */
990     u_int addr = 0xA155d;
991
992     while (unit < 0xa7) {
993         if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
994             if (hds++ == bunit)
995                 break;
996
997         if (unit >= 0xA0) {
998             int  media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F;
999
1000             if (media == 7 && hds++ == bunit)   /* SCSI MO */
1001                 return(0xFFFE0820); /* C:65535 H:8 S:32 */
1002         }
1003         if (++unit == 0x84) {
1004             unit = 0xA0;        /* SCSI HDD */
1005             addr = 0xA1482;
1006         }
1007     }
1008     if (unit == 0xa7)
1009         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
1010     v86.ctl = V86_FLAGS;
1011     v86.addr = 0x1b;
1012     v86.eax = 0x8400 | unit;
1013     v86int();
1014     if (v86.efl & 0x1)
1015         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
1016     return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
1017 }
1018
1019 /*
1020  * Return a suitable dev_t value for (dev).
1021  *
1022  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
1023  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
1024  */
1025 int
1026 bd_getdev(struct i386_devdesc *dev)
1027 {
1028     struct open_disk            *od;
1029     int                         biosdev;
1030     int                         major;
1031     int                         rootdev;
1032     char                        *nip, *cp;
1033     int                         unitofs = 0, i, unit;
1034
1035     biosdev = bd_unit2bios(dev->d_unit);
1036     DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
1037     if (biosdev == -1)                          /* not a BIOS device */
1038         return(-1);
1039     if (bd_opendisk(&od, dev) != 0)             /* oops, not a viable device */
1040         return(-1);
1041
1042     if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
1043         /* floppy (or emulated floppy) or ATAPI device */
1044         if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
1045             /* is an ATAPI disk */
1046             major = WFDMAJOR;
1047         } else {
1048             /* is a floppy disk */
1049             major = FDMAJOR;
1050         }
1051     } else {
1052         /* harddisk */
1053         if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
1054             /* label OK, disk labelled as SCSI */
1055             major = DAMAJOR;
1056             /* check for unit number correction hint, now deprecated */
1057             if ((nip = getenv("num_ide_disks")) != NULL) {
1058                 i = strtol(nip, &cp, 0);
1059                 /* check for parse error */
1060                 if ((cp != nip) && (*cp == 0))
1061                     unitofs = i;
1062             }
1063         } else {
1064             /* assume an IDE disk */
1065             major = WDMAJOR;
1066         }
1067     }
1068     /* default root disk unit number */
1069     if ((biosdev & 0xf0) == 0xa0)
1070         unit = bdinfo[dev->d_unit].bd_da_unit;
1071     else
1072         unit = biosdev & 0xf;
1073
1074     /* XXX a better kludge to set the root disk unit number */
1075     if ((nip = getenv("root_disk_unit")) != NULL) {
1076         i = strtol(nip, &cp, 0);
1077         /* check for parse error */
1078         if ((cp != nip) && (*cp == 0))
1079             unit = i;
1080     }
1081
1082     rootdev = MAKEBOOTDEV(major, dev->d_kind.biosdisk.slice + 1, unit,
1083         dev->d_kind.biosdisk.partition);
1084     DEBUG("dev is 0x%x\n", rootdev);
1085     return(rootdev);
1086 }