]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/pc98/libpc98/biosdisk.c
Direct commit to catch pc98 up to recent changes
[FreeBSD/FreeBSD.git] / stand / 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 <sys/disk.h>
41 #include <sys/limits.h>
42 #include <stand.h>
43 #include <machine/bootinfo.h>
44 #include <stdarg.h>
45
46 #include <sys/disklabel.h>
47 #include <sys/diskpc98.h>
48
49 #include <bootstrap.h>
50 #include <btxv86.h>
51 #include "disk.h"
52 #include "libi386.h"
53
54 #define BIOS_NUMDRIVES          0x475
55 #define BIOSDISK_SECSIZE        512
56 #define BUFSIZE                 (1 * BIOSDISK_SECSIZE)
57
58 #define DT_ATAPI                0x10            /* disk type for ATAPI floppies */
59 #define WDMAJOR                 0               /* major numbers for devices we frontend for */
60 #define WFDMAJOR                1
61 #define FDMAJOR                 2
62 #define DAMAJOR                 4
63
64 #ifdef DISK_DEBUG
65 # define DEBUG(fmt, args...)    printf("%s: " fmt "\n" , __func__ , ## args)
66 #else
67 # define DEBUG(fmt, args...)
68 #endif
69
70 /*
71  * List of BIOS devices, translation from disk unit number to
72  * BIOS unit number.
73  */
74 static struct bdinfo
75 {
76         int             bd_unit;        /* BIOS unit number */
77         int             bd_cyl;         /* BIOS geometry */
78         int             bd_hds;
79         int             bd_sec;
80         int             bd_flags;
81 #define BD_MODEINT13            0x0000
82 #define BD_MODEEDD1             0x0001
83 #define BD_MODEEDD3             0x0002
84 #define BD_MODEMASK             0x0003
85 #define BD_FLOPPY               0x0004
86 #define BD_LABELOK              0x0008
87 #define BD_PARTTABOK            0x0010
88 #define BD_OPTICAL              0x0020
89         int             bd_type;        /* BIOS 'drive type' (floppy only) */
90         uint16_t        bd_sectorsize;  /* Sector size */
91         uint64_t        bd_sectors;     /* Disk size */
92         int             bd_da_unit;     /* kernel unit number for da */
93         int             bd_open;        /* reference counter */
94         void            *bd_bcache;     /* buffer cache data */
95 } bdinfo [MAXBDDEV];
96 static int nbdinfo = 0;
97
98 #define BD(dev) (bdinfo[(dev)->dd.d_unit])
99
100 static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
101     caddr_t dest);
102 static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
103     caddr_t dest);
104 static int bd_int13probe(struct bdinfo *bd);
105
106 static int bd_init(void);
107 static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
108     char *buf, size_t *rsize);
109 static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
110     char *buf, size_t *rsize);
111 static int bd_open(struct open_file *f, ...);
112 static int bd_close(struct open_file *f);
113 static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
114 static int bd_print(int verbose);
115
116 struct devsw biosdisk = {
117         "disk", 
118         DEVT_DISK, 
119         bd_init,
120         bd_strategy, 
121         bd_open, 
122         bd_close, 
123         bd_ioctl,
124         bd_print,
125         NULL
126 };
127
128 /*
129  * Translate between BIOS device numbers and our private unit numbers.
130  */
131 int
132 bd_bios2unit(int biosdev)
133 {
134         int i;
135
136         DEBUG("looking for bios device 0x%x", biosdev);
137         for (i = 0; i < nbdinfo; i++) {
138                 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
139                 if (bdinfo[i].bd_unit == biosdev)
140                         return (i);
141         }
142         return (-1);
143 }
144
145 int
146 bd_unit2bios(int unit)
147 {
148
149         if ((unit >= 0) && (unit < nbdinfo))
150                 return (bdinfo[unit].bd_unit);
151         return (-1);
152 }
153
154 /*
155  * Quiz the BIOS for disk devices, save a little info about them.
156  */
157 static int
158 bd_init(void)
159 {
160         int base, unit;
161         int da_drive=0, n=-0x10;
162
163         /* sequence 0x90, 0x80, 0xa0 */
164         for (base = 0x90; base <= 0xa0; base += n, n += 0x30) {
165                 for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4);
166                      unit++) {
167                         bdinfo[nbdinfo].bd_open = 0;
168                         bdinfo[nbdinfo].bd_bcache = NULL;
169                         bdinfo[nbdinfo].bd_unit = unit;
170                         bdinfo[nbdinfo].bd_flags =
171                                 (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0;
172                         if (!bd_int13probe(&bdinfo[nbdinfo])) {
173                                 if (((unit & 0xf0) == 0x90 &&
174                                         (unit & 0x0f) < 4) ||
175                                     ((unit & 0xf0) == 0xa0 &&
176                                         (unit & 0x0f) < 6))
177                                         /* Target IDs are not contiguous. */
178                                         continue;
179                                 else
180                                         break;
181                         }
182
183                         if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY) {
184                                 /* available 1.44MB access? */
185                                 if (*(u_char *)PTOV(0xA15AE) &
186                                     (1<<(unit & 0xf))) {
187                                         /* boot media 1.2MB FD? */
188                                         if ((*(u_char *)PTOV(0xA1584) &
189                                                 0xf0) != 0x90)
190                                                 bdinfo[nbdinfo].bd_unit =
191                                                         0x30 + (unit & 0xf);
192                                 }
193                         } else {
194                                 if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */
195                                         bdinfo[nbdinfo].bd_da_unit =
196                                                 da_drive++;
197                         }
198                         /* XXX we need "disk aliases" to make this simpler */
199                         printf("BIOS drive %c: is disk%d\n",
200                             'A' + nbdinfo, nbdinfo);
201                         nbdinfo++;
202                 }
203         }
204         bcache_add_dev(nbdinfo);
205         return(0);
206 }
207
208 /*
209  * Try to detect a device supported by the legacy int13 BIOS
210  */
211 static int
212 bd_int13probe(struct bdinfo *bd)
213 {
214         int addr;
215
216         if (bd->bd_flags & BD_FLOPPY) {
217                 addr = 0xa155c;
218         } else {
219                 if ((bd->bd_unit & 0xf0) == 0x80)
220                         addr = 0xa155d;
221                 else
222                         addr = 0xa1482;
223         }
224         if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) {
225                 bd->bd_flags |= BD_MODEINT13;
226                 return (1);
227         }
228         if ((bd->bd_unit & 0xF0) == 0xA0) {
229                 int media =
230                         ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F;
231
232                 if (media == 7) { /* MO */
233                         bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL;
234                         return(1);
235                 }
236         }
237         return (0);
238 }
239
240 /*
241  * Print information about disks
242  */
243 static int
244 bd_print(int verbose)
245 {
246         char line[80];
247         struct disk_devdesc dev;
248         int i, ret = 0;
249         struct pc98_partition *dptr;
250     
251         if (nbdinfo == 0)
252                 return (0);
253
254         printf("%s devices:", biosdisk.dv_name);
255         if ((ret = pager_output("\n")) != 0)
256                 return (ret);
257
258         for (i = 0; i < nbdinfo; i++) {
259                 snprintf(line, sizeof(line),
260                     "    disk%d:   BIOS drive %c (%ju X %u):\n", i,
261                     (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
262                     ('C' + bdinfo[i].bd_unit - 0x80),
263                     (uintmax_t)bdinfo[i].bd_sectors,
264                     bdinfo[i].bd_sectorsize);
265                 if ((ret = pager_output(line)) != 0)
266                         break;
267
268                 /* try to open the whole disk */
269                 dev.dd.d_dev = &biosdisk;
270                 dev.dd.d_unit = i;
271                 dev.d_slice = -1;
272                 dev.d_partition = -1;
273                 if (disk_open(&dev,
274                     bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
275                     bdinfo[i].bd_sectorsize) == 0) {
276                         snprintf(line, sizeof(line), "    disk%d", i);
277                         ret = disk_print(&dev, line, verbose);
278                         disk_close(&dev);
279                         if (ret != 0)
280                             return (ret);
281                 }
282         }
283         return (ret);
284 }
285
286 /* Given a size in 512 byte sectors, convert it to a human-readable number. */
287 static char *
288 display_size(uint64_t size)
289 {
290         static char buf[80];
291         char unit;
292
293         size /= 2;
294         unit = 'K';
295         if (size >= 10485760000LL) {
296                 size /= 1073741824;
297                 unit = 'T';
298         } else if (size >= 10240000) {
299                 size /= 1048576;
300                 unit = 'G';
301         } else if (size >= 10000) {
302                 size /= 1024;
303                 unit = 'M';
304         }
305         sprintf(buf, "%6ld%cB", (long)size, unit);
306         return (buf);
307 }
308
309 /*
310  * Attempt to open the disk described by (dev) for use by (f).
311  *
312  * Note that the philosophy here is "give them exactly what
313  * they ask for".  This is necessary because being too "smart"
314  * about what the user might want leads to complications.
315  * (eg. given no slice or partition value, with a disk that is
316  *  sliced - are they after the first BSD slice, or the DOS
317  *  slice before it?)
318  */
319 static int 
320 bd_open(struct open_file *f, ...)
321 {
322         va_list                         ap;
323         struct disk_devdesc             *dev;
324         struct disk_devdesc             disk;
325         int                             err;
326         uint64_t                        size;
327
328         va_start(ap, f);
329         dev = va_arg(ap, struct disk_devdesc *);
330         va_end(ap);
331     
332         if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
333                 return (EIO);
334         BD(dev).bd_open++;
335         if (BD(dev).bd_bcache == NULL)
336                 BD(dev).bd_bcache = bcache_allocate();
337
338         /*
339          * Read disk size from partition.
340          * This is needed to work around buggy BIOS systems returning
341          * wrong (truncated) disk media size.
342          * During bd_probe() we tested if the mulitplication of bd_sectors
343          * would overflow so it should be safe to perform here.
344          */
345         disk.dd.d_dev = dev->dd.d_dev;
346         disk.dd.d_unit = dev->dd.d_unit;
347         disk.d_slice = -1;
348         disk.d_partition = -1;
349         disk.d_offset = 0;
350         if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
351             BD(dev).bd_sectorsize) == 0) {
352
353                 if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
354                         size /= BD(dev).bd_sectorsize;
355                         if (size > BD(dev).bd_sectors)
356                                 BD(dev).bd_sectors = size;
357                 }
358                 disk_close(&disk);
359         }
360
361         err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
362             BD(dev).bd_sectorsize);
363         return(err);
364 }
365
366 static int 
367 bd_close(struct open_file *f)
368 {
369         struct disk_devdesc *dev;
370
371         dev = (struct disk_devdesc *)f->f_devdata;
372         BD(dev).bd_open--;
373         if (BD(dev).bd_open == 0) {
374             bcache_free(BD(dev).bd_bcache);
375             BD(dev).bd_bcache = NULL;
376         }
377         return (disk_close(dev));
378 }
379
380 static int
381 bd_ioctl(struct open_file *f, u_long cmd, void *data)
382 {
383         struct disk_devdesc *dev;
384         int rc;
385
386         dev = (struct disk_devdesc *)f->f_devdata;
387
388         rc = disk_ioctl(dev, cmd, data);
389         if (rc != ENOTTY)
390                 return (rc);
391
392         switch (cmd) {
393         case DIOCGSECTORSIZE:
394                 *(u_int *)data = BD(dev).bd_sectorsize;
395                 break;
396         case DIOCGMEDIASIZE:
397                 *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
398                 break;
399         default:
400                 return (ENOTTY);
401         }
402         return (0);
403 }
404
405 static int 
406 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
407     char *buf, size_t *rsize)
408 {
409         struct bcache_devdata bcd;
410         struct disk_devdesc *dev;
411
412         dev = (struct disk_devdesc *)devdata;
413         bcd.dv_strategy = bd_realstrategy;
414         bcd.dv_devdata = devdata;
415         bcd.dv_cache = BD(dev).bd_bcache;
416         return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
417             size, buf, rsize));
418 }
419
420 static int 
421 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
422     char *buf, size_t *rsize)
423 {
424     struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
425     uint64_t            disk_blocks;
426     int                 blks, rc;
427 #ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
428     char                fragbuf[BIOSDISK_SECSIZE];
429     size_t              fragsize;
430
431     fragsize = size % BIOSDISK_SECSIZE;
432 #else
433     if (size % BD(dev).bd_sectorsize)
434         panic("bd_strategy: %d bytes I/O not multiple of block size", size);
435 #endif
436
437     DEBUG("open_disk %p", dev);
438
439     /*
440      * Check the value of the size argument. We do have quite small
441      * heap (64MB), but we do not know good upper limit, so we check against
442      * INT_MAX here. This will also protect us against possible overflows
443      * while translating block count to bytes.
444      */
445     if (size > INT_MAX) {
446         DEBUG("too large read: %zu bytes", size);
447         return (EIO);
448     }
449
450     blks = size / BD(dev).bd_sectorsize;
451     if (dblk > dblk + blks)
452         return (EIO);
453
454     if (rsize)
455         *rsize = 0;
456
457     /* Get disk blocks, this value is either for whole disk or for partition */
458     if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) {
459         /* DIOCGMEDIASIZE does return bytes. */
460         disk_blocks /= BD(dev).bd_sectorsize;
461     } else {
462         /* We should not get here. Just try to survive. */
463         disk_blocks = BD(dev).bd_sectors - dev->d_offset;
464     }
465
466     /* Validate source block address. */
467     if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
468         return (EIO);
469
470     /*
471      * Truncate if we are crossing disk or partition end.
472      */
473     if (dblk + blks >= dev->d_offset + disk_blocks) {
474         blks = dev->d_offset + disk_blocks - dblk;
475         size = blks * BD(dev).bd_sectorsize;
476         DEBUG("short read %d", blks);
477     }
478
479     switch (rw & F_MASK) {
480     case F_READ:
481         DEBUG("read %d from %lld to %p", blks, dblk, buf);
482
483         if (blks && (rc = bd_read(dev, dblk, blks, buf))) {
484             /* Filter out floppy controller errors */
485             if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
486                 printf("read %d from %lld to %p, error: 0x%x", blks, dblk,
487                     buf, rc);
488             }
489             return (EIO);
490         }
491 #ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
492         DEBUG("bd_strategy: frag read %d from %d+%d to %p",
493             fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
494         if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
495             DEBUG("frag read error");
496             return(EIO);
497         }
498         bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
499 #endif
500         break;
501     case F_WRITE :
502         DEBUG("write %d from %d to %p", blks, dblk, buf);
503
504         if (blks && bd_write(dev, dblk, blks, buf)) {
505             DEBUG("write error");
506             return (EIO);
507         }
508 #ifdef BD_SUPPORT_FRAGS
509         if(fragsize) {
510             DEBUG("Attempted to write a frag");
511             return (EIO);
512         }
513 #endif
514         break;
515     default:
516         /* DO NOTHING */
517         return (EROFS);
518     }
519
520     if (rsize)
521         *rsize = size;
522     return (0);
523 }
524
525 /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
526 #define FLOPPY_BOUNCEBUF        18
527
528 static int
529 bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
530     int write)
531 {
532     u_int       x, bpc, cyl, hd, sec;
533
534     bpc = BD(dev).bd_sec * BD(dev).bd_hds;      /* blocks per cylinder */
535     x = dblk;
536     cyl = x / bpc;                      /* block # / blocks per cylinder */
537     x %= bpc;                           /* block offset into cylinder */
538     hd = x / BD(dev).bd_sec;            /* offset / blocks per track */
539     sec = x % BD(dev).bd_sec;           /* offset into track */
540
541     v86.ctl = V86_FLAGS;
542     v86.addr = 0x1b;
543     if (write)
544         v86.eax = 0x0500 | BD(dev).bd_unit;
545     else
546         v86.eax = 0x0600 | BD(dev).bd_unit;
547     if (BD(dev).bd_flags & BD_FLOPPY) {
548         v86.eax |= 0xd000;
549         v86.ecx = 0x0200 | (cyl & 0xff);
550         v86.edx = (hd << 8) | (sec + 1);
551     } else if (BD(dev).bd_flags & BD_OPTICAL) {
552         v86.eax &= 0xFF7F;
553         v86.ecx = dblk & 0xFFFF;
554         v86.edx = dblk >> 16;
555     } else {
556         v86.ecx = cyl;
557         v86.edx = (hd << 8) | sec;
558     }
559     v86.ebx = blks * BIOSDISK_SECSIZE;
560     v86.es = VTOPSEG(dest);
561     v86.ebp = VTOPOFF(dest);
562     v86int();
563     return (V86_CY(v86.efl));
564 }
565
566 static int
567 bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
568 {
569     u_int       x, sec, result, resid, retry, maxfer;
570     caddr_t     p, xp, bbuf;
571     
572     /* Just in case some idiot actually tries to read/write -1 blocks... */
573     if (blks < 0)
574         return (-1);
575
576     resid = blks;
577     p = dest;
578
579     /* Decide whether we have to bounce */
580     if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
581         (VTOP(dest) >> 16) != (VTOP(dest +
582         blks * BD(dev).bd_sectorsize) >> 16))) {
583
584         /* 
585          * There is a 64k physical boundary somewhere in the
586          * destination buffer, or the destination buffer is above
587          * first 1MB of physical memory so we have to arrange a
588          * suitable bounce buffer.  Allocate a buffer twice as large
589          * as we need to.  Use the bottom half unless there is a break
590          * there, in which case we use the top half.
591          */
592         x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
593         x = min(x, (unsigned)blks);
594         bbuf = PTOV(V86_IO_BUFFER);
595         maxfer = x;             /* limit transfers to bounce region size */
596     } else {
597         bbuf = NULL;
598         maxfer = 0;
599     }
600     
601     while (resid > 0) {
602         /*
603          * Play it safe and don't cross track boundaries.
604          * (XXX this is probably unnecessary)
605          */
606         sec = dblk % BD(dev).bd_sec;    /* offset into track */
607         x = min(BD(dev).bd_sec - sec, resid);
608         if (maxfer > 0)
609             x = min(x, maxfer);         /* fit bounce buffer */
610
611         /* where do we transfer to? */
612         xp = bbuf == NULL ? p : bbuf;
613
614         /*
615          * Put your Data In, Put your Data out,
616          * Put your Data In, and shake it all about 
617          */
618         if (write && bbuf != NULL)
619             bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
620
621         /*
622          * Loop retrying the operation a couple of times.  The BIOS
623          * may also retry.
624          */
625         for (retry = 0; retry < 3; retry++) {
626             /* if retrying, reset the drive */
627             if (retry > 0) {
628                 v86.ctl = V86_FLAGS;
629                 v86.addr = 0x1b;
630                 v86.eax = 0x0300 | BD(dev).bd_unit;
631                 v86int();
632             }
633
634             result = bd_chs_io(dev, dblk, x, xp, write);
635             if (result == 0)
636                 break;
637         }
638
639         if (write)
640             DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
641                 p, VTOP(p), dblk, result ? "failed" : "ok");
642         else
643             DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
644                 dblk, p, VTOP(p), result ? "failed" : "ok");
645         if (result) {
646             return (result);
647         }
648         if (!write && bbuf != NULL)
649             bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
650         p += (x * BD(dev).bd_sectorsize);
651         dblk += x;
652         resid -= x;
653     }
654
655 /*    hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
656     return(0);
657 }
658
659 static int
660 bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
661     caddr_t dest)
662 {
663
664         return (bd_io(dev, dblk, blks, dest, 0));
665 }
666
667 static int
668 bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
669     caddr_t dest)
670 {
671
672         return (bd_io(dev, dblk, blks, dest, 1));
673 }
674
675 #if 0
676 static int
677 bd_getgeom(struct open_disk *od)
678 {
679
680     if (od->od_flags & BD_FLOPPY) {
681         od->od_cyl = 79;
682         od->od_hds = 2;
683         od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
684     } else if (od->od_flags & BD_OPTICAL) {
685         od->od_cyl = 0xFFFE;
686         od->od_hds = 8;
687         od->od_sec = 32;
688     } else {
689         v86.ctl = V86_FLAGS;
690         v86.addr = 0x1b;
691         v86.eax = 0x8400 | od->od_unit;
692         v86int();
693       
694         od->od_cyl = v86.ecx;
695         od->od_hds = (v86.edx >> 8) & 0xff;
696         od->od_sec = v86.edx & 0xff;
697         if (V86_CY(v86.efl))
698             return(1);
699     }
700
701     DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
702     return(0);
703 }
704 #endif
705
706 /*
707  * Return the BIOS geometry of a given "fixed drive" in a format
708  * suitable for the legacy bootinfo structure.  Since the kernel is
709  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
710  * prefer to get the information directly, rather than rely on being
711  * able to put it together from information already maintained for
712  * different purposes and for a probably different number of drives.
713  *
714  * For valid drives, the geometry is expected in the format (31..0)
715  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
716  * indicated by returning the geometry of a "1.2M" PC-format floppy
717  * disk.  And, incidentally, what is returned is not the geometry as
718  * such but the highest valid cylinder, head, and sector numbers.
719  */
720 u_int32_t
721 bd_getbigeom(int bunit)
722 {
723     int hds = 0;
724     int unit = 0x80;            /* IDE HDD */
725     u_int addr = 0xA155d;
726
727     while (unit < 0xa7) {
728         if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
729             if (hds++ == bunit)
730                 break;
731
732         if (unit >= 0xA0) {
733             int  media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F;
734
735             if (media == 7 && hds++ == bunit)   /* SCSI MO */
736                 return(0xFFFE0820); /* C:65535 H:8 S:32 */
737         }
738         if (++unit == 0x84) {
739             unit = 0xA0;        /* SCSI HDD */
740             addr = 0xA1482;
741         }
742     }
743     if (unit == 0xa7)
744         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
745     v86.ctl = V86_FLAGS;
746     v86.addr = 0x1b;
747     v86.eax = 0x8400 | unit;
748     v86int();
749     if (V86_CY(v86.efl))
750         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
751     return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
752 }
753
754 /*
755  * Return a suitable dev_t value for (dev).
756  *
757  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
758  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
759  */
760 int
761 bd_getdev(struct i386_devdesc *d)
762 {
763     struct disk_devdesc         *dev;
764     int                         biosdev;
765     int                         major;
766     int                         rootdev;
767     char                        *nip, *cp;
768     int                         unitofs = 0, i, unit;
769
770     dev = (struct disk_devdesc *)d;
771     biosdev = bd_unit2bios(dev->dd.d_unit);
772     DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
773     if (biosdev == -1)                          /* not a BIOS device */
774         return(-1);
775     if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
776         BD(dev).bd_sectorsize) != 0)            /* oops, not a viable device */
777             return (-1);
778     else
779         disk_close(dev);
780
781     if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
782         /* floppy (or emulated floppy) or ATAPI device */
783         if (BD(dev).bd_type == DT_ATAPI) {
784             /* is an ATAPI disk */
785             major = WFDMAJOR;
786         } else {
787             /* is a floppy disk */
788             major = FDMAJOR;
789         }
790     } else {
791         /* harddisk */
792         if ((BD(dev).bd_flags & BD_LABELOK) && 0) {
793 //          (BD(dev).bd_disklabel.d_type == DTYPE_SCSI)) {
794             /* label OK, disk labelled as SCSI */
795             major = DAMAJOR;
796             /* check for unit number correction hint, now deprecated */
797             if ((nip = getenv("num_ide_disks")) != NULL) {
798                 i = strtol(nip, &cp, 0);
799                 /* check for parse error */
800                 if ((cp != nip) && (*cp == 0))
801                     unitofs = i;
802             }
803         } else {
804             /* assume an IDE disk */
805             major = WDMAJOR;
806         }
807     }
808     /* default root disk unit number */
809     if ((biosdev & 0xf0) == 0xa0)
810         unit = BD(dev).bd_da_unit;
811     else
812         unit = biosdev & 0xf;
813
814     /* XXX a better kludge to set the root disk unit number */
815     if ((nip = getenv("root_disk_unit")) != NULL) {
816         i = strtol(nip, &cp, 0);
817         /* check for parse error */
818         if ((cp != nip) && (*cp == 0))
819             unit = i;
820     }
821
822     rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
823     DEBUG("dev is 0x%x\n", rootdev);
824     return(rootdev);
825 }