linux/arch/sparc/boot/piggyback.c
<<
>>
Prefs
   1/*
   2   Simple utility to make a single-image install kernel with initial ramdisk
   3   for Sparc tftpbooting without need to set up nfs.
   4
   5   Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
   6   Pete Zaitcev <zaitcev@yahoo.com> endian fixes for cross-compiles, 2000.
   7   Copyright (C) 2011 Sam Ravnborg <sam@ravnborg.org>
   8
   9   This program is free software; you can redistribute it and/or modify
  10   it under the terms of the GNU General Public License as published by
  11   the Free Software Foundation; either version 2 of the License, or
  12   (at your option) any later version.
  13
  14   This program is distributed in the hope that it will be useful,
  15   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17   GNU General Public License for more details.
  18
  19   You should have received a copy of the GNU General Public License
  20   along with this program; if not, write to the Free Software
  21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  22
  23#include <dirent.h>
  24#include <stdlib.h>
  25#include <string.h>
  26#include <unistd.h>
  27#include <ctype.h>
  28#include <errno.h>
  29#include <fcntl.h>
  30#include <stdio.h>
  31
  32#include <sys/types.h>
  33#include <sys/stat.h>
  34
  35/*
  36 * Note: run this on an a.out kernel (use elftoaout for it),
  37 * as PROM looks for a.out image only.
  38 */
  39
  40#define AOUT_TEXT_OFFSET   32
  41
  42static int is64bit = 0;
  43
  44/* align to power-of-two size */
  45static int align(int n)
  46{
  47        if (is64bit)
  48                return (n + 0x1fff) & ~0x1fff;
  49        else
  50                return (n + 0xfff) & ~0xfff;
  51}
  52
  53/* read two bytes as big endian */
  54static unsigned short ld2(char *p)
  55{
  56        return (p[0] << 8) | p[1];
  57}
  58
  59/* save 4 bytes as big endian */
  60static void st4(char *p, unsigned int x)
  61{
  62        p[0] = x >> 24;
  63        p[1] = x >> 16;
  64        p[2] = x >> 8;
  65        p[3] = x;
  66}
  67
  68static void die(const char *str)
  69{
  70        perror(str);
  71        exit(1);
  72}
  73
  74static void usage(void)
  75{
  76        /* fs_img.gz is an image of initial ramdisk. */
  77        fprintf(stderr, "Usage: piggyback bits vmlinux.aout System.map fs_img.gz\n");
  78        fprintf(stderr, "\tKernel image will be modified in place.\n");
  79        exit(1);
  80}
  81
  82static int start_line(const char *line)
  83{
  84        if (strcmp(line + 10, " _start\n") == 0)
  85                return 1;
  86        else if (strcmp(line + 18, " _start\n") == 0)
  87                return 1;
  88        return 0;
  89}
  90
  91static int end_line(const char *line)
  92{
  93        if (strcmp(line + 10, " _end\n") == 0)
  94                return 1;
  95        else if (strcmp (line + 18, " _end\n") == 0)
  96                return 1;
  97        return 0;
  98}
  99
 100/*
 101 * Find address for start and end in System.map.
 102 * The file looks like this:
 103 * f0004000 ... _start
 104 * f0379f79 ... _end
 105 * 1234567890123456
 106 * ^coloumn 1
 107 * There is support for 64 bit addresses too.
 108 *
 109 * Return 0 if either start or end is not found
 110 */
 111static int get_start_end(const char *filename, unsigned int *start,
 112                                               unsigned int *end)
 113{
 114        FILE *map;
 115        char buffer[1024];
 116
 117        *start = 0;
 118        *end = 0;
 119        map = fopen(filename, "r");
 120        if (!map)
 121                die(filename);
 122        while (fgets(buffer, 1024, map)) {
 123                if (start_line(buffer))
 124                        *start = strtoul(buffer, NULL, 16);
 125                else if (end_line(buffer))
 126                        *end = strtoul(buffer, NULL, 16);
 127        }
 128        fclose (map);
 129
 130        if (*start == 0 || *end == 0)
 131                return 0;
 132
 133        return 1;
 134}
 135
 136#define LOOKBACK (128 * 4)
 137#define BUFSIZE 1024
 138/*
 139 * Find the HdrS entry from head_32/head_64.
 140 * We check if it is at the beginning of the file (sparc64 case)
 141 * and if not we search for it.
 142 * When we search do so in steps of 4 as HdrS is on a 4-byte aligned
 143 * address (it is on same alignment as sparc instructions)
 144 * Return the offset to the HdrS entry (as off_t)
 145 */
 146static off_t get_hdrs_offset(int kernelfd, const char *filename)
 147{
 148        char buffer[BUFSIZE];
 149        off_t offset;
 150        int i;
 151
 152        if (lseek(kernelfd, 0, SEEK_SET) < 0)
 153                die("lseek");
 154        if (read(kernelfd, buffer, BUFSIZE) != BUFSIZE)
 155                die(filename);
 156
 157        if (buffer[40] == 'H' && buffer[41] == 'd' &&
 158            buffer[42] == 'r' && buffer[43] == 'S') {
 159                return 40;
 160        } else {
 161                /*  Find the gokernel label */
 162                /* Decode offset from branch instruction */
 163                offset = ld2(buffer + AOUT_TEXT_OFFSET + 2) << 2;
 164                /* Go back 512 bytes so we do not miss HdrS */
 165                offset -= LOOKBACK;
 166                /* skip a.out header */
 167                offset += AOUT_TEXT_OFFSET;
 168                if (lseek(kernelfd, offset, SEEK_SET) < 0)
 169                        die("lseek");
 170                if (read(kernelfd, buffer, BUFSIZE) != BUFSIZE)
 171                        die(filename);
 172
 173                for (i = 0; i < LOOKBACK; i += 4) {
 174                        if (buffer[i + 0] == 'H' && buffer[i + 1] == 'd' &&
 175                            buffer[i + 2] == 'r' && buffer[i + 3] == 'S') {
 176                                return offset + i;
 177                        }
 178                }
 179        }
 180        fprintf (stderr, "Couldn't find headers signature in %s\n", filename);
 181        exit(1);
 182}
 183
 184int main(int argc,char **argv)
 185{
 186        static char aout_magic[] = { 0x01, 0x03, 0x01, 0x07 };
 187        char buffer[1024];
 188        unsigned int i, start, end;
 189        off_t offset;
 190        struct stat s;
 191        int image, tail;
 192
 193        if (argc != 5)
 194                usage();
 195        if (strcmp(argv[1], "64") == 0)
 196                is64bit = 1;
 197        if (stat (argv[4], &s) < 0)
 198                die(argv[4]);
 199
 200        if (!get_start_end(argv[3], &start, &end)) {
 201                fprintf(stderr, "Could not determine start and end from %s\n",
 202                        argv[3]);
 203                exit(1);
 204        }
 205        if ((image = open(argv[2], O_RDWR)) < 0)
 206                die(argv[2]);
 207        if (read(image, buffer, 512) != 512)
 208                die(argv[2]);
 209        if (memcmp(buffer, aout_magic, 4) != 0) {
 210                fprintf (stderr, "Not a.out. Don't blame me.\n");
 211                exit(1);
 212        }
 213        /*
 214         * We need to fill in values for
 215         * sparc_ramdisk_image + sparc_ramdisk_size
 216         * To locate these symbols search for the "HdrS" text which appear
 217         * in the image a little before the gokernel symbol.
 218         * See definition of these in init_32.S
 219         */
 220
 221        offset = get_hdrs_offset(image, argv[2]);
 222        /* skip HdrS + LINUX_VERSION_CODE + HdrS version */
 223        offset += 10;
 224
 225        if (lseek(image, offset, 0) < 0)
 226                die("lseek");
 227
 228        /*
 229         * root_flags = 0
 230         * root_dev = 1 (RAMDISK_MAJOR)
 231         * ram_flags = 0
 232         * sparc_ramdisk_image = "PAGE aligned address after _end")
 233         * sparc_ramdisk_size = size of image
 234         */
 235        st4(buffer, 0);
 236        st4(buffer + 4, 0x01000000);
 237        st4(buffer + 8, align(end + 32));
 238        st4(buffer + 12, s.st_size);
 239
 240        if (write(image, buffer + 2, 14) != 14)
 241                die(argv[2]);
 242
 243        /* For sparc64 update a_text and clear a_data + a_bss */
 244        if (is64bit)
 245        {
 246                if (lseek(image, 4, 0) < 0)
 247                        die("lseek");
 248                /* a_text */
 249                st4(buffer, align(end + 32 + 8191) - (start & ~0x3fffffUL) +
 250                            s.st_size);
 251                /* a_data */
 252                st4(buffer + 4, 0);
 253                /* a_bss */
 254                st4(buffer + 8, 0);
 255                if (write(image, buffer, 12) != 12)
 256                        die(argv[2]);
 257        }
 258
 259        /* seek page aligned boundary in the image file and add boot image */
 260        if (lseek(image, AOUT_TEXT_OFFSET - start + align(end + 32), 0) < 0)
 261                die("lseek");
 262        if ((tail = open(argv[4], O_RDONLY)) < 0)
 263                die(argv[4]);
 264        while ((i = read(tail, buffer, 1024)) > 0)
 265                if (write(image, buffer, i) != i)
 266                        die(argv[2]);
 267        if (close(image) < 0)
 268                die("close");
 269        if (close(tail) < 0)
 270                die("close");
 271        return 0;
 272}
 273