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 42 static int is64bit = 0; 43 44 /* align to power-of-two size */ 45 static 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 */ 54 static unsigned short ld2(char *p) 55 { 56 return (p[0] << 8) | p[1]; 57 } 58 59 /* save 4 bytes as big endian */ 60 static 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 68 static void die(const char *str) 69 { 70 perror(str); 71 exit(1); 72 } 73 74 static 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 82 static 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 91 static 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 */ 111 static 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 */ 146 static 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 184 int 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