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