Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libelf / elf.c
diff --git a/qemu/roms/SLOF/lib/libelf/elf.c b/qemu/roms/SLOF/lib/libelf/elf.c
new file mode 100644 (file)
index 0000000..db2d2ab
--- /dev/null
@@ -0,0 +1,190 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * ELF loader
+ */
+
+#include <string.h>
+#include <cache.h>
+#include <libelf.h>
+#include <byteorder.h>
+
+/**
+ * elf_check_file tests if the file at file_addr is
+ * a correct endian, ELF PPC executable
+ * @param file_addr  pointer to the start of the ELF file
+ * @return           the class (1 for 32 bit, 2 for 64 bit)
+ *                   -1 if it is not an ELF file
+ *                   -2 if it has the wrong endianness
+ *                   -3 if it is not an ELF executable
+ *                   -4 if it is not for PPC
+ */
+static int
+elf_check_file(unsigned long *file_addr)
+{
+       struct ehdr *ehdr = (struct ehdr *) file_addr;
+       uint8_t native_endian;
+
+       /* check if it is an ELF image at all */
+       if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
+               return -1;
+
+#ifdef __BIG_ENDIAN__
+       native_endian = ELFDATA2MSB;
+#else
+       native_endian = ELFDATA2LSB;
+#endif
+
+       if (native_endian != ehdr->ei_data) {
+               switch (ehdr->ei_class) {
+               case 1:
+                       elf_byteswap_header32(file_addr);
+                       break;
+               case 2:
+                       elf_byteswap_header64(file_addr);
+                       break;
+               }
+       }
+
+       /* check if it is an ELF executable ... and also
+        * allow DYN files, since this is specified by ePAPR */
+       if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+               return -3;
+
+       /* check if it is a PPC ELF executable */
+       if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15)
+               return -4;
+
+       return ehdr->ei_class;
+}
+
+/**
+ * load_elf_file tries to load the ELF file specified in file_addr
+ *
+ * it first checks if the file is a PPC ELF executable and then loads
+ * the segments depending if it is a 64bit or 32 bit ELF file
+ *
+ * @param file_addr  pointer to the start of the elf file
+ * @param entry      pointer where the ELF loader will store
+ *                   the entry point
+ * @param pre_load   handler that is called before copying a segment
+ * @param post_load  handler that is called after copying a segment
+ * @return           1 for a 32 bit file
+ *                   2 for a 64 bit BE file
+ *                   3 for a 64 bit LE ABIv1 file
+ *                   4 for a 64 bit LE ABIv2 file
+ *                   5 for a 32 bit LE ABIv1 file
+ *                   anything else means an error during load
+ */
+int
+elf_load_file(void *file_addr, unsigned long *entry,
+              int (*pre_load)(void*, long),
+              void (*post_load)(void*, long))
+{
+       int type = elf_check_file(file_addr);
+       struct ehdr *ehdr = (struct ehdr *) file_addr;
+
+       switch (type) {
+       case 1:
+               *entry = elf_load_segments32(file_addr, 0, pre_load, post_load);
+               if (ehdr->ei_data != ELFDATA2MSB) {
+                       type = 5; /* LE32 ABIv1 */
+               }
+               break;
+       case 2:
+               *entry = elf_load_segments64(file_addr, 0, pre_load, post_load);
+               if (ehdr->ei_data != ELFDATA2MSB) {
+                       uint32_t flags = elf_get_eflags_64(file_addr);
+                       if ((flags & 0x3) == 2)
+                               type = 4; /* LE64 ABIv2 */
+                       else
+                               type = 3; /* LE64 ABIv1 */
+               }
+               break;
+       }
+       if (*entry == 0)
+               type = 0;
+
+       return type;
+}
+
+
+/**
+ * load_elf_file_to_addr loads an ELF file to given address.
+ * This is useful for 64-bit vmlinux images that use the virtual entry
+ * point address in their headers, and thereby need a special treatment.
+ *
+ * @param file_addr  pointer to the start of the elf file
+ * @param entry      pointer where the ELF loader will store
+ *                   the entry point
+ * @param pre_load   handler that is called before copying a segment
+ * @param post_load  handler that is called after copying a segment
+ * @return           1 for a 32 bit file
+ *                   2 for a 64 bit file
+ *                   anything else means an error during load
+ */
+int
+elf_load_file_to_addr(void *file_addr, void *addr, unsigned long *entry,
+                      int (*pre_load)(void*, long),
+                      void (*post_load)(void*, long))
+{
+       int type;
+       long offset;
+
+       type = elf_check_file(file_addr);
+
+       switch (type) {
+       case 1:
+               /* Parse 32-bit image */
+               offset = (long)addr - elf_get_base_addr32(file_addr);
+               *entry = elf_load_segments32(file_addr, offset, pre_load,
+                                            post_load) + offset;
+               // TODO: elf_relocate32(...)
+               break;
+       case 2:
+               /* Parse 64-bit image */
+               offset = (long)addr - elf_get_base_addr64(file_addr);
+               *entry = elf_load_segments64(file_addr, offset, pre_load,
+                                            post_load) + offset;
+               elf_relocate64(file_addr, offset);
+               break;
+       }
+
+       return type;
+}
+
+
+/**
+ * Get the base load address of the ELF image
+ * @return  The base address or -1 for error
+ */
+long
+elf_get_base_addr(void *file_addr)
+{
+       int type;
+
+       type = elf_check_file(file_addr);
+
+       switch (type) {
+       case 1:
+               /* Return 32-bit image base address */
+               return elf_get_base_addr32(file_addr);
+               break;
+       case 2:
+               /* Return 64-bit image base address */
+               return elf_get_base_addr64(file_addr);
+               break;
+       }
+
+       return -1;
+}