Imported Upstream version 3.10
[debian/elilo] / x86_64 / plain_loader.c
1 /*
2  *  Copyright (C) 2001-2002 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  *  Copyright (C) 2006-2009 Intel Corporation
9  *      Contributed by Fenghua Yu <fenghua.yu@intel.com>
10  *      Contributed by Bibo Mao <bibo.mao@intel.com>
11  *      Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
12  *
13  * This file is part of the ELILO, the EFI Linux boot loader.
14  *
15  *  ELILO is free software; you can redistribute it and/or modify
16  *  it under the terms of the GNU General Public License as published by
17  *  the Free Software Foundation; either version 2, or (at your option)
18  *  any later version.
19  *
20  *  ELILO is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *  GNU General Public License for more details.
24  *
25  *  You should have received a copy of the GNU General Public License
26  *  along with ELILO; see the file COPYING.  If not, write to the Free
27  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
28  *  02111-1307, USA.
29  *
30  * Please check out the elilo.txt for complete documentation on how
31  * to use this program.
32  */
33
34 #include <efi.h>
35 #include <efilib.h>
36
37 #include "elilo.h"
38 #include "loader.h"
39 #include "elf.h"
40 #include "private.h"
41
42 #define LD_NAME L"plain_elf64"
43
44 static INTN
45 is_valid_header(Elf64_Ehdr *ehdr)
46 {
47         UINT16 type, machine;
48
49         type    = ehdr->e_type;
50         machine = ehdr->e_machine;
51
52         DBG_PRT((L"class=%d type=%d data=%d machine=%d\n", 
53                 ehdr->e_ident[EI_CLASS],
54                 type,
55                 ehdr->e_ident[EI_DATA],
56                 machine));
57
58         return    ehdr->e_ident[EI_MAG0]  == 0x7f 
59                && ehdr->e_ident[EI_MAG1]  == 'E'
60                && ehdr->e_ident[EI_MAG2]  == 'L'
61                && ehdr->e_ident[EI_MAG3]  == 'F'
62                && ehdr->e_ident[EI_CLASS] == ELFCLASS64 
63                && type                    == ET_EXEC    /* must be executable */
64                && machine                 == EM_X86_64? 0 : -1;
65 }
66
67 static INTN
68 plain_probe(CHAR16 *kname)
69 {
70         Elf64_Ehdr ehdr;
71         EFI_STATUS status;
72         INTN ret = -1;
73         fops_fd_t fd;
74         UINTN size = sizeof(ehdr);
75
76         status = fops_open(kname, &fd);
77         if (EFI_ERROR(status)) 
78                 return -1;
79
80         VERB_PRT(3, {
81                 Print(L"plain_probe: kname=%s\n", kname);
82         });
83         status = fops_read(fd, &ehdr, &size);
84         if (EFI_ERROR(status) || size != sizeof(ehdr)) 
85                 goto error;
86
87         ret = is_valid_header(&ehdr);
88 error:
89         fops_close(fd);
90         return ret;
91 }
92
93
94 static INTN
95 load_elf(fops_fd_t fd, kdesc_t *kd)
96 {
97         Elf64_Ehdr ehdr;
98         Elf64_Phdr *phdrs;
99         EFI_STATUS status;
100         INTN ret = ELILO_LOAD_ERROR;
101         UINTN i, total_size = 0;
102         UINTN pages, size, bss_sz, osize;
103         VOID *low_addr = (VOID *)~0;
104         VOID *max_addr = (VOID *)0;
105         UINTN paddr, memsz, filesz;
106         UINT16 phnum;
107
108         Print(L"Loading Linux... ");
109
110         size = sizeof(ehdr);
111
112         status = fops_read(fd, &ehdr, &size);
113         if (EFI_ERROR(status) || size < sizeof(ehdr)) 
114                 return ELILO_LOAD_ERROR;
115
116         if (is_valid_header(&ehdr) == -1) {
117                 ERR_PRT((L"%s : not a 64-bit ELF image\n", LD_NAME));
118                 return ELILO_LOAD_ERROR;
119         }        
120         VERB_PRT(3, {
121                 Print(L"ELF Header information: \n");
122                 Print(L"\tEntry point "PTR_FMT"\n", (ehdr.e_entry & PADDR_MASK));
123                 Print(L"\t%d program headers\n", ehdr.e_phnum);
124                 Print(L"\t%d segment headers\n", ehdr.e_shnum);
125         });
126
127         phnum = ehdr.e_phnum;
128
129         if (fops_seek(fd, ehdr.e_phoff) < 0) {
130                 ERR_PRT((L"%s : seek to %d for phdrs failed", LD_NAME, ehdr.e_phoff));
131                 return ELILO_LOAD_ERROR;
132         }
133         size = osize = (phnum * sizeof(Elf64_Phdr));
134
135         DBG_PRT((L"%s : allocate %d bytes for %d pheaders each of size:%d phentsize=%d\n", 
136                 LD_NAME, size, phnum, sizeof(Elf64_Phdr), ehdr.e_phentsize));
137
138         phdrs = (Elf64_Phdr *)alloc(size, 0); 
139         if (phdrs == NULL) {
140                 ERR_PRT((L"%s : allocate for phdrs failed", LD_NAME));
141                 return ELILO_LOAD_ERROR;
142         }
143         status = fops_read(fd, phdrs, &size);
144         if (EFI_ERROR(status) || size != osize) {
145                 ERR_PRT((L"%s : phdr load failed", LD_NAME, status));
146                 goto out;
147         }
148         /*
149          * First pass to figure out total memory footprint
150          */
151         for (i = 0; i < phnum; i++) {
152
153                 paddr = (phdrs[i].p_paddr & PADDR_MASK);
154                 memsz = phdrs[i].p_memsz;
155
156                 DBG_PRT((L"Phdr %d paddr ["PTR_FMT"-"PTR_FMT"] offset "PTR_FMT""
157                         " filesz "PTR_FMT" memsz="PTR_FMT" bss_sz="PTR_FMT" p_type="PTR_FMT"\n",
158                         1+i, paddr, paddr+phdrs[i].p_filesz, phdrs[i].p_offset, 
159                         phdrs[i].p_filesz, memsz, 
160                         (memsz - phdrs[i].p_filesz), phdrs[i].p_type));
161         
162                 if (phdrs[i].p_type != PT_LOAD)
163                         continue;
164                 if (paddr < (UINTN)low_addr) 
165                         low_addr = (VOID *)paddr;
166                 if (paddr + memsz > (UINTN)max_addr) 
167                         max_addr = (VOID *)paddr + memsz;
168         }
169
170         if ((UINTN)low_addr & (EFI_PAGE_SIZE - 1)) {
171                 ERR_PRT((L"%s : kernel low address "PTR_FMT" not page aligned\n", 
172                         LD_NAME, low_addr));
173                 goto out;
174         }
175         /* how many bytes are needed to hold the kernel? */
176         total_size = (UINTN)max_addr - (UINTN)low_addr;
177
178         /* round up to get required number of pages */
179         pages = EFI_SIZE_TO_PAGES(total_size);
180
181         /* keep track of location where kernel starts and ends */
182         kd->kstart = low_addr;
183         kd->kend   = (low_addr + (pages << EFI_PAGE_SHIFT));
184         kd->kentry = (VOID *)(ehdr.e_entry & PADDR_MASK);
185         
186         VERB_PRT(3, {
187                 Print(L"Lowest PhysAddr: "PTR_FMT"\nTotalMemSize:%d bytes (%d pages)\n",
188                         low_addr, total_size, pages);
189                 Print(L"Kernel entry @ "PTR_FMT"\n", kd->kentry);
190         });
191         
192         /* now allocate memory for the kernel at the exact requested spot */
193         if (alloc_kmem(low_addr, pages) == -1) {
194                 ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", 
195                         LD_NAME, pages, low_addr));
196                 ERR_PRT((L"%s : Could not alloc %d pages for the kernel at 0x%lx "
197                         " and relocation is not not been implemented!\n", 
198                         LD_NAME, pages, low_addr));
199                 goto load_abort;
200         }
201         /* Pure paranoia.  Clear the memory first.  Just in case... */
202         Memset(low_addr, 0, (pages << EFI_PAGE_SHIFT));
203
204         VERB_PRT(1, Print(L"Press any key to interrupt\n"));
205
206         /* 
207          * Walk through the program headers
208          * and actually load data into physical memory
209          */
210
211         for (i = 0; i < phnum; i++) {
212                 /* Check for pure loadable segment; ignore if not loadable */
213                 if (phdrs[i].p_type != PT_LOAD)
214                         continue;
215
216                 VERB_PRT(3, Print(L"poffs: "PTR_FMT" (phdrs[%d].p_offset)\n", 
217                         phdrs[i].p_offset, i));
218
219                 filesz = phdrs[i].p_filesz;
220                 low_addr = (VOID *)((UINTN) phdrs[i].p_paddr & PADDR_MASK);
221
222                  /* Move to the right position */
223                 if (fops_seek(fd, phdrs[i].p_offset) < 0)
224                         goto out_kernel;
225
226                  /* How many BSS bytes to clear */
227                 bss_sz = phdrs[i].p_memsz - filesz;
228
229                 VERB_PRT(4, {
230                         Print(L"\nHeader #%d\n", i);
231                         Print(L"Offset in file "PTR_FMT"\n", phdrs[i].p_offset);
232                         Print(L"Physical addr "PTR_FMT"\n", low_addr);
233                         Print(L"BSS size %d bytes\n", bss_sz);
234                 });
235
236                 /*
237                  * Read actual segment into memory
238                  */
239                 ret = fops_read(fd, low_addr, &filesz);
240                 if (ret == ELILO_LOAD_ABORTED) goto load_abort;
241                 if (ret == ELILO_LOAD_ERROR) goto out;
242
243                 /*
244                  * Clear bss section
245                  */
246                 if (bss_sz) 
247                         Memset((VOID *)low_addr+filesz, 0, bss_sz);
248         }
249
250         free(phdrs);
251         return ELILO_LOAD_SUCCESS;
252
253 load_abort:
254         Print(L"..Aborted\n");
255         ret = ELILO_LOAD_ABORTED;
256 out_kernel:
257         /* free kernel memory */
258         free_kmem();
259 out:
260         free(phdrs);
261         return ret;
262 }
263
264 static INTN
265 plain_load_kernel(CHAR16 *kname, kdesc_t *kd)
266 {       
267         INTN ret;
268         fops_fd_t fd;
269         EFI_STATUS status;
270
271         /*
272          * Moving the open here simplifies the load_elf() error handling
273          */
274         status = fops_open(kname, &fd);
275         if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
276
277         Print(L"Loading %s...", kname);
278
279         ret = load_elf(fd, kd);
280         
281         fops_close(fd);
282         return ret;
283 }
284
285 loader_ops_t plain_loader={
286         NULL,
287         LD_NAME,
288         plain_probe,
289         plain_load_kernel
290 };
291
292 /*void plain_loader_init()
293 {
294         loader_ops_t plain={
295         NULL,
296         LD_NAME,
297         plain_probe,
298         plain_load_kernel
299         };
300         *plain_loader=*plain;
301 }*/
302