Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/i386/libi386/biosdisk.c
34859 views
1
/*-
2
* Copyright (c) 1998 Michael Smith <[email protected]>
3
* Copyright (c) 2012 Andrey V. Elsukov <[email protected]>
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
/*
29
* BIOS disk device handling.
30
*
31
* Ideas and algorithms from:
32
*
33
* - NetBSD libi386/biosdisk.c
34
* - FreeBSD biosboot/disk.c
35
*
36
*/
37
38
#include <sys/disk.h>
39
#include <sys/limits.h>
40
#include <sys/queue.h>
41
#include <stand.h>
42
#include <machine/bootinfo.h>
43
#include <stdarg.h>
44
#include <stdbool.h>
45
46
#include <bootstrap.h>
47
#include <btxv86.h>
48
#include <edd.h>
49
#include "disk.h"
50
#include "libi386.h"
51
52
#define BIOS_NUMDRIVES 0x475
53
#define BIOSDISK_SECSIZE 512
54
#define BUFSIZE (1 * BIOSDISK_SECSIZE)
55
56
#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
61
#define ACDMAJOR 117
62
#define CDMAJOR 15
63
64
/*
65
* INT13 commands
66
*/
67
#define CMD_RESET 0x0000
68
#define CMD_READ_CHS 0x0200
69
#define CMD_WRITE_CHS 0x0300
70
#define CMD_READ_PARAM 0x0800
71
#define CMD_DRIVE_TYPE 0x1500
72
#define CMD_CHECK_EDD 0x4100
73
#define CMD_READ_LBA 0x4200
74
#define CMD_WRITE_LBA 0x4300
75
#define CMD_EXT_PARAM 0x4800
76
#define CMD_CD_GET_STATUS 0x4b01
77
78
#define DISK_BIOS 0x13
79
80
#ifdef DISK_DEBUG
81
#define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
82
#else
83
#define DPRINTF(fmt, args...) ((void)0)
84
#endif
85
86
struct specification_packet {
87
uint8_t sp_size;
88
uint8_t sp_bootmedia;
89
uint8_t sp_drive;
90
uint8_t sp_controller;
91
uint32_t sp_lba;
92
uint16_t sp_devicespec;
93
uint16_t sp_buffersegment;
94
uint16_t sp_loadsegment;
95
uint16_t sp_sectorcount;
96
uint16_t sp_cylsec;
97
uint8_t sp_head;
98
uint8_t sp_dummy[16]; /* Avoid memory corruption */
99
};
100
101
/*
102
* List of BIOS devices, translation from disk unit number to
103
* BIOS unit number.
104
*/
105
typedef struct bdinfo
106
{
107
STAILQ_ENTRY(bdinfo) bd_link; /* link in device list */
108
int bd_unit; /* BIOS unit number */
109
int bd_cyl; /* BIOS geometry */
110
int bd_hds;
111
int bd_sec;
112
int bd_flags;
113
#define BD_MODEINT13 0x0000
114
#define BD_MODEEDD1 0x0001
115
#define BD_MODEEDD3 0x0002
116
#define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3)
117
#define BD_MODEMASK 0x0003
118
#define BD_FLOPPY 0x0004
119
#define BD_CDROM 0x0008
120
#define BD_NO_MEDIA 0x0010
121
int bd_type; /* BIOS 'drive type' (floppy only) */
122
uint16_t bd_sectorsize; /* Sector size */
123
uint64_t bd_sectors; /* Disk size */
124
int bd_open; /* reference counter */
125
void *bd_bcache; /* buffer cache data */
126
} bdinfo_t;
127
128
#define BD_RD 0
129
#define BD_WR 1
130
131
typedef STAILQ_HEAD(bdinfo_list, bdinfo) bdinfo_list_t;
132
static bdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
133
static bdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
134
static bdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
135
136
static void bd_io_workaround(bdinfo_t *);
137
static int bd_io(struct disk_devdesc *, bdinfo_t *, daddr_t, int, caddr_t, int);
138
static bool bd_int13probe(bdinfo_t *);
139
140
static int bd_init(void);
141
static int cd_init(void);
142
static int fd_init(void);
143
static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
144
char *buf, size_t *rsize);
145
static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
146
char *buf, size_t *rsize);
147
static int bd_open(struct open_file *f, ...);
148
static int bd_close(struct open_file *f);
149
static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
150
static int bd_print(int verbose);
151
static int cd_print(int verbose);
152
static int fd_print(int verbose);
153
static void bd_reset_disk(int);
154
static int bd_get_diskinfo_std(struct bdinfo *);
155
156
struct devsw biosfd = {
157
.dv_name = "fd",
158
.dv_type = DEVT_FD,
159
.dv_init = fd_init,
160
.dv_strategy = bd_strategy,
161
.dv_open = bd_open,
162
.dv_close = bd_close,
163
.dv_ioctl = bd_ioctl,
164
.dv_print = fd_print,
165
.dv_cleanup = nullsys,
166
};
167
168
struct devsw bioscd = {
169
.dv_name = "cd",
170
.dv_type = DEVT_CD,
171
.dv_init = cd_init,
172
.dv_strategy = bd_strategy,
173
.dv_open = bd_open,
174
.dv_close = bd_close,
175
.dv_ioctl = bd_ioctl,
176
.dv_print = cd_print,
177
.dv_cleanup = nullsys,
178
};
179
180
struct devsw bioshd = {
181
.dv_name = "disk",
182
.dv_type = DEVT_DISK,
183
.dv_init = bd_init,
184
.dv_strategy = bd_strategy,
185
.dv_open = bd_open,
186
.dv_close = bd_close,
187
.dv_ioctl = bd_ioctl,
188
.dv_print = bd_print,
189
.dv_cleanup = nullsys,
190
.dv_fmtdev = disk_fmtdev,
191
.dv_parsedev = disk_parsedev,
192
};
193
194
static bdinfo_list_t *
195
bd_get_bdinfo_list(struct devsw *dev)
196
{
197
if (dev->dv_type == DEVT_DISK)
198
return (&hdinfo);
199
if (dev->dv_type == DEVT_CD)
200
return (&cdinfo);
201
if (dev->dv_type == DEVT_FD)
202
return (&fdinfo);
203
return (NULL);
204
}
205
206
/* XXX this gets called way way too often, investigate */
207
static bdinfo_t *
208
bd_get_bdinfo(struct devdesc *dev)
209
{
210
bdinfo_list_t *bdi;
211
bdinfo_t *bd = NULL;
212
int unit;
213
214
bdi = bd_get_bdinfo_list(dev->d_dev);
215
if (bdi == NULL)
216
return (bd);
217
218
unit = 0;
219
STAILQ_FOREACH(bd, bdi, bd_link) {
220
if (unit == dev->d_unit)
221
return (bd);
222
unit++;
223
}
224
return (bd);
225
}
226
227
/*
228
* Translate between BIOS device numbers and our private unit numbers.
229
*/
230
int
231
bd_bios2unit(int biosdev)
232
{
233
bdinfo_list_t *bdi[] = { &fdinfo, &cdinfo, &hdinfo, NULL };
234
bdinfo_t *bd;
235
int i, unit;
236
237
DPRINTF("looking for bios device 0x%x", biosdev);
238
for (i = 0; bdi[i] != NULL; i++) {
239
unit = 0;
240
STAILQ_FOREACH(bd, bdi[i], bd_link) {
241
if (bd->bd_unit == biosdev) {
242
DPRINTF("bd unit %d is BIOS device 0x%x", unit,
243
bd->bd_unit);
244
return (unit);
245
}
246
unit++;
247
}
248
}
249
return (-1);
250
}
251
252
int
253
bd_unit2bios(struct i386_devdesc *dev)
254
{
255
bdinfo_list_t *bdi;
256
bdinfo_t *bd;
257
int unit;
258
259
bdi = bd_get_bdinfo_list(dev->dd.d_dev);
260
if (bdi == NULL)
261
return (-1);
262
263
unit = 0;
264
STAILQ_FOREACH(bd, bdi, bd_link) {
265
if (unit == dev->dd.d_unit)
266
return (bd->bd_unit);
267
unit++;
268
}
269
return (-1);
270
}
271
272
/*
273
* Use INT13 AH=15 - Read Drive Type.
274
*/
275
static int
276
fd_count(void)
277
{
278
int drive;
279
280
for (drive = 0; drive < MAXBDDEV; drive++) {
281
bd_reset_disk(drive);
282
283
v86.ctl = V86_FLAGS;
284
v86.addr = DISK_BIOS;
285
v86.eax = CMD_DRIVE_TYPE;
286
v86.edx = drive;
287
v86int();
288
289
if (V86_CY(v86.efl))
290
break;
291
292
if ((v86.eax & 0x300) == 0)
293
break;
294
}
295
296
return (drive);
297
}
298
299
/*
300
* Quiz the BIOS for disk devices, save a little info about them.
301
*/
302
static int
303
fd_init(void)
304
{
305
int unit, numfd;
306
bdinfo_t *bd;
307
308
numfd = fd_count();
309
for (unit = 0; unit < numfd; unit++) {
310
if ((bd = calloc(1, sizeof(*bd))) == NULL)
311
break;
312
313
bd->bd_sectorsize = BIOSDISK_SECSIZE;
314
bd->bd_flags = BD_FLOPPY;
315
bd->bd_unit = unit;
316
317
/* Use std diskinfo for floppy drive */
318
if (bd_get_diskinfo_std(bd) != 0) {
319
free(bd);
320
break;
321
}
322
if (bd->bd_sectors == 0)
323
bd->bd_flags |= BD_NO_MEDIA;
324
325
printf("BIOS drive %c: is %s%d\n", ('A' + unit),
326
biosfd.dv_name, unit);
327
328
STAILQ_INSERT_TAIL(&fdinfo, bd, bd_link);
329
}
330
331
bcache_add_dev(unit);
332
return (0);
333
}
334
335
static int
336
bd_init(void)
337
{
338
int base, unit;
339
bdinfo_t *bd;
340
341
TSENTER();
342
343
base = 0x80;
344
for (unit = 0; unit < *(unsigned char *)PTOV(BIOS_NUMDRIVES); unit++) {
345
/*
346
* Check the BIOS equipment list for number of fixed disks.
347
*/
348
if ((bd = calloc(1, sizeof(*bd))) == NULL)
349
break;
350
bd->bd_unit = base + unit;
351
if (!bd_int13probe(bd)) {
352
free(bd);
353
break;
354
}
355
356
printf("BIOS drive %c: is %s%d\n", ('C' + unit),
357
bioshd.dv_name, unit);
358
359
STAILQ_INSERT_TAIL(&hdinfo, bd, bd_link);
360
}
361
bcache_add_dev(unit);
362
TSEXIT();
363
return (0);
364
}
365
366
/*
367
* We can't quiz, we have to be told what device to use, so this function
368
* doesn't do anything. Instead, the loader calls bc_add() with the BIOS
369
* device number to add.
370
*/
371
static int
372
cd_init(void)
373
{
374
375
return (0);
376
}
377
378
/*
379
* Information from bootable CD-ROM.
380
*/
381
static int
382
bd_get_diskinfo_cd(struct bdinfo *bd)
383
{
384
struct specification_packet bc_sp;
385
int ret = -1;
386
387
(void) memset(&bc_sp, 0, sizeof (bc_sp));
388
/* Set sp_size as per specification. */
389
bc_sp.sp_size = sizeof (bc_sp) - sizeof (bc_sp.sp_dummy);
390
391
v86.ctl = V86_FLAGS;
392
v86.addr = DISK_BIOS;
393
v86.eax = CMD_CD_GET_STATUS;
394
v86.edx = bd->bd_unit;
395
v86.ds = VTOPSEG(&bc_sp);
396
v86.esi = VTOPOFF(&bc_sp);
397
v86int();
398
399
if ((v86.eax & 0xff00) == 0 &&
400
bc_sp.sp_drive == bd->bd_unit) {
401
bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) +
402
((bc_sp.sp_cylsec & 0xff00) >> 8) + 1;
403
bd->bd_sec = bc_sp.sp_cylsec & 0x3f;
404
bd->bd_hds = bc_sp.sp_head + 1;
405
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
406
407
if (bc_sp.sp_bootmedia & 0x0F) {
408
/* Floppy or hard-disk emulation */
409
bd->bd_sectorsize = BIOSDISK_SECSIZE;
410
return (-1);
411
} else {
412
bd->bd_sectorsize = 2048;
413
bd->bd_flags = BD_MODEEDD | BD_CDROM;
414
ret = 0;
415
}
416
}
417
418
/*
419
* If this is the boot_drive, default to non-emulation bootable CD-ROM.
420
*/
421
if (ret != 0 && bd->bd_unit >= 0x88) {
422
bd->bd_cyl = 0;
423
bd->bd_hds = 1;
424
bd->bd_sec = 15;
425
bd->bd_sectorsize = 2048;
426
bd->bd_flags = BD_MODEEDD | BD_CDROM;
427
bd->bd_sectors = 0;
428
ret = 0;
429
}
430
431
/*
432
* Note we can not use bd_get_diskinfo_ext() nor bd_get_diskinfo_std()
433
* here - some systems do get hung with those.
434
*/
435
/*
436
* Still no size? use 7.961GB. The size does not really matter
437
* as long as it is reasonably large to make our reads to pass
438
* the sector count check.
439
*/
440
if (bd->bd_sectors == 0)
441
bd->bd_sectors = 4173824;
442
443
return (ret);
444
}
445
446
int
447
bc_add(int biosdev)
448
{
449
bdinfo_t *bd;
450
int nbcinfo = 0;
451
452
if (!STAILQ_EMPTY(&cdinfo))
453
return (-1);
454
455
if ((bd = calloc(1, sizeof(*bd))) == NULL)
456
return (-1);
457
458
bd->bd_unit = biosdev;
459
if (bd_get_diskinfo_cd(bd) < 0) {
460
free(bd);
461
return (-1);
462
}
463
464
STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link);
465
printf("BIOS CD is cd%d\n", nbcinfo);
466
nbcinfo++;
467
bcache_add_dev(nbcinfo); /* register cd device in bcache */
468
return(0);
469
}
470
471
/*
472
* Return EDD version or 0 if EDD is not supported on this drive.
473
*/
474
static int
475
bd_check_extensions(int unit)
476
{
477
/* do not use ext calls for floppy devices */
478
if (unit < 0x80)
479
return (0);
480
481
/* Determine if we can use EDD with this device. */
482
v86.ctl = V86_FLAGS;
483
v86.addr = DISK_BIOS;
484
v86.eax = CMD_CHECK_EDD;
485
v86.edx = unit;
486
v86.ebx = EDD_QUERY_MAGIC;
487
v86int();
488
489
if (V86_CY(v86.efl) || /* carry set */
490
(v86.ebx & 0xffff) != EDD_INSTALLED) /* signature */
491
return (0);
492
493
/* extended disk access functions (AH=42h-44h,47h,48h) supported */
494
if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
495
return (0);
496
497
return ((v86.eax >> 8) & 0xff);
498
}
499
500
static void
501
bd_reset_disk(int unit)
502
{
503
/* reset disk */
504
v86.ctl = V86_FLAGS;
505
v86.addr = DISK_BIOS;
506
v86.eax = CMD_RESET;
507
v86.edx = unit;
508
v86int();
509
}
510
511
/*
512
* Read CHS info. Return 0 on success, error otherwise.
513
*/
514
static int
515
bd_get_diskinfo_std(struct bdinfo *bd)
516
{
517
bzero(&v86, sizeof(v86));
518
v86.ctl = V86_FLAGS;
519
v86.addr = DISK_BIOS;
520
v86.eax = CMD_READ_PARAM;
521
v86.edx = bd->bd_unit;
522
v86int();
523
524
if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
525
return ((v86.eax & 0xff00) >> 8);
526
527
/* return custom error on absurd sector number */
528
if ((v86.ecx & 0x3f) == 0)
529
return (0x60);
530
531
bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
532
/* Convert max head # -> # of heads */
533
bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
534
bd->bd_sec = v86.ecx & 0x3f;
535
bd->bd_type = v86.ebx;
536
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
537
538
return (0);
539
}
540
541
/*
542
* Read EDD info. Return 0 on success, error otherwise.
543
*
544
* Avoid stack corruption on some systems by adding extra bytes to
545
* params block.
546
*/
547
static int
548
bd_get_diskinfo_ext(struct bdinfo *bd)
549
{
550
struct disk_params {
551
struct edd_params head;
552
struct edd_device_path_v3 device_path;
553
uint8_t dummy[16];
554
} __packed dparams;
555
struct edd_params *params;
556
uint64_t total;
557
558
params = &dparams.head;
559
560
/* Get disk params */
561
bzero(&dparams, sizeof(dparams));
562
params->len = sizeof(struct edd_params_v3);
563
v86.ctl = V86_FLAGS;
564
v86.addr = DISK_BIOS;
565
v86.eax = CMD_EXT_PARAM;
566
v86.edx = bd->bd_unit;
567
v86.ds = VTOPSEG(&dparams);
568
v86.esi = VTOPOFF(&dparams);
569
v86int();
570
571
if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
572
return ((v86.eax & 0xff00) >> 8);
573
574
/*
575
* Sector size must be a multiple of 512 bytes.
576
* An alternate test would be to check power of 2,
577
* powerof2(params.sector_size).
578
* 16K is largest read buffer we can use at this time.
579
*/
580
if (params->sector_size >= 512 &&
581
params->sector_size <= 16384 &&
582
(params->sector_size % BIOSDISK_SECSIZE) == 0)
583
bd->bd_sectorsize = params->sector_size;
584
585
bd->bd_cyl = params->cylinders;
586
bd->bd_hds = params->heads;
587
bd->bd_sec = params->sectors_per_track;
588
589
if (params->sectors != 0) {
590
total = params->sectors;
591
} else {
592
total = (uint64_t)params->cylinders *
593
params->heads * params->sectors_per_track;
594
}
595
bd->bd_sectors = total;
596
597
return (0);
598
}
599
600
/*
601
* Try to detect a device supported by the legacy int13 BIOS
602
*/
603
static bool
604
bd_int13probe(bdinfo_t *bd)
605
{
606
int edd, ret;
607
608
bd->bd_flags &= ~BD_NO_MEDIA;
609
610
if ((bd->bd_flags & BD_CDROM) != 0) {
611
return (bd_get_diskinfo_cd(bd) == 0);
612
}
613
614
edd = bd_check_extensions(bd->bd_unit);
615
if (edd == 0)
616
bd->bd_flags |= BD_MODEINT13;
617
else if (edd < 0x30)
618
bd->bd_flags |= BD_MODEEDD1;
619
else
620
bd->bd_flags |= BD_MODEEDD3;
621
622
/* Default sector size */
623
if (bd->bd_sectorsize == 0)
624
bd->bd_sectorsize = BIOSDISK_SECSIZE;
625
626
/*
627
* Test if the floppy device is present, so we can avoid receiving
628
* bogus information from bd_get_diskinfo_std().
629
*/
630
if (bd->bd_unit < 0x80) {
631
/* reset disk */
632
bd_reset_disk(bd->bd_unit);
633
634
/* Get disk type */
635
v86.ctl = V86_FLAGS;
636
v86.addr = DISK_BIOS;
637
v86.eax = CMD_DRIVE_TYPE;
638
v86.edx = bd->bd_unit;
639
v86int();
640
if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
641
return (false);
642
}
643
644
ret = 1;
645
if (edd != 0)
646
ret = bd_get_diskinfo_ext(bd);
647
if (ret != 0 || bd->bd_sectors == 0)
648
ret = bd_get_diskinfo_std(bd);
649
650
if (ret != 0 && bd->bd_unit < 0x80) {
651
/* Set defaults for 1.44 floppy */
652
bd->bd_cyl = 80;
653
bd->bd_hds = 2;
654
bd->bd_sec = 18;
655
bd->bd_sectors = 2880;
656
/* Since we are there, there most likely is no media */
657
bd->bd_flags |= BD_NO_MEDIA;
658
ret = 0;
659
}
660
661
if (ret != 0) {
662
if (bd->bd_sectors != 0 && edd != 0) {
663
bd->bd_sec = 63;
664
bd->bd_hds = 255;
665
bd->bd_cyl =
666
(bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) /
667
bd->bd_sec * bd->bd_hds;
668
} else {
669
const char *dv_name;
670
671
if ((bd->bd_flags & BD_FLOPPY) != 0)
672
dv_name = biosfd.dv_name;
673
else
674
dv_name = bioshd.dv_name;
675
676
printf("Can not get information about %s unit %#x\n",
677
dv_name, bd->bd_unit);
678
return (false);
679
}
680
}
681
682
if (bd->bd_sec == 0)
683
bd->bd_sec = 63;
684
if (bd->bd_hds == 0)
685
bd->bd_hds = 255;
686
687
if (bd->bd_sectors == 0)
688
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
689
690
DPRINTF("unit 0x%x geometry %d/%d/%d\n", bd->bd_unit, bd->bd_cyl,
691
bd->bd_hds, bd->bd_sec);
692
693
return (true);
694
}
695
696
static int
697
bd_count(bdinfo_list_t *bdi)
698
{
699
bdinfo_t *bd;
700
int i;
701
702
i = 0;
703
STAILQ_FOREACH(bd, bdi, bd_link)
704
i++;
705
return (i);
706
}
707
708
/*
709
* Print information about disks
710
*/
711
static int
712
bd_print_common(struct devsw *dev, bdinfo_list_t *bdi, int verbose)
713
{
714
char line[80];
715
struct disk_devdesc devd;
716
bdinfo_t *bd;
717
int i, ret = 0;
718
char drive;
719
720
if (STAILQ_EMPTY(bdi))
721
return (0);
722
723
printf("%s devices:", dev->dv_name);
724
if ((ret = pager_output("\n")) != 0)
725
return (ret);
726
727
i = -1;
728
STAILQ_FOREACH(bd, bdi, bd_link) {
729
i++;
730
731
switch (dev->dv_type) {
732
case DEVT_FD:
733
drive = 'A';
734
break;
735
case DEVT_CD:
736
drive = 'C' + bd_count(&hdinfo);
737
break;
738
default:
739
drive = 'C';
740
break;
741
}
742
743
snprintf(line, sizeof(line),
744
" %s%d: BIOS drive %c (%s%ju X %u):\n",
745
dev->dv_name, i, drive + i,
746
(bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
747
"no media, " : "",
748
(uintmax_t)bd->bd_sectors,
749
bd->bd_sectorsize);
750
if ((ret = pager_output(line)) != 0)
751
break;
752
753
if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
754
continue;
755
756
if (dev->dv_type != DEVT_DISK)
757
continue;
758
759
devd.dd.d_dev = dev;
760
devd.dd.d_unit = i;
761
devd.d_slice = D_SLICENONE;
762
devd.d_partition = D_PARTNONE;
763
if (disk_open(&devd,
764
bd->bd_sectorsize * bd->bd_sectors,
765
bd->bd_sectorsize) == 0) {
766
snprintf(line, sizeof(line), " %s%d",
767
dev->dv_name, i);
768
ret = disk_print(&devd, line, verbose);
769
disk_close(&devd);
770
if (ret != 0)
771
break;
772
}
773
}
774
return (ret);
775
}
776
777
static int
778
fd_print(int verbose)
779
{
780
return (bd_print_common(&biosfd, &fdinfo, verbose));
781
}
782
783
static int
784
bd_print(int verbose)
785
{
786
return (bd_print_common(&bioshd, &hdinfo, verbose));
787
}
788
789
static int
790
cd_print(int verbose)
791
{
792
return (bd_print_common(&bioscd, &cdinfo, verbose));
793
}
794
795
/*
796
* Read disk size from partition.
797
* This is needed to work around buggy BIOS systems returning
798
* wrong (truncated) disk media size.
799
* During bd_probe() we tested if the multiplication of bd_sectors
800
* would overflow so it should be safe to perform here.
801
*/
802
static uint64_t
803
bd_disk_get_sectors(struct disk_devdesc *dev)
804
{
805
bdinfo_t *bd;
806
struct disk_devdesc disk;
807
uint64_t size;
808
809
bd = bd_get_bdinfo(&dev->dd);
810
if (bd == NULL)
811
return (0);
812
813
disk.dd.d_dev = dev->dd.d_dev;
814
disk.dd.d_unit = dev->dd.d_unit;
815
disk.d_slice = D_SLICENONE;
816
disk.d_partition = D_PARTNONE;
817
disk.d_offset = 0;
818
819
size = bd->bd_sectors * bd->bd_sectorsize;
820
if (disk_open(&disk, size, bd->bd_sectorsize) == 0) {
821
(void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size);
822
disk_close(&disk);
823
}
824
return (size / bd->bd_sectorsize);
825
}
826
827
/*
828
* Attempt to open the disk described by (dev) for use by (f).
829
*
830
* Note that the philosophy here is "give them exactly what
831
* they ask for". This is necessary because being too "smart"
832
* about what the user might want leads to complications.
833
* (eg. given no slice or partition value, with a disk that is
834
* sliced - are they after the first BSD slice, or the DOS
835
* slice before it?)
836
*/
837
static int
838
bd_open(struct open_file *f, ...)
839
{
840
bdinfo_t *bd;
841
struct disk_devdesc *dev;
842
va_list ap;
843
int rc;
844
845
TSENTER();
846
847
va_start(ap, f);
848
dev = va_arg(ap, struct disk_devdesc *);
849
va_end(ap);
850
851
bd = bd_get_bdinfo(&dev->dd);
852
if (bd == NULL)
853
return (EIO);
854
855
if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
856
if (!bd_int13probe(bd))
857
return (EIO);
858
if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
859
return (EIO);
860
}
861
if (bd->bd_bcache == NULL)
862
bd->bd_bcache = bcache_allocate();
863
864
if (bd->bd_open == 0)
865
bd->bd_sectors = bd_disk_get_sectors(dev);
866
bd->bd_open++;
867
868
rc = 0;
869
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
870
rc = disk_open(dev, bd->bd_sectors * bd->bd_sectorsize,
871
bd->bd_sectorsize);
872
if (rc != 0) {
873
bd->bd_open--;
874
if (bd->bd_open == 0) {
875
bcache_free(bd->bd_bcache);
876
bd->bd_bcache = NULL;
877
}
878
}
879
}
880
TSEXIT();
881
return (rc);
882
}
883
884
static int
885
bd_close(struct open_file *f)
886
{
887
struct disk_devdesc *dev;
888
bdinfo_t *bd;
889
int rc = 0;
890
891
dev = (struct disk_devdesc *)f->f_devdata;
892
bd = bd_get_bdinfo(&dev->dd);
893
if (bd == NULL)
894
return (EIO);
895
896
bd->bd_open--;
897
if (bd->bd_open == 0) {
898
bcache_free(bd->bd_bcache);
899
bd->bd_bcache = NULL;
900
}
901
if (dev->dd.d_dev->dv_type == DEVT_DISK)
902
rc = disk_close(dev);
903
return (rc);
904
}
905
906
static int
907
bd_ioctl(struct open_file *f, u_long cmd, void *data)
908
{
909
bdinfo_t *bd;
910
struct disk_devdesc *dev;
911
int rc;
912
913
dev = (struct disk_devdesc *)f->f_devdata;
914
bd = bd_get_bdinfo(&dev->dd);
915
if (bd == NULL)
916
return (EIO);
917
918
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
919
rc = disk_ioctl(dev, cmd, data);
920
if (rc != ENOTTY)
921
return (rc);
922
}
923
924
switch (cmd) {
925
case DIOCGSECTORSIZE:
926
*(uint32_t *)data = bd->bd_sectorsize;
927
break;
928
case DIOCGMEDIASIZE:
929
*(uint64_t *)data = bd->bd_sectors * bd->bd_sectorsize;
930
break;
931
default:
932
return (ENOTTY);
933
}
934
return (0);
935
}
936
937
static int
938
bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
939
char *buf, size_t *rsize)
940
{
941
bdinfo_t *bd;
942
struct bcache_devdata bcd;
943
struct disk_devdesc *dev;
944
daddr_t offset;
945
946
dev = (struct disk_devdesc *)devdata;
947
bd = bd_get_bdinfo(&dev->dd);
948
if (bd == NULL)
949
return (EINVAL);
950
951
bcd.dv_strategy = bd_realstrategy;
952
bcd.dv_devdata = devdata;
953
bcd.dv_cache = bd->bd_bcache;
954
955
offset = 0;
956
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
957
958
offset = dev->d_offset * bd->bd_sectorsize;
959
offset /= BIOSDISK_SECSIZE;
960
}
961
return (bcache_strategy(&bcd, rw, dblk + offset, size,
962
buf, rsize));
963
}
964
965
static int
966
bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
967
char *buf, size_t *rsize)
968
{
969
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
970
bdinfo_t *bd;
971
uint64_t disk_blocks, offset, d_offset;
972
size_t blks, blkoff, bsize, bio_size, rest;
973
caddr_t bbuf = NULL;
974
int rc;
975
976
bd = bd_get_bdinfo(&dev->dd);
977
if (bd == NULL || (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
978
return (EIO);
979
980
/*
981
* First make sure the IO size is a multiple of 512 bytes. While we do
982
* process partial reads below, the strategy mechanism is built
983
* assuming IO is a multiple of 512B blocks. If the request is not
984
* a multiple of 512B blocks, it has to be some sort of bug.
985
*/
986
if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) {
987
printf("bd_strategy: %d bytes I/O not multiple of %d\n",
988
size, BIOSDISK_SECSIZE);
989
return (EIO);
990
}
991
992
DPRINTF("open_disk %p", dev);
993
994
offset = dblk * BIOSDISK_SECSIZE;
995
dblk = offset / bd->bd_sectorsize;
996
blkoff = offset % bd->bd_sectorsize;
997
998
/*
999
* Check the value of the size argument. We do have quite small
1000
* heap (64MB), but we do not know good upper limit, so we check against
1001
* INT_MAX here. This will also protect us against possible overflows
1002
* while translating block count to bytes.
1003
*/
1004
if (size > INT_MAX) {
1005
DPRINTF("too large I/O: %zu bytes", size);
1006
return (EIO);
1007
}
1008
1009
blks = size / bd->bd_sectorsize;
1010
if (blks == 0 || (size % bd->bd_sectorsize) != 0)
1011
blks++;
1012
1013
if (dblk > dblk + blks)
1014
return (EIO);
1015
1016
if (rsize)
1017
*rsize = 0;
1018
1019
/*
1020
* Get disk blocks, this value is either for whole disk or for
1021
* partition.
1022
*/
1023
d_offset = 0;
1024
disk_blocks = 0;
1025
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1026
if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1027
/* DIOCGMEDIASIZE does return bytes. */
1028
disk_blocks /= bd->bd_sectorsize;
1029
}
1030
d_offset = dev->d_offset;
1031
}
1032
if (disk_blocks == 0)
1033
disk_blocks = bd->bd_sectors * (bd->bd_sectorsize /
1034
BIOSDISK_SECSIZE) - d_offset;
1035
1036
/* Validate source block address. */
1037
if (dblk < d_offset || dblk >= d_offset + disk_blocks)
1038
return (EIO);
1039
1040
/*
1041
* Truncate if we are crossing disk or partition end.
1042
*/
1043
if (dblk + blks >= d_offset + disk_blocks) {
1044
blks = d_offset + disk_blocks - dblk;
1045
size = blks * bd->bd_sectorsize;
1046
DPRINTF("short I/O %d", blks);
1047
}
1048
1049
bio_size = min(BIO_BUFFER_SIZE, size);
1050
while (bio_size > bd->bd_sectorsize) {
1051
bbuf = bio_alloc(bio_size);
1052
if (bbuf != NULL)
1053
break;
1054
bio_size -= bd->bd_sectorsize;
1055
}
1056
if (bbuf == NULL) {
1057
bio_size = V86_IO_BUFFER_SIZE;
1058
if (bio_size / bd->bd_sectorsize == 0)
1059
panic("BUG: Real mode buffer is too small");
1060
1061
/* Use alternate 4k buffer */
1062
bbuf = PTOV(V86_IO_BUFFER);
1063
}
1064
rest = size;
1065
rc = 0;
1066
while (blks > 0) {
1067
int x = min(blks, bio_size / bd->bd_sectorsize);
1068
1069
switch (rw & F_MASK) {
1070
case F_READ:
1071
DPRINTF("read %d from %lld to %p", x, dblk, buf);
1072
bsize = bd->bd_sectorsize * x - blkoff;
1073
if (rest < bsize)
1074
bsize = rest;
1075
1076
if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD)) != 0) {
1077
rc = EIO;
1078
goto error;
1079
}
1080
1081
bcopy(bbuf + blkoff, buf, bsize);
1082
break;
1083
case F_WRITE :
1084
DPRINTF("write %d from %lld to %p", x, dblk, buf);
1085
if (blkoff != 0) {
1086
/*
1087
* We got offset to sector, read 1 sector to
1088
* bbuf.
1089
*/
1090
x = 1;
1091
bsize = bd->bd_sectorsize - blkoff;
1092
bsize = min(bsize, rest);
1093
rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD);
1094
} else if (rest < bd->bd_sectorsize) {
1095
/*
1096
* The remaining block is not full
1097
* sector. Read 1 sector to bbuf.
1098
*/
1099
x = 1;
1100
bsize = rest;
1101
rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD);
1102
} else {
1103
/* We can write full sector(s). */
1104
bsize = bd->bd_sectorsize * x;
1105
}
1106
/*
1107
* Put your Data In, Put your Data out,
1108
* Put your Data In, and shake it all about
1109
*/
1110
bcopy(buf, bbuf + blkoff, bsize);
1111
if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_WR)) != 0) {
1112
rc = EIO;
1113
goto error;
1114
}
1115
1116
break;
1117
default:
1118
/* DO NOTHING */
1119
rc = EROFS;
1120
goto error;
1121
}
1122
1123
blkoff = 0;
1124
buf += bsize;
1125
rest -= bsize;
1126
blks -= x;
1127
dblk += x;
1128
}
1129
1130
if (rsize != NULL)
1131
*rsize = size;
1132
error:
1133
if (bbuf != PTOV(V86_IO_BUFFER))
1134
bio_free(bbuf, bio_size);
1135
return (rc);
1136
}
1137
1138
static int
1139
bd_edd_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest,
1140
int dowrite)
1141
{
1142
static struct edd_packet packet;
1143
1144
TSENTER();
1145
1146
packet.len = sizeof(struct edd_packet);
1147
packet.count = blks;
1148
packet.off = VTOPOFF(dest);
1149
packet.seg = VTOPSEG(dest);
1150
packet.lba = dblk;
1151
v86.ctl = V86_FLAGS;
1152
v86.addr = DISK_BIOS;
1153
if (dowrite == BD_WR)
1154
v86.eax = CMD_WRITE_LBA; /* maybe Write with verify 0x4302? */
1155
else
1156
v86.eax = CMD_READ_LBA;
1157
v86.edx = bd->bd_unit;
1158
v86.ds = VTOPSEG(&packet);
1159
v86.esi = VTOPOFF(&packet);
1160
v86int();
1161
if (V86_CY(v86.efl))
1162
return (v86.eax >> 8);
1163
1164
TSEXIT();
1165
return (0);
1166
}
1167
1168
static int
1169
bd_chs_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest,
1170
int dowrite)
1171
{
1172
uint32_t x, bpc, cyl, hd, sec;
1173
1174
TSENTER();
1175
1176
bpc = bd->bd_sec * bd->bd_hds; /* blocks per cylinder */
1177
x = dblk;
1178
cyl = x / bpc; /* block # / blocks per cylinder */
1179
x %= bpc; /* block offset into cylinder */
1180
hd = x / bd->bd_sec; /* offset / blocks per track */
1181
sec = x % bd->bd_sec; /* offset into track */
1182
1183
/* correct sector number for 1-based BIOS numbering */
1184
sec++;
1185
1186
if (cyl > 1023) {
1187
/* CHS doesn't support cylinders > 1023. */
1188
return (1);
1189
}
1190
1191
v86.ctl = V86_FLAGS;
1192
v86.addr = DISK_BIOS;
1193
if (dowrite == BD_WR)
1194
v86.eax = CMD_WRITE_CHS | blks;
1195
else
1196
v86.eax = CMD_READ_CHS | blks;
1197
v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
1198
v86.edx = (hd << 8) | bd->bd_unit;
1199
v86.es = VTOPSEG(dest);
1200
v86.ebx = VTOPOFF(dest);
1201
v86int();
1202
if (V86_CY(v86.efl))
1203
return (v86.eax >> 8);
1204
TSEXIT();
1205
return (0);
1206
}
1207
1208
static void
1209
bd_io_workaround(bdinfo_t *bd)
1210
{
1211
uint8_t buf[8 * 1024];
1212
1213
bd_edd_io(bd, 0xffffffff, 1, (caddr_t)buf, BD_RD);
1214
}
1215
1216
static int
1217
bd_io(struct disk_devdesc *dev, bdinfo_t *bd, daddr_t dblk, int blks,
1218
caddr_t dest, int dowrite)
1219
{
1220
int result, retry;
1221
1222
TSENTER();
1223
1224
/* Just in case some idiot actually tries to read/write -1 blocks... */
1225
if (blks < 0)
1226
return (-1);
1227
1228
/*
1229
* Workaround for a problem with some HP ProLiant BIOS failing to work
1230
* out the boot disk after installation. hrs and kuriyama discovered
1231
* this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and
1232
* discovered that an int13h call seems to cause a buffer overrun in
1233
* the bios. The problem is alleviated by doing an extra read before
1234
* the buggy read. It is not immediately known whether other models
1235
* are similarly affected.
1236
* Loop retrying the operation a couple of times. The BIOS
1237
* may also retry.
1238
*/
1239
if (dowrite == BD_RD && dblk >= 0x100000000)
1240
bd_io_workaround(bd);
1241
for (retry = 0; retry < 3; retry++) {
1242
if (bd->bd_flags & BD_MODEEDD)
1243
result = bd_edd_io(bd, dblk, blks, dest, dowrite);
1244
else
1245
result = bd_chs_io(bd, dblk, blks, dest, dowrite);
1246
1247
if (result == 0) {
1248
if (bd->bd_flags & BD_NO_MEDIA)
1249
bd->bd_flags &= ~BD_NO_MEDIA;
1250
break;
1251
}
1252
1253
bd_reset_disk(bd->bd_unit);
1254
1255
/*
1256
* Error codes:
1257
* 20h controller failure
1258
* 31h no media in drive (IBM/MS INT 13 extensions)
1259
* 80h no media in drive, VMWare (Fusion)
1260
* There is no reason to repeat the IO with errors above.
1261
*/
1262
if (result == 0x20 || result == 0x31 || result == 0x80) {
1263
bd->bd_flags |= BD_NO_MEDIA;
1264
break;
1265
}
1266
}
1267
1268
if (result != 0 && (bd->bd_flags & BD_NO_MEDIA) == 0) {
1269
if (dowrite == BD_WR) {
1270
printf("%s%d: Write %d sector(s) from %p (0x%x) "
1271
"to %lld: 0x%x\n", dev->dd.d_dev->dv_name,
1272
dev->dd.d_unit, blks, dest, VTOP(dest), dblk,
1273
result);
1274
} else {
1275
printf("%s%d: Read %d sector(s) from %lld to %p "
1276
"(0x%x): 0x%x\n", dev->dd.d_dev->dv_name,
1277
dev->dd.d_unit, blks, dblk, dest, VTOP(dest),
1278
result);
1279
}
1280
}
1281
1282
TSEXIT();
1283
1284
return (result);
1285
}
1286
1287
/*
1288
* Return a suitable dev_t value for (dev).
1289
*
1290
* In the case where it looks like (dev) is a SCSI disk, we allow the number of
1291
* IDE disks to be specified in $num_ide_disks. There should be a Better Way.
1292
*/
1293
int
1294
bd_getdev(struct i386_devdesc *d)
1295
{
1296
struct disk_devdesc *dev;
1297
bdinfo_t *bd;
1298
int biosdev;
1299
int major;
1300
int rootdev;
1301
char *nip, *cp;
1302
int i, unit, slice, partition;
1303
1304
/* XXX: Assume partition 'a'. */
1305
slice = 0;
1306
partition = 0;
1307
1308
dev = (struct disk_devdesc *)d;
1309
bd = bd_get_bdinfo(&dev->dd);
1310
if (bd == NULL)
1311
return (-1);
1312
1313
biosdev = bd_unit2bios(d);
1314
DPRINTF("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
1315
if (biosdev == -1) /* not a BIOS device */
1316
return (-1);
1317
1318
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1319
if (disk_open(dev, bd->bd_sectors * bd->bd_sectorsize,
1320
bd->bd_sectorsize) != 0) /* oops, not a viable device */
1321
return (-1);
1322
else
1323
disk_close(dev);
1324
slice = dev->d_slice + 1;
1325
partition = dev->d_partition;
1326
}
1327
1328
if (biosdev < 0x80) {
1329
/* floppy (or emulated floppy) or ATAPI device */
1330
if (bd->bd_type == DT_ATAPI) {
1331
/* is an ATAPI disk */
1332
major = WFDMAJOR;
1333
} else {
1334
/* is a floppy disk */
1335
major = FDMAJOR;
1336
}
1337
} else {
1338
/* assume an IDE disk */
1339
major = WDMAJOR;
1340
}
1341
/* default root disk unit number */
1342
unit = biosdev & 0x7f;
1343
1344
if (dev->dd.d_dev->dv_type == DEVT_CD) {
1345
/*
1346
* XXX: Need to examine device spec here to figure out if
1347
* SCSI or ATAPI. No idea on how to figure out device number.
1348
* All we can really pass to the kernel is what bus and device
1349
* on which bus we were booted from, which dev_t isn't well
1350
* suited to since those number don't match to unit numbers
1351
* very well. We may just need to engage in a hack where
1352
* we pass -C to the boot args if we are the boot device.
1353
*/
1354
major = ACDMAJOR;
1355
unit = 0; /* XXX */
1356
}
1357
1358
/* XXX a better kludge to set the root disk unit number */
1359
if ((nip = getenv("root_disk_unit")) != NULL) {
1360
i = strtol(nip, &cp, 0);
1361
/* check for parse error */
1362
if ((cp != nip) && (*cp == 0))
1363
unit = i;
1364
}
1365
1366
rootdev = MAKEBOOTDEV(major, slice, unit, partition);
1367
DPRINTF("dev is 0x%x\n", rootdev);
1368
return (rootdev);
1369
}
1370
1371