]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/boot/pc98/libpc98/biosdisk.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / 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                     switch(dptr[j].dp_mid) {
273                     case DOSMID_386BSD:
274                         sprintf(line, "      disk%ds%d", i, j + 1);
275                         bd_printbsdslice(od,
276                             dptr[j].dp_scyl * od->od_hds * od->od_sec +
277                             dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect,
278                             line, verbose);
279                         break;
280                     default:
281                         break;
282                     }
283                 }
284             }
285             bd_closedisk(od);
286         }
287     }
288 }
289
290 /*
291  * Print out each valid partition in the disklabel of a FreeBSD slice.
292  * For size calculations, we assume a 512 byte sector size.
293  */
294 static void
295 bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix,
296     int verbose)
297 {
298     char                line[80];
299     char                buf[BIOSDISK_SECSIZE];
300     struct disklabel    *lp;
301     int                 i;
302
303     /* read disklabel */
304     if (bd_read(od, offset + LABELSECTOR, 1, buf))
305         return;
306     lp =(struct disklabel *)(&buf[0]);
307     if (lp->d_magic != DISKMAGIC) {
308         sprintf(line, "%s: FFS  bad disklabel\n", prefix);
309         pager_output(line);
310         return;
311     }
312     
313     /* Print partitions */
314     for (i = 0; i < lp->d_npartitions; i++) {
315         /*
316          * For each partition, make sure we know what type of fs it is.  If
317          * not, then skip it.  However, since floppies often have bogus
318          * fstypes, print the 'a' partition on a floppy even if it is marked
319          * unused.
320          */
321         if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) ||
322             (lp->d_partitions[i].p_fstype == FS_SWAP) ||
323             (lp->d_partitions[i].p_fstype == FS_VINUM) ||
324             ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 
325              (od->od_flags & BD_FLOPPY) && (i == 0))) {
326
327             /* Only print out statistics in verbose mode */
328             if (verbose)
329                 sprintf(line, "  %s%c: %s  %.6dMB (%d - %d)\n", prefix, 'a' + i,
330                     (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 
331                     (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
332                     "FFS",
333                     lp->d_partitions[i].p_size / 2048,
334                     lp->d_partitions[i].p_offset,
335                     lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size);
336             else
337                 sprintf(line, "  %s%c: %s\n", prefix, 'a' + i,
338                     (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 
339                     (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
340                     "FFS");
341             pager_output(line);
342         }
343     }
344 }
345
346
347 /*
348  * Attempt to open the disk described by (dev) for use by (f).
349  *
350  * Note that the philosophy here is "give them exactly what
351  * they ask for".  This is necessary because being too "smart"
352  * about what the user might want leads to complications.
353  * (eg. given no slice or partition value, with a disk that is
354  *  sliced - are they after the first BSD slice, or the DOS
355  *  slice before it?)
356  */
357 static int 
358 bd_open(struct open_file *f, ...)
359 {
360     va_list                     ap;
361     struct i386_devdesc         *dev;
362     struct open_disk            *od;
363     int                         error;
364
365     va_start(ap, f);
366     dev = va_arg(ap, struct i386_devdesc *);
367     va_end(ap);
368     if ((error = bd_opendisk(&od, dev)))
369         return(error);
370     
371     /*
372      * Save our context
373      */
374     ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
375     DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
376     return(0);
377 }
378
379 static int
380 bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
381 {
382     struct open_disk            *od;
383     int                         error;
384
385     if (dev->d_unit >= nbdinfo) {
386         DEBUG("attempt to open nonexistent disk");
387         return(ENXIO);
388     }
389     
390     od = (struct open_disk *)malloc(sizeof(struct open_disk));
391     if (!od) {
392         DEBUG("no memory");
393         return (ENOMEM);
394     }
395
396     /* Look up BIOS unit number, intialise open_disk structure */
397     od->od_dkunit = dev->d_unit;
398     od->od_unit = bdinfo[od->od_dkunit].bd_unit;
399     od->od_flags = bdinfo[od->od_dkunit].bd_flags;
400     od->od_boff = 0;
401     error = 0;
402     DEBUG("open '%s', unit 0x%x slice %d partition %d",
403              i386_fmtdev(dev), dev->d_unit, 
404              dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition);
405
406     /* Get geometry for this open (removable device may have changed) */
407     if (bd_getgeom(od)) {
408         DEBUG("can't get geometry");
409         error = ENXIO;
410         goto out;
411     }
412
413     /* Determine disk layout. */
414     error = bd_open_pc98(od, dev);
415     
416  out:
417     if (error) {
418         free(od);
419     } else {
420         *odp = od;      /* return the open disk */
421     }
422     return(error);
423 }
424
425 static int
426 bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev)
427 {
428     struct pc98_partition       *dptr;
429     struct disklabel            *lp;
430     int                         sector, slice, i;
431     char                        buf[BUFSIZE];
432
433     /*
434      * Following calculations attempt to determine the correct value
435      * for d->od_boff by looking for the slice and partition specified,
436      * or searching for reasonable defaults.
437      */
438
439     /*
440      * Find the slice in the DOS slice table.
441      */
442     od->od_nslices = 0;
443     if (od->od_flags & BD_FLOPPY) {
444         sector = 0;
445         goto unsliced;
446     }
447     if (bd_read(od, 0, 1, buf)) {
448         DEBUG("error reading MBR");
449         return (EIO);
450     }
451
452     /* 
453      * Check the slice table magic.
454      */
455     if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
456         /* If a slice number was explicitly supplied, this is an error */
457         if (dev->d_kind.biosdisk.slice > 0) {
458             DEBUG("no slice table/MBR (no magic)");
459             return (ENOENT);
460         }
461         sector = 0;
462         goto unsliced;          /* may be a floppy */
463     }
464     if (bd_read(od, 1, 1, buf)) {
465         DEBUG("error reading MBR");
466         return (EIO);
467     }
468
469     /*
470      * copy the partition table, then pick up any extended partitions.
471      */
472     bcopy(buf + DOSPARTOFF, &od->od_slicetab,
473       sizeof(struct pc98_partition) * NDOSPART);
474     od->od_nslices = NDOSPART;          /* extended slices start here */
475     od->od_flags |= BD_PARTTABOK;
476     dptr = &od->od_slicetab[0];
477
478     /* Is this a request for the whole disk? */
479     if (dev->d_kind.biosdisk.slice == -1) {
480         sector = 0;
481         goto unsliced;
482     }
483
484     /*
485      * if a slice number was supplied but not found, this is an error.
486      */
487     if (dev->d_kind.biosdisk.slice > 0) {
488         slice = dev->d_kind.biosdisk.slice - 1;
489         if (slice >= od->od_nslices) {
490             DEBUG("slice %d not found", slice);
491             return (ENOENT);
492         }
493     }
494
495     /* Try to auto-detect the best slice; this should always give a slice number */
496     if (dev->d_kind.biosdisk.slice == 0) {
497         slice = bd_bestslice(od);
498         if (slice == -1) {
499             return (ENOENT);
500         }
501         dev->d_kind.biosdisk.slice = slice;
502     }
503
504     dptr = &od->od_slicetab[0];
505     /*
506      * Accept the supplied slice number unequivocally (we may be looking
507      * at a DOS partition).
508      */
509     dptr += (dev->d_kind.biosdisk.slice - 1);   /* we number 1-4, offsets are 0-3 */
510     sector = dptr->dp_scyl * od->od_hds * od->od_sec +
511         dptr->dp_shd * od->od_sec + dptr->dp_ssect;
512     {
513         int end = dptr->dp_ecyl * od->od_hds * od->od_sec +
514             dptr->dp_ehd * od->od_sec + dptr->dp_esect;
515         DEBUG("slice entry %d at %d, %d sectors",
516               dev->d_kind.biosdisk.slice - 1, sector, end-sector);
517     }
518
519     /*
520      * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
521      */
522     if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0))
523         dev->d_kind.biosdisk.partition = 0;
524
525  unsliced:
526     /* 
527      * Now we have the slice offset, look for the partition in the disklabel if we have
528      * a partition to start with.
529      *
530      * XXX we might want to check the label checksum.
531      */
532     if (dev->d_kind.biosdisk.partition < 0) {
533         od->od_boff = sector;           /* no partition, must be after the slice */
534         DEBUG("opening raw slice");
535     } else {
536         
537         if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
538             DEBUG("error reading disklabel");
539             return (EIO);
540         }
541         DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
542         bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
543         lp = &od->od_disklabel;
544         od->od_flags |= BD_LABELOK;
545
546         if (lp->d_magic != DISKMAGIC) {
547             DEBUG("no disklabel");
548             return (ENOENT);
549         }
550         if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
551             DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
552                   'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
553             return (EPART);
554         }
555
556 #ifdef DISK_DEBUG
557         /* Complain if the partition is unused unless this is a floppy. */
558         if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
559             !(od->od_flags & BD_FLOPPY))
560             DEBUG("warning, partition marked as unused");
561 #endif
562         
563         od->od_boff = 
564                 lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
565                 lp->d_partitions[RAW_PART].p_offset +
566                 sector;
567     }
568     return (0);
569 }
570
571 /*
572  * Search for a slice with the following preferences:
573  *
574  * 1: Active FreeBSD slice
575  * 2: Non-active FreeBSD slice
576  * 3: Active Linux slice
577  * 4: non-active Linux slice
578  * 5: Active FAT/FAT32 slice
579  * 6: non-active FAT/FAT32 slice
580  */
581 #define PREF_RAWDISK    0
582 #define PREF_FBSD_ACT   1
583 #define PREF_FBSD       2
584 #define PREF_LINUX_ACT  3
585 #define PREF_LINUX      4
586 #define PREF_DOS_ACT    5
587 #define PREF_DOS        6
588 #define PREF_NONE       7
589
590 /*
591  * slicelimit is in the range 0 .. NDOSPART
592  */
593 static int
594 bd_bestslice(struct open_disk *od)
595 {
596         struct pc98_partition *dp;
597         int pref, preflevel;
598         int i, prefslice;
599         
600         prefslice = 0;
601         preflevel = PREF_NONE;
602
603         dp = &od->od_slicetab[0];
604         for (i = 0; i < od->od_nslices; i++, dp++) {
605                 switch(dp->dp_mid & PC98_MID_MASK) {
606                 case PC98_MID_386BSD:           /* FreeBSD */
607                         if ((dp->dp_mid & PC98_MID_BOOTABLE) &&
608                             (preflevel > PREF_FBSD_ACT)) {
609                                 pref = i;
610                                 preflevel = PREF_FBSD_ACT;
611                         } else if (preflevel > PREF_FBSD) {
612                                 pref = i;
613                                 preflevel = PREF_FBSD;
614                         }
615                         break;
616
617                 case 0x11:                              /* DOS/Windows */
618                 case 0x20:
619                 case 0x21:
620                 case 0x22:
621                 case 0x23:
622                 case 0x63:
623                         if ((dp->dp_mid & PC98_MID_BOOTABLE) &&
624                             (preflevel > PREF_DOS_ACT)) {
625                                 pref = i;
626                                 preflevel = PREF_DOS_ACT;
627                         } else if (preflevel > PREF_DOS) {
628                                 pref = i;
629                                 preflevel = PREF_DOS;
630                         }
631                         break;
632                 }
633         }
634         return (prefslice);
635 }
636  
637 static int 
638 bd_close(struct open_file *f)
639 {
640     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
641
642     bd_closedisk(od);
643     return(0);
644 }
645
646 static void
647 bd_closedisk(struct open_disk *od)
648 {
649     DEBUG("open_disk %p", od);
650 #if 0
651     /* XXX is this required? (especially if disk already open...) */
652     if (od->od_flags & BD_FLOPPY)
653         delay(3000000);
654 #endif
655     free(od);
656 }
657
658 static int 
659 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
660 {
661     struct bcache_devdata       bcd;
662     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
663
664     bcd.dv_strategy = bd_realstrategy;
665     bcd.dv_devdata = devdata;
666     return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize));
667 }
668
669 static int 
670 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
671 {
672     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
673     int                 blks;
674 #ifdef BD_SUPPORT_FRAGS
675     char                fragbuf[BIOSDISK_SECSIZE];
676     size_t              fragsize;
677
678     fragsize = size % BIOSDISK_SECSIZE;
679 #else
680     if (size % BIOSDISK_SECSIZE)
681         panic("bd_strategy: %d bytes I/O not multiple of block size", size);
682 #endif
683
684     DEBUG("open_disk %p", od);
685     blks = size / BIOSDISK_SECSIZE;
686     if (rsize)
687         *rsize = 0;
688
689     switch(rw){
690     case F_READ:
691         DEBUG("read %d from %d to %p", blks, dblk, buf);
692
693         if (blks && bd_read(od, dblk, blks, buf)) {
694             DEBUG("read error");
695             return (EIO);
696         }
697 #ifdef BD_SUPPORT_FRAGS
698         DEBUG("bd_strategy: frag read %d from %d+%d to %p",
699             fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
700         if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
701             DEBUG("frag read error");
702             return(EIO);
703         }
704         bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
705 #endif
706         break;
707     case F_WRITE :
708         DEBUG("write %d from %d to %p", blks, dblk, buf);
709
710         if (blks && bd_write(od, dblk, blks, buf)) {
711             DEBUG("write error");
712             return (EIO);
713         }
714 #ifdef BD_SUPPORT_FRAGS
715         if(fragsize) {
716             DEBUG("Attempted to write a frag");
717             return (EIO);
718         }
719 #endif
720         break;
721     default:
722         /* DO NOTHING */
723         return (EROFS);
724     }
725
726     if (rsize)
727         *rsize = size;
728     return (0);
729 }
730
731 /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
732 #define FLOPPY_BOUNCEBUF        18
733
734 static int
735 bd_chs_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
736 {
737     u_int       x, bpc, cyl, hd, sec;
738
739     bpc = (od->od_sec * od->od_hds);    /* blocks per cylinder */
740     x = dblk;
741     cyl = x / bpc;                      /* block # / blocks per cylinder */
742     x %= bpc;                           /* block offset into cylinder */
743     hd = x / od->od_sec;                /* offset / blocks per track */
744     sec = x % od->od_sec;               /* offset into track */
745
746     v86.ctl = V86_FLAGS;
747     v86.addr = 0x1b;
748     if (write)
749         v86.eax = 0x0500 | od->od_unit;
750     else
751         v86.eax = 0x0600 | od->od_unit;
752     if (od->od_flags & BD_FLOPPY) {
753         v86.eax |= 0xd000;
754         v86.ecx = 0x0200 | (cyl & 0xff);
755         v86.edx = (hd << 8) | (sec + 1);
756     } else if (od->od_flags & BD_OPTICAL) {
757         v86.eax &= 0xFF7F;
758         v86.ecx = dblk & 0xFFFF;
759         v86.edx = dblk >> 16;
760     } else {
761         v86.ecx = cyl;
762         v86.edx = (hd << 8) | sec;
763     }
764     v86.ebx = blks * BIOSDISK_SECSIZE;
765     v86.es = VTOPSEG(dest);
766     v86.ebp = VTOPOFF(dest);
767     v86int();
768     return (v86.efl & 0x1);
769 }
770
771 static int
772 bd_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
773 {
774     u_int       x, sec, result, resid, retry, maxfer;
775     caddr_t     p, xp, bbuf, breg;
776     
777     /* Just in case some idiot actually tries to read/write -1 blocks... */
778     if (blks < 0)
779         return (-1);
780
781     resid = blks;
782     p = dest;
783
784     /* Decide whether we have to bounce */
785     if (VTOP(dest) >> 20 != 0 ||
786         ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
787
788         /* 
789          * There is a 64k physical boundary somewhere in the
790          * destination buffer, or the destination buffer is above
791          * first 1MB of physical memory so we have to arrange a
792          * suitable bounce buffer.  Allocate a buffer twice as large
793          * as we need to.  Use the bottom half unless there is a break
794          * there, in which case we use the top half.
795          */
796         x = min(od->od_sec, (unsigned)blks);
797         bbuf = alloca(x * 2 * BIOSDISK_SECSIZE);
798         if (((u_int32_t)VTOP(bbuf) & 0xffff0000) ==
799             ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
800             breg = bbuf;
801         } else {
802             breg = bbuf + x * BIOSDISK_SECSIZE;
803         }
804         maxfer = x;             /* limit transfers to bounce region size */
805     } else {
806         breg = bbuf = NULL;
807         maxfer = 0;
808     }
809     
810     while (resid > 0) {
811         /*
812          * Play it safe and don't cross track boundaries.
813          * (XXX this is probably unnecessary)
814          */
815         sec = dblk % od->od_sec;        /* offset into track */
816         x = min(od->od_sec - sec, resid);
817         if (maxfer > 0)
818             x = min(x, maxfer);         /* fit bounce buffer */
819
820         /* where do we transfer to? */
821         xp = bbuf == NULL ? p : breg;
822
823         /*
824          * Put your Data In, Put your Data out,
825          * Put your Data In, and shake it all about 
826          */
827         if (write && bbuf != NULL)
828             bcopy(p, breg, x * BIOSDISK_SECSIZE);
829
830         /*
831          * Loop retrying the operation a couple of times.  The BIOS
832          * may also retry.
833          */
834         for (retry = 0; retry < 3; retry++) {
835             /* if retrying, reset the drive */
836             if (retry > 0) {
837                 v86.ctl = V86_FLAGS;
838                 v86.addr = 0x1b;
839                 v86.eax = 0x0300 | od->od_unit;
840                 v86int();
841             }
842
843             result = bd_chs_io(od, dblk, x, xp, write);
844             if (result == 0)
845                 break;
846         }
847
848         if (write)
849             DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, VTOP(p),
850                 result ? "failed" : "ok");
851         else
852             DEBUG("%d sectors from %p (0x%x) to %lld %s", x, p, VTOP(p), dblk,
853                 result ? "failed" : "ok");
854         if (result) {
855             return(-1);
856         }
857         if (!write && bbuf != NULL)
858             bcopy(breg, p, x * BIOSDISK_SECSIZE);
859         p += (x * BIOSDISK_SECSIZE);
860         dblk += x;
861         resid -= x;
862     }
863
864 /*    hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
865     return(0);
866 }
867
868 static int
869 bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
870 {
871
872     return (bd_io(od, dblk, blks, dest, 0));
873 }
874
875 static int
876 bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
877 {
878
879     return (bd_io(od, dblk, blks, dest, 1));
880 }
881
882 static int
883 bd_getgeom(struct open_disk *od)
884 {
885
886     if (od->od_flags & BD_FLOPPY) {
887         od->od_cyl = 79;
888         od->od_hds = 2;
889         od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
890     } else if (od->od_flags & BD_OPTICAL) {
891         od->od_cyl = 0xFFFE;
892         od->od_hds = 8;
893         od->od_sec = 32;
894     } else {
895         v86.ctl = V86_FLAGS;
896         v86.addr = 0x1b;
897         v86.eax = 0x8400 | od->od_unit;
898         v86int();
899       
900         od->od_cyl = v86.ecx;
901         od->od_hds = (v86.edx >> 8) & 0xff;
902         od->od_sec = v86.edx & 0xff;
903         if (v86.efl & 0x1)
904             return(1);
905     }
906
907     DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
908     return(0);
909 }
910
911 /*
912  * Return the BIOS geometry of a given "fixed drive" in a format
913  * suitable for the legacy bootinfo structure.  Since the kernel is
914  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
915  * prefer to get the information directly, rather than rely on being
916  * able to put it together from information already maintained for
917  * different purposes and for a probably different number of drives.
918  *
919  * For valid drives, the geometry is expected in the format (31..0)
920  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
921  * indicated by returning the geometry of a "1.2M" PC-format floppy
922  * disk.  And, incidentally, what is returned is not the geometry as
923  * such but the highest valid cylinder, head, and sector numbers.
924  */
925 u_int32_t
926 bd_getbigeom(int bunit)
927 {
928     int hds = 0;
929     int unit = 0x80;            /* IDE HDD */
930     u_int addr = 0xA155d;
931
932     while (unit < 0xa7) {
933         if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
934             if (hds++ == bunit)
935                 break;
936
937         if (unit >= 0xA0) {
938             int  media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F;
939
940             if (media == 7 && hds++ == bunit)   /* SCSI MO */
941                 return(0xFFFE0820); /* C:65535 H:8 S:32 */
942         }
943         if (++unit == 0x84) {
944             unit = 0xA0;        /* SCSI HDD */
945             addr = 0xA1482;
946         }
947     }
948     if (unit == 0xa7)
949         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
950     v86.ctl = V86_FLAGS;
951     v86.addr = 0x1b;
952     v86.eax = 0x8400 | unit;
953     v86int();
954     if (v86.efl & 0x1)
955         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
956     return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
957 }
958
959 /*
960  * Return a suitable dev_t value for (dev).
961  *
962  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
963  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
964  */
965 int
966 bd_getdev(struct i386_devdesc *dev)
967 {
968     struct open_disk            *od;
969     int                         biosdev;
970     int                         major;
971     int                         rootdev;
972     char                        *nip, *cp;
973     int                         unitofs = 0, i, unit;
974
975     biosdev = bd_unit2bios(dev->d_unit);
976     DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
977     if (biosdev == -1)                          /* not a BIOS device */
978         return(-1);
979     if (bd_opendisk(&od, dev) != 0)             /* oops, not a viable device */
980         return(-1);
981
982     if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
983         /* floppy (or emulated floppy) or ATAPI device */
984         if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
985             /* is an ATAPI disk */
986             major = WFDMAJOR;
987         } else {
988             /* is a floppy disk */
989             major = FDMAJOR;
990         }
991     } else {
992         /* harddisk */
993         if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
994             /* label OK, disk labelled as SCSI */
995             major = DAMAJOR;
996             /* check for unit number correction hint, now deprecated */
997             if ((nip = getenv("num_ide_disks")) != NULL) {
998                 i = strtol(nip, &cp, 0);
999                 /* check for parse error */
1000                 if ((cp != nip) && (*cp == 0))
1001                     unitofs = i;
1002             }
1003         } else {
1004             /* assume an IDE disk */
1005             major = WDMAJOR;
1006         }
1007     }
1008     /* default root disk unit number */
1009     if ((biosdev & 0xf0) == 0xa0)
1010         unit = bdinfo[dev->d_unit].bd_da_unit;
1011     else
1012         unit = biosdev & 0xf;
1013
1014     /* XXX a better kludge to set the root disk unit number */
1015     if ((nip = getenv("root_disk_unit")) != NULL) {
1016         i = strtol(nip, &cp, 0);
1017         /* check for parse error */
1018         if ((cp != nip) && (*cp == 0))
1019             unit = i;
1020     }
1021
1022     rootdev = MAKEBOOTDEV(major, dev->d_kind.biosdisk.slice + 1, unit,
1023         dev->d_kind.biosdisk.partition);
1024     DEBUG("dev is 0x%x\n", rootdev);
1025     return(rootdev);
1026 }