|  | /* | 
|  | * Makes a prep bootable image which can be dd'd onto | 
|  | * a disk device to make a bootdisk.  Will take | 
|  | * as input a elf executable, strip off the header | 
|  | * and write out a boot image as: | 
|  | * 1) default - strips elf header | 
|  | *      suitable as a network boot image | 
|  | * 2) -pbp - strips elf header and writes out prep boot partition image | 
|  | *      cat or dd onto disk for booting | 
|  | * 3) -asm - strips elf header and writes out as asm data | 
|  | *      useful for generating data for a compressed image | 
|  | *                  -- Cort | 
|  | * | 
|  | * Modified for x86 hosted builds by Matt Porter <porter@neta.com> | 
|  | * Modified for Sparc hosted builds by Peter Wahl <PeterWahl@web.de> | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | /* size of read buffer */ | 
|  | #define SIZE 0x1000 | 
|  |  | 
|  | /* | 
|  | * Partition table entry | 
|  | *  - from the PReP spec | 
|  | */ | 
|  | typedef struct partition_entry { | 
|  | unsigned char boot_indicator; | 
|  | unsigned char starting_head; | 
|  | unsigned char starting_sector; | 
|  | unsigned char starting_cylinder; | 
|  |  | 
|  | unsigned char system_indicator; | 
|  | unsigned char ending_head; | 
|  | unsigned char ending_sector; | 
|  | unsigned char ending_cylinder; | 
|  |  | 
|  | unsigned char beginning_sector[4]; | 
|  | unsigned char number_of_sectors[4]; | 
|  | } partition_entry_t; | 
|  |  | 
|  | #define BootActive	0x80 | 
|  | #define SystemPrep	0x41 | 
|  |  | 
|  | void copy_image(FILE *, FILE *); | 
|  | void write_prep_partition(FILE *, FILE *); | 
|  | void write_asm_data(FILE *, FILE *); | 
|  |  | 
|  | unsigned int elfhdr_size = 65536; | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | FILE *in, *out; | 
|  | int argptr = 1; | 
|  | int prep = 0; | 
|  | int asmoutput = 0; | 
|  |  | 
|  | if (argc < 3 || argc > 4) { | 
|  | fprintf(stderr, "usage: %s [-pbp] [-asm] <boot-file> <image>\n", | 
|  | argv[0]); | 
|  | exit(-1); | 
|  | } | 
|  |  | 
|  | /* needs to handle args more elegantly -- but this is a small/simple program */ | 
|  |  | 
|  | /* check for -pbp */ | 
|  | if (!strcmp(argv[argptr], "-pbp")) { | 
|  | prep = 1; | 
|  | argptr++; | 
|  | } | 
|  |  | 
|  | /* check for -asm */ | 
|  | if (!strcmp(argv[argptr], "-asm")) { | 
|  | asmoutput = 1; | 
|  | argptr++; | 
|  | } | 
|  |  | 
|  | /* input file */ | 
|  | if (!strcmp(argv[argptr], "-")) | 
|  | in = stdin; | 
|  | else if (!(in = fopen(argv[argptr], "r"))) | 
|  | exit(-1); | 
|  | argptr++; | 
|  |  | 
|  | /* output file */ | 
|  | if (!strcmp(argv[argptr], "-")) | 
|  | out = stdout; | 
|  | else if (!(out = fopen(argv[argptr], "w"))) | 
|  | exit(-1); | 
|  | argptr++; | 
|  |  | 
|  | /* skip elf header in input file */ | 
|  | /*if ( !prep )*/ | 
|  | fseek(in, elfhdr_size, SEEK_SET); | 
|  |  | 
|  | /* write prep partition if necessary */ | 
|  | if (prep) | 
|  | write_prep_partition(in, out); | 
|  |  | 
|  | /* write input image to bootimage */ | 
|  | if (asmoutput) | 
|  | write_asm_data(in, out); | 
|  | else | 
|  | copy_image(in, out); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void store_le32(unsigned int v, unsigned char *p) | 
|  | { | 
|  | p[0] = v; | 
|  | p[1] = v >>= 8; | 
|  | p[2] = v >>= 8; | 
|  | p[3] = v >> 8; | 
|  | } | 
|  |  | 
|  | void write_prep_partition(FILE *in, FILE *out) | 
|  | { | 
|  | unsigned char block[512]; | 
|  | partition_entry_t pe; | 
|  | unsigned char *entry  = block; | 
|  | unsigned char *length = block + 4; | 
|  | long pos = ftell(in), size; | 
|  |  | 
|  | if (fseek(in, 0, SEEK_END) < 0) { | 
|  | fprintf(stderr,"info failed\n"); | 
|  | exit(-1); | 
|  | } | 
|  | size = ftell(in); | 
|  | if (fseek(in, pos, SEEK_SET) < 0) { | 
|  | fprintf(stderr,"info failed\n"); | 
|  | exit(-1); | 
|  | } | 
|  |  | 
|  | memset(block, '\0', sizeof(block)); | 
|  |  | 
|  | /* set entry point and boot image size skipping over elf header */ | 
|  | store_le32(0x400/*+65536*/, entry); | 
|  | store_le32(size-elfhdr_size+0x400, length); | 
|  |  | 
|  | /* sets magic number for msdos partition (used by linux) */ | 
|  | block[510] = 0x55; | 
|  | block[511] = 0xAA; | 
|  |  | 
|  | /* | 
|  | * Build a "PReP" partition table entry in the boot record | 
|  | *  - "PReP" may only look at the system_indicator | 
|  | */ | 
|  | pe.boot_indicator   = BootActive; | 
|  | pe.system_indicator = SystemPrep; | 
|  | /* | 
|  | * The first block of the diskette is used by this "boot record" which | 
|  | * actually contains the partition table. (The first block of the | 
|  | * partition contains the boot image, but I digress...)  We'll set up | 
|  | * one partition on the diskette and it shall contain the rest of the | 
|  | * diskette. | 
|  | */ | 
|  | pe.starting_head     = 0;	/* zero-based			     */ | 
|  | pe.starting_sector   = 2;	/* one-based			     */ | 
|  | pe.starting_cylinder = 0;	/* zero-based			     */ | 
|  | pe.ending_head       = 1;	/* assumes two heads		     */ | 
|  | pe.ending_sector     = 18;	/* assumes 18 sectors/track	     */ | 
|  | pe.ending_cylinder   = 79;	/* assumes 80 cylinders/diskette     */ | 
|  |  | 
|  | /* | 
|  | * The "PReP" software ignores the above fields and just looks at | 
|  | * the next two. | 
|  | *   - size of the diskette is (assumed to be) | 
|  | *     (2 tracks/cylinder)(18 sectors/tracks)(80 cylinders/diskette) | 
|  | *   - unlike the above sector numbers, the beginning sector is zero-based! | 
|  | */ | 
|  | #if 0 | 
|  | store_le32(1, pe.beginning_sector); | 
|  | #else | 
|  | /* This has to be 0 on the PowerStack? */ | 
|  | store_le32(0, pe.beginning_sector); | 
|  | #endif | 
|  |  | 
|  | store_le32(2*18*80-1, pe.number_of_sectors); | 
|  |  | 
|  | memcpy(&block[0x1BE], &pe, sizeof(pe)); | 
|  |  | 
|  | fwrite(block, sizeof(block), 1, out); | 
|  | fwrite(entry, 4, 1, out); | 
|  | fwrite(length, 4, 1, out); | 
|  | /* set file position to 2nd sector where image will be written */ | 
|  | fseek( out, 0x400, SEEK_SET ); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | void copy_image(FILE *in, FILE *out) | 
|  | { | 
|  | char buf[SIZE]; | 
|  | int n; | 
|  |  | 
|  | while ( (n = fread(buf, 1, SIZE, in)) > 0 ) | 
|  | fwrite(buf, 1, n, out); | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | write_asm_data(FILE *in, FILE *out) | 
|  | { | 
|  | int i, cnt, pos = 0; | 
|  | unsigned int cksum = 0, val; | 
|  | unsigned char *lp; | 
|  | unsigned char buf[SIZE]; | 
|  | size_t len; | 
|  |  | 
|  | fputs("\t.data\n\t.globl input_data\ninput_data:\n", out); | 
|  | while ((len = fread(buf, 1, sizeof(buf), in)) > 0) { | 
|  | cnt = 0; | 
|  | lp = buf; | 
|  | /* Round up to longwords */ | 
|  | while (len & 3) | 
|  | buf[len++] = '\0'; | 
|  | for (i = 0;  i < len;  i += 4) { | 
|  | if (cnt == 0) | 
|  | fputs("\t.long\t", out); | 
|  | fprintf(out, "0x%02X%02X%02X%02X", | 
|  | lp[0], lp[1], lp[2], lp[3]); | 
|  | val = *(unsigned long *)lp; | 
|  | cksum ^= val; | 
|  | lp += 4; | 
|  | if (++cnt == 4) { | 
|  | cnt = 0; | 
|  | fprintf(out, " # %x \n", pos+i-12); | 
|  | } else { | 
|  | fputs(",", out); | 
|  | } | 
|  | } | 
|  | if (cnt) | 
|  | fputs("0\n", out); | 
|  | pos += len; | 
|  | } | 
|  | fprintf(out, "\t.globl input_len\ninput_len:\t.long\t0x%x\n", pos); | 
|  | fprintf(stderr, "cksum = %x\n", cksum); | 
|  | } |