windows-nt/Source/XPSP1/NT/base/efiutil/diskpart/diskpart.c
2020-09-26 16:20:57 +08:00

1973 lines
47 KiB
C

/*
Module Name:
DiskPart - GUID Partition Table scheme disk partitioning program.
(The GPT version of FDISK, if you will)
Abstract:
Revision History
*/
#include "DiskPart.h"
#include "symbols.h"
#include "helpmsg.h"
//
// Globals
//
UINTN DebugLevel = DEBUG_NONE;
//UINTN DebugLevel = DEBUG_OPPROMPT;
EFI_STATUS status;
EFI_HANDLE *DiskHandleList = NULL;
INTN DiskHandleCount = 0;
INTN SelectedDisk = -1;
EFI_HANDLE SavedImageHandle;
EFI_STATUS ParseAndExecuteCommands();
BOOLEAN ExecuteSingleCommand(CHAR16 *Token[]);
VOID DumpGPT(
EFI_HANDLE DiskHandle,
PGPT_HEADER Header,
PGPT_TABLE Table,
BOOLEAN Raw,
BOOLEAN Verbose
);
VOID
PrintGptEntry(
GPT_ENTRY *Entry,
UINTN Index
);
EFI_GUID
GetGUID(
);
#define CLEAN_RANGE (1024*1024)
CHAR16 *TokenChar =
L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_+[]{}':;/?.>,<\\|";
VOID CmdInspectMBR(CHAR16 **Token);
VOID CmdCreateMBR(CHAR16 **Token);
VOID CmdNewMBR(CHAR16 **Token);
VOID CmdDeleteMBR(CHAR16 **Token);
//
// Worker function type
//
typedef
BOOLEAN
(*PCMD_FUNCTION)(
CHAR16 **Token
);
EFI_STATUS
ReinstallFSDStack(
);
//
// The parse table structure
//
typedef struct {
CHAR16 *Name;
PCMD_FUNCTION Function;
CHAR16 *HelpSummary;
} CMD_ENTRY;
//
// The parse/command table
//
CMD_ENTRY CmdTable[] = {
{ STR_LIST, CmdList, MSG_LIST },
{ STR_SELECT, CmdSelect, MSG_SELECT },
{ STR_INSPECT, CmdInspect, MSG_INSPECT },
{ STR_CLEAN, CmdClean, MSG_CLEAN },
{ STR_NEW, CmdNew, MSG_NEW },
{ STR_FIX, CmdFix, MSG_FIX },
{ STR_CREATE, CmdCreate, MSG_CREATE },
{ STR_DELETE, CmdDelete, MSG_DELETE },
{ STR_HELP, CmdHelp, MSG_HELP },
{ STR_HELP2, CmdHelp, MSG_ABBR_HELP },
{ STR_HELP3, CmdHelp, MSG_ABBR_HELP },
{ STR_EXIT, CmdExit, MSG_EXIT },
{ STR_SYMBOLS, CmdSymbols, MSG_SYMBOLS },
{ STR_REMARK, CmdRemark, MSG_REMARK },
{ STR_MAKE, CmdMake, MSG_MAKE },
{ STR_DEBUG, CmdDebug, NULL },
{ STR_ABOUT, CmdAbout, MSG_ABOUT },
{ NULL, NULL, NULL }
};
EFI_STATUS
EfiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Initialize the Library.
//
SavedImageHandle = ImageHandle;
InitializeLib (ImageHandle, SystemTable);
Print(L"DiskPart Version 0.2\n");
Print(L"Based on EFI core release ");
Print(MSG_ABOUT02,
EFI_SPECIFICATION_MAJOR_REVISION,
EFI_SPECIFICATION_MINOR_REVISION,
EFI_FIRMWARE_MAJOR_REVISION,
EFI_FIRMWARE_MINOR_REVISION
);
//
// See if there are any disks that look like candidates to be partitioned
//
status = FindPartitionableDevices(&DiskHandleList, &DiskHandleCount);
if (EFI_ERROR(status)) {
Print(L"%s\n", MSG_NO_DISKS);
return EFI_NOT_FOUND;
}
//
// Start Parse Loop
//
ParseAndExecuteCommands();
ReinstallFSDStack();
DoFree(DiskHandleList);
return status;
}
EFI_STATUS
ReinstallFSDStack(
)
/*++
Make sure that the partition driver is alerted to the changes in the
the file system structures. One way to effect this end is to
reinstall all of the blkio interfaces such that the partition driver
will receive a notification and probe the partition tables to reconstruct
the file system stack.
--*/
{
INTN Index;
EFI_STATUS Status;
EFI_BLOCK_IO *IBlkIo;
Status = EFI_SUCCESS;
for (Index = 0; Index < DiskHandleCount; Index++) {
Status = BS->HandleProtocol(
DiskHandleList[Index],
&BlockIoProtocol,
&IBlkIo
);
if (!EFI_ERROR(Status)) {
Status = BS->ReinstallProtocolInterface (
DiskHandleList[Index],
&BlockIoProtocol,
IBlkIo,
IBlkIo
);
}
}
return Status;
}
EFI_STATUS
ParseAndExecuteCommands()
/*
ParseAndExecuteCommands reads 1 line at a time from stdin.
Lines are parsed for commands and arguments.
The symbol ";" (semicolon) is used to mark the end of a command
and start a new one.
\ is the escape, \\ => '\'
All commands are upper cased before parsing to give us
case insensitive operations. This does not apply to literal
strings.
Any white space is treated a token separator.
Commandline is set quite long.
*/
{
CHAR16 *Prompt = MSG_PROMPT;
CHAR16 CommandLine[COMMAND_LINE_MAX];
CHAR16 *Token[TOKEN_COUNT_MAX];
UINTN i;
NewLine:
while (TRUE) {
for (i = 0; i < COMMAND_LINE_MAX; i++ ) {
CommandLine[i] = NUL;
}
Input(Prompt, CommandLine, COMMAND_LINE_MAX);
Print(L"\n");
if (CommandLine[0] == NUL) {
continue;
}
StrUpr(CommandLine);
Tokenize(CommandLine, Token);
if (Token[0] == (CHAR16 *)-1) {
//
// syntax error
//
Print(L"???\n");
goto NewLine;
}
if (ExecuteSingleCommand(Token) == TRUE) {
return TRUE;
}
if (DebugLevel >= DEBUG_ERRPRINT) {
if (EFI_ERROR(status)) {
Print(L"status = %x %r\n", status, status);
}
}
}
return EFI_SUCCESS;
}
VOID
Tokenize(
CHAR16 *CommandLine,
CHAR16 *Token[]
)
/*
Tokenize -
find the tokens, where a token is any string of letter & numbers.
tokens to contain blanks, etc, must begin end with "
return
Token[] set. if Token[0] = NULL, nothing in THIS command
*/
{
CHAR16 *ch;
UINTN tx;
//
// init the token array
//
for (tx = 0; tx < TOKEN_COUNT_MAX; tx++) {
Token[tx] = NULL;
}
tx = 0;
//
// sweep for tokens
//
ch = CommandLine;
while (TRUE) {
//
// if we see a quote, advance to the closing quote
// and call the result a token.
//
if (*ch == '"') {
ch++;
Token[tx] = ch;
while ((*ch != '"') && (*ch != NUL)) {
ch++;
}
if (*ch == '"') {
//
// we have the closing ", we have a token
//
*ch = NUL; // mark end of token
ch++;
tx++;
} else {
Token[0] = (CHAR16 *)-1; // report error
return;
}
} else {
//
// not a quoted string, so pick off a normal token
//
// Start by finding start of token
//
for ( ; *ch != NUL; ch++) {
if (IsIn(*ch, TokenChar)) {
Token[tx] = ch;
tx++;
break;
}
}
while (IsIn(*ch, TokenChar)) {
ch++;
}
//
// if we're at the end of the command line, we're done
// else, trim off token and go on
//
if (*ch == NUL) {
//
// we hit the end
//
Token[tx] = NULL;
return;
} else {
*ch = NUL;
ch++;
}
} // else
} // while
}
BOOLEAN
ExecuteSingleCommand(
CHAR16 *Token[]
)
/*
Returns TRUE to tell program to exit, else FALSE
*/
{
UINTN i;
for (i = 0; CmdTable[i].Name != NULL; i++) {
if (StrCmp(CmdTable[i].Name, Token[0]) == 0) {
return CmdTable[i].Function(Token);
}
}
//
// If we're here, we didn't understand the command
//
Print(L"%s\n%s\n", MSG_BAD_CMD, MSG_GET_HELP);
return FALSE;
}
BOOLEAN
CmdAbout(
CHAR16 **Token
)
{
Print(MSG_ABOUT02,
EFI_FIRMWARE_MAJOR_REVISION,
EFI_FIRMWARE_MINOR_REVISION,
EFI_FIRMWARE_MAJOR_REVISION,
EFI_FIRMWARE_MINOR_REVISION
);
return FALSE;
}
BOOLEAN
CmdList(
CHAR16 **Token
)
/*
CmdList - print the list of Partionable Disks
Globals: DiskHandleList, DiskHandleCount
Cmd Args: None.
*/
{
INTN i;
EFI_BLOCK_IO *BlkIo;
CHAR16 c;
Print(MSG_LIST01);
Print(MSG_LIST01B);
for (i = 0; i < DiskHandleCount; i++) {
status = BS->HandleProtocol(DiskHandleList[i], &BlockIoProtocol, &BlkIo);
if (i == SelectedDisk) {
c = '*';
} else {
c = ' ';
}
if (EFI_ERROR(status)) {
Print(MSG_LIST03, i);
} else {
Print(
MSG_LIST02,
c,
i,
BlkIo->Media->BlockSize,
BlkIo->Media->LastBlock+1
);
}
}
return FALSE;
}
BOOLEAN
CmdSelect(
CHAR16 **Token
)
/*
CmdSelect - Select the disk that most commands operate on.
Globals: SelectedDisk, DiskHandleCount
Options: None.
Cmd Args: none for display, number to select
*/
{
INTN NewSelect;
if (Token[1] == NULL) {
if (SelectedDisk == -1) {
Print(MSG_SELECT01);
} else {
Print(MSG_SELECT02, SelectedDisk);
}
} else {
NewSelect = Atoi(Token[1]);
if ((NewSelect >= 0) &&
(NewSelect < DiskHandleCount) &&
(IsIn(*Token[1], L"0123456789")) )
{
SelectedDisk = NewSelect;
Print(MSG_SELECT02, SelectedDisk);
} else {
Print(MSG_SELECT03);
}
}
return FALSE;
}
BOOLEAN
CmdInspect(
CHAR16 **Token
)
/*
CmdInspect - report data on the currently selected disk
Globals: SelectedDisk, DiskHandleList
Cmd Args: [RAW] [VER]
*/
{
EFI_HANDLE DiskHandle;
UINTN DiskType = 0;
PGPT_HEADER Header = NULL;
PGPT_TABLE Table = NULL;
PLBA_BLOCK LbaBlock = NULL;
BOOLEAN Raw;
BOOLEAN Verbose;
UINTN i;
if (SelectedDisk == -1) {
Print(MSG_INSPECT01);
return FALSE;
}
Print(MSG_SELECT02, SelectedDisk);
DiskHandle = DiskHandleList[SelectedDisk];
status = ReadGPT(DiskHandle, &Header, &Table, &LbaBlock, &DiskType);
if (EFI_ERROR(status)) {
return FALSE;
}
if (DiskType == DISK_RAW) {
Print(MSG_INSPECT04);
goto Exit;
} else if (DiskType == DISK_MBR) {
CmdInspectMBR(Token);
goto Exit;
} else if (DiskType == DISK_GPT_BAD) {
Print(MSG_INSPECT06);
goto Exit;
} else if ( (DiskType != DISK_GPT_UPD) &&
(DiskType != DISK_GPT))
{
TerribleError(L"Bad Disk Type returnted to Inspect!");
goto Exit;
}
if (DiskType == DISK_GPT_UPD) {
Print(MSG_INSPECT03);
}
if ( (Token[1]) &&
(StrCmp(Token[1], STR_HELP) == 0) )
{
PrintHelp(InspectHelpText);
goto Exit;
}
Raw = FALSE;
Verbose = FALSE;
for (i = 1; Token[i]; i++) {
if (StrCmp(Token[i], STR_RAW) == 0) {
Raw = TRUE;
} else if (StrCmp(Token[i], STR_VER) == 0) {
Verbose = TRUE;
} else {
PrintHelp(InspectHelpText);
goto Exit;
}
}
DumpGPT(DiskHandle, Header, Table, Raw, Verbose);
Exit:
DoFree(Header);
DoFree(Table);
DoFree(LbaBlock);
return FALSE;
}
typedef struct {
UINTN Slot;
EFI_LBA StartingLBA;
} SORT_SLOT_ENTRY;
VOID
DumpGPT(
EFI_HANDLE DiskHandle,
PGPT_HEADER Header,
PGPT_TABLE Table,
BOOLEAN Raw,
BOOLEAN Verbose
)
/*
DumpGPT - print out the GPT passed in via Header and Table
if (Raw) print slot order, all slots, table order.
else print only allocated slots, StartingLBA order.
if (Verbose) print out the Header data.
*/
{
EFI_BLOCK_IO *BlkIo;
UINTN i;
UINTN AllocatedSlots;
CHAR16 Buffer[PART_NAME_LEN+1];
BOOLEAN changed;
SORT_SLOT_ENTRY *SortSlot;
GPT_ENTRY Entry;
UINTN tslot;
EFI_LBA tlba;
SortSlot = DoAllocate(Header->EntriesAllocated * sizeof(SORT_SLOT_ENTRY));
if (SortSlot == NULL) {
status = EFI_OUT_OF_RESOURCES;
Print(MSG_INSPECT02);
return;
}
//
// Dump the handle data just as List would
//
BS->HandleProtocol(DiskHandle, &BlockIoProtocol, &BlkIo);
Print(MSG_LIST01);
Print(MSG_LIST01B);
Print(MSG_LIST02, '*', SelectedDisk, BlkIo->Media->BlockSize, BlkIo->Media->LastBlock+1);
if (Verbose) {
//
// Dump the header
//
Print(L"\nHeader Structure\n");
Print(L" Signature= %16lx Revision=%8X\n",
Header->Signature, Header->Revision);
Print(L" HeaderSize=%8x HeaderCRC32=%8x\n",
Header->HeaderSize, Header->HeaderCRC32);
Print(L" MyLBA=%16lx AlternateLBA=%16lx\n",
Header->MyLBA, Header->AlternateLBA);
Print(L" FirstUsableLBA=%16lx LastUsableLBA=%16lx\n",
Header->FirstUsableLBA, Header->LastUsableLBA);
Print(L" TableLBA=%16lx\n", Header->TableLBA);
Print(L" EntrySize=%8x EntriesAllowed=%8x TableCRC=%8x\n\n",
Header->SizeOfGPT_ENTRY, Header->EntriesAllocated, Header->TableCRC32);
}
//
// Print the Table of GPT entries
//
if (!Raw) {
//
// !Raw == Cooked -> Print the Allocated entries in StartingLBA
// SORTED order...
//
// Find ALL of the allocated entries
//
AllocatedSlots = 0;
for (i = 0; i < Header->EntriesAllocated; i++) {
CopyMem(&Entry, &Table->Entry[i], sizeof(GPT_ENTRY));
if (CompareMem(&(Entry.PartitionType), &GuidNull, sizeof(EFI_GUID)) != 0) {
SortSlot[AllocatedSlots].Slot = i;
SortSlot[AllocatedSlots].StartingLBA = Entry.StartingLBA;
AllocatedSlots++;
}
}
// j has the count of allocated entries
//
// Sort them - yes this is a bubble sort, but the list is probably
// in order and probably small, so for the vastly typical case
// this is actually optimal
//
if (AllocatedSlots > 0) {
do {
changed = FALSE;
for (i = 0; i < AllocatedSlots-1; i++) {
if (SortSlot[i].StartingLBA > SortSlot[i+1].StartingLBA) {
tslot = SortSlot[i+1].Slot;
tlba = SortSlot[i+1].StartingLBA;
changed = TRUE;
SortSlot[i+1].Slot = SortSlot[i].Slot;
SortSlot[i+1].StartingLBA = SortSlot[i].StartingLBA;
SortSlot[i].Slot = tslot;
SortSlot[i].StartingLBA = tlba;
}
}
} while (changed);
//
// Print them, but print the SLOT number, not the row number.
// This is to make Delete be reliable.
//
for (i = 0; i < AllocatedSlots; i++) {
PrintGptEntry(&Table->Entry[SortSlot[i].Slot], SortSlot[i].Slot);
if (((i+1) % 4) == 0) {
Input(MSG_MORE, Buffer, PART_NAME_LEN);
}
}
}
} else {
//
// Raw -> Print ALL of the entries in Table order
// (mostly for test, debug, looking at cratered disks
//
Print(L"RAW RAW RAW\n");
for (i = 0; i < Header->EntriesAllocated; i++) {
PrintGptEntry(&Table->Entry[i], i);
if (((i+1) % 4) == 0) {
Input(MSG_MORE, Buffer, PART_NAME_LEN);
}
}
Print(L"RAW RAW RAW\n");
}
DoFree(SortSlot);
return;
}
VOID
PrintGptEntry(
GPT_ENTRY *Entry,
UINTN Index
)
{
CHAR16 Buffer[PART_NAME_LEN+1];
UINTN j;
Print(L"\n%3d: ", Index);
ZeroMem(Buffer, (PART_NAME_LEN+1)*sizeof(CHAR16));
CopyMem(Buffer, &(Entry->PartitionName), PART_NAME_LEN*sizeof(CHAR16));
Print(L"%s\n ", Buffer);
PrintGuidString(&(Entry->PartitionType));
for (j = 0; SymbolList[j].SymName; j++) {
if (CompareMem(&(Entry->PartitionType), SymbolList[j].Value, sizeof(EFI_GUID)) == 0) {
Print(L" = %s", SymbolList[j].SymName);
}
}
if (CompareMem(&(Entry->PartitionType), &GuidNull, sizeof(EFI_GUID)) == 0) {
Print(L" = UNALLOCATED SLOT");
}
Print(L"\n ");
PrintGuidString(&(Entry->PartitionID));
Print(L" @%16x\n", Entry->Attributes);
Print(L" %16lx - %16lx\n",
Entry->StartingLBA,
Entry->EndingLBA
);
}
BOOLEAN
CmdClean(
CHAR16 **Token
)
/*
CmdClean - Clean off the disk
Globals: SelectedDisk, DiskHandleList
Cmd Args: ALL to clean whole disk, rather than just 1st and last megabyte
We write out 1 block at a time. While this is slow, it avoids wondering
about the Block protocol write size limit, and about how big a buffer
we can allocate.
*/
{
EFI_HANDLE DiskHandle;
CHAR16 Answer[COMMAND_LINE_MAX];
BOOLEAN CleanAll;
UINT32 BlockSize;
UINT64 DiskSize;
UINT64 DiskBytes;
UINT64 RangeBlocks;
UINT64 EndRange;
UINT64 i;
CHAR8 *zbuf;
if (SelectedDisk == -1) {
Print(MSG_INSPECT01);
return FALSE;
}
DiskHandle = DiskHandleList[SelectedDisk];
BlockSize = GetBlockSize(DiskHandle);
DiskSize = GetDiskSize(DiskHandle);
DiskBytes = MultU64x32(DiskSize, BlockSize);
zbuf = DoAllocate(CLEAN_RANGE);
if (zbuf == NULL) return FALSE;
ZeroMem(zbuf, CLEAN_RANGE);
//
// Are you sure?
//
Print(MSG_CLEAN01, SelectedDisk);
Input(STR_CLEAN_PROMPT, Answer, COMMAND_LINE_MAX);
StrUpr(Answer);
Print(L"\n");
if (StrCmp(Answer, L"Y") != 0) {
DoFree(zbuf);
return FALSE;
}
//
// Are you REALLY Sure?
//
Print(MSG_CLEAN02);
Input(STR_CLEAN_PROMPT, Answer, COMMAND_LINE_MAX);
Print(L"\n");
if (StrCmp(Answer, STR_CLEAN_ANS) != 0) {
DoFree(zbuf);
return FALSE;
}
//
// OK, the user really wants to do this
//
//
// All? or just start and end?
//
CleanAll = FALSE;
if (Token[1]) {
if (StrCmp(Token[1], STR_CLEAN03) == 0) {
CleanAll = TRUE;
}
}
if (DiskBytes > (2 * CLEAN_RANGE)) {
RangeBlocks = CLEAN_RANGE / BlockSize;
WriteBlock(DiskHandle, zbuf, 0, CLEAN_RANGE);
EndRange = DiskSize - RangeBlocks;
if (CleanAll) {
for (i=RangeBlocks; i < DiskSize; i += RangeBlocks) {
WriteBlock(DiskHandle, zbuf, i, CLEAN_RANGE);
}
}
WriteBlock(DiskHandle, zbuf, EndRange, CLEAN_RANGE);
} else {
//
// Kind of a small disk, clean it all always
//
for (i = 0; i < DiskSize; i++) {
WriteBlock(DiskHandle, zbuf, i, BlockSize);
}
}
FlushBlock(DiskHandle);
DoFree(zbuf);
return FALSE;
}
BOOLEAN
CmdNew(
CHAR16 **Token
)
/*
CmdNew [mbr | [gpt=numentry]
Changes a RAW disk into either an MBR (well, somebody) or GPT disk
"new mbr" - you want an mbr disk (not implemented)
"new gpt" - you want a gpt disk, you get a default table
"new gpt=xyz" - you want a gpt disk, with at least xyz entries
(you will get less than xyz if exceeds sanity threshold)
anything else - try again with right syntax
*/
{
EFI_HANDLE DiskHandle;
PGPT_HEADER Header;
PGPT_TABLE Table;
PLBA_BLOCK LbaBlock;
UINTN DiskType;
UINTN GptOptSize;
if (SelectedDisk == -1) {
Print(MSG_INSPECT01);
return FALSE;
}
DiskHandle = DiskHandleList[SelectedDisk];
status = ReadGPT(DiskHandle, &Header, &Table, &LbaBlock, &DiskType);
if (EFI_ERROR(status)) {
return FALSE;
}
if (DiskType != DISK_RAW) {
Print(MSG_NEW01, SelectedDisk);
Print(MSG_NEW02);
return FALSE;
}
//
// OK, it's a raw disk...
//
GptOptSize = 0;
if (Token[1]) {
if (StrCmp(Token[1], STR_GPT) == 0) {
if (Token[2]) {
GptOptSize = Atoi(Token[2]);
}
CreateGPT(DiskHandle, GptOptSize);
} else if (StrCmp(Token[1], STR_MBR) == 0) {
CmdNewMBR(Token);
}
} else {
Print(MSG_NEW03);
}
return FALSE;
}
BOOLEAN
CmdFix(
CHAR16 **Token
)
/*
CmdFix - very basic tool to try to fix up GPT disks.
Basic strategy is to read the GPT, if it seems to be a
GPT disk (not MBR, RAW, or totally dead) then call
WriteGPT, which will write both GPTs (and thus sync them)
and rebuild the shadow MBR, all as a matter of course.
*/
{
EFI_HANDLE DiskHandle;
PGPT_HEADER Header = NULL;
PGPT_TABLE Table = NULL;
PLBA_BLOCK LbaBlock = NULL;
UINTN DiskType;
//
// Setup parameters and error handling
//
if (SelectedDisk == -1) {
Print(MSG_INSPECT01);
return FALSE;
}
if ( (Token[1]) &&
(StrCmp(Token[1], STR_HELP) == 0) )
{
PrintHelp(FixHelpText);
return FALSE;
}
DiskHandle = DiskHandleList[SelectedDisk];
status = ReadGPT(DiskHandle, &Header, &Table, &LbaBlock, &DiskType);
if (EFI_ERROR(status)) {
Print(MSG_FIX05);
return FALSE;
}
//
// From this point on, must exit this Procedure with a goto Exit
// to free up allocated stuff, otherwise we leak pool...
//
if (DiskType == DISK_RAW) {
Print(MSG_FIX01);
goto Exit;
}
if (DiskType == DISK_MBR) {
Print(MSG_FIX02);
goto Exit;
}
if ((DiskType != DISK_GPT) &&
(DiskType != DISK_GPT_UPD)) {
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"DiskType = %d\n", DiskType);
}
Print(MSG_FIX03);
goto Exit;
}
status = WriteGPT(DiskHandle, Header, Table, LbaBlock);
if (EFI_ERROR(status)) {
Print(MSG_FIX04);
}
Exit:
DoFree(Header);
DoFree(Table);
DoFree(LbaBlock);
return FALSE;
}
//
// ----- Create and sub procs thereof
//
BOOLEAN
CmdCreate(
CHAR16 **Token
)
/*
CmdCreate - Create a new partition
(Actually, this routine is a GPT only partition creator)
create name="name string" size=sss type=name typeguid=<guid> attributes=hex
name is label string
offset is in megabytes, or start at the end of the last partition if absent
size is in megabytes, or "fill the disk" if 0 or absent or > free space
type is any named symbol type (symbols gives list)
typeguid is an arbitrary type guid
attributes is hex 32bit flag
if "help" is first arg, print better help data
name, type or typeguid, required
if all that parses out OK, read the gpt, edit it, write it back,
and voila, we have a new partition.
*/
{
EFI_HANDLE DiskHandle;
PGPT_HEADER Header = NULL;
PGPT_TABLE Table = NULL;
PLBA_BLOCK LbaBlock = NULL;
UINTN DiskType;
UINT64 SizeInMegaBytes = 0;
UINT64 OffsetInBlocks = 0;
UINT64 StartBlock;
UINT64 EndBlock;
UINT64 SizeInBytes = 0;
UINT64 Attributes = 0;
UINTN i;
UINTN j;
EFI_GUID *TypeGuid = NULL;
EFI_GUID GuidBody;
EFI_GUID PartitionIdGuid;
CHAR16 *TypeName = NULL;
CHAR16 PartName[PART_NAME_LEN+1]; // 36 allowed by spec plus NUL we need
CHAR16 Buffer[10];
BOOLEAN Verbose = FALSE;
UINT32 BlockSize;
UINT64 DiskSizeBlocks;
UINT8 *p;
BOOLEAN OffsetSpecified = FALSE;
BOOLEAN AllZeros;
INTN AllZeroEntry;
INTN OldFreeEntry;
UINT64 AvailBlocks;
UINT64 BlocksToAllocate;
UINT64 HighSeen;
UINTN Slot;
//
// Setup parameters and error handling
//
if (SelectedDisk == -1) {
Print(MSG_INSPECT01);
return FALSE;
}
if (Token[1] == NULL) {
PrintHelp(CreateHelpText);
return FALSE;
}
if ( (Token[1]) &&
(StrCmp(Token[1], STR_HELP) == 0) )
{
PrintHelp(CreateHelpText);
return FALSE;
}
DiskHandle = DiskHandleList[SelectedDisk];
status = ReadGPT(DiskHandle, &Header, &Table, &LbaBlock, &DiskType);
if (EFI_ERROR(status)) {
return FALSE;
}
BlockSize = GetBlockSize(DiskHandle);
DiskSizeBlocks = GetDiskSize(DiskHandle);
//
// From this point on, must exit this Procedure with a goto Exit
// to free up allocated stuff, otherwise we leak pool...
//
if (DiskType == DISK_RAW) {
Print(MSG_CREATE01, SelectedDisk);
goto Exit;
}
if (DiskType == DISK_MBR) {
CmdCreateMBR(Token);
goto Exit;
}
if (DiskType != DISK_GPT) {
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"DiskType = %d\n", DiskType);
}
Print(MSG_CREATE02);
goto Exit;
}
//
// Parse arguments...
//
for (i = 1; Token[i]; i++) {
if (StrCmp(Token[i], STR_NAME) == 0) {
ZeroMem(PartName, (PART_NAME_LEN+1)*sizeof(CHAR16));
StrCpy(PartName, Token[i+1]);
i++;
} else if (StrCmp(Token[i], STR_TYPE) == 0) {
if (Token[i+1] == NULL) {
PrintHelp(CreateHelpText);
goto Exit;
}
for (j = 0; SymbolList[j].SymName != NULL; j++) {
if (StrCmp(SymbolList[j].SymName, Token[i+1]) == 0) {
TypeGuid = SymbolList[j].Value;
TypeName = SymbolList[j].SymName;
break;
}
}
if (TypeGuid == NULL) {
Print(MSG_CREATE03);
goto Exit;
}
i++;
} else if (StrCmp(Token[i], STR_TYPEGUID) == 0) {
if (Token[i+1] == NULL) {
PrintHelp(CreateHelpText);
goto Exit;
}
status = GetGuidFromString(Token[i+1], &GuidBody);
if (EFI_ERROR(status)) {
PrintHelp(CreateHelpText);
goto Exit;
}
TypeGuid = &GuidBody;
i++;
} else if (StrCmp(Token[i], STR_OFFSET) == 0) {
if (Token[i+1] == NULL) {
PrintHelp(CreateHelpText);
goto Exit;
}
OffsetInBlocks = Xtoi64(Token[i+1]);
OffsetSpecified = TRUE;
i++;
} else if (StrCmp(Token[i], STR_SIZE) == 0) {
if (Token[i+1] == NULL) {
PrintHelp(CreateHelpText);
goto Exit;
}
SizeInMegaBytes = Atoi64(Token[i+1]);
i++;
} else if (StrCmp(Token[i], STR_ATTR) == 0) {
if (Token[i+1] == NULL) {
PrintHelp(CreateHelpText);
goto Exit;
}
Attributes = Xtoi64(Token[i+1]);
i++;
} else if (StrCmp(Token[i], STR_VER) == 0) {
Verbose = TRUE;
// do NOT increment i, we only consumed 1 Token
} else {
Print(L"\n??? % ???\n", Token[i]);
PrintHelp(CreateHelpText);
goto Exit;
}
}
if ( (PartName == NULL) ||
(TypeGuid == NULL) )
{
PrintHelp(CreateHelpText);
goto Exit;
}
if ( (DebugLevel >= DEBUG_ARGPRINT) ||
(Verbose) )
{
Print(L"CmdCreate arguments:\n");
Print(L"SelectedDisk = %d\n", SelectedDisk);
Print(L"Name=%s\n", PartName);
Print(L"TypeGuid = ");
PrintGuidString(TypeGuid);
Print(L"\n");
if (TypeName) {
Print(L"TypeName = %s\n", TypeName);
}
Print(L"Requested OffsetInBlocks = %lx\n", OffsetInBlocks);
Print(L"Requested SizeInMegaBytes = %ld\n", SizeInMegaBytes);
Print(L"Attributes = %lx\n", Attributes);
}
if (DebugLevel >= DEBUG_OPPROMPT) {
Input(L"\nCreate = Enter to Continue\n", Buffer, 10);
}
//
// If Requested size is 0, or greater than size remaining,
// we want to fill the disk.
// Otherwise, use enough blocks to provide at *least* the
// required storage. (Not likely to be a problem...)
//
//
// First, scan the Table and decide where the first unallocated
// space is. Note that for this procedure's primitive space allocation,
// holes between the beginning of the first allocated partition and
// the last allocated partition are ignored.
//
AllZeroEntry = -1;
OldFreeEntry = -1;
HighSeen = Header->FirstUsableLBA - 1;
if (OffsetSpecified) {
//
// if offset is specified, compute the start and end blocks
//
StartBlock = OffsetInBlocks;
if (StartBlock < Header->FirstUsableLBA ||
StartBlock > Header->LastUsableLBA) {
//
// Offset specified is too large
//
status = EFI_INVALID_PARAMETER;
Print(MSG_CREATE08);
goto Exit;
}
SizeInBytes = MultU64x32(SizeInMegaBytes, (1024*1024));
if (SizeInBytes < SizeInMegaBytes || SizeInBytes == 0) {
//
// If size is not specified or too large,
// try to make the partition as big as it can be
//
BlocksToAllocate = EndBlock = SizeInBytes = 0xffffffffffffffff;
} else {
BlocksToAllocate = DivU64x32(SizeInBytes, BlockSize, NULL);
EndBlock = StartBlock + BlocksToAllocate - 1;
if (EndBlock > Header->LastUsableLBA) {
EndBlock = Header->LastUsableLBA;
BlocksToAllocate = EndBlock - StartBlock + 1;
}
}
}
for (i = 0; i < Header->EntriesAllocated; i++) {
if (CompareMem(
&(Table->Entry[i].PartitionType),
&GuidNull,
sizeof(EFI_GUID)
) != 0)
{
//
// Type not null, so it's allocated
//
if (Table->Entry[i].EndingLBA > HighSeen) {
HighSeen = Table->Entry[i].EndingLBA;
}
if (OffsetSpecified) {
//
// make sure new partition does not overlap with existing partitions
//
if (Table->Entry[i].StartingLBA <= StartBlock &&
StartBlock <= Table->Entry[i].EndingLBA) {
//
// starting block is inside an existing partition
//
status = EFI_INVALID_PARAMETER;
Print(MSG_CREATE08);
goto Exit;
}
if ((Table->Entry[i].StartingLBA <= EndBlock &&
EndBlock <= Table->Entry[i].EndingLBA) ||
(StartBlock <= Table->Entry[i].StartingLBA &&
Table->Entry[i].StartingLBA <= EndBlock) ||
(StartBlock <= Table->Entry[i].EndingLBA &&
Table->Entry[i].EndingLBA <= EndBlock)) {
//
// new partition overlaps with an existing partition
// readjust new partition size to avoid overlapping
//
EndBlock = Table->Entry[i].StartingLBA-1;
if (EndBlock < StartBlock) {
status = EFI_INVALID_PARAMETER;
Print(MSG_CREATE08);
goto Exit;
} else {
BlocksToAllocate = EndBlock - StartBlock + 1;
}
}
}
} else {
p = (UINT8 *)(&(Table->Entry[i]));
AllZeros = TRUE;
for (j = 0; j < sizeof(GPT_ENTRY); j++) {
if (p[j] != 0) {
AllZeros = FALSE;
}
}
if (AllZeros) {
if (AllZeroEntry == -1) {
AllZeroEntry = i;
}
} else if (OldFreeEntry == -1) {
OldFreeEntry = i;
}
}
}
//
// AllZeroEntry - if not -1, is pointer to a never before used entry (free)
// OldFreeEntry - if not -1, is pointer to some pre-used free entry
//
if ( (AllZeroEntry == -1) && (OldFreeEntry == -1) ) {
//
// TABLE IS FULL!!
//
status = EFI_OUT_OF_RESOURCES;
Print(MSG_CREATE04);
goto Exit;
}
if (OffsetSpecified) {
//
// the user haven't specified the new partition size and we haven't
// run into any partition that will limit the size of this new partition.
// So, use the max it can
//
if (BlocksToAllocate == -1) {
EndBlock = Header->LastUsableLBA;
BlocksToAllocate = EndBlock - StartBlock + 1;
}
} else {
//
// [HighSeen+1 ... LastUsableLBA] is available...
// avail = (LastUsableLBA - (HighSeen+1)) + 1 => LastUsabbleLBA - HighSeen
//
AvailBlocks = Header->LastUsableLBA - HighSeen;
if (AvailBlocks == 0) {
status = EFI_OUT_OF_RESOURCES;
Print(MSG_CREATE07);
goto Exit;
}
SizeInBytes = MultU64x32(SizeInMegaBytes, (1024*1024));
if (SizeInBytes < SizeInMegaBytes) {
//
// overflow, force a very big answer
//
SizeInBytes = 0xffffffffffffffff;
}
if ((SizeInBytes == 0) ||
(SizeInBytes > (MultU64x32(AvailBlocks, BlockSize)) ) )
{
//
// User asked for zero, or for more than we've got,
// so give them all that is left
//
BlocksToAllocate = AvailBlocks;
} else {
//
// We would have to have a BlockSize > 1mb for Remainder to
// not be 0. Since we cannot actually test this case, we
// ingore it...
//
BlocksToAllocate = DivU64x32(SizeInBytes, BlockSize, NULL);
}
}
//
// We have a name
// We have a type guid
// We have a size in blocks
// We have an attribute mask
//
if (BlocksToAllocate < ((1024*1024)/BlockSize)) {
status = EFI_OUT_OF_RESOURCES;
Print(MSG_CREATE09);
goto Exit;
}
if ( (Verbose) ||
(DebugLevel > DEBUG_ARGPRINT) )
{
Print(L"Requested SizeInMegaBytes = %ld\n", SizeInMegaBytes);
Print(L"Resulting size in Blocks = %ld\n", BlocksToAllocate);
Print(L"Results size in Bytes = %ld\n", MultU64x32(BlocksToAllocate, BlockSize));
}
if (AllZeroEntry != -1) {
Slot = AllZeroEntry;
} else {
Slot = OldFreeEntry;
}
PartitionIdGuid = GetGUID();
CopyMem(&(Table->Entry[Slot].PartitionType), TypeGuid, sizeof(EFI_GUID));
CopyMem(&(Table->Entry[Slot].PartitionID), &PartitionIdGuid, sizeof(EFI_GUID));
if (OffsetSpecified) {
Table->Entry[Slot].StartingLBA = StartBlock;
Table->Entry[Slot].EndingLBA = EndBlock;
} else {
Table->Entry[Slot].StartingLBA = HighSeen + 1;
Table->Entry[Slot].EndingLBA = HighSeen + BlocksToAllocate;
}
if (! ( ((Table->Entry[Slot].EndingLBA - Table->Entry[Slot].StartingLBA) + 1) == BlocksToAllocate) ) {
TerribleError(L"Wrong Size for new partiton in CmdCreate\n");
}
if ( (Table->Entry[Slot].StartingLBA < Header->FirstUsableLBA) ||
(Table->Entry[Slot].EndingLBA > Header->LastUsableLBA) )
{
TerribleError(L"New Partition out of bounds in CmdCreate\n");
}
Table->Entry[Slot].Attributes = Attributes;
CopyMem(&(Table->Entry[Slot].PartitionName[0]), PartName, PART_NAME_LEN*sizeof(CHAR16));
status = WriteGPT(DiskHandle, Header, Table, LbaBlock);
if (EFI_ERROR(status)) {
Print(MSG_CREATE05);
}
Exit:
DoFree(Header);
DoFree(Table);
DoFree(LbaBlock);
return FALSE;
}
//
// -----
//
BOOLEAN
CmdDelete(
CHAR16 **Token
)
/*
CmdDelete - deletes a partition from the currently selected disk
*/
{
EFI_HANDLE DiskHandle;
UINTN DiskType = 0;
PGPT_HEADER Header = NULL;
PGPT_TABLE Table = NULL;
PLBA_BLOCK LbaBlock = NULL;
INTN Victim;
CHAR16 Answer[COMMAND_LINE_MAX];
GPT_ENTRY Entry;
if (SelectedDisk == -1) {
Print(MSG_INSPECT01);
return FALSE;
}
if (Token[1] == NULL) {
PrintHelp(DeleteHelpText);
return FALSE;
}
if ( (Token[1]) &&
(StrCmp(Token[1], STR_HELP) == 0) )
{
PrintHelp(DeleteHelpText);
return FALSE;
}
Print(MSG_SELECT02, SelectedDisk);
DiskHandle = DiskHandleList[SelectedDisk];
status = ReadGPT(DiskHandle, &Header, &Table, &LbaBlock, &DiskType);
if (EFI_ERROR(status)) {
return FALSE;
}
if (DiskType == DISK_RAW) {
Print(MSG_DELETE02);
goto Exit;
} else if (DiskType == DISK_MBR) {
CmdInspectMBR(Token);
goto Exit;
} else if (DiskType == DISK_GPT_UPD) {
Print(MSG_DELETE03);
goto Exit;
} else if (DiskType == DISK_GPT_BAD) {
Print(MSG_DELETE04);
goto Exit;
} else if (DiskType != DISK_GPT) {
TerribleError(L"Bad Disk Type returned to Delete!");
goto Exit;
}
//
// OK, it's a Good GPT disk, so do the Delete thing for GPT...
//
if ( (Token[1] == NULL) ||
(Token[2] != NULL) )
{
PrintHelp(InspectHelpText);
goto Exit;
}
Victim = Atoi(Token[1]);
if ( (Victim < 0) ||
((UINT32)Victim > Header->EntriesAllocated) )
{
Print(MSG_DELETE05);
goto Exit;
}
CopyMem(&Entry, &Table->Entry[Victim], sizeof(GPT_ENTRY));
if (CompareMem(&(Entry.PartitionType), &GuidNull, sizeof(EFI_GUID)) == 0) {
Print(MSG_DELETE06);
goto Exit;
}
//
// What you are going to delete, are you sure, are you really sure...
//
Print(MSG_DELETE07, Victim);
PrintGptEntry(&Entry, Victim);
Print(L"\n\n");
Print(MSG_DELETE09);
Print(MSG_DELETE10);
Input(STR_DELETE_PROMPT, Answer, COMMAND_LINE_MAX);
Print(L"\n");
StrUpr(Answer);
if (StrCmp(Answer, L"Y") != 0) {
goto Exit;
}
Print(MSG_DELETE11);
Input(STR_DELETE_PROMPT, Answer, COMMAND_LINE_MAX);
Print(L"\n");
StrUpr(Answer);
if (StrCmp(Answer, STR_DELETE_ANS) != 0) {
goto Exit;
}
//
// If we get here, then...
// Victim is the number of legal GPT slot
// Victim refers to a slot which is allocated
// The user has seen confirmation of which slot that is
// The user says they realy truly want to delete it
//
CopyMem(&(Table->Entry[Victim].PartitionType), &GuidNull, sizeof(EFI_GUID));
status = WriteGPT(DiskHandle, Header, Table, LbaBlock);
if (EFI_ERROR(status)) {
Print(MSG_DELETE08);
}
Exit:
DoFree(Header);
DoFree(Table);
DoFree(LbaBlock);
return FALSE;
}
BOOLEAN
CmdHelp(
CHAR16 **Token
)
{
UINTN i;
for (i = 0; CmdTable[i].Name != NULL; i++) {
Print(L"%s %s\n", CmdTable[i].Name, CmdTable[i].HelpSummary);
}
return FALSE;
}
BOOLEAN
CmdExit(
CHAR16 **Token
)
{
Print(L"%s\n", MSG_EXITING);
return TRUE;
}
BOOLEAN
CmdSymbols(
CHAR16 **Token
)
/*
CmdSymbols - print out the GUID symbols compiled into the program
For predefined symbol (see ...) we print it's friendly name,
it's text definition, and it's actual value.
*/
{
UINTN i;
EFI_GUID *Guid;
BOOLEAN Verbose = FALSE;
if ( (Token[1]) &&
(StrCmp(Token[1], STR_VER) == 0) )
{
Verbose = TRUE;
}
for (i = 0; SymbolList[i].SymName != NULL; i++) {
Guid = SymbolList[i].Value;
Print(L"%s = %s\n", SymbolList[i].SymName, SymbolList[i].Comment);
if (Verbose) {
PrintGuidString(Guid);
Print(L"\n\n");
}
}
return FALSE;
}
BOOLEAN
CmdRemark(
CHAR16 **Token
)
{
//
// The remark command does nothing...
return FALSE;
}
BOOLEAN
CmdMake(
CHAR16 **Token
)
{
UINTN i;
Token++;
if (Token[0] != NULL) {
for (i = 0; ScriptTable[i].Name != NULL; i++) {
if (StrCmp(ScriptTable[i].Name, Token[0]) == 0) {
return ScriptTable[i].Function(Token);
}
}
}
//
// Nothing we know about, so run list
//
return ScriptList(Token);
}
BOOLEAN
CmdDebug(
CHAR16 **Token
)
/*
Debug -
Without args, shows last status value, and AllocCount
If an arg, it sets the debug/checkout support level
0 = do nothing extra
1 = print full computed arguments before starting an operation
2 = print full computed arguments and hold for prompt before
doing a major operation.
*/
{
if (Token[1]) {
DebugLevel = Atoi(Token[1]);
}
Print(L"status = %x %r\n", status, status);
Print(L"AllocCount = %d\n", AllocCount);
Print(L"DebugLevel = %d\n", DebugLevel);
return FALSE;
}
//
// ----- SubUnits to do MBR operations -----
//
VOID
CmdInspectMBR(
CHAR16 **Token
)
/*
CmdInspectMBR - dumps the partition data for an MBR disk
*/
{
Print(MSG_INSPECT05);
return;
}
VOID
CmdCreateMBR(
CHAR16 **Token
)
/*
CmdCreateMBR - creates an MBR parititon
*/
{
Print(MSG_CREATE06);
return;
}
VOID
CmdNewMBR(
CHAR16 **Token
)
/*
CmdCreateMBR - creates an MBR parititon
*/
{
Print(MSG_NEW04);
return;
}
VOID
CmdDeleteMBR(
CHAR16 **Token
)
/*
CmdDeleteMBR - deletes an MBR parititon
*/
{
Print(MSG_DELETE01);
return;
}
//
// ----- Various Support Routines -----
//
VOID
PrintGuidString(
EFI_GUID *Guid
)
{
CHAR16 Buffer[40];
SPrint(Buffer, 40, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
Guid->Data1,
Guid->Data2,
Guid->Data3,
Guid->Data4[0],
Guid->Data4[1],
Guid->Data4[2],
Guid->Data4[3],
Guid->Data4[4],
Guid->Data4[5],
Guid->Data4[6],
Guid->Data4[7]
);
Print(L"%s", Buffer);
return;
}
EFI_STATUS
GetGuidFromString(
CHAR16 *String,
EFI_GUID *Guid
)
/*
GetGuidFromString
This routine scans the string looking for 32 hex digits. Each
such digits is shifted into the Guid. Non hex digits are skipped.
This means the guid MUST begin with the first digit, not with a filler
0 or 0x or the like. However, because non hex digits are skipped,
any set of dashes, dots, etc. may be used as punctuation.
So:
01234567-abcd-ef01-12-34-56-78-9a-bc-de-f0
&
01.23.45.67-ab.cd.ef.01-12.34.56.78-9a.bc.de.f0
Will create the same Guid value.
*/
{
EFI_GUID TempGuid;
INTN x;
UINTN i;
UINTN j;
//
// scan until we have the right number of hex digits for each part
// of the guid structure, skipping over non hex digits
//
ZeroMem((CHAR16 *)&TempGuid, sizeof(EFI_GUID));
//
// 1st uint32
//
for (i = 0; i < 8; String++) {
if (*String == NUL) {
status = EFI_INVALID_PARAMETER;
return status;
}
x = HexChar(*String);
if (x != -1) {
TempGuid.Data1 = (UINT32)((TempGuid.Data1 * 16) + x);
i++;
}
}
//
// 2nd unit - uint16
//
for (i = 0; i < 4; String++) {
if (*String == NUL) {
status = EFI_INVALID_PARAMETER;
return status;
}
x = HexChar(*String);
if (x != -1) {
TempGuid.Data2 = (TempGuid.Data2 * 16) + (UINT16)x;
i++;
}
}
//
// 3nd unit - uint16
//
for (i = 0; i < 4; String++) {
if (*String == NUL) {
status = EFI_INVALID_PARAMETER;
return status;
}
x = HexChar(*String);
if (x != -1) {
TempGuid.Data3 = (TempGuid.Data3 * 16) + (UINT16)x;
i++;
}
}
//
// 4th unit - 8 uint8s
//
for (j = 0; j < 8; j++) {
for (i = 0; i < 2; String++) {
if (*String == NUL) {
status = EFI_INVALID_PARAMETER;
return status;
}
x = HexChar(*String);
if (x != -1) {
TempGuid.Data4[j] = (TempGuid.Data4[j] * 16) + (UINT8)x;
i++;
}
}
}
CopyMem(Guid, &TempGuid, sizeof(EFI_GUID));
return status = EFI_SUCCESS;
}
INTN
HexChar(
CHAR16 Ch
)
/*
HexChar just finds the offset of Ch in the string "0123456789ABCDEF",
which in effect converts a hex digit to a number.
(a one char at a time xtoi)
If Ch isn't a hex digit, -1 is returned.
*/
{
UINTN i;
CHAR16 *String = L"0123456789ABCDEF";
for (i = 0; String[i] != NUL; i++) {
if (Ch == String[i]) {
return i;
}
}
return -1;
}
UINT64
Xtoi64(
CHAR16 *String
)
/*
Xtoi64 is NOT fully xtoi compatible, it requires that the hex
number start at the first character and stops at first non hex char
Always returns a 64bit value
*/
{
UINT64 BigHex;
INT32 x;
BigHex = 0;
x = (INT32)HexChar(*String);
while (x != -1) {
BigHex = MultU64x32(BigHex, 16) + x;
String++;
x = (INT32)HexChar(*String);
}
return BigHex;
}
UINT64
Atoi64(
CHAR16 *String
)
/*
Atoi64 is NOT fully atoi compatible, it requires that the number
start at the first character and stops at first non number char
Always returns a 64bit value
*/
{
UINT64 BigNum;
INT32 x;
BigNum = 0;
x = (INT32)HexChar(*String);
while ( (x >= 0) && (x <= 9) ) {
BigNum = MultU64x32(BigNum, 10);
BigNum = BigNum + x;
String++;
x = (INT32)HexChar(*String);
}
return BigNum;
}
BOOLEAN
IsIn(
CHAR16 What,
CHAR16 *InWhat
)
/*
IsIn - return TRUE if What is found in InWhat, else FALSE;
*/
{
UINTN i;
for (i = 0; InWhat[i] != NUL; i++) {
if (InWhat[i] == What) {
return TRUE;
}
}
return FALSE;
}
VOID
PrintHelp(
CHAR16 *HelpText[]
)
{
UINTN i;
for (i = 0; HelpText[i] != NULL; i++) {
Print(HelpText[i]);
}
return;
}
VOID
TerribleError(
CHAR16 *String
)
{
CHAR16 *Buffer;
Buffer = AllocatePool(512);
SPrint(Buffer, 512, L"Terrible Error = %s status=%x %r\nProgram terminated.\n", String, status, status);
Print(Buffer);
BS->Exit(SavedImageHandle, EFI_VOLUME_CORRUPTED, StrLen(Buffer), Buffer);
}