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