xref: /netbsd-src/sys/arch/prep/stand/installboot/installboot.c (revision df7f595ecd6efe54ea7c11083e2dbf711cad4b31)
1*df7f595eScegger /*	$NetBSD: installboot.c,v 1.8 2009/03/18 10:22:34 cegger Exp $	*/
20079d5e3Snonaka 
30079d5e3Snonaka /*
429c72c57Skeihan  * Copyright (c) 2000 NONAKA Kimihiro (nonaka@NetBSD.org).
50079d5e3Snonaka  * All rights reserved.
60079d5e3Snonaka  *
70079d5e3Snonaka  * Redistribution and use in source and binary forms, with or without
80079d5e3Snonaka  * modification, are permitted provided that the following conditions
90079d5e3Snonaka  * are met:
100079d5e3Snonaka  * 1. Redistributions of source code must retain the above copyright
110079d5e3Snonaka  *    notice, this list of conditions and the following disclaimer.
120079d5e3Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
130079d5e3Snonaka  *    notice, this list of conditions and the following disclaimer in the
140079d5e3Snonaka  *    documentation and/or other materials provided with the distribution.
150079d5e3Snonaka  * 3. The name of the author may not be used to endorse or promote products
160079d5e3Snonaka  *    derived from this software without specific prior written permission.
170079d5e3Snonaka  *
180079d5e3Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
190079d5e3Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
200079d5e3Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
210079d5e3Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
220079d5e3Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
230079d5e3Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
240079d5e3Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
250079d5e3Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
260079d5e3Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
270079d5e3Snonaka  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
280079d5e3Snonaka  */
290079d5e3Snonaka 
300079d5e3Snonaka #include <sys/param.h>
310079d5e3Snonaka #include <sys/exec_elf.h>
321c33b4e6Slukem #include <sys/bootblock.h>
330079d5e3Snonaka 
340079d5e3Snonaka #include <stdio.h>
350079d5e3Snonaka #include <stdlib.h>
360079d5e3Snonaka #include <unistd.h>
370079d5e3Snonaka #include <fcntl.h>
380079d5e3Snonaka #include <err.h>
39c50a7952She #include <string.h>
400079d5e3Snonaka 
410079d5e3Snonaka int nowrite, verbose;
420079d5e3Snonaka char *boot, *dev;
430079d5e3Snonaka 
440079d5e3Snonaka void usage(void);
450079d5e3Snonaka int devread(int, void *, daddr_t, size_t, char *);
460079d5e3Snonaka char *load_boot(char *, size_t *);
470079d5e3Snonaka int load_prep_partition(int, struct mbr_partition *);
480079d5e3Snonaka int main(int, char **);
490079d5e3Snonaka 
500079d5e3Snonaka void
usage(void)51*df7f595eScegger usage(void)
520079d5e3Snonaka {
530079d5e3Snonaka 
548a986b2eScgd 	fprintf(stderr, "usage: %s [-n] [-v] <boot> <device>\n",
558a986b2eScgd 	    getprogname());
560079d5e3Snonaka 	exit(1);
570079d5e3Snonaka }
580079d5e3Snonaka 
590079d5e3Snonaka int
devread(int fd,void * buf,daddr_t blk,size_t size,char * msg)600079d5e3Snonaka devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
610079d5e3Snonaka {
620079d5e3Snonaka 
630079d5e3Snonaka 	if (lseek(fd, (off_t)dbtob(blk), SEEK_SET) != dbtob(blk)) {
640079d5e3Snonaka 		warn("%s: devread: lseek", msg);
650079d5e3Snonaka 		return 1;
660079d5e3Snonaka 	}
670079d5e3Snonaka 	if (read(fd, buf, size) != size) {
680079d5e3Snonaka 		warn("%s: devread: read", msg);
690079d5e3Snonaka 		return 1;
700079d5e3Snonaka 	}
710079d5e3Snonaka 	return 0;
720079d5e3Snonaka }
730079d5e3Snonaka 
740079d5e3Snonaka char *
load_boot(char * boot,size_t * bootsize)750079d5e3Snonaka load_boot(char *boot, size_t *bootsize)
760079d5e3Snonaka {
770079d5e3Snonaka 	Elf32_Ehdr eh;
780079d5e3Snonaka 	Elf32_Phdr ph;
790079d5e3Snonaka 	struct stat st;
800079d5e3Snonaka 	int fd;
810079d5e3Snonaka 	int i;
820079d5e3Snonaka 	size_t imgsz = 0;
830079d5e3Snonaka 	char *bp = NULL;
840079d5e3Snonaka 
850079d5e3Snonaka 	if ((fd = open(boot, O_RDONLY)) < 0) {
860079d5e3Snonaka 		warn("open: %s", boot);
870079d5e3Snonaka 		return NULL;
880079d5e3Snonaka 	}
890079d5e3Snonaka 
900079d5e3Snonaka 	if (fstat(fd, &st) != 0) {
910079d5e3Snonaka 		warn("fstat: %s", boot);
920079d5e3Snonaka 		goto out;
930079d5e3Snonaka 	}
940079d5e3Snonaka 
950079d5e3Snonaka 	/*
960079d5e3Snonaka 	 * First, check ELF.
970079d5e3Snonaka 	 */
980079d5e3Snonaka 	if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) {
990079d5e3Snonaka 		warn("read: eh: %s", boot);
1000079d5e3Snonaka 		goto out;
1010079d5e3Snonaka 	}
1020079d5e3Snonaka 	if (memcmp(eh.e_ident, ELFMAG, SELFMAG) != 0 ||
1030079d5e3Snonaka 	    eh.e_ident[EI_CLASS] != ELFCLASS32) {
1040079d5e3Snonaka 		lseek(fd, 0L, SEEK_SET);
1050079d5e3Snonaka 		goto notelf;
1060079d5e3Snonaka 	}
1070079d5e3Snonaka 	if (be16toh(eh.e_machine) != EM_PPC) {
1080079d5e3Snonaka 		warn("not PowerPC binary.");
1090079d5e3Snonaka 		goto out;
1100079d5e3Snonaka 	}
1110079d5e3Snonaka 
1120079d5e3Snonaka 	for (i = 0; i < be16toh(eh.e_phnum); i++) {
1130079d5e3Snonaka 		(void)lseek(fd, be32toh(eh.e_phoff) + sizeof(ph) * i, SEEK_SET);
1140079d5e3Snonaka 		if (read(fd, &ph, sizeof(ph)) != sizeof(ph)) {
1150079d5e3Snonaka 			warn("read: ph: %s", boot);
1160079d5e3Snonaka 			goto out;
1170079d5e3Snonaka 		}
1180079d5e3Snonaka 
1190079d5e3Snonaka 		if ((be32toh(ph.p_type) != PT_LOAD) ||
1200079d5e3Snonaka 		    !(be32toh(ph.p_flags) & PF_X))
1210079d5e3Snonaka 			continue;
1220079d5e3Snonaka 
1230079d5e3Snonaka 		imgsz = st.st_size - be32toh(ph.p_offset);
1240079d5e3Snonaka 		lseek(fd, be32toh(ph.p_offset), SEEK_SET);
1250079d5e3Snonaka 		break;
1260079d5e3Snonaka 	}
1270079d5e3Snonaka 
1280079d5e3Snonaka notelf:
1290079d5e3Snonaka 	/*
1300079d5e3Snonaka 	 * Second, check PReP bootable image.
1310079d5e3Snonaka 	 */
1320079d5e3Snonaka 	if (imgsz == 0) {
1330079d5e3Snonaka 		char buf[DEV_BSIZE];
1340079d5e3Snonaka 
1350079d5e3Snonaka 		printf("Bootable image: ");
1360079d5e3Snonaka 		if (load_prep_partition(fd, 0)) {
1370079d5e3Snonaka 			warn("no PReP bootable image.");
1380079d5e3Snonaka 			goto out;
1390079d5e3Snonaka 		}
1400079d5e3Snonaka 
1410079d5e3Snonaka 		if (lseek(fd, (off_t)dbtob(1), SEEK_SET) != dbtob(1)) {
1420079d5e3Snonaka 			warn("bootable image lseek sector 1");
1430079d5e3Snonaka 			goto out;
1440079d5e3Snonaka 		}
1450079d5e3Snonaka 		if (read(fd, buf, DEV_BSIZE) != DEV_BSIZE) {
1460079d5e3Snonaka 			warn("read: start/size");
1470079d5e3Snonaka 			goto out;
1480079d5e3Snonaka 		}
1490079d5e3Snonaka 
1500079d5e3Snonaka 		imgsz = le32toh(*(u_int32_t *)(buf + sizeof(u_int32_t)))
1510079d5e3Snonaka 		    - dbtob(2);
1520079d5e3Snonaka 		lseek(fd, le32toh(*(u_int32_t *)buf), SEEK_SET);
1530079d5e3Snonaka 	}
1540079d5e3Snonaka 
1550079d5e3Snonaka 	if ((bp = (char *)calloc(roundup(imgsz, DEV_BSIZE), 1)) == NULL) {
1560079d5e3Snonaka 		warn("calloc: no memory for boot image.");
1570079d5e3Snonaka 		goto out;
1580079d5e3Snonaka 	}
1590079d5e3Snonaka 
1600079d5e3Snonaka 	if (read(fd, bp, imgsz) != imgsz) {
1610079d5e3Snonaka 		warn("read: boot image: %s", boot);
1620079d5e3Snonaka 		goto out;
1630079d5e3Snonaka 	}
1640079d5e3Snonaka 
1650079d5e3Snonaka 	if (verbose) {
1660079d5e3Snonaka 		printf("image size = %d\n", imgsz);
1670079d5e3Snonaka 	}
1680079d5e3Snonaka 
1690079d5e3Snonaka 	*bootsize = roundup(imgsz, DEV_BSIZE);
1700079d5e3Snonaka 
1710079d5e3Snonaka 	close(fd);
1720079d5e3Snonaka 	return bp;
1730079d5e3Snonaka 
1740079d5e3Snonaka out:
1750079d5e3Snonaka 	if (bp != NULL)
1760079d5e3Snonaka 		free(bp);
1770079d5e3Snonaka 	if (fd >= 0)
1780079d5e3Snonaka 		close(fd);
1790079d5e3Snonaka 	return NULL;
1800079d5e3Snonaka }
1810079d5e3Snonaka 
1820079d5e3Snonaka int
load_prep_partition(int devfd,struct mbr_partition * ppp)1830079d5e3Snonaka load_prep_partition(int devfd, struct mbr_partition *ppp)
1840079d5e3Snonaka {
1850079d5e3Snonaka 	char mbr[512];
1860079d5e3Snonaka 	struct mbr_partition *mbrp;
1870079d5e3Snonaka 	int i;
1880079d5e3Snonaka 
1890079d5e3Snonaka 	if (devread(devfd, mbr, MBR_BBSECTOR, DEV_BSIZE, "MBR") != 0)
1900079d5e3Snonaka 		return 1;
1911c33b4e6Slukem 	if (*(u_int16_t *)&mbr[MBR_MAGIC_OFFSET] != htole16(MBR_MAGIC)) {
1920079d5e3Snonaka 		warn("no MBR_MAGIC");
1930079d5e3Snonaka 		return 1;
1940079d5e3Snonaka 	}
1950079d5e3Snonaka 
1961c33b4e6Slukem 	mbrp = (struct mbr_partition *)&mbr[MBR_PART_OFFSET];
1971c33b4e6Slukem 	for (i = 0; i < MBR_PART_COUNT; i++) {
1981c33b4e6Slukem 		if (mbrp[i].mbrp_type == MBR_PTYPE_PREP)
1990079d5e3Snonaka 			break;
2000079d5e3Snonaka 	}
2011c33b4e6Slukem 	if (i == MBR_PART_COUNT) {
2020079d5e3Snonaka 		warn("no PReP partition.");
2030079d5e3Snonaka 		return 1;
2040079d5e3Snonaka 	}
2050079d5e3Snonaka 
2060079d5e3Snonaka 	if (verbose) {
2070079d5e3Snonaka 		printf("PReP partition: start = %d, size = %d\n",
2080079d5e3Snonaka 		    le32toh(mbrp[i].mbrp_start), le32toh(mbrp[i].mbrp_size));
2090079d5e3Snonaka 	}
2100079d5e3Snonaka 
2110079d5e3Snonaka 	if (ppp) {
2120079d5e3Snonaka 		*ppp = mbrp[i];
2130079d5e3Snonaka 		ppp->mbrp_start = le32toh(ppp->mbrp_start);
2140079d5e3Snonaka 		ppp->mbrp_size = le32toh(ppp->mbrp_size);
2150079d5e3Snonaka 	}
2160079d5e3Snonaka 
2170079d5e3Snonaka 	return 0;
2180079d5e3Snonaka }
2190079d5e3Snonaka 
2200079d5e3Snonaka int
main(int argc,char ** argv)2210079d5e3Snonaka main(int argc, char **argv)
2220079d5e3Snonaka {
2230079d5e3Snonaka 	struct mbr_partition ppp;
2240079d5e3Snonaka 	size_t bootsize;
2250079d5e3Snonaka 	int c;
2260079d5e3Snonaka 	int boot00[512/sizeof(int)];
2270079d5e3Snonaka 	int devfd = -1;
2280079d5e3Snonaka 	char *bp;
2290079d5e3Snonaka 
2300079d5e3Snonaka 	while ((c = getopt(argc, argv, "vn")) != EOF) {
2310079d5e3Snonaka 		switch (c) {
2320079d5e3Snonaka 		case 'n':
2330079d5e3Snonaka 			nowrite = 1;
2340079d5e3Snonaka 			break;
2350079d5e3Snonaka 		case 'v':
2360079d5e3Snonaka 			verbose = 1;
2370079d5e3Snonaka 			break;
2380079d5e3Snonaka 		default:
2390079d5e3Snonaka 			usage();
2400079d5e3Snonaka 			break;
2410079d5e3Snonaka 		}
2420079d5e3Snonaka 	}
2430079d5e3Snonaka 
2440079d5e3Snonaka 	if (argc - optind < 2)
2450079d5e3Snonaka 		usage();
2460079d5e3Snonaka 
2470079d5e3Snonaka 	boot = argv[optind];
2480079d5e3Snonaka 	dev = argv[optind + 1];
2490079d5e3Snonaka 	if (verbose) {
2500079d5e3Snonaka 		printf("boot: %s\n", boot);
2510079d5e3Snonaka 		printf("dev: %s\n", dev);
2520079d5e3Snonaka 	}
2530079d5e3Snonaka 
2540079d5e3Snonaka 	if ((bp = load_boot(boot, &bootsize)) == NULL)
2550079d5e3Snonaka 		return 1;
2560079d5e3Snonaka 
2570079d5e3Snonaka 	if ((devfd = open(dev, O_RDONLY, 0)) < 0) {
2580079d5e3Snonaka 		warn("open: %s", dev);
2590079d5e3Snonaka 		goto out;
2600079d5e3Snonaka 	}
2610079d5e3Snonaka 
2620079d5e3Snonaka 	if (load_prep_partition(devfd, &ppp)) {
2630079d5e3Snonaka 		warn("load_prep_partition");
2640079d5e3Snonaka 		goto out;
2650079d5e3Snonaka 	}
2660079d5e3Snonaka 
2670079d5e3Snonaka 	if (bootsize + dbtob(2) > dbtob(ppp.mbrp_size)) {
2680079d5e3Snonaka 		warn("boot image is too big.");
2690079d5e3Snonaka 		goto out;
2700079d5e3Snonaka 	}
2710079d5e3Snonaka 
2720079d5e3Snonaka 	close(devfd);
2730079d5e3Snonaka 
2740079d5e3Snonaka 	if (nowrite) {
2750079d5e3Snonaka 		free(bp);
2760079d5e3Snonaka 		return 0;
2770079d5e3Snonaka 	}
2780079d5e3Snonaka 
2790079d5e3Snonaka 	if ((devfd = open(dev, O_RDWR, 0)) < 0) {
2800079d5e3Snonaka 		warn("open: %s", dev);
2810079d5e3Snonaka 		goto out;
2820079d5e3Snonaka 	}
2830079d5e3Snonaka 
2840079d5e3Snonaka 	/*
2850079d5e3Snonaka 	 * Write boot image.
2860079d5e3Snonaka 	 */
2870079d5e3Snonaka 	memset(boot00, 0, sizeof(boot00));
2880079d5e3Snonaka 	(void)lseek(devfd, (off_t)dbtob(ppp.mbrp_start), SEEK_SET);
2890079d5e3Snonaka 	if (write(devfd, boot00, sizeof(boot00)) != sizeof(boot00)) {
2900079d5e3Snonaka 		warn("write boot00(prep mbr)");
2910079d5e3Snonaka 		goto out;
2920079d5e3Snonaka 	}
2930079d5e3Snonaka 
2940079d5e3Snonaka 	(void)lseek(devfd, (off_t)dbtob(ppp.mbrp_start+1), SEEK_SET);
2950079d5e3Snonaka 	boot00[0] = htole32(dbtob(2));
2960079d5e3Snonaka 	boot00[1] = htole32(bootsize);
2970079d5e3Snonaka 	if (write(devfd, boot00, sizeof(boot00)) != sizeof(boot00)) {
2980079d5e3Snonaka 		warn("write boot00(prep start/size)");
2990079d5e3Snonaka 		goto out;
3000079d5e3Snonaka 	}
3010079d5e3Snonaka 
3020079d5e3Snonaka 	if (devread(devfd, boot00, 1, DEV_BSIZE, "start/size") != 0)
3030079d5e3Snonaka 		goto out;
3040079d5e3Snonaka 	boot00[0] = htole32(dbtob(ppp.mbrp_start));
3050079d5e3Snonaka 	boot00[1] = htole32(bootsize + dbtob(2));
3060079d5e3Snonaka 	(void)lseek(devfd, (off_t)dbtob(1), SEEK_SET);
3070079d5e3Snonaka 	if (write(devfd, boot00, sizeof(boot00)) != sizeof(boot00)) {
3080079d5e3Snonaka 		warn("write boot00(master start/size)");
3090079d5e3Snonaka 		goto out;
3100079d5e3Snonaka 	}
3110079d5e3Snonaka 
3120079d5e3Snonaka 	(void)lseek(devfd, (off_t)dbtob(ppp.mbrp_start+2), SEEK_SET);
3130079d5e3Snonaka 	if (write(devfd, bp, bootsize) != bootsize) {
3140079d5e3Snonaka 		warn("write boot loader");
3150079d5e3Snonaka 		goto out;
3160079d5e3Snonaka 	}
3170079d5e3Snonaka 
3180079d5e3Snonaka 	close(devfd);
3190079d5e3Snonaka 	free(bp);
3200079d5e3Snonaka 	return 0;
3210079d5e3Snonaka 
3220079d5e3Snonaka out:
3230079d5e3Snonaka 	if (devfd >= 0)
3240079d5e3Snonaka 		close(devfd);
3250079d5e3Snonaka 	if (bp != NULL)
3260079d5e3Snonaka 		free(bp);
3270079d5e3Snonaka 	return 1;
3280079d5e3Snonaka }
329