]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/i386/libi386/biosdisk.c
loader: issue edd probe before legacy ah=08 and detect no media
[FreeBSD/FreeBSD.git] / stand / i386 / libi386 / biosdisk.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * BIOS disk device handling.
33  *
34  * Ideas and algorithms from:
35  *
36  * - NetBSD libi386/biosdisk.c
37  * - FreeBSD biosboot/disk.c
38  *
39  */
40
41 #include <sys/disk.h>
42 #include <sys/limits.h>
43 #include <stand.h>
44 #include <machine/bootinfo.h>
45 #include <stdarg.h>
46
47 #include <bootstrap.h>
48 #include <btxv86.h>
49 #include <edd.h>
50 #include "disk.h"
51 #include "libi386.h"
52
53 #define BIOS_NUMDRIVES          0x475
54 #define BIOSDISK_SECSIZE        512
55 #define BUFSIZE                 (1 * BIOSDISK_SECSIZE)
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 /*
70  * List of BIOS devices, translation from disk unit number to
71  * BIOS unit number.
72  */
73 static struct bdinfo
74 {
75         int             bd_unit;        /* BIOS unit number */
76         int             bd_cyl;         /* BIOS geometry */
77         int             bd_hds;
78         int             bd_sec;
79         int             bd_flags;
80 #define BD_MODEINT13    0x0000
81 #define BD_MODEEDD1     0x0001
82 #define BD_MODEEDD3     0x0002
83 #define BD_MODEEDD      (BD_MODEEDD1 | BD_MODEEDD3)
84 #define BD_MODEMASK     0x0003
85 #define BD_FLOPPY       0x0004
86 #define BD_NO_MEDIA     0x0008
87         int             bd_type;        /* BIOS 'drive type' (floppy only) */
88         uint16_t        bd_sectorsize;  /* Sector size */
89         uint64_t        bd_sectors;     /* Disk size */
90         int             bd_open;        /* reference counter */
91         void            *bd_bcache;     /* buffer cache data */
92 } bdinfo [MAXBDDEV];
93 static int nbdinfo = 0;
94
95 #define BD(dev)         (bdinfo[(dev)->dd.d_unit])
96 #define BD_RD           0
97 #define BD_WR           1
98
99 static void bd_io_workaround(struct disk_devdesc *dev);
100
101 static int bd_io(struct disk_devdesc *, daddr_t, int, caddr_t, int);
102 static int bd_int13probe(struct bdinfo *bd);
103
104 static int bd_init(void);
105 static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
106     char *buf, size_t *rsize);
107 static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
108     char *buf, size_t *rsize);
109 static int bd_open(struct open_file *f, ...);
110 static int bd_close(struct open_file *f);
111 static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
112 static int bd_print(int verbose);
113
114 struct devsw biosdisk = {
115         "disk",
116         DEVT_DISK,
117         bd_init,
118         bd_strategy,
119         bd_open,
120         bd_close,
121         bd_ioctl,
122         bd_print,
123         NULL
124 };
125
126 /*
127  * Translate between BIOS device numbers and our private unit numbers.
128  */
129 int
130 bd_bios2unit(int biosdev)
131 {
132         int i;
133
134         DEBUG("looking for bios device 0x%x", biosdev);
135         for (i = 0; i < nbdinfo; i++) {
136                 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
137                 if (bdinfo[i].bd_unit == biosdev)
138                         return (i);
139         }
140         return (-1);
141 }
142
143 int
144 bd_unit2bios(int unit)
145 {
146
147         if ((unit >= 0) && (unit < nbdinfo))
148                 return (bdinfo[unit].bd_unit);
149         return (-1);
150 }
151
152 /*
153  * Quiz the BIOS for disk devices, save a little info about them.
154  */
155 static int
156 bd_init(void)
157 {
158         int base, unit, nfd = 0;
159
160         /* sequence 0, 0x80 */
161         for (base = 0; base <= 0x80; base += 0x80) {
162                 for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
163 #ifndef VIRTUALBOX
164                         /*
165                          * Check the BIOS equipment list for number
166                          * of fixed disks.
167                          */
168                         if (base == 0x80 &&
169                             (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
170                                 break;
171 #endif
172                         bdinfo[nbdinfo].bd_open = 0;
173                         bdinfo[nbdinfo].bd_bcache = NULL;
174                         bdinfo[nbdinfo].bd_unit = unit;
175                         bdinfo[nbdinfo].bd_flags = unit < 0x80 ? BD_FLOPPY: 0;
176                         if (!bd_int13probe(&bdinfo[nbdinfo]))
177                                 break;
178
179                         /* XXX we need "disk aliases" to make this simpler */
180                         printf("BIOS drive %c: is disk%d\n", (unit < 0x80) ?
181                             ('A' + unit): ('C' + unit - 0x80), nbdinfo);
182                         nbdinfo++;
183                         if (base == 0x80)
184                                 nfd++;
185                 }
186         }
187         bcache_add_dev(nbdinfo);
188         return (0);
189 }
190
191 /*
192  * Return EDD version or 0 if EDD is not supported on this drive.
193  */
194 static int
195 bd_check_extensions(int unit)
196 {
197         /* Determine if we can use EDD with this device. */
198         v86.ctl = V86_FLAGS;
199         v86.addr = 0x13;
200         v86.eax = 0x4100;
201         v86.edx = unit;
202         v86.ebx = 0x55aa;
203         v86int();
204
205         if (V86_CY(v86.efl) ||                  /* carry set */
206             (v86.ebx & 0xffff) != 0xaa55)       /* signature */
207                 return (0);
208
209         /* extended disk access functions (AH=42h-44h,47h,48h) supported */
210         if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
211                 return (0);
212
213         return ((v86.eax >> 8) & 0xff);
214 }
215
216 static void
217 bd_reset_disk(int unit)
218 {
219         /* reset disk */
220         v86.ctl = V86_FLAGS;
221         v86.addr = 0x13;
222         v86.eax = 0;
223         v86.edx = unit;
224         v86int();
225 }
226
227 /*
228  * Read CHS info. Return 0 on success, error otherwise.
229  */
230 static int
231 bd_get_diskinfo_std(struct bdinfo *bd)
232 {
233         bzero(&v86, sizeof(v86));
234         v86.ctl = V86_FLAGS;
235         v86.addr = 0x13;
236         v86.eax = 0x800;
237         v86.edx = bd->bd_unit;
238         v86int();
239
240         if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
241                 return ((v86.eax & 0xff00) >> 8);
242
243         /* return custom error on absurd sector number */
244         if ((v86.ecx & 0x3f) == 0)
245                 return (0x60);
246
247         bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
248         /* Convert max head # -> # of heads */
249         bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
250         bd->bd_sec = v86.ecx & 0x3f;
251         bd->bd_type = v86.ebx;
252         bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
253
254         return (0);
255 }
256
257 /*
258  * Read EDD info. Return 0 on success, error otherwise.
259  */
260 static int
261 bd_get_diskinfo_ext(struct bdinfo *bd)
262 {
263         struct edd_params params;
264         uint64_t total;
265
266         /* Get disk params */
267         bzero(&params, sizeof(params));
268         params.len = sizeof(params);
269         v86.ctl = V86_FLAGS;
270         v86.addr = 0x13;
271         v86.eax = 0x4800;
272         v86.edx = bd->bd_unit;
273         v86.ds = VTOPSEG(&params);
274         v86.esi = VTOPOFF(&params);
275         v86int();
276
277         if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
278                 return ((v86.eax & 0xff00) >> 8);
279
280         /*
281          * Sector size must be a multiple of 512 bytes.
282          * An alternate test would be to check power of 2,
283          * powerof2(params.sector_size).
284          * 4K is largest read buffer we can use at this time.
285          */
286         if (params.sector_size >= 512 &&
287             params.sector_size <= 4096 &&
288             (params.sector_size % BIOSDISK_SECSIZE) == 0)
289                 bd->bd_sectorsize = params.sector_size;
290
291         bd->bd_cyl = params.cylinders;
292         bd->bd_hds = params.heads;
293         bd->bd_sec = params.sectors_per_track;
294
295         if (params.sectors != 0) {
296                 total = params.sectors;
297         } else {
298                 total = (uint64_t)params.cylinders *
299                     params.heads * params.sectors_per_track;
300         }
301         bd->bd_sectors = total;
302
303         return (0);
304 }
305
306 /*
307  * Try to detect a device supported by the legacy int13 BIOS
308  */
309 static int
310 bd_int13probe(struct bdinfo *bd)
311 {
312         int edd;
313         int ret;
314
315         bd->bd_flags &= ~BD_NO_MEDIA;
316
317         edd = bd_check_extensions(bd->bd_unit);
318         if (edd == 0)
319                 bd->bd_flags |= BD_MODEINT13;
320         else if (edd < 0x30)
321                 bd->bd_flags |= BD_MODEEDD1;
322         else
323                 bd->bd_flags |= BD_MODEEDD3;
324
325         /* Default sector size */
326         bd->bd_sectorsize = BIOSDISK_SECSIZE;
327
328         /*
329          * Test if the floppy device is present, so we can avoid receiving
330          * bogus information from bd_get_diskinfo_std().
331          */
332         if (bd->bd_unit < 0x80) {
333                 /* reset disk */
334                 bd_reset_disk(bd->bd_unit);
335
336                 /* Get disk type */
337                 v86.ctl = V86_FLAGS;
338                 v86.addr = 0x13;
339                 v86.eax = 0x1500;
340                 v86.edx = bd->bd_unit;
341                 v86int();
342                 if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
343                         return (0);
344         }
345
346         ret = 1;
347         if (edd != 0)
348                 ret = bd_get_diskinfo_ext(bd);
349         if (ret != 0 || bd->bd_sectors == 0)
350                 ret = bd_get_diskinfo_std(bd);
351
352         if (ret != 0 && bd->bd_unit < 0x80) {
353                 /* Set defaults for 1.44 floppy */
354                 bd->bd_cyl = 80;
355                 bd->bd_hds = 2;
356                 bd->bd_sec = 18;
357                 bd->bd_type = 4;
358                 bd->bd_sectors = 2880;
359                 /* Since we are there, there most likely is no media */
360                 bd->bd_flags |= BD_NO_MEDIA;
361                 ret = 0;
362         }
363
364         if (ret != 0) {
365                 if (bd->bd_sectors != 0 && edd != 0) {
366                         bd->bd_sec = 63;
367                         bd->bd_hds = 255;
368                         bd->bd_cyl =
369                             (bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) /
370                             bd->bd_sec * bd->bd_hds;
371                 } else {
372                         printf("Can not get information about %s unit %#x\n",
373                             biosdisk.dv_name, bd->bd_unit);
374                         return (0);
375                 }
376         }
377
378         if (bd->bd_sec == 0)
379                 bd->bd_sec = 63;
380         if (bd->bd_hds == 0)
381                 bd->bd_hds = 255;
382
383         if (bd->bd_sectors == 0)
384                 bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
385
386         DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
387             bd->bd_hds, bd->bd_sec);
388
389         return (1);
390 }
391
392 /*
393  * Print information about disks
394  */
395 static int
396 bd_print(int verbose)
397 {
398         static char line[80];
399         struct disk_devdesc dev;
400         int i, ret = 0;
401
402         if (nbdinfo == 0)
403                 return (0);
404
405         printf("%s devices:", biosdisk.dv_name);
406         if ((ret = pager_output("\n")) != 0)
407                 return (ret);
408
409         for (i = 0; i < nbdinfo; i++) {
410                 snprintf(line, sizeof(line),
411                     "    disk%d:   BIOS drive %c (%s%ju X %u):\n", i,
412                     (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
413                     ('C' + bdinfo[i].bd_unit - 0x80),
414                     (bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
415                     "no media, " : "",
416                     (uintmax_t)bdinfo[i].bd_sectors,
417                     bdinfo[i].bd_sectorsize);
418                 if ((ret = pager_output(line)) != 0)
419                         break;
420
421                 dev.dd.d_dev = &biosdisk;
422                 dev.dd.d_unit = i;
423                 dev.d_slice = -1;
424                 dev.d_partition = -1;
425                 if (disk_open(&dev,
426                     bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
427                     bdinfo[i].bd_sectorsize) == 0) {
428                         snprintf(line, sizeof(line), "    disk%d", i);
429                         ret = disk_print(&dev, line, verbose);
430                         disk_close(&dev);
431                         if (ret != 0)
432                                 break;
433                 }
434         }
435         return (ret);
436 }
437
438 /*
439  * Attempt to open the disk described by (dev) for use by (f).
440  *
441  * Note that the philosophy here is "give them exactly what
442  * they ask for".  This is necessary because being too "smart"
443  * about what the user might want leads to complications.
444  * (eg. given no slice or partition value, with a disk that is
445  *  sliced - are they after the first BSD slice, or the DOS
446  *  slice before it?)
447  */
448 static int
449 bd_open(struct open_file *f, ...)
450 {
451         struct disk_devdesc *dev;
452         struct disk_devdesc disk;
453         va_list ap;
454         uint64_t size;
455         int rc;
456
457         va_start(ap, f);
458         dev = va_arg(ap, struct disk_devdesc *);
459         va_end(ap);
460
461         if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
462                 return (EIO);
463
464         if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
465                 if (!bd_int13probe(&BD(dev)))
466                         return (EIO);
467         }
468         BD(dev).bd_open++;
469         if (BD(dev).bd_bcache == NULL)
470             BD(dev).bd_bcache = bcache_allocate();
471
472         /*
473          * Read disk size from partition.
474          * This is needed to work around buggy BIOS systems returning
475          * wrong (truncated) disk media size.
476          * During bd_probe() we tested if the mulitplication of bd_sectors
477          * would overflow so it should be safe to perform here.
478          */
479         disk.dd.d_dev = dev->dd.d_dev;
480         disk.dd.d_unit = dev->dd.d_unit;
481         disk.d_slice = -1;
482         disk.d_partition = -1;
483         disk.d_offset = 0;
484
485         if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
486             BD(dev).bd_sectorsize) == 0) {
487
488                 if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
489                         size /= BD(dev).bd_sectorsize;
490                         if (size > BD(dev).bd_sectors)
491                                 BD(dev).bd_sectors = size;
492                 }
493                 disk_close(&disk);
494         }
495
496         rc = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
497             BD(dev).bd_sectorsize);
498         if (rc != 0) {
499                 BD(dev).bd_open--;
500                 if (BD(dev).bd_open == 0) {
501                         bcache_free(BD(dev).bd_bcache);
502                         BD(dev).bd_bcache = NULL;
503                 }
504         }
505         return (rc);
506 }
507
508 static int
509 bd_close(struct open_file *f)
510 {
511         struct disk_devdesc *dev;
512
513         dev = (struct disk_devdesc *)f->f_devdata;
514         BD(dev).bd_open--;
515         if (BD(dev).bd_open == 0) {
516             bcache_free(BD(dev).bd_bcache);
517             BD(dev).bd_bcache = NULL;
518         }
519         return (disk_close(dev));
520 }
521
522 static int
523 bd_ioctl(struct open_file *f, u_long cmd, void *data)
524 {
525         struct disk_devdesc *dev;
526         int rc;
527
528         dev = (struct disk_devdesc *)f->f_devdata;
529
530         rc = disk_ioctl(dev, cmd, data);
531         if (rc != ENOTTY)
532                 return (rc);
533
534         switch (cmd) {
535         case DIOCGSECTORSIZE:
536                 *(uint32_t *)data = BD(dev).bd_sectorsize;
537                 break;
538         case DIOCGMEDIASIZE:
539                 *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
540                 break;
541         default:
542                 return (ENOTTY);
543         }
544         return (0);
545 }
546
547 static int
548 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
549     char *buf, size_t *rsize)
550 {
551         struct bcache_devdata bcd;
552         struct disk_devdesc *dev;
553
554         dev = (struct disk_devdesc *)devdata;
555         bcd.dv_strategy = bd_realstrategy;
556         bcd.dv_devdata = devdata;
557         bcd.dv_cache = BD(dev).bd_bcache;
558         return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, size,
559             buf, rsize));
560 }
561
562 static int
563 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
564     char *buf, size_t *rsize)
565 {
566         struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
567         uint64_t disk_blocks, offset;
568         size_t blks, blkoff, bsize, rest;
569         caddr_t bbuf;
570         int rc;
571
572         /*
573          * First make sure the IO size is a multiple of 512 bytes. While we do
574          * process partial reads below, the strategy mechanism is built
575          * assuming IO is a multiple of 512B blocks. If the request is not
576          * a multiple of 512B blocks, it has to be some sort of bug.
577          */
578         if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) {
579                 printf("bd_strategy: %d bytes I/O not multiple of %d\n",
580                     size, BIOSDISK_SECSIZE);
581                 return (EIO);
582         }
583
584         DEBUG("open_disk %p", dev);
585
586         offset = dblk * BIOSDISK_SECSIZE;
587         dblk = offset / BD(dev).bd_sectorsize;
588         blkoff = offset % BD(dev).bd_sectorsize;
589
590         /*
591          * Check the value of the size argument. We do have quite small
592          * heap (64MB), but we do not know good upper limit, so we check against
593          * INT_MAX here. This will also protect us against possible overflows
594          * while translating block count to bytes.
595          */
596         if (size > INT_MAX) {
597                 DEBUG("too large I/O: %zu bytes", size);
598                 return (EIO);
599         }
600
601         blks = size / BD(dev).bd_sectorsize;
602         if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0)
603                 blks++;
604
605         if (dblk > dblk + blks)
606                 return (EIO);
607
608         if (rsize)
609                 *rsize = 0;
610
611         /*
612          * Get disk blocks, this value is either for whole disk or for
613          * partition.
614          */
615         if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
616                 /* DIOCGMEDIASIZE does return bytes. */
617                 disk_blocks /= BD(dev).bd_sectorsize;
618         } else {
619                 /* We should not get here. Just try to survive. */
620                 disk_blocks = BD(dev).bd_sectors - dev->d_offset;
621         }
622
623         /* Validate source block address. */
624         if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
625                 return (EIO);
626
627         /*
628          * Truncate if we are crossing disk or partition end.
629          */
630         if (dblk + blks >= dev->d_offset + disk_blocks) {
631                 blks = dev->d_offset + disk_blocks - dblk;
632                 size = blks * BD(dev).bd_sectorsize;
633                 DEBUG("short I/O %d", blks);
634         }
635
636         if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0)
637                 panic("BUG: Real mode buffer is too small\n");
638
639         bbuf = PTOV(V86_IO_BUFFER);
640         rest = size;
641
642         while (blks > 0) {
643                 int x = min(blks, V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize);
644
645                 switch (rw & F_MASK) {
646                 case F_READ:
647                         DEBUG("read %d from %lld to %p", x, dblk, buf);
648                         bsize = BD(dev).bd_sectorsize * x - blkoff;
649                         if (rest < bsize)
650                                 bsize = rest;
651
652                         if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0)
653                                 return (EIO);
654
655                         bcopy(bbuf + blkoff, buf, bsize);
656                         break;
657                 case F_WRITE :
658                         DEBUG("write %d from %lld to %p", x, dblk, buf);
659                         if (blkoff != 0) {
660                                 /*
661                                  * We got offset to sector, read 1 sector to
662                                  * bbuf.
663                                  */
664                                 x = 1;
665                                 bsize = BD(dev).bd_sectorsize - blkoff;
666                                 bsize = min(bsize, rest);
667                                 rc = bd_io(dev, dblk, x, bbuf, BD_RD);
668                         } else if (rest < BD(dev).bd_sectorsize) {
669                                 /*
670                                  * The remaining block is not full
671                                  * sector. Read 1 sector to bbuf.
672                                  */
673                                 x = 1;
674                                 bsize = rest;
675                                 rc = bd_io(dev, dblk, x, bbuf, BD_RD);
676                         } else {
677                                 /* We can write full sector(s). */
678                                 bsize = BD(dev).bd_sectorsize * x;
679                         }
680                         /*
681                          * Put your Data In, Put your Data out,
682                          * Put your Data In, and shake it all about
683                          */
684                         bcopy(buf, bbuf + blkoff, bsize);
685                         if ((rc = bd_io(dev, dblk, x, bbuf, BD_WR)) != 0)
686                                 return (EIO);
687
688                         break;
689                 default:
690                         /* DO NOTHING */
691                         return (EROFS);
692                 }
693
694                 blkoff = 0;
695                 buf += bsize;
696                 rest -= bsize;
697                 blks -= x;
698                 dblk += x;
699         }
700
701         if (rsize != NULL)
702                 *rsize = size;
703         return (0);
704 }
705
706 static int
707 bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
708     int dowrite)
709 {
710         static struct edd_packet packet;
711
712         packet.len = sizeof(struct edd_packet);
713         packet.count = blks;
714         packet.off = VTOPOFF(dest);
715         packet.seg = VTOPSEG(dest);
716         packet.lba = dblk;
717         v86.ctl = V86_FLAGS;
718         v86.addr = 0x13;
719         /* Should we Write with verify ?? 0x4302 ? */
720         if (dowrite == BD_WR)
721                 v86.eax = 0x4300;
722         else
723                 v86.eax = 0x4200;
724         v86.edx = BD(dev).bd_unit;
725         v86.ds = VTOPSEG(&packet);
726         v86.esi = VTOPOFF(&packet);
727         v86int();
728         if (V86_CY(v86.efl))
729                 return (v86.eax >> 8);
730         return (0);
731 }
732
733 static int
734 bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
735     int dowrite)
736 {
737         uint32_t x, bpc, cyl, hd, sec;
738
739         bpc = BD(dev).bd_sec * BD(dev).bd_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 / BD(dev).bd_sec;                /* offset / blocks per track */
744         sec = x % BD(dev).bd_sec;               /* offset into track */
745
746         /* correct sector number for 1-based BIOS numbering */
747         sec++;
748
749         if (cyl > 1023) {
750                 /* CHS doesn't support cylinders > 1023. */
751                 return (1);
752         }
753
754         v86.ctl = V86_FLAGS;
755         v86.addr = 0x13;
756         if (dowrite == BD_WR)
757                 v86.eax = 0x300 | blks;
758         else
759                 v86.eax = 0x200 | blks;
760         v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
761         v86.edx = (hd << 8) | BD(dev).bd_unit;
762         v86.es = VTOPSEG(dest);
763         v86.ebx = VTOPOFF(dest);
764         v86int();
765         if (V86_CY(v86.efl))
766                 return (v86.eax >> 8);
767         return (0);
768 }
769
770 static void
771 bd_io_workaround(struct disk_devdesc *dev)
772 {
773         uint8_t buf[8 * 1024];
774
775         bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD);
776 }
777
778 static int
779 bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
780     int dowrite)
781 {
782         int result, retry;
783
784         /* Just in case some idiot actually tries to read/write -1 blocks... */
785         if (blks < 0)
786                 return (-1);
787
788         /*
789          * Workaround for a problem with some HP ProLiant BIOS failing to work
790          * out the boot disk after installation. hrs and kuriyama discovered
791          * this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and
792          * discovered that an int13h call seems to cause a buffer overrun in
793          * the bios. The problem is alleviated by doing an extra read before
794          * the buggy read. It is not immediately known whether other models
795          * are similarly affected.
796          * Loop retrying the operation a couple of times.  The BIOS
797          * may also retry.
798          */
799         if (dowrite == BD_RD && dblk >= 0x100000000)
800                 bd_io_workaround(dev);
801         for (retry = 0; retry < 3; retry++) {
802                 if (BD(dev).bd_flags & BD_MODEEDD)
803                         result = bd_edd_io(dev, dblk, blks, dest, dowrite);
804                 else
805                         result = bd_chs_io(dev, dblk, blks, dest, dowrite);
806
807                 if (result == 0) {
808                         if (BD(dev).bd_flags & BD_NO_MEDIA)
809                                 BD(dev).bd_flags &= ~BD_NO_MEDIA;
810                         break;
811                 }
812
813                 bd_reset_disk(BD(dev).bd_unit);
814
815                 /*
816                  * Error codes:
817                  * 20h  controller failure
818                  * 31h  no media in drive (IBM/MS INT 13 extensions)
819                  * 80h  no media in drive, VMWare (Fusion)
820                  * There is no reason to repeat the IO with errors above.
821                  */
822                 if (result == 0x20 || result == 0x31 || result == 0x80) {
823                         BD(dev).bd_flags |= BD_NO_MEDIA;
824                         break;
825                 }
826         }
827
828         if (result != 0 && (BD(dev).bd_flags & BD_NO_MEDIA) == 0) {
829                 if (dowrite == BD_WR) {
830                         printf("%s%d: Write %d sector(s) from %p (0x%x) "
831                             "to %lld: 0x%x\n", dev->dd.d_dev->dv_name,
832                             dev->dd.d_unit, blks, dest, VTOP(dest), dblk,
833                             result);
834                 } else {
835                         printf("%s%d: Read %d sector(s) from %lld to %p "
836                             "(0x%x): 0x%x\n", dev->dd.d_dev->dv_name,
837                             dev->dd.d_unit, blks, dblk, dest, VTOP(dest),
838                             result);
839                 }
840         }
841
842         return (result);
843 }
844
845 /*
846  * Return the BIOS geometry of a given "fixed drive" in a format
847  * suitable for the legacy bootinfo structure.  Since the kernel is
848  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
849  * prefer to get the information directly, rather than rely on being
850  * able to put it together from information already maintained for
851  * different purposes and for a probably different number of drives.
852  *
853  * For valid drives, the geometry is expected in the format (31..0)
854  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
855  * indicated by returning the geometry of a "1.2M" PC-format floppy
856  * disk.  And, incidentally, what is returned is not the geometry as
857  * such but the highest valid cylinder, head, and sector numbers.
858  */
859 uint32_t
860 bd_getbigeom(int bunit)
861 {
862
863         v86.ctl = V86_FLAGS;
864         v86.addr = 0x13;
865         v86.eax = 0x800;
866         v86.edx = 0x80 + bunit;
867         v86int();
868         if (V86_CY(v86.efl))
869                 return (0x4f010f);
870         return (((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
871             (v86.edx & 0xff00) | (v86.ecx & 0x3f));
872 }
873
874 /*
875  * Return a suitable dev_t value for (dev).
876  *
877  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
878  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
879  */
880 int
881 bd_getdev(struct i386_devdesc *d)
882 {
883         struct disk_devdesc *dev;
884         int     biosdev;
885         int     major;
886         int     rootdev;
887         char    *nip, *cp;
888         int     i, unit;
889
890         dev = (struct disk_devdesc *)d;
891         biosdev = bd_unit2bios(dev->dd.d_unit);
892         DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
893         if (biosdev == -1)                      /* not a BIOS device */
894                 return (-1);
895         if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
896             BD(dev).bd_sectorsize) != 0)        /* oops, not a viable device */
897                 return (-1);
898         else
899                 disk_close(dev);
900
901         if (biosdev < 0x80) {
902                 /* floppy (or emulated floppy) or ATAPI device */
903                 if (bdinfo[dev->dd.d_unit].bd_type == DT_ATAPI) {
904                         /* is an ATAPI disk */
905                         major = WFDMAJOR;
906                 } else {
907                         /* is a floppy disk */
908                         major = FDMAJOR;
909                 }
910         } else {
911                 /* assume an IDE disk */
912                 major = WDMAJOR;
913         }
914         /* default root disk unit number */
915         unit = biosdev & 0x7f;
916
917         /* XXX a better kludge to set the root disk unit number */
918         if ((nip = getenv("root_disk_unit")) != NULL) {
919                 i = strtol(nip, &cp, 0);
920                 /* check for parse error */
921                 if ((cp != nip) && (*cp == 0))
922                         unit = i;
923         }
924
925         rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
926         DEBUG("dev is 0x%x\n", rootdev);
927         return (rootdev);
928 }