|
@@ -1,699 +0,0 @@
|
1
|
|
-#include "elf.h"
|
2
|
|
-
|
3
|
|
-#ifndef ELF_CHECK_ARCH
|
4
|
|
-#error ELF_CHECK_ARCH not defined
|
5
|
|
-#endif
|
6
|
|
-
|
7
|
|
-#define ELF_NOTES 1
|
8
|
|
-#define ELF_DEBUG 0
|
9
|
|
-
|
10
|
|
-struct elf_state
|
11
|
|
-{
|
12
|
|
- union {
|
13
|
|
- Elf32_Ehdr elf32;
|
14
|
|
- Elf64_Ehdr elf64;
|
15
|
|
- } e;
|
16
|
|
- union {
|
17
|
|
- Elf32_Phdr phdr32[1];
|
18
|
|
- Elf64_Phdr phdr64[1];
|
19
|
|
- unsigned char dummy[1024];
|
20
|
|
- } p;
|
21
|
|
- unsigned long curaddr;
|
22
|
|
- int segment; /* current segment number, -1 for none */
|
23
|
|
- uint64_t loc; /* start offset of current block */
|
24
|
|
- uint64_t skip; /* padding to be skipped to current segment */
|
25
|
|
- unsigned long toread; /* remaining data to be read in the segment */
|
26
|
|
-#if ELF_NOTES
|
27
|
|
- int check_ip_checksum;
|
28
|
|
- uint16_t ip_checksum;
|
29
|
|
- unsigned long ip_checksum_offset;
|
30
|
|
-#endif
|
31
|
|
-};
|
32
|
|
-
|
33
|
|
-static struct elf_state estate;
|
34
|
|
-
|
35
|
|
-static unsigned long find_segment(unsigned long size, unsigned long align)
|
36
|
|
-{
|
37
|
|
- unsigned i;
|
38
|
|
- /* Verify I have a power of 2 alignment */
|
39
|
|
- if (align & (align - 1)) {
|
40
|
|
- return ULONG_MAX;
|
41
|
|
- }
|
42
|
|
- for(i = 0; i < meminfo.map_count; i++) {
|
43
|
|
- unsigned long r_start, r_end;
|
44
|
|
- if (meminfo.map[i].type != E820_RAM)
|
45
|
|
- continue;
|
46
|
|
- if ((meminfo.map[i].addr + meminfo.map[i].size) > ULONG_MAX) {
|
47
|
|
- continue;
|
48
|
|
- }
|
49
|
|
- r_start = meminfo.map[i].addr;
|
50
|
|
- r_end = r_start + meminfo.map[i].size;
|
51
|
|
- /* Don't allow the segment to overlap etherboot */
|
52
|
|
- if ((r_end > virt_to_phys(_text)) && (r_start < virt_to_phys(_text))) {
|
53
|
|
- r_end = virt_to_phys(_text);
|
54
|
|
- }
|
55
|
|
- if ((r_start > virt_to_phys(_text)) && (r_start < virt_to_phys(_end))) {
|
56
|
|
- r_start = virt_to_phys(_end);
|
57
|
|
- }
|
58
|
|
- /* Don't allow the segment to overlap the heap */
|
59
|
|
- if ((r_end > heap_ptr) && (r_start < heap_ptr)) {
|
60
|
|
- r_end = heap_ptr;
|
61
|
|
- }
|
62
|
|
- if ((r_start > heap_ptr) && (r_start < heap_bot)) {
|
63
|
|
- r_start = heap_ptr;
|
64
|
|
- }
|
65
|
|
- r_start = (r_start + align - 1) & ~(align - 1);
|
66
|
|
- if ((r_end >= r_start) && ((r_end - r_start) >= size)) {
|
67
|
|
- return r_start;
|
68
|
|
- }
|
69
|
|
- }
|
70
|
|
- /* I did not find anything :( */
|
71
|
|
- return ULONG_MAX;
|
72
|
|
-}
|
73
|
|
-
|
74
|
|
-static void elf_boot(unsigned long machine, unsigned long entry)
|
75
|
|
-{
|
76
|
|
- int result;
|
77
|
|
- struct Elf_Bhdr *hdr;
|
78
|
|
- multiboot_boot(entry);
|
79
|
|
- /* We cleanup unconditionally, and then reawaken the network
|
80
|
|
- * adapter after the longjmp.
|
81
|
|
- */
|
82
|
|
- hdr = prepare_boot_params(&estate.e);
|
83
|
|
- result = elf_start(machine, entry, virt_to_phys(hdr));
|
84
|
|
- if (result == 0) {
|
85
|
|
- result = -1;
|
86
|
|
- }
|
87
|
|
- printf("Secondary program returned %d\n", result);
|
88
|
|
- longjmp(restart_etherboot, result);
|
89
|
|
-}
|
90
|
|
-
|
91
|
|
-#if ELF_NOTES
|
92
|
|
-static int elf_prep_segment(
|
93
|
|
- unsigned long start __unused, unsigned long mid __unused, unsigned long end __unused,
|
94
|
|
- unsigned long istart, unsigned long iend)
|
95
|
|
-
|
96
|
|
-{
|
97
|
|
- if (estate.check_ip_checksum) {
|
98
|
|
- if ((istart <= estate.ip_checksum_offset) &&
|
99
|
|
- (iend > estate.ip_checksum_offset)) {
|
100
|
|
- /* The checksum note is also loaded in a
|
101
|
|
- * PT_LOAD segment, so the computed checksum
|
102
|
|
- * should be 0.
|
103
|
|
- */
|
104
|
|
- estate.ip_checksum = 0;
|
105
|
|
- }
|
106
|
|
- }
|
107
|
|
- return 1;
|
108
|
|
-}
|
109
|
|
-#else
|
110
|
|
-#define elf_prep_segment(start, mid, end, istart, iend) (1)
|
111
|
|
-#endif
|
112
|
|
-
|
113
|
|
-
|
114
|
|
-#if ELF_NOTES
|
115
|
|
-static void process_elf_notes(unsigned char *header,
|
116
|
|
- unsigned long offset, unsigned long length)
|
117
|
|
-{
|
118
|
|
- unsigned char *note, *end;
|
119
|
|
- char *program, *version;
|
120
|
|
-
|
121
|
|
- estate.check_ip_checksum = 0;
|
122
|
|
- note = header + offset;
|
123
|
|
- end = note + length;
|
124
|
|
- program = version = 0;
|
125
|
|
- while(note < end) {
|
126
|
|
- Elf_Nhdr *hdr;
|
127
|
|
- unsigned char *n_name, *n_desc, *next;
|
128
|
|
- hdr = (Elf_Nhdr *)note;
|
129
|
|
- n_name = note + sizeof(*hdr);
|
130
|
|
- n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
|
131
|
|
- next = n_desc + ((hdr->n_descsz + 3) & ~3);
|
132
|
|
- if (next > end) {
|
133
|
|
- break;
|
134
|
|
- }
|
135
|
|
- if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) &&
|
136
|
|
- (memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
|
137
|
|
- switch(hdr->n_type) {
|
138
|
|
- case EIN_PROGRAM_NAME:
|
139
|
|
- if (n_desc[hdr->n_descsz -1] == 0) {
|
140
|
|
- program = n_desc;
|
141
|
|
- }
|
142
|
|
- break;
|
143
|
|
- case EIN_PROGRAM_VERSION:
|
144
|
|
- if (n_desc[hdr->n_descsz -1] == 0) {
|
145
|
|
- version = n_desc;
|
146
|
|
- }
|
147
|
|
- break;
|
148
|
|
- case EIN_PROGRAM_CHECKSUM:
|
149
|
|
- estate.check_ip_checksum = 1;
|
150
|
|
- estate.ip_checksum = *((uint16_t *)n_desc);
|
151
|
|
- /* Remember where the segment is so
|
152
|
|
- * I can detect segment overlaps.
|
153
|
|
- */
|
154
|
|
- estate.ip_checksum_offset = n_desc - header;
|
155
|
|
-#if ELF_DEBUG
|
156
|
|
- printf("Checksum: %hx\n", estate.ip_checksum);
|
157
|
|
-#endif
|
158
|
|
-
|
159
|
|
- break;
|
160
|
|
- }
|
161
|
|
- }
|
162
|
|
-#if ELF_DEBUG
|
163
|
|
- printf("n_type: %x n_name(%d): %s n_desc(%d): %s\n",
|
164
|
|
- hdr->n_type,
|
165
|
|
- hdr->n_namesz, n_name,
|
166
|
|
- hdr->n_descsz, n_desc);
|
167
|
|
-#endif
|
168
|
|
- note = next;
|
169
|
|
- }
|
170
|
|
- if (program && version) {
|
171
|
|
- printf("\nLoading %s version: %s\n", program, version);
|
172
|
|
- }
|
173
|
|
-}
|
174
|
|
-#endif
|
175
|
|
-
|
176
|
|
-#ifdef ELF_IMAGE
|
177
|
|
-static sector_t elf32_download(unsigned char *data, unsigned int len, int eof);
|
178
|
|
-static inline os_download_t elf32_probe(unsigned char *data, unsigned int len)
|
179
|
|
-{
|
180
|
|
- unsigned long phdr_size;
|
181
|
|
- if (len < sizeof(estate.e.elf32)) {
|
182
|
|
- return 0;
|
183
|
|
- }
|
184
|
|
- memcpy(&estate.e.elf32, data, sizeof(estate.e.elf32));
|
185
|
|
- if ((estate.e.elf32.e_ident[EI_MAG0] != ELFMAG0) ||
|
186
|
|
- (estate.e.elf32.e_ident[EI_MAG1] != ELFMAG1) ||
|
187
|
|
- (estate.e.elf32.e_ident[EI_MAG2] != ELFMAG2) ||
|
188
|
|
- (estate.e.elf32.e_ident[EI_MAG3] != ELFMAG3) ||
|
189
|
|
- (estate.e.elf32.e_ident[EI_CLASS] != ELFCLASS32) ||
|
190
|
|
- (estate.e.elf32.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
|
191
|
|
- (estate.e.elf32.e_ident[EI_VERSION] != EV_CURRENT) ||
|
192
|
|
- ( (estate.e.elf32.e_type != ET_EXEC) &&
|
193
|
|
- (estate.e.elf32.e_type != ET_DYN)) ||
|
194
|
|
- (estate.e.elf32.e_version != EV_CURRENT) ||
|
195
|
|
- (estate.e.elf32.e_ehsize != sizeof(Elf32_Ehdr)) ||
|
196
|
|
- (estate.e.elf32.e_phentsize != sizeof(Elf32_Phdr)) ||
|
197
|
|
- !ELF_CHECK_ARCH(estate.e.elf32)) {
|
198
|
|
- return 0;
|
199
|
|
- }
|
200
|
|
- printf("(ELF");
|
201
|
|
- elf_freebsd_probe();
|
202
|
|
- printf(")... ");
|
203
|
|
- phdr_size = estate.e.elf32.e_phnum * estate.e.elf32.e_phentsize;
|
204
|
|
- if (estate.e.elf32.e_phoff + phdr_size > len) {
|
205
|
|
- printf("ELF header outside first block\n");
|
206
|
|
- return dead_download;
|
207
|
|
- }
|
208
|
|
- if (phdr_size > sizeof(estate.p.dummy)) {
|
209
|
|
- printf("Program header too big\n");
|
210
|
|
- return dead_download;
|
211
|
|
- }
|
212
|
|
- memcpy(&estate.p.phdr32, data + estate.e.elf32.e_phoff, phdr_size);
|
213
|
|
- if (estate.e.elf32.e_type == ET_DYN) {
|
214
|
|
- Elf32_Addr min, max, base_addr, delta, align;
|
215
|
|
- min = -1;
|
216
|
|
- max = 0;
|
217
|
|
- align = 1;
|
218
|
|
- for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
219
|
|
- Elf32_Addr val;
|
220
|
|
- if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
|
221
|
|
- continue;
|
222
|
|
- val = estate.p.phdr32[estate.segment].p_paddr;
|
223
|
|
- if (val < min) {
|
224
|
|
- min = val;
|
225
|
|
- }
|
226
|
|
- val += estate.p.phdr32[estate.segment].p_memsz;
|
227
|
|
- if (val > max) {
|
228
|
|
- max = val;
|
229
|
|
- }
|
230
|
|
- if (estate.p.phdr32[estate.segment].p_align > align) {
|
231
|
|
- align = estate.p.phdr32[estate.segment].p_align;
|
232
|
|
- }
|
233
|
|
- }
|
234
|
|
- if (align & (align -1)) {
|
235
|
|
- printf("ELF base address alignment is not a power of 2\n");
|
236
|
|
- return dead_download;
|
237
|
|
- }
|
238
|
|
- base_addr = find_segment(max - min, align);
|
239
|
|
- if (base_addr == ULONG_MAX) {
|
240
|
|
- printf("ELF base address not available for size %ld\n", max - min);
|
241
|
|
- return dead_download;
|
242
|
|
- }
|
243
|
|
- /* Compute the change in base address and fix up the addresses */
|
244
|
|
- delta = base_addr - min;
|
245
|
|
- for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
246
|
|
- /* Change the base address of the object to load */
|
247
|
|
- estate.p.phdr32[estate.segment].p_paddr += delta;
|
248
|
|
- }
|
249
|
|
- estate.e.elf32.e_entry += delta;
|
250
|
|
- }
|
251
|
|
-#if ELF_NOTES
|
252
|
|
- /* Load ELF notes from the image */
|
253
|
|
- estate.check_ip_checksum = 0;
|
254
|
|
- for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
255
|
|
- if (estate.p.phdr32[estate.segment].p_type != PT_NOTE)
|
256
|
|
- continue;
|
257
|
|
- if (estate.p.phdr32[estate.segment].p_offset + estate.p.phdr32[estate.segment].p_filesz > len) {
|
258
|
|
- /* Ignore ELF notes outside of the first block */
|
259
|
|
- continue;
|
260
|
|
- }
|
261
|
|
- process_elf_notes(data,
|
262
|
|
- estate.p.phdr32[estate.segment].p_offset, estate.p.phdr32[estate.segment].p_filesz);
|
263
|
|
- }
|
264
|
|
-#endif
|
265
|
|
- /* Check for Etherboot related limitations. Memory
|
266
|
|
- * between _text and _end is not allowed.
|
267
|
|
- * Reasons: the Etherboot code/data area.
|
268
|
|
- */
|
269
|
|
- for (estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
270
|
|
- unsigned long start, mid, end, istart, iend;
|
271
|
|
- if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
|
272
|
|
- continue;
|
273
|
|
-
|
274
|
|
- elf_freebsd_fixup_segment();
|
275
|
|
-
|
276
|
|
- start = estate.p.phdr32[estate.segment].p_paddr;
|
277
|
|
- mid = start + estate.p.phdr32[estate.segment].p_filesz;
|
278
|
|
- end = start + estate.p.phdr32[estate.segment].p_memsz;
|
279
|
|
- istart = estate.p.phdr32[estate.segment].p_offset;
|
280
|
|
- iend = istart + estate.p.phdr32[estate.segment].p_filesz;
|
281
|
|
- if (!prep_segment(start, mid, end, istart, iend)) {
|
282
|
|
- return dead_download;
|
283
|
|
- }
|
284
|
|
- if (!elf_prep_segment(start, mid, end, istart, iend)) {
|
285
|
|
- return dead_download;
|
286
|
|
- }
|
287
|
|
- }
|
288
|
|
- estate.segment = -1;
|
289
|
|
- estate.loc = 0;
|
290
|
|
- estate.skip = 0;
|
291
|
|
- estate.toread = 0;
|
292
|
|
- multiboot_init();
|
293
|
|
- return elf32_download;
|
294
|
|
-}
|
295
|
|
-
|
296
|
|
-static sector_t elf32_download(unsigned char *data, unsigned int len, int eof)
|
297
|
|
-{
|
298
|
|
- unsigned long skip_sectors = 0;
|
299
|
|
- unsigned int offset; /* working offset in the current data block */
|
300
|
|
- int i;
|
301
|
|
-
|
302
|
|
- offset = 0;
|
303
|
|
- do {
|
304
|
|
- if (estate.segment != -1) {
|
305
|
|
- if (estate.skip) {
|
306
|
|
- if (estate.skip >= len - offset) {
|
307
|
|
- estate.skip -= len - offset;
|
308
|
|
- break;
|
309
|
|
- }
|
310
|
|
- offset += estate.skip;
|
311
|
|
- estate.skip = 0;
|
312
|
|
- }
|
313
|
|
-
|
314
|
|
- if (estate.toread) {
|
315
|
|
- unsigned int cplen;
|
316
|
|
- cplen = len - offset;
|
317
|
|
- if (cplen >= estate.toread) {
|
318
|
|
- cplen = estate.toread;
|
319
|
|
- }
|
320
|
|
- memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
|
321
|
|
- estate.curaddr += cplen;
|
322
|
|
- estate.toread -= cplen;
|
323
|
|
- offset += cplen;
|
324
|
|
- if (estate.toread)
|
325
|
|
- break;
|
326
|
|
- elf_freebsd_find_segment_end();
|
327
|
|
- }
|
328
|
|
- }
|
329
|
|
-
|
330
|
|
- /* Data left, but current segment finished - look for the next
|
331
|
|
- * segment (in file offset order) that needs to be loaded.
|
332
|
|
- * We can only seek forward, so select the program headers,
|
333
|
|
- * in the correct order.
|
334
|
|
- */
|
335
|
|
- estate.segment = -1;
|
336
|
|
- for (i = 0; i < estate.e.elf32.e_phnum; i++) {
|
337
|
|
- if (estate.p.phdr32[i].p_type != PT_LOAD)
|
338
|
|
- continue;
|
339
|
|
- if (estate.p.phdr32[i].p_filesz == 0)
|
340
|
|
- continue;
|
341
|
|
- if (estate.p.phdr32[i].p_offset < estate.loc + offset)
|
342
|
|
- continue; /* can't go backwards */
|
343
|
|
- if ((estate.segment != -1) &&
|
344
|
|
- (estate.p.phdr32[i].p_offset >= estate.p.phdr32[estate.segment].p_offset))
|
345
|
|
- continue; /* search minimum file offset */
|
346
|
|
- estate.segment = i;
|
347
|
|
- }
|
348
|
|
- if (estate.segment == -1) {
|
349
|
|
- if (elf_freebsd_debug_loader(offset)) {
|
350
|
|
- estate.segment = 0; /* -1 makes it not read anymore */
|
351
|
|
- continue;
|
352
|
|
- }
|
353
|
|
- /* No more segments to be loaded, so just start the
|
354
|
|
- * kernel. This saves a lot of network bandwidth if
|
355
|
|
- * debug info is in the kernel but not loaded. */
|
356
|
|
- goto elf_startkernel;
|
357
|
|
- break;
|
358
|
|
- }
|
359
|
|
- estate.curaddr = estate.p.phdr32[estate.segment].p_paddr;
|
360
|
|
- estate.skip = estate.p.phdr32[estate.segment].p_offset - (estate.loc + offset);
|
361
|
|
- estate.toread = estate.p.phdr32[estate.segment].p_filesz;
|
362
|
|
-#if ELF_DEBUG
|
363
|
|
- printf("PHDR %d, size %#lX, curaddr %#lX\n",
|
364
|
|
- estate.segment, estate.toread, estate.curaddr);
|
365
|
|
-#endif
|
366
|
|
- } while (offset < len);
|
367
|
|
-
|
368
|
|
- estate.loc += len + (estate.skip & ~0x1ff);
|
369
|
|
- skip_sectors = estate.skip >> 9;
|
370
|
|
- estate.skip &= 0x1ff;
|
371
|
|
-
|
372
|
|
- if (eof) {
|
373
|
|
- unsigned long entry;
|
374
|
|
- unsigned long machine;
|
375
|
|
-elf_startkernel:
|
376
|
|
- entry = estate.e.elf32.e_entry;
|
377
|
|
- machine = estate.e.elf32.e_machine;
|
378
|
|
-
|
379
|
|
-#if ELF_NOTES
|
380
|
|
- if (estate.check_ip_checksum) {
|
381
|
|
- unsigned long bytes = 0;
|
382
|
|
- uint16_t sum, new_sum;
|
383
|
|
-
|
384
|
|
- sum = ipchksum(&estate.e.elf32, sizeof(estate.e.elf32));
|
385
|
|
- bytes = sizeof(estate.e.elf32);
|
386
|
|
-#if ELF_DEBUG
|
387
|
|
- printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
|
388
|
|
- sum, sum, bytes, bytes);
|
389
|
|
-#endif
|
390
|
|
-
|
391
|
|
- new_sum = ipchksum(estate.p.phdr32, sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum);
|
392
|
|
- sum = add_ipchksums(bytes, sum, new_sum);
|
393
|
|
- bytes += sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum;
|
394
|
|
-#if ELF_DEBUG
|
395
|
|
- printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
|
396
|
|
- new_sum, sum,
|
397
|
|
- sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum, bytes);
|
398
|
|
-#endif
|
399
|
|
-
|
400
|
|
- for(i = 0; i < estate.e.elf32.e_phnum; i++) {
|
401
|
|
- if (estate.p.phdr32[i].p_type != PT_LOAD)
|
402
|
|
- continue;
|
403
|
|
- new_sum = ipchksum(phys_to_virt(estate.p.phdr32[i].p_paddr),
|
404
|
|
- estate.p.phdr32[i].p_memsz);
|
405
|
|
- sum = add_ipchksums(bytes, sum, new_sum);
|
406
|
|
- bytes += estate.p.phdr32[i].p_memsz;
|
407
|
|
-#if ELF_DEBUG
|
408
|
|
- printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
|
409
|
|
- i, new_sum, sum,
|
410
|
|
- estate.p.phdr32[i].p_memsz, bytes);
|
411
|
|
-#endif
|
412
|
|
-
|
413
|
|
- }
|
414
|
|
- if (estate.ip_checksum != sum) {
|
415
|
|
- printf("\nImage checksum: %hx != computed checksum: %hx\n",
|
416
|
|
- estate.ip_checksum, sum);
|
417
|
|
- longjmp(restart_etherboot, -2);
|
418
|
|
- }
|
419
|
|
- }
|
420
|
|
-#endif
|
421
|
|
- done(1);
|
422
|
|
- /* Fixup the offset to the program header so you can find the program headers from
|
423
|
|
- * the ELF header mknbi needs this.
|
424
|
|
- */
|
425
|
|
- estate.e.elf32.e_phoff = (char *)&estate.p - (char *)&estate.e;
|
426
|
|
- elf_freebsd_boot(entry);
|
427
|
|
- elf_boot(machine,entry);
|
428
|
|
- }
|
429
|
|
- return skip_sectors;
|
430
|
|
-}
|
431
|
|
-#endif /* ELF_IMAGE */
|
432
|
|
-
|
433
|
|
-#ifdef ELF64_IMAGE
|
434
|
|
-static sector_t elf64_download(unsigned char *data, unsigned int len, int eof);
|
435
|
|
-static inline os_download_t elf64_probe(unsigned char *data, unsigned int len)
|
436
|
|
-{
|
437
|
|
- unsigned long phdr_size;
|
438
|
|
- if (len < sizeof(estate.e.elf64)) {
|
439
|
|
- return 0;
|
440
|
|
- }
|
441
|
|
- memcpy(&estate.e.elf64, data, sizeof(estate.e.elf64));
|
442
|
|
- if ((estate.e.elf64.e_ident[EI_MAG0] != ELFMAG0) ||
|
443
|
|
- (estate.e.elf64.e_ident[EI_MAG1] != ELFMAG1) ||
|
444
|
|
- (estate.e.elf64.e_ident[EI_MAG2] != ELFMAG2) ||
|
445
|
|
- (estate.e.elf64.e_ident[EI_MAG3] != ELFMAG3) ||
|
446
|
|
- (estate.e.elf64.e_ident[EI_CLASS] != ELFCLASS64) ||
|
447
|
|
- (estate.e.elf64.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
|
448
|
|
- (estate.e.elf64.e_ident[EI_VERSION] != EV_CURRENT) ||
|
449
|
|
- ( (estate.e.elf64.e_type != ET_EXEC) &&
|
450
|
|
- (estate.e.elf64.e_type != ET_DYN)) ||
|
451
|
|
- (estate.e.elf64.e_version != EV_CURRENT) ||
|
452
|
|
- (estate.e.elf64.e_ehsize != sizeof(Elf64_Ehdr)) ||
|
453
|
|
- (estate.e.elf64.e_phentsize != sizeof(Elf64_Phdr)) ||
|
454
|
|
- !ELF_CHECK_ARCH(estate.e.elf64)) {
|
455
|
|
- return 0;
|
456
|
|
- }
|
457
|
|
- printf("(ELF64)... ");
|
458
|
|
- phdr_size = estate.e.elf64.e_phnum * estate.e.elf64.e_phentsize;
|
459
|
|
- if (estate.e.elf64.e_phoff + phdr_size > len) {
|
460
|
|
- printf("ELF header outside first block\n");
|
461
|
|
- return dead_download;
|
462
|
|
- }
|
463
|
|
- if (phdr_size > sizeof(estate.p.dummy)) {
|
464
|
|
- printf("Program header to big\n");
|
465
|
|
- return dead_download;
|
466
|
|
- }
|
467
|
|
- if (estate.e.elf64.e_entry > ULONG_MAX) {
|
468
|
|
- printf("ELF entry point exceeds address space\n");
|
469
|
|
- return dead_download;
|
470
|
|
- }
|
471
|
|
- memcpy(&estate.p.phdr64, data + estate.e.elf64.e_phoff, phdr_size);
|
472
|
|
- if (estate.e.elf64.e_type == ET_DYN) {
|
473
|
|
- Elf64_Addr min, max, base_addr, delta, align;
|
474
|
|
- min = -1;
|
475
|
|
- max = 0;
|
476
|
|
- align = 1;
|
477
|
|
- for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
478
|
|
- Elf64_Addr val;
|
479
|
|
- if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
|
480
|
|
- continue;
|
481
|
|
- val = estate.p.phdr64[estate.segment].p_paddr;
|
482
|
|
- if (val < min) {
|
483
|
|
- min = val;
|
484
|
|
- }
|
485
|
|
- val += estate.p.phdr64[estate.segment].p_memsz;
|
486
|
|
- if (val > max) {
|
487
|
|
- max = val;
|
488
|
|
- }
|
489
|
|
- if (estate.p.phdr64[estate.segment].p_align > align) {
|
490
|
|
- align = estate.p.phdr64[estate.segment].p_align;
|
491
|
|
- }
|
492
|
|
- }
|
493
|
|
- if (align > ULONG_MAX) {
|
494
|
|
- printf("ELF base address alignment exceeds address space\n");
|
495
|
|
- return dead_download;
|
496
|
|
- }
|
497
|
|
- if (align & (align -1)) {
|
498
|
|
- printf("ELF base address alignment is not a power of 2\n");
|
499
|
|
- return dead_download;
|
500
|
|
- }
|
501
|
|
- if ((max - min) > ULONG_MAX) {
|
502
|
|
- printf("ELF size exceeds address space\n");
|
503
|
|
- return dead_download;
|
504
|
|
- }
|
505
|
|
- base_addr = find_segment(max - min, align);
|
506
|
|
- if (base_addr == ULONG_MAX) {
|
507
|
|
- printf("ELF base address not available for size %ld\n", max - min);
|
508
|
|
- return dead_download;
|
509
|
|
- }
|
510
|
|
- /* Compute the change in base address and fix up the addresses */
|
511
|
|
- delta = base_addr - min;
|
512
|
|
- for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
513
|
|
- /* Change the base address of the object to load */
|
514
|
|
- estate.p.phdr64[estate.segment].p_paddr += delta;
|
515
|
|
- }
|
516
|
|
- estate.e.elf64.e_entry += delta;
|
517
|
|
- }
|
518
|
|
-#if ELF_NOTES
|
519
|
|
- /* Load ELF notes from the image */
|
520
|
|
- estate.check_ip_checksum = 0;
|
521
|
|
- for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
522
|
|
- if (estate.p.phdr64[estate.segment].p_type != PT_NOTE)
|
523
|
|
- continue;
|
524
|
|
- if (estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz > len) {
|
525
|
|
- /* Ignore ELF notes outside of the first block */
|
526
|
|
- continue;
|
527
|
|
- }
|
528
|
|
- process_elf_notes(data,
|
529
|
|
- estate.p.phdr64[estate.segment].p_offset, estate.p.phdr64[estate.segment].p_filesz);
|
530
|
|
- }
|
531
|
|
-#endif
|
532
|
|
- /* Check for Etherboot related limitations. Memory
|
533
|
|
- * between _text and _end is not allowed.
|
534
|
|
- * Reasons: the Etherboot code/data area.
|
535
|
|
- */
|
536
|
|
- for (estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
537
|
|
- unsigned long start, mid, end, istart, iend;
|
538
|
|
- if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
|
539
|
|
- continue;
|
540
|
|
- if ((estate.p.phdr64[estate.segment].p_paddr > ULONG_MAX) ||
|
541
|
|
- ((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_filesz) > ULONG_MAX) ||
|
542
|
|
- ((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_memsz) > ULONG_MAX)) {
|
543
|
|
- printf("ELF segment exceeds address space\n");
|
544
|
|
- return dead_download;
|
545
|
|
- }
|
546
|
|
- start = estate.p.phdr64[estate.segment].p_paddr;
|
547
|
|
- mid = start + estate.p.phdr64[estate.segment].p_filesz;
|
548
|
|
- end = start + estate.p.phdr64[estate.segment].p_memsz;
|
549
|
|
- istart = iend = ULONG_MAX;
|
550
|
|
- if ((estate.p.phdr64[estate.segment].p_offset < ULONG_MAX) &&
|
551
|
|
- ((estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz) < ULONG_MAX))
|
552
|
|
- {
|
553
|
|
- istart = estate.p.phdr64[estate.segment].p_offset;
|
554
|
|
- iend = istart + estate.p.phdr64[estate.segment].p_filesz;
|
555
|
|
- }
|
556
|
|
- if (!prep_segment(start, mid, end, istart, iend)) {
|
557
|
|
- return dead_download;
|
558
|
|
- }
|
559
|
|
- if (!elf_prep_segment(start, mid, end, istart, iend)) {
|
560
|
|
- return dead_download;
|
561
|
|
- }
|
562
|
|
- }
|
563
|
|
- estate.segment = -1;
|
564
|
|
- estate.loc = 0;
|
565
|
|
- estate.skip = 0;
|
566
|
|
- estate.toread = 0;
|
567
|
|
- return elf64_download;
|
568
|
|
-}
|
569
|
|
-
|
570
|
|
-static sector_t elf64_download(unsigned char *data, unsigned int len, int eof)
|
571
|
|
-{
|
572
|
|
- unsigned long skip_sectors = 0;
|
573
|
|
- unsigned int offset; /* working offset in the current data block */
|
574
|
|
- int i;
|
575
|
|
-
|
576
|
|
- offset = 0;
|
577
|
|
- do {
|
578
|
|
- if (estate.segment != -1) {
|
579
|
|
- if (estate.skip) {
|
580
|
|
- if (estate.skip >= len - offset) {
|
581
|
|
- estate.skip -= len - offset;
|
582
|
|
- break;
|
583
|
|
- }
|
584
|
|
- offset += estate.skip;
|
585
|
|
- estate.skip = 0;
|
586
|
|
- }
|
587
|
|
-
|
588
|
|
- if (estate.toread) {
|
589
|
|
- unsigned int cplen;
|
590
|
|
- cplen = len - offset;
|
591
|
|
- if (cplen >= estate.toread) {
|
592
|
|
- cplen = estate.toread;
|
593
|
|
- }
|
594
|
|
- memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
|
595
|
|
- estate.curaddr += cplen;
|
596
|
|
- estate.toread -= cplen;
|
597
|
|
- offset += cplen;
|
598
|
|
- if (estate.toread)
|
599
|
|
- break;
|
600
|
|
- }
|
601
|
|
- }
|
602
|
|
-
|
603
|
|
- /* Data left, but current segment finished - look for the next
|
604
|
|
- * segment (in file offset order) that needs to be loaded.
|
605
|
|
- * We can only seek forward, so select the program headers,
|
606
|
|
- * in the correct order.
|
607
|
|
- */
|
608
|
|
- estate.segment = -1;
|
609
|
|
- for (i = 0; i < estate.e.elf64.e_phnum; i++) {
|
610
|
|
- if (estate.p.phdr64[i].p_type != PT_LOAD)
|
611
|
|
- continue;
|
612
|
|
- if (estate.p.phdr64[i].p_filesz == 0)
|
613
|
|
- continue;
|
614
|
|
- if (estate.p.phdr64[i].p_offset < estate.loc + offset)
|
615
|
|
- continue; /* can't go backwards */
|
616
|
|
- if ((estate.segment != -1) &&
|
617
|
|
- (estate.p.phdr64[i].p_offset >= estate.p.phdr64[estate.segment].p_offset))
|
618
|
|
- continue; /* search minimum file offset */
|
619
|
|
- estate.segment = i;
|
620
|
|
- }
|
621
|
|
- if (estate.segment == -1) {
|
622
|
|
- /* No more segments to be loaded, so just start the
|
623
|
|
- * kernel. This saves a lot of network bandwidth if
|
624
|
|
- * debug info is in the kernel but not loaded. */
|
625
|
|
- goto elf_startkernel;
|
626
|
|
- break;
|
627
|
|
- }
|
628
|
|
- estate.curaddr = estate.p.phdr64[estate.segment].p_paddr;
|
629
|
|
- estate.skip = estate.p.phdr64[estate.segment].p_offset - (estate.loc + offset);
|
630
|
|
- estate.toread = estate.p.phdr64[estate.segment].p_filesz;
|
631
|
|
-#if ELF_DEBUG
|
632
|
|
- printf("PHDR %d, size %#lX, curaddr %#lX\n",
|
633
|
|
- estate.segment, estate.toread, estate.curaddr);
|
634
|
|
-#endif
|
635
|
|
- } while (offset < len);
|
636
|
|
-
|
637
|
|
- estate.loc += len + (estate.skip & ~0x1ff);
|
638
|
|
- skip_sectors = estate.skip >> 9;
|
639
|
|
- estate.skip &= 0x1ff;
|
640
|
|
-
|
641
|
|
- if (eof) {
|
642
|
|
- unsigned long entry;
|
643
|
|
- unsigned long machine;
|
644
|
|
-elf_startkernel:
|
645
|
|
- entry = estate.e.elf64.e_entry;
|
646
|
|
- machine = estate.e.elf64.e_machine;
|
647
|
|
-#if ELF_NOTES
|
648
|
|
- if (estate.check_ip_checksum) {
|
649
|
|
- unsigned long bytes = 0;
|
650
|
|
- uint16_t sum, new_sum;
|
651
|
|
-
|
652
|
|
- sum = ipchksum(&estate.e.elf64, sizeof(estate.e.elf64));
|
653
|
|
- bytes = sizeof(estate.e.elf64);
|
654
|
|
-#if ELF_DEBUG
|
655
|
|
- printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
|
656
|
|
- sum, sum, bytes, bytes);
|
657
|
|
-#endif
|
658
|
|
-
|
659
|
|
- new_sum = ipchksum(estate.p.phdr64, sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum);
|
660
|
|
- sum = add_ipchksums(bytes, sum, new_sum);
|
661
|
|
- bytes += sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum;
|
662
|
|
-#if ELF_DEBUG
|
663
|
|
- printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
|
664
|
|
- new_sum, sum,
|
665
|
|
- sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum, bytes);
|
666
|
|
-#endif
|
667
|
|
-
|
668
|
|
- for(i = 0; i < estate.e.elf64.e_phnum; i++) {
|
669
|
|
- if (estate.p.phdr64[i].p_type != PT_LOAD)
|
670
|
|
- continue;
|
671
|
|
- new_sum = ipchksum(phys_to_virt(estate.p.phdr64[i].p_paddr),
|
672
|
|
- estate.p.phdr64[i].p_memsz);
|
673
|
|
- sum = add_ipchksums(bytes, sum, new_sum);
|
674
|
|
- bytes += estate.p.phdr64[i].p_memsz;
|
675
|
|
-#if ELF_DEBUG
|
676
|
|
- printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
|
677
|
|
- i, new_sum, sum,
|
678
|
|
- estate.p.phdr64[i].p_memsz, bytes);
|
679
|
|
-#endif
|
680
|
|
-
|
681
|
|
- }
|
682
|
|
- if (estate.ip_checksum != sum) {
|
683
|
|
- printf("\nImage checksum: %hx != computed checksum: %hx\n",
|
684
|
|
- estate.ip_checksum, sum);
|
685
|
|
- longjmp(restart_etherboot, -2);
|
686
|
|
- }
|
687
|
|
- }
|
688
|
|
-#endif
|
689
|
|
- done(1);
|
690
|
|
- /* Fixup the offset to the program header so you can find the program headers from
|
691
|
|
- * the ELF header mknbi needs this.
|
692
|
|
- */
|
693
|
|
- estate.e.elf64.e_phoff = (char *)&estate.p - (char *)&estate.e;
|
694
|
|
- elf_boot(machine,entry);
|
695
|
|
- }
|
696
|
|
- return skip_sectors;
|
697
|
|
-}
|
698
|
|
-
|
699
|
|
-#endif /* ELF64_IMAGE */
|