xref: /netbsd/sys/arch/landisk/stand/boot/biosdisk.c (revision 6550d01e)
1 /*	$NetBSD: biosdisk.c,v 1.1 2006/09/01 21:26:18 uwe 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@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@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 
69 #include <lib/libsa/stand.h>
70 #include <lib/libsa/saerrno.h>
71 #include <lib/libsa/loadfile.h>
72 
73 #include <machine/stdarg.h>
74 #include <machine/disklabel.h>
75 
76 #include "biosdisk.h"
77 #include "biosdisk_ll.h"
78 
79 #include "bootinfo.h"
80 
81 #define BUFSIZE (1 * BIOSDISK_SECSIZE)
82 
83 struct biosdisk {
84 	int	dev;
85 	int	boff;
86 	char	buf[BUFSIZE];
87 };
88 
89 static struct btinfo_bootdisk bi_disk;
90 
91 #define	RF_PROTECTED_SECTORS	64	/* XXX refer to <.../rf_optnames.h> */
92 
93 static struct biosdisk *
94 alloc_biosdisk(int dev)
95 {
96 	struct biosdisk *d;
97 
98 	d = (struct biosdisk *)ALLOC(sizeof(*d));
99 	if (d == NULL)
100 		return (NULL);
101 
102 	memset(d, 0, sizeof(*d));
103 	d->dev = dev;
104 
105 	return (d);
106 }
107 
108 static int
109 check_label(struct biosdisk *d, int sector)
110 {
111 	struct disklabel *lp;
112 
113 	/* find partition in NetBSD disklabel */
114 	if (readsects(d->dev, sector + LABELSECTOR, d->buf, 1)) {
115 #ifdef DISK_DEBUG
116 		printf("Error reading disklabel\n");
117 #endif
118 		return (EIO);
119 	}
120 
121 	lp = (struct disklabel *)(d->buf + LABELOFFSET);
122 	if (lp->d_magic != DISKMAGIC || dkcksum(lp)) {
123 #ifdef DISK_DEBUG
124 		printf("warning: no disklabel\n");
125 #endif
126 		return (-1);
127 	}
128 
129 	d->boff = sector;
130 	return (0);
131 }
132 
133 static int
134 read_label(struct biosdisk *d)
135 {
136 	struct disklabel dflt_lbl;
137 	struct mbr_partition mbr[MBR_PART_COUNT];
138 	struct partition *p;
139 	int sector, i;
140 	int error;
141 	int typ;
142 	int ext_base, this_ext, next_ext;
143 #ifdef COMPAT_386BSD_MBRPART
144 	int sector_386bsd = -1;
145 #endif
146 
147 	memset(&dflt_lbl, 0, sizeof dflt_lbl);
148 	dflt_lbl.d_npartitions = 8;
149 
150 	d->boff = 0;
151 
152 	/*
153 	 * find NetBSD Partition in DOS partition table
154 	 * XXX check magic???
155 	 */
156 	ext_base = 0;
157 	next_ext = 0;
158 	for (;;) {
159 		this_ext = ext_base + next_ext;
160 		next_ext = 0;
161 		if (readsects(d->dev, this_ext, d->buf, 1)) {
162 #ifdef DISK_DEBUG
163 			printf("error reading MBR sector %d\n", this_ext);
164 #endif
165 			return (EIO);
166 		}
167 		memcpy(&mbr, ((struct mbr_sector *)d->buf)->mbr_parts, sizeof(mbr));
168 		/* Look for NetBSD partition ID */
169 		for (i = 0; i < MBR_PART_COUNT; i++) {
170 			typ = mbr[i].mbrp_type;
171 			if (typ == 0)
172 				continue;
173 			sector = this_ext + mbr[i].mbrp_start;
174 			if (typ == MBR_PTYPE_NETBSD) {
175 				error = check_label(d, sector);
176 				if (error >= 0)
177 					return error;
178 			}
179 			if (MBR_IS_EXTENDED(typ)) {
180 				next_ext = mbr[i].mbrp_start;
181 				continue;
182 			}
183 #ifdef COMPAT_386BSD_MBRPART
184 			if (this_ext == 0 && typ == MBR_PTYPE_386BSD)
185 				sector_386bsd = sector;
186 #endif
187 			if (this_ext != 0) {
188 				if (dflt_lbl.d_npartitions >= MAXPARTITIONS)
189 					continue;
190 				p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++];
191 			} else
192 				p = &dflt_lbl.d_partitions[i];
193 			p->p_offset = sector;
194 			p->p_size = mbr[i].mbrp_size;
195 			p->p_fstype = xlat_mbr_fstype(typ);
196 		}
197 		if (next_ext == 0)
198 			break;
199 		if (ext_base == 0) {
200 			ext_base = next_ext;
201 			next_ext = 0;
202 		}
203 	}
204 
205 	sector = 0;
206 #ifdef COMPAT_386BSD_MBRPART
207 	if (sector_386bsd != -1) {
208 		printf("old BSD partition ID!\n");
209 		sector = sector_386bsd;
210 	}
211 #endif
212 
213 	/*
214 	 * One of two things:
215 	 * 	1. no MBR
216 	 *	2. no NetBSD partition in MBR
217 	 *
218 	 * We simply default to "start of disk" in this case and
219 	 * press on.
220 	 */
221 	error = check_label(d, sector);
222 	if (error >= 0)
223 		return (error);
224 
225 	/*
226 	 * Nothing at start of disk, return info from mbr partitions.
227 	 */
228 	/* XXX fill it to make checksum match kernel one */
229 	dflt_lbl.d_checksum = dkcksum(&dflt_lbl);
230 	memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl));
231 	return (-1);
232 }
233 
234 
235 /*
236  * Determine likely partition for possible sector number of dos
237  * partition.
238  */
239 u_int
240 biosdisk_findptn(int biosdev, u_int sector)
241 {
242 	struct biosdisk *d;
243 	u_int partition = 0;
244 	struct disklabel *lp;
245 
246 	/* Look for netbsd partition that is the dos boot one */
247 	d = alloc_biosdisk(biosdev);
248 	if (d == NULL)
249 		return (0);
250 
251 	if (read_label(d) == 0) {
252 		lp = (struct disklabel *)(d->buf + LABELOFFSET);
253 		for (partition = lp->d_npartitions; --partition;){
254 			if (lp->d_partitions[partition].p_fstype == FS_UNUSED)
255 				continue;
256 			if (lp->d_partitions[partition].p_offset == sector)
257 				break;
258 		}
259 	}
260 
261 	DEALLOC(d, sizeof(*d));
262 	return (partition);
263 }
264 
265 int
266 biosdisk_open(struct open_file *f, ...)
267 {
268 	va_list ap;
269 	struct biosdisk *d;
270 	struct disklabel *lp;
271 	int dev;
272 	int partition;
273 	int error = 0;
274 
275 	va_start(ap, f);
276 	dev = va_arg(ap, int);
277 	d = alloc_biosdisk(dev);
278 	if (d == NULL) {
279 		error = ENXIO;
280 		goto out;
281 	}
282 
283 	partition = va_arg(ap, int);
284 	bi_disk.biosdev = d->dev;
285 	bi_disk.partition = partition;
286 	bi_disk.labelsector = -1;
287 
288 	if (partition == RAW_PART)
289 		goto nolabel;
290 	error = read_label(d);
291 	if (error == -1) {
292 		error = 0;
293 		goto nolabel;
294 	}
295 	if (error)
296 		goto out;
297 
298 	lp = (struct disklabel *)(d->buf + LABELOFFSET);
299 	if (partition >= lp->d_npartitions ||
300 	   lp->d_partitions[partition].p_fstype == FS_UNUSED) {
301 #ifdef DISK_DEBUG
302 		printf("illegal partition\n");
303 #endif
304 		error = EPART;
305 		goto out;
306 	}
307 	bi_disk.labelsector = d->boff + LABELSECTOR;
308 	bi_disk.label.type = lp->d_type;
309 	memcpy(bi_disk.label.packname, lp->d_packname, 16);
310 	bi_disk.label.checksum = lp->d_checksum;
311 
312 	d->boff = lp->d_partitions[partition].p_offset;
313 	if (lp->d_partitions[partition].p_fstype == FS_RAID) {
314 		d->boff += RF_PROTECTED_SECTORS;
315 	}
316 nolabel:
317 
318 #ifdef DISK_DEBUG
319 	printf("partition @%d\n", d->boff);
320 #endif
321 
322 	BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk));
323 
324 	f->f_devdata = d;
325 out:
326         va_end(ap);
327 	if (error)
328 		DEALLOC(d, sizeof(struct biosdisk));
329 	return (error);
330 }
331 
332 int
333 biosdisk_close(struct open_file *f)
334 {
335 	struct biosdisk *d = f->f_devdata;
336 
337 	DEALLOC(d, sizeof(struct biosdisk));
338 	f->f_devdata = NULL;
339 
340 	return (0);
341 }
342 
343 int
344 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg)
345 {
346 
347 	return (EIO);
348 }
349 
350 int
351 biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize)
352 {
353 	struct biosdisk *d;
354 	int blks;
355 	int frag;
356 
357 	if (flag != F_READ)
358 		return EROFS;
359 
360 	d = (struct biosdisk *)devdata;
361 
362 	dblk += d->boff;
363 
364 	blks = size / BIOSDISK_SECSIZE;
365 	if (blks && readsects(d->dev, dblk, buf, blks)) {
366 		if (rsize)
367 			*rsize = 0;
368 		return (EIO);
369 	}
370 
371 	/* do we really need this? */
372 	frag = size % BIOSDISK_SECSIZE;
373 	if (frag) {
374 		if (readsects(d->dev, dblk + blks, d->buf, 1)) {
375 			if (rsize)
376 				*rsize = blks * BIOSDISK_SECSIZE;
377 			return (EIO);
378 		}
379 		memcpy(buf + blks * BIOSDISK_SECSIZE, d->buf, frag);
380 	}
381 
382 	if (rsize)
383 		*rsize = size;
384 	return (0);
385 }
386