1226586Sdim/*- 2226586Sdim * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org> 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6226586Sdim * modification, are permitted provided that the following conditions 7226586Sdim * are met: 8226586Sdim * 1. Redistributions of source code must retain the above copyright 9226586Sdim * notice, this list of conditions and the following disclaimer. 10226586Sdim * 2. Redistributions in binary form must reproduce the above copyright 11226586Sdim * notice, this list of conditions and the following disclaimer in the 12226586Sdim * documentation and/or other materials provided with the distribution. 13249423Sdim * 14314564Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15226586Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16226586Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17226586Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18226586Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19226586Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20226586Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21226586Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22341825Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23327952Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24327952Sdim * SUCH DAMAGE. 25327952Sdim */ 26327952Sdim 27327952Sdim#define __ELF_WORD_SIZE 64 28327952Sdim 29327952Sdim#include <sys/param.h> 30327952Sdim#include <sys/endian.h> 31327952Sdim#include <sys/linker.h> 32327952Sdim 33327952Sdim#include <machine/metadata.h> 34327952Sdim#include <machine/elf.h> 35327952Sdim 36327952Sdim#include <stand.h> 37327952Sdim 38327952Sdim#include "bootstrap.h" 39327952Sdim#include "syscall_nr.h" 40226586Sdim#include "host_syscall.h" 41226586Sdim#include "modinfo.h" 42226586Sdim#include "kboot.h" 43226586Sdim 44226586Sdimextern char end[]; 45226586Sdimextern void *kerneltramp; 46296417Sdimextern size_t szkerneltramp; 47226586Sdim 48226586Sdimstruct trampoline_data { 49226586Sdim uint32_t kernel_entry; 50226586Sdim uint32_t dtb; 51296417Sdim uint32_t phys_mem_offset; 52288943Sdim uint32_t of_entry; 53226586Sdim uint32_t mdp; 54226586Sdim uint32_t mdp_size; 55296417Sdim}; 56234353Sdim 57234353Sdimint 58234353Sdimppc64_elf_loadfile(char *filename, uint64_t dest, 59234353Sdim struct preloaded_file **result) 60296417Sdim{ 61226586Sdim int r; 62296417Sdim 63226586Sdim r = __elfN(loadfile)(filename, dest, result); 64226586Sdim if (r != 0) 65226586Sdim return (r); 66234353Sdim 67243830Sdim return (0); 68276479Sdim} 69276479Sdim 70226586Sdimint 71226586Sdimppc64_elf_exec(struct preloaded_file *fp) 72226586Sdim{ 73239462Sdim struct file_metadata *fmp; 74234353Sdim vm_offset_t mdp, dtb; 75234353Sdim Elf_Ehdr *e; 76226586Sdim int error; 77226586Sdim uint32_t *trampoline; 78296417Sdim uint64_t entry; 79226586Sdim uint64_t trampolinebase; 80243830Sdim struct trampoline_data *trampoline_data; 81239462Sdim int nseg; 82239462Sdim void *kseg; 83239462Sdim 84239462Sdim if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { 85243830Sdim return(EFTYPE); 86327952Sdim } 87327952Sdim e = (Elf_Ehdr *)&fmp->md_data; 88327952Sdim 89327952Sdim /* 90239462Sdim * Figure out where to put it. 91239462Sdim * 92276479Sdim * Linux does not allow to do kexec_load into 93276479Sdim * any part of memory. Ask arch_loadaddr to 94276479Sdim * resolve the first available chunk of physical 95276479Sdim * memory where loading is possible (load_addr). 96276479Sdim * 97276479Sdim * Memory organization is shown below. 98226586Sdim * It is assumed, that text segment offset of 99226586Sdim * kernel ELF (KERNPHYSADDR) is non-zero, 100296417Sdim * which is true for PPC/PPC64 architectures, 101327952Sdim * where default is 0x100000. 102327952Sdim * 103353358Sdim * load_addr: trampoline code 104353358Sdim * load_addr + KERNPHYSADDR: kernel text segment 105360784Sdim */ 106360784Sdim trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0); 107296417Sdim printf("Load address at %#jx\n", (uintmax_t)trampolinebase); 108226586Sdim printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset); 109234353Sdim 110226586Sdim /* Set up loader trampoline */ 111226586Sdim trampoline = malloc(szkerneltramp); 112296417Sdim memcpy(trampoline, &kerneltramp, szkerneltramp); 113226586Sdim 114296417Sdim /* Parse function descriptor for ELFv1 kernels */ 115226586Sdim if ((e->e_flags & 3) == 2) 116226586Sdim entry = e->e_entry; 117226586Sdim else { 118226586Sdim archsw.arch_copyout(e->e_entry + elf64_relocation_offset, 119226586Sdim &entry, 8); 120226586Sdim entry = be64toh(entry); 121226586Sdim } 122226586Sdim 123226586Sdim /* 124226586Sdim * Placeholder for trampoline data is at trampolinebase + 0x08 125226586Sdim * CAUTION: all data must be Big Endian 126226586Sdim */ 127226586Sdim trampoline_data = (void*)&trampoline[2]; 128226586Sdim trampoline_data->kernel_entry = htobe32(entry + elf64_relocation_offset); 129296417Sdim trampoline_data->phys_mem_offset = htobe32(0); 130226586Sdim trampoline_data->of_entry = htobe32(0); 131226586Sdim 132226586Sdim if ((error = md_load64(fp->f_args, &mdp, &dtb)) != 0) 133226586Sdim return (error); 134234353Sdim 135296417Sdim trampoline_data->dtb = htobe32(dtb); 136226586Sdim trampoline_data->mdp = htobe32(mdp); 137226586Sdim trampoline_data->mdp_size = htobe32(0xfb5d104d); 138226586Sdim 139226586Sdim printf("Kernel entry at %#jx (%#x) ...\n", 140234353Sdim entry, be32toh(trampoline_data->kernel_entry)); 141234353Sdim printf("DTB at %#x, mdp at %#x\n", 142296417Sdim be32toh(trampoline_data->dtb), be32toh(trampoline_data->mdp)); 143226586Sdim 144226586Sdim dev_cleanup(); 145226586Sdim 146226586Sdim archsw.arch_copyin(trampoline, trampolinebase, szkerneltramp); 147296417Sdim free(trampoline); 148226586Sdim 149226586Sdim kboot_kseg_get(&nseg, &kseg); 150226586Sdim 151296417Sdim error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_PPC64); 152226586Sdim if (error != 0) 153296417Sdim panic("kexec_load returned error: %d", error); 154226586Sdim 155226586Sdim error = host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC, 156296417Sdim (uintptr_t)NULL); 157226586Sdim if (error != 0) 158226586Sdim panic("reboot returned error: %d", error); 159226586Sdim 160296417Sdim while (1) {} 161226586Sdim} 162226586Sdim 163296417Sdimstruct file_format ppc_elf64 = 164226586Sdim{ 165296417Sdim ppc64_elf_loadfile, 166234353Sdim ppc64_elf_exec 167226586Sdim}; 168226586Sdim 169226586Sdim/* 170276479Sdim * Sort formats so that those that can detect based on arguments rather than 171243830Sdim * reading the file first. 172226586Sdim */ 173226586Sdim 174226586Sdimstruct file_format *file_formats[] = { 175226586Sdim &ppc_elf64, 176226586Sdim NULL 177226586Sdim}; 178226586Sdim