Commit | Line | Data |
---|---|---|
984263bc MD |
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 | * | |
c7e270b1 | 26 | * $FreeBSD: src/sys/boot/i386/libi386/biosdisk.c,v 1.45 2004/09/21 06:46:44 wes Exp $ |
984263bc MD |
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 | */ | |
38 | ||
39 | #include <stand.h> | |
40 | ||
2b961883 | 41 | #include <sys/disklabel32.h> |
6080181b | 42 | #include <sys/disklabel64.h> |
5ee58eed | 43 | #include <sys/diskmbr.h> |
ba0cc1ab | 44 | #include <sys/dtype.h> |
5ee58eed | 45 | #include <machine/bootinfo.h> |
4905c847 | 46 | #include <machine/psl.h> |
984263bc MD |
47 | |
48 | #include <stdarg.h> | |
49 | ||
50 | #include <bootstrap.h> | |
51 | #include <btxv86.h> | |
52 | #include "libi386.h" | |
53 | ||
54 | #define BIOS_NUMDRIVES 0x475 | |
55 | #define BIOSDISK_SECSIZE 512 | |
6080181b | 56 | #define BUFSIZE (4 * BIOSDISK_SECSIZE) |
984263bc MD |
57 | #define MAXBDDEV MAXDEV |
58 | ||
5ee58eed MD |
59 | #define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ |
60 | #define WDMAJOR 0 /* major numbers for devices we frontend for */ | |
61 | #define WFDMAJOR 1 | |
62 | #define FDMAJOR 2 | |
63 | #define DAMAJOR 4 | |
984263bc MD |
64 | |
65 | #ifdef DISK_DEBUG | |
5ee58eed | 66 | # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) |
984263bc MD |
67 | #else |
68 | # define DEBUG(fmt, args...) | |
69 | #endif | |
70 | ||
71 | struct open_disk { | |
72 | int od_dkunit; /* disk unit number */ | |
73 | int od_unit; /* BIOS unit number */ | |
74 | int od_cyl; /* BIOS geometry */ | |
75 | int od_hds; | |
76 | int od_sec; | |
77 | int od_boff; /* block offset from beginning of BIOS disk */ | |
78 | int od_flags; | |
79 | #define BD_MODEINT13 0x0000 | |
80 | #define BD_MODEEDD1 0x0001 | |
81 | #define BD_MODEEDD3 0x0002 | |
82 | #define BD_MODEMASK 0x0003 | |
83 | #define BD_FLOPPY 0x0004 | |
84 | #define BD_LABELOK 0x0008 | |
85 | #define BD_PARTTABOK 0x0010 | |
6080181b SS |
86 | union { |
87 | struct disklabel32 od_disklabel; | |
88 | struct disklabel64 od_disklabel64; | |
89 | }; | |
984263bc | 90 | int od_nslices; /* slice count */ |
5ee58eed | 91 | struct dos_partition od_slicetab[NEXTDOSPART]; |
984263bc MD |
92 | }; |
93 | ||
94 | /* | |
95 | * List of BIOS devices, translation from disk unit number to | |
96 | * BIOS unit number. | |
97 | */ | |
98 | static struct bdinfo | |
99 | { | |
100 | int bd_unit; /* BIOS unit number */ | |
101 | int bd_flags; | |
102 | int bd_type; /* BIOS 'drive type' (floppy only) */ | |
103 | } bdinfo [MAXBDDEV]; | |
104 | static int nbdinfo = 0; | |
105 | ||
106 | static int bd_getgeom(struct open_disk *od); | |
107 | static int bd_read(struct open_disk *od, daddr_t dblk, int blks, | |
108 | caddr_t dest); | |
5ee58eed MD |
109 | static int bd_write(struct open_disk *od, daddr_t dblk, int blks, |
110 | caddr_t dest); | |
984263bc MD |
111 | |
112 | static int bd_int13probe(struct bdinfo *bd); | |
113 | ||
114 | static void bd_printslice(struct open_disk *od, struct dos_partition *dp, | |
115 | char *prefix, int verbose); | |
116 | static void bd_printbsdslice(struct open_disk *od, daddr_t offset, | |
117 | char *prefix, int verbose); | |
118 | ||
119 | static int bd_init(void); | |
120 | static int bd_strategy(void *devdata, int flag, daddr_t dblk, | |
121 | size_t size, char *buf, size_t *rsize); | |
122 | static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, | |
123 | size_t size, char *buf, size_t *rsize); | |
124 | static int bd_open(struct open_file *f, ...); | |
125 | static int bd_close(struct open_file *f); | |
126 | static void bd_print(int verbose); | |
127 | ||
128 | struct devsw biosdisk = { | |
129 | "disk", | |
130 | DEVT_DISK, | |
131 | bd_init, | |
132 | bd_strategy, | |
133 | bd_open, | |
134 | bd_close, | |
135 | noioctl, | |
136 | bd_print, | |
137 | NULL | |
138 | }; | |
139 | ||
140 | static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); | |
141 | static void bd_closedisk(struct open_disk *od); | |
142 | static int bd_bestslice(struct open_disk *od); | |
7da5c4fd MD |
143 | static void bd_chainextended(struct open_disk *od, u_int32_t base, |
144 | u_int32_t offset); | |
145 | ||
69318c29 MD |
146 | char bounce_base[BOUNCEBUF_SIZE]; /* also used by CD code */ |
147 | ||
984263bc MD |
148 | |
149 | /* | |
150 | * Translate between BIOS device numbers and our private unit numbers. | |
151 | */ | |
152 | int | |
153 | bd_bios2unit(int biosdev) | |
154 | { | |
155 | int i; | |
156 | ||
157 | DEBUG("looking for bios device 0x%x", biosdev); | |
158 | for (i = 0; i < nbdinfo; i++) { | |
159 | DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); | |
160 | if (bdinfo[i].bd_unit == biosdev) | |
161 | return(i); | |
162 | } | |
163 | return(-1); | |
164 | } | |
165 | ||
166 | int | |
167 | bd_unit2bios(int unit) | |
168 | { | |
169 | if ((unit >= 0) && (unit < nbdinfo)) | |
170 | return(bdinfo[unit].bd_unit); | |
171 | return(-1); | |
172 | } | |
173 | ||
174 | /* | |
175 | * Quiz the BIOS for disk devices, save a little info about them. | |
176 | */ | |
177 | static int | |
178 | bd_init(void) | |
179 | { | |
7da5c4fd MD |
180 | int base, unit, nfd = 0; |
181 | ||
984263bc MD |
182 | /* sequence 0, 0x80 */ |
183 | for (base = 0; base <= 0x80; base += 0x80) { | |
184 | for (unit = base; (nbdinfo < MAXBDDEV); unit++) { | |
185 | /* check the BIOS equipment list for number of fixed disks */ | |
186 | if((base == 0x80) && | |
187 | (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) | |
188 | break; | |
189 | ||
190 | bdinfo[nbdinfo].bd_unit = unit; | |
191 | bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; | |
192 | ||
193 | if (!bd_int13probe(&bdinfo[nbdinfo])) | |
194 | break; | |
195 | ||
196 | /* XXX we need "disk aliases" to make this simpler */ | |
197 | printf("BIOS drive %c: is disk%d\n", | |
198 | (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); | |
199 | nbdinfo++; | |
200 | if (base == 0x80) | |
201 | nfd++; | |
202 | } | |
203 | } | |
204 | return(0); | |
205 | } | |
206 | ||
207 | /* | |
208 | * Try to detect a device supported by the legacy int13 BIOS | |
209 | */ | |
210 | static int | |
211 | bd_int13probe(struct bdinfo *bd) | |
212 | { | |
213 | v86.ctl = V86_FLAGS; | |
214 | v86.addr = 0x13; | |
215 | v86.eax = 0x800; | |
216 | v86.edx = bd->bd_unit; | |
217 | v86int(); | |
218 | ||
4905c847 | 219 | if (!(v86.efl & PSL_C) && /* carry clear */ |
984263bc | 220 | ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ |
c7e270b1 MD |
221 | |
222 | /* | |
223 | * Ignore devices with an absurd sector size. | |
224 | */ | |
225 | if ((v86.ecx & 0x3f) == 0) { | |
226 | DEBUG("Invalid geometry for unit %d", bd->bd_unit); | |
227 | return(0); | |
228 | } | |
984263bc MD |
229 | bd->bd_flags |= BD_MODEINT13; |
230 | bd->bd_type = v86.ebx & 0xff; | |
231 | ||
232 | /* Determine if we can use EDD with this device. */ | |
e70dab52 MD |
233 | v86.ctl = V86_FLAGS; |
234 | v86.addr = 0x13; | |
984263bc MD |
235 | v86.eax = 0x4100; |
236 | v86.edx = bd->bd_unit; | |
237 | v86.ebx = 0x55aa; | |
238 | v86int(); | |
4905c847 | 239 | if (!(v86.efl & PSL_C) && /* carry clear */ |
984263bc MD |
240 | ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ |
241 | (v86.ecx & 0x1)) { /* packets mode ok */ | |
242 | bd->bd_flags |= BD_MODEEDD1; | |
243 | if((v86.eax & 0xff00) > 0x300) | |
244 | bd->bd_flags |= BD_MODEEDD3; | |
245 | } | |
246 | return(1); | |
247 | } | |
248 | return(0); | |
249 | } | |
250 | ||
251 | /* | |
252 | * Print information about disks | |
253 | */ | |
254 | static void | |
255 | bd_print(int verbose) | |
256 | { | |
257 | int i, j; | |
258 | char line[80]; | |
259 | struct i386_devdesc dev; | |
260 | struct open_disk *od; | |
261 | struct dos_partition *dptr; | |
262 | ||
263 | for (i = 0; i < nbdinfo; i++) { | |
264 | sprintf(line, " disk%d: BIOS drive %c:\n", i, | |
265 | (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); | |
266 | pager_output(line); | |
267 | ||
268 | /* try to open the whole disk */ | |
269 | dev.d_kind.biosdisk.unit = i; | |
270 | dev.d_kind.biosdisk.slice = -1; | |
271 | dev.d_kind.biosdisk.partition = -1; | |
272 | ||
273 | if (!bd_opendisk(&od, &dev)) { | |
274 | ||
275 | /* Do we have a partition table? */ | |
276 | if (od->od_flags & BD_PARTTABOK) { | |
277 | dptr = &od->od_slicetab[0]; | |
278 | ||
279 | /* Check for a "dedicated" disk */ | |
0a7482e0 AH |
280 | if (((dptr[3].dp_typ == DOSPTYP_386BSD) || |
281 | (dptr[3].dp_typ == DOSPTYP_NETBSD)) && | |
984263bc MD |
282 | (dptr[3].dp_start == 0) && |
283 | (dptr[3].dp_size == 50000)) { | |
284 | sprintf(line, " disk%d", i); | |
285 | bd_printbsdslice(od, 0, line, verbose); | |
286 | } else { | |
287 | for (j = 0; j < od->od_nslices; j++) { | |
288 | sprintf(line, " disk%ds%d", i, j + 1); | |
289 | bd_printslice(od, &dptr[j], line, verbose); | |
290 | } | |
291 | } | |
292 | } | |
293 | bd_closedisk(od); | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
298 | /* | |
299 | * Print information about slices on a disk. For the size calculations we | |
300 | * assume a 512 byte sector. | |
301 | */ | |
302 | static void | |
303 | bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, | |
304 | int verbose) | |
305 | { | |
306 | char line[80]; | |
307 | ||
308 | switch (dp->dp_typ) { | |
309 | case DOSPTYP_386BSD: | |
0a7482e0 AH |
310 | case DOSPTYP_NETBSD: |
311 | /* XXX: possibly add types 0 and 1, as in subr_disk, for gpt magic */ | |
984263bc MD |
312 | bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); |
313 | return; | |
314 | case DOSPTYP_LINSWP: | |
315 | if (verbose) | |
316 | sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", | |
317 | prefix, dp->dp_size / 2048, | |
318 | dp->dp_start, dp->dp_start + dp->dp_size); | |
319 | else | |
320 | sprintf(line, "%s: Linux swap\n", prefix); | |
321 | break; | |
322 | case DOSPTYP_LINUX: | |
323 | /* | |
324 | * XXX | |
325 | * read the superblock to confirm this is an ext2fs partition? | |
326 | */ | |
327 | if (verbose) | |
328 | sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, | |
329 | dp->dp_size / 2048, dp->dp_start, | |
330 | dp->dp_start + dp->dp_size); | |
331 | else | |
332 | sprintf(line, "%s: ext2fs\n", prefix); | |
333 | break; | |
334 | case 0x00: /* unused partition */ | |
335 | case DOSPTYP_EXT: | |
336 | return; | |
337 | case 0x01: | |
338 | if (verbose) | |
339 | sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, | |
340 | dp->dp_size / 2048, dp->dp_start, | |
341 | dp->dp_start + dp->dp_size); | |
342 | else | |
343 | sprintf(line, "%s: FAT-12\n", prefix); | |
344 | break; | |
345 | case 0x04: | |
346 | case 0x06: | |
347 | case 0x0e: | |
348 | if (verbose) | |
349 | sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, | |
350 | dp->dp_size / 2048, dp->dp_start, | |
351 | dp->dp_start + dp->dp_size); | |
352 | else | |
353 | sprintf(line, "%s: FAT-16\n", prefix); | |
354 | break; | |
355 | case 0x0b: | |
356 | case 0x0c: | |
357 | if (verbose) | |
358 | sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, | |
359 | dp->dp_size / 2048, dp->dp_start, | |
360 | dp->dp_start + dp->dp_size); | |
361 | else | |
362 | sprintf(line, "%s: FAT-32\n", prefix); | |
363 | break; | |
364 | default: | |
365 | if (verbose) | |
366 | sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", | |
367 | prefix, dp->dp_typ, dp->dp_size / 2048, | |
368 | dp->dp_start, dp->dp_start + dp->dp_size); | |
369 | else | |
370 | sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, | |
371 | dp->dp_typ); | |
372 | } | |
373 | pager_output(line); | |
374 | } | |
375 | ||
984263bc | 376 | static void |
0a7482e0 AH |
377 | print_partition(u_int8_t fstype, unsigned long long offset, |
378 | unsigned long long size, int i, int od_flags, | |
379 | char *prefix, int verbose, int type) | |
984263bc | 380 | { |
0a7482e0 AH |
381 | char line[80]; |
382 | ||
984263bc MD |
383 | /* |
384 | * For each partition, make sure we know what type of fs it is. If | |
385 | * not, then skip it. However, since floppies often have bogus | |
386 | * fstypes, print the 'a' partition on a floppy even if it is marked | |
387 | * unused. | |
388 | */ | |
0a7482e0 AH |
389 | if ((fstype == FS_SWAP) || |
390 | (fstype == FS_VINUM) || | |
391 | (fstype == FS_HAMMER) || | |
cdfd0a3e | 392 | (fstype == FS_HAMMER2) || |
0a7482e0 AH |
393 | (fstype == FS_BSDFFS) || |
394 | (fstype == FS_ZFS) || | |
395 | (fstype == FS_JFS2) || | |
396 | ((fstype == FS_UNUSED) && | |
397 | (od_flags & BD_FLOPPY) && (i == 0))) { | |
398 | ||
399 | /* Only print out statistics in verbose mode */ | |
400 | if (verbose) { | |
4f6dc91b AH |
401 | sprintf(line, "%c %s%c: %s %.6lluMB (%llu - %llu)\n", |
402 | /* prefix disks that can be used to load modules with '*' */ | |
403 | ((fstype == FS_BSDFFS) || (fstype == FS_UNUSED) || | |
404 | (fstype == FS_VINUM)) ? '*' : ' ', | |
0a7482e0 AH |
405 | prefix, 'a' + i, |
406 | (fstype == FS_SWAP) ? "swap" : | |
407 | (fstype == FS_VINUM) ? "vinum" : | |
408 | (fstype == FS_HAMMER) ? "HAMMER" : | |
cdfd0a3e | 409 | (fstype == FS_HAMMER2) ? "HAMMER2" : |
0a7482e0 AH |
410 | (fstype == FS_JFS2) ? "JFS2" : |
411 | (fstype == FS_ZFS) ? "ZFS" : | |
412 | (fstype == FS_BSDFFS) ? "FFS" : | |
413 | "(unknown)", | |
414 | (type==32)?(size / 2048):(size/1024/1024), | |
415 | offset, | |
416 | offset + size); | |
417 | } else { | |
4f6dc91b AH |
418 | sprintf(line, "%c %s%c: %s\n", |
419 | /* prefix disks that can be used to load modules with '*' */ | |
420 | ((fstype == FS_BSDFFS) || (fstype == FS_UNUSED) || | |
421 | (fstype == FS_VINUM)) ? '*' : ' ', | |
422 | prefix, 'a' + i, | |
0a7482e0 AH |
423 | (fstype == FS_SWAP) ? "swap" : |
424 | (fstype == FS_VINUM) ? "vinum" : | |
425 | (fstype == FS_HAMMER) ? "HAMMER" : | |
cdfd0a3e | 426 | (fstype == FS_HAMMER2) ? "HAMMER2" : |
0a7482e0 AH |
427 | (fstype == FS_JFS2) ? "JFS2" : |
428 | (fstype == FS_ZFS) ? "ZFS" : | |
429 | (fstype == FS_BSDFFS) ? "FFS" : | |
430 | "(unknown)"); | |
431 | } | |
432 | ||
433 | pager_output(line); | |
434 | } | |
435 | } | |
436 | ||
437 | /* | |
438 | * Print out each valid partition in the disklabel of a FreeBSD slice. | |
439 | * For size calculations, we assume a 512 byte sector size. | |
440 | */ | |
441 | static void | |
442 | bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, | |
443 | int verbose) | |
444 | { | |
445 | char line[80]; | |
446 | char buf[BIOSDISK_SECSIZE*2]; | |
447 | struct disklabel32 *lp = NULL; | |
448 | struct disklabel64 *lp64 = NULL; | |
449 | int i; | |
450 | ||
451 | /* read disklabel */ | |
452 | if (bd_read(od, offset + LABELSECTOR32, 1, buf)) | |
453 | return; | |
454 | lp =(struct disklabel32 *)(&buf[0]); | |
455 | if (lp->d_magic != DISKMAGIC32) { | |
456 | if (bd_read(od, offset, 2, buf)) | |
457 | return; | |
458 | ||
459 | lp64 =(struct disklabel64 *)(&buf[0]); | |
460 | if (lp64->d_magic != DISKMAGIC64) { | |
461 | sprintf(line, "%s: bad disklabel\n", prefix); | |
462 | pager_output(line); | |
463 | return; | |
464 | } | |
465 | lp = NULL; /* PARANOID */ | |
466 | } | |
467 | if (lp64) { | |
468 | /* We got ourselves a disklabel64 here */ | |
469 | for (i = 0; i < lp64->d_npartitions; i++) { | |
470 | if (lp64->d_partitions[i].p_bsize == 0) | |
471 | continue; | |
472 | ||
473 | print_partition(lp64->d_partitions[i].p_fstype, | |
474 | lp64->d_partitions[i].p_boffset, | |
475 | lp64->d_partitions[i].p_bsize, | |
476 | i, od->od_flags, prefix, verbose, 64); | |
477 | } | |
478 | } else if (lp) { | |
479 | /* Print partitions */ | |
480 | for (i = 0; i < lp->d_npartitions; i++) { | |
481 | print_partition(lp->d_partitions[i].p_fstype, | |
482 | lp->d_partitions[i].p_offset, | |
483 | lp->d_partitions[i].p_size, | |
484 | i, od->od_flags, prefix, verbose, 32); | |
485 | } | |
984263bc | 486 | } |
984263bc MD |
487 | } |
488 | ||
489 | ||
490 | /* | |
491 | * Attempt to open the disk described by (dev) for use by (f). | |
492 | * | |
493 | * Note that the philosophy here is "give them exactly what | |
494 | * they ask for". This is necessary because being too "smart" | |
495 | * about what the user might want leads to complications. | |
496 | * (eg. given no slice or partition value, with a disk that is | |
497 | * sliced - are they after the first BSD slice, or the DOS | |
498 | * slice before it?) | |
499 | */ | |
500 | static int | |
501 | bd_open(struct open_file *f, ...) | |
502 | { | |
5ee58eed | 503 | va_list ap; |
984263bc MD |
504 | struct i386_devdesc *dev; |
505 | struct open_disk *od; | |
506 | int error; | |
507 | ||
5ee58eed MD |
508 | va_start(ap, f); |
509 | dev = va_arg(ap, struct i386_devdesc *); | |
510 | va_end(ap); | |
984263bc MD |
511 | if ((error = bd_opendisk(&od, dev))) |
512 | return(error); | |
513 | ||
514 | /* | |
515 | * Save our context | |
516 | */ | |
517 | ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; | |
518 | DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); | |
519 | return(0); | |
520 | } | |
521 | ||
522 | static int | |
523 | bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) | |
524 | { | |
525 | struct dos_partition *dptr; | |
2b961883 | 526 | struct disklabel32 *lp; |
6080181b | 527 | struct disklabel64 *lp64; |
984263bc MD |
528 | struct open_disk *od; |
529 | int sector, slice, i; | |
530 | int error; | |
6080181b | 531 | static char buf[BUFSIZE]; |
984263bc MD |
532 | |
533 | if (dev->d_kind.biosdisk.unit >= nbdinfo) { | |
534 | DEBUG("attempt to open nonexistent disk"); | |
535 | return(ENXIO); | |
536 | } | |
537 | ||
538 | od = (struct open_disk *)malloc(sizeof(struct open_disk)); | |
539 | if (!od) { | |
540 | DEBUG("no memory"); | |
541 | return (ENOMEM); | |
542 | } | |
543 | ||
544 | /* Look up BIOS unit number, intialise open_disk structure */ | |
545 | od->od_dkunit = dev->d_kind.biosdisk.unit; | |
546 | od->od_unit = bdinfo[od->od_dkunit].bd_unit; | |
547 | od->od_flags = bdinfo[od->od_dkunit].bd_flags; | |
548 | od->od_boff = 0; | |
549 | od->od_nslices = 0; | |
550 | error = 0; | |
551 | DEBUG("open '%s', unit 0x%x slice %d partition %c", | |
552 | i386_fmtdev(dev), dev->d_kind.biosdisk.unit, | |
553 | dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); | |
554 | ||
555 | /* Get geometry for this open (removable device may have changed) */ | |
556 | if (bd_getgeom(od)) { | |
557 | DEBUG("can't get geometry"); | |
558 | error = ENXIO; | |
559 | goto out; | |
560 | } | |
561 | ||
562 | /* | |
563 | * Following calculations attempt to determine the correct value | |
564 | * for d->od_boff by looking for the slice and partition specified, | |
565 | * or searching for reasonable defaults. | |
566 | */ | |
567 | ||
568 | /* | |
569 | * Find the slice in the DOS slice table. | |
570 | */ | |
571 | if (bd_read(od, 0, 1, buf)) { | |
572 | DEBUG("error reading MBR"); | |
573 | error = EIO; | |
574 | goto out; | |
575 | } | |
576 | ||
577 | /* | |
578 | * Check the slice table magic. | |
579 | */ | |
580 | if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { | |
581 | /* If a slice number was explicitly supplied, this is an error */ | |
582 | if (dev->d_kind.biosdisk.slice > 0) { | |
583 | DEBUG("no slice table/MBR (no magic)"); | |
584 | error = ENOENT; | |
585 | goto out; | |
586 | } | |
587 | sector = 0; | |
588 | goto unsliced; /* may be a floppy */ | |
589 | } | |
590 | ||
591 | /* | |
7755d172 MD |
592 | * copy the partition table, then pick up any extended partitions. The |
593 | * base partition table always has four entries, even if some of them | |
594 | * represented extended partitions. However, any additional sub-extended | |
595 | * partitions will be silently recursed and not included in the slice | |
596 | * table. | |
984263bc MD |
597 | */ |
598 | bcopy(buf + DOSPARTOFF, &od->od_slicetab, | |
599 | sizeof(struct dos_partition) * NDOSPART); | |
7755d172 MD |
600 | od->od_nslices = NDOSPART; |
601 | ||
602 | dptr = &od->od_slicetab[0]; | |
603 | for (i = 0; i < NDOSPART; i++, dptr++) { | |
604 | if ((dptr->dp_typ == DOSPTYP_EXT) || (dptr->dp_typ == DOSPTYP_EXTLBA)) | |
605 | bd_chainextended(od, dptr->dp_start, 0); /* 1st offset is zero */ | |
606 | } | |
984263bc MD |
607 | od->od_flags |= BD_PARTTABOK; |
608 | dptr = &od->od_slicetab[0]; | |
609 | ||
7755d172 MD |
610 | /* |
611 | * Overflow entries are not loaded into memory but we still keep | |
612 | * track of the count. Fix it up now. | |
613 | */ | |
614 | if (od->od_nslices > NEXTDOSPART) | |
615 | od->od_nslices = NEXTDOSPART; | |
616 | ||
617 | /* | |
618 | * Is this a request for the whole disk? | |
619 | */ | |
984263bc MD |
620 | if (dev->d_kind.biosdisk.slice == -1) { |
621 | sector = 0; | |
622 | goto unsliced; | |
623 | } | |
624 | ||
625 | /* | |
626 | * if a slice number was supplied but not found, this is an error. | |
627 | */ | |
628 | if (dev->d_kind.biosdisk.slice > 0) { | |
629 | slice = dev->d_kind.biosdisk.slice - 1; | |
630 | if (slice >= od->od_nslices) { | |
631 | DEBUG("slice %d not found", slice); | |
632 | error = ENOENT; | |
633 | goto out; | |
634 | } | |
635 | } | |
636 | ||
637 | /* | |
638 | * Check for the historically bogus MBR found on true dedicated disks | |
639 | */ | |
640 | if ((dptr[3].dp_typ == DOSPTYP_386BSD) && | |
641 | (dptr[3].dp_start == 0) && | |
642 | (dptr[3].dp_size == 50000)) { | |
643 | sector = 0; | |
644 | goto unsliced; | |
645 | } | |
646 | ||
647 | /* Try to auto-detect the best slice; this should always give a slice number */ | |
648 | if (dev->d_kind.biosdisk.slice == 0) { | |
649 | slice = bd_bestslice(od); | |
650 | if (slice == -1) { | |
651 | error = ENOENT; | |
652 | goto out; | |
653 | } | |
654 | dev->d_kind.biosdisk.slice = slice; | |
655 | } | |
656 | ||
657 | dptr = &od->od_slicetab[0]; | |
658 | /* | |
659 | * Accept the supplied slice number unequivocally (we may be looking | |
660 | * at a DOS partition). | |
661 | */ | |
662 | dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ | |
663 | sector = dptr->dp_start; | |
664 | DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); | |
665 | ||
666 | /* | |
667 | * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition | |
668 | */ | |
669 | if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) | |
670 | dev->d_kind.biosdisk.partition = 0; | |
671 | ||
8f706258 | 672 | unsliced: |
984263bc MD |
673 | /* |
674 | * Now we have the slice offset, look for the partition in the disklabel if we have | |
675 | * a partition to start with. | |
676 | * | |
677 | * XXX we might want to check the label checksum. | |
678 | */ | |
679 | if (dev->d_kind.biosdisk.partition < 0) { | |
680 | od->od_boff = sector; /* no partition, must be after the slice */ | |
681 | DEBUG("opening raw slice"); | |
682 | } else { | |
683 | ||
2b961883 | 684 | if (bd_read(od, sector + LABELSECTOR32, 1, buf)) { |
984263bc MD |
685 | DEBUG("error reading disklabel"); |
686 | error = EIO; | |
687 | goto out; | |
688 | } | |
2b961883 MD |
689 | DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel32), buf + LABELOFFSET32, &od->od_disklabel); |
690 | bcopy(buf + LABELOFFSET32, &od->od_disklabel, sizeof(struct disklabel32)); | |
984263bc | 691 | lp = &od->od_disklabel; |
984263bc | 692 | |
6080181b SS |
693 | if (lp->d_magic == DISKMAGIC32) { |
694 | od->od_flags |= BD_LABELOK; | |
695 | ||
696 | if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { | |
697 | DEBUG("partition '%c' exceeds partitions in table (a-'%c')", | |
698 | 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); | |
699 | error = EPART; | |
700 | goto out; | |
701 | ||
702 | } | |
703 | ||
704 | #ifdef DISK_DEBUG | |
705 | /* Complain if the partition is unused unless this is a floppy. */ | |
706 | if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && | |
707 | !(od->od_flags & BD_FLOPPY)) | |
708 | DEBUG("warning, partition marked as unused"); | |
709 | #endif | |
710 | ||
711 | od->od_boff = | |
712 | lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset - | |
713 | lp->d_partitions[RAW_PART].p_offset + | |
714 | sector; | |
715 | ||
984263bc MD |
716 | goto out; |
717 | } | |
984263bc | 718 | |
6080181b SS |
719 | /* else maybe DISKLABEL64? */ |
720 | ||
721 | if (bd_read(od, sector, (sizeof(od->od_disklabel64) + 511) / 512, buf)) { | |
722 | DEBUG("error reading disklabel"); | |
723 | error = EIO; | |
724 | goto out; | |
984263bc | 725 | } |
6080181b SS |
726 | DEBUG("copy %d bytes of label from %p to %p", sizeof(od->od_disklabel64), buf, &od->od_disklabel64); |
727 | bcopy(buf, &od->od_disklabel64, sizeof(od->od_disklabel64)); | |
728 | lp64 = &od->od_disklabel64; | |
984263bc | 729 | |
6080181b SS |
730 | if (lp64->d_magic == DISKMAGIC64) { |
731 | od->od_flags |= BD_LABELOK; | |
732 | ||
733 | if (dev->d_kind.biosdisk.partition >= lp64->d_npartitions || | |
734 | lp64->d_partitions[dev->d_kind.biosdisk.partition].p_bsize == 0) { | |
735 | DEBUG("partition '%c' exceeds partitions in table (a-'%c')", | |
736 | 'a' + dev->d_kind.biosdisk.partition, 'a' + lp64->d_npartitions); | |
737 | error = EPART; | |
738 | goto out; | |
739 | ||
740 | } | |
741 | ||
742 | od->od_boff = | |
743 | lp64->d_partitions[dev->d_kind.biosdisk.partition].p_boffset / 512 + | |
744 | sector; | |
745 | ||
746 | DEBUG("disklabel64 slice at %d", od->od_boff); | |
747 | ||
748 | } else { | |
749 | DEBUG("no disklabel"); | |
750 | error = ENOENT; | |
751 | } | |
984263bc | 752 | } |
6080181b | 753 | |
984263bc MD |
754 | out: |
755 | if (error) { | |
756 | free(od); | |
757 | } else { | |
758 | *odp = od; /* return the open disk */ | |
759 | } | |
760 | return(error); | |
761 | } | |
762 | ||
7755d172 | 763 | |
984263bc | 764 | static void |
7755d172 | 765 | bd_chainextended(struct open_disk *od, u_int32_t base, u_int32_t offset) |
984263bc | 766 | { |
7755d172 MD |
767 | char buf[BIOSDISK_SECSIZE]; |
768 | struct dos_partition *dp1, *dp2; | |
769 | int i; | |
770 | ||
771 | if (bd_read(od, (daddr_t)(base + offset), 1, buf)) { | |
772 | printf("\nerror reading extended partition table"); | |
773 | return; | |
984263bc | 774 | } |
7755d172 MD |
775 | |
776 | /* | |
777 | * dp1 points to the first record in the on-disk XPT, | |
778 | * dp2 points to the next entry in the in-memory parition table. | |
779 | * | |
780 | * NOTE: dp2 may be out of bounds if od_nslices >= NEXTDOSPART. | |
781 | * | |
782 | * NOTE: unlike the extended partitions in our primary dos partition | |
783 | * table, we do not record recursed extended partitions themselves | |
784 | * in our in-memory partition table. | |
785 | * | |
786 | * NOTE: recording within our in-memory table must be breadth first | |
787 | * ot match what the kernel does. Thus, two passes are required. | |
788 | * | |
789 | * NOTE: partitioning programs which support extended partitions seem | |
790 | * to always use the first entry for the user partition and the | |
791 | * second entry to chain, and also appear to disallow more then one | |
792 | * extended partition at each level. Nevertheless we make our code | |
793 | * somewhat more generic (and the same as the kernel's own slice | |
794 | * scan). | |
795 | */ | |
796 | dp1 = (struct dos_partition *)(&buf[DOSPARTOFF]); | |
797 | dp2 = &od->od_slicetab[od->od_nslices]; | |
798 | ||
799 | for (i = 0; i < NDOSPART; ++i, ++dp1) { | |
800 | if (dp1->dp_scyl == 0 && dp1->dp_shd == 0 && | |
801 | dp1->dp_ssect == 0 && dp1->dp_start == 0 && | |
802 | dp1->dp_size == 0) { | |
984263bc | 803 | continue; |
7755d172 MD |
804 | } |
805 | if ((dp1->dp_typ == DOSPTYP_EXT) || | |
806 | (dp1->dp_typ == DOSPTYP_EXTLBA)) { | |
807 | /* | |
808 | * breadth first traversal, must skip in the | |
809 | * first pass | |
810 | */ | |
811 | continue; | |
812 | } | |
813 | ||
814 | /* | |
815 | * Only load up the in-memory data if we haven't overflowed | |
816 | * our in-memory array. | |
817 | */ | |
818 | if (od->od_nslices < NEXTDOSPART) { | |
819 | dp2->dp_typ = dp1->dp_typ; | |
820 | dp2->dp_start = base + offset + dp1->dp_start; | |
821 | dp2->dp_size = dp1->dp_size; | |
822 | } | |
823 | ++od->od_nslices; | |
824 | ++dp2; | |
984263bc | 825 | } |
984263bc MD |
826 | |
827 | /* | |
7755d172 MD |
828 | * Pass 2 - handle extended partitions. Note that the extended |
829 | * slice itself is not stored in the slice array when we recurse, | |
830 | * but any 'original' top-level extended slices are. This is to | |
831 | * match what the kernel does. | |
984263bc | 832 | */ |
7755d172 MD |
833 | dp1 -= NDOSPART; |
834 | for (i = 0; i < NDOSPART; ++i, ++dp1) { | |
835 | if (dp1->dp_scyl == 0 && dp1->dp_shd == 0 && | |
836 | dp1->dp_ssect == 0 && dp1->dp_start == 0 && | |
837 | dp1->dp_size == 0) { | |
838 | continue; | |
839 | } | |
840 | if ((dp1->dp_typ == DOSPTYP_EXT) || | |
c598b182 | 841 | (dp1->dp_typ == DOSPTYP_EXTLBA)) |
7755d172 | 842 | bd_chainextended(od, base, dp1->dp_start); |
7755d172 | 843 | } |
984263bc MD |
844 | } |
845 | ||
846 | /* | |
847 | * Search for a slice with the following preferences: | |
848 | * | |
849 | * 1: Active FreeBSD slice | |
850 | * 2: Non-active FreeBSD slice | |
851 | * 3: Active Linux slice | |
852 | * 4: non-active Linux slice | |
853 | * 5: Active FAT/FAT32 slice | |
854 | * 6: non-active FAT/FAT32 slice | |
855 | */ | |
856 | #define PREF_RAWDISK 0 | |
857 | #define PREF_FBSD_ACT 1 | |
858 | #define PREF_FBSD 2 | |
859 | #define PREF_LINUX_ACT 3 | |
860 | #define PREF_LINUX 4 | |
861 | #define PREF_DOS_ACT 5 | |
862 | #define PREF_DOS 6 | |
863 | #define PREF_NONE 7 | |
864 | ||
865 | /* | |
866 | * slicelimit is in the range 0 .. NDOSPART | |
867 | */ | |
868 | static int | |
869 | bd_bestslice(struct open_disk *od) | |
870 | { | |
871 | struct dos_partition *dp; | |
872 | int pref, preflevel; | |
873 | int i, prefslice; | |
874 | ||
875 | prefslice = 0; | |
876 | preflevel = PREF_NONE; | |
877 | ||
878 | dp = &od->od_slicetab[0]; | |
879 | for (i = 0; i < od->od_nslices; i++, dp++) { | |
984263bc MD |
880 | switch (dp->dp_typ) { |
881 | case DOSPTYP_386BSD: /* FreeBSD */ | |
882 | pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; | |
883 | break; | |
884 | ||
885 | case DOSPTYP_LINUX: | |
886 | pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; | |
887 | break; | |
888 | ||
889 | case 0x01: /* DOS/Windows */ | |
890 | case 0x04: | |
891 | case 0x06: | |
892 | case 0x0b: | |
893 | case 0x0c: | |
894 | case 0x0e: | |
895 | pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; | |
896 | break; | |
897 | ||
898 | default: | |
899 | pref = PREF_NONE; | |
900 | } | |
901 | if (pref < preflevel) { | |
902 | preflevel = pref; | |
903 | prefslice = i + 1; | |
904 | } | |
905 | } | |
906 | return (prefslice); | |
907 | } | |
908 | ||
909 | static int | |
910 | bd_close(struct open_file *f) | |
911 | { | |
912 | struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); | |
913 | ||
914 | bd_closedisk(od); | |
915 | return(0); | |
916 | } | |
917 | ||
918 | static void | |
919 | bd_closedisk(struct open_disk *od) | |
920 | { | |
921 | DEBUG("open_disk %p", od); | |
922 | #if 0 | |
923 | /* XXX is this required? (especially if disk already open...) */ | |
924 | if (od->od_flags & BD_FLOPPY) | |
925 | delay(3000000); | |
926 | #endif | |
927 | free(od); | |
928 | } | |
929 | ||
930 | static int | |
931 | bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) | |
932 | { | |
933 | struct bcache_devdata bcd; | |
934 | struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); | |
935 | ||
936 | bcd.dv_strategy = bd_realstrategy; | |
937 | bcd.dv_devdata = devdata; | |
938 | return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); | |
939 | } | |
940 | ||
941 | static int | |
942 | bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) | |
943 | { | |
944 | struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); | |
945 | int blks; | |
946 | #ifdef BD_SUPPORT_FRAGS | |
947 | char fragbuf[BIOSDISK_SECSIZE]; | |
948 | size_t fragsize; | |
949 | ||
950 | fragsize = size % BIOSDISK_SECSIZE; | |
951 | #else | |
952 | if (size % BIOSDISK_SECSIZE) | |
953 | panic("bd_strategy: %d bytes I/O not multiple of block size", size); | |
954 | #endif | |
955 | ||
956 | DEBUG("open_disk %p", od); | |
957 | ||
5644a38d MD |
958 | switch(rw){ |
959 | case F_READ: | |
960 | blks = size / BIOSDISK_SECSIZE; | |
961 | DEBUG("read %d from %d to %p", blks, dblk, buf); | |
984263bc | 962 | |
5644a38d MD |
963 | if (rsize) |
964 | *rsize = 0; | |
965 | if (blks && bd_read(od, dblk, blks, buf)) { | |
966 | DEBUG("read error"); | |
967 | return (EIO); | |
968 | } | |
984263bc | 969 | #ifdef BD_SUPPORT_FRAGS |
5644a38d | 970 | DEBUG("bd_strategy: frag read %d from %d+%d to %p", |
984263bc | 971 | fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); |
5644a38d MD |
972 | if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { |
973 | DEBUG("frag read error"); | |
974 | return(EIO); | |
975 | } | |
976 | bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); | |
984263bc | 977 | #endif |
5644a38d MD |
978 | if (rsize) |
979 | *rsize = size; | |
980 | return (0); | |
981 | case F_WRITE : | |
982 | blks = size / BIOSDISK_SECSIZE; | |
983 | DEBUG("write %d from %d to %p", blks, dblk, buf); | |
984 | ||
985 | if (rsize) | |
986 | *rsize = 0; | |
987 | if (blks && bd_write(od, dblk, blks, buf)) { | |
988 | DEBUG("write error"); | |
989 | return (EIO); | |
990 | } | |
5ee58eed MD |
991 | #ifdef BD_SUPPORT_FRAGS |
992 | if(fragsize) { | |
5644a38d MD |
993 | DEBUG("Attempted to write a frag"); |
994 | return (EIO); | |
5ee58eed MD |
995 | } |
996 | #endif | |
5644a38d MD |
997 | if (rsize) |
998 | *rsize = size; | |
999 | return (0); | |
1000 | default: | |
1001 | break; /* DO NOTHING */ | |
1002 | } | |
1003 | return EROFS; | |
984263bc MD |
1004 | } |
1005 | ||
984263bc MD |
1006 | static int |
1007 | bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) | |
1008 | { | |
1009 | u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; | |
1010 | caddr_t p, xp, bbuf, breg; | |
1011 | ||
1012 | /* Just in case some idiot actually tries to read -1 blocks... */ | |
1013 | if (blks < 0) | |
1014 | return (-1); | |
1015 | ||
1016 | bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ | |
1017 | resid = blks; | |
1018 | p = dest; | |
1019 | ||
5fc9dc6d | 1020 | /* |
7da5c4fd | 1021 | * Always bounce. Our buffer is probably not segment-addressable. |
5fc9dc6d | 1022 | */ |
7da5c4fd | 1023 | if (1) { |
984263bc MD |
1024 | |
1025 | /* | |
5fc9dc6d MD |
1026 | * There is a 64k physical boundary somewhere in the destination |
1027 | * buffer, so we have to arrange a suitable bounce buffer. Allocate | |
1028 | * a buffer twice as large as we need to. Use the bottom half unless | |
1029 | * there is a break there, in which case we use the top half. | |
984263bc | 1030 | */ |
7da5c4fd MD |
1031 | bbuf = bounce_base; |
1032 | x = min(BOUNCEBUF_SECTS, (unsigned)blks); | |
1033 | ||
1034 | if (((u_int32_t)VTOP(bbuf) & 0xffff8000) == | |
1035 | ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE - 1) & 0xffff8000)) { | |
984263bc MD |
1036 | breg = bbuf; |
1037 | } else { | |
1038 | breg = bbuf + x * BIOSDISK_SECSIZE; | |
1039 | } | |
5fc9dc6d | 1040 | maxfer = x; /* limit transfers to bounce region size */ |
984263bc MD |
1041 | } else { |
1042 | breg = bbuf = NULL; | |
1043 | maxfer = 0; | |
1044 | } | |
1045 | ||
1046 | while (resid > 0) { | |
1047 | x = dblk; | |
1048 | cyl = x / bpc; /* block # / blocks per cylinder */ | |
1049 | x %= bpc; /* block offset into cylinder */ | |
1050 | hd = x / od->od_sec; /* offset / blocks per track */ | |
1051 | sec = x % od->od_sec; /* offset into track */ | |
1052 | ||
1053 | /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ | |
e54488bb | 1054 | x = szmin(od->od_sec - sec, resid); |
984263bc MD |
1055 | if (maxfer > 0) |
1056 | x = min(x, maxfer); /* fit bounce buffer */ | |
1057 | ||
1058 | /* where do we transfer to? */ | |
7da5c4fd | 1059 | xp = (bbuf == NULL ? p : breg); |
984263bc MD |
1060 | |
1061 | /* correct sector number for 1-based BIOS numbering */ | |
1062 | sec++; | |
1063 | ||
ed987dc9 MD |
1064 | /* |
1065 | * Loop retrying the operation a couple of times. The BIOS may also | |
1066 | * retry. | |
1067 | */ | |
984263bc | 1068 | for (retry = 0; retry < 3; retry++) { |
ed987dc9 MD |
1069 | /* |
1070 | * If retrying, reset the drive. | |
1071 | */ | |
984263bc MD |
1072 | if (retry > 0) { |
1073 | v86.ctl = V86_FLAGS; | |
1074 | v86.addr = 0x13; | |
1075 | v86.eax = 0; | |
1076 | v86.edx = od->od_unit; | |
1077 | v86int(); | |
1078 | } | |
1079 | ||
ed987dc9 MD |
1080 | /* |
1081 | * Always use EDD if the disk supports it, otherwise fall back | |
1082 | * to CHS mode (returning an error if the cylinder number is | |
1083 | * too large). | |
1084 | */ | |
1085 | if (od->od_flags & BD_MODEEDD1) { | |
1086 | static unsigned short packet[8]; | |
1087 | ||
1088 | packet[0] = 0x10; | |
1089 | packet[1] = x; | |
1090 | packet[2] = VTOPOFF(xp); | |
1091 | packet[3] = VTOPSEG(xp); | |
1092 | packet[4] = dblk & 0xffff; | |
1093 | packet[5] = dblk >> 16; | |
1094 | packet[6] = 0; | |
1095 | packet[7] = 0; | |
1096 | v86.ctl = V86_FLAGS; | |
1097 | v86.addr = 0x13; | |
1098 | v86.eax = 0x4200; | |
1099 | v86.edx = od->od_unit; | |
1100 | v86.ds = VTOPSEG(packet); | |
1101 | v86.esi = VTOPOFF(packet); | |
1102 | v86int(); | |
4905c847 | 1103 | result = (v86.efl & PSL_C); |
ed987dc9 | 1104 | if (result == 0) |
984263bc | 1105 | break; |
ed987dc9 | 1106 | } else if (cyl < 1024) { |
984263bc MD |
1107 | /* Use normal CHS addressing */ |
1108 | v86.ctl = V86_FLAGS; | |
1109 | v86.addr = 0x13; | |
1110 | v86.eax = 0x200 | x; | |
1111 | v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; | |
1112 | v86.edx = (hd << 8) | od->od_unit; | |
1113 | v86.es = VTOPSEG(xp); | |
1114 | v86.ebx = VTOPOFF(xp); | |
1115 | v86int(); | |
4905c847 | 1116 | result = (v86.efl & PSL_C); |
984263bc | 1117 | if (result == 0) |
ed987dc9 MD |
1118 | break; |
1119 | } else { | |
1120 | result = 1; | |
1121 | break; | |
984263bc MD |
1122 | } |
1123 | } | |
1124 | ||
7da5c4fd MD |
1125 | DEBUG("%u sectors from %u/%u/%d to %p (0x%x) %s", |
1126 | x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); | |
984263bc MD |
1127 | /* BUG here, cannot use v86 in printf because putchar uses it too */ |
1128 | DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", | |
7da5c4fd MD |
1129 | 0x200 | x, |
1130 | ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, | |
1131 | (hd << 8) | od->od_unit, | |
1132 | (v86.eax >> 8) & 0xff); | |
1133 | if (result) | |
984263bc | 1134 | return(-1); |
984263bc MD |
1135 | if (bbuf != NULL) |
1136 | bcopy(breg, p, x * BIOSDISK_SECSIZE); | |
1137 | p += (x * BIOSDISK_SECSIZE); | |
1138 | dblk += x; | |
1139 | resid -= x; | |
1140 | } | |
1141 | ||
1142 | /* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ | |
984263bc MD |
1143 | return(0); |
1144 | } | |
1145 | ||
5ee58eed MD |
1146 | |
1147 | static int | |
1148 | bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) | |
1149 | { | |
1150 | u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; | |
1151 | caddr_t p, xp, bbuf, breg; | |
1152 | ||
1153 | /* Just in case some idiot actually tries to read -1 blocks... */ | |
1154 | if (blks < 0) | |
1155 | return (-1); | |
1156 | ||
1157 | bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ | |
1158 | resid = blks; | |
1159 | p = dest; | |
1160 | ||
7da5c4fd MD |
1161 | /* |
1162 | * Always bounce. Our buffer is probably not segment-addressable. | |
1163 | */ | |
1164 | if (1) { | |
5ee58eed | 1165 | /* |
5fc9dc6d MD |
1166 | * There is a 64k physical boundary somewhere in the destination |
1167 | * buffer, so we have to arrange a suitable bounce buffer. Allocate | |
1168 | * a buffer twice as large as we need to. Use the bottom half | |
1169 | * unless there is a break there, in which case we use the top half. | |
5ee58eed | 1170 | */ |
7da5c4fd MD |
1171 | bbuf = bounce_base; |
1172 | x = min(BOUNCEBUF_SECTS, (unsigned)blks); | |
5ee58eed | 1173 | |
7da5c4fd MD |
1174 | if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == |
1175 | ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE - 1) & 0xffff0000)) { | |
5ee58eed MD |
1176 | breg = bbuf; |
1177 | } else { | |
1178 | breg = bbuf + x * BIOSDISK_SECSIZE; | |
1179 | } | |
7da5c4fd | 1180 | maxfer = x; /* limit transfers to bounce region size */ |
5ee58eed MD |
1181 | } else { |
1182 | breg = bbuf = NULL; | |
1183 | maxfer = 0; | |
1184 | } | |
1185 | ||
1186 | while (resid > 0) { | |
1187 | x = dblk; | |
1188 | cyl = x / bpc; /* block # / blocks per cylinder */ | |
1189 | x %= bpc; /* block offset into cylinder */ | |
1190 | hd = x / od->od_sec; /* offset / blocks per track */ | |
1191 | sec = x % od->od_sec; /* offset into track */ | |
1192 | ||
1193 | /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ | |
e54488bb | 1194 | x = szmin(od->od_sec - sec, resid); |
5ee58eed | 1195 | if (maxfer > 0) |
e54488bb | 1196 | x = szmin(x, maxfer); /* fit bounce buffer */ |
5ee58eed MD |
1197 | |
1198 | /* where do we transfer to? */ | |
7da5c4fd | 1199 | xp = (bbuf == NULL ? p : breg); |
5ee58eed MD |
1200 | |
1201 | /* correct sector number for 1-based BIOS numbering */ | |
1202 | sec++; | |
1203 | ||
1204 | ||
1205 | /* Put your Data In, Put your Data out, | |
1206 | Put your Data In, and shake it all about | |
1207 | */ | |
1208 | if (bbuf != NULL) | |
1209 | bcopy(p, breg, x * BIOSDISK_SECSIZE); | |
1210 | p += (x * BIOSDISK_SECSIZE); | |
1211 | dblk += x; | |
1212 | resid -= x; | |
1213 | ||
ed987dc9 MD |
1214 | /* |
1215 | * Loop retrying the operation a couple of times. The BIOS may also | |
1216 | * retry. | |
1217 | */ | |
5ee58eed | 1218 | for (retry = 0; retry < 3; retry++) { |
ed987dc9 MD |
1219 | /* |
1220 | * If retrying, reset the drive. | |
1221 | */ | |
5ee58eed MD |
1222 | if (retry > 0) { |
1223 | v86.ctl = V86_FLAGS; | |
1224 | v86.addr = 0x13; | |
1225 | v86.eax = 0; | |
1226 | v86.edx = od->od_unit; | |
1227 | v86int(); | |
1228 | } | |
ed987dc9 MD |
1229 | |
1230 | /* | |
1231 | * Always use EDD if the disk supports it, otherwise fall back | |
1232 | * to CHS mode (returning an error if the cylinder number is | |
1233 | * too large). | |
1234 | */ | |
1235 | if (od->od_flags & BD_MODEEDD1) { | |
1236 | static unsigned short packet[8]; | |
1237 | ||
1238 | packet[0] = 0x10; | |
1239 | packet[1] = x; | |
1240 | packet[2] = VTOPOFF(xp); | |
1241 | packet[3] = VTOPSEG(xp); | |
1242 | packet[4] = dblk & 0xffff; | |
1243 | packet[5] = dblk >> 16; | |
1244 | packet[6] = 0; | |
1245 | packet[7] = 0; | |
1246 | v86.ctl = V86_FLAGS; | |
1247 | v86.addr = 0x13; | |
1248 | /* Should we Write with verify ?? 0x4302 ? */ | |
1249 | v86.eax = 0x4300; | |
1250 | v86.edx = od->od_unit; | |
1251 | v86.ds = VTOPSEG(packet); | |
1252 | v86.esi = VTOPOFF(packet); | |
1253 | v86int(); | |
4905c847 | 1254 | result = (v86.efl & PSL_C); |
ed987dc9 | 1255 | if (result == 0) |
5ee58eed | 1256 | break; |
ed987dc9 | 1257 | } else if (cyl < 1024) { |
5ee58eed MD |
1258 | /* Use normal CHS addressing */ |
1259 | v86.ctl = V86_FLAGS; | |
1260 | v86.addr = 0x13; | |
1261 | v86.eax = 0x300 | x; | |
1262 | v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; | |
1263 | v86.edx = (hd << 8) | od->od_unit; | |
1264 | v86.es = VTOPSEG(xp); | |
1265 | v86.ebx = VTOPOFF(xp); | |
1266 | v86int(); | |
4905c847 | 1267 | result = (v86.efl & PSL_C); |
5ee58eed | 1268 | if (result == 0) |
ed987dc9 MD |
1269 | break; |
1270 | } else { | |
1271 | result = 1; | |
1272 | break; | |
5ee58eed MD |
1273 | } |
1274 | } | |
1275 | ||
e451885f | 1276 | DEBUG("%u sectors from %u/%u/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); |
5ee58eed MD |
1277 | /* BUG here, cannot use v86 in printf because putchar uses it too */ |
1278 | DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", | |
1279 | 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); | |
7da5c4fd | 1280 | if (result) |
5ee58eed | 1281 | return(-1); |
5ee58eed MD |
1282 | } |
1283 | ||
7da5c4fd | 1284 | /* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ |
5ee58eed MD |
1285 | return(0); |
1286 | } | |
984263bc MD |
1287 | static int |
1288 | bd_getgeom(struct open_disk *od) | |
1289 | { | |
1290 | ||
1291 | v86.ctl = V86_FLAGS; | |
1292 | v86.addr = 0x13; | |
1293 | v86.eax = 0x800; | |
1294 | v86.edx = od->od_unit; | |
1295 | v86int(); | |
1296 | ||
4905c847 | 1297 | if ((v86.efl & PSL_C) || /* carry set */ |
984263bc MD |
1298 | ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ |
1299 | return(1); | |
1300 | ||
1301 | /* convert max cyl # -> # of cylinders */ | |
1302 | od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; | |
1303 | /* convert max head # -> # of heads */ | |
1304 | od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; | |
1305 | od->od_sec = v86.ecx & 0x3f; | |
1306 | ||
1307 | DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); | |
1308 | return(0); | |
1309 | } | |
1310 | ||
1311 | /* | |
1312 | * Return the BIOS geometry of a given "fixed drive" in a format | |
1313 | * suitable for the legacy bootinfo structure. Since the kernel is | |
1314 | * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we | |
1315 | * prefer to get the information directly, rather than rely on being | |
1316 | * able to put it together from information already maintained for | |
1317 | * different purposes and for a probably different number of drives. | |
1318 | * | |
1319 | * For valid drives, the geometry is expected in the format (31..0) | |
1320 | * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are | |
1321 | * indicated by returning the geometry of a "1.2M" PC-format floppy | |
1322 | * disk. And, incidentally, what is returned is not the geometry as | |
1323 | * such but the highest valid cylinder, head, and sector numbers. | |
1324 | */ | |
1325 | u_int32_t | |
1326 | bd_getbigeom(int bunit) | |
1327 | { | |
1328 | ||
1329 | v86.ctl = V86_FLAGS; | |
1330 | v86.addr = 0x13; | |
1331 | v86.eax = 0x800; | |
1332 | v86.edx = 0x80 + bunit; | |
1333 | v86int(); | |
4905c847 | 1334 | if (v86.efl & PSL_C) |
984263bc MD |
1335 | return 0x4f010f; |
1336 | return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | | |
1337 | (v86.edx & 0xff00) | (v86.ecx & 0x3f); | |
1338 | } | |
1339 | ||
1340 | /* | |
b13267a5 | 1341 | * Return a suitable cdev_t value for (dev). |
984263bc MD |
1342 | * |
1343 | * In the case where it looks like (dev) is a SCSI disk, we allow the number of | |
1344 | * IDE disks to be specified in $num_ide_disks. There should be a Better Way. | |
1345 | */ | |
1346 | int | |
1347 | bd_getdev(struct i386_devdesc *dev) | |
1348 | { | |
1349 | struct open_disk *od; | |
1350 | int biosdev; | |
1351 | int major; | |
1352 | int rootdev; | |
1353 | char *nip, *cp; | |
1354 | int unitofs = 0, i, unit; | |
1355 | ||
1356 | biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); | |
1357 | DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); | |
1358 | if (biosdev == -1) /* not a BIOS device */ | |
1359 | return(-1); | |
1360 | if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ | |
1361 | return(-1); | |
1362 | ||
1363 | if (biosdev < 0x80) { | |
1364 | /* floppy (or emulated floppy) or ATAPI device */ | |
1365 | if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { | |
1366 | /* is an ATAPI disk */ | |
1367 | major = WFDMAJOR; | |
1368 | } else { | |
1369 | /* is a floppy disk */ | |
1370 | major = FDMAJOR; | |
1371 | } | |
1372 | } else { | |
1373 | /* harddisk */ | |
1374 | if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { | |
1375 | /* label OK, disk labelled as SCSI */ | |
1376 | major = DAMAJOR; | |
1377 | /* check for unit number correction hint, now deprecated */ | |
1378 | if ((nip = getenv("num_ide_disks")) != NULL) { | |
1379 | i = strtol(nip, &cp, 0); | |
1380 | /* check for parse error */ | |
1381 | if ((cp != nip) && (*cp == 0)) | |
1382 | unitofs = i; | |
1383 | } | |
1384 | } else { | |
1385 | /* assume an IDE disk */ | |
1386 | major = WDMAJOR; | |
1387 | } | |
1388 | } | |
1389 | /* default root disk unit number */ | |
1390 | unit = (biosdev & 0x7f) - unitofs; | |
1391 | ||
1392 | /* XXX a better kludge to set the root disk unit number */ | |
1393 | if ((nip = getenv("root_disk_unit")) != NULL) { | |
1394 | i = strtol(nip, &cp, 0); | |
1395 | /* check for parse error */ | |
1396 | if ((cp != nip) && (*cp == 0)) | |
1397 | unit = i; | |
1398 | } | |
1399 | ||
1400 | rootdev = MAKEBOOTDEV(major, | |
1401 | (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ | |
1402 | (dev->d_kind.biosdisk.slice + 1) & 0xf, | |
1403 | unit, | |
1404 | dev->d_kind.biosdisk.partition); | |
1405 | DEBUG("dev is 0x%x\n", rootdev); | |
1406 | return(rootdev); | |
1407 | } |