1ae115bc7Smrj /* 2ae115bc7Smrj * CDDL HEADER START 3ae115bc7Smrj * 4ae115bc7Smrj * The contents of this file are subject to the terms of the 5ae115bc7Smrj * Common Development and Distribution License (the "License"). 6ae115bc7Smrj * You may not use this file except in compliance with the License. 7ae115bc7Smrj * 8ae115bc7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9ae115bc7Smrj * or http://www.opensolaris.org/os/licensing. 10ae115bc7Smrj * See the License for the specific language governing permissions 11ae115bc7Smrj * and limitations under the License. 12ae115bc7Smrj * 13ae115bc7Smrj * When distributing Covered Code, include this CDDL HEADER in each 14ae115bc7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15ae115bc7Smrj * If applicable, add the following below this CDDL HEADER, with the 16ae115bc7Smrj * fields enclosed by brackets "[]" replaced with your own identifying 17ae115bc7Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 18ae115bc7Smrj * 19ae115bc7Smrj * CDDL HEADER END 20ae115bc7Smrj */ 21ae115bc7Smrj 22ae115bc7Smrj /* 23ae115bc7Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24ae115bc7Smrj * Use is subject to license terms. 25ae115bc7Smrj */ 26ae115bc7Smrj 27ae115bc7Smrj #include <stdlib.h> 28d1827f25Srie #include <errno.h> 29ae115bc7Smrj #include <fcntl.h> 30ae115bc7Smrj #include <strings.h> 31ae115bc7Smrj #include <stdio.h> 32ae115bc7Smrj #include <sys/types.h> 33ae115bc7Smrj #include <sys/inttypes.h> 34ae115bc7Smrj #include <sys/elf.h> 35ae115bc7Smrj #include <sys/elf_notes.h> 36ae115bc7Smrj #include <sys/mman.h> 37ae115bc7Smrj #include <sys/stat.h> 38*d2670fc4SToomas Soome #include <sys/sysmacros.h> 39ae115bc7Smrj #include "sys/multiboot.h" 40*d2670fc4SToomas Soome #include "sys/multiboot2.h" 41ae115bc7Smrj 42ae115bc7Smrj static char *pname; 43ae115bc7Smrj static char *fname; 44ae115bc7Smrj static char *image; /* pointer to the ELF file in memory */ 45ae115bc7Smrj 46ae115bc7Smrj #define ELFSEEK(offset) ((void *)(image + offset)) 47ae115bc7Smrj 48ae115bc7Smrj /* 49*d2670fc4SToomas Soome * Find MB2 header tags for entry and patch it. 50*d2670fc4SToomas Soome * The first tag is right after header. 51*d2670fc4SToomas Soome */ 52*d2670fc4SToomas Soome static int 53*d2670fc4SToomas Soome patch64_mb2(multiboot2_header_t *mbh2, int file_offset, 54*d2670fc4SToomas Soome Elf64_Addr ptload_start, Elf32_Off ptload_offset) 55*d2670fc4SToomas Soome { 56*d2670fc4SToomas Soome multiboot_header_tag_t *tagp = mbh2->mb2_tags; 57*d2670fc4SToomas Soome multiboot_header_tag_address_t *mbaddr = NULL; 58*d2670fc4SToomas Soome multiboot_header_tag_entry_address_t *mbentry = NULL; 59*d2670fc4SToomas Soome 60*d2670fc4SToomas Soome /* 61*d2670fc4SToomas Soome * Loop until we get end TAG or we have both tags. 62*d2670fc4SToomas Soome */ 63*d2670fc4SToomas Soome while (tagp->mbh_type != MULTIBOOT_HEADER_TAG_END && 64*d2670fc4SToomas Soome (mbaddr == NULL || mbentry == NULL)) { 65*d2670fc4SToomas Soome switch (tagp->mbh_type) { 66*d2670fc4SToomas Soome case MULTIBOOT_HEADER_TAG_ADDRESS: 67*d2670fc4SToomas Soome mbaddr = (multiboot_header_tag_address_t *)tagp; 68*d2670fc4SToomas Soome break; 69*d2670fc4SToomas Soome case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: 70*d2670fc4SToomas Soome mbentry = (multiboot_header_tag_entry_address_t *)tagp; 71*d2670fc4SToomas Soome break; 72*d2670fc4SToomas Soome } 73*d2670fc4SToomas Soome tagp = (multiboot_header_tag_t *) 74*d2670fc4SToomas Soome ((uintptr_t)tagp + 75*d2670fc4SToomas Soome P2ROUNDUP(tagp->mbh_size, MULTIBOOT_TAG_ALIGN)); 76*d2670fc4SToomas Soome } 77*d2670fc4SToomas Soome 78*d2670fc4SToomas Soome if (mbaddr == NULL || mbentry == NULL) { 79*d2670fc4SToomas Soome (void) fprintf(stderr, "Missing multiboot2 %s tag\n", 80*d2670fc4SToomas Soome (mbaddr == NULL)? "address" : "entry"); 81*d2670fc4SToomas Soome return (1); 82*d2670fc4SToomas Soome } 83*d2670fc4SToomas Soome 84*d2670fc4SToomas Soome /* Patch it. */ 85*d2670fc4SToomas Soome mbaddr->mbh_load_addr = ptload_start - ptload_offset; 86*d2670fc4SToomas Soome mbaddr->mbh_header_addr = mbaddr->mbh_load_addr + file_offset; 87*d2670fc4SToomas Soome mbentry->mbh_entry_addr = ptload_start; 88*d2670fc4SToomas Soome 89*d2670fc4SToomas Soome #ifdef VERBOSE 90*d2670fc4SToomas Soome (void) printf(" ELF64 MB2 header patched\n"); 91*d2670fc4SToomas Soome (void) printf("\tload_addr now: 0x%x\n", mbaddr->mbh_load_addr); 92*d2670fc4SToomas Soome (void) printf("\theader_addr now: 0x%x\n", mbaddr->mbh_header_addr); 93*d2670fc4SToomas Soome (void) printf("\tentry_addr now: 0x%x\n", mbentry->mbh_entry_addr); 94*d2670fc4SToomas Soome #endif 95*d2670fc4SToomas Soome return (0); 96*d2670fc4SToomas Soome } 97*d2670fc4SToomas Soome 98*d2670fc4SToomas Soome /* 99*d2670fc4SToomas Soome * Patch the load address / entry address for MB1 and MB2 if present. 100ae115bc7Smrj * Find the physical load address of the 1st PT_LOAD segment. 101ae115bc7Smrj * Find the amount that e_entry exceeds that amount. 102ae115bc7Smrj * Now go back and subtract the excess from the p_paddr of the LOAD segment. 103ae115bc7Smrj */ 104d1827f25Srie static int 105ae115bc7Smrj patch64(Elf64_Ehdr *eh) 106ae115bc7Smrj { 107ae115bc7Smrj Elf64_Phdr *phdr; 108d1827f25Srie caddr_t phdrs = NULL; 109*d2670fc4SToomas Soome int ndx, mem, mem2; 110ae115bc7Smrj multiboot_header_t *mbh; 111*d2670fc4SToomas Soome multiboot2_header_t *mbh2; 112ae115bc7Smrj 113d1827f25Srie /* 114d1827f25Srie * Verify some ELF basics - this must be an executable with program 115d1827f25Srie * headers. 116d1827f25Srie */ 117ae115bc7Smrj if (eh->e_type != ET_EXEC) { 118d1827f25Srie (void) fprintf(stderr, "%s: %s: not ET_EXEC, e_type = 0x%x\n", 119d1827f25Srie pname, fname, eh->e_type); 120d1827f25Srie return (1); 121ae115bc7Smrj } 122d1827f25Srie if ((eh->e_phnum == 0) || (eh->e_phoff == 0)) { 123d1827f25Srie (void) fprintf(stderr, "%s: %s: no program headers\n", pname, 124d1827f25Srie fname); 125d1827f25Srie return (1); 126ae115bc7Smrj } 127ae115bc7Smrj 128ae115bc7Smrj /* 129ae115bc7Smrj * Get the program headers. 130ae115bc7Smrj */ 131d1827f25Srie if ((phdrs = ELFSEEK(eh->e_phoff)) == NULL) { 132d1827f25Srie (void) fprintf(stderr, "%s: %s: failed to get %d program " 133d1827f25Srie "hdrs\n", pname, fname, eh->e_phnum); 134d1827f25Srie return (1); 135ae115bc7Smrj } 136ae115bc7Smrj 137ae115bc7Smrj /* 138*d2670fc4SToomas Soome * Look for multiboot1 header. It must be 32-bit aligned and 139ae115bc7Smrj * completely contained in the 1st 8K of the file. 140ae115bc7Smrj */ 141d1827f25Srie for (mem = 0; mem < 8192 - sizeof (multiboot_header_t); mem += 4) { 142d1827f25Srie mbh = ELFSEEK(mem); 143ae115bc7Smrj if (mbh->magic == MB_HEADER_MAGIC) 144ae115bc7Smrj break; 145ae115bc7Smrj } 146ae115bc7Smrj 147d1827f25Srie if (mem >= 8192 - sizeof (multiboot_header_t)) { 148d1827f25Srie (void) fprintf(stderr, "%s: %s: Didn't find multiboot header\n", 149d1827f25Srie pname, fname); 150d1827f25Srie return (1); 151ae115bc7Smrj } 152ae115bc7Smrj 153ae115bc7Smrj /* 154*d2670fc4SToomas Soome * Look for multiboot2 header. It must be 64-bit aligned and 155*d2670fc4SToomas Soome * completely contained in the 1st 32K of the file. 156*d2670fc4SToomas Soome * We do not require it to be present. 157*d2670fc4SToomas Soome */ 158*d2670fc4SToomas Soome ndx = 0; 159*d2670fc4SToomas Soome for (mem2 = 0; 160*d2670fc4SToomas Soome mem2 <= MULTIBOOT_SEARCH - sizeof (multiboot2_header_t); 161*d2670fc4SToomas Soome mem2 += MULTIBOOT_HEADER_ALIGN) { 162*d2670fc4SToomas Soome mbh2 = ELFSEEK(mem2); 163*d2670fc4SToomas Soome ndx = mbh2->mb2_header_length; 164*d2670fc4SToomas Soome if (mbh2->mb2_magic == MULTIBOOT2_HEADER_MAGIC) 165*d2670fc4SToomas Soome break; 166*d2670fc4SToomas Soome ndx = 0; 167*d2670fc4SToomas Soome } 168*d2670fc4SToomas Soome 169*d2670fc4SToomas Soome if (ndx == 0 || mem2 + ndx > MULTIBOOT_SEARCH) { 170*d2670fc4SToomas Soome #ifdef VERBOSE 171*d2670fc4SToomas Soome (void) fprintf(stderr, "%s: %s: Didn't find multiboot2 " 172*d2670fc4SToomas Soome "header\n", pname, fname); 173*d2670fc4SToomas Soome #endif 174*d2670fc4SToomas Soome mbh2 = NULL; 175*d2670fc4SToomas Soome } 176*d2670fc4SToomas Soome 177*d2670fc4SToomas Soome /* 178ae115bc7Smrj * Find the 1:1 mapped PT_LOAD section 179ae115bc7Smrj */ 180d1827f25Srie for (ndx = 0; ndx < eh->e_phnum; ndx++) { 181ae115bc7Smrj /*LINTED [ELF program header alignment]*/ 182d1827f25Srie phdr = (Elf64_Phdr *)(phdrs + eh->e_phentsize * ndx); 183ae115bc7Smrj 184ae115bc7Smrj /* 185ae115bc7Smrj * Find the low memory 1:1 PT_LOAD section! 186ae115bc7Smrj */ 187ae115bc7Smrj if (phdr->p_type != PT_LOAD) 188ae115bc7Smrj continue; 189ae115bc7Smrj 190ae115bc7Smrj if (phdr->p_memsz == 0) 191ae115bc7Smrj continue; 192ae115bc7Smrj 193ae115bc7Smrj if (phdr->p_paddr != phdr->p_vaddr) 194ae115bc7Smrj continue; 195ae115bc7Smrj 196ae115bc7Smrj /* 197d1827f25Srie * Make sure the multiboot header is part of the first PT_LOAD 198d1827f25Srie * segment, and that the executables entry point starts at the 199d1827f25Srie * same segment. 200ae115bc7Smrj */ 201d1827f25Srie if ((mem < phdr->p_offset) || 202d1827f25Srie (mem >= (phdr->p_offset + phdr->p_filesz))) { 203d1827f25Srie (void) fprintf(stderr, "%s: %s: identity mapped " 204d1827f25Srie "PT_LOAD wasn't 1st PT_LOAD\n", pname, fname); 205d1827f25Srie return (1); 206d1827f25Srie } 207ae115bc7Smrj if (eh->e_entry != phdr->p_paddr) { 208d1827f25Srie (void) fprintf(stderr, "%s: %s: entry != paddr\n", 209d1827f25Srie pname, fname); 210d1827f25Srie return (1); 211ae115bc7Smrj } 212ae115bc7Smrj 213*d2670fc4SToomas Soome if (mbh2 != NULL && ((mem2 < phdr->p_offset) || 214*d2670fc4SToomas Soome (mem2 >= (phdr->p_offset + phdr->p_filesz)))) { 215*d2670fc4SToomas Soome #ifdef VERBOSE 216*d2670fc4SToomas Soome (void) fprintf(stderr, "%s: %s: multiboot2 header not" 217*d2670fc4SToomas Soome " in 1st PT_LOAD\n", pname, fname); 218*d2670fc4SToomas Soome #endif 219*d2670fc4SToomas Soome mem2 = 0; 220*d2670fc4SToomas Soome mbh2 = NULL; 221*d2670fc4SToomas Soome } 222*d2670fc4SToomas Soome 223ae115bc7Smrj /* 224d1827f25Srie * Patch the multiboot header fields to get entire file loaded. 225ae115bc7Smrj * Grub uses the MB header for 64 bit loading. 226ae115bc7Smrj */ 227ae115bc7Smrj mbh->load_addr = phdr->p_paddr - phdr->p_offset; 228ae115bc7Smrj mbh->entry_addr = phdr->p_paddr; 229d1827f25Srie mbh->header_addr = mbh->load_addr + mem; 230ae115bc7Smrj #ifdef VERBOSE 231d1827f25Srie (void) printf(" %s: ELF64 MB header patched\n", fname); 232d1827f25Srie (void) printf("\tload_addr now: 0x%x\n", mbh->load_addr); 233d1827f25Srie (void) printf("\tentry_addr now: 0x%x\n", mbh->entry_addr); 234d1827f25Srie (void) printf("\theader_addr now: 0x%x\n", mbh->header_addr); 235ae115bc7Smrj #endif 236*d2670fc4SToomas Soome if (mbh2 != NULL) 237*d2670fc4SToomas Soome return (patch64_mb2(mbh2, mem2, phdr->p_paddr, 238*d2670fc4SToomas Soome phdr->p_offset)); 239d1827f25Srie return (0); 240ae115bc7Smrj } 241ae115bc7Smrj 242d1827f25Srie (void) fprintf(stderr, "%s: %s: Didn't find 1:1 mapped PT_LOAD " 243d1827f25Srie "section\n", pname, fname); 244d1827f25Srie return (1); 245ae115bc7Smrj } 246ae115bc7Smrj 247ae115bc7Smrj int 248ae115bc7Smrj main(int argc, char **argv) 249ae115bc7Smrj { 250ae115bc7Smrj int fd; 251ae115bc7Smrj uchar_t *ident; 252ae115bc7Smrj void *hdr = NULL; 253*d2670fc4SToomas Soome struct stat sb; 254ae115bc7Smrj 255ae115bc7Smrj /* 256*d2670fc4SToomas Soome * We expect one argument -- the elf file. 257ae115bc7Smrj */ 258ae115bc7Smrj if (argc != 2) { 259ae115bc7Smrj (void) fprintf(stderr, "usage: %s <unix-elf-file>\n", argv[0]); 260d1827f25Srie return (1); 261ae115bc7Smrj } 262ae115bc7Smrj 263ae115bc7Smrj pname = strrchr(argv[0], '/'); 264ae115bc7Smrj if (pname == NULL) 265ae115bc7Smrj pname = argv[0]; 266ae115bc7Smrj else 267ae115bc7Smrj ++pname; 268ae115bc7Smrj 269ae115bc7Smrj fname = argv[1]; 270d1827f25Srie if ((fd = open(fname, O_RDWR)) < 0) { 271d1827f25Srie (void) fprintf(stderr, "%s: open(%s, O_RDWR) failed: %s\n", 272d1827f25Srie pname, fname, strerror(errno)); 273d1827f25Srie return (1); 274ae115bc7Smrj } 275ae115bc7Smrj 276*d2670fc4SToomas Soome if (fstat(fd, &sb) != 0) { 277*d2670fc4SToomas Soome (void) fprintf(stderr, "%s: fstat failed: %s\n", 278*d2670fc4SToomas Soome pname, strerror(errno)); 279*d2670fc4SToomas Soome return (1); 280*d2670fc4SToomas Soome } 281*d2670fc4SToomas Soome 282*d2670fc4SToomas Soome /* Make sure we have at least MULTIBOOT_SEARCH bytes. */ 283*d2670fc4SToomas Soome if (sb.st_size < MULTIBOOT_SEARCH) { 284*d2670fc4SToomas Soome (void) fprintf(stderr, "%s: %s is too small for a kernel\n", 285*d2670fc4SToomas Soome pname, fname); 286*d2670fc4SToomas Soome return (1); 287*d2670fc4SToomas Soome } 288*d2670fc4SToomas Soome 289ae115bc7Smrj /* 290*d2670fc4SToomas Soome * mmap the 1st 32K -- MB1 header is within first 8k and MB2 header 291*d2670fc4SToomas Soome * is within 32k. 292ae115bc7Smrj */ 293*d2670fc4SToomas Soome image = mmap(NULL, MULTIBOOT_SEARCH, PROT_READ | PROT_WRITE, 294*d2670fc4SToomas Soome MAP_SHARED, fd, 0); 295ae115bc7Smrj if (image == MAP_FAILED) { 296d1827f25Srie (void) fprintf(stderr, "%s: mmap() of %s failed: %s\n", 297d1827f25Srie pname, fname, strerror(errno)); 298d1827f25Srie return (1); 299ae115bc7Smrj } 300ae115bc7Smrj 301ae115bc7Smrj ident = ELFSEEK(0); 302ae115bc7Smrj if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || 303ae115bc7Smrj ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { 304d1827f25Srie (void) fprintf(stderr, "%s: %s: not an ELF file!\n", pname, 305d1827f25Srie fname); 306d1827f25Srie return (1); 307ae115bc7Smrj } 308ae115bc7Smrj 309ae115bc7Smrj if (ident[EI_CLASS] == ELFCLASS64) { 310ae115bc7Smrj hdr = ELFSEEK(0); 311d1827f25Srie return (patch64(hdr)); 312d1827f25Srie } 313d1827f25Srie if (ident[EI_CLASS] != ELFCLASS32) { 314ae115bc7Smrj (void) fprintf(stderr, "%s: Unknown ELF class 0x%x\n", pname, 315ae115bc7Smrj ident[EI_CLASS]); 316d1827f25Srie return (1); 317ae115bc7Smrj } 318ae115bc7Smrj return (0); 319ae115bc7Smrj } 320