elf_trampoline.c revision 202908
1/*- 2 * Copyright (c) 2005 Olivier Houchard. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include <sys/cdefs.h> 26__FBSDID("$FreeBSD: head/sys/mips/mips/elf_trampoline.c 202908 2010-01-24 02:59:22Z gonzo $"); 27#include <machine/asm.h> 28#include <sys/param.h> 29 30#ifdef __mips_n64 31#include <sys/elf64.h> 32#else 33#include <sys/elf32.h> 34#endif 35#include <sys/inflate.h> 36#include <machine/elf.h> 37#include <machine/cpufunc.h> 38#include <machine/stdarg.h> 39 40/* 41 * Since we are compiled outside of the normal kernel build process, we 42 * need to include opt_global.h manually. 43 */ 44#include "opt_global.h" 45#include "opt_kernname.h" 46 47extern char kernel_start[]; 48extern char kernel_end[]; 49 50static __inline void * 51memcpy(void *dst, const void *src, size_t len) 52{ 53 const char *s = src; 54 char *d = dst; 55 56 while (len) { 57 if (0 && len >= 4 && !((vm_offset_t)d & 3) && 58 !((vm_offset_t)s & 3)) { 59 *(uint32_t *)d = *(uint32_t *)s; 60 s += 4; 61 d += 4; 62 len -= 4; 63 } else { 64 *d++ = *s++; 65 len--; 66 } 67 } 68 return (dst); 69} 70 71static __inline void 72bzero(void *addr, size_t count) 73{ 74 char *tmp = (char *)addr; 75 76 while (count > 0) { 77 if (count >= 4 && !((vm_offset_t)tmp & 3)) { 78 *(uint32_t *)tmp = 0; 79 tmp += 4; 80 count -= 4; 81 } else { 82 *tmp = 0; 83 tmp++; 84 count--; 85 } 86 } 87} 88 89/* 90 * Relocate PT_LOAD segements of kernel ELF image to their respective 91 * virtual addresses and return entry point 92 */ 93void * 94load_kernel(void * kstart) 95{ 96#ifdef __mips_n64 97 Elf64_Ehdr *eh; 98 Elf64_Phdr phdr[64] /* XXX */; 99 Elf64_Phdr shdr[64] /* XXX */; 100#else 101 Elf32_Ehdr *eh; 102 Elf32_Phdr phdr[64] /* XXX */; 103 Elf32_Shdr shdr[64] /* XXX */; 104#endif 105 int i, j; 106 void *entry_point; 107 vm_offset_t lastaddr = 0; 108 int symtabindex = -1; 109 int symstrindex = -1; 110 111#ifdef __mips_n64 112 eh = (Elf64_Ehdr *)kstart; 113#else 114 eh = (Elf32_Ehdr *)kstart; 115#endif 116 entry_point = (void*)eh->e_entry; 117 memcpy(phdr, (void *)(kstart + eh->e_phoff ), 118 eh->e_phnum * sizeof(phdr[0])); 119 120 memcpy(shdr, (void *)(kstart + eh->e_shoff), 121 sizeof(*shdr) * eh->e_shnum); 122 123 if (eh->e_shnum * eh->e_shentsize != 0 && eh->e_shoff != 0) { 124 for (i = 0; i < eh->e_shnum; i++) { 125 if (shdr[i].sh_type == SHT_SYMTAB) { 126 /* 127 * XXX: check if .symtab is in PT_LOAD? 128 */ 129 if (shdr[i].sh_offset != 0 && 130 shdr[i].sh_size != 0) { 131 symtabindex = i; 132 symstrindex = shdr[i].sh_link; 133 } 134 } 135 } 136 } 137 138 /* 139 * Copy loadable segments 140 */ 141 for (i = 0; i < eh->e_phnum; i++) { 142 volatile char c; 143 144 if (phdr[i].p_type != PT_LOAD) 145 continue; 146 147 memcpy((void *)(phdr[i].p_vaddr), 148 (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz); 149 150 /* Clean space from oversized segments, eg: bss. */ 151 if (phdr[i].p_filesz < phdr[i].p_memsz) 152 bzero((void *)(phdr[i].p_vaddr + phdr[i].p_filesz), 153 phdr[i].p_memsz - phdr[i].p_filesz); 154 155 if (lastaddr < phdr[i].p_vaddr + phdr[i].p_memsz) 156 lastaddr = phdr[i].p_vaddr + phdr[i].p_memsz; 157 } 158 159 /* Now grab the symbol tables. */ 160 if (symtabindex >= 0 && symstrindex >= 0) { 161 *(Elf_Size *)lastaddr = SYMTAB_MAGIC; 162 lastaddr += sizeof(Elf_Size); 163 *(Elf_Size *)lastaddr = shdr[symtabindex].sh_size + 164 shdr[symstrindex].sh_size + 2*sizeof(Elf_Size); 165 lastaddr += sizeof(Elf_Size); 166 /* .symtab size */ 167 *(Elf_Size *)lastaddr = shdr[symtabindex].sh_size; 168 lastaddr += sizeof(shdr[symtabindex].sh_size); 169 /* .symtab data */ 170 memcpy((void*)lastaddr, 171 shdr[symtabindex].sh_offset + kstart, 172 shdr[symtabindex].sh_size); 173 lastaddr += shdr[symtabindex].sh_size; 174 175 /* .strtab size */ 176 *(Elf_Size *)lastaddr = shdr[symstrindex].sh_size; 177 lastaddr += sizeof(shdr[symstrindex].sh_size); 178 179 /* .strtab data */ 180 memcpy((void*)lastaddr, 181 shdr[symstrindex].sh_offset + kstart, 182 shdr[symstrindex].sh_size); 183 } else 184 /* Do not take any chances */ 185 *(Elf_Size *)lastaddr = 0; 186 187 return entry_point; 188} 189 190void 191_startC(register_t a0, register_t a1, register_t a2, register_t a3) 192{ 193 unsigned int * code; 194 int i; 195 void (*entry_point)(register_t, register_t, register_t, register_t); 196 197 /* 198 * Relocate segment to the predefined memory location 199 * Most likely it will be KSEG0/KSEG1 address 200 */ 201 entry_point = load_kernel(kernel_start); 202 203 /* Pass saved registers to original _start */ 204 entry_point(a0, a1, a2, a3); 205} 206