blob: 3db2144763ecb3ed52b9086d274dee219d8a1694 [file] [log] [blame]
Toomas Soome76b35942018-03-13 11:58:56 +02001/*
Toomas Soome199767f2015-10-25 00:06:51 +03002 * 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>
Toomas Soome199767f2015-10-25 00:06:51 +030029
30/*
31 * BIOS disk device handling.
Toomas Soome560b2482017-02-05 18:44:23 +020032 *
Toomas Soome199767f2015-10-25 00:06:51 +030033 * Ideas and algorithms from:
34 *
35 * - NetBSD libi386/biosdisk.c
36 * - FreeBSD biosboot/disk.c
37 *
38 */
39
40#include <sys/disk.h>
Toomas Soomecff899e2016-11-20 14:51:31 +020041#include <sys/limits.h>
Toomas Soome199767f2015-10-25 00:06:51 +030042#include <stand.h>
43#include <machine/bootinfo.h>
44#include <stdarg.h>
45
46#include <bootstrap.h>
47#include <btxv86.h>
48#include <edd.h>
49#include "disk.h"
50#include "libi386.h"
51
Toomas Soome5855bf02018-05-19 18:50:26 +030052#define BIOS_NUMDRIVES 0x475
53#define BIOSDISK_SECSIZE 512
54#define BUFSIZE (1 * BIOSDISK_SECSIZE)
Toomas Soome199767f2015-10-25 00:06:51 +030055
Toomas Soome5855bf02018-05-19 18:50:26 +030056#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */
57#define WDMAJOR 0 /* major numbers for devices we frontend for */
58#define WFDMAJOR 1
59#define FDMAJOR 2
60#define DAMAJOR 4
Toomas Soome199767f2015-10-25 00:06:51 +030061
62#ifdef DISK_DEBUG
Toomas Soome5855bf02018-05-19 18:50:26 +030063#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
Toomas Soome199767f2015-10-25 00:06:51 +030064#else
Toomas Soome5855bf02018-05-19 18:50:26 +030065#define DEBUG(fmt, args...)
Toomas Soome199767f2015-10-25 00:06:51 +030066#endif
67
68/*
69 * List of BIOS devices, translation from disk unit number to
70 * BIOS unit number.
71 */
72static struct bdinfo
73{
74 int bd_unit; /* BIOS unit number */
75 int bd_cyl; /* BIOS geometry */
76 int bd_hds;
77 int bd_sec;
78 int bd_flags;
79#define BD_MODEINT13 0x0000
80#define BD_MODEEDD1 0x0001
81#define BD_MODEEDD3 0x0002
Toomas Soomeb75eb7e2017-06-03 01:58:24 +030082#define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3)
Toomas Soome199767f2015-10-25 00:06:51 +030083#define BD_MODEMASK 0x0003
84#define BD_FLOPPY 0x0004
Toomas Soomeb75eb7e2017-06-03 01:58:24 +030085#define BD_NO_MEDIA 0x0008
Toomas Soome199767f2015-10-25 00:06:51 +030086 int bd_type; /* BIOS 'drive type' (floppy only) */
87 uint16_t bd_sectorsize; /* Sector size */
88 uint64_t bd_sectors; /* Disk size */
89 int bd_open; /* reference counter */
90 void *bd_bcache; /* buffer cache data */
91} bdinfo [MAXBDDEV];
92static int nbdinfo = 0;
93
Toomas Soome76b35942018-03-13 11:58:56 +020094#define BD(dev) (bdinfo[(dev)->dd.d_unit])
Toomas Soomec9a7c752018-08-16 12:04:31 +030095#define BD_RD 0
96#define BD_WR 1
Toomas Soome199767f2015-10-25 00:06:51 +030097
Toomas Soomef8e0ecf2018-08-16 14:26:58 +030098static void bd_io_workaround(struct disk_devdesc *dev);
99
Toomas Soome4c004ea2017-05-19 03:06:10 +0300100static int bd_io(struct disk_devdesc *, daddr_t, int, caddr_t, int);
Toomas Soome199767f2015-10-25 00:06:51 +0300101static int bd_int13probe(struct bdinfo *bd);
102
103static int bd_init(void);
Toomas Soome38dea912016-11-26 00:42:58 +0200104static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
105 char *buf, size_t *rsize);
106static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
107 char *buf, size_t *rsize);
Toomas Soome199767f2015-10-25 00:06:51 +0300108static int bd_open(struct open_file *f, ...);
109static int bd_close(struct open_file *f);
110static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
111static int bd_print(int verbose);
Toomas Soome199767f2015-10-25 00:06:51 +0300112
113struct devsw biosdisk = {
114 "disk",
115 DEVT_DISK,
116 bd_init,
117 bd_strategy,
118 bd_open,
119 bd_close,
120 bd_ioctl,
121 bd_print,
Toomas Soome74207d12017-02-22 20:40:10 +0200122 NULL
Toomas Soome199767f2015-10-25 00:06:51 +0300123};
124
125/*
126 * Translate between BIOS device numbers and our private unit numbers.
127 */
128int
129bd_bios2unit(int biosdev)
130{
131 int i;
132
133 DEBUG("looking for bios device 0x%x", biosdev);
134 for (i = 0; i < nbdinfo; i++) {
135 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
136 if (bdinfo[i].bd_unit == biosdev)
137 return (i);
138 }
139 return (-1);
140}
141
142int
143bd_unit2bios(int unit)
144{
145
146 if ((unit >= 0) && (unit < nbdinfo))
147 return (bdinfo[unit].bd_unit);
148 return (-1);
149}
150
151/*
152 * Quiz the BIOS for disk devices, save a little info about them.
153 */
154static int
155bd_init(void)
156{
157 int base, unit, nfd = 0;
158
159 /* sequence 0, 0x80 */
160 for (base = 0; base <= 0x80; base += 0x80) {
161 for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
162#ifndef VIRTUALBOX
163 /*
164 * Check the BIOS equipment list for number
165 * of fixed disks.
166 */
Toomas Soome5855bf02018-05-19 18:50:26 +0300167 if (base == 0x80 &&
Toomas Soome199767f2015-10-25 00:06:51 +0300168 (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
169 break;
170#endif
171 bdinfo[nbdinfo].bd_open = 0;
172 bdinfo[nbdinfo].bd_bcache = NULL;
173 bdinfo[nbdinfo].bd_unit = unit;
174 bdinfo[nbdinfo].bd_flags = unit < 0x80 ? BD_FLOPPY: 0;
175 if (!bd_int13probe(&bdinfo[nbdinfo]))
176 break;
177
178#ifndef BOOT2
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#endif
183 nbdinfo++;
184 if (base == 0x80)
185 nfd++;
186 }
187 }
188 bcache_add_dev(nbdinfo);
Toomas Soome5855bf02018-05-19 18:50:26 +0300189 return (0);
Toomas Soome199767f2015-10-25 00:06:51 +0300190}
191
Toomas Soome199767f2015-10-25 00:06:51 +0300192/*
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300193 * Return EDD version or 0 if EDD is not supported on this drive.
Toomas Soome199767f2015-10-25 00:06:51 +0300194 */
195static int
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300196bd_check_extensions(int unit)
Toomas Soome199767f2015-10-25 00:06:51 +0300197{
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300198 /* Determine if we can use EDD with this device. */
199 v86.ctl = V86_FLAGS;
200 v86.addr = 0x13;
201 v86.eax = 0x4100;
202 v86.edx = unit;
203 v86.ebx = 0x55aa;
204 v86int();
Toomas Soome199767f2015-10-25 00:06:51 +0300205
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300206 if (V86_CY(v86.efl) || /* carry set */
207 (v86.ebx & 0xffff) != 0xaa55) /* signature */
208 return (0);
209
210 /* extended disk access functions (AH=42h-44h,47h,48h) supported */
211 if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
212 return (0);
213
214 return ((v86.eax >> 8) & 0xff);
215}
216
217static void
218bd_reset_disk(int unit)
219{
220 /* reset disk */
221 v86.ctl = V86_FLAGS;
222 v86.addr = 0x13;
223 v86.eax = 0;
224 v86.edx = unit;
225 v86int();
226}
227
228/*
229 * Read CHS info. Return 0 on success, error otherwise.
230 */
231static int
232bd_get_diskinfo_std(struct bdinfo *bd)
233{
234 bzero(&v86, sizeof (v86));
Toomas Soome199767f2015-10-25 00:06:51 +0300235 v86.ctl = V86_FLAGS;
236 v86.addr = 0x13;
237 v86.eax = 0x800;
238 v86.edx = bd->bd_unit;
239 v86int();
240
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300241 if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
242 return ((v86.eax & 0xff00) >> 8);
Toomas Soome199767f2015-10-25 00:06:51 +0300243
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300244 /* return custom error on absurd sector number */
245 if ((v86.ecx & 0x3f) == 0)
246 return (0x60);
Toomas Soome199767f2015-10-25 00:06:51 +0300247
Toomas Soome199767f2015-10-25 00:06:51 +0300248 bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
249 /* Convert max head # -> # of heads */
250 bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
251 bd->bd_sec = v86.ecx & 0x3f;
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300252 bd->bd_type = v86.ebx;
253 bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
Toomas Soome199767f2015-10-25 00:06:51 +0300254
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300255 return (0);
256}
Toomas Soome199767f2015-10-25 00:06:51 +0300257
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300258/*
259 * Read EDD info. Return 0 on success, error otherwise.
260 */
261static int
262bd_get_diskinfo_ext(struct bdinfo *bd)
263{
264 struct edd_params params;
265 uint64_t total;
Toomas Soome199767f2015-10-25 00:06:51 +0300266
Toomas Soome199767f2015-10-25 00:06:51 +0300267 /* Get disk params */
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300268 bzero(&params, sizeof (params));
269 params.len = sizeof (params);
Toomas Soome199767f2015-10-25 00:06:51 +0300270 v86.ctl = V86_FLAGS;
271 v86.addr = 0x13;
272 v86.eax = 0x4800;
273 v86.edx = bd->bd_unit;
274 v86.ds = VTOPSEG(&params);
275 v86.esi = VTOPOFF(&params);
276 v86int();
Toomas Soome199767f2015-10-25 00:06:51 +0300277
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300278 if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
279 return ((v86.eax & 0xff00) >> 8);
Toomas Soomecff899e2016-11-20 14:51:31 +0200280
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300281 /*
282 * Sector size must be a multiple of 512 bytes.
283 * An alternate test would be to check power of 2,
284 * powerof2(params.sector_size).
285 * 4K is largest read buffer we can use at this time.
286 */
287 if (params.sector_size >= 512 &&
288 params.sector_size <= 4096 &&
289 (params.sector_size % BIOSDISK_SECSIZE) == 0)
290 bd->bd_sectorsize = params.sector_size;
Toomas Soome199767f2015-10-25 00:06:51 +0300291
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300292 bd->bd_cyl = params.cylinders;
293 bd->bd_hds = params.heads;
294 bd->bd_sec = params.sectors_per_track;
295
296 if (params.sectors != 0) {
297 total = params.sectors;
298 } else {
Toomas Soome199767f2015-10-25 00:06:51 +0300299 total = (uint64_t)params.cylinders *
300 params.heads * params.sectors_per_track;
Toomas Soome199767f2015-10-25 00:06:51 +0300301 }
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300302 bd->bd_sectors = total;
303
304 return (0);
305}
306
307/*
308 * Try to detect a device supported by the legacy int13 BIOS
309 */
310static int
311bd_int13probe(struct bdinfo *bd)
312{
313 int edd;
314 int ret;
315
316 bd->bd_flags &= ~BD_NO_MEDIA;
317
318 edd = bd_check_extensions(bd->bd_unit);
319 if (edd == 0)
320 bd->bd_flags |= BD_MODEINT13;
321 else if (edd < 0x30)
322 bd->bd_flags |= BD_MODEEDD1;
323 else
324 bd->bd_flags |= BD_MODEEDD3;
325
326 /* Default sector size */
327 bd->bd_sectorsize = BIOSDISK_SECSIZE;
328
329 /*
330 * Test if the floppy device is present, so we can avoid receiving
331 * bogus information from bd_get_diskinfo_std().
332 */
333 if (bd->bd_unit < 0x80) {
334 /* reset disk */
335 bd_reset_disk(bd->bd_unit);
336
337 /* Get disk type */
338 v86.ctl = V86_FLAGS;
339 v86.addr = 0x13;
340 v86.eax = 0x1500;
341 v86.edx = bd->bd_unit;
342 v86int();
343 if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
344 return (0);
345 }
346
347 ret = 1;
348 if (edd != 0)
349 ret = bd_get_diskinfo_ext(bd);
350 if (ret != 0 || bd->bd_sectors == 0)
351 ret = bd_get_diskinfo_std(bd);
352
353 if (ret != 0 && bd->bd_unit < 0x80) {
354 /* Set defaults for 1.44 floppy */
355 bd->bd_cyl = 80;
356 bd->bd_hds = 2;
357 bd->bd_sec = 18;
358 bd->bd_type = 4;
359 bd->bd_sectors = 2880;
360 /* Since we are there, there most likely is no media */
361 bd->bd_flags |= BD_NO_MEDIA;
362 ret = 0;
363 }
364
365 if (ret != 0) {
366 if (bd->bd_sectors != 0 && edd != 0) {
367 bd->bd_sec = 63;
368 bd->bd_hds = 255;
369 bd->bd_cyl =
370 (bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) /
371 bd->bd_sec * bd->bd_hds;
372 } else {
373 printf("Can not get information about %s unit %#x\n",
374 biosdisk.dv_name, bd->bd_unit);
375 return (0);
376 }
377 }
378
379 if (bd->bd_sec == 0)
380 bd->bd_sec = 63;
381 if (bd->bd_hds == 0)
382 bd->bd_hds = 255;
383
384 if (bd->bd_sectors == 0)
385 bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
386
387 DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
388 bd->bd_hds, bd->bd_sec);
389
390 return (1);
Toomas Soome199767f2015-10-25 00:06:51 +0300391}
392
393/*
394 * Print information about disks
395 */
396static int
397bd_print(int verbose)
398{
399 static char line[80];
400 struct disk_devdesc dev;
401 int i, ret = 0;
402
Toomas Soome502b33a2016-11-18 15:26:15 +0200403 if (nbdinfo == 0)
404 return (0);
405
406 printf("%s devices:", biosdisk.dv_name);
407 if ((ret = pager_output("\n")) != 0)
408 return (ret);
409
Toomas Soome199767f2015-10-25 00:06:51 +0300410 for (i = 0; i < nbdinfo; i++) {
Dan McDonald9c02c7e2016-11-18 14:34:54 -0500411 snprintf(line, sizeof (line),
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300412 " disk%d: BIOS drive %c (%s%ju X %u):\n", i,
Toomas Soome199767f2015-10-25 00:06:51 +0300413 (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
Dan McDonald9c02c7e2016-11-18 14:34:54 -0500414 ('C' + bdinfo[i].bd_unit - 0x80),
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300415 (bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
416 "no media, " : "",
Dan McDonald9c02c7e2016-11-18 14:34:54 -0500417 (uintmax_t)bdinfo[i].bd_sectors,
418 bdinfo[i].bd_sectorsize);
Toomas Soomee8fda352018-08-16 14:21:13 +0300419 if ((ret = pager_output(line)) != 0)
420 break;
Toomas Soome199767f2015-10-25 00:06:51 +0300421
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300422 if ((bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
423 continue;
424
Toomas Soome76b35942018-03-13 11:58:56 +0200425 dev.dd.d_dev = &biosdisk;
426 dev.dd.d_unit = i;
Toomas Soome199767f2015-10-25 00:06:51 +0300427 dev.d_slice = -1;
428 dev.d_partition = -1;
429 if (disk_open(&dev,
430 bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
Toomas Soome74207d12017-02-22 20:40:10 +0200431 bdinfo[i].bd_sectorsize) == 0) {
Toomas Soomee8fda352018-08-16 14:21:13 +0300432 snprintf(line, sizeof (line), " disk%d", i);
Toomas Soome199767f2015-10-25 00:06:51 +0300433 ret = disk_print(&dev, line, verbose);
434 disk_close(&dev);
435 if (ret != 0)
Toomas Soomee8fda352018-08-16 14:21:13 +0300436 break;
Toomas Soome199767f2015-10-25 00:06:51 +0300437 }
438 }
439 return (ret);
440}
441
442/*
443 * Attempt to open the disk described by (dev) for use by (f).
444 *
445 * Note that the philosophy here is "give them exactly what
446 * they ask for". This is necessary because being too "smart"
447 * about what the user might want leads to complications.
448 * (eg. given no slice or partition value, with a disk that is
449 * sliced - are they after the first BSD slice, or the DOS
450 * slice before it?)
451 */
452static int
453bd_open(struct open_file *f, ...)
454{
455 struct disk_devdesc *dev;
Toomas Soomecff899e2016-11-20 14:51:31 +0200456 struct disk_devdesc disk;
Toomas Soome199767f2015-10-25 00:06:51 +0300457 va_list ap;
Toomas Soomecff899e2016-11-20 14:51:31 +0200458 uint64_t size;
Toomas Soomebde44122017-02-18 16:10:20 +0200459 int rc;
Toomas Soome199767f2015-10-25 00:06:51 +0300460
461 va_start(ap, f);
462 dev = va_arg(ap, struct disk_devdesc *);
463 va_end(ap);
464
Toomas Soome76b35942018-03-13 11:58:56 +0200465 if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
Toomas Soome199767f2015-10-25 00:06:51 +0300466 return (EIO);
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300467
468 if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
469 if (!bd_int13probe(&BD(dev)))
470 return (EIO);
471 if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
472 return (EIO);
473 }
Toomas Soome199767f2015-10-25 00:06:51 +0300474 BD(dev).bd_open++;
475 if (BD(dev).bd_bcache == NULL)
476 BD(dev).bd_bcache = bcache_allocate();
Toomas Soomecff899e2016-11-20 14:51:31 +0200477
478 /*
479 * Read disk size from partition.
480 * This is needed to work around buggy BIOS systems returning
481 * wrong (truncated) disk media size.
482 * During bd_probe() we tested if the mulitplication of bd_sectors
483 * would overflow so it should be safe to perform here.
484 */
Toomas Soome76b35942018-03-13 11:58:56 +0200485 disk.dd.d_dev = dev->dd.d_dev;
Toomas Soome76b35942018-03-13 11:58:56 +0200486 disk.dd.d_unit = dev->dd.d_unit;
Toomas Soomecff899e2016-11-20 14:51:31 +0200487 disk.d_slice = -1;
488 disk.d_partition = -1;
489 disk.d_offset = 0;
490
491 if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
Toomas Soome74207d12017-02-22 20:40:10 +0200492 BD(dev).bd_sectorsize) == 0) {
Toomas Soomecff899e2016-11-20 14:51:31 +0200493
494 if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
495 size /= BD(dev).bd_sectorsize;
496 if (size > BD(dev).bd_sectors)
497 BD(dev).bd_sectors = size;
498 }
499 disk_close(&disk);
500 }
501
Toomas Soomebde44122017-02-18 16:10:20 +0200502 rc = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
503 BD(dev).bd_sectorsize);
504 if (rc != 0) {
505 BD(dev).bd_open--;
506 if (BD(dev).bd_open == 0) {
507 bcache_free(BD(dev).bd_bcache);
508 BD(dev).bd_bcache = NULL;
509 }
510 }
511 return (rc);
Toomas Soome199767f2015-10-25 00:06:51 +0300512}
513
514static int
515bd_close(struct open_file *f)
516{
517 struct disk_devdesc *dev;
518
519 dev = (struct disk_devdesc *)f->f_devdata;
520 BD(dev).bd_open--;
521 if (BD(dev).bd_open == 0) {
522 bcache_free(BD(dev).bd_bcache);
523 BD(dev).bd_bcache = NULL;
524 }
525 return (disk_close(dev));
526}
527
528static int
529bd_ioctl(struct open_file *f, u_long cmd, void *data)
530{
531 struct disk_devdesc *dev;
Toomas Soomeedb35042016-11-07 15:37:12 +0200532 int rc;
Toomas Soome199767f2015-10-25 00:06:51 +0300533
534 dev = (struct disk_devdesc *)f->f_devdata;
Toomas Soomeedb35042016-11-07 15:37:12 +0200535
536 rc = disk_ioctl(dev, cmd, data);
537 if (rc != ENOTTY)
538 return (rc);
539
Toomas Soome199767f2015-10-25 00:06:51 +0300540 switch (cmd) {
541 case DIOCGSECTORSIZE:
Toomas Soome5855bf02018-05-19 18:50:26 +0300542 *(uint32_t *)data = BD(dev).bd_sectorsize;
Toomas Soome199767f2015-10-25 00:06:51 +0300543 break;
544 case DIOCGMEDIASIZE:
Toomas Soome79bea512016-12-04 13:15:56 +0200545 *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
Toomas Soome199767f2015-10-25 00:06:51 +0300546 break;
547 default:
548 return (ENOTTY);
549 }
550 return (0);
551}
552
553static int
Toomas Soome38dea912016-11-26 00:42:58 +0200554bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
Toomas Soome199767f2015-10-25 00:06:51 +0300555 char *buf, size_t *rsize)
556{
557 struct bcache_devdata bcd;
558 struct disk_devdesc *dev;
559
560 dev = (struct disk_devdesc *)devdata;
561 bcd.dv_strategy = bd_realstrategy;
562 bcd.dv_devdata = devdata;
563 bcd.dv_cache = BD(dev).bd_bcache;
Toomas Soome38dea912016-11-26 00:42:58 +0200564 return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, size,
Toomas Soome199767f2015-10-25 00:06:51 +0300565 buf, rsize));
566}
567
568static int
Toomas Soome38dea912016-11-26 00:42:58 +0200569bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
Toomas Soome199767f2015-10-25 00:06:51 +0300570 char *buf, size_t *rsize)
571{
Toomas Soome4c004ea2017-05-19 03:06:10 +0300572 struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
573 uint64_t disk_blocks, offset;
574 size_t blks, blkoff, bsize, rest;
575 caddr_t bbuf;
576 int rc;
Toomas Soome199767f2015-10-25 00:06:51 +0300577
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300578 if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
579 return (EIO);
580
Toomas Soome4c004ea2017-05-19 03:06:10 +0300581 /*
Toomas Soome78916de2017-06-13 20:35:48 +0300582 * First make sure the IO size is a multiple of 512 bytes. While we do
Toomas Soome4c004ea2017-05-19 03:06:10 +0300583 * process partial reads below, the strategy mechanism is built
Toomas Soome78916de2017-06-13 20:35:48 +0300584 * assuming IO is a multiple of 512B blocks. If the request is not
585 * a multiple of 512B blocks, it has to be some sort of bug.
Toomas Soome4c004ea2017-05-19 03:06:10 +0300586 */
587 if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) {
588 printf("bd_strategy: %d bytes I/O not multiple of %d\n",
589 size, BIOSDISK_SECSIZE);
590 return (EIO);
Toomas Soome199767f2015-10-25 00:06:51 +0300591 }
Toomas Soome199767f2015-10-25 00:06:51 +0300592
Toomas Soome4c004ea2017-05-19 03:06:10 +0300593 DEBUG("open_disk %p", dev);
Toomas Soome199767f2015-10-25 00:06:51 +0300594
Toomas Soome4c004ea2017-05-19 03:06:10 +0300595 offset = dblk * BIOSDISK_SECSIZE;
596 dblk = offset / BD(dev).bd_sectorsize;
597 blkoff = offset % BD(dev).bd_sectorsize;
598
599 /*
600 * Check the value of the size argument. We do have quite small
601 * heap (64MB), but we do not know good upper limit, so we check against
602 * INT_MAX here. This will also protect us against possible overflows
603 * while translating block count to bytes.
604 */
605 if (size > INT_MAX) {
Toomas Soome43f9d552018-08-16 14:23:38 +0300606 DEBUG("too large I/O: %zu bytes", size);
Toomas Soome4c004ea2017-05-19 03:06:10 +0300607 return (EIO);
608 }
609
610 blks = size / BD(dev).bd_sectorsize;
611 if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0)
612 blks++;
613
614 if (dblk > dblk + blks)
615 return (EIO);
616
617 if (rsize)
618 *rsize = 0;
619
620 /*
621 * Get disk blocks, this value is either for whole disk or for
622 * partition.
623 */
624 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
625 /* DIOCGMEDIASIZE does return bytes. */
626 disk_blocks /= BD(dev).bd_sectorsize;
627 } else {
628 /* We should not get here. Just try to survive. */
629 disk_blocks = BD(dev).bd_sectors - dev->d_offset;
630 }
631
632 /* Validate source block address. */
633 if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
634 return (EIO);
635
636 /*
637 * Truncate if we are crossing disk or partition end.
638 */
639 if (dblk + blks >= dev->d_offset + disk_blocks) {
640 blks = dev->d_offset + disk_blocks - dblk;
641 size = blks * BD(dev).bd_sectorsize;
Toomas Soome43f9d552018-08-16 14:23:38 +0300642 DEBUG("short I/O %d", blks);
Toomas Soome4c004ea2017-05-19 03:06:10 +0300643 }
644
645 if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0)
646 panic("BUG: Real mode buffer is too small\n");
647
648 bbuf = PTOV(V86_IO_BUFFER);
649 rest = size;
650
651 while (blks > 0) {
652 int x = min(blks, V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize);
653
654 switch (rw & F_MASK) {
655 case F_READ:
656 DEBUG("read %d from %lld to %p", x, dblk, buf);
657 bsize = BD(dev).bd_sectorsize * x - blkoff;
658 if (rest < bsize)
659 bsize = rest;
660
Toomas Soomec9a7c752018-08-16 12:04:31 +0300661 if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0)
Toomas Soome4c004ea2017-05-19 03:06:10 +0300662 return (EIO);
663
664 bcopy(bbuf + blkoff, buf, bsize);
665 break;
666 case F_WRITE :
667 DEBUG("write %d from %lld to %p", x, dblk, buf);
668 if (blkoff != 0) {
669 /*
670 * We got offset to sector, read 1 sector to
671 * bbuf.
672 */
673 x = 1;
674 bsize = BD(dev).bd_sectorsize - blkoff;
675 bsize = min(bsize, rest);
Toomas Soomec9a7c752018-08-16 12:04:31 +0300676 rc = bd_io(dev, dblk, x, bbuf, BD_RD);
Toomas Soome4c004ea2017-05-19 03:06:10 +0300677 } else if (rest < BD(dev).bd_sectorsize) {
678 /*
679 * The remaining block is not full
680 * sector. Read 1 sector to bbuf.
681 */
682 x = 1;
683 bsize = rest;
Toomas Soomec9a7c752018-08-16 12:04:31 +0300684 rc = bd_io(dev, dblk, x, bbuf, BD_RD);
Toomas Soome4c004ea2017-05-19 03:06:10 +0300685 } else {
686 /* We can write full sector(s). */
687 bsize = BD(dev).bd_sectorsize * x;
688 }
689 /*
690 * Put your Data In, Put your Data out,
691 * Put your Data In, and shake it all about
692 */
693 bcopy(buf, bbuf + blkoff, bsize);
Toomas Soomec9a7c752018-08-16 12:04:31 +0300694 if ((rc = bd_io(dev, dblk, x, bbuf, BD_WR)) != 0)
Toomas Soome4c004ea2017-05-19 03:06:10 +0300695 return (EIO);
696
697 break;
698 default:
699 /* DO NOTHING */
700 return (EROFS);
701 }
702
703 blkoff = 0;
704 buf += bsize;
705 rest -= bsize;
706 blks -= x;
707 dblk += x;
708 }
709
710 if (rsize != NULL)
711 *rsize = size;
712 return (0);
Toomas Soome199767f2015-10-25 00:06:51 +0300713}
714
Toomas Soome199767f2015-10-25 00:06:51 +0300715static int
716bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
717 int dowrite)
718{
Toomas Soome5855bf02018-05-19 18:50:26 +0300719 static struct edd_packet packet;
Toomas Soome199767f2015-10-25 00:06:51 +0300720
Toomas Soome5855bf02018-05-19 18:50:26 +0300721 packet.len = sizeof (struct edd_packet);
722 packet.count = blks;
723 packet.off = VTOPOFF(dest);
724 packet.seg = VTOPSEG(dest);
725 packet.lba = dblk;
726 v86.ctl = V86_FLAGS;
727 v86.addr = 0x13;
Toomas Soome199767f2015-10-25 00:06:51 +0300728 /* Should we Write with verify ?? 0x4302 ? */
Toomas Soomec9a7c752018-08-16 12:04:31 +0300729 if (dowrite == BD_WR)
Toomas Soome5855bf02018-05-19 18:50:26 +0300730 v86.eax = 0x4300;
731 else
732 v86.eax = 0x4200;
733 v86.edx = BD(dev).bd_unit;
734 v86.ds = VTOPSEG(&packet);
735 v86.esi = VTOPOFF(&packet);
736 v86int();
737 if (V86_CY(v86.efl))
738 return (v86.eax >> 8);
739 return (0);
Toomas Soome199767f2015-10-25 00:06:51 +0300740}
741
742static int
743bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
744 int dowrite)
745{
Toomas Soome5855bf02018-05-19 18:50:26 +0300746 uint32_t x, bpc, cyl, hd, sec;
Toomas Soome199767f2015-10-25 00:06:51 +0300747
Toomas Soome5855bf02018-05-19 18:50:26 +0300748 bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */
749 x = dblk;
750 cyl = x / bpc; /* block # / blocks per cylinder */
751 x %= bpc; /* block offset into cylinder */
752 hd = x / BD(dev).bd_sec; /* offset / blocks per track */
753 sec = x % BD(dev).bd_sec; /* offset into track */
Toomas Soome199767f2015-10-25 00:06:51 +0300754
Toomas Soome5855bf02018-05-19 18:50:26 +0300755 /* correct sector number for 1-based BIOS numbering */
756 sec++;
Toomas Soome199767f2015-10-25 00:06:51 +0300757
Toomas Soome5855bf02018-05-19 18:50:26 +0300758 if (cyl > 1023) {
759 /* CHS doesn't support cylinders > 1023. */
760 return (1);
761 }
Toomas Soome199767f2015-10-25 00:06:51 +0300762
Toomas Soome5855bf02018-05-19 18:50:26 +0300763 v86.ctl = V86_FLAGS;
764 v86.addr = 0x13;
Toomas Soomec9a7c752018-08-16 12:04:31 +0300765 if (dowrite == BD_WR)
Toomas Soome5855bf02018-05-19 18:50:26 +0300766 v86.eax = 0x300 | blks;
767 else
768 v86.eax = 0x200 | blks;
769 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
770 v86.edx = (hd << 8) | BD(dev).bd_unit;
771 v86.es = VTOPSEG(dest);
772 v86.ebx = VTOPOFF(dest);
773 v86int();
774 if (V86_CY(v86.efl))
775 return (v86.eax >> 8);
776 return (0);
Toomas Soome199767f2015-10-25 00:06:51 +0300777}
778
Toomas Soomef8e0ecf2018-08-16 14:26:58 +0300779static void
780bd_io_workaround(struct disk_devdesc *dev)
781{
782 uint8_t buf[8 * 1024];
783
Toomas Soomec9a7c752018-08-16 12:04:31 +0300784 bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD);
Toomas Soomef8e0ecf2018-08-16 14:26:58 +0300785}
786
Toomas Soome199767f2015-10-25 00:06:51 +0300787static int
Toomas Soome560b2482017-02-05 18:44:23 +0200788bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
789 int dowrite)
Toomas Soome199767f2015-10-25 00:06:51 +0300790{
Toomas Soome5855bf02018-05-19 18:50:26 +0300791 int result, retry;
Toomas Soome560b2482017-02-05 18:44:23 +0200792
Toomas Soome4c004ea2017-05-19 03:06:10 +0300793 /* Just in case some idiot actually tries to read/write -1 blocks... */
794 if (blks < 0)
795 return (-1);
Toomas Soome199767f2015-10-25 00:06:51 +0300796
797 /*
Toomas Soomef8e0ecf2018-08-16 14:26:58 +0300798 * Workaround for a problem with some HP ProLiant BIOS failing to work
799 * out the boot disk after installation. hrs and kuriyama discovered
800 * this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and
801 * discovered that an int13h call seems to cause a buffer overrun in
802 * the bios. The problem is alleviated by doing an extra read before
803 * the buggy read. It is not immediately known whether other models
804 * are similarly affected.
Toomas Soome199767f2015-10-25 00:06:51 +0300805 * Loop retrying the operation a couple of times. The BIOS
806 * may also retry.
807 */
Toomas Soomec9a7c752018-08-16 12:04:31 +0300808 if (dowrite == BD_RD && dblk >= 0x100000000)
Toomas Soomef8e0ecf2018-08-16 14:26:58 +0300809 bd_io_workaround(dev);
Toomas Soome199767f2015-10-25 00:06:51 +0300810 for (retry = 0; retry < 3; retry++) {
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300811 if (BD(dev).bd_flags & BD_MODEEDD)
Toomas Soome4c004ea2017-05-19 03:06:10 +0300812 result = bd_edd_io(dev, dblk, blks, dest, dowrite);
813 else
814 result = bd_chs_io(dev, dblk, blks, dest, dowrite);
815
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300816 if (result == 0) {
817 if (BD(dev).bd_flags & BD_NO_MEDIA)
818 BD(dev).bd_flags &= ~BD_NO_MEDIA;
Toomas Soome4c004ea2017-05-19 03:06:10 +0300819 break;
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300820 }
821
822 bd_reset_disk(BD(dev).bd_unit);
823
824 /*
825 * Error codes:
826 * 20h controller failure
827 * 31h no media in drive (IBM/MS INT 13 extensions)
828 * 80h no media in drive, VMWare (Fusion)
829 * There is no reason to repeat the IO with errors above.
830 */
831 if (result == 0x20 || result == 0x31 || result == 0x80) {
832 BD(dev).bd_flags |= BD_NO_MEDIA;
833 break;
834 }
Toomas Soome199767f2015-10-25 00:06:51 +0300835 }
836
Toomas Soomeb75eb7e2017-06-03 01:58:24 +0300837 if (result != 0 && (BD(dev).bd_flags & BD_NO_MEDIA) == 0) {
Toomas Soomec9a7c752018-08-16 12:04:31 +0300838 if (dowrite == BD_WR) {
Toomas Soome4c004ea2017-05-19 03:06:10 +0300839 printf("%s%d: Write %d sector(s) from %p (0x%x) "
Toomas Soome5855bf02018-05-19 18:50:26 +0300840 "to %lld: 0x%x\n", dev->dd.d_dev->dv_name,
Toomas Soome76b35942018-03-13 11:58:56 +0200841 dev->dd.d_unit, blks, dest, VTOP(dest), dblk,
842 result);
Toomas Soome4c004ea2017-05-19 03:06:10 +0300843 } else {
844 printf("%s%d: Read %d sector(s) from %lld to %p "
Toomas Soome5855bf02018-05-19 18:50:26 +0300845 "(0x%x): 0x%x\n", dev->dd.d_dev->dv_name,
Toomas Soome76b35942018-03-13 11:58:56 +0200846 dev->dd.d_unit, blks, dblk, dest, VTOP(dest),
847 result);
Toomas Soome5855bf02018-05-19 18:50:26 +0300848 }
Toomas Soome199767f2015-10-25 00:06:51 +0300849 }
Toomas Soome4c004ea2017-05-19 03:06:10 +0300850
Toomas Soome5855bf02018-05-19 18:50:26 +0300851 return (result);
Toomas Soome199767f2015-10-25 00:06:51 +0300852}
853
Toomas Soome199767f2015-10-25 00:06:51 +0300854/*
855 * Return the BIOS geometry of a given "fixed drive" in a format
856 * suitable for the legacy bootinfo structure. Since the kernel is
857 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
858 * prefer to get the information directly, rather than rely on being
859 * able to put it together from information already maintained for
860 * different purposes and for a probably different number of drives.
861 *
862 * For valid drives, the geometry is expected in the format (31..0)
863 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
864 * indicated by returning the geometry of a "1.2M" PC-format floppy
865 * disk. And, incidentally, what is returned is not the geometry as
866 * such but the highest valid cylinder, head, and sector numbers.
867 */
Toomas Soome5855bf02018-05-19 18:50:26 +0300868uint32_t
Toomas Soome199767f2015-10-25 00:06:51 +0300869bd_getbigeom(int bunit)
870{
871
Toomas Soome5855bf02018-05-19 18:50:26 +0300872 v86.ctl = V86_FLAGS;
873 v86.addr = 0x13;
874 v86.eax = 0x800;
875 v86.edx = 0x80 + bunit;
876 v86int();
877 if (V86_CY(v86.efl))
878 return (0x4f010f);
879 return (((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
880 (v86.edx & 0xff00) | (v86.ecx & 0x3f));
Toomas Soome199767f2015-10-25 00:06:51 +0300881}
882
883/*
884 * Return a suitable dev_t value for (dev).
885 *
886 * In the case where it looks like (dev) is a SCSI disk, we allow the number of
887 * IDE disks to be specified in $num_ide_disks. There should be a Better Way.
888 */
889int
890bd_getdev(struct i386_devdesc *d)
891{
Toomas Soome5855bf02018-05-19 18:50:26 +0300892 struct disk_devdesc *dev;
893 int biosdev;
894 int major;
895 int rootdev;
896 char *nip, *cp;
897 int i, unit;
Toomas Soome199767f2015-10-25 00:06:51 +0300898
Toomas Soome5855bf02018-05-19 18:50:26 +0300899 dev = (struct disk_devdesc *)d;
900 biosdev = bd_unit2bios(dev->dd.d_unit);
901 DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
902 if (biosdev == -1) /* not a BIOS device */
903 return (-1);
904 if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
905 BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */
906 return (-1);
907 else
908 disk_close(dev);
Toomas Soome199767f2015-10-25 00:06:51 +0300909
Toomas Soome5855bf02018-05-19 18:50:26 +0300910 if (biosdev < 0x80) {
911 /* floppy (or emulated floppy) or ATAPI device */
912 if (bdinfo[dev->dd.d_unit].bd_type == DT_ATAPI) {
913 /* is an ATAPI disk */
914 major = WFDMAJOR;
915 } else {
916 /* is a floppy disk */
917 major = FDMAJOR;
918 }
Toomas Soome199767f2015-10-25 00:06:51 +0300919 } else {
Toomas Soome5855bf02018-05-19 18:50:26 +0300920 /* assume an IDE disk */
921 major = WDMAJOR;
Toomas Soome199767f2015-10-25 00:06:51 +0300922 }
Toomas Soome5855bf02018-05-19 18:50:26 +0300923 /* default root disk unit number */
924 unit = biosdev & 0x7f;
Toomas Soome199767f2015-10-25 00:06:51 +0300925
Toomas Soome5855bf02018-05-19 18:50:26 +0300926 /* XXX a better kludge to set the root disk unit number */
927 if ((nip = getenv("root_disk_unit")) != NULL) {
928 i = strtol(nip, &cp, 0);
929 /* check for parse error */
930 if ((cp != nip) && (*cp == 0))
931 unit = i;
932 }
Toomas Soome199767f2015-10-25 00:06:51 +0300933
Toomas Soome5855bf02018-05-19 18:50:26 +0300934 rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
935 DEBUG("dev is 0x%x\n", rootdev);
936 return (rootdev);
Toomas Soome199767f2015-10-25 00:06:51 +0300937}