biosdisk.c revision 1.27 1 /* $NetBSD: biosdisk.c,v 1.27 2008/01/04 20:05:46 dsl Exp $ */
2 #define DISK_DEBUG
3
4 /*
5 * Copyright (c) 1996, 1998
6 * Matthias Drochner. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30 /*
31 * raw BIOS disk device for libsa.
32 * needs lowlevel parts from bios_disk.S and biosdisk_ll.c
33 * partly from netbsd:sys/arch/i386/boot/disk.c
34 * no bad144 handling!
35 *
36 * A lot of this must match sys/kern/subr_disk_mbr.c
37 */
38
39 /*
40 * Ported to boot 386BSD by Julian Elischer (julian (at) tfs.com) Sept 1992
41 *
42 * Mach Operating System
43 * Copyright (c) 1992, 1991 Carnegie Mellon University
44 * All Rights Reserved.
45 *
46 * Permission to use, copy, modify and distribute this software and its
47 * documentation is hereby granted, provided that both the copyright
48 * notice and this permission notice appear in all copies of the
49 * software, derivative works or modified versions, and any portions
50 * thereof, and that both notices appear in supporting documentation.
51 *
52 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
53 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
54 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
55 *
56 * Carnegie Mellon requests users of this software to return to
57 *
58 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
59 * School of Computer Science
60 * Carnegie Mellon University
61 * Pittsburgh PA 15213-3890
62 *
63 * any improvements or extensions that they make and grant Carnegie Mellon
64 * the rights to redistribute these changes.
65 */
66
67 #include <sys/types.h>
68 #include <sys/disklabel.h>
69 #include <sys/md5.h>
70 #include <sys/param.h>
71
72 #include <fs/cd9660/iso.h>
73
74 #include <lib/libsa/stand.h>
75 #include <lib/libsa/saerrno.h>
76 #include <machine/stdarg.h>
77
78 #include "libi386.h"
79 #include "biosdisk_ll.h"
80 #include "biosdisk.h"
81 #ifdef _STANDALONE
82 #include "bootinfo.h"
83 #endif
84
85 #define BUFSIZE 2048 /* must be large enough for a CD sector */
86
87 struct biosdisk {
88 struct biosdisk_ll ll;
89 int boff;
90 char buf[BUFSIZE];
91 };
92
93 #ifdef _STANDALONE
94 static struct btinfo_bootdisk bi_disk;
95 static struct btinfo_bootwedge bi_wedge;
96 #endif
97
98 #define RF_PROTECTED_SECTORS 64 /* XXX refer to <.../rf_optnames.h> */
99
100 int
101 biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
102 void *buf, size_t *rsize)
103 {
104 struct biosdisk *d;
105 int blks, frag;
106
107 if (flag != F_READ)
108 return EROFS;
109
110 d = (struct biosdisk *) devdata;
111
112 if (d->ll.type == BIOSDISK_TYPE_CD)
113 dblk = dblk * DEV_BSIZE / ISO_DEFAULT_BLOCK_SIZE;
114
115 dblk += d->boff;
116
117 blks = size / d->ll.secsize;
118 if (blks && readsects(&d->ll, dblk, blks, buf, 0)) {
119 if (rsize)
120 *rsize = 0;
121 return EIO;
122 }
123
124 /* needed for CD */
125 frag = size % d->ll.secsize;
126 if (frag) {
127 if (readsects(&d->ll, dblk + blks, 1, d->buf, 0)) {
128 if (rsize)
129 *rsize = blks * d->ll.secsize;
130 return EIO;
131 }
132 memcpy(buf + blks * d->ll.secsize, d->buf, frag);
133 }
134
135 if (rsize)
136 *rsize = size;
137 return 0;
138 }
139
140 static struct biosdisk *
141 alloc_biosdisk(int biosdev)
142 {
143 struct biosdisk *d;
144
145 d = alloc(sizeof(*d));
146 if (d == NULL)
147 return NULL;
148 memset(d, 0, sizeof(*d));
149
150 d->ll.dev = biosdev;
151 if (set_geometry(&d->ll, NULL)) {
152 #ifdef DISK_DEBUG
153 printf("no geometry information\n");
154 #endif
155 dealloc(d, sizeof(*d));
156 return NULL;
157 }
158 return d;
159 }
160
161 #ifndef NO_DISKLABEL
162 static int
163 check_label(struct biosdisk *d, int sector)
164 {
165 struct disklabel *lp;
166
167 /* find partition in NetBSD disklabel */
168 if (readsects(&d->ll, sector + LABELSECTOR, 1, d->buf, 0)) {
169 #ifdef DISK_DEBUG
170 printf("Error reading disklabel\n");
171 #endif
172 return EIO;
173 }
174 lp = (struct disklabel *) (d->buf + LABELOFFSET);
175 if (lp->d_magic != DISKMAGIC || dkcksum(lp)) {
176 #ifdef DISK_DEBUG
177 printf("warning: no disklabel in sector %u\n", sector);
178 #endif
179 return -1;
180 }
181
182 d->boff = sector;
183 return 0;
184 }
185
186 static int
187 read_label(struct biosdisk *d)
188 {
189 struct disklabel dflt_lbl;
190 struct mbr_partition mbr[MBR_PART_COUNT];
191 struct partition *p;
192 int sector, i;
193 int error;
194 int typ;
195 int ext_base, this_ext, next_ext;
196 #ifdef COMPAT_386BSD_MBRPART
197 int sector_386bsd = -1;
198 #endif
199
200 memset(&dflt_lbl, 0, sizeof(dflt_lbl));
201 dflt_lbl.d_npartitions = 8;
202
203 d->boff = 0;
204
205 if (d->ll.type != BIOSDISK_TYPE_HD)
206 /* No label on floppy and CD */
207 return -1;
208
209 /*
210 * find NetBSD Partition in DOS partition table
211 * XXX check magic???
212 */
213 ext_base = 0;
214 next_ext = 0;
215 for (;;) {
216 this_ext = ext_base + next_ext;
217 next_ext = 0;
218 if (readsects(&d->ll, this_ext, 1, d->buf, 0)) {
219 #ifdef DISK_DEBUG
220 printf("error reading MBR sector %d\n", this_ext);
221 #endif
222 return EIO;
223 }
224 memcpy(&mbr, ((struct mbr_sector *)d->buf)->mbr_parts,
225 sizeof(mbr));
226 /* Look for NetBSD partition ID */
227 for (i = 0; i < MBR_PART_COUNT; i++) {
228 typ = mbr[i].mbrp_type;
229 if (typ == 0)
230 continue;
231 sector = this_ext + mbr[i].mbrp_start;
232 #ifdef DISK_DEBUG
233 printf("ptn type %d in sector %u\n", typ, sector);
234 #endif
235 if (typ == MBR_PTYPE_NETBSD) {
236 error = check_label(d, sector);
237 if (error >= 0)
238 return error;
239 }
240 if (MBR_IS_EXTENDED(typ)) {
241 next_ext = mbr[i].mbrp_start;
242 continue;
243 }
244 #ifdef COMPAT_386BSD_MBRPART
245 if (this_ext == 0 && typ == MBR_PTYPE_386BSD)
246 sector_386bsd = sector;
247 #endif
248 if (this_ext != 0) {
249 if (dflt_lbl.d_npartitions >= MAXPARTITIONS)
250 continue;
251 p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++];
252 } else
253 p = &dflt_lbl.d_partitions[i];
254 p->p_offset = sector;
255 p->p_size = mbr[i].mbrp_size;
256 p->p_fstype = xlat_mbr_fstype(typ);
257 }
258 if (next_ext == 0)
259 break;
260 if (ext_base == 0) {
261 ext_base = next_ext;
262 next_ext = 0;
263 }
264 }
265
266 sector = 0;
267 #ifdef COMPAT_386BSD_MBRPART
268 if (sector_386bsd != -1) {
269 printf("old BSD partition ID!\n");
270 sector = sector_386bsd;
271 }
272 #endif
273
274 /*
275 * One of two things:
276 * 1. no MBR
277 * 2. no NetBSD partition in MBR
278 *
279 * We simply default to "start of disk" in this case and
280 * press on.
281 */
282 error = check_label(d, sector);
283 if (error >= 0)
284 return error;
285
286 /*
287 * Nothing at start of disk, return info from mbr partitions.
288 */
289 /* XXX fill it to make checksum match kernel one */
290 dflt_lbl.d_checksum = dkcksum(&dflt_lbl);
291 memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl));
292 return 0;
293 }
294 #endif /* NO_DISKLABEL */
295
296 /* Determine likely partition for possible sector number of dos
297 * partition.
298 */
299
300 int
301 biosdisk_findpartition(int biosdev, u_int sector)
302 {
303 #ifdef NO_DISKLABEL
304 return 0;
305 #else
306 struct biosdisk *d;
307 int partition = 0;
308 struct disklabel *lp;
309 #ifdef DISK_DEBUG
310 printf("looking for partition device %x, sector %u\n", biosdev, sector);
311 #endif
312
313 /* Look for netbsd partition that is the dos boot one */
314 d = alloc_biosdisk(biosdev);
315 if (d == NULL)
316 return 0;
317
318 if (read_label(d) == 0) {
319 lp = (struct disklabel *)(d->buf + LABELOFFSET);
320 for (partition = lp->d_npartitions; --partition;){
321 if (lp->d_partitions[partition].p_fstype == FS_UNUSED)
322 continue;
323 if (lp->d_partitions[partition].p_offset == sector)
324 break;
325 }
326 }
327
328 dealloc(d, sizeof(*d));
329 return partition;
330 #endif /* NO_DISKLABEL */
331 }
332
333 int
334 biosdisk_open(struct open_file *f, ...)
335 /* struct open_file *f, int biosdev, int partition */
336 {
337 va_list ap;
338 struct biosdisk *d;
339 int biosdev;
340 int partition;
341 #ifndef NO_DISKLABEL
342 struct disklabel *lp;
343 #endif
344 int error = 0;
345
346 va_start(ap, f);
347 biosdev = va_arg(ap, int);
348 d = alloc_biosdisk(biosdev);
349 if (d == NULL) {
350 error = ENXIO;
351 goto out;
352 }
353
354 partition = va_arg(ap, int);
355 #ifdef _STANDALONE
356 bi_disk.biosdev = d->ll.dev;
357 bi_disk.partition = partition;
358 bi_disk.labelsector = -1;
359
360 bi_wedge.biosdev = d->ll.dev;
361 bi_wedge.matchblk = -1;
362 #endif
363
364 #ifndef NO_DISKLABEL
365 if (partition == RAW_PART)
366 goto nolabel;
367 error = read_label(d);
368 if (error == -1) {
369 error = 0;
370 goto nolabel;
371 }
372 if (error)
373 goto out;
374
375 lp = (struct disklabel *) (d->buf + LABELOFFSET);
376 if (partition >= lp->d_npartitions ||
377 lp->d_partitions[partition].p_fstype == FS_UNUSED) {
378 #ifdef DISK_DEBUG
379 printf("illegal partition\n");
380 #endif
381 error = EPART;
382 goto out;
383 }
384 #ifdef _STANDALONE
385 bi_disk.labelsector = d->boff + LABELSECTOR;
386 bi_disk.label.type = lp->d_type;
387 memcpy(bi_disk.label.packname, lp->d_packname, 16);
388 bi_disk.label.checksum = lp->d_checksum;
389
390 bi_wedge.startblk = lp->d_partitions[partition].p_offset;
391 bi_wedge.nblks = lp->d_partitions[partition].p_size;
392 bi_wedge.matchblk = d->boff + LABELSECTOR;
393 bi_wedge.matchnblks = 1;
394 {
395 MD5_CTX ctx;
396
397 MD5Init(&ctx);
398 MD5Update(&ctx, (void *) d->buf, 512);
399 MD5Final(bi_wedge.matchhash, &ctx);
400 }
401 #endif
402 d->boff = lp->d_partitions[partition].p_offset;
403 if (lp->d_partitions[partition].p_fstype == FS_RAID)
404 d->boff += RF_PROTECTED_SECTORS;
405 nolabel:
406 #endif /* NO_DISKLABEL */
407
408 #ifdef DISK_DEBUG
409 printf("partition @%d\n", d->boff);
410 #endif
411
412 #ifdef _STANDALONE
413 BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk));
414 BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge));
415 #endif
416
417 f->f_devdata = d;
418 out:
419 va_end(ap);
420 if (error)
421 dealloc(d, sizeof(struct biosdisk));
422 return error;
423 }
424
425 #ifndef LIBSA_NO_FS_CLOSE
426 int
427 biosdisk_close(struct open_file *f)
428 {
429 struct biosdisk *d = f->f_devdata;
430
431 /* let the floppy drive go off */
432 if (d->ll.type == BIOSDISK_TYPE_FD)
433 delay(3000000); /* 2s is enough on all PCs I found */
434
435 dealloc(d, sizeof(struct biosdisk));
436 f->f_devdata = NULL;
437 return 0;
438 }
439 #endif
440
441 int
442 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg)
443 {
444 return EIO;
445 }
446