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