xref: /openbsd/sbin/fdisk/mbr.c (revision e5dd7070)
1 /*	$OpenBSD: mbr.c,v 1.67 2016/09/01 16:17:46 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Tobias Weingartner
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>	/* DEV_BSIZE */
20 #include <sys/ioctl.h>
21 #include <sys/disklabel.h>
22 #include <sys/dkio.h>
23 
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "disk.h"
30 #include "part.h"
31 #include "misc.h"
32 #include "mbr.h"
33 #include "gpt.h"
34 
35 struct mbr initial_mbr;
36 
37 static int gpt_chk_mbr(struct dos_partition *, u_int64_t);
38 
39 int
40 MBR_protective_mbr(struct mbr *mbr)
41 {
42 	struct dos_partition dp[NDOSPART], dos_partition;
43 	int i;
44 
45 	for (i = 0; i < NDOSPART; i++) {
46 		PRT_make(&mbr->part[i], mbr->offset, mbr->reloffset,
47 		    &dos_partition);
48 		memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
49 	}
50 
51 	return (gpt_chk_mbr(dp, DL_GETDSIZE(&dl)));
52 }
53 
54 void
55 MBR_init_GPT(struct mbr *mbr)
56 {
57 	memset(&mbr->part, 0, sizeof(mbr->part));
58 
59 	/* Use whole disk, starting after MBR.
60 	 *
61 	 * Always set the partition size to UINT32_MAX (as MS does). EFI
62 	 * firmware has been encountered that lies in unpredictable ways
63 	 * about the size of the disk, thus making it impossible to boot
64 	 * such devices.
65 	 */
66 	mbr->part[0].id = DOSPTYP_EFI;
67 	mbr->part[0].bs = 1;
68 	mbr->part[0].ns = UINT32_MAX;
69 
70 	/* Fix up start/length fields. */
71 	PRT_fix_CHS(&mbr->part[0]);
72 }
73 
74 void
75 MBR_init(struct mbr *mbr)
76 {
77 	extern u_int32_t b_arg;
78 	u_int64_t adj;
79 	daddr_t i;
80 
81 	/*
82 	 * XXX Do *NOT* zap all MBR parts! Some archs still read initmbr
83 	 * from disk!! Just mark them inactive until -b goodness spreads
84 	 * further.
85 	 */
86 	mbr->part[0].flag = 0;
87 	mbr->part[1].flag = 0;
88 	mbr->part[2].flag = 0;
89 
90 	memset(&gh, 0, sizeof(gh));
91 	memset(&gp, 0, sizeof(gp));
92 
93 	mbr->part[3].flag = DOSACTIVE;
94 	mbr->signature = DOSMBR_SIGNATURE;
95 
96 	/* Use whole disk. Reserve first track, or first cyl, if possible. */
97 	mbr->part[3].id = DOSPTYP_OPENBSD;
98 	if (disk.heads > 1)
99 		mbr->part[3].shead = 1;
100 	else
101 		mbr->part[3].shead = 0;
102 	if (disk.heads < 2 && disk.cylinders > 1)
103 		mbr->part[3].scyl = 1;
104 	else
105 		mbr->part[3].scyl = 0;
106 	mbr->part[3].ssect = 1;
107 
108 	/* Go right to the end */
109 	mbr->part[3].ecyl = disk.cylinders - 1;
110 	mbr->part[3].ehead = disk.heads - 1;
111 	mbr->part[3].esect = disk.sectors;
112 
113 	/* Fix up start/length fields */
114 	PRT_fix_BN(&mbr->part[3], 3);
115 
116 #if defined(__powerpc__) || defined(__mips__)
117 	/* Now fix up for the MS-DOS boot partition on PowerPC. */
118 	mbr->part[0].flag = DOSACTIVE;	/* Boot from dos part */
119 	mbr->part[3].flag = 0;
120 	mbr->part[3].ns += mbr->part[3].bs;
121 	mbr->part[3].bs = mbr->part[0].bs + mbr->part[0].ns;
122 	mbr->part[3].ns -= mbr->part[3].bs;
123 	PRT_fix_CHS(&mbr->part[3]);
124 	if ((mbr->part[3].shead != 1) || (mbr->part[3].ssect != 1)) {
125 		/* align the partition on a cylinder boundary */
126 		mbr->part[3].shead = 0;
127 		mbr->part[3].ssect = 1;
128 		mbr->part[3].scyl += 1;
129 	}
130 	/* Fix up start/length fields */
131 	PRT_fix_BN(&mbr->part[3], 3);
132 #endif
133 #if defined(__i386__) || defined(__amd64__)
134 	if (b_arg > 0) {
135 		/* Add an EFI system partition on i386/amd64. */
136 		mbr->part[0].id = DOSPTYP_EFISYS;
137 		mbr->part[0].bs = 64;
138 		mbr->part[0].ns = b_arg;
139 		PRT_fix_CHS(&mbr->part[0]);
140 		mbr->part[3].ns += mbr->part[3].bs;
141 		mbr->part[3].bs = mbr->part[0].bs + mbr->part[0].ns;
142 		mbr->part[3].ns -= mbr->part[3].bs;
143 		PRT_fix_CHS(&mbr->part[3]);
144 	}
145 #endif
146 
147 	/* Start OpenBSD MBR partition on a power of 2 block number. */
148 	i = 1;
149 	while (i < DL_SECTOBLK(&dl, mbr->part[3].bs))
150 		i *= 2;
151 	adj = DL_BLKTOSEC(&dl, i) - mbr->part[3].bs;
152 	mbr->part[3].bs += adj;
153 	mbr->part[3].ns -= adj;
154 	PRT_fix_CHS(&mbr->part[3]);
155 }
156 
157 void
158 MBR_parse(struct dos_mbr *dos_mbr, off_t offset, off_t reloff, struct mbr *mbr)
159 {
160 	struct dos_partition dos_parts[NDOSPART];
161 	int i;
162 
163 	memcpy(mbr->code, dos_mbr->dmbr_boot, sizeof(mbr->code));
164 	mbr->offset = offset;
165 	mbr->reloffset = reloff;
166 	mbr->signature = letoh16(dos_mbr->dmbr_sign);
167 
168 	memcpy(dos_parts, dos_mbr->dmbr_parts, sizeof(dos_parts));
169 
170 	for (i = 0; i < NDOSPART; i++)
171 		PRT_parse(&dos_parts[i], offset, reloff, &mbr->part[i]);
172 }
173 
174 void
175 MBR_make(struct mbr *mbr, struct dos_mbr *dos_mbr)
176 {
177 	struct dos_partition dos_partition;
178 	int i;
179 
180 	memcpy(dos_mbr->dmbr_boot, mbr->code, sizeof(dos_mbr->dmbr_boot));
181 	dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE);
182 
183 	for (i = 0; i < NDOSPART; i++) {
184 		PRT_make(&mbr->part[i], mbr->offset, mbr->reloffset,
185 		    &dos_partition);
186 		memcpy(&dos_mbr->dmbr_parts[i], &dos_partition,
187 		    sizeof(dos_mbr->dmbr_parts[i]));
188 	}
189 }
190 
191 void
192 MBR_print(struct mbr *mbr, char *units)
193 {
194 	int i;
195 
196 	DISK_printgeometry(NULL);
197 
198 	/* Header */
199 	printf("Offset: %lld\t", (long long)mbr->offset);
200 	printf("Signature: 0x%X\n", (int)mbr->signature);
201 	PRT_print(0, NULL, units);
202 
203 	/* Entries */
204 	for (i = 0; i < NDOSPART; i++)
205 		PRT_print(i, &mbr->part[i], units);
206 }
207 
208 int
209 MBR_read(off_t where, struct dos_mbr *dos_mbr)
210 {
211 	char *secbuf;
212 
213 	secbuf = DISK_readsector(where);
214 	if (secbuf == NULL)
215 		return (-1);
216 
217 	memcpy(dos_mbr, secbuf, sizeof(*dos_mbr));
218 	free(secbuf);
219 
220 	return (0);
221 }
222 
223 int
224 MBR_write(off_t where, struct dos_mbr *dos_mbr)
225 {
226 	char *secbuf;
227 
228 	secbuf = DISK_readsector(where);
229 	if (secbuf == NULL)
230 		return (-1);
231 
232 	/*
233 	 * Place the new MBR at the start of the sector and
234 	 * write the sector back to "disk".
235 	 */
236 	memcpy(secbuf, dos_mbr, sizeof(*dos_mbr));
237 	DISK_writesector(secbuf, where);
238 
239 	/* Refresh in-kernel disklabel from the updated disk information. */
240 	ioctl(disk.fd, DIOCRLDINFO, 0);
241 
242 	free(secbuf);
243 
244 	return (0);
245 }
246 
247 /*
248  * If *dos_mbr has a 0xee or 0xef partition, nothing needs to happen. If no
249  * such partition is present but the first or last sector on the disk has a
250  * GPT, zero the GPT to ensure the MBR takes priority and fewer BIOSes get
251  * confused.
252  */
253 void
254 MBR_zapgpt(struct dos_mbr *dos_mbr, uint64_t lastsec)
255 {
256 	struct dos_partition dos_parts[NDOSPART];
257 	char *secbuf;
258 	uint64_t sig;
259 	int i;
260 
261 	memcpy(dos_parts, dos_mbr->dmbr_parts, sizeof(dos_parts));
262 
263 	for (i = 0; i < NDOSPART; i++)
264 		if ((dos_parts[i].dp_typ == DOSPTYP_EFI) ||
265 		    (dos_parts[i].dp_typ == DOSPTYP_EFISYS))
266 			return;
267 
268 	secbuf = DISK_readsector(GPTSECTOR);
269 	if (secbuf == NULL)
270 		return;
271 
272 	memcpy(&sig, secbuf, sizeof(sig));
273 	if (letoh64(sig) == GPTSIGNATURE) {
274 		memset(secbuf, 0, sizeof(sig));
275 		DISK_writesector(secbuf, GPTSECTOR);
276 	}
277 	free(secbuf);
278 
279 	secbuf = DISK_readsector(lastsec);
280 	if (secbuf == NULL)
281 		return;
282 
283 	memcpy(&sig, secbuf, sizeof(sig));
284 	if (letoh64(sig) == GPTSIGNATURE) {
285 		memset(secbuf, 0, sizeof(sig));
286 		DISK_writesector(secbuf, lastsec);
287 	}
288 	free(secbuf);
289 }
290 
291 /*
292  * Returns 0 if the MBR with the provided partition array is a GPT protective
293  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
294  * one MBR partition, an EFI partition that either covers the whole disk or as
295  * much of it as is possible with a 32bit size field.
296  *
297  * Taken from kern/subr_disk.c.
298  *
299  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
300  */
301 int
302 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
303 {
304 	struct dos_partition *dp2;
305 	int efi, found, i;
306 	u_int32_t psize;
307 
308 	found = efi = 0;
309 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
310 		if (dp2->dp_typ == DOSPTYP_UNUSED)
311 			continue;
312 		found++;
313 		if (dp2->dp_typ != DOSPTYP_EFI)
314 			continue;
315 		psize = letoh32(dp2->dp_size);
316 		if (psize == (dsize - 1) ||
317 		    psize == UINT32_MAX) {
318 			if (letoh32(dp2->dp_start) == 1)
319 				efi++;
320 		}
321 	}
322 	if (found == 1 && efi == 1)
323 		return (0);
324 
325 	return (1);
326 }
327 
328