GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/installboot/i386_installboot.c Lines: 0 396 0.0 %
Date: 2017-11-13 Branches: 0 260 0.0 %

Line Branch Exec Source
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
}