Merge branch 'master'
[linux-2.6] / arch / powerpc / 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 #include <elf.h>
9
10 #define ElfHeaderSize  (64 * 1024)
11 #define ElfPages  (ElfHeaderSize / 4096)
12 #define KERNELBASE (0xc000000000000000)
13 #define _ALIGN_UP(addr,size)    (((addr)+((size)-1))&(~((size)-1)))
14
15 struct addr_range {
16         unsigned long long addr;
17         unsigned long memsize;
18         unsigned long offset;
19 };
20
21 static int check_elf64(void *p, int size, struct addr_range *r)
22 {
23         Elf64_Ehdr *elf64 = p;
24         Elf64_Phdr *elf64ph;
25
26         if (elf64->e_ident[EI_MAG0] != ELFMAG0 ||
27             elf64->e_ident[EI_MAG1] != ELFMAG1 ||
28             elf64->e_ident[EI_MAG2] != ELFMAG2 ||
29             elf64->e_ident[EI_MAG3] != ELFMAG3 ||
30             elf64->e_ident[EI_CLASS] != ELFCLASS64 ||
31             elf64->e_ident[EI_DATA] != ELFDATA2MSB ||
32             elf64->e_type != ET_EXEC || elf64->e_machine != EM_PPC64)
33                 return 0;
34
35         if ((elf64->e_phoff + sizeof(Elf64_Phdr)) > size)
36                 return 0;
37
38         elf64ph = (Elf64_Phdr *) ((unsigned long)elf64 +
39                                   (unsigned long)elf64->e_phoff);
40
41         r->memsize = (unsigned long)elf64ph->p_memsz;
42         r->offset = (unsigned long)elf64ph->p_offset;
43         r->addr = (unsigned long long)elf64ph->p_vaddr;
44
45 #ifdef DEBUG
46         printf("PPC64 ELF file, ph:\n");
47         printf("p_type   0x%08x\n", elf64ph->p_type);
48         printf("p_flags  0x%08x\n", elf64ph->p_flags);
49         printf("p_offset 0x%016llx\n", elf64ph->p_offset);
50         printf("p_vaddr  0x%016llx\n", elf64ph->p_vaddr);
51         printf("p_paddr  0x%016llx\n", elf64ph->p_paddr);
52         printf("p_filesz 0x%016llx\n", elf64ph->p_filesz);
53         printf("p_memsz  0x%016llx\n", elf64ph->p_memsz);
54         printf("p_align  0x%016llx\n", elf64ph->p_align);
55         printf("... skipping 0x%08lx bytes of ELF header\n",
56                (unsigned long)elf64ph->p_offset);
57 #endif
58
59         return 64;
60 }
61 void get4k(FILE *file, char *buf )
62 {
63         unsigned j;
64         unsigned num = fread(buf, 1, 4096, file);
65         for ( j=num; j<4096; ++j )
66                 buf[j] = 0;
67 }
68
69 void put4k(FILE *file, char *buf )
70 {
71         fwrite(buf, 1, 4096, file);
72 }
73
74 void death(const char *msg, FILE *fdesc, const char *fname) 
75 {
76         fprintf(stderr, msg);
77         fclose(fdesc);
78         unlink(fname);
79         exit(1);
80 }
81
82 int main(int argc, char **argv)
83 {
84         char inbuf[4096];
85         struct addr_range vmlinux;
86         FILE *ramDisk;
87         FILE *inputVmlinux;
88         FILE *outputVmlinux;
89
90         char *rd_name, *lx_name, *out_name;
91
92         size_t i;
93         unsigned long ramFileLen;
94         unsigned long ramLen;
95         unsigned long roundR;
96         unsigned long offset_end;
97
98         unsigned long kernelLen;
99         unsigned long actualKernelLen;
100         unsigned long round;
101         unsigned long roundedKernelLen;
102         unsigned long ramStartOffs;
103         unsigned long ramPages;
104         unsigned long roundedKernelPages;
105         unsigned long hvReleaseData;
106         u_int32_t eyeCatcher = 0xc8a5d9c4;
107         unsigned long naca;
108         unsigned long xRamDisk;
109         unsigned long xRamDiskSize;
110         long padPages;
111   
112   
113         if (argc < 2) {
114                 fprintf(stderr, "Name of RAM disk file missing.\n");
115                 exit(1);
116         }
117         rd_name = argv[1];
118
119         if (argc < 3) {
120                 fprintf(stderr, "Name of vmlinux file missing.\n");
121                 exit(1);
122         }
123         lx_name = argv[2];
124
125         if (argc < 4) {
126                 fprintf(stderr, "Name of vmlinux output file missing.\n");
127                 exit(1);
128         }
129         out_name = argv[3];
130
131
132         ramDisk = fopen(rd_name, "r");
133         if ( ! ramDisk ) {
134                 fprintf(stderr, "RAM disk file \"%s\" failed to open.\n", rd_name);
135                 exit(1);
136         }
137
138         inputVmlinux = fopen(lx_name, "r");
139         if ( ! inputVmlinux ) {
140                 fprintf(stderr, "vmlinux file \"%s\" failed to open.\n", lx_name);
141                 exit(1);
142         }
143   
144         outputVmlinux = fopen(out_name, "w+");
145         if ( ! outputVmlinux ) {
146                 fprintf(stderr, "output vmlinux file \"%s\" failed to open.\n", out_name);
147                 exit(1);
148         }
149
150         i = fread(inbuf, 1, sizeof(inbuf), inputVmlinux);
151         if (i != sizeof(inbuf)) {
152                 fprintf(stderr, "can not read vmlinux file %s: %u\n", lx_name, i);
153                 exit(1);
154         }
155
156         i = check_elf64(inbuf, sizeof(inbuf), &vmlinux);
157         if (i == 0) {
158                 fprintf(stderr, "You must have a linux kernel specified as argv[2]\n");
159                 exit(1);
160         }
161
162         /* Input Vmlinux file */
163         fseek(inputVmlinux, 0, SEEK_END);
164         kernelLen = ftell(inputVmlinux);
165         fseek(inputVmlinux, 0, SEEK_SET);
166         printf("kernel file size = %lu\n", kernelLen);
167
168         actualKernelLen = kernelLen - ElfHeaderSize;
169
170         printf("actual kernel length (minus ELF header) = %lu\n", actualKernelLen);
171
172         round = actualKernelLen % 4096;
173         roundedKernelLen = actualKernelLen;
174         if ( round )
175                 roundedKernelLen += (4096 - round);
176         printf("Vmlinux length rounded up to a 4k multiple = %ld/0x%lx \n", roundedKernelLen, roundedKernelLen);
177         roundedKernelPages = roundedKernelLen / 4096;
178         printf("Vmlinux pages to copy = %ld/0x%lx \n", roundedKernelPages, roundedKernelPages);
179
180         offset_end = _ALIGN_UP(vmlinux.memsize, 4096);
181         /* calc how many pages we need to insert between the vmlinux and the start of the ram disk */
182         padPages = offset_end/4096 - roundedKernelPages;
183
184         /* Check and see if the vmlinux is already larger than _end in System.map */
185         if (padPages < 0) {
186                 /* vmlinux is larger than _end - adjust the offset to the start of the embedded ram disk */ 
187                 offset_end = roundedKernelLen;
188                 printf("vmlinux is larger than _end indicates it needs to be - offset_end = %lx \n", offset_end);
189                 padPages = 0;
190                 printf("will insert %lx pages between the vmlinux and the start of the ram disk \n", padPages);
191         }
192         else {
193                 /* _end is larger than vmlinux - use the offset to _end that we calculated from the system map */
194                 printf("vmlinux is smaller than _end indicates is needed - offset_end = %lx \n", offset_end);
195                 printf("will insert %lx pages between the vmlinux and the start of the ram disk \n", padPages);
196         }
197
198
199
200         /* Input Ram Disk file */
201         // Set the offset that the ram disk will be started at.
202         ramStartOffs = offset_end;  /* determined from the input vmlinux file and the system map */
203         printf("Ram Disk will start at offset = 0x%lx \n", ramStartOffs);
204   
205         fseek(ramDisk, 0, SEEK_END);
206         ramFileLen = ftell(ramDisk);
207         fseek(ramDisk, 0, SEEK_SET);
208         printf("%s file size = %ld/0x%lx \n", rd_name, ramFileLen, ramFileLen);
209
210         ramLen = ramFileLen;
211
212         roundR = 4096 - (ramLen % 4096);
213         if ( roundR ) {
214                 printf("Rounding RAM disk file up to a multiple of 4096, adding %ld/0x%lx \n", roundR, roundR);
215                 ramLen += roundR;
216         }
217
218         printf("Rounded RAM disk size is %ld/0x%lx \n", ramLen, ramLen);
219         ramPages = ramLen / 4096;
220         printf("RAM disk pages to copy = %ld/0x%lx\n", ramPages, ramPages);
221
222
223
224   // Copy 64K ELF header
225         for (i=0; i<(ElfPages); ++i) {
226                 get4k( inputVmlinux, inbuf );
227                 put4k( outputVmlinux, inbuf );
228         }
229
230         /* Copy the vmlinux (as full pages). */
231         fseek(inputVmlinux, ElfHeaderSize, SEEK_SET);
232         for ( i=0; i<roundedKernelPages; ++i ) {
233                 get4k( inputVmlinux, inbuf );
234                 put4k( outputVmlinux, inbuf );
235         }
236   
237         /* Insert pad pages (if appropriate) that are needed between */
238         /* | the end of the vmlinux and the ram disk. */
239         for (i=0; i<padPages; ++i) {
240                 memset(inbuf, 0, 4096);
241                 put4k(outputVmlinux, inbuf);
242         }
243
244         /* Copy the ram disk (as full pages). */
245         for ( i=0; i<ramPages; ++i ) {
246                 get4k( ramDisk, inbuf );
247                 put4k( outputVmlinux, inbuf );
248         }
249
250         /* Close the input files */
251         fclose(ramDisk);
252         fclose(inputVmlinux);
253         /* And flush the written output file */
254         fflush(outputVmlinux);
255
256
257
258         /* Fixup the new vmlinux to contain the ram disk starting offset (xRamDisk) and the ram disk size (xRamDiskSize) */
259         /* fseek to the hvReleaseData pointer */
260         fseek(outputVmlinux, ElfHeaderSize + 0x24, SEEK_SET);
261         if (fread(&hvReleaseData, 4, 1, outputVmlinux) != 1) {
262                 death("Could not read hvReleaseData pointer\n", outputVmlinux, out_name);
263         }
264         hvReleaseData = ntohl(hvReleaseData); /* Convert to native int */
265         printf("hvReleaseData is at %08lx\n", hvReleaseData);
266
267         /* fseek to the hvReleaseData */
268         fseek(outputVmlinux, ElfHeaderSize + hvReleaseData, SEEK_SET);
269         if (fread(inbuf, 0x40, 1, outputVmlinux) != 1) {
270                 death("Could not read hvReleaseData\n", outputVmlinux, out_name);
271         }
272         /* Check hvReleaseData sanity */
273         if (memcmp(inbuf, &eyeCatcher, 4) != 0) {
274                 death("hvReleaseData is invalid\n", outputVmlinux, out_name);
275         }
276         /* Get the naca pointer */
277         naca = ntohl(*((u_int32_t*) &inbuf[0x0C])) - KERNELBASE;
278         printf("Naca is at offset 0x%lx \n", naca);
279
280         /* fseek to the naca */
281         fseek(outputVmlinux, ElfHeaderSize + naca, SEEK_SET);
282         if (fread(inbuf, 0x18, 1, outputVmlinux) != 1) {
283                 death("Could not read naca\n", outputVmlinux, out_name);
284         }
285         xRamDisk = ntohl(*((u_int32_t *) &inbuf[0x0c]));
286         xRamDiskSize = ntohl(*((u_int32_t *) &inbuf[0x14]));
287         /* Make sure a RAM disk isn't already present */
288         if ((xRamDisk != 0) || (xRamDiskSize != 0)) {
289                 death("RAM disk is already attached to this kernel\n", outputVmlinux, out_name);
290         }
291         /* Fill in the values */
292         *((u_int32_t *) &inbuf[0x0c]) = htonl(ramStartOffs);
293         *((u_int32_t *) &inbuf[0x14]) = htonl(ramPages);
294
295         /* Write out the new naca */
296         fflush(outputVmlinux);
297         fseek(outputVmlinux, ElfHeaderSize + naca, SEEK_SET);
298         if (fwrite(inbuf, 0x18, 1, outputVmlinux) != 1) {
299                 death("Could not write naca\n", outputVmlinux, out_name);
300         }
301         printf("Ram Disk of 0x%lx pages is attached to the kernel at offset 0x%08lx\n",
302                ramPages, ramStartOffs);
303
304         /* Done */
305         fclose(outputVmlinux);
306         /* Set permission to executable */
307         chmod(out_name, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
308
309         return 0;
310 }
311