1187418Sgonzo/*- 2187418Sgonzo * Copyright (c) 2005 Olivier Houchard. All rights reserved. 3187418Sgonzo * 4187418Sgonzo * Redistribution and use in source and binary forms, with or without 5187418Sgonzo * modification, are permitted provided that the following conditions 6187418Sgonzo * are met: 7187418Sgonzo * 1. Redistributions of source code must retain the above copyright 8187418Sgonzo * notice, this list of conditions and the following disclaimer. 9187418Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 10187418Sgonzo * notice, this list of conditions and the following disclaimer in the 11187418Sgonzo * documentation and/or other materials provided with the distribution. 12187418Sgonzo * 13187418Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14187418Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15187418Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16187418Sgonzo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17187418Sgonzo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18187418Sgonzo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19187418Sgonzo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20187418Sgonzo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21187418Sgonzo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22187418Sgonzo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23187418Sgonzo */ 24187418Sgonzo 25187418Sgonzo#include <sys/cdefs.h> 26187418Sgonzo__FBSDID("$FreeBSD$"); 27187418Sgonzo#include <machine/asm.h> 28187418Sgonzo#include <sys/param.h> 29197013Simp 30224105Sjchandra#if ELFSIZE == 64 31197014Simp#include <sys/elf64.h> 32197014Simp#else 33187418Sgonzo#include <sys/elf32.h> 34197014Simp#endif 35187418Sgonzo 36187418Sgonzo/* 37187418Sgonzo * Since we are compiled outside of the normal kernel build process, we 38187418Sgonzo * need to include opt_global.h manually. 39187418Sgonzo */ 40187418Sgonzo#include "opt_global.h" 41187418Sgonzo 42256171Sadrian#include <sys/inflate.h> 43256171Sadrian#include <machine/elf.h> 44256171Sadrian#include <machine/cpufunc.h> 45256171Sadrian#include <machine/stdarg.h> 46256171Sadrian 47216474Sjchandra#ifndef KERNNAME 48216474Sjchandra#error Kernel name not provided 49216474Sjchandra#endif 50216474Sjchandra 51187418Sgonzoextern char kernel_start[]; 52187418Sgonzoextern char kernel_end[]; 53187418Sgonzo 54187418Sgonzostatic __inline void * 55197013Simpmemcpy(void *dst, const void *src, size_t len) 56187418Sgonzo{ 57187418Sgonzo const char *s = src; 58187418Sgonzo char *d = dst; 59187418Sgonzo 60187418Sgonzo while (len) { 61187418Sgonzo if (0 && len >= 4 && !((vm_offset_t)d & 3) && 62187418Sgonzo !((vm_offset_t)s & 3)) { 63187418Sgonzo *(uint32_t *)d = *(uint32_t *)s; 64187418Sgonzo s += 4; 65187418Sgonzo d += 4; 66187418Sgonzo len -= 4; 67187418Sgonzo } else { 68187418Sgonzo *d++ = *s++; 69187418Sgonzo len--; 70187418Sgonzo } 71187418Sgonzo } 72187418Sgonzo return (dst); 73187418Sgonzo} 74187418Sgonzo 75187418Sgonzostatic __inline void 76197013Simpbzero(void *addr, size_t count) 77187418Sgonzo{ 78187418Sgonzo char *tmp = (char *)addr; 79187418Sgonzo 80187418Sgonzo while (count > 0) { 81187418Sgonzo if (count >= 4 && !((vm_offset_t)tmp & 3)) { 82187418Sgonzo *(uint32_t *)tmp = 0; 83187418Sgonzo tmp += 4; 84187418Sgonzo count -= 4; 85187418Sgonzo } else { 86187418Sgonzo *tmp = 0; 87187418Sgonzo tmp++; 88187418Sgonzo count--; 89187418Sgonzo } 90187418Sgonzo } 91187418Sgonzo} 92187418Sgonzo 93187418Sgonzo/* 94224105Sjchandra * Convert number to pointer, truncate on 64->32 case, sign extend 95224105Sjchandra * in 32->64 case 96224105Sjchandra */ 97224105Sjchandra#define mkptr(x) ((void *)(intptr_t)(int)(x)) 98224105Sjchandra 99224105Sjchandra/* 100187418Sgonzo * Relocate PT_LOAD segements of kernel ELF image to their respective 101187418Sgonzo * virtual addresses and return entry point 102187418Sgonzo */ 103187418Sgonzovoid * 104187418Sgonzoload_kernel(void * kstart) 105187418Sgonzo{ 106224105Sjchandra#if ELFSIZE == 64 107197014Simp Elf64_Ehdr *eh; 108197014Simp Elf64_Phdr phdr[64] /* XXX */; 109204052Simp Elf64_Shdr shdr[64] /* XXX */; 110197014Simp#else 111187418Sgonzo Elf32_Ehdr *eh; 112187418Sgonzo Elf32_Phdr phdr[64] /* XXX */; 113202908Sgonzo Elf32_Shdr shdr[64] /* XXX */; 114197014Simp#endif 115202908Sgonzo int i, j; 116187418Sgonzo void *entry_point; 117224105Sjchandra vm_offset_t loadend = 0; 118224105Sjchandra intptr_t lastaddr; 119202908Sgonzo int symtabindex = -1; 120202908Sgonzo int symstrindex = -1; 121224105Sjchandra Elf_Size tmp; 122187418Sgonzo 123224105Sjchandra#if ELFSIZE == 64 124197014Simp eh = (Elf64_Ehdr *)kstart; 125197014Simp#else 126187418Sgonzo eh = (Elf32_Ehdr *)kstart; 127197014Simp#endif 128224105Sjchandra entry_point = mkptr(eh->e_entry); 129224105Sjchandra memcpy(phdr, (void *)(kstart + eh->e_phoff), 130187418Sgonzo eh->e_phnum * sizeof(phdr[0])); 131187418Sgonzo 132202908Sgonzo memcpy(shdr, (void *)(kstart + eh->e_shoff), 133202908Sgonzo sizeof(*shdr) * eh->e_shnum); 134202908Sgonzo 135202908Sgonzo if (eh->e_shnum * eh->e_shentsize != 0 && eh->e_shoff != 0) { 136202908Sgonzo for (i = 0; i < eh->e_shnum; i++) { 137202908Sgonzo if (shdr[i].sh_type == SHT_SYMTAB) { 138202908Sgonzo /* 139202908Sgonzo * XXX: check if .symtab is in PT_LOAD? 140202908Sgonzo */ 141202908Sgonzo if (shdr[i].sh_offset != 0 && 142202908Sgonzo shdr[i].sh_size != 0) { 143202908Sgonzo symtabindex = i; 144202908Sgonzo symstrindex = shdr[i].sh_link; 145202908Sgonzo } 146202908Sgonzo } 147202908Sgonzo } 148202908Sgonzo } 149202908Sgonzo 150202908Sgonzo /* 151202908Sgonzo * Copy loadable segments 152202908Sgonzo */ 153187418Sgonzo for (i = 0; i < eh->e_phnum; i++) { 154187418Sgonzo volatile char c; 155187418Sgonzo 156187418Sgonzo if (phdr[i].p_type != PT_LOAD) 157187418Sgonzo continue; 158187418Sgonzo 159224105Sjchandra memcpy(mkptr(phdr[i].p_vaddr), 160187418Sgonzo (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz); 161202908Sgonzo 162187418Sgonzo /* Clean space from oversized segments, eg: bss. */ 163187418Sgonzo if (phdr[i].p_filesz < phdr[i].p_memsz) 164224105Sjchandra bzero(mkptr(phdr[i].p_vaddr + phdr[i].p_filesz), 165187418Sgonzo phdr[i].p_memsz - phdr[i].p_filesz); 166202908Sgonzo 167224105Sjchandra if (loadend < phdr[i].p_vaddr + phdr[i].p_memsz) 168224105Sjchandra loadend = phdr[i].p_vaddr + phdr[i].p_memsz; 169187418Sgonzo } 170187418Sgonzo 171202908Sgonzo /* Now grab the symbol tables. */ 172224105Sjchandra lastaddr = (intptr_t)(int)loadend; 173202908Sgonzo if (symtabindex >= 0 && symstrindex >= 0) { 174224105Sjchandra tmp = SYMTAB_MAGIC; 175224105Sjchandra memcpy((void *)lastaddr, &tmp, sizeof(tmp)); 176202908Sgonzo lastaddr += sizeof(Elf_Size); 177224105Sjchandra tmp = shdr[symtabindex].sh_size + 178202908Sgonzo shdr[symstrindex].sh_size + 2*sizeof(Elf_Size); 179224105Sjchandra memcpy((void *)lastaddr, &tmp, sizeof(tmp)); 180202908Sgonzo lastaddr += sizeof(Elf_Size); 181202908Sgonzo /* .symtab size */ 182224105Sjchandra tmp = shdr[symtabindex].sh_size; 183224105Sjchandra memcpy((void *)lastaddr, &tmp, sizeof(tmp)); 184202908Sgonzo lastaddr += sizeof(shdr[symtabindex].sh_size); 185202908Sgonzo /* .symtab data */ 186202908Sgonzo memcpy((void*)lastaddr, 187202908Sgonzo shdr[symtabindex].sh_offset + kstart, 188202908Sgonzo shdr[symtabindex].sh_size); 189202908Sgonzo lastaddr += shdr[symtabindex].sh_size; 190202908Sgonzo 191202908Sgonzo /* .strtab size */ 192224105Sjchandra tmp = shdr[symstrindex].sh_size; 193224105Sjchandra memcpy((void *)lastaddr, &tmp, sizeof(tmp)); 194202908Sgonzo lastaddr += sizeof(shdr[symstrindex].sh_size); 195202908Sgonzo 196202908Sgonzo /* .strtab data */ 197202908Sgonzo memcpy((void*)lastaddr, 198202908Sgonzo shdr[symstrindex].sh_offset + kstart, 199202908Sgonzo shdr[symstrindex].sh_size); 200224105Sjchandra } else { 201202908Sgonzo /* Do not take any chances */ 202224105Sjchandra tmp = 0; 203224105Sjchandra memcpy((void *)lastaddr, &tmp, sizeof(tmp)); 204224105Sjchandra } 205202908Sgonzo 206187418Sgonzo return entry_point; 207187418Sgonzo} 208187418Sgonzo 209187418Sgonzovoid 210187418Sgonzo_startC(register_t a0, register_t a1, register_t a2, register_t a3) 211187418Sgonzo{ 212187418Sgonzo unsigned int * code; 213187418Sgonzo int i; 214187418Sgonzo void (*entry_point)(register_t, register_t, register_t, register_t); 215187418Sgonzo 216187418Sgonzo /* 217187418Sgonzo * Relocate segment to the predefined memory location 218187418Sgonzo * Most likely it will be KSEG0/KSEG1 address 219187418Sgonzo */ 220187418Sgonzo entry_point = load_kernel(kernel_start); 221187418Sgonzo 222187418Sgonzo /* Pass saved registers to original _start */ 223187418Sgonzo entry_point(a0, a1, a2, a3); 224187418Sgonzo} 225