util.c

来自「EFI(Extensible Firmware Interface)是下一代BI」· C语言 代码 · 共 504 行

C
504
字号
/* *  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"#define TENTH_SEC		1000000	 	/* 1/10th second in 100ns unit */#define READ_BLOCK_SIZE		(4*EFI_PAGE_SIZE) /* block size for read_file */#define is_cr(k)		(((k)==CHAR_LINEFEED)||((k)==CHAR_CARRIAGE_RETURN))#define CHAR_SPACE 		L' 'static INTNread_keypress(EFI_INPUT_KEY *key){	return systab->ConIn->ReadKeyStroke(systab->ConIn, key);}EFI_STATUScheck_abort(VOID){	EFI_INPUT_KEY key;	return read_keypress(&key);}inline VOIDreset_input(VOID){	systab->ConIn->Reset(systab->ConIn, 1);}#if 0INTNwait_keypress_abort(VOID){	SIMPLE_INPUT_INTERFACE *conin = systab->ConIn;	EFI_INPUT_KEY key;	EFI_STATUS status;	reset_input();	Print(L"Hit ENTER to continue or ANY other key to cancel");	/* cleanup buffer first */	while (conin->ReadKeyStroke(conin, &key) == EFI_SUCCESS);	while ((status=conin->ReadKeyStroke(conin, &key)) == EFI_NOT_READY );	if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;	Print(L"\n");	return is_cr(key.UnicodeChar) ? ELILO_LOAD_SUCCESS: ELILO_BOOT_ABORTED;}#endif/* * wait for timeout to expire or keypress * Return: * 	0 : timeout expired * 	1 : a key was pressed (still input stream to process) * 	-1: an error occured */INTNwait_timeout(UINTN timeout){       	EFI_STATUS status;	EFI_EVENT timer;	EFI_EVENT list[2];	UINTN idx;	if (timeout == 0) return 0;	/* Create a timeout timer */	status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &timer);	if (EFI_ERROR(status)) {		ERR_PRT((L" waitkey CreateEvent failed %r", status));		return -1;	}	/* In 100ns increments */	status = BS->SetTimer(timer, TimerPeriodic, TENTH_SEC);	if (EFI_ERROR(status)) {		ERR_PRT((L"waitkey SetTimer failed %r", status));		return -1;	}	list[0] = timer;	list[1] = systab->ConIn->WaitForKey;	do {		status = BS->WaitForEvent(2, list, &idx);		if (EFI_ERROR(status)) {			ERR_PRT((L"waitkey WaitForEvent failed %r", status));			return -1;		}	} while (timeout-- && idx == 0);		/*	 * SetTimer(timer, TimerCancel, 0) is causing problems on IA-32 and gcc3	 * I do not know why it dies with EFI12.35. So let's fake a key stroke.	 */	status = BS->SetTimer(timer, TimerCancel, 0);	if (EFI_ERROR(status)) {		ERR_PRT((L"waitkey SetTimer(TimerCancel) failed %r", status));		return -1;	}	BS->CloseEvent(timer);	return idx ? 1 : 0;}INTNargify(CHAR16 *buf, UINTN len, CHAR16 **argv)   {        UINTN     i=0, j=0;        CHAR16   *p = buf;	        if (buf == 0) { 		argv[0] = NULL;		return 0;	}	/* len represents the number of bytes, not the number of 16 bytes chars */	len = len >> 1;	/*	 * Here we use CHAR_NULL as the terminator rather than the length	 * because it seems like the EFI shell return rather bogus values for it.	 * Apparently, we are guaranteed to find the '\0' character in the buffer	 * where the real input arguments stop, so we use it instead.	 */	for(;;) {		while (buf[i] == CHAR_SPACE && buf[i] != CHAR_NULL && i < len) i++;		if (buf[i] == CHAR_NULL || i == len) goto end;		p = buf+i;		i++;		while (buf[i] != CHAR_SPACE && buf[i] != CHAR_NULL && i < len) i++;		argv[j++] = p;		if (buf[i] == CHAR_NULL) goto end;		buf[i]  = CHAR_NULL;		if (i == len)  goto end;		i++;		if (j == MAX_ARGS-1) {			ERR_PRT((L"too many arguments (%d) truncating", j));			goto end;		}	}end:#if 0	if (i != len) {		ERR_PRT((L"ignoring trailing %d characters on command line", len-i));	}#endif        argv[j] = NULL;	return j;}VOIDunargify(CHAR16 **argv, CHAR16 **args){	if ( *argv == 0 ) {		*args = L"";		return;	}	*args = *argv;	while ( argv[1] ) {		(*argv)[StrLen(*argv)] = CHAR_SPACE;		argv++;	}}VOIDsplit_args(CHAR16 *buffer, CHAR16 *kname, CHAR16 *args){	CHAR16 *tmp;	/* find beginning of kernel name */	while (*buffer && *buffer == CHAR_SPACE) buffer++;	tmp = buffer;		/* scan through kernel name */		while (*buffer && *buffer != CHAR_SPACE) buffer++;	if (*buffer) {		*buffer++ = CHAR_NULL;		StrCpy(kname, tmp);	}	/* skip space between kernel and args */		while (*buffer && *buffer == CHAR_SPACE) buffer++;	StrCpy(args, buffer);}INTNread_file(UINTN fd, UINTN total_size, CHAR8 *buffer){	INTN size, j=0;	EFI_STATUS status;	CHAR16 helicopter[4] = { L'|' , L'/' , L'-' , L'\\' };	INTN ret = ELILO_LOAD_SUCCESS;	UINTN sum = 0;	/*	 * We load by chunks rather than a single big read because	 * early versions of EFI had troubles loading files	 * from floppies in a single big request.  Breaking	 * the read down into chunks of 4KB fixed that 	 * problem. While this problem has been fixed, we still prefer	 * this method because it tells us whether or not we're making	 * forward progress.	 */	while (total_size > 0) {		size = total_size < READ_BLOCK_SIZE? total_size : READ_BLOCK_SIZE;		status = fops_read(fd, buffer, &size);		if (EFI_ERROR(status)) {			ERR_PRT((L"read_file failed %r", status));			return ELILO_LOAD_ERROR;		}		sum += size;		Print(L"%c\b",helicopter[j++%4]);		buffer     += size;		total_size -= size; 		if (check_abort() == EFI_SUCCESS) {			ret = ELILO_LOAD_ABORTED;			break;		}	}	return ret;}INTNget_memmap(mmap_desc_t *desc){#define	ELILO_MEMMAP_SIZE_DEFAULT	EFI_PAGE_SIZE#define	ELILO_MEMMAP_INC		(sizeof(EFI_MEMORY_DESCRIPTOR)<<1)	EFI_STATUS status;	desc->map_size = ELILO_MEMMAP_SIZE_DEFAULT;	for(;;) {		desc->md = (EFI_MEMORY_DESCRIPTOR *)alloc(desc->map_size, EfiLoaderData);		if (desc->md == NULL) {			ERR_PRT((L"failed to allocate memory map buffer"));			return -1;		}		status = (*BS->GetMemoryMap)(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);		if (status == EFI_SUCCESS) break;		free(desc->md);		if (status != EFI_BUFFER_TOO_SMALL) {			ERR_PRT((L"failed to obtain memory map %r"));			return -1;		}		desc->map_size += ELILO_MEMMAP_INC;	}	DBG_PRT((L"final get_memmap map_size=%ld", desc->map_size));	return 0;}#if 0INTNget_memmap(mmap_desc_t *desc){	EFI_STATUS status;	/* will get the right size in return */	desc->map_size = 0;	status = BS->GetMemoryMap(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);	if (status != EFI_BUFFER_TOO_SMALL) return -1;	desc->md = (EFI_MEMORY_DESCRIPTOR *)alloc(desc->map_size, EfiLoaderData);	if (desc->md == NULL) {		ERR_PRT((L"failed to allocate memory map buffer"));		return -1;	}	status = BS->GetMemoryMap(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);	if (EFI_ERROR(status)) {		ERR_PRT((L"failed to obtain memory map %d: %r", desc->map_size, status));		free(desc->md);		return -1;	}	DBG_PRT((L"final get_memmap map_size=%d", desc->map_size));	return 0;}#endifVOIDfree_memmap(mmap_desc_t *desc){	if (desc->md) {		free(desc->md);		desc->md = NULL;	}}VOIDprint_memmap(mmap_desc_t *desc){	EFI_MEMORY_DESCRIPTOR *md;	UINTN desc_size;	VOID *p;	VOID *md_end;	INT8 printed;	UINTN ntypes;	CHAR16* str;	static CHAR16 *memtypes[]={		L"ReservedMemoryType",		L"LoaderCode",		L"LoaderData",		L"BootServicesCode",		L"BootServicesData",		L"RuntimeServicesCode",		L"RuntimeServicesData",		L"ConventionalMemory",		L"UnusableMemory",		L"ACPIReclaimMemory",		L"ACPIMemoryNVS",		L"MemoryMappedIO",		L"MemoryMappedIOPortSpace",		L"PalCode"	};	md_end = ((VOID *)desc->md)+desc->map_size;	desc_size = desc->desc_size;	ntypes = sizeof(memtypes)/sizeof(CHAR16 *);	for(p = desc->md; p < md_end; p += desc_size) {		md = p;		str = md->Type < ntypes ? memtypes[md->Type] : L"Unknown";		Print(L"%24s %lx-%lx %8lx", str, md->PhysicalStart,				md->PhysicalStart+(md->NumberOfPages<<EFI_PAGE_SHIFT),				md->NumberOfPages);		printed=0;#define P_FLG(f)	{ \	Print(L" %s %s", printed ? L"|":L"", f); \	printed=1; \}		if (md->Attribute & EFI_MEMORY_UC) {			P_FLG(L"UC");		}		if (md->Attribute & EFI_MEMORY_WC) {			P_FLG(L"WC");		}		if (md->Attribute & EFI_MEMORY_WT) {			P_FLG(L"WT");		}		if (md->Attribute & EFI_MEMORY_WB) {			P_FLG(L"WB");		}		if (md->Attribute & EFI_MEMORY_UCE) {			P_FLG(L"UCE");		}		if (md->Attribute & EFI_MEMORY_WP) {			P_FLG(L"WP");		}		if (md->Attribute & EFI_MEMORY_RP) {			P_FLG(L"RP");		}		if (md->Attribute & EFI_MEMORY_XP) {			P_FLG(L"XP");		}		if (md->Attribute & EFI_MEMORY_RUNTIME) {			P_FLG(L"RT");		}		Print(L"\n");	}}INTNfind_kernel_memory(VOID* low_addr, VOID* max_addr, UINTN alignment, VOID** start){       #define HIGHEST_ADDR (VOID*)(~0)        mmap_desc_t mdesc;        EFI_MEMORY_DESCRIPTOR *md;	UINT64 size;	VOID *p, *addr;	VOID *desc_end, *md_end, *best_addr = HIGHEST_ADDR;	/*	 * first get up-to-date memory map	 *	 * XXX: is there a danger of not seeing the latest version if interrupted	 * during our scan ?	 *	 */        if (get_memmap(&mdesc) == -1) {                ERR_PRT((L"find_kernel_memory :GetMemoryMap() failed"));                return -1;        }	desc_end = ((VOID *)mdesc.md) + mdesc.map_size;	size     = max_addr - low_addr;        /*	 * Find memory which covers the desired range	 */	for(p = mdesc.md; p < desc_end; p += mdesc.desc_size) {		md = p;		/*		 * restrict to decent memory types. 		 *		 * the EFI memory map report where memory is and how it is currently used	 	 * using types.		 *		 * EfiLoaderData which is used by the AllocatePages() cannot be used		 * here because it may hold some valid information. Same thing for most		 * of the memory types with the exception of EfiConventional which 		 * can be assumed as being free to use. 		 */		if (md->Type != EfiConventionalMemory) continue;		/* 		 * compute aligned address and upper boundary for range		 */		md_end = (VOID*)(md->PhysicalStart + md->NumberOfPages * EFI_PAGE_SIZE);			addr   = (VOID*)ROUNDUP(md->PhysicalStart, alignment);		/*		 * need to check if:		 * - aligned address still in the range		 * - the range [addr-addr+size) still fits into memory range		 * if so we have a match. We do not assume that the memory ranges		 * are sorted by EFI, therefore we must record the match and only		 * keep the lowest possible one.		 */		if (addr < best_addr && addr < md_end && addr+size <= md_end) best_addr = addr;        }        if (best_addr == HIGHEST_ADDR) {                free_memmap(&mdesc);                ERR_PRT((L"Could not find memory suitable for loading image"));                return -1;        }        *start = best_addr;        free_memmap(&mdesc);        return 0;}

⌨️ 快捷键说明

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