Imported Upstream version 3.10
[debian/elilo] / ia32 / 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  * 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_elf32"
38
39 static INTN
40 is_valid_header(Elf32_Ehdr *ehdr)
41 {
42         UINT16 type, machine;
43
44         type    = ehdr->e_type;
45         machine = ehdr->e_machine;
46
47         DBG_PRT((L"class=%d type=%d data=%d machine=%d\n", 
48                 ehdr->e_ident[EI_CLASS],
49                 type,
50                 ehdr->e_ident[EI_DATA],
51                 machine));
52
53         return    ehdr->e_ident[EI_MAG0]  == 0x7f 
54                && ehdr->e_ident[EI_MAG1]  == 'E'
55                && ehdr->e_ident[EI_MAG2]  == 'L'
56                && ehdr->e_ident[EI_MAG3]  == 'F'
57                && ehdr->e_ident[EI_CLASS] == ELFCLASS32 
58                && type                    == ET_EXEC    /* must be executable */
59                && machine                 == EM_386 ? 0 : -1;
60 }
61
62 static INTN
63 plain_probe(CHAR16 *kname)
64 {
65         Elf32_Ehdr ehdr;
66         EFI_STATUS status;
67         INTN ret = -1;
68         fops_fd_t fd;
69         UINTN size = sizeof(ehdr);
70
71         status = fops_open(kname, &fd);
72         if (EFI_ERROR(status)) 
73                 return -1;
74
75         status = fops_read(fd, &ehdr, &size);
76         if (EFI_ERROR(status) || size != sizeof(ehdr)) 
77                 goto error;
78
79         ret = is_valid_header(&ehdr);
80 error:
81         fops_close(fd);
82         return ret;
83 }
84
85
86 static INTN
87 load_elf(fops_fd_t fd, kdesc_t *kd)
88 {
89         Elf32_Ehdr ehdr;
90         Elf32_Phdr *phdrs;
91         EFI_STATUS status;
92         INTN ret = ELILO_LOAD_ERROR;
93         UINTN i, total_size = 0;
94         UINTN pages, size, bss_sz, osize;
95         VOID *low_addr = (VOID *)~0;
96         VOID *max_addr = (VOID *)0;
97         UINTN paddr, memsz, filesz;
98         UINT16 phnum;
99
100         Print(L"Loading Linux... ");
101
102         size = sizeof(ehdr);
103
104         status = fops_read(fd, &ehdr, &size);
105         if (EFI_ERROR(status) || size < sizeof(ehdr)) 
106                 return ELILO_LOAD_ERROR;
107
108         if (is_valid_header(&ehdr) == -1) {
109                 ERR_PRT((L"%s : not a 32-bit ELF image\n", LD_NAME));
110                 return ELILO_LOAD_ERROR;
111         }        
112         VERB_PRT(3, {
113                 Print(L"ELF Header information: \n");
114                 Print(L"\tEntry point "PTR_FMT"\n", (ehdr.e_entry & PADDR_MASK));
115                 Print(L"\t%d program headers\n", ehdr.e_phnum);
116                 Print(L"\t%d segment headers\n", ehdr.e_shnum);
117         });
118
119         phnum = ehdr.e_phnum;
120
121         if (fops_seek(fd, ehdr.e_phoff) < 0) {
122                 ERR_PRT((L"%s : seek to %d for phdrs failed", LD_NAME, ehdr.e_phoff));
123                 return ELILO_LOAD_ERROR;
124         }
125         size = osize = (phnum * sizeof(Elf32_Phdr));
126
127         DBG_PRT((L"%s : allocate %d bytes for %d pheaders each of size:%d phentsize=%d\n", 
128                 LD_NAME, size, phnum, sizeof(Elf32_Phdr), ehdr.e_phentsize));
129
130         phdrs = (Elf32_Phdr *)alloc(size, 0); 
131         if (phdrs == NULL) {
132                 ERR_PRT((L"%s : allocate for phdrs failed", LD_NAME));
133                 return ELILO_LOAD_ERROR;
134         }
135         status = fops_read(fd, phdrs, &size);
136         if (EFI_ERROR(status) || size != osize) {
137                 ERR_PRT((L"%s : phdr load failed", LD_NAME, status));
138                 goto out;
139         }
140         /*
141          * First pass to figure out total memory footprint
142          */
143         for (i = 0; i < phnum; i++) {
144
145                 paddr = (phdrs[i].p_paddr & PADDR_MASK);
146                 memsz = phdrs[i].p_memsz;
147
148                 DBG_PRT((L"Phdr %d paddr ["PTR_FMT"-"PTR_FMT"] offset "PTR_FMT""
149                         " filesz "PTR_FMT" memsz="PTR_FMT" bss_sz="PTR_FMT" p_type="PTR_FMT"\n",
150                         1+i, paddr, paddr+phdrs[i].p_filesz, phdrs[i].p_offset, 
151                         phdrs[i].p_filesz, memsz, 
152                         (memsz - phdrs[i].p_filesz), phdrs[i].p_type));
153         
154                 if (phdrs[i].p_type != PT_LOAD)
155                         continue;
156                 if (paddr < (UINTN)low_addr) 
157                         low_addr = (VOID *)paddr;
158                 if (paddr + memsz > (UINTN)max_addr) 
159                         max_addr = (VOID *)paddr + memsz;
160         }
161
162         if ((UINTN)low_addr & (EFI_PAGE_SIZE - 1)) {
163                 ERR_PRT((L"%s : kernel low address "PTR_FMT" not page aligned\n", 
164                         LD_NAME, low_addr));
165                 goto out;
166         }
167         /* how many bytes are needed to hold the kernel? */
168         total_size = (UINTN)max_addr - (UINTN)low_addr;
169
170         /* round up to get required number of pages */
171         pages = EFI_SIZE_TO_PAGES(total_size);
172
173         /* keep track of location where kernel starts and ends */
174         kd->kstart = low_addr;
175         kd->kend   = (low_addr + (pages << EFI_PAGE_SHIFT));
176         kd->kentry = (VOID *)(ehdr.e_entry & PADDR_MASK);
177         
178         VERB_PRT(3, {
179                 Print(L"Lowest PhysAddr: "PTR_FMT"\nTotalMemSize:%d bytes (%d pages)\n",
180                         low_addr, total_size, pages);
181                 Print(L"Kernel entry @ "PTR_FMT"\n", kd->kentry);
182         });
183         
184         /* now allocate memory for the kernel at the exact requested spot */
185         if (alloc_kmem(low_addr, pages) == -1) {
186                 ERR_PRT((L"%s : AllocatePages(%d, "PTR_FMT") for kernel failed\n", 
187                         LD_NAME, pages, low_addr));
188                 ERR_PRT((L"%s : Could not alloc %d pages for the kernel at "PTR_FMT""
189                         " and relocation is not not been implemented!\n", 
190                         LD_NAME, pages, low_addr));
191                 goto load_abort;
192         }
193         /* Pure paranoia.  Clear the memory first.  Just in case... */
194         Memset(low_addr, 0, (pages << EFI_PAGE_SHIFT));
195
196         VERB_PRT(1, Print(L"Press any key to interrupt\n"));
197
198         /* 
199          * Walk through the program headers
200          * and actually load data into physical memory
201          */
202
203         for (i = 0; i < phnum; i++) {
204
205                 /* Check for pure loadable segment; ignore if not loadable */
206                 if (phdrs[i].p_type != PT_LOAD)
207                         continue;
208
209                 VERB_PRT(3, Print(L"poffs: "PTR_FMT" (phdrs[%d].p_offset)\n", 
210                         phdrs[i].p_offset, i));
211
212                 filesz = phdrs[i].p_filesz;
213                 low_addr = (VOID *)((UINTN) phdrs[i].p_paddr & PADDR_MASK);
214
215                  /* Move to the right position */
216                 if (fops_seek(fd, phdrs[i].p_offset) < 0)
217                         goto out_kernel;
218
219                  /* How many BSS bytes to clear */
220                 bss_sz = phdrs[i].p_memsz - filesz;
221
222                 VERB_PRT(4, {
223                         Print(L"\nHeader #%d\n", i);
224                         Print(L"Offset in file "PTR_FMT"\n", phdrs[i].p_offset);
225                         Print(L"Physical addr "PTR_FMT"\n", low_addr);
226                         Print(L"BSS size %d bytes\n", bss_sz);
227                 });
228
229                 /*
230                  * Read actual segment into memory
231                  */
232                 ret = fops_read(fd, low_addr, &filesz);
233                 if (ret == ELILO_LOAD_ABORTED) goto load_abort;
234                 if (ret == ELILO_LOAD_ERROR) goto out;
235
236                 /*
237                  * Clear bss section
238                  */
239                 if (bss_sz) 
240                         Memset((VOID *)low_addr+filesz, 0, bss_sz);
241         }
242
243         free(phdrs);
244
245         Print(L"..Done\n");
246         return ELILO_LOAD_SUCCESS;
247
248 load_abort:
249         Print(L"..Aborted\n");
250         ret = ELILO_LOAD_ABORTED;
251 out_kernel:
252         /* free kernel memory */
253         free_kmem();
254 out:
255         free(phdrs);
256         return ret;
257 }
258
259 static INTN
260 plain_load_kernel(CHAR16 *kname, kdesc_t *kd)
261 {       
262         INTN ret;
263         fops_fd_t fd;
264         EFI_STATUS status;
265
266         /*
267          * Moving the open here simplifies the load_elf() error handling
268          */
269         status = fops_open(kname, &fd);
270         if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
271
272         Print(L"Loading %s...", kname);
273
274         ret = load_elf(fd, kd);
275         
276         fops_close(fd);
277         return ret;
278 }
279
280 loader_ops_t plain_loader={
281         NULL,
282         LD_NAME,
283         plain_probe,
284         plain_load_kernel
285 };