1*3a50f0a9Sjmc /*	$OpenBSD: landisk_installboot.c,v 1.12 2022/12/28 21:30:16 jmc Exp $	*/
2b4544c7cSjsing 
3b4544c7cSjsing /*
4b4544c7cSjsing  * Copyright (c) 2013 Joel Sing <jsing@openbsd.org>
5b4544c7cSjsing  *
6b4544c7cSjsing  * Permission to use, copy, modify, and distribute this software for any
7b4544c7cSjsing  * purpose with or without fee is hereby granted, provided that the above
8b4544c7cSjsing  * copyright notice and this permission notice appear in all copies.
9b4544c7cSjsing  *
10b4544c7cSjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11b4544c7cSjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12b4544c7cSjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13b4544c7cSjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14b4544c7cSjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15b4544c7cSjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16b4544c7cSjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17b4544c7cSjsing  */
18b4544c7cSjsing 
190ea62af5Smiod #include <sys/param.h>	/* DEV_BSIZE */
200ea62af5Smiod #include <sys/disklabel.h>
210ea62af5Smiod #include <sys/dkio.h>
220ea62af5Smiod #include <sys/ioctl.h>
230ea62af5Smiod #include <sys/stat.h>
240ea62af5Smiod 
250ea62af5Smiod #include <ufs/ffs/fs.h>
260ea62af5Smiod 
270ea62af5Smiod #include <err.h>
280ea62af5Smiod #include <fcntl.h>
290ea62af5Smiod #include <stdio.h>
30ea9a2450Sderaadt #include <stdlib.h>
310ea62af5Smiod #include <string.h>
323b574945Stobiasu #include <unistd.h>
33ea9a2450Sderaadt 
34b4544c7cSjsing #include "installboot.h"
35b4544c7cSjsing 
360ea62af5Smiod void	md_bootstrap(int, char *, char *);
370ea62af5Smiod 
38c297c651Smiod char	*bootldr;
39c297c651Smiod 
40b4544c7cSjsing void
md_init(void)41b4544c7cSjsing md_init(void)
42b4544c7cSjsing {
43b4544c7cSjsing 	stages = 2;
44b4544c7cSjsing 	stage1 = "/usr/mdec/xxboot";
45c297c651Smiod 	stage2 = "/usr/mdec/boot";
46c297c651Smiod 
47c297c651Smiod 	bootldr = "/boot";
48b4544c7cSjsing }
49b4544c7cSjsing 
50b4544c7cSjsing void
md_loadboot(void)51b4544c7cSjsing md_loadboot(void)
52b4544c7cSjsing {
53b4544c7cSjsing }
54b4544c7cSjsing 
55b4544c7cSjsing void
md_prepareboot(int devfd,char * dev)56c3e1bf61Skettenis md_prepareboot(int devfd, char *dev)
57c3e1bf61Skettenis {
58c3e1bf61Skettenis }
59c3e1bf61Skettenis 
60c3e1bf61Skettenis void
md_installboot(int devfd,char * dev)61b4544c7cSjsing md_installboot(int devfd, char *dev)
62b4544c7cSjsing {
6368507edaSjsing 	/* XXX - is this necessary? */
6468507edaSjsing 	sync();
65c297c651Smiod 
66c297c651Smiod 	bootldr = fileprefix(root, bootldr);
67597864b7Skrw 	if (bootldr == NULL)
68597864b7Skrw 		exit(1);
69c297c651Smiod 	if (!nowrite)
70597864b7Skrw 		if (filecopy(stage2, bootldr) == -1)
71597864b7Skrw 			exit(1);
72c297c651Smiod 
730ea62af5Smiod 	/*
74*3a50f0a9Sjmc 	 * Write bootblock into the beginning of the OpenBSD partition or
750ea62af5Smiod 	 * at the beginning of the disk.
760ea62af5Smiod 	 */
770ea62af5Smiod 	md_bootstrap(devfd, dev, stage1);
780ea62af5Smiod }
790ea62af5Smiod 
800ea62af5Smiod void
md_bootstrap(int devfd,char * dev,char * bootfile)810ea62af5Smiod md_bootstrap(int devfd, char *dev, char *bootfile)
820ea62af5Smiod {
830ea62af5Smiod 	struct disklabel dl;
840ea62af5Smiod 	struct disklabel *lp;
850ea62af5Smiod 	struct partition *pp;
860ea62af5Smiod 	char *boot, *p, part;
870ea62af5Smiod 	size_t bootsize;
880ea62af5Smiod 	size_t bootsec;
890ea62af5Smiod 	struct stat sb;
900ea62af5Smiod 	daddr_t bootpos = 0;
910ea62af5Smiod 	int fd, i;
920ea62af5Smiod 
930ea62af5Smiod 	/*
940ea62af5Smiod 	 * Install bootstrap code onto the given disk, preserving the
950ea62af5Smiod 	 * existing disklabel.
960ea62af5Smiod 	 */
970ea62af5Smiod 
980ea62af5Smiod 	/* Read disklabel from disk. */
990ea62af5Smiod 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
1000ea62af5Smiod 		err(1, "disklabel");
1010ea62af5Smiod 	if (dl.d_secsize == 0) {
1020ea62af5Smiod 		warnx("disklabel has sector size of 0, assuming %d", DEV_BSIZE);
1030ea62af5Smiod 		dl.d_secsize = DEV_BSIZE;
1040ea62af5Smiod 	}
1050ea62af5Smiod 
1060ea62af5Smiod 	/* Read bootstrap file. */
1070ea62af5Smiod 	if (verbose)
1080ea62af5Smiod 		fprintf(stderr, "reading bootstrap from %s\n", bootfile);
1090ea62af5Smiod 	fd = open(bootfile, O_RDONLY);
1100ea62af5Smiod 	if (fd == -1)
1110ea62af5Smiod 		err(1, "open %s", bootfile);
1120ea62af5Smiod 	if (fstat(fd, &sb) == -1)
1130ea62af5Smiod 		err(1, "fstat %s", bootfile);
1140ea62af5Smiod 	bootsec = howmany((ssize_t)sb.st_size, dl.d_secsize);
1150ea62af5Smiod 	bootsize = bootsec * dl.d_secsize;
1160ea62af5Smiod 	if (verbose)
1170ea62af5Smiod 		fprintf(stderr, "bootstrap is %zu bytes "
1180ea62af5Smiod 		    "(%zu sectors @ %u bytes = %zu bytes)\n",
1190ea62af5Smiod 		    (ssize_t)sb.st_size, bootsec, dl.d_secsize, bootsize);
1200ea62af5Smiod 	boot = calloc(1, bootsize);
1210ea62af5Smiod 	if (boot == NULL)
1220ea62af5Smiod 		err(1, "calloc");
1230ea62af5Smiod 	if (read(fd, boot, bootsize) != (ssize_t)sb.st_size)
1240ea62af5Smiod 		err(1, "read");
1250ea62af5Smiod 	close(fd);
1260ea62af5Smiod 
1270ea62af5Smiod 	/*
1280ea62af5Smiod 	 * The landisk bootstrap can work when put either at the start of the
1290ea62af5Smiod 	 * disk, or at the start of an OpenBSD MBR partition, invoked from
1300ea62af5Smiod 	 * /usr/mdec/mbr.
1310ea62af5Smiod 	 * Check for a partition table in order to decide where to put the
1320ea62af5Smiod 	 * first-level bootstrap code.
1330ea62af5Smiod 	 */
1340ea62af5Smiod 
1350ea62af5Smiod 	if (dl.d_secsize >= sizeof(struct dos_mbr)) {
1360ea62af5Smiod 		uint8_t *secbuf;
1370ea62af5Smiod 		struct dos_mbr *mbr;
1380ea62af5Smiod 
1390ea62af5Smiod 		secbuf = malloc(dl.d_secsize);
1400ea62af5Smiod 		if (secbuf == NULL)
1410ea62af5Smiod 			err(1, "malloc");
1420ea62af5Smiod 
1430ea62af5Smiod 		/* Read MBR. */
1440ea62af5Smiod 		if (pread(devfd, secbuf, dl.d_secsize, 0) != dl.d_secsize)
1450ea62af5Smiod 			err(4, "can't read mbr");
1460ea62af5Smiod 		mbr = (struct dos_mbr *)secbuf;
1470ea62af5Smiod 		/* safe check because landisk is little endian */
1480ea62af5Smiod 		if (mbr->dmbr_sign == DOSMBR_SIGNATURE) {
1490ea62af5Smiod 			for (i = 0; i < NDOSPART; i++) {
1500ea62af5Smiod 				if (mbr->dmbr_parts[i].dp_typ ==
1510ea62af5Smiod 				    DOSPTYP_OPENBSD) {
1520ea62af5Smiod 					bootpos = mbr->dmbr_parts[i].dp_start;
1530ea62af5Smiod 					break;
1540ea62af5Smiod 				}
1550ea62af5Smiod 			}
1560ea62af5Smiod 		}
1570ea62af5Smiod 
1580ea62af5Smiod 		free(secbuf);
1590ea62af5Smiod 	}
1600ea62af5Smiod 
1610ea62af5Smiod 	if (bootpos == 0) {
1620ea62af5Smiod 		/*
1630ea62af5Smiod 		 * Installing at the start of the disk.
1640ea62af5Smiod 		 * Check that the bootstrap will fit - partitions must not
1650ea62af5Smiod 		 * overlap, or if they do, the partition type must be either
1660ea62af5Smiod 		 * FS_BOOT or FS_UNUSED. The 'c' partition will always overlap
1670ea62af5Smiod 		 * and is ignored.
1680ea62af5Smiod 		 */
1690ea62af5Smiod 		if (verbose)
1700ea62af5Smiod 			fprintf(stderr,
1710ea62af5Smiod 			    "ensuring used partitions do not overlap "
1720ea62af5Smiod 			    "with bootstrap sectors 0-%zu\n", bootsec);
1730ea62af5Smiod 		for (i = 0; i < dl.d_npartitions; i++) {
1740ea62af5Smiod 			part = 'a' + i;
1750ea62af5Smiod 			pp = &dl.d_partitions[i];
1760ea62af5Smiod 			if (i == RAW_PART)
1770ea62af5Smiod 				continue;
1780ea62af5Smiod 			if (DL_GETPSIZE(pp) == 0)
1790ea62af5Smiod 				continue;
1800ea62af5Smiod 			if (bootpos + (u_int64_t)bootsec <= DL_GETPOFFSET(pp))
1810ea62af5Smiod 				continue;
1820ea62af5Smiod 			switch (pp->p_fstype) {
1830ea62af5Smiod 			case FS_BOOT:
1840ea62af5Smiod 				break;
1850ea62af5Smiod 			case FS_UNUSED:
1860ea62af5Smiod 				warnx("bootstrap overlaps "
1870ea62af5Smiod 				    "with unused partition %c", part);
1880ea62af5Smiod 				break;
1890ea62af5Smiod 			default:
1900ea62af5Smiod 				errx(1, "bootstrap overlaps with partition %c",
1910ea62af5Smiod 				    part);
1920ea62af5Smiod 			}
1930ea62af5Smiod 		}
1940ea62af5Smiod 	} else {
1950ea62af5Smiod 		/*
1960ea62af5Smiod 		 * Installing at the start of the OpenBSD partition.
1970ea62af5Smiod 		 * We only need to ensure the bootstrap code fits in the
1980ea62af5Smiod 		 * BBSIZE reserved area at the beginning of the file
1990ea62af5Smiod 		 * system.
2000ea62af5Smiod 		 */
2010ea62af5Smiod 		if (bootsize > BBSIZE)
2020ea62af5Smiod 			errx(1, "bootstrap is too large");
2030ea62af5Smiod 	}
2040ea62af5Smiod 
2050ea62af5Smiod 	/*
2060ea62af5Smiod 	 * Make sure the bootstrap has left space for the disklabel.
2070ea62af5Smiod 	 * N.B.: LABELSECTOR *is* a DEV_BSIZE quantity!
2080ea62af5Smiod 	 */
2090ea62af5Smiod 	lp = (struct disklabel *)(boot + (LABELSECTOR * DEV_BSIZE) +
2100ea62af5Smiod 	    LABELOFFSET);
2110ea62af5Smiod 	for (i = 0, p = (char *)lp; i < (int)sizeof(*lp); i++)
2120ea62af5Smiod 		if (p[i] != 0)
2130ea62af5Smiod 			errx(1, "bootstrap has data in disklabel area");
2140ea62af5Smiod 
2150ea62af5Smiod 	/* Patch the disklabel into the bootstrap code. */
2160ea62af5Smiod 	memcpy(lp, &dl, sizeof(dl));
2170ea62af5Smiod 
2180ea62af5Smiod 	/* Write the bootstrap out to the disk. */
2190ea62af5Smiod 	bootpos *= dl.d_secsize;
2200ea62af5Smiod 	if (verbose)
2210ea62af5Smiod 		fprintf(stderr, "%s bootstrap to disk at offset %llx\n",
2220ea62af5Smiod 		    (nowrite ? "would write" : "writing"), bootpos);
2230ea62af5Smiod 	if (nowrite)
2240ea62af5Smiod 		return;
2250ea62af5Smiod 	if (pwrite(devfd, boot, bootsize, bootpos) != (ssize_t)bootsize)
2260ea62af5Smiod 		err(1, "pwrite");
227b4544c7cSjsing }
228