Linux-2.6.12-rc2
[cascardo/linux.git] / arch / ppc64 / boot / addRamDisk.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <netinet/in.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <string.h>
8
9 #define ElfHeaderSize  (64 * 1024)
10 #define ElfPages  (ElfHeaderSize / 4096)
11 #define KERNELBASE (0xc000000000000000)
12
13 void get4k(FILE *file, char *buf )
14 {
15         unsigned j;
16         unsigned num = fread(buf, 1, 4096, file);
17         for ( j=num; j<4096; ++j )
18                 buf[j] = 0;
19 }
20
21 void put4k(FILE *file, char *buf )
22 {
23         fwrite(buf, 1, 4096, file);
24 }
25
26 void death(const char *msg, FILE *fdesc, const char *fname) 
27 {
28         fprintf(stderr, msg);
29         fclose(fdesc);
30         unlink(fname);
31         exit(1);
32 }
33
34 int main(int argc, char **argv)
35 {
36         char inbuf[4096];
37         FILE *ramDisk = NULL;
38         FILE *sysmap = NULL;
39         FILE *inputVmlinux = NULL;
40         FILE *outputVmlinux = NULL;
41   
42         unsigned i = 0;
43         unsigned long ramFileLen = 0;
44         unsigned long ramLen = 0;
45         unsigned long roundR = 0;
46   
47         unsigned long sysmapFileLen = 0;
48         unsigned long sysmapLen = 0;
49         unsigned long sysmapPages = 0;
50         char* ptr_end = NULL; 
51         unsigned long offset_end = 0;
52
53         unsigned long kernelLen = 0;
54         unsigned long actualKernelLen = 0;
55         unsigned long round = 0;
56         unsigned long roundedKernelLen = 0;
57         unsigned long ramStartOffs = 0;
58         unsigned long ramPages = 0;
59         unsigned long roundedKernelPages = 0;
60         unsigned long hvReleaseData = 0;
61         u_int32_t eyeCatcher = 0xc8a5d9c4;
62         unsigned long naca = 0;
63         unsigned long xRamDisk = 0;
64         unsigned long xRamDiskSize = 0;
65         long padPages = 0;
66   
67   
68         if (argc < 2) {
69                 fprintf(stderr, "Name of RAM disk file missing.\n");
70                 exit(1);
71         }
72
73         if (argc < 3) {
74                 fprintf(stderr, "Name of System Map input file is missing.\n");
75                 exit(1);
76         }
77   
78         if (argc < 4) {
79                 fprintf(stderr, "Name of vmlinux file missing.\n");
80                 exit(1);
81         }
82
83         if (argc < 5) {
84                 fprintf(stderr, "Name of vmlinux output file missing.\n");
85                 exit(1);
86         }
87
88
89         ramDisk = fopen(argv[1], "r");
90         if ( ! ramDisk ) {
91                 fprintf(stderr, "RAM disk file \"%s\" failed to open.\n", argv[1]);
92                 exit(1);
93         }
94
95         sysmap = fopen(argv[2], "r");
96         if ( ! sysmap ) {
97                 fprintf(stderr, "System Map file \"%s\" failed to open.\n", argv[2]);
98                 exit(1);
99         }
100   
101         inputVmlinux = fopen(argv[3], "r");
102         if ( ! inputVmlinux ) {
103                 fprintf(stderr, "vmlinux file \"%s\" failed to open.\n", argv[3]);
104                 exit(1);
105         }
106   
107         outputVmlinux = fopen(argv[4], "w+");
108         if ( ! outputVmlinux ) {
109                 fprintf(stderr, "output vmlinux file \"%s\" failed to open.\n", argv[4]);
110                 exit(1);
111         }
112   
113   
114   
115         /* Input Vmlinux file */
116         fseek(inputVmlinux, 0, SEEK_END);
117         kernelLen = ftell(inputVmlinux);
118         fseek(inputVmlinux, 0, SEEK_SET);
119         printf("kernel file size = %d\n", kernelLen);
120         if ( kernelLen == 0 ) {
121                 fprintf(stderr, "You must have a linux kernel specified as argv[3]\n");
122                 exit(1);
123         }
124
125         actualKernelLen = kernelLen - ElfHeaderSize;
126
127         printf("actual kernel length (minus ELF header) = %d\n", actualKernelLen);
128
129         round = actualKernelLen % 4096;
130         roundedKernelLen = actualKernelLen;
131         if ( round )
132                 roundedKernelLen += (4096 - round);
133         printf("Vmlinux length rounded up to a 4k multiple = %ld/0x%lx \n", roundedKernelLen, roundedKernelLen);
134         roundedKernelPages = roundedKernelLen / 4096;
135         printf("Vmlinux pages to copy = %ld/0x%lx \n", roundedKernelPages, roundedKernelPages);
136
137
138
139         /* Input System Map file */
140         /* (needs to be processed simply to determine if we need to add pad pages due to the static variables not being included in the vmlinux) */
141         fseek(sysmap, 0, SEEK_END);
142         sysmapFileLen = ftell(sysmap);
143         fseek(sysmap, 0, SEEK_SET);
144         printf("%s file size = %ld/0x%lx \n", argv[2], sysmapFileLen, sysmapFileLen);
145
146         sysmapLen = sysmapFileLen;
147
148         roundR = 4096 - (sysmapLen % 4096);
149         if (roundR) {
150                 printf("Rounding System Map file up to a multiple of 4096, adding %ld/0x%lx \n", roundR, roundR);
151                 sysmapLen += roundR;
152         }
153         printf("Rounded System Map size is %ld/0x%lx \n", sysmapLen, sysmapLen);
154   
155         /* Process the Sysmap file to determine where _end is */
156         sysmapPages = sysmapLen / 4096;
157         /* read the whole file line by line, expect that it doesn't fail */
158         while ( fgets(inbuf, 4096, sysmap) )  ;
159         /* search for _end in the last page of the system map */
160         ptr_end = strstr(inbuf, " _end");
161         if (!ptr_end) {
162                 fprintf(stderr, "Unable to find _end in the sysmap file \n");
163                 fprintf(stderr, "inbuf: \n");
164                 fprintf(stderr, "%s \n", inbuf);
165                 exit(1);
166         }
167         printf("Found _end in the last page of the sysmap - backing up 10 characters it looks like %s", ptr_end-10);
168         /* convert address of _end in system map to hex offset. */
169         offset_end = (unsigned int)strtol(ptr_end-10, NULL, 16);
170         /* calc how many pages we need to insert between the vmlinux and the start of the ram disk */
171         padPages = offset_end/4096 - roundedKernelPages;
172
173         /* Check and see if the vmlinux is already larger than _end in System.map */
174         if (padPages < 0) {
175                 /* vmlinux is larger than _end - adjust the offset to the start of the embedded ram disk */ 
176                 offset_end = roundedKernelLen;
177                 printf("vmlinux is larger than _end indicates it needs to be - offset_end = %lx \n", offset_end);
178                 padPages = 0;
179                 printf("will insert %lx pages between the vmlinux and the start of the ram disk \n", padPages);
180         }
181         else {
182                 /* _end is larger than vmlinux - use the offset to _end that we calculated from the system map */
183                 printf("vmlinux is smaller than _end indicates is needed - offset_end = %lx \n", offset_end);
184                 printf("will insert %lx pages between the vmlinux and the start of the ram disk \n", padPages);
185         }
186
187
188
189         /* Input Ram Disk file */
190         // Set the offset that the ram disk will be started at.
191         ramStartOffs = offset_end;  /* determined from the input vmlinux file and the system map */
192         printf("Ram Disk will start at offset = 0x%lx \n", ramStartOffs);
193   
194         fseek(ramDisk, 0, SEEK_END);
195         ramFileLen = ftell(ramDisk);
196         fseek(ramDisk, 0, SEEK_SET);
197         printf("%s file size = %ld/0x%lx \n", argv[1], ramFileLen, ramFileLen);
198
199         ramLen = ramFileLen;
200
201         roundR = 4096 - (ramLen % 4096);
202         if ( roundR ) {
203                 printf("Rounding RAM disk file up to a multiple of 4096, adding %ld/0x%lx \n", roundR, roundR);
204                 ramLen += roundR;
205         }
206
207         printf("Rounded RAM disk size is %ld/0x%lx \n", ramLen, ramLen);
208         ramPages = ramLen / 4096;
209         printf("RAM disk pages to copy = %ld/0x%lx\n", ramPages, ramPages);
210
211
212
213   // Copy 64K ELF header
214         for (i=0; i<(ElfPages); ++i) {
215                 get4k( inputVmlinux, inbuf );
216                 put4k( outputVmlinux, inbuf );
217         }
218
219         /* Copy the vmlinux (as full pages). */
220         fseek(inputVmlinux, ElfHeaderSize, SEEK_SET);
221         for ( i=0; i<roundedKernelPages; ++i ) {
222                 get4k( inputVmlinux, inbuf );
223                 put4k( outputVmlinux, inbuf );
224         }
225   
226         /* Insert pad pages (if appropriate) that are needed between */
227         /* | the end of the vmlinux and the ram disk. */
228         for (i=0; i<padPages; ++i) {
229                 memset(inbuf, 0, 4096);
230                 put4k(outputVmlinux, inbuf);
231         }
232
233         /* Copy the ram disk (as full pages). */
234         for ( i=0; i<ramPages; ++i ) {
235                 get4k( ramDisk, inbuf );
236                 put4k( outputVmlinux, inbuf );
237         }
238
239         /* Close the input files */
240         fclose(ramDisk);
241         fclose(inputVmlinux);
242         /* And flush the written output file */
243         fflush(outputVmlinux);
244
245
246
247         /* Fixup the new vmlinux to contain the ram disk starting offset (xRamDisk) and the ram disk size (xRamDiskSize) */
248         /* fseek to the hvReleaseData pointer */
249         fseek(outputVmlinux, ElfHeaderSize + 0x24, SEEK_SET);
250         if (fread(&hvReleaseData, 4, 1, outputVmlinux) != 1) {
251                 death("Could not read hvReleaseData pointer\n", outputVmlinux, argv[4]);
252         }
253         hvReleaseData = ntohl(hvReleaseData); /* Convert to native int */
254         printf("hvReleaseData is at %08x\n", hvReleaseData);
255
256         /* fseek to the hvReleaseData */
257         fseek(outputVmlinux, ElfHeaderSize + hvReleaseData, SEEK_SET);
258         if (fread(inbuf, 0x40, 1, outputVmlinux) != 1) {
259                 death("Could not read hvReleaseData\n", outputVmlinux, argv[4]);
260         }
261         /* Check hvReleaseData sanity */
262         if (memcmp(inbuf, &eyeCatcher, 4) != 0) {
263                 death("hvReleaseData is invalid\n", outputVmlinux, argv[4]);
264         }
265         /* Get the naca pointer */
266         naca = ntohl(*((u_int32_t*) &inbuf[0x0C])) - KERNELBASE;
267         printf("Naca is at offset 0x%lx \n", naca);
268
269         /* fseek to the naca */
270         fseek(outputVmlinux, ElfHeaderSize + naca, SEEK_SET);
271         if (fread(inbuf, 0x18, 1, outputVmlinux) != 1) {
272                 death("Could not read naca\n", outputVmlinux, argv[4]);
273         }
274         xRamDisk = ntohl(*((u_int32_t *) &inbuf[0x0c]));
275         xRamDiskSize = ntohl(*((u_int32_t *) &inbuf[0x14]));
276         /* Make sure a RAM disk isn't already present */
277         if ((xRamDisk != 0) || (xRamDiskSize != 0)) {
278                 death("RAM disk is already attached to this kernel\n", outputVmlinux, argv[4]);
279         }
280         /* Fill in the values */
281         *((u_int32_t *) &inbuf[0x0c]) = htonl(ramStartOffs);
282         *((u_int32_t *) &inbuf[0x14]) = htonl(ramPages);
283
284         /* Write out the new naca */
285         fflush(outputVmlinux);
286         fseek(outputVmlinux, ElfHeaderSize + naca, SEEK_SET);
287         if (fwrite(inbuf, 0x18, 1, outputVmlinux) != 1) {
288                 death("Could not write naca\n", outputVmlinux, argv[4]);
289         }
290         printf("Ram Disk of 0x%lx pages is attached to the kernel at offset 0x%08x\n",
291                ramPages, ramStartOffs);
292
293         /* Done */
294         fclose(outputVmlinux);
295         /* Set permission to executable */
296         chmod(argv[4], S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
297
298         return 0;
299 }
300