1 |
|
|
/* $OpenBSD: mbr.c,v 1.65 2015/12/30 17:21:39 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 |
|
|
u_int64_t sz; |
58 |
|
|
|
59 |
|
|
sz = DL_GETDSIZE(&dl); |
60 |
|
|
|
61 |
|
|
memset(&mbr->part, 0, sizeof(mbr->part)); |
62 |
|
|
|
63 |
|
|
/* Use whole disk, starting after MBR. */ |
64 |
|
|
mbr->part[0].id = DOSPTYP_EFI; |
65 |
|
|
mbr->part[0].bs = 1; |
66 |
|
|
if (sz > UINT32_MAX) |
67 |
|
|
mbr->part[0].ns = UINT32_MAX; |
68 |
|
|
else |
69 |
|
|
mbr->part[0].ns = sz - 1; |
70 |
|
|
|
71 |
|
|
/* Fix up start/length fields. */ |
72 |
|
|
PRT_fix_CHS(&mbr->part[0]); |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
void |
76 |
|
|
MBR_init(struct mbr *mbr) |
77 |
|
|
{ |
78 |
|
|
extern u_int32_t b_arg; |
79 |
|
|
u_int64_t adj; |
80 |
|
|
daddr_t i; |
81 |
|
|
|
82 |
|
|
/* |
83 |
|
|
* XXX Do *NOT* zap all MBR parts! Some archs still read initmbr |
84 |
|
|
* from disk!! Just mark them inactive until -b goodness spreads |
85 |
|
|
* further. |
86 |
|
|
*/ |
87 |
|
|
mbr->part[0].flag = 0; |
88 |
|
|
mbr->part[1].flag = 0; |
89 |
|
|
mbr->part[2].flag = 0; |
90 |
|
|
|
91 |
|
|
memset(&gh, 0, sizeof(gh)); |
92 |
|
|
memset(&gp, 0, sizeof(gp)); |
93 |
|
|
|
94 |
|
|
mbr->part[3].flag = DOSACTIVE; |
95 |
|
|
mbr->signature = DOSMBR_SIGNATURE; |
96 |
|
|
|
97 |
|
|
/* Use whole disk. Reserve first track, or first cyl, if possible. */ |
98 |
|
|
mbr->part[3].id = DOSPTYP_OPENBSD; |
99 |
|
|
if (disk.heads > 1) |
100 |
|
|
mbr->part[3].shead = 1; |
101 |
|
|
else |
102 |
|
|
mbr->part[3].shead = 0; |
103 |
|
|
if (disk.heads < 2 && disk.cylinders > 1) |
104 |
|
|
mbr->part[3].scyl = 1; |
105 |
|
|
else |
106 |
|
|
mbr->part[3].scyl = 0; |
107 |
|
|
mbr->part[3].ssect = 1; |
108 |
|
|
|
109 |
|
|
/* Go right to the end */ |
110 |
|
|
mbr->part[3].ecyl = disk.cylinders - 1; |
111 |
|
|
mbr->part[3].ehead = disk.heads - 1; |
112 |
|
|
mbr->part[3].esect = disk.sectors; |
113 |
|
|
|
114 |
|
|
/* Fix up start/length fields */ |
115 |
|
|
PRT_fix_BN(&mbr->part[3], 3); |
116 |
|
|
|
117 |
|
|
#if defined(__powerpc__) || defined(__mips__) |
118 |
|
|
/* Now fix up for the MS-DOS boot partition on PowerPC. */ |
119 |
|
|
mbr->part[0].flag = DOSACTIVE; /* Boot from dos part */ |
120 |
|
|
mbr->part[3].flag = 0; |
121 |
|
|
mbr->part[3].ns += mbr->part[3].bs; |
122 |
|
|
mbr->part[3].bs = mbr->part[0].bs + mbr->part[0].ns; |
123 |
|
|
mbr->part[3].ns -= mbr->part[3].bs; |
124 |
|
|
PRT_fix_CHS(&mbr->part[3]); |
125 |
|
|
if ((mbr->part[3].shead != 1) || (mbr->part[3].ssect != 1)) { |
126 |
|
|
/* align the partition on a cylinder boundary */ |
127 |
|
|
mbr->part[3].shead = 0; |
128 |
|
|
mbr->part[3].ssect = 1; |
129 |
|
|
mbr->part[3].scyl += 1; |
130 |
|
|
} |
131 |
|
|
/* Fix up start/length fields */ |
132 |
|
|
PRT_fix_BN(&mbr->part[3], 3); |
133 |
|
|
#endif |
134 |
|
|
#if defined(__i386__) || defined(__amd64__) |
135 |
|
|
if (b_arg > 0) { |
136 |
|
|
/* Add an EFI system partition on i386/amd64. */ |
137 |
|
|
mbr->part[0].id = DOSPTYP_EFISYS; |
138 |
|
|
mbr->part[0].bs = 64; |
139 |
|
|
mbr->part[0].ns = b_arg; |
140 |
|
|
PRT_fix_CHS(&mbr->part[0]); |
141 |
|
|
mbr->part[3].ns += mbr->part[3].bs; |
142 |
|
|
mbr->part[3].bs = mbr->part[0].bs + mbr->part[0].ns; |
143 |
|
|
mbr->part[3].ns -= mbr->part[3].bs; |
144 |
|
|
PRT_fix_CHS(&mbr->part[3]); |
145 |
|
|
} |
146 |
|
|
#endif |
147 |
|
|
|
148 |
|
|
/* Start OpenBSD MBR partition on a power of 2 block number. */ |
149 |
|
|
i = 1; |
150 |
|
|
while (i < DL_SECTOBLK(&dl, mbr->part[3].bs)) |
151 |
|
|
i *= 2; |
152 |
|
|
adj = DL_BLKTOSEC(&dl, i) - mbr->part[3].bs; |
153 |
|
|
mbr->part[3].bs += adj; |
154 |
|
|
mbr->part[3].ns -= adj; |
155 |
|
|
PRT_fix_CHS(&mbr->part[3]); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
void |
159 |
|
|
MBR_parse(struct dos_mbr *dos_mbr, off_t offset, off_t reloff, struct mbr *mbr) |
160 |
|
|
{ |
161 |
|
|
struct dos_partition dos_parts[NDOSPART]; |
162 |
|
|
int i; |
163 |
|
|
|
164 |
|
|
memcpy(mbr->code, dos_mbr->dmbr_boot, sizeof(mbr->code)); |
165 |
|
|
mbr->offset = offset; |
166 |
|
|
mbr->reloffset = reloff; |
167 |
|
|
mbr->signature = letoh16(dos_mbr->dmbr_sign); |
168 |
|
|
|
169 |
|
|
memcpy(dos_parts, dos_mbr->dmbr_parts, sizeof(dos_parts)); |
170 |
|
|
|
171 |
|
|
for (i = 0; i < NDOSPART; i++) |
172 |
|
|
PRT_parse(&dos_parts[i], offset, reloff, &mbr->part[i]); |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
void |
176 |
|
|
MBR_make(struct mbr *mbr, struct dos_mbr *dos_mbr) |
177 |
|
|
{ |
178 |
|
|
struct dos_partition dos_partition; |
179 |
|
|
int i; |
180 |
|
|
|
181 |
|
|
memcpy(dos_mbr->dmbr_boot, mbr->code, sizeof(dos_mbr->dmbr_boot)); |
182 |
|
|
dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE); |
183 |
|
|
|
184 |
|
|
for (i = 0; i < NDOSPART; i++) { |
185 |
|
|
PRT_make(&mbr->part[i], mbr->offset, mbr->reloffset, |
186 |
|
|
&dos_partition); |
187 |
|
|
memcpy(&dos_mbr->dmbr_parts[i], &dos_partition, |
188 |
|
|
sizeof(dos_mbr->dmbr_parts[i])); |
189 |
|
|
} |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
void |
193 |
|
|
MBR_print(struct mbr *mbr, char *units) |
194 |
|
|
{ |
195 |
|
|
int i; |
196 |
|
|
|
197 |
|
|
DISK_printgeometry(NULL); |
198 |
|
|
|
199 |
|
|
/* Header */ |
200 |
|
|
printf("Offset: %lld\t", (long long)mbr->offset); |
201 |
|
|
printf("Signature: 0x%X\n", (int)mbr->signature); |
202 |
|
|
PRT_print(0, NULL, units); |
203 |
|
|
|
204 |
|
|
/* Entries */ |
205 |
|
|
for (i = 0; i < NDOSPART; i++) |
206 |
|
|
PRT_print(i, &mbr->part[i], units); |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
int |
210 |
|
|
MBR_read(off_t where, struct dos_mbr *dos_mbr) |
211 |
|
|
{ |
212 |
|
|
char *secbuf; |
213 |
|
|
|
214 |
|
|
secbuf = DISK_readsector(where); |
215 |
|
|
if (secbuf == NULL) |
216 |
|
|
return (-1); |
217 |
|
|
|
218 |
|
|
memcpy(dos_mbr, secbuf, sizeof(*dos_mbr)); |
219 |
|
|
free(secbuf); |
220 |
|
|
|
221 |
|
|
return (0); |
222 |
|
|
} |
223 |
|
|
|
224 |
|
|
int |
225 |
|
|
MBR_write(off_t where, struct dos_mbr *dos_mbr) |
226 |
|
|
{ |
227 |
|
|
char *secbuf; |
228 |
|
|
|
229 |
|
|
secbuf = DISK_readsector(where); |
230 |
|
|
if (secbuf == NULL) |
231 |
|
|
return (-1); |
232 |
|
|
|
233 |
|
|
/* |
234 |
|
|
* Place the new MBR at the start of the sector and |
235 |
|
|
* write the sector back to "disk". |
236 |
|
|
*/ |
237 |
|
|
memcpy(secbuf, dos_mbr, sizeof(*dos_mbr)); |
238 |
|
|
DISK_writesector(secbuf, where); |
239 |
|
|
|
240 |
|
|
/* Refresh in-kernel disklabel from the updated disk information. */ |
241 |
|
|
ioctl(disk.fd, DIOCRLDINFO, 0); |
242 |
|
|
|
243 |
|
|
free(secbuf); |
244 |
|
|
|
245 |
|
|
return (0); |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
/* |
249 |
|
|
* If *dos_mbr has a 0xee or 0xef partition, nothing needs to happen. If no |
250 |
|
|
* such partition is present but the first or last sector on the disk has a |
251 |
|
|
* GPT, zero the GPT to ensure the MBR takes priority and fewer BIOSes get |
252 |
|
|
* confused. |
253 |
|
|
*/ |
254 |
|
|
void |
255 |
|
|
MBR_zapgpt(struct dos_mbr *dos_mbr, uint64_t lastsec) |
256 |
|
|
{ |
257 |
|
|
struct dos_partition dos_parts[NDOSPART]; |
258 |
|
|
char *secbuf; |
259 |
|
|
uint64_t sig; |
260 |
|
|
int i; |
261 |
|
|
|
262 |
|
|
memcpy(dos_parts, dos_mbr->dmbr_parts, sizeof(dos_parts)); |
263 |
|
|
|
264 |
|
|
for (i = 0; i < NDOSPART; i++) |
265 |
|
|
if ((dos_parts[i].dp_typ == DOSPTYP_EFI) || |
266 |
|
|
(dos_parts[i].dp_typ == DOSPTYP_EFISYS)) |
267 |
|
|
return; |
268 |
|
|
|
269 |
|
|
secbuf = DISK_readsector(GPTSECTOR); |
270 |
|
|
if (secbuf == NULL) |
271 |
|
|
return; |
272 |
|
|
|
273 |
|
|
memcpy(&sig, secbuf, sizeof(sig)); |
274 |
|
|
if (letoh64(sig) == GPTSIGNATURE) { |
275 |
|
|
memset(secbuf, 0, sizeof(sig)); |
276 |
|
|
DISK_writesector(secbuf, GPTSECTOR); |
277 |
|
|
} |
278 |
|
|
free(secbuf); |
279 |
|
|
|
280 |
|
|
secbuf = DISK_readsector(lastsec); |
281 |
|
|
if (secbuf == NULL) |
282 |
|
|
return; |
283 |
|
|
|
284 |
|
|
memcpy(&sig, secbuf, sizeof(sig)); |
285 |
|
|
if (letoh64(sig) == GPTSIGNATURE) { |
286 |
|
|
memset(secbuf, 0, sizeof(sig)); |
287 |
|
|
DISK_writesector(secbuf, lastsec); |
288 |
|
|
} |
289 |
|
|
free(secbuf); |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
/* |
293 |
|
|
* Returns 0 if the MBR with the provided partition array is a GPT protective |
294 |
|
|
* MBR, and returns 1 otherwise. A GPT protective MBR would have one and only |
295 |
|
|
* one MBR partition, an EFI partition that either covers the whole disk or as |
296 |
|
|
* much of it as is possible with a 32bit size field. |
297 |
|
|
* |
298 |
|
|
* Taken from kern/subr_disk.c. |
299 |
|
|
* |
300 |
|
|
* NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** |
301 |
|
|
*/ |
302 |
|
|
int |
303 |
|
|
gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) |
304 |
|
|
{ |
305 |
|
|
struct dos_partition *dp2; |
306 |
|
|
int efi, found, i; |
307 |
|
|
u_int32_t psize; |
308 |
|
|
|
309 |
|
|
found = efi = 0; |
310 |
|
|
for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { |
311 |
|
|
if (dp2->dp_typ == DOSPTYP_UNUSED) |
312 |
|
|
continue; |
313 |
|
|
found++; |
314 |
|
|
if (dp2->dp_typ != DOSPTYP_EFI) |
315 |
|
|
continue; |
316 |
|
|
psize = letoh32(dp2->dp_size); |
317 |
|
|
if (psize == (dsize - 1) || |
318 |
|
|
psize == UINT32_MAX) { |
319 |
|
|
if (letoh32(dp2->dp_start) == 1) |
320 |
|
|
efi++; |
321 |
|
|
} |
322 |
|
|
} |
323 |
|
|
if (found == 1 && efi == 1) |
324 |
|
|
return (0); |
325 |
|
|
|
326 |
|
|
return (1); |
327 |
|
|
} |
328 |
|
|
|