]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
681c774d | 8 | #include <elf.h> |
1da177e4 LT |
9 | |
10 | #define ElfHeaderSize (64 * 1024) | |
11 | #define ElfPages (ElfHeaderSize / 4096) | |
12 | #define KERNELBASE (0xc000000000000000) | |
681c774d | 13 | #define _ALIGN_UP(addr,size) (((addr)+((size)-1))&(~((size)-1))) |
1da177e4 | 14 | |
681c774d OH |
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 | } | |
95981e7d | 61 | static void get4k(FILE *file, char *buf ) |
1da177e4 LT |
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 | ||
95981e7d | 69 | static void put4k(FILE *file, char *buf ) |
1da177e4 LT |
70 | { |
71 | fwrite(buf, 1, 4096, file); | |
72 | } | |
73 | ||
95981e7d | 74 | static void death(const char *msg, FILE *fdesc, const char *fname) |
1da177e4 LT |
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]; | |
681c774d | 85 | struct addr_range vmlinux; |
eba2fb2d | 86 | FILE *ramDisk; |
eba2fb2d OH |
87 | FILE *inputVmlinux; |
88 | FILE *outputVmlinux; | |
89 | ||
58638bff | 90 | char *rd_name, *lx_name, *out_name; |
681c774d OH |
91 | |
92 | size_t i; | |
eba2fb2d OH |
93 | unsigned long ramFileLen; |
94 | unsigned long ramLen; | |
95 | unsigned long roundR; | |
eba2fb2d | 96 | unsigned long offset_end; |
1da177e4 | 97 | |
eba2fb2d OH |
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; | |
1da177e4 | 106 | u_int32_t eyeCatcher = 0xc8a5d9c4; |
eba2fb2d OH |
107 | unsigned long naca; |
108 | unsigned long xRamDisk; | |
109 | unsigned long xRamDiskSize; | |
110 | long padPages; | |
1da177e4 LT |
111 | |
112 | ||
113 | if (argc < 2) { | |
114 | fprintf(stderr, "Name of RAM disk file missing.\n"); | |
115 | exit(1); | |
116 | } | |
681c774d | 117 | rd_name = argv[1]; |
1da177e4 LT |
118 | |
119 | if (argc < 3) { | |
1da177e4 LT |
120 | fprintf(stderr, "Name of vmlinux file missing.\n"); |
121 | exit(1); | |
122 | } | |
681c774d | 123 | lx_name = argv[2]; |
1da177e4 | 124 | |
681c774d | 125 | if (argc < 4) { |
1da177e4 LT |
126 | fprintf(stderr, "Name of vmlinux output file missing.\n"); |
127 | exit(1); | |
128 | } | |
681c774d | 129 | out_name = argv[3]; |
1da177e4 LT |
130 | |
131 | ||
58638bff | 132 | ramDisk = fopen(rd_name, "r"); |
1da177e4 | 133 | if ( ! ramDisk ) { |
58638bff | 134 | fprintf(stderr, "RAM disk file \"%s\" failed to open.\n", rd_name); |
1da177e4 LT |
135 | exit(1); |
136 | } | |
137 | ||
58638bff | 138 | inputVmlinux = fopen(lx_name, "r"); |
1da177e4 | 139 | if ( ! inputVmlinux ) { |
58638bff | 140 | fprintf(stderr, "vmlinux file \"%s\" failed to open.\n", lx_name); |
1da177e4 LT |
141 | exit(1); |
142 | } | |
143 | ||
58638bff | 144 | outputVmlinux = fopen(out_name, "w+"); |
1da177e4 | 145 | if ( ! outputVmlinux ) { |
58638bff | 146 | fprintf(stderr, "output vmlinux file \"%s\" failed to open.\n", out_name); |
1da177e4 LT |
147 | exit(1); |
148 | } | |
681c774d OH |
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 | ||
1da177e4 LT |
162 | /* Input Vmlinux file */ |
163 | fseek(inputVmlinux, 0, SEEK_END); | |
164 | kernelLen = ftell(inputVmlinux); | |
165 | fseek(inputVmlinux, 0, SEEK_SET); | |
c44bc68d | 166 | printf("kernel file size = %lu\n", kernelLen); |
1da177e4 LT |
167 | |
168 | actualKernelLen = kernelLen - ElfHeaderSize; | |
169 | ||
c44bc68d | 170 | printf("actual kernel length (minus ELF header) = %lu\n", actualKernelLen); |
1da177e4 LT |
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 | ||
681c774d | 180 | offset_end = _ALIGN_UP(vmlinux.memsize, 4096); |
1da177e4 LT |
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); | |
58638bff | 208 | printf("%s file size = %ld/0x%lx \n", rd_name, ramFileLen, ramFileLen); |
1da177e4 LT |
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) { | |
58638bff | 262 | death("Could not read hvReleaseData pointer\n", outputVmlinux, out_name); |
1da177e4 LT |
263 | } |
264 | hvReleaseData = ntohl(hvReleaseData); /* Convert to native int */ | |
c44bc68d | 265 | printf("hvReleaseData is at %08lx\n", hvReleaseData); |
1da177e4 LT |
266 | |
267 | /* fseek to the hvReleaseData */ | |
268 | fseek(outputVmlinux, ElfHeaderSize + hvReleaseData, SEEK_SET); | |
269 | if (fread(inbuf, 0x40, 1, outputVmlinux) != 1) { | |
58638bff | 270 | death("Could not read hvReleaseData\n", outputVmlinux, out_name); |
1da177e4 LT |
271 | } |
272 | /* Check hvReleaseData sanity */ | |
273 | if (memcmp(inbuf, &eyeCatcher, 4) != 0) { | |
58638bff | 274 | death("hvReleaseData is invalid\n", outputVmlinux, out_name); |
1da177e4 LT |
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) { | |
58638bff | 283 | death("Could not read naca\n", outputVmlinux, out_name); |
1da177e4 LT |
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)) { | |
58638bff | 289 | death("RAM disk is already attached to this kernel\n", outputVmlinux, out_name); |
1da177e4 LT |
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) { | |
58638bff | 299 | death("Could not write naca\n", outputVmlinux, out_name); |
1da177e4 | 300 | } |
c44bc68d | 301 | printf("Ram Disk of 0x%lx pages is attached to the kernel at offset 0x%08lx\n", |
1da177e4 LT |
302 | ramPages, ramStartOffs); |
303 | ||
304 | /* Done */ | |
305 | fclose(outputVmlinux); | |
306 | /* Set permission to executable */ | |
58638bff | 307 | chmod(out_name, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); |
1da177e4 LT |
308 | |
309 | return 0; | |
310 | } | |
311 |