1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdlib.h> 30 #include <fcntl.h> 31 #include <strings.h> 32 #include <stdio.h> 33 #include <sys/types.h> 34 #include <sys/inttypes.h> 35 #include <sys/elf.h> 36 #include <sys/elf_notes.h> 37 #include <sys/mman.h> 38 #include <sys/stat.h> 39 #include "sys/multiboot.h" 40 41 static char *pname; 42 static char *fname; 43 static char *image; /* pointer to the ELF file in memory */ 44 45 #define ELFSEEK(offset) ((void *)(image + offset)) 46 47 /* 48 * patch the load address / entry address 49 * Find the physical load address of the 1st PT_LOAD segment. 50 * Find the amount that e_entry exceeds that amount. 51 * Now go back and subtract the excess from the p_paddr of the LOAD segment. 52 */ 53 static void 54 patch64(Elf64_Ehdr *eh) 55 { 56 Elf64_Phdr *phdr; 57 caddr_t allphdrs; 58 int i; 59 int m; 60 int extra; 61 multiboot_header_t *mbh; 62 63 allphdrs = NULL; 64 65 if (eh->e_type != ET_EXEC) { 66 (void) fprintf(stderr, "%s: not ET_EXEC, e_type = 0x%x\n", 67 pname, eh->e_type); 68 exit(1); 69 } 70 if (eh->e_phnum == 0 || eh->e_phoff == 0) { 71 (void) fprintf(stderr, "%s: no program headers\n", pname); 72 exit(1); 73 } 74 75 /* 76 * Get the program headers. 77 */ 78 allphdrs = ELFSEEK(eh->e_phoff); 79 if (allphdrs == NULL) { 80 (void) fprintf(stderr, "%s: Failed to get %d program hdrs\n", 81 pname, eh->e_phnum); 82 exit(1); 83 } 84 85 /* 86 * Look for multiboot header. It must be 32-bit aligned and 87 * completely contained in the 1st 8K of the file. 88 */ 89 for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) { 90 mbh = (void *)(image + m); 91 if (mbh->magic == MB_HEADER_MAGIC) 92 break; 93 } 94 95 if (m >= 8192 - sizeof (multiboot_header_t)) { 96 (void) fprintf(stderr, "%s: Didn't find multiboot header\n", 97 pname); 98 exit(1); 99 } 100 101 /* 102 * Find the 1:1 mapped PT_LOAD section 103 */ 104 for (i = 0; i < eh->e_phnum; i++) { 105 /*LINTED [ELF program header alignment]*/ 106 phdr = (Elf64_Phdr *)(allphdrs + eh->e_phentsize * i); 107 108 /* 109 * Find the low memory 1:1 PT_LOAD section! 110 */ 111 if (phdr->p_type != PT_LOAD) 112 continue; 113 114 if (phdr->p_memsz == 0) 115 continue; 116 117 if (phdr->p_paddr != phdr->p_vaddr) 118 continue; 119 120 if (i != 0) { 121 (void) fprintf(stderr, "%s: identity mapped PT_LOAD" 122 " wasn't 1st\n", pname); 123 exit(1); 124 } 125 126 /* 127 * Patch the multiboot header fields to get entire 128 * file loaded, with entry aligned at extra. 129 */ 130 if (eh->e_entry != phdr->p_paddr) { 131 (void) fprintf(stderr, "%s: entry != paddr\n", pname); 132 exit(1); 133 } 134 135 /* 136 * Grub uses the MB header for 64 bit loading. 137 */ 138 mbh->load_addr = phdr->p_paddr - phdr->p_offset; 139 mbh->entry_addr = phdr->p_paddr; 140 mbh->header_addr = mbh->load_addr + m; 141 #ifdef VERBOSE 142 (void) printf("%s ELF64 MB header patched by 0x%0x " 143 "bytes:\n", fname, extra); 144 (void) printf("\tload_addr now 0x%x\n", mbh->load_addr); 145 (void) printf("\tentry_addr now 0x%x\n", mbh->entry_addr); 146 (void) printf("\theader_addr now 0x%x\n", mbh->header_addr); 147 #endif 148 exit(0); 149 } 150 151 (void) fprintf(stderr, "%s: Didn't find 1:1 mapped PT_LOAD section\n", 152 pname); 153 exit(1); 154 } 155 156 157 int 158 main(int argc, char **argv) 159 { 160 int fd; 161 uchar_t *ident; 162 void *hdr = NULL; 163 164 /* 165 * we expect one argument -- the elf file 166 */ 167 if (argc != 2) { 168 (void) fprintf(stderr, "usage: %s <unix-elf-file>\n", argv[0]); 169 exit(1); 170 } 171 172 pname = strrchr(argv[0], '/'); 173 if (pname == NULL) 174 pname = argv[0]; 175 else 176 ++pname; 177 178 fname = argv[1]; 179 fd = open(fname, O_RDWR); 180 if (fd < 0) { 181 (void) fprintf(stderr, "%s: open(%s, O_RDWR) failed\n", 182 pname, fname); 183 exit(1); 184 } 185 186 /* 187 * mmap just the 1st 8K -- since that's where the GRUB 188 * multiboot header must be located. 189 */ 190 image = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 191 if (image == MAP_FAILED) { 192 (void) fprintf(stderr, "%s: mmap() of %s failed\n", 193 pname, fname); 194 exit(1); 195 } 196 197 ident = ELFSEEK(0); 198 if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || 199 ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { 200 (void) fprintf(stderr, "%s: not an ELF file!\n", pname); 201 exit(1); 202 } 203 204 if (ident[EI_CLASS] == ELFCLASS64) { 205 hdr = ELFSEEK(0); 206 patch64(hdr); 207 } else if (ident[EI_CLASS] != ELFCLASS32) { 208 (void) fprintf(stderr, "%s: Unknown ELF class 0x%x\n", pname, 209 ident[EI_CLASS]); 210 exit(1); 211 } 212 return (0); 213 } 214