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