1*8ad87631Schristos /* $NetBSD: installboot.c,v 1.10 2016/01/21 16:58:36 christos Exp $ */
27378a103Swdk
37378a103Swdk /*
47378a103Swdk * Copyright (c) 2000 The NetBSD Foundation, Inc.
57378a103Swdk * All rights reserved.
67378a103Swdk *
77378a103Swdk * This code is derived from software contributed to The NetBSD Foundation
87378a103Swdk * by Wayne Knowles
97378a103Swdk *
107378a103Swdk * Redistribution and use in source and binary forms, with or without
117378a103Swdk * modification, are permitted provided that the following conditions
127378a103Swdk * are met:
137378a103Swdk * 1. Redistributions of source code must retain the above copyright
147378a103Swdk * notice, this list of conditions and the following disclaimer.
157378a103Swdk * 2. Redistributions in binary form must reproduce the above copyright
167378a103Swdk * notice, this list of conditions and the following disclaimer in the
177378a103Swdk * documentation and/or other materials provided with the distribution.
187378a103Swdk *
197378a103Swdk * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
207378a103Swdk * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
217378a103Swdk * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
227378a103Swdk * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
237378a103Swdk * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
247378a103Swdk * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
257378a103Swdk * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
267378a103Swdk * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
277378a103Swdk * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
287378a103Swdk * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
297378a103Swdk * POSSIBILITY OF SUCH DAMAGE.
307378a103Swdk */
317378a103Swdk
327378a103Swdk #include <assert.h>
337378a103Swdk #include <err.h>
347378a103Swdk #include <fcntl.h>
357378a103Swdk #include <stdlib.h>
367378a103Swdk #include <stdio.h>
377378a103Swdk #include <string.h>
387378a103Swdk #include <unistd.h>
397378a103Swdk
407378a103Swdk #include <sys/param.h>
417378a103Swdk #include <sys/stat.h>
427378a103Swdk #include <sys/disklabel.h>
437378a103Swdk
447378a103Swdk
457378a103Swdk #define VERBOSE(msg) if (verbose) \
467378a103Swdk fprintf(stderr, msg)
477378a103Swdk #define FATAL(a1,a2) errx(EXIT_FAILURE, a1, a2)
487378a103Swdk #define FATALIO(a1,a2) err(EXIT_FAILURE, a1, a2)
497378a103Swdk
507378a103Swdk #define BOOTBLOCK_NUMBER 2
517378a103Swdk #define BOOTBLOCK_OFFSET BOOTBLOCK_NUMBER*DEV_BSIZE
527378a103Swdk #define DEFAULT_BOOTFILE "boot"
537378a103Swdk
5402cdf4d2Sdsl static void usage(void);
5502cdf4d2Sdsl static void do_list(const char *);
5602cdf4d2Sdsl static void do_remove(const char *, const char *);
5702cdf4d2Sdsl static void do_install(const char *, const char *, const char *);
5802cdf4d2Sdsl static int mipsvh_cksum(struct mips_volheader *);
5902cdf4d2Sdsl static void read_volheader(const char *, struct mips_volheader *);
6002cdf4d2Sdsl static void write_volheader(const char *, struct mips_volheader *);
6102cdf4d2Sdsl static struct mips_voldir *voldir_findfile(struct mips_volheader *,
6202cdf4d2Sdsl const char *, int);
637378a103Swdk
647378a103Swdk int verbose, nowrite;
657378a103Swdk
667378a103Swdk static void
usage(void)67df7f595eScegger usage(void)
687378a103Swdk {
697378a103Swdk
707378a103Swdk fprintf(stderr, "usage:\n");
71c2bdafabScgd fprintf(stderr, "\t%s [-nv] disk bootstrap [name]\n", getprogname());
72c2bdafabScgd fprintf(stderr, "\t%s -r [-nv] disk [name]\n", getprogname());
73c2bdafabScgd fprintf(stderr, "\t%s -l [-nv] disk\n", getprogname());
747378a103Swdk exit(EXIT_FAILURE);
757378a103Swdk }
767378a103Swdk
777378a103Swdk int
main(int argc,char * argv[])787378a103Swdk main(int argc, char *argv[])
797378a103Swdk {
807378a103Swdk const char *disk;
817378a103Swdk int c, rflag, lflag;
827378a103Swdk
837378a103Swdk rflag = lflag = verbose = nowrite = 0;
847378a103Swdk
857378a103Swdk while ((c = getopt(argc, argv, "lnrv")) != -1) {
867378a103Swdk switch (c) {
877378a103Swdk case 'l':
887378a103Swdk /* List volume directory contents */
897378a103Swdk lflag = 1;
907378a103Swdk break;
917378a103Swdk case 'n':
927378a103Swdk /* Disable write of boot sectors */
937378a103Swdk nowrite = 1;
947378a103Swdk break;
957378a103Swdk case 'r':
967378a103Swdk /* Clear any existing boot block */
977378a103Swdk rflag = 1;
987378a103Swdk break;
997378a103Swdk case 'v':
1007378a103Swdk /* Verbose output */
1017378a103Swdk verbose = 1;
1027378a103Swdk break;
1037378a103Swdk default:
1047378a103Swdk usage();
1057378a103Swdk }
1067378a103Swdk }
1077378a103Swdk
1087378a103Swdk argc -= optind;
1097378a103Swdk argv += optind;
1107378a103Swdk
1117378a103Swdk if ((lflag && rflag) || argc < 1 || (lflag && argc != 1) ||
1127378a103Swdk (rflag && argc > 3) || argc > 4)
1137378a103Swdk usage();
1147378a103Swdk
1157378a103Swdk disk = argv[0];
1167378a103Swdk
1177378a103Swdk if (lflag)
1187378a103Swdk do_list(disk);
1197378a103Swdk else if (rflag)
1207378a103Swdk do_remove(disk, argc==2?argv[1]:DEFAULT_BOOTFILE);
1217378a103Swdk else
1227378a103Swdk do_install(disk, argv[1], argc==3?argv[2]:DEFAULT_BOOTFILE);
1237378a103Swdk
1247378a103Swdk exit(EXIT_SUCCESS);
1257378a103Swdk }
1267378a103Swdk
1277378a103Swdk static void
do_list(const char * disk)128454af1c0Sdsl do_list(const char *disk)
1297378a103Swdk {
1307378a103Swdk struct mips_volheader vh;
1317378a103Swdk struct mips_voldir *vdp;
1327378a103Swdk int i;
1337378a103Swdk
1347378a103Swdk read_volheader(disk, &vh);
1357378a103Swdk
1367378a103Swdk printf("Slot\t LBN\tLength\tFilename\n");
1377378a103Swdk printf("------------------------------------------\n");
1387378a103Swdk for (i=0, vdp=vh.vh_voldir; i<MIPS_NVOLDIR; i++, vdp++)
1397378a103Swdk if (vdp->vd_len)
1407378a103Swdk printf("%2d:\t%5d\t%6d\t%s\n", i, vdp->vd_lba,
1417378a103Swdk vdp->vd_len, vdp->vd_name);
1427378a103Swdk }
1437378a103Swdk
1447378a103Swdk static void
do_remove(const char * disk,const char * filename)145454af1c0Sdsl do_remove(const char *disk, const char *filename)
1467378a103Swdk {
1477378a103Swdk struct mips_volheader vh;
1487378a103Swdk struct mips_voldir *vdp;
1497378a103Swdk
1507378a103Swdk read_volheader(disk, &vh);
1517378a103Swdk vdp = voldir_findfile(&vh, filename, 0);
1527378a103Swdk if (vdp == NULL)
1537378a103Swdk FATAL("%s: file not found", disk);
1547378a103Swdk
155fa993060Swdk memset(vdp, 0, sizeof(*vdp));
1567378a103Swdk
1577378a103Swdk /* Update volume header */
1587378a103Swdk write_volheader(disk, &vh);
1597378a103Swdk }
1607378a103Swdk
1617378a103Swdk static void
do_install(const char * disk,const char * bootstrap,const char * bootname)162454af1c0Sdsl do_install(const char *disk, const char *bootstrap, const char *bootname)
1637378a103Swdk {
1647378a103Swdk struct stat bootstrapsb;
1657378a103Swdk struct mips_volheader vh;
1667378a103Swdk struct mips_voldir *vdp;
1677378a103Swdk int fd;
1687378a103Swdk char *boot_code;
1697378a103Swdk size_t boot_size;
1707378a103Swdk ssize_t len;
1717378a103Swdk
1727378a103Swdk /* Open the input file and check it out */
1737378a103Swdk if ((fd = open(bootstrap, O_RDONLY)) == -1)
1747378a103Swdk FATALIO("open %s", bootstrap);
1757378a103Swdk if (fstat(fd, &bootstrapsb) == -1)
1767378a103Swdk FATALIO("fstat %s", bootstrap);
1777378a103Swdk if (!S_ISREG(bootstrapsb.st_mode))
1787378a103Swdk FATAL("%s must be a regular file", bootstrap);
1797378a103Swdk
1807378a103Swdk boot_size = roundup(bootstrapsb.st_size, DEV_BSIZE);
1817378a103Swdk
1827378a103Swdk if (boot_size > 8192-1024)
1837378a103Swdk FATAL("bootstrap program too large (%d bytes)", boot_size);
1847378a103Swdk
1857378a103Swdk boot_code = malloc(boot_size);
1867378a103Swdk if (boot_code == NULL)
1877378a103Swdk FATAL("malloc %d bytes failed", boot_size);
188fa993060Swdk memset(boot_code, 0, boot_size);
1897378a103Swdk
1907378a103Swdk /* read the file into the buffer */
1917378a103Swdk len = read(fd, boot_code, bootstrapsb.st_size);
1927378a103Swdk if (len == -1)
1937378a103Swdk FATALIO("read %s", bootstrap);
1947378a103Swdk else if (len != bootstrapsb.st_size)
1957378a103Swdk FATAL("read %s: short read", bootstrap);
1967378a103Swdk (void)close(fd);
1977378a103Swdk
1987378a103Swdk read_volheader(disk, &vh);
1997378a103Swdk
2007378a103Swdk vdp = voldir_findfile(&vh, bootname, 1);
2017378a103Swdk if (vdp == NULL)
2027378a103Swdk FATAL("%s: volume directory full", disk);
2037378a103Swdk
2047378a103Swdk strcpy(vdp->vd_name, bootname);
2057378a103Swdk vdp->vd_lba = BOOTBLOCK_NUMBER;
2067378a103Swdk vdp->vd_len = bootstrapsb.st_size;
2077378a103Swdk
2087378a103Swdk if (nowrite) {
2097378a103Swdk if (verbose)
2107378a103Swdk fprintf(stderr, "not writing\n");
211*8ad87631Schristos free(boot_code);
2127378a103Swdk return;
2137378a103Swdk }
2147378a103Swdk
2157378a103Swdk if (verbose)
2167378a103Swdk fprintf(stderr, "writing bootstrap (%d bytes at logical block %d)\n",
2177378a103Swdk boot_size, 2);
2187378a103Swdk
2197378a103Swdk /* Write bootstrap */
2207378a103Swdk if ((fd = open(disk, O_WRONLY)) == -1)
2217378a103Swdk FATALIO("open %s", bootstrap);
2227378a103Swdk len = pwrite(fd, boot_code, boot_size, BOOTBLOCK_OFFSET);
223d05665daSchristos free(boot_code);
2247378a103Swdk if (len == -1)
2257378a103Swdk FATAL("write %s", disk);
2267378a103Swdk if (len != boot_size)
2277378a103Swdk FATAL("write %s: short write", disk);
2287378a103Swdk (void) close(fd);
2297378a103Swdk
2307378a103Swdk /* Update volume header */
2317378a103Swdk write_volheader(disk, &vh);
2327378a103Swdk }
2337378a103Swdk
2347378a103Swdk static void
read_volheader(const char * disk,struct mips_volheader * vhp)235454af1c0Sdsl read_volheader(const char *disk, struct mips_volheader *vhp)
2367378a103Swdk {
2377378a103Swdk int vfd;
2387378a103Swdk ssize_t len;
2397378a103Swdk
2407378a103Swdk if ((vfd = open(disk, O_RDONLY)) == -1)
2417378a103Swdk FATALIO("open %s", disk);
2427378a103Swdk
2437378a103Swdk len = pread(vfd, vhp, sizeof(*vhp), MIPS_VHSECTOR*DEV_BSIZE);
2447378a103Swdk
2457378a103Swdk (void) close(vfd);
2467378a103Swdk
2477378a103Swdk if (len == -1)
2487378a103Swdk FATALIO("read %s", disk);
2497378a103Swdk if (len != sizeof(*vhp))
2507378a103Swdk FATAL("read %s: short read", disk);
2517378a103Swdk
2527378a103Swdk /* Check volume header magic */
2537378a103Swdk if (vhp->vh_magic != MIPS_VHMAGIC)
2547378a103Swdk FATAL("%s: no volume header", disk);
2557378a103Swdk
2567378a103Swdk /* check volume header checksum */
2577378a103Swdk if (mipsvh_cksum(vhp))
2587378a103Swdk FATAL("%s: volume header corrupted", disk);
2597378a103Swdk }
2607378a103Swdk
2617378a103Swdk static void
write_volheader(const char * disk,struct mips_volheader * vhp)262454af1c0Sdsl write_volheader(const char *disk, struct mips_volheader *vhp)
2637378a103Swdk {
2647378a103Swdk int vfd;
2657378a103Swdk ssize_t len;
2667378a103Swdk
2677378a103Swdk /* update volume header checksum */
2687378a103Swdk vhp->vh_cksum = 0;
2697378a103Swdk vhp->vh_cksum = -mipsvh_cksum(vhp);
2707378a103Swdk
2717378a103Swdk if ((vfd = open(disk, O_WRONLY)) == -1)
2727378a103Swdk FATALIO("open %s", disk);
2737378a103Swdk
2747378a103Swdk if (verbose)
2757378a103Swdk fprintf(stderr, "%s: writing volume header\n", disk);
2767378a103Swdk
2777378a103Swdk len = pwrite(vfd, vhp, sizeof(*vhp), MIPS_VHSECTOR*512); /* XXX */
2787378a103Swdk if (len == -1)
2797378a103Swdk FATALIO("write %s", disk);
2807378a103Swdk if (len != sizeof(*vhp))
2817378a103Swdk FATAL("write %s: short write", disk);
2827378a103Swdk
2837378a103Swdk (void) close(vfd);
2847378a103Swdk }
2857378a103Swdk
2867378a103Swdk /*
2877378a103Swdk * Compute checksum for MIPS disk volume header
2887378a103Swdk *
2897378a103Swdk * Mips volume header checksum is the 32bit 2's complement sum
2907378a103Swdk * of the entire volume header structure
2917378a103Swdk */
2927378a103Swdk int
mipsvh_cksum(struct mips_volheader * vhp)293454af1c0Sdsl mipsvh_cksum(struct mips_volheader *vhp)
2947378a103Swdk {
2957378a103Swdk int i, *ptr;
2967378a103Swdk int cksum = 0;
2977378a103Swdk
2987378a103Swdk ptr = (int *)vhp;
2997378a103Swdk i = sizeof(*vhp) / sizeof(*ptr);
3007378a103Swdk while (i--)
3017378a103Swdk cksum += *ptr++;
3027378a103Swdk return cksum;
3037378a103Swdk }
3047378a103Swdk
3057378a103Swdk
3067378a103Swdk /*
3077378a103Swdk * Locate the volume directory slot that matches a filename
3087378a103Swdk *
3097378a103Swdk * If the file entry cannot be found and create is non-zero the next
3107378a103Swdk * empty slot is returned, otherwise return NULL
3117378a103Swdk */
3127378a103Swdk static struct mips_voldir *
voldir_findfile(struct mips_volheader * vhp,const char * file,int create)31382357f6dSdsl voldir_findfile(struct mips_volheader *vhp, const char *file, int create)
31482357f6dSdsl /* create: return unused entry if not found */
3157378a103Swdk {
3167378a103Swdk struct mips_voldir *vdp = vhp->vh_voldir;
3177378a103Swdk int i;
3187378a103Swdk
3197378a103Swdk for (i=0; i<MIPS_NVOLDIR; i++, vdp++) {
3207378a103Swdk if (strcmp(vdp->vd_name, file) == 0)
3217378a103Swdk return vdp;
3227378a103Swdk }
3237378a103Swdk if (create) {
3247378a103Swdk vdp = vhp->vh_voldir;
3257378a103Swdk for (i=0; i<MIPS_NVOLDIR; i++, vdp++)
3267378a103Swdk if (vdp->vd_len == 0)
3277378a103Swdk return vdp;
3287378a103Swdk }
3297378a103Swdk return NULL;
3307378a103Swdk }
331