/* 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= 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); }