⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 plain_loader.c

📁 EFI(Extensible Firmware Interface)是下一代BIOS
💻 C
字号:
/* *  Copyright (C) 2001-2003 Hewlett-Packard Co. *	Contributed by Stephane Eranian <eranian@hpl.hp.com> * *  Copyright (C) 2001 Silicon Graphics, Inc. *      Contributed by Brent Casavant <bcasavan@sgi.com> * * This file is part of the ELILO, the EFI Linux boot loader. * *  ELILO is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2, or (at your option) *  any later version. * *  ELILO is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with ELILO; see the file COPYING.  If not, write to the Free *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA *  02111-1307, USA. * * Please check out the elilo.txt for complete documentation on how * to use this program. */#include <efi.h>#include <efilib.h>#include "elilo.h"#include "loader.h"#include "elf.h"#include "private.h"#define LD_NAME L"plain_elf64"#define PLAIN_MIN_BLOCK_SIZE	sizeof(Elf64_Ehdr) /* see load_elf() for details */#define SKIPBUFSIZE	2048	/* minimal default size of the skip buffer */static CHAR8 *skip_buffer;	/* used to skip over unneeded data */static UINTN skip_bufsize;static UINTN elf_is_big_endian;	/* true if ELF file is big endian */static inline UINT64 bswap64(UINT64 v){        if(elf_is_big_endian) v = __ia64_swab64(v);        return v;}static inline UINT32bswap32(UINT32 v){        if(elf_is_big_endian) v = __ia64_swab32(v);        return v;}static inline UINT16bswap16(UINT16 v){        if(elf_is_big_endian) v = __ia64_swab16(v);        return v;}static INTNis_valid_header(Elf64_Ehdr *ehdr){	UINT16 type, machine;        if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {		type    = __ia64_swab16(ehdr->e_type);		machine = __ia64_swab16(ehdr->e_machine);	} else {		type    = ehdr->e_type;		machine = ehdr->e_machine;	}	DBG_PRT((L"class=%d type=%d data=%d machine=%d\n", 		ehdr->e_ident[EI_CLASS],		type,		ehdr->e_ident[EI_DATA],		machine));	return    ehdr->e_ident[EI_MAG0]  == 0x7f 	       && ehdr->e_ident[EI_MAG1]  == 'E'	       && ehdr->e_ident[EI_MAG2]  == 'L'	       && ehdr->e_ident[EI_MAG3]  == 'F'	       && ehdr->e_ident[EI_CLASS] == ELFCLASS64 	       && type                    == ET_EXEC	/* must be executable */	       && machine                 == EM_IA_64 ? 0 : -1;}static INTNplain_probe(CHAR16 *kname){	Elf64_Ehdr ehdr;	EFI_STATUS status;	INTN ret = -1;	fops_fd_t fd;	UINTN size = sizeof(ehdr);	status = fops_open(kname, &fd);	if (EFI_ERROR(status)) return -1;	status = fops_read(fd, &ehdr, &size);	if (EFI_ERROR(status) || size != sizeof(ehdr)) goto error;	ret = is_valid_header(&ehdr);error:	fops_close(fd);	return ret;}/* * move skip bytes forward in the file * this is required because we cannot assume fileops has * seek() capabilities. */static INTNskip_bytes(fops_fd_t fd, UINTN curpos, UINTN newpos){	EFI_STATUS status;	UINTN n, skip;	skip = newpos - curpos;	/* check if seek capability exists */		status = fops_seek(fd, newpos);	if (status == EFI_SUCCESS) return 0;	if (status != EFI_UNSUPPORTED) goto error;	/* unsupported case */	if (skip_buffer == NULL) {		skip_bufsize = MAX(skip, SKIPBUFSIZE);		skip_buffer= (CHAR8 *)alloc(skip_bufsize, EfiLoaderData);		if (skip_buffer == NULL) return -1;	}	while (skip) {		n = skip > skip_bufsize? skip_bufsize : skip;		status = fops_read(fd, skip_buffer, &n);		if (EFI_ERROR(status)) goto error;		skip -=n;	}	return 0;error:	ERR_PRT((L"%s : cannot skip %d bytes\n", LD_NAME, n));	return -1;}static INTNload_elf(fops_fd_t fd, kdesc_t *kd){	Elf64_Ehdr ehdr;	Elf64_Phdr *phdrs;	EFI_STATUS status;	INTN ret = ELILO_LOAD_ERROR;	UINTN i, total_size = 0;	UINTN pages, size, bss_sz, osize;	UINTN offs = 0;	VOID *low_addr = (VOID *)~0;	VOID *max_addr = (VOID *)0;	UINTN load_offset = 0;	UINTN paddr, memsz, filesz, poffs;	UINT16 phnum;	Print(L"Loading Linux... ");	size = sizeof(ehdr);	status = fops_read(fd, &ehdr, &size);	if (EFI_ERROR(status) ||size < sizeof(ehdr)) return ELILO_LOAD_ERROR;	offs += size;	/*	 * do some sanity checking on the file	 */	if (is_valid_header(&ehdr) == -1) {		ERR_PRT((L"%s : not an elf 64-bit file\n", LD_NAME));		return ELILO_LOAD_ERROR;	}	 	/* determine file endianess */        elf_is_big_endian = ehdr.e_ident[EI_DATA] == ELFDATA2MSB ? 1 : 0;	VERB_PRT(3, { 			Print(L"ELF file is %s\n", elf_is_big_endian ? L"big endian" : L"little endian");			Print(L"Entry point 0x%lx\n", bswap64(ehdr.e_entry));			Print(L"%d program headers\n", bswap16(ehdr.e_phnum));			Print(L"%d segment headers\n", bswap16(ehdr.e_shnum));		   });	phnum = bswap16(ehdr.e_phnum);	if (skip_bytes(fd, offs, bswap64(ehdr.e_phoff)) != 0) {		ERR_PRT((L"%s : skip tp %ld for phdrs failed", LD_NAME, offs));		return ELILO_LOAD_ERROR;	}	offs  = bswap64(ehdr.e_phoff);	size = osize = phnum*sizeof(Elf64_Phdr);	DBG_PRT((L"%s : phdrs allocate %d bytes sizeof=%d entsize=%d\n", LD_NAME, size,sizeof(Elf64_Phdr), bswap16(ehdr.e_phentsize)));	phdrs = (Elf64_Phdr *)alloc(size, 0);	if (phdrs == NULL) {		ERR_PRT((L"%s : allocate phdrs failed", LD_NAME));		return ELILO_LOAD_ERROR;	}	status = fops_read(fd, phdrs, &size);	if (EFI_ERROR(status) || size != osize) {		ERR_PRT((L"%s : load phdrs failed", LD_NAME, status));		goto out;	}	offs += size;	/*	 * First pass to figure out:	 *	- lowest physical address	 *	- total memory footprint	 */	for (i = 0; i < phnum; i++) {		paddr = bswap64(phdrs[i].p_paddr);		memsz = bswap64(phdrs[i].p_memsz);		DBG_PRT((L"Phdr %d paddr [0x%lx-0x%lx] offset %ld"			   " filesz %ld memsz=%ld bss_sz=%ld p_type=%d\n",			   1+i, 			   paddr, 			   paddr+bswap64(phdrs[i].p_filesz), 			   bswap64(phdrs[i].p_offset), 			   bswap64(phdrs[i].p_filesz), 			   memsz, 			   memsz - bswap64(phdrs[i].p_filesz), bswap32(phdrs[i].p_type)));			if (bswap32(phdrs[i].p_type) != PT_LOAD) continue;		if (paddr < (UINTN)low_addr) low_addr = (VOID *)paddr;		if (paddr + memsz > (UINTN)max_addr) 			max_addr = (VOID *)paddr + memsz;	}	if ((UINTN)low_addr & (EFI_PAGE_SIZE - 1)) {		ERR_PRT((L"%s : kernel low address 0x%lx not page aligned\n", LD_NAME, low_addr));		goto out;	}	/* how many bytes are needed to hold the kernel */	total_size = (UINTN)max_addr - (UINTN)low_addr;	/* round up to get required number of pages */	pages = EFI_SIZE_TO_PAGES(total_size);	/* keep track of location where kernel ends for	 * the initrd ramdisk (it will be put right after the kernel) 	 */	kd->kstart = low_addr;	kd->kend   = low_addr+ (pages << EFI_PAGE_SHIFT);	/*	 * that's the kernel entry point (virtual address)	 */	kd->kentry = (VOID *)bswap64(ehdr.e_entry);		if (((UINTN)kd->kentry >> 61) != 0) {		ERR_PRT((L"%s:  <<ERROR>> entry point is a virtual address 0x%lx : not supported anymore\n", LD_NAME, kd->kentry));	}	VERB_PRT(3, {		Print(L"Lowest PhysAddr: 0x%lx\nTotalMemSize:%d bytes (%d pages)\n",	      		low_addr, total_size, pages);		Print(L"Kernel entry @ 0x%lx\n", kd->kentry);	});	/*	 * now allocate memory for the kernel at the exact requested spot	 */	if (alloc_kmem(low_addr, pages) == -1) {		VOID *new_addr;		ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));		if (ia64_can_relocate() == 0) {			ERR_PRT((L"relocation is disabled, cannot load kernel"));			goto out;		}		/*		 * could not allocate at requested spot, try to find a		 * suitable location to relocate the kernel		 *		 * The maximum sized Itanium TLB translation entry is 256 MB.		 * If we relocate the kernel by this amount we know for sure		 * that alignment constraints will be satisified, regardless		 * of the kernel used.		 */		Print(L"Attempting to relocate kernel.\n");		if (find_kernel_memory(low_addr, max_addr, 256*MB, &new_addr) == -1) {			ERR_PRT((L"%s : find_kernel_memory(0x%lx, 0x%lx, 0x%lx, 0x%lx) failed\n", LD_NAME, low_addr, max_addr, 256*MB, &load_offset));			goto out;		}		/* unsigned arithmetic */                load_offset = (UINTN) (new_addr - ROUNDDOWN((UINTN) low_addr,256*MB));		VERB_PRT(3, Print(L"low_addr=0x%lx new_addr=0x%lx offset=0x%lx", low_addr, new_addr, load_offset));		/*		 * correct various addesses for non-zero load_offset		 */		low_addr = (VOID*) ((UINTN) low_addr + load_offset);		max_addr = (VOID*) ((UINTN) max_addr + load_offset);		kd->kstart = (VOID *) ((UINTN) kd->kstart + load_offset);		kd->kend = (VOID *) ((UINTN) kd->kend + load_offset);		kd->kentry = (VOID *) ((UINTN) kd->kentry + load_offset);		/*		 * try one last time to get memory for the kernel		 */		if (alloc_kmem(low_addr, pages) == -1) {			ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));			ERR_PRT((L"Relocation by 0x%lx bytes failed.\n", load_offset));			goto out;		}	}	VERB_PRT(1, Print(L"Press any key to interrupt\n"));	/* Second pass:	 * Walk through the program headers	 * and actually load data into physical memory	 */	for (i = 0; i < phnum; i++) {		/*		 * Check for pure loadable segment; ignore if not loadable		 */		if (bswap32(phdrs[i].p_type) != PT_LOAD) continue;		poffs = bswap64(phdrs[i].p_offset);		size = poffs - offs;		VERB_PRT(3, Print(L"\noff=%ld poffs=%ld size=%ld\n", offs, poffs, size));		filesz = bswap64(phdrs[i].p_filesz);                /*                 * correct p_paddr for non-zero load offset                 */		phdrs[i].p_paddr = (Elf64_Addr) ((UINTN) bswap64(phdrs[i].p_paddr) + load_offset);		/*		 * Move to the right position		 */		if (size && skip_bytes(fd, offs, poffs) != 0) goto out_kernel;		/*		 * Keep track of current position in file		 */		offs += size;		/*		 * How many BSS bytes to clear		 */		bss_sz = bswap64(phdrs[i].p_memsz) - filesz;		VERB_PRT(4, {			Print(L"\nHeader #%d\n", i);			Print(L"offset %ld\n", poffs);			Print(L"Phys addr 0x%lx\n", phdrs[i].p_paddr); /* already endian adjusted */			Print(L"BSS size %ld bytes\n", bss_sz);			Print(L"skip=%ld offs=%ld\n", size, offs);		});		/*		 * Read actual segment into memory		 */		ret = read_file(fd, filesz, (CHAR8 *)phdrs[i].p_paddr);		if (ret == ELILO_LOAD_ABORTED) goto load_abort;		if (ret == ELILO_LOAD_ERROR) goto out;		/*		 * update file position		 */		offs += filesz;		/*		 * Clear bss section		 */		if (bss_sz) Memset((VOID *) phdrs[i].p_paddr+filesz, 0, bss_sz);	}	free(phdrs);	Print(L"..done\n");	return ELILO_LOAD_SUCCESS;load_abort:	Print(L"..Aborted\n");	ret = ELILO_LOAD_ABORTED;out_kernel:	/* free kernel memory */	free_kmem();out:	free(phdrs);	return ret;}static INTNplain_load_kernel(CHAR16 *kname, kdesc_t *kd){		INTN ret;	fops_fd_t fd;	EFI_STATUS status;	/*	 * Moving the open here simplifies the load_elf() error handling	 */	status = fops_open(kname, &fd);	if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;	Print(L"Loading %s...", kname);	ret = load_elf(fd, kd);	fops_close(fd);	/*	 * if the skip buffer was ever used, free it	 */	if (skip_buffer) {		free(skip_buffer);		/* in case we come back */		skip_buffer = NULL;	}	return ret;}loader_ops_t plain_loader={	NULL,	LD_NAME,	plain_probe,	plain_load_kernel};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -