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 |
|
|
|