Imported Upstream version 3.10
[debian/elilo] / ia64 / plain_loader.c
1 /*
2  *  Copyright (C) 2001-2003 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *
5  *  Copyright (C) 2001 Silicon Graphics, Inc.
6  *      Contributed by Brent Casavant <bcasavan@sgi.com>
7  *
8  * This file is part of the ELILO, the EFI Linux boot loader.
9  *
10  *  ELILO is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  ELILO is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with ELILO; see the file COPYING.  If not, write to the Free
22  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23  *  02111-1307, USA.
24  *
25  * Please check out the elilo.txt for complete documentation on how
26  * to use this program.
27  */
28
29 #include <efi.h>
30 #include <efilib.h>
31
32 #include "elilo.h"
33 #include "loader.h"
34 #include "elf.h"
35 #include "private.h"
36
37 #define LD_NAME L"plain_elf64"
38
39 #define PLAIN_MIN_BLOCK_SIZE    sizeof(Elf64_Ehdr) /* see load_elf() for details */
40
41 #define SKIPBUFSIZE     2048    /* minimal default size of the skip buffer */
42 static CHAR8 *skip_buffer;      /* used to skip over unneeded data */
43 static UINTN skip_bufsize;
44 static UINTN elf_is_big_endian; /* true if ELF file is big endian */
45
46 static inline UINT64 
47 bswap64(UINT64 v)
48 {
49         if(elf_is_big_endian) v = __ia64_swab64(v);
50         return v;
51 }
52
53 static inline UINT32
54 bswap32(UINT32 v)
55 {
56         if(elf_is_big_endian) v = __ia64_swab32(v);
57         return v;
58 }
59
60 static inline UINT16
61 bswap16(UINT16 v)
62 {
63         if(elf_is_big_endian) v = __ia64_swab16(v);
64         return v;
65 }
66
67 static INTN
68 is_valid_header(Elf64_Ehdr *ehdr)
69 {
70         UINT16 type, machine;
71
72         if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
73                 type    = __ia64_swab16(ehdr->e_type);
74                 machine = __ia64_swab16(ehdr->e_machine);
75         } else {
76                 type    = ehdr->e_type;
77                 machine = ehdr->e_machine;
78         }
79         DBG_PRT((L"class=%d type=%d data=%d machine=%d\n", 
80                 ehdr->e_ident[EI_CLASS],
81                 type,
82                 ehdr->e_ident[EI_DATA],
83                 machine));
84
85         return    ehdr->e_ident[EI_MAG0]  == 0x7f 
86                && ehdr->e_ident[EI_MAG1]  == 'E'
87                && ehdr->e_ident[EI_MAG2]  == 'L'
88                && ehdr->e_ident[EI_MAG3]  == 'F'
89                && ehdr->e_ident[EI_CLASS] == ELFCLASS64 
90                && type                    == ET_EXEC    /* must be executable */
91                && machine                 == EM_IA_64 ? 0 : -1;
92 }
93
94 static INTN
95 plain_probe(CHAR16 *kname)
96 {
97         Elf64_Ehdr ehdr;
98         EFI_STATUS status;
99         INTN ret = -1;
100         fops_fd_t fd;
101         UINTN size = sizeof(ehdr);
102
103         status = fops_open(kname, &fd);
104         if (EFI_ERROR(status)) return -1;
105
106         status = fops_read(fd, &ehdr, &size);
107
108         if (EFI_ERROR(status) || size != sizeof(ehdr)) goto error;
109
110         ret = is_valid_header(&ehdr);
111 error:
112         fops_close(fd);
113         return ret;
114 }
115
116 /*
117  * move skip bytes forward in the file
118  * this is required because we cannot assume fileops has
119  * seek() capabilities.
120  */
121 static INTN
122 skip_bytes(fops_fd_t fd, UINTN curpos, UINTN newpos)
123 {
124         EFI_STATUS status;
125         UINTN n, skip;
126
127         skip = newpos - curpos;
128         /* check if seek capability exists */   
129
130         status = fops_seek(fd, newpos);
131         if (status == EFI_SUCCESS) return 0;
132
133         if (status != EFI_UNSUPPORTED) goto error;
134
135         /* unsupported case */
136
137         if (skip_buffer == NULL) {
138                 skip_bufsize = MAX(skip, SKIPBUFSIZE);
139                 skip_buffer= (CHAR8 *)alloc(skip_bufsize, EfiLoaderData);
140                 if (skip_buffer == NULL) return -1;
141         }
142         while (skip) {
143                 n = skip > skip_bufsize? skip_bufsize : skip;
144
145                 status = fops_read(fd, skip_buffer, &n);
146                 if (EFI_ERROR(status)) goto error;
147
148                 skip -=n;
149         }
150         return 0;
151
152 error:
153         ERR_PRT((L"%s : cannot skip %d bytes\n", LD_NAME, n));
154         return -1;
155 }
156
157 static INTN
158 load_elf(fops_fd_t fd, kdesc_t *kd)
159 {
160         Elf64_Ehdr ehdr;
161         Elf64_Phdr *phdrs;
162         EFI_STATUS status;
163         INTN ret = ELILO_LOAD_ERROR;
164         UINTN i, total_size = 0;
165         UINTN pages, size, bss_sz, osize;
166         UINTN offs = 0;
167         VOID *low_addr = (VOID *)~0;
168         VOID *max_addr = (VOID *)0;
169         UINTN load_offset = 0;
170         UINTN paddr, memsz, filesz, poffs;
171         UINT16 phnum;
172
173         Print(L"Loading Linux... ");
174
175         size = sizeof(ehdr);
176
177         status = fops_read(fd, &ehdr, &size);
178         if (EFI_ERROR(status) ||size < sizeof(ehdr)) return ELILO_LOAD_ERROR;
179
180         offs += size;
181
182         /*
183          * do some sanity checking on the file
184          */
185         if (is_valid_header(&ehdr) == -1) {
186                 ERR_PRT((L"%s : not an elf 64-bit file\n", LD_NAME));
187                 return ELILO_LOAD_ERROR;
188         }        
189
190         /* determine file endianess */
191         elf_is_big_endian = ehdr.e_ident[EI_DATA] == ELFDATA2MSB ? 1 : 0;
192
193         VERB_PRT(3, { 
194                         Print(L"ELF file is %s\n", elf_is_big_endian ? L"big endian" : L"little endian");
195                         Print(L"Entry point 0x%lx\n", bswap64(ehdr.e_entry));
196                         Print(L"%d program headers\n", bswap16(ehdr.e_phnum));
197                         Print(L"%d segment headers\n", bswap16(ehdr.e_shnum));
198                    });
199
200         phnum = bswap16(ehdr.e_phnum);
201
202         if (skip_bytes(fd, offs, bswap64(ehdr.e_phoff)) != 0) {
203                 ERR_PRT((L"%s : skip tp %ld for phdrs failed", LD_NAME, offs));
204                 return ELILO_LOAD_ERROR;
205         }
206         offs  = bswap64(ehdr.e_phoff);
207
208         size = osize = phnum*sizeof(Elf64_Phdr);
209
210         DBG_PRT((L"%s : phdrs allocate %d bytes sizeof=%d entsize=%d\n", LD_NAME, size,sizeof(Elf64_Phdr), bswap16(ehdr.e_phentsize)));
211
212         phdrs = (Elf64_Phdr *)alloc(size, 0);
213         if (phdrs == NULL) {
214                 ERR_PRT((L"%s : allocate phdrs failed", LD_NAME));
215                 return ELILO_LOAD_ERROR;
216         }
217
218         status = fops_read(fd, phdrs, &size);
219         if (EFI_ERROR(status) || size != osize) {
220                 ERR_PRT((L"%s : load phdrs failed", LD_NAME, status));
221                 goto out;
222         }
223         offs += size;
224         /*
225          * First pass to figure out:
226          *      - lowest physical address
227          *      - total memory footprint
228          */
229         for (i = 0; i < phnum; i++) {
230
231                 paddr = bswap64(phdrs[i].p_paddr);
232                 memsz = bswap64(phdrs[i].p_memsz);
233
234                 DBG_PRT((L"Phdr %d paddr [0x%lx-0x%lx] offset %ld"
235                            " filesz %ld memsz=%ld bss_sz=%ld p_type=%d\n",
236                            1+i, 
237                            paddr, 
238                            paddr+bswap64(phdrs[i].p_filesz), 
239                            bswap64(phdrs[i].p_offset), 
240                            bswap64(phdrs[i].p_filesz), 
241                            memsz, 
242                            memsz - bswap64(phdrs[i].p_filesz), bswap32(phdrs[i].p_type)));
243         
244                 if (bswap32(phdrs[i].p_type) != PT_LOAD) continue;
245
246
247                 if (paddr < (UINTN)low_addr) low_addr = (VOID *)paddr;
248
249                 if (paddr + memsz > (UINTN)max_addr) 
250                         max_addr = (VOID *)paddr + memsz;
251         }
252
253         if ((UINTN)low_addr & (EFI_PAGE_SIZE - 1)) {
254                 ERR_PRT((L"%s : kernel low address 0x%lx not page aligned\n", LD_NAME, low_addr));
255                 goto out;
256         }
257
258         /* how many bytes are needed to hold the kernel */
259         total_size = (UINTN)max_addr - (UINTN)low_addr;
260
261         /* round up to get required number of pages */
262         pages = EFI_SIZE_TO_PAGES(total_size);
263
264         /* keep track of location where kernel ends for
265          * the initrd ramdisk (it will be put right after the kernel) 
266          */
267         kd->kstart = low_addr;
268         kd->kend   = low_addr+ (pages << EFI_PAGE_SHIFT);
269
270         /*
271          * that's the kernel entry point (virtual address)
272          */
273         kd->kentry = (VOID *)bswap64(ehdr.e_entry);
274         
275         if (((UINTN)kd->kentry >> 61) != 0) {
276                 ERR_PRT((L"%s:  <<ERROR>> entry point is a virtual address 0x%lx : not supported anymore\n", LD_NAME, kd->kentry));
277         }
278
279         VERB_PRT(3, {
280                 Print(L"Lowest PhysAddr: 0x%lx\nTotalMemSize:%d bytes (%d pages)\n",
281                         low_addr, total_size, pages);
282                 Print(L"Kernel entry @ 0x%lx\n", kd->kentry);
283         });
284
285         /*
286          * now allocate memory for the kernel at the exact requested spot
287          */
288         if (alloc_kmem(low_addr, pages) == -1) {
289                 VOID *new_addr;
290
291                 VERB_PRT(1, Print(L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));
292
293                 if (ia64_can_relocate() == 0) {
294                         ERR_PRT((L"relocation is disabled, cannot load kernel"));
295                         goto out;
296                 }
297
298                 /*
299                  * could not allocate at requested spot, try to find a
300                  * suitable location to relocate the kernel
301                  *
302                  * The maximum sized Itanium TLB translation entry is 256 MB.
303                  * If we relocate the kernel by this amount we know for sure
304                  * that alignment constraints will be satisified, regardless
305                  * of the kernel used.
306                  */
307                 Print(L"Attempting to relocate kernel.\n");
308                 if (find_kernel_memory(low_addr, max_addr, 256*MB, &new_addr) == -1) {
309                         ERR_PRT((L"%s : find_kernel_memory(0x%lx, 0x%lx, 0x%lx, 0x%lx) failed\n", LD_NAME, low_addr, max_addr, 256*MB, &load_offset));
310                         goto out;
311                 }
312                 /* unsigned arithmetic */
313                 load_offset = (UINTN) (new_addr - ROUNDDOWN((UINTN) low_addr,256*MB));
314
315                 VERB_PRT(3, Print(L"low_addr=0x%lx new_addr=0x%lx offset=0x%lx", low_addr, new_addr, load_offset));
316
317                 /*
318                  * correct various addesses for non-zero load_offset
319                  */
320                 low_addr = (VOID*) ((UINTN) low_addr + load_offset);
321                 max_addr = (VOID*) ((UINTN) max_addr + load_offset);
322                 kd->kstart = (VOID *) ((UINTN) kd->kstart + load_offset);
323                 kd->kend = (VOID *) ((UINTN) kd->kend + load_offset);
324                 kd->kentry = (VOID *) ((UINTN) kd->kentry + load_offset);
325
326                 /*
327                  * try one last time to get memory for the kernel
328                  */
329                 if (alloc_kmem(low_addr, pages) == -1) {
330                         ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));
331                         ERR_PRT((L"Relocation by 0x%lx bytes failed.\n", load_offset));
332                         goto out;
333                 }
334         }
335
336         VERB_PRT(1, Print(L"Press any key to interrupt\n"));
337
338         /* Second pass:
339          * Walk through the program headers
340          * and actually load data into physical memory
341          */
342         for (i = 0; i < phnum; i++) {
343
344                 /*
345                  * Check for pure loadable segment; ignore if not loadable
346                  */
347                 if (bswap32(phdrs[i].p_type) != PT_LOAD) continue;
348
349                 poffs = bswap64(phdrs[i].p_offset);
350
351                 size = poffs - offs;
352
353                 VERB_PRT(3, Print(L"\noff=%ld poffs=%ld size=%ld\n", offs, poffs, size));
354
355                 filesz = bswap64(phdrs[i].p_filesz);
356                 /*
357                  * correct p_paddr for non-zero load offset
358                  */
359                 phdrs[i].p_paddr = (Elf64_Addr) ((UINTN) bswap64(phdrs[i].p_paddr) + load_offset);
360
361                 /*
362                  * Move to the right position
363                  */
364                 if (size && skip_bytes(fd, offs, poffs) != 0) goto out_kernel;
365
366                 /*
367                  * Keep track of current position in file
368                  */
369                 offs += size;
370
371                 /*
372                  * How many BSS bytes to clear
373                  */
374                 bss_sz = bswap64(phdrs[i].p_memsz) - filesz;
375
376                 VERB_PRT(4, {
377                         Print(L"\nHeader #%d\n", i);
378                         Print(L"offset %ld\n", poffs);
379                         Print(L"Phys addr 0x%lx\n", phdrs[i].p_paddr); /* already endian adjusted */
380                         Print(L"BSS size %ld bytes\n", bss_sz);
381                         Print(L"skip=%ld offs=%ld\n", size, offs);
382                 });
383
384                 /*
385                  * Read actual segment into memory
386                  */
387                 ret = read_file(fd, filesz, (CHAR8 *)phdrs[i].p_paddr);
388                 if (ret == ELILO_LOAD_ABORTED) goto load_abort;
389                 if (ret == ELILO_LOAD_ERROR) goto out;
390                 if (bswap32(phdrs[i].p_flags) & PF_X)
391                         flush_dcache ((CHAR8 *)phdrs[i].p_paddr, filesz);
392
393                 /*
394                  * update file position
395                  */
396                 offs += filesz;
397
398                 /*
399                  * Clear bss section
400                  */
401                 if (bss_sz) Memset((VOID *) phdrs[i].p_paddr+filesz, 0, bss_sz);
402         }
403
404         free(phdrs);
405
406         Print(L"..done\n");
407         return ELILO_LOAD_SUCCESS;
408
409 load_abort:
410         Print(L"..Aborted\n");
411         ret = ELILO_LOAD_ABORTED;
412 out_kernel:
413         /* free kernel memory */
414         free_kmem();
415 out:
416         free(phdrs);
417         return ret;
418 }
419
420 static INTN
421 plain_load_kernel(CHAR16 *kname, kdesc_t *kd)
422 {       
423         INTN ret;
424         fops_fd_t fd;
425         EFI_STATUS status;
426
427         /*
428          * Moving the open here simplifies the load_elf() error handling
429          */
430         status = fops_open(kname, &fd);
431         if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
432
433         Print(L"Loading %s...", kname);
434
435         ret = load_elf(fd, kd);
436
437         fops_close(fd);
438
439         /*
440          * if the skip buffer was ever used, free it
441          */
442         if (skip_buffer) {
443                 free(skip_buffer);
444                 /* in case we come back */
445                 skip_buffer = NULL;
446         }
447         return ret;
448 }
449
450 loader_ops_t plain_loader={
451         NULL,
452         LD_NAME,
453         plain_probe,
454         plain_load_kernel
455 };