xref: /dragonfly/sys/kern/subr_diskmbr.c (revision 1d1731fa)
1 /*-
2  * Copyright (c) 1994 Bruce D. Evans.
3  * All rights reserved.
4  *
5  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
6  * 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  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	from: @(#)ufs_disksubr.c	7.16 (Berkeley) 5/4/91
37  *	from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
38  * $FreeBSD: src/sys/kern/subr_diskmbr.c,v 1.45 2000/01/28 10:22:07 bde Exp $
39  * $DragonFly: src/sys/kern/subr_diskmbr.c,v 1.4 2003/08/26 21:09:02 rob Exp $
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/buf.h>
45 #include <sys/conf.h>
46 #ifdef PC98
47 #define	PC98_ATCOMPAT
48 #define	dsinit			atcompat_dsinit
49 #endif
50 #include <sys/disklabel.h>
51 #define	DOSPTYP_EXTENDED	5
52 #define	DOSPTYP_EXTENDEDX	15
53 #define	DOSPTYP_ONTRACK		84
54 #include <sys/diskslice.h>
55 #include <sys/malloc.h>
56 #include <sys/syslog.h>
57 #include <sys/device.h>
58 
59 #define TRACE(str)	do { if (dsi_debug) printf str; } while (0)
60 
61 static volatile u_char dsi_debug;
62 
63 static struct dos_partition historical_bogus_partition_table[NDOSPART] = {
64 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
65 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
66 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
67 	{ 0x80, 0, 1, 0, DOSPTYP_386BSD, 255, 255, 255, 0, 50000, },
68 };
69 
70 static int check_part (char *sname, struct dos_partition *dp,
71 			   u_long offset, int nsectors, int ntracks,
72 			   u_long mbr_offset);
73 static void mbr_extended (dev_t dev, struct disklabel *lp,
74 			      struct diskslices *ssp, u_long ext_offset,
75 			      u_long ext_size, u_long base_ext_offset,
76 			      int nsectors, int ntracks, u_long mbr_offset,
77 			      int level);
78 static int mbr_setslice (char *sname, struct disklabel *lp,
79 			     struct diskslice *sp, struct dos_partition *dp,
80 			     u_long br_offset);
81 
82 static int
83 check_part(sname, dp, offset, nsectors, ntracks, mbr_offset )
84 	char	*sname;
85 	struct dos_partition *dp;
86 	u_long	offset;
87 	int	nsectors;
88 	int	ntracks;
89 	u_long	mbr_offset;
90 {
91 	int	chs_ecyl;
92 	int	chs_esect;
93 	int	chs_scyl;
94 	int	chs_ssect;
95 	int	error;
96 	u_long	esector;
97 	u_long	esector1;
98 	u_long	secpercyl;
99 	u_long	ssector;
100 	u_long	ssector1;
101 
102 	secpercyl = (u_long)nsectors * ntracks;
103 	chs_scyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
104 	chs_ssect = DPSECT(dp->dp_ssect);
105 	ssector = chs_ssect - 1 + dp->dp_shd * nsectors + chs_scyl * secpercyl
106 		  + mbr_offset;
107 	ssector1 = offset + dp->dp_start;
108 
109 	/*
110 	 * If ssector1 is on a cylinder >= 1024, then ssector can't be right.
111 	 * Allow the C/H/S for it to be 1023/ntracks-1/nsectors, or correct
112 	 * apart from the cylinder being reduced modulo 1024.  Always allow
113 	 * 1023/255/63.
114 	 */
115 	if ((ssector < ssector1
116 	     && ((chs_ssect == nsectors && dp->dp_shd == ntracks - 1
117 		  && chs_scyl == 1023)
118 		 || (secpercyl != 0
119 		     && (ssector1 - ssector) % (1024 * secpercyl) == 0)))
120 	    || (dp->dp_scyl == 255 && dp->dp_shd == 255
121 		&& dp->dp_ssect == 255)) {
122 		TRACE(("%s: C/H/S start %d/%d/%d, start %lu: allow\n",
123 		       sname, chs_scyl, dp->dp_shd, chs_ssect, ssector1));
124 		ssector = ssector1;
125 	}
126 
127 	chs_ecyl = DPCYL(dp->dp_ecyl, dp->dp_esect);
128 	chs_esect = DPSECT(dp->dp_esect);
129 	esector = chs_esect - 1 + dp->dp_ehd * nsectors + chs_ecyl * secpercyl
130 		  + mbr_offset;
131 	esector1 = ssector1 + dp->dp_size - 1;
132 
133 	/* Allow certain bogus C/H/S values for esector, as above. */
134 	if ((esector < esector1
135 	     && ((chs_esect == nsectors && dp->dp_ehd == ntracks - 1
136 		  && chs_ecyl == 1023)
137 		 || (secpercyl != 0
138 		     && (esector1 - esector) % (1024 * secpercyl) == 0)))
139 	    || (dp->dp_ecyl == 255 && dp->dp_ehd == 255
140 		&& dp->dp_esect == 255)) {
141 		TRACE(("%s: C/H/S end %d/%d/%d, end %lu: allow\n",
142 		       sname, chs_ecyl, dp->dp_ehd, chs_esect, esector1));
143 		esector = esector1;
144 	}
145 
146 	error = (ssector == ssector1 && esector == esector1) ? 0 : EINVAL;
147 	if (bootverbose)
148 		printf("%s: type 0x%x, start %lu, end = %lu, size %lu %s\n",
149 		       sname, dp->dp_typ, ssector1, esector1,
150 		       (u_long)dp->dp_size, error ? "" : ": OK");
151 	if (ssector != ssector1 && bootverbose)
152 		printf("%s: C/H/S start %d/%d/%d (%lu) != start %lu: invalid\n",
153 		       sname, chs_scyl, dp->dp_shd, chs_ssect,
154 		       ssector, ssector1);
155 	if (esector != esector1 && bootverbose)
156 		printf("%s: C/H/S end %d/%d/%d (%lu) != end %lu: invalid\n",
157 		       sname, chs_ecyl, dp->dp_ehd, chs_esect,
158 		       esector, esector1);
159 	return (error);
160 }
161 
162 int
163 dsinit(dev, lp, sspp)
164 	dev_t	dev;
165 	struct disklabel *lp;
166 	struct diskslices **sspp;
167 {
168 	struct buf *bp;
169 	u_char	*cp;
170 	int	dospart;
171 	struct dos_partition *dp;
172 	struct dos_partition *dp0;
173 	struct dos_partition dpcopy[NDOSPART];
174 	int	error;
175 	int	max_ncyls;
176 	int	max_nsectors;
177 	int	max_ntracks;
178 	u_long	mbr_offset;
179 	char	partname[2];
180 	u_long	secpercyl;
181 	char	*sname;
182 	struct diskslice *sp;
183 	struct diskslices *ssp;
184 
185 	mbr_offset = DOSBBSECTOR;
186 reread_mbr:
187 	/* Read master boot record. */
188 	bp = geteblk((int)lp->d_secsize);
189 	bp->b_dev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
190 	bp->b_blkno = mbr_offset;
191 	bp->b_bcount = lp->d_secsize;
192 	bp->b_flags |= B_READ;
193 	BUF_STRATEGY(bp, 1);
194 	if (biowait(bp) != 0) {
195 		diskerr(bp, "reading primary partition table: error",
196 		    LOG_PRINTF, 0, (struct disklabel *)NULL);
197 		printf("\n");
198 		error = EIO;
199 		goto done;
200 	}
201 
202 	/* Weakly verify it. */
203 	cp = bp->b_data;
204 	sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, RAW_PART, partname);
205 	if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
206 		if (bootverbose)
207 			printf("%s: invalid primary partition table: no magic\n",
208 			       sname);
209 		error = EINVAL;
210 		goto done;
211 	}
212 
213 	/* Make a copy of the partition table to avoid alignment problems. */
214 	memcpy(&dpcopy[0], cp + DOSPARTOFF, sizeof(dpcopy));
215 
216 	dp0 = &dpcopy[0];
217 
218 	/* Check for "Ontrack Diskmanager". */
219 	for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
220 		if (dp->dp_typ == DOSPTYP_ONTRACK) {
221 			if (bootverbose)
222 				printf(
223 	    "%s: Found \"Ontrack Disk Manager\" on this disk.\n", sname);
224 			bp->b_flags |= B_INVAL | B_AGE;
225 			brelse(bp);
226 			mbr_offset = 63;
227 			goto reread_mbr;
228 		}
229 	}
230 
231 	if (bcmp(dp0, historical_bogus_partition_table,
232 		 sizeof historical_bogus_partition_table) == 0) {
233 		TRACE(("%s: invalid primary partition table: historical\n",
234 		       sname));
235 		error = EINVAL;
236 		goto done;
237 	}
238 
239 	/* Guess the geometry. */
240 	/*
241 	 * TODO:
242 	 * Perhaps skip entries with 0 size.
243 	 * Perhaps only look at entries of type DOSPTYP_386BSD.
244 	 */
245 	max_ncyls = 0;
246 	max_nsectors = 0;
247 	max_ntracks = 0;
248 	for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
249 		int	ncyls;
250 		int	nsectors;
251 		int	ntracks;
252 
253 		ncyls = DPCYL(dp->dp_ecyl, dp->dp_esect) + 1;
254 		if (max_ncyls < ncyls)
255 			max_ncyls = ncyls;
256 		nsectors = DPSECT(dp->dp_esect);
257 		if (max_nsectors < nsectors)
258 			max_nsectors = nsectors;
259 		ntracks = dp->dp_ehd + 1;
260 		if (max_ntracks < ntracks)
261 			max_ntracks = ntracks;
262 	}
263 
264 	/*
265 	 * Check that we have guessed the geometry right by checking the
266 	 * partition entries.
267 	 */
268 	/*
269 	 * TODO:
270 	 * As above.
271 	 * Check for overlaps.
272 	 * Check against d_secperunit if the latter is reliable.
273 	 */
274 	error = 0;
275 	for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
276 		if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0
277 		    && dp->dp_start == 0 && dp->dp_size == 0)
278 			continue;
279 		sname = dsname(dev, dkunit(dev), BASE_SLICE + dospart,
280 			       RAW_PART, partname);
281 
282 		/*
283 		 * Temporarily ignore errors from this check.  We could
284 		 * simplify things by accepting the table eariler if we
285 		 * always ignore errors here.  Perhaps we should always
286 		 * accept the table if the magic is right but not let
287 		 * bad entries affect the geometry.
288 		 */
289 		check_part(sname, dp, mbr_offset, max_nsectors, max_ntracks,
290 			   mbr_offset);
291 	}
292 	if (error != 0)
293 		goto done;
294 
295 	/*
296 	 * Accept the DOS partition table.
297 	 * First adjust the label (we have been careful not to change it
298 	 * before we can guarantee success).
299 	 */
300 	secpercyl = (u_long)max_nsectors * max_ntracks;
301 	if (secpercyl != 0) {
302 #if 0
303 		u_long	secperunit;
304 #endif
305 
306 		lp->d_nsectors = max_nsectors;
307 		lp->d_ntracks = max_ntracks;
308 		lp->d_secpercyl = secpercyl;
309 		/*
310 		 * Temporarily, don't even consider adjusting the drive's
311 		 * size, since the adjusted size may exceed the hardware's
312 		 * addressing capabilities.  The adjustment helped mainly
313 		 * for ancient MFM drives with > 1024 cylinders, but now
314 		 * breaks at least IDE drives with 63*16*65536 sectors if
315 		 * they are controlled by the wd driver in CHS mode.
316 		 */
317 #if 0
318 		secperunit = secpercyl * max_ncyls;
319 		if (lp->d_secperunit < secperunit)
320 			lp->d_secperunit = secperunit;
321 #endif
322 		lp->d_ncylinders = lp->d_secperunit / secpercyl;
323 	}
324 
325 	/*
326 	 * We are passed a pointer to a suitably initialized minimal
327 	 * slices "struct" with no dangling pointers in it.  Replace it
328 	 * by a maximal one.  This usually oversizes the "struct", but
329 	 * enlarging it while searching for logical drives would be
330 	 * inconvenient.
331 	 */
332 	free(*sspp, M_DEVBUF);
333 	ssp = dsmakeslicestruct(MAX_SLICES, lp);
334 	*sspp = ssp;
335 
336 	/* Initialize normal slices. */
337 	sp = &ssp->dss_slices[BASE_SLICE];
338 	for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++, sp++) {
339 		sname = dsname(dev, dkunit(dev), BASE_SLICE + dospart,
340 			       RAW_PART, partname);
341 		(void)mbr_setslice(sname, lp, sp, dp, mbr_offset);
342 	}
343 	ssp->dss_nslices = BASE_SLICE + NDOSPART;
344 
345 	/* Handle extended partitions. */
346 	sp -= NDOSPART;
347 	for (dospart = 0; dospart < NDOSPART; dospart++, sp++)
348 		if (sp->ds_type == DOSPTYP_EXTENDED ||
349 		    sp->ds_type == DOSPTYP_EXTENDEDX)
350 			mbr_extended(bp->b_dev, lp, ssp,
351 				     sp->ds_offset, sp->ds_size, sp->ds_offset,
352 				     max_nsectors, max_ntracks, mbr_offset, 1);
353 
354 	/*
355 	 * mbr_extended() abuses ssp->dss_nslices for the number of slices
356 	 * that would be found if there were no limit on the number of slices
357 	 * in *ssp.  Cut it back now.
358 	 */
359 	if (ssp->dss_nslices > MAX_SLICES)
360 		ssp->dss_nslices = MAX_SLICES;
361 
362 done:
363 	bp->b_flags |= B_INVAL | B_AGE;
364 	brelse(bp);
365 	if (error == EINVAL)
366 		error = 0;
367 	return (error);
368 }
369 
370 void
371 mbr_extended(dev, lp, ssp, ext_offset, ext_size, base_ext_offset, nsectors,
372 	     ntracks, mbr_offset, level)
373 	dev_t	dev;
374 	struct disklabel *lp;
375 	struct diskslices *ssp;
376 	u_long	ext_offset;
377 	u_long	ext_size;
378 	u_long	base_ext_offset;
379 	int	nsectors;
380 	int	ntracks;
381 	u_long	mbr_offset;
382 	int	level;
383 {
384 	struct buf *bp;
385 	u_char	*cp;
386 	int	dospart;
387 	struct dos_partition *dp;
388 	struct dos_partition dpcopy[NDOSPART];
389 	u_long	ext_offsets[NDOSPART];
390 	u_long	ext_sizes[NDOSPART];
391 	char	partname[2];
392 	int	slice;
393 	char	*sname;
394 	struct diskslice *sp;
395 
396 	if (level >= 16) {
397 		printf(
398 	"%s: excessive recursion in search for slices; aborting search\n",
399 		       devtoname(dev));
400 		return;
401 	}
402 
403 	/* Read extended boot record. */
404 	bp = geteblk((int)lp->d_secsize);
405 	bp->b_dev = dev;
406 	bp->b_blkno = ext_offset;
407 	bp->b_bcount = lp->d_secsize;
408 	bp->b_flags |= B_READ;
409 	BUF_STRATEGY(bp, 1);
410 	if (biowait(bp) != 0) {
411 		diskerr(bp, "reading extended partition table: error",
412 		    LOG_PRINTF, 0, (struct disklabel *)NULL);
413 		printf("\n");
414 		goto done;
415 	}
416 
417 	/* Weakly verify it. */
418 	cp = bp->b_data;
419 	if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
420 		sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, RAW_PART,
421 			       partname);
422 		if (bootverbose)
423 			printf("%s: invalid extended partition table: no magic\n",
424 			       sname);
425 		goto done;
426 	}
427 
428 	/* Make a copy of the partition table to avoid alignment problems. */
429 	memcpy(&dpcopy[0], cp + DOSPARTOFF, sizeof(dpcopy));
430 
431 	slice = ssp->dss_nslices;
432 	for (dospart = 0, dp = &dpcopy[0]; dospart < NDOSPART;
433 	    dospart++, dp++) {
434 		ext_sizes[dospart] = 0;
435 		if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0
436 		    && dp->dp_start == 0 && dp->dp_size == 0)
437 			continue;
438 		if (dp->dp_typ == DOSPTYP_EXTENDED ||
439 		    dp->dp_typ == DOSPTYP_EXTENDEDX) {
440 			static char buf[32];
441 
442 			sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE,
443 				       RAW_PART, partname);
444 			snprintf(buf, sizeof(buf), "%s", sname);
445 			if (strlen(buf) < sizeof buf - 11)
446 				strcat(buf, "<extended>");
447 			check_part(buf, dp, base_ext_offset, nsectors,
448 				   ntracks, mbr_offset);
449 			ext_offsets[dospart] = base_ext_offset + dp->dp_start;
450 			ext_sizes[dospart] = dp->dp_size;
451 		} else {
452 			sname = dsname(dev, dkunit(dev), slice, RAW_PART,
453 				       partname);
454 			check_part(sname, dp, ext_offset, nsectors, ntracks,
455 				   mbr_offset);
456 			if (slice >= MAX_SLICES) {
457 				printf("%s: too many slices\n", sname);
458 				slice++;
459 				continue;
460 			}
461 			sp = &ssp->dss_slices[slice];
462 			if (mbr_setslice(sname, lp, sp, dp, ext_offset) != 0)
463 				continue;
464 			slice++;
465 		}
466 	}
467 	ssp->dss_nslices = slice;
468 
469 	/* If we found any more slices, recursively find all the subslices. */
470 	for (dospart = 0; dospart < NDOSPART; dospart++)
471 		if (ext_sizes[dospart] != 0)
472 			mbr_extended(dev, lp, ssp, ext_offsets[dospart],
473 				     ext_sizes[dospart], base_ext_offset,
474 				     nsectors, ntracks, mbr_offset, ++level);
475 
476 done:
477 	bp->b_flags |= B_INVAL | B_AGE;
478 	brelse(bp);
479 }
480 
481 static int
482 mbr_setslice(sname, lp, sp, dp, br_offset)
483 	char	*sname;
484 	struct disklabel *lp;
485 	struct diskslice *sp;
486 	struct dos_partition *dp;
487 	u_long	br_offset;
488 {
489 	u_long	offset;
490 	u_long	size;
491 
492 	offset = br_offset + dp->dp_start;
493 	if (offset > lp->d_secperunit || offset < br_offset) {
494 		printf(
495 		"%s: slice starts beyond end of the disk: rejecting it\n",
496 		       sname);
497 		return (1);
498 	}
499 	size = lp->d_secperunit - offset;
500 	if (size >= dp->dp_size)
501 		size = dp->dp_size;
502 	else
503 		printf(
504 "%s: slice extends beyond end of disk: truncating from %lu to %lu sectors\n",
505 		       sname, (u_long)dp->dp_size, size);
506 	sp->ds_offset = offset;
507 	sp->ds_size = size;
508 	sp->ds_type = dp->dp_typ;
509 #ifdef PC98_ATCOMPAT
510 	/* Fake FreeBSD(98). */
511 	if (sp->ds_type == DOSPTYP_386BSD)
512 		sp->ds_type = 0x94;
513 #endif
514 #if 0
515 	lp->d_subtype |= (lp->d_subtype & 3) | dospart | DSTYPE_INDOSPART;
516 #endif
517 	return (0);
518 }
519 
520 #ifdef __alpha__
521 void
522 alpha_fix_srm_checksum(bp)
523 	struct buf *bp;
524 {
525 	u_int64_t *p;
526 	u_int64_t sum;
527 	int i;
528 
529 	p = (u_int64_t *) bp->b_data;
530 	sum = 0;
531 	for (i = 0; i < 63; i++)
532 		sum += p[i];
533 	p[63] = sum;
534 }
535 #endif
536