1 |
|
|
/* $OpenBSD: i386_installboot.c,v 1.31 2017/10/27 16:47:08 mpi Exp $ */ |
2 |
|
|
/* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 2011 Joel Sing <jsing@openbsd.org> |
6 |
|
|
* Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org> |
7 |
|
|
* Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com> |
8 |
|
|
* Copyright (c) 1997 Michael Shalayeff |
9 |
|
|
* Copyright (c) 1994 Paul Kranenburg |
10 |
|
|
* All rights reserved. |
11 |
|
|
* |
12 |
|
|
* Redistribution and use in source and binary forms, with or without |
13 |
|
|
* modification, are permitted provided that the following conditions |
14 |
|
|
* are met: |
15 |
|
|
* 1. Redistributions of source code must retain the above copyright |
16 |
|
|
* notice, this list of conditions and the following disclaimer. |
17 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
18 |
|
|
* notice, this list of conditions and the following disclaimer in the |
19 |
|
|
* documentation and/or other materials provided with the distribution. |
20 |
|
|
* 3. All advertising materials mentioning features or use of this software |
21 |
|
|
* must display the following acknowledgement: |
22 |
|
|
* This product includes software developed by Paul Kranenburg. |
23 |
|
|
* 4. The name of the author may not be used to endorse or promote products |
24 |
|
|
* derived from this software without specific prior written permission |
25 |
|
|
* |
26 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
27 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
28 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
29 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
30 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
31 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
32 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
33 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
34 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 |
|
|
*/ |
37 |
|
|
|
38 |
|
|
#define ELFSIZE 32 |
39 |
|
|
|
40 |
|
|
#include <sys/param.h> /* DEV_BSIZE */ |
41 |
|
|
#include <sys/disklabel.h> |
42 |
|
|
#include <sys/dkio.h> |
43 |
|
|
#include <sys/ioctl.h> |
44 |
|
|
#include <sys/mount.h> |
45 |
|
|
#include <sys/reboot.h> |
46 |
|
|
#include <sys/stat.h> |
47 |
|
|
#include <sys/sysctl.h> |
48 |
|
|
#include <sys/time.h> |
49 |
|
|
|
50 |
|
|
#include <ufs/ufs/dinode.h> |
51 |
|
|
#include <ufs/ufs/dir.h> |
52 |
|
|
#include <ufs/ffs/fs.h> |
53 |
|
|
|
54 |
|
|
#include <machine/cpu.h> |
55 |
|
|
#include <machine/biosvar.h> |
56 |
|
|
|
57 |
|
|
#include <elf.h> |
58 |
|
|
#include <err.h> |
59 |
|
|
#include <errno.h> |
60 |
|
|
#include <fcntl.h> |
61 |
|
|
#include <nlist.h> |
62 |
|
|
#include <stdlib.h> |
63 |
|
|
#include <stdio.h> |
64 |
|
|
#include <stdint.h> |
65 |
|
|
#include <string.h> |
66 |
|
|
#include <unistd.h> |
67 |
|
|
#include <util.h> |
68 |
|
|
#include <uuid.h> |
69 |
|
|
|
70 |
|
|
#include "installboot.h" |
71 |
|
|
#include "i386_installboot.h" |
72 |
|
|
|
73 |
|
|
char *bootldr; |
74 |
|
|
|
75 |
|
|
char *blkstore; |
76 |
|
|
size_t blksize; |
77 |
|
|
|
78 |
|
|
struct sym_data pbr_symbols[] = { |
79 |
|
|
{"_fs_bsize_p", 2}, |
80 |
|
|
{"_fs_bsize_s", 2}, |
81 |
|
|
{"_fsbtodb", 1}, |
82 |
|
|
{"_p_offset", 4}, |
83 |
|
|
{"_inodeblk", 4}, |
84 |
|
|
{"_inodedbl", 4}, |
85 |
|
|
{"_nblocks", 2}, |
86 |
|
|
{NULL} |
87 |
|
|
}; |
88 |
|
|
|
89 |
|
|
static void devread(int, void *, daddr_t, size_t, char *); |
90 |
|
|
static u_int findopenbsd(int, struct disklabel *); |
91 |
|
|
static int getbootparams(char *, int, struct disklabel *); |
92 |
|
|
static char *loadproto(char *, long *); |
93 |
|
|
static int gpt_chk_mbr(struct dos_partition *, u_int64_t); |
94 |
|
|
|
95 |
|
|
/* |
96 |
|
|
* Read information about /boot's inode and filesystem parameters, then |
97 |
|
|
* put biosboot (partition boot record) on the target drive with these |
98 |
|
|
* parameters patched in. |
99 |
|
|
*/ |
100 |
|
|
|
101 |
|
|
void |
102 |
|
|
md_init(void) |
103 |
|
|
{ |
104 |
|
|
stages = 2; |
105 |
|
|
stage1 = "/usr/mdec/biosboot"; |
106 |
|
|
stage2 = "/usr/mdec/boot"; |
107 |
|
|
|
108 |
|
|
bootldr = "/boot"; |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
void |
112 |
|
|
md_loadboot(void) |
113 |
|
|
{ |
114 |
|
|
/* Load prototype boot blocks. */ |
115 |
|
|
if ((blkstore = loadproto(stage1, &blksize)) == NULL) |
116 |
|
|
exit(1); |
117 |
|
|
|
118 |
|
|
/* XXX - Paranoia: Make sure size is aligned! */ |
119 |
|
|
if (blksize & (DEV_BSIZE - 1)) |
120 |
|
|
errx(1, "proto %s bad size=%ld", stage1, blksize); |
121 |
|
|
|
122 |
|
|
if (blksize > SBSIZE - DEV_BSIZE) |
123 |
|
|
errx(1, "proto bootblocks too big"); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
void |
127 |
|
|
md_installboot(int devfd, char *dev) |
128 |
|
|
{ |
129 |
|
|
struct disklabel dl; |
130 |
|
|
int part; |
131 |
|
|
|
132 |
|
|
/* Get and check disklabel. */ |
133 |
|
|
if (ioctl(devfd, DIOCGDINFO, &dl) != 0) |
134 |
|
|
err(1, "disklabel: %s", dev); |
135 |
|
|
if (dl.d_magic != DISKMAGIC) |
136 |
|
|
errx(1, "bad disklabel magic=0x%08x", dl.d_magic); |
137 |
|
|
|
138 |
|
|
/* Warn on unknown disklabel types. */ |
139 |
|
|
if (dl.d_type == 0) |
140 |
|
|
warnx("disklabel type unknown"); |
141 |
|
|
|
142 |
|
|
part = findgptefisys(devfd, &dl); |
143 |
|
|
if (part != -1) { |
144 |
|
|
write_efisystem(&dl, (char)part); |
145 |
|
|
return; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
bootldr = fileprefix(root, bootldr); |
149 |
|
|
if (bootldr == NULL) |
150 |
|
|
exit(1); |
151 |
|
|
if (verbose) |
152 |
|
|
fprintf(stderr, "%s %s to %s\n", |
153 |
|
|
(nowrite ? "would copy" : "copying"), stage2, bootldr); |
154 |
|
|
if (!nowrite) |
155 |
|
|
if (filecopy(stage2, bootldr) == -1) |
156 |
|
|
exit(1); |
157 |
|
|
|
158 |
|
|
/* Get bootstrap parameters to patch into proto. */ |
159 |
|
|
if (getbootparams(bootldr, devfd, &dl) != 0) |
160 |
|
|
exit(1); |
161 |
|
|
|
162 |
|
|
/* Write boot blocks to device. */ |
163 |
|
|
write_bootblocks(devfd, dev, &dl); |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
void |
167 |
|
|
write_bootblocks(int devfd, char *dev, struct disklabel *dl) |
168 |
|
|
{ |
169 |
|
|
struct stat sb; |
170 |
|
|
u_int8_t *secbuf; |
171 |
|
|
u_int start = 0; |
172 |
|
|
|
173 |
|
|
/* Write patched proto bootblock(s) into the superblock. */ |
174 |
|
|
if (fstat(devfd, &sb) < 0) |
175 |
|
|
err(1, "fstat: %s", dev); |
176 |
|
|
|
177 |
|
|
if (!S_ISCHR(sb.st_mode)) |
178 |
|
|
errx(1, "%s: not a character device", dev); |
179 |
|
|
|
180 |
|
|
/* Patch the parameters into the proto bootstrap sector. */ |
181 |
|
|
pbr_set_symbols(stage1, blkstore, pbr_symbols); |
182 |
|
|
|
183 |
|
|
if (!nowrite) { |
184 |
|
|
/* Sync filesystems (to clean in-memory superblock?). */ |
185 |
|
|
sync(); sleep(1); |
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
/* |
189 |
|
|
* Find OpenBSD partition. Floppies are special, getting an |
190 |
|
|
* everything-in-one /boot starting at sector 0. |
191 |
|
|
*/ |
192 |
|
|
if (dl->d_type != DTYPE_FLOPPY) { |
193 |
|
|
start = findopenbsd(devfd, dl); |
194 |
|
|
if (start == (u_int)-1) |
195 |
|
|
errx(1, "no OpenBSD partition"); |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
if (verbose) |
199 |
|
|
fprintf(stderr, "%s will be written at sector %u\n", |
200 |
|
|
stage1, start); |
201 |
|
|
|
202 |
|
|
if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC) |
203 |
|
|
warnx("%s extends beyond sector %u. OpenBSD might not boot.", |
204 |
|
|
stage1, BOOTBIOS_MAXSEC); |
205 |
|
|
|
206 |
|
|
if (!nowrite) { |
207 |
|
|
secbuf = calloc(1, dl->d_secsize); |
208 |
|
|
if (pread(devfd, secbuf, dl->d_secsize, (off_t)start * |
209 |
|
|
dl->d_secsize) != dl->d_secsize) |
210 |
|
|
err(1, "pread boot sector"); |
211 |
|
|
bcopy(blkstore, secbuf, blksize); |
212 |
|
|
if (pwrite(devfd, secbuf, dl->d_secsize, (off_t)start * |
213 |
|
|
dl->d_secsize) != dl->d_secsize) |
214 |
|
|
err(1, "pwrite bootstrap"); |
215 |
|
|
free(secbuf); |
216 |
|
|
} |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
void |
220 |
|
|
write_efisystem(struct disklabel *dl, char part) |
221 |
|
|
{ |
222 |
|
|
static char *fsckfmt = "/sbin/fsck_msdos %s >/dev/null"; |
223 |
|
|
static char *newfsfmt ="/sbin/newfs_msdos %s >/dev/null"; |
224 |
|
|
struct msdosfs_args args; |
225 |
|
|
char cmd[60]; |
226 |
|
|
char dst[PATH_MAX]; |
227 |
|
|
char *src; |
228 |
|
|
size_t mntlen, pathlen, srclen; |
229 |
|
|
int rslt; |
230 |
|
|
|
231 |
|
|
src = NULL; |
232 |
|
|
|
233 |
|
|
/* Create directory for temporary mount point. */ |
234 |
|
|
strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst)); |
235 |
|
|
if (mkdtemp(dst) == NULL) |
236 |
|
|
err(1, "mkdtemp('%s') failed", dst); |
237 |
|
|
mntlen = strlen(dst); |
238 |
|
|
|
239 |
|
|
/* Mount <duid>.<part> as msdos filesystem. */ |
240 |
|
|
memset(&args, 0, sizeof(args)); |
241 |
|
|
rslt = asprintf(&args.fspec, |
242 |
|
|
"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", |
243 |
|
|
dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], |
244 |
|
|
dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], |
245 |
|
|
part); |
246 |
|
|
if (rslt == -1) { |
247 |
|
|
warn("bad special device"); |
248 |
|
|
goto rmdir; |
249 |
|
|
} |
250 |
|
|
|
251 |
|
|
args.export_info.ex_root = -2; /* unchecked anyway on DOS fs */ |
252 |
|
|
args.export_info.ex_flags = 0; |
253 |
|
|
|
254 |
|
|
if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { |
255 |
|
|
/* Try fsck'ing it. */ |
256 |
|
|
rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec); |
257 |
|
|
if (rslt >= sizeof(cmd)) { |
258 |
|
|
warnx("can't build fsck command"); |
259 |
|
|
rslt = -1; |
260 |
|
|
goto rmdir; |
261 |
|
|
} |
262 |
|
|
rslt = system(cmd); |
263 |
|
|
if (rslt == -1) { |
264 |
|
|
warn("system('%s') failed", cmd); |
265 |
|
|
goto rmdir; |
266 |
|
|
} |
267 |
|
|
if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { |
268 |
|
|
/* Try newfs'ing it. */ |
269 |
|
|
rslt = snprintf(cmd, sizeof(cmd), newfsfmt, |
270 |
|
|
args.fspec); |
271 |
|
|
if (rslt >= sizeof(cmd)) { |
272 |
|
|
warnx("can't build newfs command"); |
273 |
|
|
rslt = -1; |
274 |
|
|
goto rmdir; |
275 |
|
|
} |
276 |
|
|
rslt = system(cmd); |
277 |
|
|
if (rslt == -1) { |
278 |
|
|
warn("system('%s') failed", cmd); |
279 |
|
|
goto rmdir; |
280 |
|
|
} |
281 |
|
|
rslt = mount(MOUNT_MSDOS, dst, 0, &args); |
282 |
|
|
if (rslt == -1) { |
283 |
|
|
warn("unable to mount EFI System partition"); |
284 |
|
|
goto rmdir; |
285 |
|
|
} |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
/* Create "/efi/BOOT" directory in <duid>.<part>. */ |
290 |
|
|
if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) { |
291 |
|
|
rslt = -1; |
292 |
|
|
warn("unable to build /efi directory"); |
293 |
|
|
goto umount; |
294 |
|
|
} |
295 |
|
|
rslt = mkdir(dst, 0); |
296 |
|
|
if (rslt == -1 && errno != EEXIST) { |
297 |
|
|
warn("mkdir('%s') failed", dst); |
298 |
|
|
goto umount; |
299 |
|
|
} |
300 |
|
|
if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) { |
301 |
|
|
rslt = -1; |
302 |
|
|
warn("unable to build /BOOT directory"); |
303 |
|
|
goto umount; |
304 |
|
|
} |
305 |
|
|
rslt = mkdir(dst, 0); |
306 |
|
|
if (rslt == -1 && errno != EEXIST) { |
307 |
|
|
warn("mkdir('%s') failed", dst); |
308 |
|
|
goto umount; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
/* |
312 |
|
|
* Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/BOOT/. |
313 |
|
|
* |
314 |
|
|
* N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused! |
315 |
|
|
*/ |
316 |
|
|
pathlen = strlen(dst); |
317 |
|
|
if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) { |
318 |
|
|
rslt = -1; |
319 |
|
|
warn("unable to build /BOOTIA32.EFI path"); |
320 |
|
|
goto umount; |
321 |
|
|
} |
322 |
|
|
src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI"); |
323 |
|
|
if (src == NULL) { |
324 |
|
|
rslt = -1; |
325 |
|
|
goto umount; |
326 |
|
|
} |
327 |
|
|
srclen = strlen(src); |
328 |
|
|
if (verbose) |
329 |
|
|
fprintf(stderr, "%s %s to %s\n", |
330 |
|
|
(nowrite ? "would copy" : "copying"), src, dst); |
331 |
|
|
if (!nowrite) { |
332 |
|
|
rslt = filecopy(src, dst); |
333 |
|
|
if (rslt == -1) |
334 |
|
|
goto umount; |
335 |
|
|
} |
336 |
|
|
src[srclen - strlen("/BOOTIA32.EFI")] = '\0'; |
337 |
|
|
|
338 |
|
|
dst[pathlen] = '\0'; |
339 |
|
|
if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) { |
340 |
|
|
rslt = -1; |
341 |
|
|
warn("unable to build /BOOTX64.EFI dst path"); |
342 |
|
|
goto umount; |
343 |
|
|
} |
344 |
|
|
if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) { |
345 |
|
|
rslt = -1; |
346 |
|
|
warn("unable to build /BOOTX64.EFI src path"); |
347 |
|
|
goto umount; |
348 |
|
|
} |
349 |
|
|
if (verbose) |
350 |
|
|
fprintf(stderr, "%s %s to %s\n", |
351 |
|
|
(nowrite ? "would copy" : "copying"), src, dst); |
352 |
|
|
if (!nowrite) { |
353 |
|
|
rslt = filecopy(src, dst); |
354 |
|
|
if (rslt == -1) |
355 |
|
|
goto umount; |
356 |
|
|
} |
357 |
|
|
|
358 |
|
|
rslt = 0; |
359 |
|
|
|
360 |
|
|
umount: |
361 |
|
|
dst[mntlen] = '\0'; |
362 |
|
|
if (unmount(dst, MNT_FORCE) == -1) |
363 |
|
|
err(1, "unmount('%s') failed", dst); |
364 |
|
|
|
365 |
|
|
rmdir: |
366 |
|
|
free(args.fspec); |
367 |
|
|
dst[mntlen] = '\0'; |
368 |
|
|
if (rmdir(dst) == -1) |
369 |
|
|
err(1, "rmdir('%s') failed", dst); |
370 |
|
|
|
371 |
|
|
free(src); |
372 |
|
|
|
373 |
|
|
if (rslt == -1) |
374 |
|
|
exit(1); |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
u_int |
378 |
|
|
findopenbsd(int devfd, struct disklabel *dl) |
379 |
|
|
{ |
380 |
|
|
struct dos_mbr mbr; |
381 |
|
|
u_int mbroff = DOSBBSECTOR; |
382 |
|
|
u_int mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */ |
383 |
|
|
struct dos_partition *dp; |
384 |
|
|
u_int8_t *secbuf; |
385 |
|
|
u_int maxebr = DOS_MAXEBR, nextebr; |
386 |
|
|
int i; |
387 |
|
|
|
388 |
|
|
again: |
389 |
|
|
if (!maxebr--) { |
390 |
|
|
if (verbose) |
391 |
|
|
fprintf(stderr, "Traversed more than %d Extended Boot " |
392 |
|
|
"Records (EBRs)\n", DOS_MAXEBR); |
393 |
|
|
return ((u_int)-1); |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
if (verbose) |
397 |
|
|
fprintf(stderr, "%s boot record (%cBR) at sector %u\n", |
398 |
|
|
(mbroff == DOSBBSECTOR) ? "master" : "extended", |
399 |
|
|
(mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff); |
400 |
|
|
|
401 |
|
|
if ((secbuf = malloc(dl->d_secsize)) == NULL) |
402 |
|
|
err(1, NULL); |
403 |
|
|
if (pread(devfd, secbuf, dl->d_secsize, (off_t)mbroff * dl->d_secsize) |
404 |
|
|
< (ssize_t)sizeof(mbr)) |
405 |
|
|
err(4, "can't pread boot record"); |
406 |
|
|
bcopy(secbuf, &mbr, sizeof(mbr)); |
407 |
|
|
free(secbuf); |
408 |
|
|
|
409 |
|
|
if (mbr.dmbr_sign != DOSMBR_SIGNATURE) |
410 |
|
|
errx(1, "invalid boot record signature (0x%04X) @ sector %u", |
411 |
|
|
mbr.dmbr_sign, mbroff); |
412 |
|
|
|
413 |
|
|
nextebr = 0; |
414 |
|
|
for (i = 0; i < NDOSPART; i++) { |
415 |
|
|
dp = &mbr.dmbr_parts[i]; |
416 |
|
|
if (!dp->dp_size) |
417 |
|
|
continue; |
418 |
|
|
|
419 |
|
|
if (verbose) |
420 |
|
|
fprintf(stderr, |
421 |
|
|
"\tpartition %d: type 0x%02X offset %u size %u\n", |
422 |
|
|
i, dp->dp_typ, dp->dp_start, dp->dp_size); |
423 |
|
|
|
424 |
|
|
if (dp->dp_typ == DOSPTYP_OPENBSD) { |
425 |
|
|
if (dp->dp_start > (dp->dp_start + mbroff)) |
426 |
|
|
continue; |
427 |
|
|
return (dp->dp_start + mbroff); |
428 |
|
|
} |
429 |
|
|
|
430 |
|
|
if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || |
431 |
|
|
dp->dp_typ == DOSPTYP_EXTENDL)) { |
432 |
|
|
nextebr = dp->dp_start + mbr_eoff; |
433 |
|
|
if (nextebr < dp->dp_start) |
434 |
|
|
nextebr = (u_int)-1; |
435 |
|
|
if (mbr_eoff == DOSBBSECTOR) |
436 |
|
|
mbr_eoff = dp->dp_start; |
437 |
|
|
} |
438 |
|
|
} |
439 |
|
|
|
440 |
|
|
if (nextebr && nextebr != (u_int)-1) { |
441 |
|
|
mbroff = nextebr; |
442 |
|
|
goto again; |
443 |
|
|
} |
444 |
|
|
|
445 |
|
|
return ((u_int)-1); |
446 |
|
|
} |
447 |
|
|
|
448 |
|
|
/* |
449 |
|
|
* Returns 0 if the MBR with the provided partition array is a GPT protective |
450 |
|
|
* MBR, and returns 1 otherwise. A GPT protective MBR would have one and only |
451 |
|
|
* one MBR partition, an EFI partition that either covers the whole disk or as |
452 |
|
|
* much of it as is possible with a 32bit size field. |
453 |
|
|
* |
454 |
|
|
* NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** |
455 |
|
|
*/ |
456 |
|
|
static int |
457 |
|
|
gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) |
458 |
|
|
{ |
459 |
|
|
struct dos_partition *dp2; |
460 |
|
|
int efi, found, i; |
461 |
|
|
u_int32_t psize; |
462 |
|
|
|
463 |
|
|
found = efi = 0; |
464 |
|
|
for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { |
465 |
|
|
if (dp2->dp_typ == DOSPTYP_UNUSED) |
466 |
|
|
continue; |
467 |
|
|
found++; |
468 |
|
|
if (dp2->dp_typ != DOSPTYP_EFI) |
469 |
|
|
continue; |
470 |
|
|
psize = letoh32(dp2->dp_size); |
471 |
|
|
if (psize == (dsize - 1) || |
472 |
|
|
psize == UINT32_MAX) { |
473 |
|
|
if (letoh32(dp2->dp_start) == 1) |
474 |
|
|
efi++; |
475 |
|
|
} |
476 |
|
|
} |
477 |
|
|
if (found == 1 && efi == 1) |
478 |
|
|
return (0); |
479 |
|
|
|
480 |
|
|
return (1); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
int |
484 |
|
|
findgptefisys(int devfd, struct disklabel *dl) |
485 |
|
|
{ |
486 |
|
|
struct gpt_partition gp[NGPTPARTITIONS]; |
487 |
|
|
struct gpt_header gh; |
488 |
|
|
struct dos_partition dp[NDOSPART]; |
489 |
|
|
struct uuid efisys_uuid; |
490 |
|
|
const char efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM; |
491 |
|
|
off_t off; |
492 |
|
|
ssize_t len; |
493 |
|
|
u_int64_t start; |
494 |
|
|
int i; |
495 |
|
|
uint32_t orig_csum, new_csum; |
496 |
|
|
uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec; |
497 |
|
|
u_int8_t *secbuf; |
498 |
|
|
|
499 |
|
|
/* Prepare EFI System UUID */ |
500 |
|
|
uuid_dec_be(efisys_uuid_code, &efisys_uuid); |
501 |
|
|
|
502 |
|
|
if ((secbuf = malloc(dl->d_secsize)) == NULL) |
503 |
|
|
err(1, NULL); |
504 |
|
|
|
505 |
|
|
/* Check that there is a protective MBR. */ |
506 |
|
|
len = pread(devfd, secbuf, dl->d_secsize, 0); |
507 |
|
|
if (len != dl->d_secsize) |
508 |
|
|
err(4, "can't read mbr"); |
509 |
|
|
memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp)); |
510 |
|
|
if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) { |
511 |
|
|
free(secbuf); |
512 |
|
|
return (-1); |
513 |
|
|
} |
514 |
|
|
|
515 |
|
|
/* Check GPT Header. */ |
516 |
|
|
off = dl->d_secsize; /* Read header from sector 1. */ |
517 |
|
|
len = pread(devfd, secbuf, dl->d_secsize, off); |
518 |
|
|
if (len != dl->d_secsize) |
519 |
|
|
err(4, "can't pread gpt header"); |
520 |
|
|
|
521 |
|
|
memcpy(&gh, secbuf, sizeof(gh)); |
522 |
|
|
free(secbuf); |
523 |
|
|
|
524 |
|
|
/* Check signature */ |
525 |
|
|
if (letoh64(gh.gh_sig) != GPTSIGNATURE) |
526 |
|
|
return (-1); |
527 |
|
|
|
528 |
|
|
if (letoh32(gh.gh_rev) != GPTREVISION) |
529 |
|
|
return (-1); |
530 |
|
|
|
531 |
|
|
ghsize = letoh32(gh.gh_size); |
532 |
|
|
if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) |
533 |
|
|
return (-1); |
534 |
|
|
|
535 |
|
|
/* Check checksum */ |
536 |
|
|
orig_csum = gh.gh_csum; |
537 |
|
|
gh.gh_csum = 0; |
538 |
|
|
new_csum = crc32((unsigned char *)&gh, ghsize); |
539 |
|
|
gh.gh_csum = orig_csum; |
540 |
|
|
if (letoh32(orig_csum) != new_csum) |
541 |
|
|
return (-1); |
542 |
|
|
|
543 |
|
|
off = letoh64(gh.gh_part_lba) * dl->d_secsize; |
544 |
|
|
ghpartsize = letoh32(gh.gh_part_size); |
545 |
|
|
ghpartspersec = dl->d_secsize / ghpartsize; |
546 |
|
|
ghpartnum = letoh32(gh.gh_part_num); |
547 |
|
|
if ((secbuf = malloc(dl->d_secsize)) == NULL) |
548 |
|
|
err(1, NULL); |
549 |
|
|
for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) { |
550 |
|
|
len = pread(devfd, secbuf, dl->d_secsize, off); |
551 |
|
|
if (len != dl->d_secsize) { |
552 |
|
|
free(secbuf); |
553 |
|
|
return (-1); |
554 |
|
|
} |
555 |
|
|
memcpy(gp + i * ghpartspersec, secbuf, |
556 |
|
|
ghpartspersec * sizeof(struct gpt_partition)); |
557 |
|
|
off += dl->d_secsize; |
558 |
|
|
} |
559 |
|
|
free(secbuf); |
560 |
|
|
new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize); |
561 |
|
|
if (new_csum != letoh32(gh.gh_part_csum)) |
562 |
|
|
return (-1); |
563 |
|
|
|
564 |
|
|
start = 0; |
565 |
|
|
for (i = 0; i < ghpartnum && start == 0; i++) { |
566 |
|
|
if (memcmp(&gp[i].gp_type, &efisys_uuid, |
567 |
|
|
sizeof(struct uuid)) == 0) |
568 |
|
|
start = letoh64(gp[i].gp_lba_start); |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
if (start) { |
572 |
|
|
for (i = 0; i < MAXPARTITIONS; i++) { |
573 |
|
|
if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 && |
574 |
|
|
DL_GETPOFFSET(&dl->d_partitions[i]) == start) |
575 |
|
|
return ('a' + i); |
576 |
|
|
} |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
return (-1); |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
/* |
583 |
|
|
* Load the prototype boot sector (biosboot) into memory. |
584 |
|
|
*/ |
585 |
|
|
static char * |
586 |
|
|
loadproto(char *fname, long *size) |
587 |
|
|
{ |
588 |
|
|
int fd; |
589 |
|
|
size_t tdsize; /* text+data size */ |
590 |
|
|
char *bp; |
591 |
|
|
Elf_Ehdr eh; |
592 |
|
|
Elf_Word phsize; |
593 |
|
|
Elf_Phdr *ph; |
594 |
|
|
|
595 |
|
|
if ((fd = open(fname, O_RDONLY)) < 0) |
596 |
|
|
err(1, "%s", fname); |
597 |
|
|
|
598 |
|
|
if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) |
599 |
|
|
errx(1, "%s: read failed", fname); |
600 |
|
|
|
601 |
|
|
if (!IS_ELF(eh)) |
602 |
|
|
errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname, |
603 |
|
|
eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], |
604 |
|
|
eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); |
605 |
|
|
|
606 |
|
|
/* |
607 |
|
|
* We have to include the exec header in the beginning of |
608 |
|
|
* the buffer, and leave extra space at the end in case |
609 |
|
|
* the actual write to disk wants to skip the header. |
610 |
|
|
*/ |
611 |
|
|
|
612 |
|
|
/* Program load header. */ |
613 |
|
|
if (eh.e_phnum != 1) |
614 |
|
|
errx(1, "%s: %u ELF load sections (only support 1)", |
615 |
|
|
fname, eh.e_phnum); |
616 |
|
|
|
617 |
|
|
ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr)); |
618 |
|
|
if (ph == NULL) |
619 |
|
|
err(1, NULL); |
620 |
|
|
phsize = eh.e_phnum * sizeof(Elf_Phdr); |
621 |
|
|
|
622 |
|
|
if (pread(fd, ph, phsize, eh.e_phoff) != phsize) |
623 |
|
|
errx(1, "%s: can't pread header", fname); |
624 |
|
|
|
625 |
|
|
tdsize = ph->p_filesz; |
626 |
|
|
|
627 |
|
|
/* |
628 |
|
|
* Allocate extra space here because the caller may copy |
629 |
|
|
* the boot block starting at the end of the exec header. |
630 |
|
|
* This prevents reading beyond the end of the buffer. |
631 |
|
|
*/ |
632 |
|
|
if ((bp = calloc(tdsize, 1)) == NULL) |
633 |
|
|
err(1, NULL); |
634 |
|
|
|
635 |
|
|
/* Read the rest of the file. */ |
636 |
|
|
if (pread(fd, bp, tdsize, ph->p_offset) != (ssize_t)tdsize) |
637 |
|
|
errx(1, "%s: pread failed", fname); |
638 |
|
|
|
639 |
|
|
*size = tdsize; /* not aligned to DEV_BSIZE */ |
640 |
|
|
|
641 |
|
|
close(fd); |
642 |
|
|
return bp; |
643 |
|
|
} |
644 |
|
|
|
645 |
|
|
static void |
646 |
|
|
devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) |
647 |
|
|
{ |
648 |
|
|
if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size) |
649 |
|
|
err(1, "%s: devread: pread", msg); |
650 |
|
|
} |
651 |
|
|
|
652 |
|
|
/* |
653 |
|
|
* Read information about /boot's inode, then put this and filesystem |
654 |
|
|
* parameters from the superblock into pbr_symbols. |
655 |
|
|
*/ |
656 |
|
|
static int |
657 |
|
|
getbootparams(char *boot, int devfd, struct disklabel *dl) |
658 |
|
|
{ |
659 |
|
|
int fd; |
660 |
|
|
struct stat dsb, fsb; |
661 |
|
|
struct statfs fssb; |
662 |
|
|
struct partition *pp; |
663 |
|
|
struct fs *fs; |
664 |
|
|
char *sblock, *buf; |
665 |
|
|
u_int blk, *ap; |
666 |
|
|
struct ufs1_dinode *ip; |
667 |
|
|
int ndb; |
668 |
|
|
int mib[3]; |
669 |
|
|
size_t size; |
670 |
|
|
dev_t dev; |
671 |
|
|
|
672 |
|
|
/* |
673 |
|
|
* Open 2nd-level boot program and record enough details about |
674 |
|
|
* where it is on the filesystem represented by `devfd' |
675 |
|
|
* (inode block, offset within that block, and various filesystem |
676 |
|
|
* parameters essentially taken from the superblock) for biosboot |
677 |
|
|
* to be able to load it later. |
678 |
|
|
*/ |
679 |
|
|
|
680 |
|
|
/* Make sure the (probably new) boot file is on disk. */ |
681 |
|
|
sync(); sleep(1); |
682 |
|
|
|
683 |
|
|
if ((fd = open(boot, O_RDONLY)) < 0) |
684 |
|
|
err(1, "open: %s", boot); |
685 |
|
|
|
686 |
|
|
if (fstatfs(fd, &fssb) != 0) |
687 |
|
|
err(1, "statfs: %s", boot); |
688 |
|
|
|
689 |
|
|
if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) && |
690 |
|
|
strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) ) |
691 |
|
|
errx(1, "%s: not on an FFS filesystem", boot); |
692 |
|
|
|
693 |
|
|
#if 0 |
694 |
|
|
if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) |
695 |
|
|
errx(1, "read: %s", boot); |
696 |
|
|
|
697 |
|
|
if (!IS_ELF(eh)) { |
698 |
|
|
errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", |
699 |
|
|
boot, |
700 |
|
|
eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], |
701 |
|
|
eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); |
702 |
|
|
} |
703 |
|
|
#endif |
704 |
|
|
|
705 |
|
|
if (fsync(fd) != 0) |
706 |
|
|
err(1, "fsync: %s", boot); |
707 |
|
|
|
708 |
|
|
if (fstat(fd, &fsb) != 0) |
709 |
|
|
err(1, "fstat: %s", boot); |
710 |
|
|
|
711 |
|
|
if (fstat(devfd, &dsb) != 0) |
712 |
|
|
err(1, "fstat: %d", devfd); |
713 |
|
|
|
714 |
|
|
/* Check devices. */ |
715 |
|
|
mib[0] = CTL_MACHDEP; |
716 |
|
|
mib[1] = CPU_CHR2BLK; |
717 |
|
|
mib[2] = dsb.st_rdev; |
718 |
|
|
size = sizeof(dev); |
719 |
|
|
if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) { |
720 |
|
|
if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS) |
721 |
|
|
errx(1, "cross-device install"); |
722 |
|
|
} |
723 |
|
|
|
724 |
|
|
pp = &dl->d_partitions[DISKPART(fsb.st_dev)]; |
725 |
|
|
close(fd); |
726 |
|
|
|
727 |
|
|
/* Read superblock. */ |
728 |
|
|
if ((sblock = malloc(SBSIZE)) == NULL) |
729 |
|
|
err(1, NULL); |
730 |
|
|
|
731 |
|
|
devread(devfd, sblock, DL_SECTOBLK(dl, pp->p_offset) + SBLOCK, |
732 |
|
|
SBSIZE, "superblock"); |
733 |
|
|
fs = (struct fs *)sblock; |
734 |
|
|
|
735 |
|
|
/* Sanity-check super-block. */ |
736 |
|
|
if (fs->fs_magic != FS_MAGIC) |
737 |
|
|
errx(1, "Bad magic number in superblock"); |
738 |
|
|
if (fs->fs_inopb <= 0) |
739 |
|
|
err(1, "Bad inopb=%d in superblock", fs->fs_inopb); |
740 |
|
|
|
741 |
|
|
/* Read inode. */ |
742 |
|
|
if ((buf = malloc(fs->fs_bsize)) == NULL) |
743 |
|
|
err(1, NULL); |
744 |
|
|
|
745 |
|
|
blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino)); |
746 |
|
|
|
747 |
|
|
devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk, |
748 |
|
|
fs->fs_bsize, "inode"); |
749 |
|
|
ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, fsb.st_ino); |
750 |
|
|
|
751 |
|
|
/* |
752 |
|
|
* Have the inode. Figure out how many filesystem blocks (not disk |
753 |
|
|
* sectors) there are for biosboot to load. |
754 |
|
|
*/ |
755 |
|
|
ndb = howmany(ip->di_size, fs->fs_bsize); |
756 |
|
|
if (ndb <= 0) |
757 |
|
|
errx(1, "No blocks to load"); |
758 |
|
|
|
759 |
|
|
/* |
760 |
|
|
* Now set the values that will need to go into biosboot |
761 |
|
|
* (the partition boot record, a.k.a. the PBR). |
762 |
|
|
*/ |
763 |
|
|
sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16)); |
764 |
|
|
sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / |
765 |
|
|
dl->d_secsize)); |
766 |
|
|
|
767 |
|
|
/* |
768 |
|
|
* fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The |
769 |
|
|
* ino_to_fsba() return value is the number of fs_fsize units. |
770 |
|
|
* Calculate the shift to convert fs_fsize into physical sectors, |
771 |
|
|
* which are added to p_offset to get the sector address BIOS |
772 |
|
|
* will use. |
773 |
|
|
* |
774 |
|
|
* N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize. |
775 |
|
|
*/ |
776 |
|
|
sym_set_value(pbr_symbols, "_fsbtodb", |
777 |
|
|
ffs(fs->fs_fsize / dl->d_secsize) - 1); |
778 |
|
|
|
779 |
|
|
sym_set_value(pbr_symbols, "_p_offset", pp->p_offset); |
780 |
|
|
sym_set_value(pbr_symbols, "_inodeblk", |
781 |
|
|
ino_to_fsba(fs, fsb.st_ino)); |
782 |
|
|
ap = ip->di_db; |
783 |
|
|
sym_set_value(pbr_symbols, "_inodedbl", |
784 |
|
|
((((char *)ap) - buf) + INODEOFF)); |
785 |
|
|
sym_set_value(pbr_symbols, "_nblocks", ndb); |
786 |
|
|
|
787 |
|
|
if (verbose) { |
788 |
|
|
fprintf(stderr, "%s is %d blocks x %d bytes\n", |
789 |
|
|
boot, ndb, fs->fs_bsize); |
790 |
|
|
fprintf(stderr, "fs block shift %u; part offset %u; " |
791 |
|
|
"inode block %lld, offset %u\n", |
792 |
|
|
ffs(fs->fs_fsize / dl->d_secsize) - 1, |
793 |
|
|
pp->p_offset, |
794 |
|
|
ino_to_fsba(fs, fsb.st_ino), |
795 |
|
|
(unsigned int)((((char *)ap) - buf) + INODEOFF)); |
796 |
|
|
} |
797 |
|
|
|
798 |
|
|
free (sblock); |
799 |
|
|
free (buf); |
800 |
|
|
|
801 |
|
|
return 0; |
802 |
|
|
} |
803 |
|
|
|
804 |
|
|
void |
805 |
|
|
sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value) |
806 |
|
|
{ |
807 |
|
|
struct sym_data *p; |
808 |
|
|
|
809 |
|
|
for (p = sym_list; p->sym_name != NULL; p++) { |
810 |
|
|
if (strcmp(p->sym_name, sym) == 0) |
811 |
|
|
break; |
812 |
|
|
} |
813 |
|
|
|
814 |
|
|
if (p->sym_name == NULL) |
815 |
|
|
errx(1, "%s: no such symbol", sym); |
816 |
|
|
|
817 |
|
|
p->sym_value = value; |
818 |
|
|
p->sym_set = 1; |
819 |
|
|
} |
820 |
|
|
|
821 |
|
|
/* |
822 |
|
|
* Write the parameters stored in sym_list into the in-memory copy of |
823 |
|
|
* the prototype biosboot (proto), ready for it to be written to disk. |
824 |
|
|
*/ |
825 |
|
|
void |
826 |
|
|
pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list) |
827 |
|
|
{ |
828 |
|
|
struct sym_data *sym; |
829 |
|
|
struct nlist *nl; |
830 |
|
|
char *vp; |
831 |
|
|
u_int32_t *lp; |
832 |
|
|
u_int16_t *wp; |
833 |
|
|
u_int8_t *bp; |
834 |
|
|
|
835 |
|
|
for (sym = sym_list; sym->sym_name != NULL; sym++) { |
836 |
|
|
if (!sym->sym_set) |
837 |
|
|
errx(1, "%s not set", sym->sym_name); |
838 |
|
|
|
839 |
|
|
/* Allocate space for 2; second is null-terminator for list. */ |
840 |
|
|
nl = calloc(2, sizeof(struct nlist)); |
841 |
|
|
if (nl == NULL) |
842 |
|
|
err(1, NULL); |
843 |
|
|
|
844 |
|
|
nl->n_name = sym->sym_name; |
845 |
|
|
|
846 |
|
|
if (nlist_elf32(fname, nl) != 0) |
847 |
|
|
errx(1, "%s: symbol %s not found", |
848 |
|
|
fname, sym->sym_name); |
849 |
|
|
|
850 |
|
|
if (nl->n_type != (N_TEXT)) |
851 |
|
|
errx(1, "%s: %s: wrong type (%x)", |
852 |
|
|
fname, sym->sym_name, nl->n_type); |
853 |
|
|
|
854 |
|
|
/* Get a pointer to where the symbol's value needs to go. */ |
855 |
|
|
vp = proto + nl->n_value; |
856 |
|
|
|
857 |
|
|
switch (sym->sym_size) { |
858 |
|
|
case 4: /* u_int32_t */ |
859 |
|
|
lp = (u_int32_t *) vp; |
860 |
|
|
*lp = sym->sym_value; |
861 |
|
|
break; |
862 |
|
|
case 2: /* u_int16_t */ |
863 |
|
|
if (sym->sym_value >= 0x10000) /* out of range */ |
864 |
|
|
errx(1, "%s: symbol out of range (%u)", |
865 |
|
|
sym->sym_name, sym->sym_value); |
866 |
|
|
wp = (u_int16_t *) vp; |
867 |
|
|
*wp = (u_int16_t) sym->sym_value; |
868 |
|
|
break; |
869 |
|
|
case 1: /* u_int16_t */ |
870 |
|
|
if (sym->sym_value >= 0x100) /* out of range */ |
871 |
|
|
errx(1, "%s: symbol out of range (%u)", |
872 |
|
|
sym->sym_name, sym->sym_value); |
873 |
|
|
bp = (u_int8_t *) vp; |
874 |
|
|
*bp = (u_int8_t) sym->sym_value; |
875 |
|
|
break; |
876 |
|
|
default: |
877 |
|
|
errx(1, "%s: bad symbol size %d", |
878 |
|
|
sym->sym_name, sym->sym_size); |
879 |
|
|
/* NOTREACHED */ |
880 |
|
|
} |
881 |
|
|
|
882 |
|
|
free(nl); |
883 |
|
|
} |
884 |
|
|
} |