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