Deleted Added
full compact
biosdisk.c (39724) biosdisk.c (39896)
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: biosdisk.c,v 1.7 1998/09/28 20:07:39 peter Exp $
26 * $Id: biosdisk.c,v 1.8 1998/09/28 20:08:34 peter Exp $
27 */
28
29/*
30 * BIOS disk device handling.
31 *
32 * Ideas and algorithms from:
33 *
34 * - NetBSD libi386/biosdisk.c
35 * - FreeBSD biosboot/disk.c
36 *
37 * XXX Todo: add bad144 support.
38 */
39
40#include <stand.h>
41
42#include <sys/disklabel.h>
43#include <sys/diskslice.h>
27 */
28
29/*
30 * BIOS disk device handling.
31 *
32 * Ideas and algorithms from:
33 *
34 * - NetBSD libi386/biosdisk.c
35 * - FreeBSD biosboot/disk.c
36 *
37 * XXX Todo: add bad144 support.
38 */
39
40#include <stand.h>
41
42#include <sys/disklabel.h>
43#include <sys/diskslice.h>
44#include <sys/reboot.h>
44
45#include <bootstrap.h>
46#include <btxv86.h>
47#include "libi386.h"
48
49#define BIOSDISK_SECSIZE 512
50#define BUFSIZE (1 * BIOSDISK_SECSIZE)
51#define MAXBDDEV MAXDEV
52
45
46#include <bootstrap.h>
47#include <btxv86.h>
48#include "libi386.h"
49
50#define BIOSDISK_SECSIZE 512
51#define BUFSIZE (1 * BIOSDISK_SECSIZE)
52#define MAXBDDEV MAXDEV
53
54#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */
55#define WDMAJOR 0 /* major numbers for devices we frontend for */
56#define WFDMAJOR 1
57#define FDMAJOR 2
58#define DAMAJOR 4
59
53#ifdef DISK_DEBUG
54# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
55#else
56# define DEBUG(fmt, args...)
57#endif
58
59
60struct open_disk {
61 int od_dkunit; /* disk unit number */
62 int od_unit; /* BIOS unit number */
63 int od_cyl; /* BIOS geometry */
64 int od_hds;
65 int od_sec;
66 int od_boff; /* block offset from beginning of BIOS disk */
67 int od_flags;
68#define BD_MODEMASK 0x3
69#define BD_MODEINT13 0x0
70#define BD_MODEEDD1 0x1
71#define BD_MODEEDD3 0x2
72#define BD_FLOPPY (1<<2)
60#ifdef DISK_DEBUG
61# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
62#else
63# define DEBUG(fmt, args...)
64#endif
65
66
67struct open_disk {
68 int od_dkunit; /* disk unit number */
69 int od_unit; /* BIOS unit number */
70 int od_cyl; /* BIOS geometry */
71 int od_hds;
72 int od_sec;
73 int od_boff; /* block offset from beginning of BIOS disk */
74 int od_flags;
75#define BD_MODEMASK 0x3
76#define BD_MODEINT13 0x0
77#define BD_MODEEDD1 0x1
78#define BD_MODEEDD3 0x2
79#define BD_FLOPPY (1<<2)
73 u_char od_buf[BUFSIZE]; /* transfer buffer (do we want/need this?) */
80 struct disklabel od_disklabel;
81 struct dos_partition od_parttab;
82#define BD_LABELOK (1<<3)
83#define BD_PARTTABOK (1<<4)
74};
75
84};
85
86/*
87 * List of BIOS devices, translation from disk unit number to
88 * BIOS unit number.
89 */
90static struct bdinfo
91{
92 int bd_unit; /* BIOS unit number */
93 int bd_flags;
94 int bd_type; /* BIOS 'drive type' (floppy only) */
95} bdinfo [MAXBDDEV];
96static int nbdinfo = 0;
97
76static int bd_getgeom(struct open_disk *od);
77static int bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest);
78
98static int bd_getgeom(struct open_disk *od);
99static int bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest);
100
79static int bd_edd3probe(int unit);
80static int bd_edd1probe(int unit);
81static int bd_int13probe(int unit);
101static int bd_int13probe(struct bdinfo *bd);
82
83static int bd_init(void);
84static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize);
85static int bd_open(struct open_file *f, void *vdev);
86static int bd_close(struct open_file *f);
87
88struct devsw biosdisk = {
89 "disk",
90 DEVT_DISK,
91 bd_init,
92 bd_strategy,
93 bd_open,
94 bd_close,
95 noioctl
96};
97
102
103static int bd_init(void);
104static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize);
105static int bd_open(struct open_file *f, void *vdev);
106static int bd_close(struct open_file *f);
107
108struct devsw biosdisk = {
109 "disk",
110 DEVT_DISK,
111 bd_init,
112 bd_strategy,
113 bd_open,
114 bd_close,
115 noioctl
116};
117
118static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
119static void bd_closedisk(struct open_disk *od);
120
98/*
121/*
99 * List of BIOS devices, translation from disk unit number to
100 * BIOS unit number.
122 * Translate between BIOS device numbers and our private unit numbers.
101 */
123 */
102static struct
124int
125bd_bios2unit(int biosdev)
103{
126{
104 int bd_unit; /* BIOS unit number */
105 int bd_flags;
106} bdinfo [MAXBDDEV];
107static int nbdinfo = 0;
127 int i;
128
129 for (i = 0; i < nbdinfo; i++)
130 if (bdinfo[i].bd_unit == biosdev)
131 return(i);
132 return(-1);
133}
108
134
135int
136bd_unit2bios(int unit)
137{
138 if ((unit >= 0) && (unit < nbdinfo))
139 return(bdinfo[unit].bd_unit);
140 return(-1);
141}
142
109/*
110 * Quiz the BIOS for disk devices, save a little info about them.
111 *
112 * XXX should we be consulting the BIOS equipment list, specifically
113 * the value at 0x475?
114 */
115static int
116bd_init(void)
117{
118 int base, unit;
119
120 /* sequence 0, 0x80 */
121 for (base = 0; base <= 0x80; base += 0x80) {
122 for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
143/*
144 * Quiz the BIOS for disk devices, save a little info about them.
145 *
146 * XXX should we be consulting the BIOS equipment list, specifically
147 * the value at 0x475?
148 */
149static int
150bd_init(void)
151{
152 int base, unit;
153
154 /* sequence 0, 0x80 */
155 for (base = 0; base <= 0x80; base += 0x80) {
156 for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
123 bdinfo[nbdinfo].bd_unit = -1;
157 bdinfo[nbdinfo].bd_unit = unit;
124 bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0;
158 bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0;
125
126 if (bd_edd3probe(unit)) {
127 bdinfo[nbdinfo].bd_flags |= BD_MODEEDD3;
128 } else if (bd_edd1probe(unit)) {
129 bdinfo[nbdinfo].bd_flags |= BD_MODEEDD1;
130 } else if (bd_int13probe(unit)) {
131 bdinfo[nbdinfo].bd_flags |= BD_MODEINT13;
132 } else {
159
160 /* XXX add EDD probes */
161 if (!bd_int13probe(&bdinfo[nbdinfo]))
133 break;
162 break;
134 }
163
135 /* XXX we need "disk aliases" to make this simpler */
136 printf("BIOS drive %c: is disk%d\n",
137 (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo);
138 bdinfo[nbdinfo].bd_unit = unit;
139 nbdinfo++;
140 }
141 }
142 return(0);
143}
144
164 /* XXX we need "disk aliases" to make this simpler */
165 printf("BIOS drive %c: is disk%d\n",
166 (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo);
167 bdinfo[nbdinfo].bd_unit = unit;
168 nbdinfo++;
169 }
170 }
171 return(0);
172}
173
145/*
146 * Try to detect a device supported by an Enhanced Disk Drive 3.0-compliant BIOS
147 */
148static int
149bd_edd3probe(int unit)
150{
151 return(0); /* XXX not implemented yet */
152}
153
154/*
155 * Try to detect a device supported by an Enhanced Disk Drive 1.1-compliant BIOS
156 */
157static int
158bd_edd1probe(int unit)
159{
160 return(0); /* XXX not implemented yet */
161}
162
163/*
164 * Try to detect a device supported by the legacy int13 BIOS
165 */
166
167static int
174/*
175 * Try to detect a device supported by the legacy int13 BIOS
176 */
177
178static int
168bd_int13probe(int unit)
179bd_int13probe(struct bdinfo *bd)
169{
180{
181
170 v86.ctl = V86_FLAGS;
171 v86.addr = 0x13;
172 v86.eax = 0x800;
182 v86.ctl = V86_FLAGS;
183 v86.addr = 0x13;
184 v86.eax = 0x800;
173 v86.edx = unit;
185 v86.edx = bd->bd_unit;
174 v86int();
175
186 v86int();
187
176 if (!(v86.efl & 0x1) && /* carry clear */
177 ((v86.edx & 0xff) > (unit & 0x7f))) /* unit # OK */
188 if (!(v86.efl & 0x1) && /* carry clear */
189 ((v86.edx & 0xff) > (bd->bd_unit & 0x7f))) { /* unit # OK */
190 bd->bd_flags |= BD_MODEINT13;
191 bd->bd_type = v86.ebx & 0xff;
178 return(1);
192 return(1);
193 }
179 return(0);
180}
181
182/*
183 * Attempt to open the disk described by (dev) for use by (f).
184 *
185 * Note that the philosophy here is "give them exactly what
186 * they ask for". This is necessary because being too "smart"
187 * about what the user might want leads to complications.
188 * (eg. given no slice or partition value, with a disk that is
189 * sliced - are they after the first BSD slice, or the DOS
190 * slice before it?)
191 */
192static int
193bd_open(struct open_file *f, void *vdev)
194{
195 struct i386_devdesc *dev = (struct i386_devdesc *)vdev;
194 return(0);
195}
196
197/*
198 * Attempt to open the disk described by (dev) for use by (f).
199 *
200 * Note that the philosophy here is "give them exactly what
201 * they ask for". This is necessary because being too "smart"
202 * about what the user might want leads to complications.
203 * (eg. given no slice or partition value, with a disk that is
204 * sliced - are they after the first BSD slice, or the DOS
205 * slice before it?)
206 */
207static int
208bd_open(struct open_file *f, void *vdev)
209{
210 struct i386_devdesc *dev = (struct i386_devdesc *)vdev;
196 struct dos_partition *dptr;
197 struct open_disk *od;
211 struct open_disk *od;
212 int error;
213
214 if ((error = bd_opendisk(&od, dev)))
215 return(error);
216
217 /*
218 * Save our context
219 */
220 ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
221 DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
222 return(0);
223}
224
225static int
226bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
227{
228 struct dos_partition *dptr;
198 struct disklabel *lp;
229 struct disklabel *lp;
230 struct open_disk *od;
199 int sector, slice, i;
200 int error;
231 int sector, slice, i;
232 int error;
233 u_char buf[BUFSIZE];
201
202 if (dev->d_kind.biosdisk.unit >= nbdinfo) {
203 DEBUG("attempt to open nonexistent disk");
204 return(ENXIO);
205 }
206
207 od = (struct open_disk *)malloc(sizeof(struct open_disk));
208 if (!od) {
209 DEBUG("no memory");
210 return (ENOMEM);
211 }
212
213 /* Look up BIOS unit number, intialise open_disk structure */
214 od->od_dkunit = dev->d_kind.biosdisk.unit;
215 od->od_unit = bdinfo[od->od_dkunit].bd_unit;
216 od->od_flags = bdinfo[od->od_dkunit].bd_flags;
217 od->od_boff = 0;
218 error = 0;
219 DEBUG("open '%s', unit 0x%x slice %d partition %c",
220 i386_fmtdev(dev), dev->d_kind.biosdisk.unit,
221 dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
222
223 /* Get geometry for this open (removable device may have changed) */
224 if (bd_getgeom(od)) {
225 DEBUG("can't get geometry");
226 error = ENXIO;
227 goto out;
228 }
229
230 /*
231 * Following calculations attempt to determine the correct value
232 * for d->od_boff by looking for the slice and partition specified,
233 * or searching for reasonable defaults.
234 */
235
236 /*
237 * Find the slice in the DOS slice table.
238 */
234
235 if (dev->d_kind.biosdisk.unit >= nbdinfo) {
236 DEBUG("attempt to open nonexistent disk");
237 return(ENXIO);
238 }
239
240 od = (struct open_disk *)malloc(sizeof(struct open_disk));
241 if (!od) {
242 DEBUG("no memory");
243 return (ENOMEM);
244 }
245
246 /* Look up BIOS unit number, intialise open_disk structure */
247 od->od_dkunit = dev->d_kind.biosdisk.unit;
248 od->od_unit = bdinfo[od->od_dkunit].bd_unit;
249 od->od_flags = bdinfo[od->od_dkunit].bd_flags;
250 od->od_boff = 0;
251 error = 0;
252 DEBUG("open '%s', unit 0x%x slice %d partition %c",
253 i386_fmtdev(dev), dev->d_kind.biosdisk.unit,
254 dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
255
256 /* Get geometry for this open (removable device may have changed) */
257 if (bd_getgeom(od)) {
258 DEBUG("can't get geometry");
259 error = ENXIO;
260 goto out;
261 }
262
263 /*
264 * Following calculations attempt to determine the correct value
265 * for d->od_boff by looking for the slice and partition specified,
266 * or searching for reasonable defaults.
267 */
268
269 /*
270 * Find the slice in the DOS slice table.
271 */
239 if (bd_read(od, 0, 1, od->od_buf)) {
272 if (bd_read(od, 0, 1, buf)) {
240 DEBUG("error reading MBR");
241 error = EIO;
242 goto out;
243 }
244
245 /*
246 * Check the slice table magic.
247 */
273 DEBUG("error reading MBR");
274 error = EIO;
275 goto out;
276 }
277
278 /*
279 * Check the slice table magic.
280 */
248 if ((od->od_buf[0x1fe] != 0x55) || (od->od_buf[0x1ff] != 0xaa)) {
281 if ((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) {
249 /* If a slice number was explicitly supplied, this is an error */
250 if (dev->d_kind.biosdisk.slice > 0) {
251 DEBUG("no slice table/MBR (no magic)");
252 error = ENOENT;
253 goto out;
254 }
255 sector = 0;
256 goto unsliced; /* may be a floppy */
257 }
282 /* If a slice number was explicitly supplied, this is an error */
283 if (dev->d_kind.biosdisk.slice > 0) {
284 DEBUG("no slice table/MBR (no magic)");
285 error = ENOENT;
286 goto out;
287 }
288 sector = 0;
289 goto unsliced; /* may be a floppy */
290 }
258 dptr = (struct dos_partition *) & od->od_buf[DOSPARTOFF];
291 bcopy(buf + DOSPARTOFF, &od->od_parttab, sizeof(struct dos_partition));
292 dptr = &od->od_parttab;
293 od->od_flags |= BD_PARTTABOK;
259
260 /*
261 * XXX No support here for 'extended' slices
262 */
263 if (dev->d_kind.biosdisk.slice <= 0) {
264 /*
265 * Search for the first FreeBSD slice; this also works on "unsliced"
266 * disks, as they contain a "historically bogus" MBR.
267 */
268 for (i = 0; i < NDOSPART; i++, dptr++)
269 if (dptr->dp_typ == DOSPTYP_386BSD) {
270 sector = dptr->dp_start;
271 break;
272 }
273 /* Did we find something? */
274 if (sector == -1) {
275 error = ENOENT;
276 goto out;
277 }
278 } else {
279 /*
280 * Accept the supplied slice number unequivocally (we may be looking
281 * for a DOS partition) if we can handle it.
282 */
283 if ((dev->d_kind.biosdisk.slice > NDOSPART) || (dev->d_kind.biosdisk.slice < 1)) {
284 error = ENOENT;
285 goto out;
286 }
287 dptr += (dev->d_kind.biosdisk.slice - 1);
288 sector = dptr->dp_start;
289 }
290 unsliced:
291 /*
292 * Now we have the slice, look for the partition in the disklabel if we have
293 * a partition to start with.
294 *
295 * XXX we might want to check the label checksum.
296 */
297 if (dev->d_kind.biosdisk.partition < 0) {
298 od->od_boff = sector; /* no partition, must be after the slice */
299 DEBUG("opening raw slice");
300 } else {
301
294
295 /*
296 * XXX No support here for 'extended' slices
297 */
298 if (dev->d_kind.biosdisk.slice <= 0) {
299 /*
300 * Search for the first FreeBSD slice; this also works on "unsliced"
301 * disks, as they contain a "historically bogus" MBR.
302 */
303 for (i = 0; i < NDOSPART; i++, dptr++)
304 if (dptr->dp_typ == DOSPTYP_386BSD) {
305 sector = dptr->dp_start;
306 break;
307 }
308 /* Did we find something? */
309 if (sector == -1) {
310 error = ENOENT;
311 goto out;
312 }
313 } else {
314 /*
315 * Accept the supplied slice number unequivocally (we may be looking
316 * for a DOS partition) if we can handle it.
317 */
318 if ((dev->d_kind.biosdisk.slice > NDOSPART) || (dev->d_kind.biosdisk.slice < 1)) {
319 error = ENOENT;
320 goto out;
321 }
322 dptr += (dev->d_kind.biosdisk.slice - 1);
323 sector = dptr->dp_start;
324 }
325 unsliced:
326 /*
327 * Now we have the slice, look for the partition in the disklabel if we have
328 * a partition to start with.
329 *
330 * XXX we might want to check the label checksum.
331 */
332 if (dev->d_kind.biosdisk.partition < 0) {
333 od->od_boff = sector; /* no partition, must be after the slice */
334 DEBUG("opening raw slice");
335 } else {
336
302 if (bd_read(od, sector + LABELSECTOR, 1, od->od_buf)) {
337 if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
303 DEBUG("error reading disklabel");
304 error = EIO;
305 goto out;
306 }
338 DEBUG("error reading disklabel");
339 error = EIO;
340 goto out;
341 }
307 lp = (struct disklabel *) (od->od_buf + LABELOFFSET);
342 DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
343 bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
344 lp = &od->od_disklabel;
345 od->od_flags |= BD_LABELOK;
308
309 if (lp->d_magic != DISKMAGIC) {
310 DEBUG("no disklabel");
311 error = ENOENT;
312 goto out;
313 }
314 if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
315 DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
316 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
317 error = EPART;
318 goto out;
319
320 }
321
322 /* Complain if the partition type is wrong */
323 if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
324 !(od->od_flags & BD_FLOPPY)) /* Floppies often have bogus fstype */
325 DEBUG("warning, partition marked as unused");
326
327 od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset;
328 }
346
347 if (lp->d_magic != DISKMAGIC) {
348 DEBUG("no disklabel");
349 error = ENOENT;
350 goto out;
351 }
352 if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
353 DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
354 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
355 error = EPART;
356 goto out;
357
358 }
359
360 /* Complain if the partition type is wrong */
361 if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
362 !(od->od_flags & BD_FLOPPY)) /* Floppies often have bogus fstype */
363 DEBUG("warning, partition marked as unused");
364
365 od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset;
366 }
329 /*
330 * Save our context
331 */
332 ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
333 DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
334
367
335 out:
368 out:
336 if (error)
369 if (error) {
337 free(od);
370 free(od);
371 } else {
372 *odp = od; /* return the open disk */
373 }
338 return(error);
339}
340
374 return(error);
375}
376
377
341static int
342bd_close(struct open_file *f)
343{
344 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
345
378static int
379bd_close(struct open_file *f)
380{
381 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
382
383 bd_closedisk(od);
384 return(0);
385}
386
387static void
388bd_closedisk(struct open_disk *od)
389{
346 DEBUG("open_disk %p", od);
347#if 0
348 /* XXX is this required? (especially if disk already open...) */
349 if (od->od_flags & BD_FLOPPY)
350 delay(3000000);
351#endif
352 free(od);
390 DEBUG("open_disk %p", od);
391#if 0
392 /* XXX is this required? (especially if disk already open...) */
393 if (od->od_flags & BD_FLOPPY)
394 delay(3000000);
395#endif
396 free(od);
353 return(0);
354}
355
356static int
357bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
358{
359 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
360 int blks;
361#ifdef BD_SUPPORT_FRAGS
362 char fragbuf[BIOSDISK_SECSIZE];
363 size_t fragsize;
364
365 fragsize = size % BIOSDISK_SECSIZE;
366#else
367 if (size % BIOSDISK_SECSIZE)
368 panic("bd_strategy: %d bytes I/O not multiple of block size", size);
369#endif
370
371 DEBUG("open_disk %p", od);
372
373 if (rw != F_READ)
374 return(EROFS);
375
376
377 blks = size / BIOSDISK_SECSIZE;
378 DEBUG("read %d from %d+%d to %p", blks, od->od_boff, dblk, buf);
379
380 if (rsize)
381 *rsize = 0;
382 if (blks && bd_read(od, dblk + od->od_boff, blks, buf)) {
383 DEBUG("read error");
384 return (EIO);
385 }
386#ifdef BD_SUPPORT_FRAGS
387 DEBUG("bd_strategy: frag read %d from %d+%d+d to %p\n",
388 fragsize, od->od_boff, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
389 if (fragsize && bd_read(od, dblk + od->od_boff + blks, 1, fragsize)) {
390 DEBUG("frag read error");
391 return(EIO);
392 }
393 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
394#endif
395 if (rsize)
396 *rsize = size;
397 return (0);
398}
399
400/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
401#define FLOPPY_BOUNCEBUF 18
402
403static int
404bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
405{
406 int x, bpc, cyl, hd, sec, result, resid, cnt, retry, maxfer;
407 caddr_t p, xp, bbuf, breg;
408
409 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */
410 resid = blks;
411 p = dest;
412
413 /* Decide whether we have to bounce */
414 if ((od->od_unit < 0x80) &&
415 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
416
417 /*
418 * There is a 64k physical boundary somewhere in the destination buffer, so we have
419 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we
420 * need to. Use the bottom half unless there is a break there, in which case we
421 * use the top half.
422 */
423 x = min(FLOPPY_BOUNCEBUF, blks);
424 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE);
425 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
426 breg = bbuf;
427 } else {
428 breg = bbuf + x * BIOSDISK_SECSIZE;
429 }
430 maxfer = x; /* limit transfers to bounce region size */
431 } else {
432 bbuf = NULL;
433 maxfer = 0;
434 }
435
436 while (resid > 0) {
437 x = dblk;
438 cyl = x / bpc; /* block # / blocks per cylinder */
439 x %= bpc; /* block offset into cylinder */
440 hd = x / od->od_sec; /* offset / blocks per track */
441 sec = x % od->od_sec; /* offset into track */
442
443 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
444 x = min(od->od_sec - sec, resid);
445 if (maxfer > 0)
446 x = min(x, maxfer); /* fit bounce buffer */
447
448 /* where do we transfer to? */
449 xp = bbuf == NULL ? p : breg;
450
451 /* correct sector number for 1-based BIOS numbering */
452 sec++;
453
454 /* Loop retrying the operation a couple of times. The BIOS may also retry. */
455 for (retry = 0; retry < 3; retry++) {
456 /* if retrying, reset the drive */
457 if (retry > 0) {
458 v86.ctl = V86_FLAGS;
459 v86.addr = 0x13;
460 v86.eax = 0;
461 v86.edx = od->od_unit;
462 v86int();
463 }
464
465 /* build request XXX support EDD requests too */
466 v86.ctl = V86_FLAGS;
467 v86.addr = 0x13;
468 v86.eax = 0x200 | x;
469 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
470 v86.edx = (hd << 8) | od->od_unit;
471 v86.es = VTOPSEG(xp);
472 v86.ebx = VTOPOFF(xp);
473 v86int();
474 result = (v86.efl & 0x1);
475 if (result == 0)
476 break;
477 }
478
479 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
480 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x\n",
481 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
482 if (result) {
483 if (bbuf != NULL)
484 free(bbuf);
485 return(-1);
486 }
487 if (bbuf != NULL)
488 bcopy(breg, p, x * BIOSDISK_SECSIZE);
489 p += (x * BIOSDISK_SECSIZE);
490 dblk += x;
491 resid -= x;
492 }
493
494/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
495 if (bbuf != NULL)
496 free(bbuf);
497 return(0);
498}
499
500static int
501bd_getgeom(struct open_disk *od)
502{
503
504 v86.ctl = V86_FLAGS;
505 v86.addr = 0x13;
506 v86.eax = 0x800;
507 v86.edx = od->od_unit;
508 v86int();
509
510 if ((v86.efl & 0x1) || /* carry set */
511 ((v86.edx & 0xff) <= (od->od_unit & 0x7f))) /* unit # bad */
512 return(1);
513
514 /* convert max cyl # -> # of cylinders */
515 od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
516 /* convert max head # -> # of heads */
517 od->od_hds = ((v86.edx & 0xff00) >> 8) + 1;
518 od->od_sec = v86.ecx & 0x3f;
519
520
521 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
522 return(0);
523}
397}
398
399static int
400bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
401{
402 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
403 int blks;
404#ifdef BD_SUPPORT_FRAGS
405 char fragbuf[BIOSDISK_SECSIZE];
406 size_t fragsize;
407
408 fragsize = size % BIOSDISK_SECSIZE;
409#else
410 if (size % BIOSDISK_SECSIZE)
411 panic("bd_strategy: %d bytes I/O not multiple of block size", size);
412#endif
413
414 DEBUG("open_disk %p", od);
415
416 if (rw != F_READ)
417 return(EROFS);
418
419
420 blks = size / BIOSDISK_SECSIZE;
421 DEBUG("read %d from %d+%d to %p", blks, od->od_boff, dblk, buf);
422
423 if (rsize)
424 *rsize = 0;
425 if (blks && bd_read(od, dblk + od->od_boff, blks, buf)) {
426 DEBUG("read error");
427 return (EIO);
428 }
429#ifdef BD_SUPPORT_FRAGS
430 DEBUG("bd_strategy: frag read %d from %d+%d+d to %p\n",
431 fragsize, od->od_boff, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
432 if (fragsize && bd_read(od, dblk + od->od_boff + blks, 1, fragsize)) {
433 DEBUG("frag read error");
434 return(EIO);
435 }
436 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
437#endif
438 if (rsize)
439 *rsize = size;
440 return (0);
441}
442
443/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
444#define FLOPPY_BOUNCEBUF 18
445
446static int
447bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
448{
449 int x, bpc, cyl, hd, sec, result, resid, cnt, retry, maxfer;
450 caddr_t p, xp, bbuf, breg;
451
452 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */
453 resid = blks;
454 p = dest;
455
456 /* Decide whether we have to bounce */
457 if ((od->od_unit < 0x80) &&
458 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
459
460 /*
461 * There is a 64k physical boundary somewhere in the destination buffer, so we have
462 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we
463 * need to. Use the bottom half unless there is a break there, in which case we
464 * use the top half.
465 */
466 x = min(FLOPPY_BOUNCEBUF, blks);
467 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE);
468 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
469 breg = bbuf;
470 } else {
471 breg = bbuf + x * BIOSDISK_SECSIZE;
472 }
473 maxfer = x; /* limit transfers to bounce region size */
474 } else {
475 bbuf = NULL;
476 maxfer = 0;
477 }
478
479 while (resid > 0) {
480 x = dblk;
481 cyl = x / bpc; /* block # / blocks per cylinder */
482 x %= bpc; /* block offset into cylinder */
483 hd = x / od->od_sec; /* offset / blocks per track */
484 sec = x % od->od_sec; /* offset into track */
485
486 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
487 x = min(od->od_sec - sec, resid);
488 if (maxfer > 0)
489 x = min(x, maxfer); /* fit bounce buffer */
490
491 /* where do we transfer to? */
492 xp = bbuf == NULL ? p : breg;
493
494 /* correct sector number for 1-based BIOS numbering */
495 sec++;
496
497 /* Loop retrying the operation a couple of times. The BIOS may also retry. */
498 for (retry = 0; retry < 3; retry++) {
499 /* if retrying, reset the drive */
500 if (retry > 0) {
501 v86.ctl = V86_FLAGS;
502 v86.addr = 0x13;
503 v86.eax = 0;
504 v86.edx = od->od_unit;
505 v86int();
506 }
507
508 /* build request XXX support EDD requests too */
509 v86.ctl = V86_FLAGS;
510 v86.addr = 0x13;
511 v86.eax = 0x200 | x;
512 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
513 v86.edx = (hd << 8) | od->od_unit;
514 v86.es = VTOPSEG(xp);
515 v86.ebx = VTOPOFF(xp);
516 v86int();
517 result = (v86.efl & 0x1);
518 if (result == 0)
519 break;
520 }
521
522 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
523 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x\n",
524 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
525 if (result) {
526 if (bbuf != NULL)
527 free(bbuf);
528 return(-1);
529 }
530 if (bbuf != NULL)
531 bcopy(breg, p, x * BIOSDISK_SECSIZE);
532 p += (x * BIOSDISK_SECSIZE);
533 dblk += x;
534 resid -= x;
535 }
536
537/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
538 if (bbuf != NULL)
539 free(bbuf);
540 return(0);
541}
542
543static int
544bd_getgeom(struct open_disk *od)
545{
546
547 v86.ctl = V86_FLAGS;
548 v86.addr = 0x13;
549 v86.eax = 0x800;
550 v86.edx = od->od_unit;
551 v86int();
552
553 if ((v86.efl & 0x1) || /* carry set */
554 ((v86.edx & 0xff) <= (od->od_unit & 0x7f))) /* unit # bad */
555 return(1);
556
557 /* convert max cyl # -> # of cylinders */
558 od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
559 /* convert max head # -> # of heads */
560 od->od_hds = ((v86.edx & 0xff00) >> 8) + 1;
561 od->od_sec = v86.ecx & 0x3f;
562
563
564 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
565 return(0);
566}
567
568/*
569 * Return a suitable dev_t value for (dev)
570 */
571int
572bd_getdev(struct i386_devdesc *dev)
573{
574 struct open_disk *od;
575 int biosdev;
576 int major;
577
578 biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit);
579 if (biosdev == -1) /* not a BIOS device */
580 return(-1);
581 if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */
582 return(-1);
583
584 if (biosdev < 0x80) {
585 /* floppy (or emulated floppy) or ATAPI device */
586 if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) {
587 /* is an ATAPI disk */
588 major = WFDMAJOR;
589 } else {
590 /* is a floppy disk */
591 major = FDMAJOR;
592 }
593 } else {
594 /* harddisk */
595 if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
596 /* label OK, disk labelled as SCSI */
597 major = DAMAJOR;
598 } else {
599 /* assume an IDE disk */
600 major = WDMAJOR;
601 }
602 }
603 return(MAKEBOOTDEV(major,
604 (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */
605 (dev->d_kind.biosdisk.slice + 1) & 0xf,
606 biosdev & 0x7f, /* XXX allow/compute shift for da when wd present */
607 dev->d_kind.biosdisk.partition));
608}