Imported Upstream version 3.7
[debian/elilo] / x86_64 / plain_loader.c
diff --git a/x86_64/plain_loader.c b/x86_64/plain_loader.c
new file mode 100644 (file)
index 0000000..13cc9e5
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ *  Copyright (C) 2001-2002 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *      Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ *  Copyright (C) 2006-2009 Intel Corporation
+ *     Contributed by Fenghua Yu <fenghua.yu@intel.com>
+ *     Contributed by Bibo Mao <bibo.mao@intel.com>
+ *     Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "loader.h"
+#include "elf.h"
+#include "private.h"
+
+#define LD_NAME L"plain_elf64"
+
+static INTN
+is_valid_header(Elf64_Ehdr *ehdr)
+{
+       UINT16 type, machine;
+
+       type    = ehdr->e_type;
+       machine = ehdr->e_machine;
+
+       DBG_PRT((L"class=%d type=%d data=%d machine=%d\n", 
+               ehdr->e_ident[EI_CLASS],
+               type,
+               ehdr->e_ident[EI_DATA],
+               machine));
+
+       return    ehdr->e_ident[EI_MAG0]  == 0x7f 
+              && ehdr->e_ident[EI_MAG1]  == 'E'
+              && ehdr->e_ident[EI_MAG2]  == 'L'
+              && ehdr->e_ident[EI_MAG3]  == 'F'
+              && ehdr->e_ident[EI_CLASS] == ELFCLASS64 
+              && type                    == ET_EXEC    /* must be executable */
+              && machine                 == EM_X86_64? 0 : -1;
+}
+
+static INTN
+plain_probe(CHAR16 *kname)
+{
+       Elf64_Ehdr ehdr;
+       EFI_STATUS status;
+       INTN ret = -1;
+       fops_fd_t fd;
+       UINTN size = sizeof(ehdr);
+
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) 
+               return -1;
+
+       VERB_PRT(3, {
+               Print(L"plain_probe: kname=%s\n", kname);
+       });
+       status = fops_read(fd, &ehdr, &size);
+       if (EFI_ERROR(status) || size != sizeof(ehdr)) 
+               goto error;
+
+       ret = is_valid_header(&ehdr);
+error:
+       fops_close(fd);
+       return ret;
+}
+
+
+static INTN
+load_elf(fops_fd_t fd, kdesc_t *kd)
+{
+       Elf64_Ehdr ehdr;
+       Elf64_Phdr *phdrs;
+       EFI_STATUS status;
+       INTN ret = ELILO_LOAD_ERROR;
+       UINTN i, total_size = 0;
+       UINTN pages, size, bss_sz, osize;
+       VOID *low_addr = (VOID *)~0;
+       VOID *max_addr = (VOID *)0;
+       UINTN paddr, memsz, filesz;
+       UINT16 phnum;
+
+       Print(L"Loading Linux... ");
+
+       size = sizeof(ehdr);
+
+       status = fops_read(fd, &ehdr, &size);
+       if (EFI_ERROR(status) || size < sizeof(ehdr)) 
+               return ELILO_LOAD_ERROR;
+
+       if (is_valid_header(&ehdr) == -1) {
+               ERR_PRT((L"%s : not a 64-bit ELF image\n", LD_NAME));
+               return ELILO_LOAD_ERROR;
+       }        
+       VERB_PRT(3, {
+               Print(L"ELF Header information: \n");
+               Print(L"\tEntry point 0x%x\n", (ehdr.e_entry & PADDR_MASK));
+               Print(L"\t%d program headers\n", ehdr.e_phnum);
+               Print(L"\t%d segment headers\n", ehdr.e_shnum);
+       });
+
+       phnum = ehdr.e_phnum;
+
+       if (fops_seek(fd, ehdr.e_phoff) < 0) {
+               ERR_PRT((L"%s : seek to %d for phdrs failed", LD_NAME, ehdr.e_phoff));
+               return ELILO_LOAD_ERROR;
+       }
+       size = osize = (phnum * sizeof(Elf64_Phdr));
+
+       DBG_PRT((L"%s : allocate %d bytes for %d pheaders each of size:%d phentsize=%d\n", 
+               LD_NAME, size, phnum, sizeof(Elf64_Phdr), ehdr.e_phentsize));
+
+       phdrs = (Elf64_Phdr *)alloc(size, 0); 
+       if (phdrs == NULL) {
+               ERR_PRT((L"%s : allocate for phdrs failed", LD_NAME));
+               return ELILO_LOAD_ERROR;
+       }
+       status = fops_read(fd, phdrs, &size);
+       if (EFI_ERROR(status) || size != osize) {
+               ERR_PRT((L"%s : phdr load failed", LD_NAME, status));
+               goto out;
+       }
+       /*
+        * First pass to figure out total memory footprint
+        */
+       for (i = 0; i < phnum; i++) {
+
+               paddr = (phdrs[i].p_paddr & PADDR_MASK);
+               memsz = phdrs[i].p_memsz;
+
+               DBG_PRT((L"Phdr %d paddr [0x%x-0x%x] offset 0x%x"
+                       " filesz 0x%x memsz=0x%x bss_sz=0x%x p_type=0x%x\n",
+                       1+i, paddr, paddr+phdrs[i].p_filesz, phdrs[i].p_offset, 
+                       phdrs[i].p_filesz, memsz, 
+                       (memsz - phdrs[i].p_filesz), phdrs[i].p_type));
+       
+               if (phdrs[i].p_type != PT_LOAD)
+                       continue;
+               if (paddr < (UINTN)low_addr) 
+                       low_addr = (VOID *)paddr;
+               if (paddr + memsz > (UINTN)max_addr) 
+                       max_addr = (VOID *)paddr + memsz;
+       }
+
+       if ((UINTN)low_addr & (EFI_PAGE_SIZE - 1)) {
+               ERR_PRT((L"%s : kernel low address 0x%x not page aligned\n", 
+                       LD_NAME, low_addr));
+               goto out;
+       }
+       /* how many bytes are needed to hold the kernel? */
+       total_size = (UINTN)max_addr - (UINTN)low_addr;
+
+       /* round up to get required number of pages */
+       pages = EFI_SIZE_TO_PAGES(total_size);
+
+       /* keep track of location where kernel starts and ends */
+       kd->kstart = low_addr;
+       kd->kend   = (low_addr + (pages << EFI_PAGE_SHIFT));
+       kd->kentry = (VOID *)(ehdr.e_entry & PADDR_MASK);
+       
+       VERB_PRT(3, {
+               Print(L"Lowest PhysAddr: 0x%x\nTotalMemSize:%d bytes (%d pages)\n",
+                       low_addr, total_size, pages);
+               Print(L"Kernel entry @ 0x%x\n", kd->kentry);
+       });
+       
+       /* now allocate memory for the kernel at the exact requested spot */
+       if (alloc_kmem(low_addr, pages) == -1) {
+               ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", 
+                       LD_NAME, pages, low_addr));
+               ERR_PRT((L"%s : Could not alloc %d pages for the kernel at 0x%lx "
+                       " and relocation is not not been implemented!\n", 
+                       LD_NAME, pages, low_addr));
+               goto load_abort;
+       }
+       /* Pure paranoia.  Clear the memory first.  Just in case... */
+       Memset(low_addr, 0, (pages << EFI_PAGE_SHIFT));
+
+       VERB_PRT(1, Print(L"Press any key to interrupt\n"));
+
+       /* 
+        * Walk through the program headers
+        * and actually load data into physical memory
+        */
+
+       for (i = 0; i < phnum; i++) {
+               /* Check for pure loadable segment; ignore if not loadable */
+               if (phdrs[i].p_type != PT_LOAD)
+                       continue;
+
+               VERB_PRT(3, Print(L"poffs: 0x%x (phdrs[%d].p_offset)\n", 
+                       phdrs[i].p_offset, i));
+
+               filesz = phdrs[i].p_filesz;
+               low_addr = (VOID *)((UINTN) phdrs[i].p_paddr & PADDR_MASK);
+
+                /* Move to the right position */
+               if (fops_seek(fd, phdrs[i].p_offset) < 0)
+                       goto out_kernel;
+
+                /* How many BSS bytes to clear */
+               bss_sz = phdrs[i].p_memsz - filesz;
+
+               VERB_PRT(4, {
+                       Print(L"\nHeader #%d\n", i);
+                       Print(L"Offset in file 0x%x\n", phdrs[i].p_offset);
+                       Print(L"Physical addr 0x%x\n", low_addr);
+                       Print(L"BSS size 0x%x bytes\n", bss_sz);
+               });
+
+               /*
+                * Read actual segment into memory
+                */
+               ret = fops_read(fd, low_addr, &filesz);
+               if (ret == ELILO_LOAD_ABORTED) goto load_abort;
+               if (ret == ELILO_LOAD_ERROR) goto out;
+
+               /*
+                * Clear bss section
+                */
+               if (bss_sz) 
+                       Memset((VOID *)low_addr+filesz, 0, bss_sz);
+       }
+
+       free(phdrs);
+       return ELILO_LOAD_SUCCESS;
+
+load_abort:
+       Print(L"..Aborted\n");
+       ret = ELILO_LOAD_ABORTED;
+out_kernel:
+       /* free kernel memory */
+       free_kmem();
+out:
+       free(phdrs);
+       return ret;
+}
+
+static INTN
+plain_load_kernel(CHAR16 *kname, kdesc_t *kd)
+{      
+       INTN ret;
+       fops_fd_t fd;
+       EFI_STATUS status;
+
+       /*
+        * Moving the open here simplifies the load_elf() error handling
+        */
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
+
+       Print(L"Loading %s...", kname);
+
+       ret = load_elf(fd, kd);
+       
+       fops_close(fd);
+       return ret;
+}
+
+loader_ops_t plain_loader={
+       NULL,
+       LD_NAME,
+       plain_probe,
+       plain_load_kernel
+};
+
+/*void plain_loader_init()
+{
+       loader_ops_t plain={
+        NULL,
+        LD_NAME,
+        plain_probe,
+        plain_load_kernel
+       };
+       *plain_loader=*plain;
+}*/
+