xref: /netbsd/sys/arch/ofppc/ofppc/disksubr.c (revision bf9ec67e)
1 /*	$NetBSD: disksubr.c,v 1.10 2001/08/26 02:47:41 matt Exp $	*/
2 
3 /*
4  * Copyright (C) 1996 Wolfgang Solfrank.
5  * Copyright (C) 1996 TooLs GmbH.
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 TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 #include <sys/param.h>
34 #include <sys/buf.h>
35 #include <sys/conf.h>
36 #include <sys/device.h>
37 #include <sys/disk.h>
38 #include <sys/disklabel.h>
39 #include <sys/disklabel_mbr.h>
40 #include <sys/fcntl.h>
41 #include <sys/ioctl.h>
42 #include <sys/malloc.h>
43 #include <sys/stat.h>
44 #include <sys/systm.h>
45 
46 #include "opt_mbr.h"
47 
48 static inline unsigned short get_short __P((void *p));
49 static inline unsigned long get_long __P((void *p));
50 static int get_netbsd_label __P((dev_t dev, void (*strat)(struct buf *),
51 				 struct disklabel *lp, daddr_t bno));
52 static int mbr_to_label __P((dev_t dev, void (*strat)(struct buf *),
53 			     daddr_t bno, struct disklabel *lp,
54 			     unsigned short *pnpart,
55 			     struct cpu_disklabel *osdep, daddr_t off));
56 
57 /*
58  * Little endian access routines
59  */
60 static inline unsigned short
61 get_short(p)
62 	void *p;
63 {
64 	unsigned char *cp = p;
65 
66 	return cp[0] | (cp[1] << 8);
67 }
68 
69 static inline unsigned long
70 get_long(p)
71 	void *p;
72 {
73 	unsigned char *cp = p;
74 
75 	return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
76 }
77 
78 /*
79  * Get real NetBSD disk label
80  */
81 static int
82 get_netbsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
83 	daddr_t bno)
84 {
85 	struct buf *bp;
86 	struct disklabel *dlp;
87 
88 	/* get a buffer and initialize it */
89 	bp = geteblk((int)lp->d_secsize);
90 	bp->b_dev = dev;
91 
92 	/* Now get the label block */
93 	bp->b_blkno = bno + LABELSECTOR;
94 	bp->b_bcount = lp->d_secsize;
95 	bp->b_flags |= B_READ;
96 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
97 	(*strat)(bp);
98 
99 	if (biowait(bp))
100 		goto done;
101 
102 	for (dlp = (struct disklabel *)bp->b_data;
103 	     dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof (*dlp));
104 	     dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
105 		if (dlp->d_magic == DISKMAGIC
106 		    && dlp->d_magic2 == DISKMAGIC
107 		    && dlp->d_npartitions <= MAXPARTITIONS
108 		    && dkcksum(dlp) == 0) {
109 			*lp = *dlp;
110 			brelse(bp);
111 			return 1;
112 		}
113 	}
114 done:
115 	brelse(bp);
116 	return 0;
117 }
118 
119 /*
120  * Construct disklabel entries from partition entries.
121  */
122 static int
123 mbr_to_label(dev_t dev, void (*strat)(struct buf *), daddr_t bno,
124 	struct disklabel *lp, unsigned short *pnpart,
125 	struct cpu_disklabel *osdep, daddr_t off)
126 {
127 	static int recursion = 0;
128 	struct mbr_partition *mp;
129 	struct partition *pp;
130 	struct buf *bp;
131 	int i, found = 0;
132 
133 	/* Check for recursion overflow. */
134 	if (recursion > MAXPARTITIONS)
135 		return 0;
136 
137 	/*
138 	 * Extended partitions seem to be relative to their first occurence?
139 	 */
140 	if (recursion++ == 1)
141 		off = bno;
142 
143 	/* get a buffer and initialize it */
144 	bp = geteblk((int)lp->d_secsize);
145 	bp->b_dev = dev;
146 
147 	/* Now get the MBR */
148 	bp->b_blkno = bno;
149 	bp->b_bcount = lp->d_secsize;
150 	bp->b_flags |= B_READ;
151 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
152 	(*strat)(bp);
153 
154 	if (biowait(bp))
155 		goto done;
156 
157 	if (get_short(bp->b_data + MBR_MAGICOFF) != MBR_MAGIC)
158 		goto done;
159 
160 	/* Extract info from MBR partition table */
161 	mp = (struct mbr_partition *)(bp->b_data + MBR_PARTOFF);
162 	for (i = 0; i < NMBRPART; i++, mp++) {
163 		if (get_long(&mp->mbrp_size)) {
164 			switch (mp->mbrp_typ) {
165 			case MBR_PTYPE_EXT:
166 				if (*pnpart < MAXPARTITIONS) {
167 					pp = lp->d_partitions + *pnpart;
168 					memset(pp, 0, sizeof *pp);
169 					pp->p_size = get_long(&mp->mbrp_size);
170 					pp->p_offset = off + get_long(&mp->mbrp_start);
171 					++*pnpart;
172 				}
173 				found = mbr_to_label(dev, strat,
174 				    off + get_long(&mp->mbrp_start),
175 				    lp, pnpart, osdep, off);
176 				if (found)
177 					goto done;
178 				break;
179 #ifdef COMPAT_386BSD_MBRPART
180 			case MBR_PTYPE_386BSD:
181 				printf("WARNING: old BSD partition ID!\n");
182 				/* FALLTHROUGH */
183 #endif
184 			case MBR_PTYPE_NETBSD:
185 				/* Found the real NetBSD partition, use it */
186 				osdep->cd_start = off + get_long(&mp->mbrp_start);
187 				found = get_netbsd_label(dev, strat, lp,
188 				    osdep->cd_start);
189 				if (found)
190 					goto done;
191 				/* FALLTHROUGH */
192 			default:
193 				if (*pnpart < MAXPARTITIONS) {
194 					pp = lp->d_partitions + *pnpart;
195 					memset(pp, 0, sizeof *pp);
196 					pp->p_size = get_long(&mp->mbrp_size);
197 					pp->p_offset = off + get_long(&mp->mbrp_start);
198 					++*pnpart;
199 				}
200 				break;
201 			}
202 		}
203 	}
204 done:
205 	recursion--;
206 	brelse(bp);
207 	return found;
208 }
209 
210 /*
211  * Attempt to read a disk label from a device
212  * using the indicated strategy routine.
213  *
214  * If we can't find a NetBSD label, we attempt to fake one
215  * based on the MBR (and extended partition) information
216  */
217 char *
218 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
219 	struct cpu_disklabel *osdep)
220 {
221 	int i;
222 
223 	/* Initialize disk label with some defaults */
224 	if (lp->d_secsize == 0)
225 		lp->d_secsize = DEV_BSIZE;
226 	if (lp->d_secpercyl == 0)
227 		lp->d_secpercyl = 1;
228 	if (lp->d_secperunit == 0)
229 		lp->d_secperunit = 0x7fffffff;
230 	lp->d_npartitions = RAW_PART + 1;
231 	for (i = 0; i < MAXPARTITIONS; i++) {
232 		if (i != RAW_PART) {
233 			lp->d_partitions[i].p_size = 0;
234 			lp->d_partitions[i].p_offset = 0;
235 		}
236 	}
237 	if (lp->d_partitions[RAW_PART].p_size == 0) {
238 		lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
239 		lp->d_partitions[RAW_PART].p_offset = 0;
240 	}
241 
242 	osdep->cd_start = -1;
243 
244 	mbr_to_label(dev, strat, MBR_BBSECTOR, lp, &lp->d_npartitions, osdep, 0);
245 	return 0;
246 }
247 
248 /*
249  * Check new disk label for sensibility before setting it.
250  */
251 int
252 setdisklabel(olp, nlp, openmask, osdep)
253 	struct disklabel *olp, *nlp;
254 	u_long openmask;
255 	struct cpu_disklabel *osdep;
256 {
257 	/* sanity clause */
258 	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
259 	    || (nlp->d_secsize % DEV_BSIZE) != 0)
260 		return EINVAL;
261 
262 	/* special case to allow disklabel to be invalidated */
263 	if (nlp->d_magic == 0xffffffff) {
264 		*olp = *nlp;
265 		return 0;
266 	}
267 
268 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC
269 	    || dkcksum(nlp) != 0)
270 		return EINVAL;
271 
272 	/* openmask parameter ignored */
273 
274 	*olp = *nlp;
275 	return 0;
276 }
277 
278 /*
279  * Write disk label back to device after modification.
280  */
281 int
282 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
283 	struct cpu_disklabel *osdep)
284 {
285 	struct buf *bp;
286 	int error;
287 	struct disklabel label;
288 
289 	/*
290 	 * Try to re-read a disklabel, in case he changed the MBR.
291 	 */
292 	label = *lp;
293 	readdisklabel(dev, strat, &label, osdep);
294 	if (osdep->cd_start < 0)
295 		return EINVAL;
296 
297 	/* get a buffer and initialize it */
298 	bp = geteblk(lp->d_secsize);
299 	bp->b_dev = dev;
300 
301 	bp->b_blkno = osdep->cd_start + LABELSECTOR;
302 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
303 	bp->b_bcount = lp->d_secsize;
304 	bp->b_flags |= B_WRITE;
305 
306 	memcpy((caddr_t)bp->b_data, (caddr_t)lp, sizeof *lp);
307 
308 	(*strat)(bp);
309 	error = biowait(bp);
310 
311 	brelse(bp);
312 
313 	return error;
314 }
315 
316 /*
317  * Determine the size of the transfer, and make sure it is
318  * within the boundaris of the partition.  Adjust transfer
319  * if needed, and signal errors or early completion.
320  */
321 int
322 bounds_check_with_label(bp, lp, wlabel)
323 	struct buf *bp;
324 	struct disklabel *lp;
325 	int wlabel;
326 {
327 	struct partition *p = lp->d_partitions + DISKPART(bp->b_dev);
328 	int sz;
329 
330 	sz = howmany(bp->b_bcount, lp->d_secsize);
331 
332 	if (bp->b_blkno + sz > p->p_size) {
333 		sz = p->p_size - bp->b_blkno;
334 		if (sz == 0) {
335 			/* If axactly at end of disk, return EOF. */
336 			bp->b_resid = bp->b_bcount;
337 			goto done;
338 		}
339 		if (sz < 0) {
340 			/* If past end of disk, return EINVAL. */
341 			bp->b_error = EINVAL;
342 			goto bad;
343 		}
344 		/* Otherwise truncate request. */
345 		bp->b_bcount = sz * lp->d_secsize;
346 	}
347 
348 	/* calculate cylinder for disksort to order transfers with */
349 	bp->b_cylinder = (bp->b_blkno + p->p_offset)
350 			 / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
351 
352 	return 1;
353 
354 bad:
355 	bp->b_flags |= B_ERROR;
356 done:
357 	return 0;
358 }
359