| /* |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com |
| */ |
| |
| #if !(defined hpux || defined __hpux) |
| # define LONG_OPTIONS 1 |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #if LONG_OPTIONS |
| # define _GNU_SOURCE |
| # include <getopt.h> |
| #endif |
| #include "load.h" |
| #include "palo.h" |
| |
| |
| #define B32(x) __be32_to_cpu((x)) |
| |
| /* maximum allowed HP boot loader (IPL) size */ |
| #define MAXBLSIZE (256 * 1024) |
| |
| static int Install = 0; |
| int verbose = 0; |
| |
| int disk_2gb_limit = 0; |
| |
| /* compute the sum of words in an 4-byte aligned region */ |
| int |
| checksum(void *p, size_t len) |
| { |
| int xsum = 0; |
| int *x = (int *)p; |
| int i; |
| |
| if (verbose) printf("checksum(%p, %u) = ", p, len); |
| len /= 4; |
| |
| for (i = 0; i < len; i++) |
| { |
| xsum += B32(x[i]); |
| } |
| |
| if (verbose) printf("0x%08x\n", xsum); |
| |
| return (xsum); |
| } |
| |
| static void |
| fb_init(struct firstblock *f) |
| { |
| f->lifmagic0 = 0x80; |
| f->lifmagic1 = 0x00; |
| f->rd_sz = f->kern32_sz = f->kern64_sz = 0; |
| f->kern32_native_sz = f->kern64_native_sz = 0; |
| f->rd_offset = f->kern32_offset = f->kern64_offset = 0; |
| strcpy(f->palomagic, PALOMAGIC); |
| f->version = PALOHDRVERSION; |
| f->flags = 0; |
| if (Install) |
| f->flags |= PFLAG_INSTALL; |
| } |
| |
| int |
| check_bootloader(int media, int line) |
| { |
| struct firstblock f; |
| int ipl_words; |
| int xsum; |
| unsigned int *ipl; |
| |
| if (verbose) printf("check_bootloader %d\n", line); |
| |
| STRUCTREAD(media, f, 0); |
| |
| if (f.lifmagic0 != 0x80 || f.lifmagic1 != 0x00) |
| { |
| fprintf(stderr, "0x%x 0x%x\n", f.lifmagic0, f.lifmagic1); |
| error(6, "LIF header"); |
| } |
| |
| if (B32(f.ipl_addr) < 2048 || |
| (B32(f.ipl_addr) % 2048) != 0) |
| error(6, "ipl_addr"); |
| |
| if (B32(f.ipl_size) <= 0 || |
| B32(f.ipl_size) >= MAXBLSIZE || |
| (B32(f.ipl_size) % 2048) != 0) |
| error(6, "ipl_size"); |
| |
| if (B32(f.ipl_entry) >= B32(f.ipl_size) || |
| B32(f.ipl_entry) < 0 || |
| (B32(f.ipl_entry) % 4) != 0) |
| error(6, "ipl_entry"); |
| |
| ipl_words = B32(f.ipl_size) / sizeof ipl[0]; |
| |
| /* seekread the IPL */ |
| ipl = calloc(ipl_words, sizeof ipl[0]); |
| |
| /* would be a good idea to check this call? */ |
| if (seekread(media, (char *)ipl, B32(f.ipl_size), B32(f.ipl_addr)) == -1) |
| error(6, "read failed"); |
| |
| /* calculate xsum */ |
| xsum = checksum(ipl, B32(f.ipl_size)); |
| |
| if (xsum != 0) |
| { |
| fprintf(stderr, "calculated xsum 0x%x got 0x%x\n", |
| -xsum, ipl[ipl_words - 1]); |
| xsum -= B32(ipl[ipl_words - 1]); |
| fprintf(stderr, "calculated xsum 0x%x got 0x%x\n", |
| -xsum, ipl[ipl_words - 1]); |
| exit(2); |
| } |
| |
| printf("ipl: addr %d size %d entry 0x%x\n", |
| B32(f.ipl_addr), |
| B32(f.ipl_size), |
| B32(f.ipl_entry)); |
| |
| if (strncmp(f.palomagic, PALOMAGIC, 4) != 0) |
| { |
| error(6, "bad magic"); |
| } |
| |
| if (f.version != PALOHDRVERSION) |
| { |
| fprintf(stderr, "Warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" |
| "Boot loader header version is %d, I only know how\n" |
| "to handle version %d. It MIGHT work anyway.\n", |
| f.version, PALOHDRVERSION); |
| } |
| |
| #if 1 |
| printf(" ko 0x%x ksz %d knsz %d k64o 0x%x k64sz %d k64nsz %d rdo %d rdsz %d\n<%s>\n", |
| B32(f.kern32_offset), B32(f.kern32_sz), B32(f.kern32_native_sz), |
| B32(f.kern64_offset), B32(f.kern64_sz), B32(f.kern64_native_sz), |
| B32(f.rd_offset), B32(f.rd_sz), |
| f.cmdline[0] ? f.cmdline : f.cmdline_old); |
| #endif |
| |
| return 1; |
| } |
| |
| |
| unsigned |
| write_bootloader(int media, int bootloader, |
| unsigned where, |
| unsigned f0end, |
| struct firstblock *f) |
| { |
| size_t rblsize; /* sector-rounded boot loader size */ |
| struct loadable loadable; |
| int xsum1; |
| void *blimage; |
| int r; |
| int wide; |
| |
| /* We want prepare_ to fail, otherwise we've been given the output */ |
| /* of the linker instead of mkbootable */ |
| r = prepare_loadable(bootloader, &loadable, &wide); |
| if (r) |
| error(12); |
| |
| /* make sure max size boot loader would fit */ |
| if (where + MAXBLSIZE > f0end) { |
| printf("where %d, where+MAX=%d f0end=%d\n", |
| where, where + MAXBLSIZE, f0end); |
| error(9); |
| } |
| /* load the boot loader into RAM */ |
| rblsize = fsize(bootloader); |
| assert((rblsize % FW_BLOCKSIZE) == 0); |
| blimage = (void *)calloc(1, rblsize); |
| assert(blimage != NULL); |
| if ((r = seekread(bootloader, blimage, rblsize, 0)) != rblsize) |
| error(13); |
| |
| /* Is it really a boot loader? */ |
| #define BLWORD1 0xe8000032 /* palo 2.00 and above */ |
| if (BLWORD1 != __be32_to_cpu(*(unsigned *)blimage)) |
| { |
| fprintf(stderr, "ERROR: first word of boot loader was 0x%08x," |
| " expected 0x%08x\n", __be32_to_cpu(*(unsigned *)blimage), |
| BLWORD1); |
| exit(2); |
| } |
| |
| /* write it out */ |
| seekwrite(media, blimage, rblsize, where); |
| close(bootloader); |
| |
| f->ipl_addr = __cpu_to_be32(where); |
| f->ipl_size = __cpu_to_be32(rblsize); |
| /* entry point relative to where IPL is loaded in RAM by firmware */ |
| f->ipl_entry = __cpu_to_be32(loadable.entry - loadable.first); |
| |
| STRUCTWRITE(media, *f, 0); |
| |
| check_bootloader(media, __LINE__); |
| |
| free(blimage); |
| return where + rblsize; |
| } |
| |
| unsigned |
| write_kernel32(int media, int kernel, unsigned where, unsigned end, |
| struct firstblock *f, int native_size) |
| { |
| /* always allow max size boot loader */ |
| f->kern32_offset = __cpu_to_be32(where); |
| f->kern32_sz = __cpu_to_be32(fsize(kernel)); |
| f->kern32_native_sz = __cpu_to_be32(native_size); |
| |
| if (B32(f->kern32_sz) + where >= end) |
| error(14, "32-bit-kernel"); |
| lseek(media, where, SEEK_SET); |
| return where + cat(media, kernel); |
| } |
| |
| unsigned |
| write_kernel64(int media, int kernel, unsigned where, unsigned end, |
| struct firstblock *f, int native_size) |
| { |
| /* always allow max size boot loader */ |
| f->kern64_offset = __cpu_to_be32(where); |
| f->kern64_sz = __cpu_to_be32(fsize(kernel)); |
| f->kern64_native_sz = __cpu_to_be32(native_size); |
| |
| if (B32(f->kern64_sz) + where >= end) |
| error(14, "64-bit-kernel"); |
| lseek(media, where, SEEK_SET); |
| return where + cat(media, kernel); |
| } |
| |
| unsigned |
| write_ramdisk(int media, int rd, unsigned where, unsigned end, |
| struct firstblock *f) |
| { |
| /* always allow max size boot loader */ |
| f->rd_offset = __cpu_to_be32(where); |
| f->rd_sz = __cpu_to_be32(fsize(rd)); |
| |
| if (B32(f->rd_sz) + where >= end) |
| error(14, "ramdisk"); |
| |
| lseek(media, where, SEEK_SET); |
| where += cat(media, rd); |
| |
| return where; |
| } |
| |
| /* assumes the previous contents of the file are to be overwritten, */ |
| /* that kernel and bootloader are valid file pointers, and that */ |
| /* commandline will fit given size in fixed_pm. */ |
| void |
| do_sequential(int media, int kernel32, int kernel64, |
| const char *commandline, int bootloader, int ramdisk, |
| int kern32_nsz, int kern64_nsz) |
| { |
| struct firstblock f; |
| /* always load IPL at second 2k block on sequential media */ |
| int where = 1 * FW_BLOCKSIZE; |
| unsigned end = 800 * 1024 * 1024; |
| |
| /* must have a boot loader for initialization */ |
| assert(bootloader != -1); |
| assert(kernel32 != -1 || kernel64 != 1); |
| |
| memset(&f, 0, sizeof f); |
| fb_init(&f); |
| |
| /* Update the boot loader, ignore size in this case */ |
| where = write_bootloader(media, bootloader, where, end, &f); |
| |
| if (kernel32 != -1) |
| where = write_kernel32(media, kernel32, where, end, &f, kern32_nsz); |
| |
| /* write some padding to the 2k boundary */ |
| where += write(media, &f, FW_BLOCKSIZE - (where % FW_BLOCKSIZE)); |
| |
| if (kernel64 != -1) |
| where = write_kernel64(media, kernel64, where, end, &f, kern64_nsz); |
| |
| /* write some padding to the 2k boundary */ |
| where += write(media, &f, FW_BLOCKSIZE - (where % FW_BLOCKSIZE)); |
| |
| if (ramdisk != -1) |
| where = write_ramdisk(media, ramdisk, where, end, &f); |
| |
| /* write some padding to the 2k boundary */ |
| where += write(media, &f, FW_BLOCKSIZE - (where % FW_BLOCKSIZE)); |
| |
| if (commandline != 0) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| STRUCTWRITE(media, f, 0); |
| |
| check_bootloader(media, __LINE__); |
| } |
| |
| /* look on each 2k boundary for the file 'target' on 'media'. Assumes |
| * that 'target' is at least 2k in length. |
| */ |
| static int |
| lookfor(int media, int target) |
| { |
| char *targetptr; |
| char *mbuf; |
| int offset; |
| int r = -1; |
| int targetsize = fsize(target); |
| float mediasize = fsize(media); |
| |
| targetptr = mmap(0, targetsize, PROT_READ, MAP_SHARED, target, 0); |
| if ((long) targetptr == -1) |
| { |
| perror("mmap(targetptr)"); |
| return -1; |
| } |
| |
| mbuf = malloc(targetsize); |
| assert(mbuf != NULL); |
| |
| for (offset = 0; 1; offset += 2048) |
| { |
| int n; |
| const int PREVIEW = 512; |
| |
| n = seekread(media, mbuf, PREVIEW, offset); |
| if (n < PREVIEW) |
| break; |
| if (memcmp(mbuf, targetptr, PREVIEW) == 0) |
| { |
| n = seekread(media, mbuf, targetsize, offset); |
| if (n < targetsize) |
| break; |
| if (memcmp(mbuf, targetptr, targetsize) == 0) |
| { |
| r = offset; |
| break; |
| } |
| } |
| } |
| |
| munmap(targetptr, targetsize); |
| free(mbuf); |
| |
| return r; |
| } |
| |
| /* Given a writeable ISO9660 CD image containing within its file system |
| * both the boot loader and kernel specified also on the palo command line, |
| * write a "sequential" or "F0" type boot header. |
| */ |
| void |
| do_cdrom(int media, int kernel32, int kernel64, |
| const char *commandline, int bootloader, int ramdisk, |
| int kern32_nsz, int kern64_nsz) |
| { |
| struct firstblock f; |
| /* always load IPL at second 2k block on sequential media */ |
| int where = 1 * FW_BLOCKSIZE; |
| unsigned end = 800 * 1024 * 1024; |
| /* int is safe since CD-ROM is < 2G */ |
| int bootloader_offset; |
| int kernel32_offset; |
| int kernel64_offset; |
| int ramdisk_offset; |
| |
| /* must have a boot loader and kernel */ |
| assert(bootloader != -1); |
| assert(kernel32 != -1 || kernel64 != 1); |
| |
| /* read the first block/sector, which is FW_BLOCKSIZE, which also */ |
| /* conveniently is the CD block size too */ |
| /* STRUCTREAD(media, f, 0); Probably don't need to read it... */ |
| memset(&f, 0, sizeof f); |
| fb_init(&f); |
| |
| /* search the ISO image for the boot loader and kernel files */ |
| if ((bootloader_offset = lookfor(media, bootloader)) == -1) |
| { |
| error(15, "boot loader"); |
| } |
| |
| if (kernel32 != -1 && (kernel32_offset = lookfor(media, kernel32)) == -1) |
| error(15, "32-bit-kernel image"); |
| |
| if (kernel64 != -1 && (kernel64_offset = lookfor(media, kernel64)) == -1) |
| error(15, "64-bit-kernel image"); |
| |
| if (ramdisk != -1) |
| { |
| if ((ramdisk_offset = lookfor(media, ramdisk)) == -1) |
| error(15, "ramdisk image"); |
| } |
| |
| /* Overwrite the boot loader because write_bootloader() is easy to use */ |
| write_bootloader(media, bootloader, bootloader_offset, end, &f); |
| |
| /* Overwrite the kernel because write_kernel() is easy to use */ |
| if (kernel32 != -1) |
| write_kernel32(media, kernel32, kernel32_offset, end, &f, kern32_nsz); |
| |
| if (kernel64 != -1) |
| write_kernel64(media, kernel64, kernel64_offset, end, &f, kern64_nsz); |
| |
| if (ramdisk != -1) |
| { |
| write_ramdisk(media, ramdisk, ramdisk_offset, end, &f); |
| } |
| |
| if (commandline != 0) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| STRUCTWRITE(media, f, 0); |
| |
| check_bootloader(media, __LINE__); |
| } |
| |
| #define KSIZE (2 * 1024 * 1024) |
| |
| /* Blocksize for the ext2/3 fs in the palo partition */ |
| #define EXT2_BLOCKSIZE 1024 |
| |
| /* size in ext2 blocks of hole for bootloader */ |
| #define EXT2_HOLE ((MAXBLSIZE + 1) / EXT2_BLOCKSIZE) |
| |
| /* offset in bytes before start of hole, ext2 doesn't allow holes at |
| * to cover the first four blocks of the filesystem |
| * |
| * Note: modern ext2/3 has a resize_inode covering blocks 3-258 so you |
| * must either always include the -O^resize_inode when creating the |
| * filesystem or define EXT2_OFFSET to (259*EXT2_BLOCKSIZE)*/ |
| #define EXT2_OFFSET (4*EXT2_BLOCKSIZE) |
| |
| int |
| do_at_start(int init, int media, int start, |
| int bootloaderfd, const char *commandline) |
| { |
| struct firstblock f; |
| |
| STRUCTREAD(media, f, 0); |
| |
| if (init) { |
| fb_init(&f); |
| } else if (f.ipl_addr != start) { |
| printf("Current IPL start not consistent with disklabel, use -I to reinitialise\n"); |
| error(11); |
| } |
| |
| if(commandline) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| write_bootloader(media, bootloaderfd, start, |
| start + MAXBLSIZE, &f); |
| |
| STRUCTWRITE(media, f, 0); |
| } |
| |
| int |
| do_formatted(int init, int media, const char *medianame, int partition, |
| int f0start, int f0length, int bootloaderfd, int do_format, |
| const char *commandline) |
| { |
| char partitionname[256]; |
| struct firstblock f; |
| int partitionfd; |
| |
| /* FIXME: assuming we can simply add the partition to the end |
| * of the whole disc device isn't always true */ |
| snprintf(partitionname, sizeof(partitionname), "%s%d", medianame, |
| partition); |
| |
| if ((partitionfd = open(partitionname, O_RDWR)) < 0) { |
| perror(partitionname); |
| exit(1); |
| } |
| close(partitionfd); |
| |
| if (init) { |
| /* make the bootloader align at 256k */ |
| unsigned int holestart = (f0start + 0x3ffff + EXT2_OFFSET) & ~0x3ffff; |
| unsigned int partition_offset = holestart - f0start; |
| char badblockfilename[256]; |
| int fd, i; |
| char cmd[512]; |
| |
| printf("Initializing %s as ext%d\n", partitionname, do_format); |
| |
| if (verbose) |
| printf("f0 partition starts %d, hole %d-%d, end %d\n", |
| f0start, holestart, holestart + EXT2_HOLE*EXT2_BLOCKSIZE, |
| f0start + f0length); |
| |
| if(partition_offset + EXT2_HOLE > f0length) |
| error(14, "bootloader"); |
| |
| sprintf(badblockfilename, "/tmp/paloblk-%d", getpid()); |
| if ((fd = open(badblockfilename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) { |
| perror(badblockfilename); |
| exit(1); |
| } |
| |
| for (i = (holestart - f0start)/EXT2_BLOCKSIZE; |
| i < (holestart - f0start)/EXT2_BLOCKSIZE + EXT2_HOLE; i++) { |
| char buf[128]; |
| int len; |
| sprintf(buf, "%d\n", i); |
| len = strlen(buf); |
| if (len != write(fd, buf, len)) { |
| perror("Error writing badblock file"); |
| exit(1); |
| } |
| } |
| |
| sprintf(cmd, "mke2fs -t ext%d -O^resize_inode -b %d -l %s %s", |
| do_format, EXT2_BLOCKSIZE, badblockfilename, partitionname); |
| |
| if (verbose) |
| printf("Executing: %s\n", cmd); |
| else |
| strcat(cmd, " > /dev/null 2>&1"); |
| |
| i = system(cmd); |
| unlink(badblockfilename); |
| |
| if(WEXITSTATUS(i) != 0) |
| error(19, WEXITSTATUS(i)); |
| |
| |
| |
| STRUCTREAD(media, f, 0); |
| fb_init(&f); |
| f.flags |= PFLAG_EXT2; |
| |
| if(commandline) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| write_bootloader(media, bootloaderfd, holestart, |
| holestart + EXT2_HOLE*EXT2_BLOCKSIZE, &f); |
| |
| STRUCTWRITE(media, f, 0); |
| |
| } else { |
| STRUCTREAD(media, f, 0); |
| |
| if(verbose) |
| printf("Updating formatted ver=%d, start=%d\n", |
| f.version, f.ipl_addr); |
| |
| if ((f.version < 4) || (!f.flags & PFLAG_EXT2)) { |
| printf("Can not update %s. Please initialize first.\n", |
| partitionname); |
| return -1; |
| } |
| |
| if(commandline) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| write_bootloader(media, bootloaderfd, f.ipl_addr, |
| f.ipl_addr + EXT2_HOLE*EXT2_BLOCKSIZE, &f); |
| |
| STRUCTWRITE(media, f, 0); |
| } |
| return 0; |
| } |
| |
| void |
| do_randomaccess(int init, int media, int kernel32, int kernel64, |
| const char *commandline, int bootloader, int ramdisk, |
| unsigned f0start, unsigned f0length, int kern32_nsz, int kern64_nsz) |
| { |
| struct firstblock f; |
| int bstart = 0; |
| |
| if (verbose) printf("do_ra(%d, %d, %d, %d, '%s', %d, %d, %u, %u)\n", |
| init, media, kernel32, kernel64, commandline, bootloader, ramdisk, f0start, |
| f0length); |
| |
| assert((f0start % 512) == 0); |
| |
| /* figure out where the boot loader should be */ |
| /* f0start is a sector (512-byte) multiple. IPL must be aligned 2k */ |
| bstart = (f0start + 2047) & ~2047; |
| |
| if (init) |
| { |
| /* initialize a partitioned medium */ |
| unsigned where = bstart; |
| unsigned end = f0start + f0length; |
| |
| /* must have a boot loader for initialization */ |
| assert(bootloader != -1); |
| |
| STRUCTREAD(media, f, 0); |
| fb_init(&f); |
| |
| /* Update the boot loader, ignore size in this case */ |
| write_bootloader(media, bootloader, where, end, &f); |
| |
| where += MAXBLSIZE; |
| |
| if (kernel32 != -1) |
| { |
| unsigned p, ksize; |
| p = write_kernel32(media, kernel32, where, end, &f, kern32_nsz); |
| |
| ksize = p - where; |
| |
| /* allow for bigger kernels */ |
| if (ksize < KSIZE) |
| ksize = KSIZE; |
| |
| where = p; |
| } |
| |
| if (kernel64 != -1) |
| { |
| unsigned p, ksize; |
| p = write_kernel64(media, kernel64, where, end, &f, kern64_nsz); |
| |
| ksize = p - where; |
| |
| /* allow for bigger kernels */ |
| if (ksize < KSIZE) |
| ksize = KSIZE; |
| |
| where = p; |
| } |
| |
| if (ramdisk != -1) |
| where = write_ramdisk(media, ramdisk, where, end, &f); |
| |
| if (commandline != 0) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| STRUCTWRITE(media, f, 0); |
| } |
| else /* update */ |
| { |
| unsigned end = f0start + f0length; |
| |
| fprintf(stderr, "palo -U on unformatted (non-ext2) palo partitions doesn't work yet\n"); exit(2); |
| #if 0 |
| |
| /* make sure we have a good bootloader on there now */ |
| check_bootloader(media, __LINE__); |
| |
| /* update a partitioned medium */ |
| STRUCTREAD(media, f, 0); |
| |
| if (bootloader != -1) |
| { |
| write_bootloader(media, bootloader, bstart, B32(f.kern_offset), &f); |
| } |
| |
| if (kernel != -1) |
| { |
| write_kernel(media, kernel, B32(f.kern_offset), |
| B32(f.rd_offset), &f); |
| } |
| |
| if (kernel != -1) |
| { |
| write_kernel(media, kernel, B32(f.kern_offset), |
| B32(f.rd_offset), &f); |
| } |
| |
| if (ramdisk != -1) |
| { |
| write_ramdisk(media, ramdisk, B32(f.rd_offset), end, &f); |
| } |
| |
| if (commandline != 0) |
| strncpy(f.cmdline, commandline, sizeof(f.cmdline)-1); |
| |
| STRUCTWRITE(media, f, 0); |
| #endif |
| } |
| |
| check_bootloader(media, __LINE__); |
| } |
| |
| #define MAXARGS 200 |
| |
| #if LONG_OPTIONS |
| static struct option Longopts[] = |
| { |
| {"configfile", 1, 0, 'f'}, |
| {"commandline", 1, 0, 'c'}, |
| {"recoverykernel", 1, 0, 'k'}, |
| {"bootloader", 1, 0, 'b'}, |
| {"ramdisk", 1, 0, 'r'}, |
| {"init-partitioned", 1, 0, 'I'}, |
| {"format-as", 1, 0, 'e'}, |
| {"update-partitioned", 1, 0, 'U'}, |
| {"init-tape", 1, 0, 's'}, |
| {"init-cdrom", 1, 0, 'C'}, |
| {"verbose", 0, 0, 'v'}, |
| {"help", 0, 0, '?'}, |
| {"version", 0, 0, 'V'}, |
| {0, 0, 0, 0} |
| }; |
| # define GETOPT(argc, argv, optstring) \ |
| getopt_long(argc, argv, optstring, Longopts, 0) |
| #else |
| # define GETOPT(argc, argv, optstring) getopt(argc, argv, optstring) |
| #endif |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| int c; |
| enum {SEQUENTIAL, PARTITIONED, CDROM} mediatype = PARTITIONED; |
| int bootloader = -1; |
| int kernel32 = -1; |
| int kernel64 = -1; |
| int kern32_nsz = 0, kern64_nsz = 0; |
| int fd; |
| struct loadable loadable; |
| int wide; |
| char *commandline = 0; |
| int media = -1; |
| int ramdisk = -1; |
| int cdrom = 0; |
| char *medianame = NULL; |
| char *bootloaderfile = "/usr/share/palo/iplboot"; |
| int init = 0; |
| extern const char bld_info[]; |
| const char gargs[] = "f:C:s:b:k:c:r:I:e:U:v?V"; |
| char *config_file = "/etc/palo.conf"; |
| char *newargv[MAXARGS]; |
| int newargc, format_as = 0; |
| FILE *fconfig; |
| |
| assert(sizeof (struct firstblock) == 2048); |
| { |
| struct firstblock f; |
| assert((unsigned long)&f.kern32_offset - (unsigned long)&f == 8); |
| assert((unsigned long)&f.ipl_addr - (unsigned long)&f == 0xf0); |
| } |
| |
| fputs("palo " PALOVERSION " - boot media management tool for PA-RISC/HPPA.\n", stdout); |
| |
| /* do we have a -f? */ |
| while ((c = GETOPT(argc, argv, gargs)) != EOF) |
| { |
| switch(c) |
| { |
| case 'f': |
| config_file = optarg; |
| break; |
| case '?': |
| error(0, argv[0]); |
| break; |
| case 'V': |
| fputs("palo version " PALOVERSION "\n", stdout); |
| puts(bld_info); |
| return 0; |
| } |
| } |
| |
| newargc = 0; |
| newargv[newargc++] = argv[0]; |
| /* grab args from the config file */ |
| if ((fconfig = fopen(config_file, "r")) == NULL) |
| { |
| fprintf(stderr, "Warning: "); |
| perror(config_file); |
| } |
| else |
| { |
| char buf[256]; |
| |
| while(newargc < MAXARGS && fgets(buf, sizeof buf, fconfig) != NULL) |
| { |
| char *ptr = buf; |
| char *end; |
| |
| /* skip over leading whitespace */ |
| while (isspace(*ptr)) |
| ptr++; |
| |
| /* skip comment */ |
| if (*ptr == '#') |
| continue; |
| |
| /* strip trailing whitespace */ |
| end = ptr + strlen(ptr) - 1; |
| while (isspace(*end)) |
| end--; |
| *++end = '\0'; |
| |
| /* skip blank lines */ |
| if (*ptr == '\0') |
| continue; |
| |
| newargv[newargc++] = strdup(buf); |
| } |
| fclose(fconfig); |
| } |
| |
| /* add the original command-line args */ |
| for (c = 1; c < argc && newargc < MAXARGS; c++) |
| { |
| newargv[newargc++] = argv[c]; |
| } |
| |
| /* NULL pointer for good measure */ |
| if (newargc < MAXARGS) |
| newargv[newargc] = 0; |
| |
| /* convince getopt to re-start */ |
| optind = 1; |
| |
| while ((c = GETOPT(newargc, newargv, gargs)) != EOF) |
| { |
| switch(c) |
| { |
| case 'f': |
| break; |
| case 'C': |
| mediatype = CDROM; |
| medianame = optarg; |
| break; |
| case 'I': |
| init = 1; |
| mediatype = PARTITIONED; |
| medianame = optarg; |
| break; |
| case 'U': |
| init = 0; |
| mediatype = PARTITIONED; |
| medianame = optarg; |
| break; |
| case 's': |
| mediatype = SEQUENTIAL; |
| medianame = optarg; |
| break; |
| case 'b': |
| bootloaderfile = optarg; |
| break; |
| case 'k': |
| if ((fd = open(optarg, O_RDONLY)) == -1) |
| { |
| perror(optarg); |
| return 2; |
| } |
| if (!prepare_loadable(fd, &loadable, &wide)) |
| error(16, optarg); |
| if ((wide && kernel64 != -1) || (!wide && kernel32 != -1)) |
| error(17, optarg); |
| lseek(fd, 0, SEEK_SET); /* rewind */ |
| if (wide) { |
| kernel64 = fd; |
| kern64_nsz = loadable.uncompressed_size; |
| } else { |
| kernel32 = fd; |
| kern32_nsz = loadable.uncompressed_size; |
| } |
| break; |
| case 'r': |
| if ((ramdisk = open(optarg, O_RDONLY)) == -1) |
| { |
| perror(optarg); |
| return 2; |
| } |
| break; |
| case 'c': |
| commandline = optarg; |
| if (strlen(commandline) >= CMDLINELEN) |
| error(3,CMDLINELEN-1,strlen(commandline)); |
| break; |
| case 'e': |
| if(strcmp(optarg, "2") == 0) |
| format_as = 2; |
| else if(strcmp(optarg, "3") == 0) |
| format_as = 3; |
| else if (strcmp(optarg, "4") == 0) |
| format_as = 4; |
| else |
| error(0, argv[0]); |
| break; |
| case 'v': |
| verbose = 1; |
| break; |
| default: |
| error(0, argv[0]); |
| break; |
| } |
| } |
| |
| if ((bootloader = open(bootloaderfile, O_RDONLY)) == -1) |
| { |
| perror(bootloaderfile); |
| return 2; |
| } |
| |
| if (optind < argc) |
| { |
| error(0, argv[0]); |
| } |
| |
| if (medianame == NULL) |
| { |
| fprintf(stderr, "%s: Need one of -CIs or -U\n", argv[0]); |
| error(0, argv[0]); |
| } |
| |
| switch (mediatype) |
| { |
| case SEQUENTIAL: |
| if ((media = open(medianame, O_RDWR|O_CREAT|O_TRUNC, 0666)) == -1) |
| { |
| perror(medianame); |
| return 2; |
| } |
| do_sequential(media, kernel32, kernel64, commandline, bootloader, |
| ramdisk, kern32_nsz, kern64_nsz); |
| break; |
| case PARTITIONED: |
| { |
| struct diskpartition ptab[MAXPARTS]; |
| int partitioned; |
| int f0; |
| struct firstblock f; |
| int at_start = 0; /* place ipl before first partition */ |
| |
| memset(ptab, 0, sizeof ptab); |
| |
| /* non-sequential boot creation requires a file/device */ |
| if ((media = open(medianame, O_RDWR)) == -1) |
| { |
| perror(medianame); |
| if (errno == ENOENT) /* file probably doesn't exist */ |
| { |
| error(1); |
| } |
| return 2; |
| } |
| |
| /* it better have a partition table too... */ |
| partitioned = load_partitions(media, ptab, MAXPARTS); |
| if (!partitioned) |
| { |
| error(10); |
| } |
| |
| /* find the F0 partition */ |
| for (f0 = 0; f0 < MAXPARTS; f0++) |
| { |
| if (ptab[f0].id == PALO_PARTITION) |
| break; |
| } |
| if (f0 == MAXPARTS) { |
| /* is the partition layout sufficient to support |
| * iplboot before the first parition. Allow 64 |
| * sectors for the gpt label (like we'll ever support |
| * gpt, but just in case) allow for 8 extra sectors to |
| * align the iplboot */ |
| if (ptab[0].start > 64 + MAXBLSIZE/512 + 8) |
| at_start = (ptab[0].start * 512 - MAXBLSIZE) & ~0xfff; |
| else |
| error(11); |
| } |
| if(verbose) { |
| print_ptab_pretty(ptab, MAXPARTS); |
| |
| if (at_start) |
| printf("Placing bootloader within disk label at %d\n", |
| at_start/512); |
| else |
| printf("F0 partition start sector %d length %d\n", |
| ptab[f0].start, ptab[f0].length); |
| } |
| |
| if (at_start) { |
| if (format_as) |
| printf("No F0(palo) partition found, placing iplboot inside label\n"); |
| |
| do_at_start(init, media, at_start, bootloader, commandline); |
| } else if (format_as) { |
| /* if we're going to be a formatted partition, we can't |
| * load anything into it, so check we haven't been asked |
| * to */ |
| if(kernel32 != -1 || kernel64 != -1 || ramdisk != -1) |
| error(18); |
| |
| if (do_formatted(init, media, medianame, f0 + 1, |
| ptab[f0].start * 512, ptab[f0].length * 512, |
| bootloader, format_as, commandline)) |
| error(20); |
| } else |
| do_randomaccess(init, media, kernel32, kernel64, commandline, |
| bootloader, ramdisk, ptab[f0].start * 512, |
| ptab[f0].length * 512, |
| kern32_nsz, kern64_nsz); |
| } |
| break; |
| case CDROM: |
| if ((media = open(medianame, O_RDWR)) == -1) |
| { |
| perror(medianame); |
| return 2; |
| } |
| do_cdrom(media, kernel32, kernel64, commandline, bootloader, ramdisk, |
| kern32_nsz, kern64_nsz); |
| break; |
| } |
| |
| fsync(media); |
| close(media); |
| |
| return 0; |
| } |