From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-pf0-x231.google.com (mail-pf0-x231.google.com [IPv6:2607:f8b0:400e:c00::231]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 8A6741A1E56 for ; Wed, 19 Oct 2016 18:31:56 -0700 (PDT) Received: by mail-pf0-x231.google.com with SMTP id e6so25503721pfk.3 for ; Wed, 19 Oct 2016 18:31:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id; bh=QuW/CnAuyITorX12a9W4t7Gh0tUlf1/zcPWwdV2msdQ=; b=Wnv4y4BV2gH0W24yzluMhFDa6NxQxYzNPiWJG+mJc2KOAbU3VYeV65lwxVNpdPzML7 2PYznZ1DbB4SUbVMuIREScFuUTSep8+a20ohcSnMjgfH2dfuggAg/LCWNoNoCJDeZ98g uqGDw8iCY8S+khHWVremkrh8Yl0gkyOUgPxAY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=QuW/CnAuyITorX12a9W4t7Gh0tUlf1/zcPWwdV2msdQ=; b=dh5uwiCyRuSK2qcaebHIkQe2RY3PAHRyRVs7svKejlpL0MfdDB74EvjxqyjKNEFiga B1iIRuo8RZitCuJ3wVcWu3NiPyYGm66b8jIJ8lZafsBcamQNLvi5A4B8suwLSOl087Uw mZtQuskw7RgmMYYj+4hSYu7pyP2psKCPYEAzS/n/JerR+t5Yj27AopjOw6I8eZ0qW3T9 aNnXyRpfbgp41La5PIMHuvXR/9mdPk0OAPyQpAookEJtw1t3a2V4q0zBuB8VnkHhMFvb K+2jrkyOBcqRKZDRR1OQ3lCeDUaez9VGD+4Ge183/PSCqLPveSmnTPrcx8mcRdD1i/br rD/w== X-Gm-Message-State: AA6/9Rn8+8QMeqMKConaO02wH376qxNVLkhFzbp9TCL+KWCOjNOXlwi6HIA1UwG/EY5jiIo0 X-Received: by 10.98.7.148 with SMTP id 20mr16831218pfh.18.1476927113858; Wed, 19 Oct 2016 18:31:53 -0700 (PDT) Received: from LBRMN-LNXUB114.ric.broadcom.com ([216.31.219.19]) by smtp.gmail.com with ESMTPSA id c5sm66464686pfj.71.2016.10.19.18.31.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 19 Oct 2016 18:31:53 -0700 (PDT) From: Vladimir Olovyannikov To: edk2-devel@lists.01.org Cc: Vladimir Olovyannikov , Carsey Jaben , Ni Ruiyu Date: Wed, 19 Oct 2016 18:31:57 -0700 Message-Id: <1476927117-4521-1-git-send-email-vladimir.olovyannikov@broadcom.com> X-Mailer: git-send-email 1.9.1 Subject: [PATCH] ShellPkg: Added GPT Shell Application/Library X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 20 Oct 2016 01:31:56 -0000 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Vladimir Olovyannikov This tool allows managing (create, delete, modify, fat format) of GPT partitions from within UEFI Shell. See usage examples in the .uni file Hope that this tool can be useful for anybody wishing to manipulate GPT partitions from within the UEFI Shell. I am planning to create a shared library for GPT-related activities. But because of time shortage this could happen much later than planned. This is the final version of the tool which we are going to use. If anybody wants to use this tool and create a separate library please by all means go ahead. --- .../Library/UefiShellGptCommandLib/FatFormat.c | 693 +++++++ .../Library/UefiShellGptCommandLib/FatFormat.h | 115 ++ .../Library/UefiShellGptCommandLib/GptWorker.c | 1922 ++++++++++++++++++++ .../Library/UefiShellGptCommandLib/GptWorker.h | 188 ++ .../UefiShellGptCommandLib.c | 1267 +++++++++++++ .../UefiShellGptCommandLib.inf | 74 + .../UefiShellGptCommandLib.uni | 113 ++ ShellPkg/ShellPkg.dec | 1 + ShellPkg/ShellPkg.dsc | 4 + 9 files changed, 4377 insertions(+) create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/FatFormat.c create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/FatFormat.h create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/GptWorker.c create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/GptWorker.h create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.c create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.inf create mode 100644 ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.uni diff --git a/ShellPkg/Library/UefiShellGptCommandLib/FatFormat.c b/ShellPkg/Library/UefiShellGptCommandLib/FatFormat.c new file mode 100644 index 000000000000..dea8505c49ff --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/FatFormat.c @@ -0,0 +1,693 @@ +/** @file + + Copyright (c) 2003 - 2012, Rob Riglar, Ultra-Embedded.com. All rights reserved
+ Copyright (c) 2016, Broadcom. All rights reserved
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "FatFormat.h" + +//----------------------------------------------------------------------------- +// Tables +//----------------------------------------------------------------------------- +typedef struct +{ + UINT64 Sectors; + UINT8 SectorsPerCluster; +} SectorsPerClusterTable; + +STATIC SectorsPerClusterTable ClusterSizeTable16[] = +{ + { 32680, 2 }, // 16MB - 1K + { 262144, 4 }, // 128MB - 2K + { 524288, 8 }, // 256MB - 4K + { 1048576, 16 }, // 512MB - 8K + { 2097152, 32 }, // 1GB - 16K + { 4194304, 64 }, // 2GB - 32K + { 8388608, 128 }, // 2GB - 64K [Warning only supported by Windows XP onwards] + { 0, 0 } // Invalid +}; + +STATIC SectorsPerClusterTable ClusterSizeTable32[] = +{ + { 532480, 1 }, // 260MB - 512b + { 16777216, 8 }, // 8GB - 4K + { 33554432, 16 }, // 16GB - 8K + { 67108864, 32 }, // 32GB - 16K + { 0xFFFFFFFF, 64 }, // >32GB - 32K + { 0, 0 } // Invalid +}; + +/** + Calculates LBA corresponding to the cluster number. + + @param FS Pointer to the FAT_FS structure + @param ClusterNumber Cluster number to get LBA for + + @return EFI_LBA LBA corresponding to the given cluster number +**/ +STATIC +EFI_LBA LbaOfCluster ( + IN FATFS *Fs, + IN UINT64 ClusterNumber + ) +{ + if (Fs->FatType == FAT_TYPE_16) + return (Fs->ClusterBeginLba + + (Fs->RootEntryCount * 32 / FAT_SECTOR_SIZE) + + ((ClusterNumber - 2) * Fs->SectorsPerCluster)); + else + return ((Fs->ClusterBeginLba + + ((ClusterNumber - 2) * Fs->SectorsPerCluster))); +} + +/** + Calculates the cluster size to be used. + + @param Sectors Requested number of sectors + @param IsFat32 Whether FAT32 is to be used + + @return UINT8 Cluster size from the table + +**/ +STATIC +UINT8 CalcClusterSize ( + IN UINTN Sectors, + IN BOOLEAN IsFat32) +{ + UINTN Index; + + if (!IsFat32) { + for (Index = 0; ClusterSizeTable16[Index].SectorsPerCluster != 0; Index++) + if (Sectors <= ClusterSizeTable16[Index].Sectors) + return ClusterSizeTable16[Index].SectorsPerCluster; + } else { + for (Index = 0; ClusterSizeTable32[Index].SectorsPerCluster != 0; Index++) + if (Sectors <= ClusterSizeTable32[Index].Sectors) + return ClusterSizeTable32[Index].SectorsPerCluster; + } + + return 0; +} + +/** + Erase a number of sectors. + + @param FS Pointer to the FAT_FS structure + @param LBA LBA to erase sectors in + @param Count Number of sectors to be erased + + @return EFI_SUCCESS Operation succeeded + @return other Failed to erase fat sectors + +**/ +STATIC +EFI_STATUS +EraseSectors ( + IN FATFS *Fs, + IN EFI_LBA Lba, + IN UINTN Count + ) +{ + UINTN Index; + EFI_STATUS Status; + + // Zero Sector first + ZeroMem (Fs->CurrentSector.Sector, FAT_SECTOR_SIZE); + + for (Index = 0; Index < Count; Index++) + Status = Fs->DiskIo->WriteDisk ( + Fs->DiskIo, Fs->BlockIo->Media->MediaId, + MultU64x32 (Fs->StartingLBA, Fs->BlockIo->Media->BlockSize) + + MultU64x32 ((Lba + Index), FAT_SECTOR_SIZE), + FAT_SECTOR_SIZE, Fs->CurrentSector.Sector); + + return Status; +} + +/** + Create the boot sector. + + @param Fs Pointer to the FAT_FS structure + @param BootSectorLba LBA of the boot sector + @param VolSectors Number of sectors in the volume + @param Name Volume label + @param IsFat32 Whether it is a FAT32 boot sector + + @return EFI_SUCCESS Boot sector was created successfully + @return other Failed to create the boot sector + +**/ +STATIC +EFI_STATUS +CreateBootSector ( + IN FATFS *Fs, + IN EFI_LBA BootSectorLba, + IN UINT64 VolSectors, + IN CONST CHAR8 *Name, + IN BOOLEAN IsFat32 + ) +{ + UINTN TotalClusters; + UINTN Index; + EFI_STATUS Status; + + // Zero Sector initially + ZeroMem (Fs->CurrentSector.Sector, FAT_SECTOR_SIZE); + + // OEM Name & Jump Code + Fs->CurrentSector.Sector[0] = 0xEB; + Fs->CurrentSector.Sector[1] = 0x3C; + Fs->CurrentSector.Sector[2] = 0x90; + Fs->CurrentSector.Sector[3] = 0x4D; + Fs->CurrentSector.Sector[4] = 0x53; + Fs->CurrentSector.Sector[5] = 0x44; + Fs->CurrentSector.Sector[6] = 0x4F; + Fs->CurrentSector.Sector[7] = 0x53; + Fs->CurrentSector.Sector[8] = 0x35; + Fs->CurrentSector.Sector[9] = 0x2E; + Fs->CurrentSector.Sector[10] = 0x30; + + // Bytes per Sector + Fs->CurrentSector.Sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF; + Fs->CurrentSector.Sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF; + + // Get sectors per cluster size for the disk + Fs->SectorsPerCluster = CalcClusterSize (VolSectors, IsFat32); + if (!Fs->SectorsPerCluster) + return 0; // Invalid disk size + + // Sectors per cluster + Fs->CurrentSector.Sector[13] = Fs->SectorsPerCluster; + + // Reserved Sectors + if (!IsFat32) + Fs->ReservedSectors = 8; + else + Fs->ReservedSectors = 32; + Fs->CurrentSector.Sector[14] = (Fs->ReservedSectors >> 0) & 0xFF; + Fs->CurrentSector.Sector[15] = (Fs->ReservedSectors >> 8) & 0xFF; + + // Number of FATS + Fs->NumOfFats = 2; + Fs->CurrentSector.Sector[16] = Fs->NumOfFats; + + // Max entries in root dir (FAT16 only) + if (!IsFat32) { + Fs->RootEntryCount = 512; + Fs->CurrentSector.Sector[17] = (Fs->RootEntryCount >> 0) & 0xFF; + Fs->CurrentSector.Sector[18] = (Fs->RootEntryCount >> 8) & 0xFF; + } else { + Fs->RootEntryCount = 0; + Fs->CurrentSector.Sector[17] = 0; + Fs->CurrentSector.Sector[18] = 0; + } + + // [FAT16] Total sectors (use FAT32 count instead) + Fs->CurrentSector.Sector[19] = 0x00; + Fs->CurrentSector.Sector[20] = 0x00; + + // Media type + Fs->CurrentSector.Sector[21] = 0xF8; + + + // FAT16 BS Details + if (!IsFat32) { + // Count of sectors used by the FAT table (FAT16 only) + TotalClusters = (VolSectors / Fs->SectorsPerCluster) + 1; + Fs->FatSectors = (TotalClusters / (FAT_SECTOR_SIZE / 2)) + 1; + Fs->CurrentSector.Sector[22] = (UINT8)((Fs->FatSectors >> 0) & 0xFF); + Fs->CurrentSector.Sector[23] = (UINT8)((Fs->FatSectors >> 8) & 0xFF); + + // Sectors per track + Fs->CurrentSector.Sector[24] = 0x00; + Fs->CurrentSector.Sector[25] = 0x00; + + // Heads + Fs->CurrentSector.Sector[26] = 0x00; + Fs->CurrentSector.Sector[27] = 0x00; + + // Hidden sectors + Fs->CurrentSector.Sector[28] = 0x20; + Fs->CurrentSector.Sector[29] = 0x00; + Fs->CurrentSector.Sector[30] = 0x00; + Fs->CurrentSector.Sector[31] = 0x00; + + // Total sectors for this volume + Fs->CurrentSector.Sector[32] = (UINT8)((VolSectors >> 0) & 0xFF); + Fs->CurrentSector.Sector[33] = (UINT8)((VolSectors >> 8) & 0xFF); + Fs->CurrentSector.Sector[34] = (UINT8)((VolSectors >> 16) & 0xFF); + Fs->CurrentSector.Sector[35] = (UINT8)((VolSectors >> 24) & 0xFF); + + // Drive number + Fs->CurrentSector.Sector[36] = 0x00; + + // Reserved + Fs->CurrentSector.Sector[37] = 0x00; + + // Boot signature + Fs->CurrentSector.Sector[38] = 0x29; + + // Volume ID + Fs->CurrentSector.Sector[39] = 0x12; + Fs->CurrentSector.Sector[40] = 0x34; + Fs->CurrentSector.Sector[41] = 0x56; + Fs->CurrentSector.Sector[42] = 0x78; + + // Volume name + for (Index = 0; Index < 11; Index++) { + if (Index < AsciiStrLen (Name)) + Fs->CurrentSector.Sector[Index + 43] = Name[Index]; + else + Fs->CurrentSector.Sector[Index + 43] = ' '; + } + + // File sys type + Fs->CurrentSector.Sector[54] = 'F'; + Fs->CurrentSector.Sector[55] = 'A'; + Fs->CurrentSector.Sector[56] = 'T'; + Fs->CurrentSector.Sector[57] = '1'; + Fs->CurrentSector.Sector[58] = '6'; + Fs->CurrentSector.Sector[59] = ' '; + Fs->CurrentSector.Sector[60] = ' '; + Fs->CurrentSector.Sector[61] = ' '; + + // Signature + Fs->CurrentSector.Sector[510] = 0x55; + Fs->CurrentSector.Sector[511] = 0xAA; + } + // FAT32 BS Details + else { + // Count of sectors used by the FAT table (FAT16 only) + Fs->CurrentSector.Sector[22] = 0; + Fs->CurrentSector.Sector[23] = 0; + + // Sectors per track (default) + Fs->CurrentSector.Sector[24] = 0x3F; + Fs->CurrentSector.Sector[25] = 0x00; + + // Heads (default) + Fs->CurrentSector.Sector[26] = 0xFF; + Fs->CurrentSector.Sector[27] = 0x00; + + // Hidden sectors + Fs->CurrentSector.Sector[28] = 0x00; + Fs->CurrentSector.Sector[29] = 0x00; + Fs->CurrentSector.Sector[30] = 0x00; + Fs->CurrentSector.Sector[31] = 0x00; + + // Total sectors for this volume + Fs->CurrentSector.Sector[32] = (UINT8)((VolSectors >> 0) & 0xFF); + Fs->CurrentSector.Sector[33] = (UINT8)((VolSectors >> 8) & 0xFF); + Fs->CurrentSector.Sector[34] = (UINT8)((VolSectors >> 16) & 0xFF); + Fs->CurrentSector.Sector[35] = (UINT8)((VolSectors >> 24) & 0xFF); + + TotalClusters = (VolSectors / Fs->SectorsPerCluster) + 1; + Fs->FatSectors = (TotalClusters / (FAT_SECTOR_SIZE / 4)) + 1; + + // BPB_FATSz32 + Fs->CurrentSector.Sector[36] = (UINT8)((Fs->FatSectors >> 0) & 0xFF); + Fs->CurrentSector.Sector[37] = (UINT8)((Fs->FatSectors >> 8) & 0xFF); + Fs->CurrentSector.Sector[38] = (UINT8)((Fs->FatSectors >> 16) & 0xFF); + Fs->CurrentSector.Sector[39] = (UINT8)((Fs->FatSectors >> 24) & 0xFF); + + // BPB_ExtFlags + Fs->CurrentSector.Sector[40] = 0; + Fs->CurrentSector.Sector[41] = 0; + + // BPB_FSVer + Fs->CurrentSector.Sector[42] = 0; + Fs->CurrentSector.Sector[43] = 0; + + // BPB_RootClus + Fs->CurrentSector.Sector[44] = (UINT8)((Fs->RootdirFirstCluster >> 0) & 0xFF); + Fs->CurrentSector.Sector[45] = (UINT8)((Fs->RootdirFirstCluster >> 8) & 0xFF); + Fs->CurrentSector.Sector[46] = (UINT8)((Fs->RootdirFirstCluster >> 16) & 0xFF); + Fs->CurrentSector.Sector[47] = (UINT8)((Fs->RootdirFirstCluster >> 24) & 0xFF); + + // BPB_FSInfo + Fs->CurrentSector.Sector[48] = (UINT8)((Fs->FsInfoSector >> 0) & 0xFF); + Fs->CurrentSector.Sector[49] = (UINT8)((Fs->FsInfoSector >> 8) & 0xFF); + + // BPB_BkBootSec + Fs->CurrentSector.Sector[50] = 6; + Fs->CurrentSector.Sector[51] = 0; + + // Drive number + Fs->CurrentSector.Sector[64] = 0x00; + + // Boot signature + Fs->CurrentSector.Sector[66] = 0x29; + + // Volume ID + Fs->CurrentSector.Sector[67] = 0x12; + Fs->CurrentSector.Sector[68] = 0x34; + Fs->CurrentSector.Sector[69] = 0x56; + Fs->CurrentSector.Sector[70] = 0x78; + + // Volume name + for (Index = 0; Index < 11; Index++) { + if (Index < (int)AsciiStrLen (Name)) + Fs->CurrentSector.Sector[Index + 71] = Name[Index]; + else + Fs->CurrentSector.Sector[Index + 71] = ' '; + } + + // File sys type + Fs->CurrentSector.Sector[82] = 'F'; + Fs->CurrentSector.Sector[83] = 'A'; + Fs->CurrentSector.Sector[84] = 'T'; + Fs->CurrentSector.Sector[85] = '3'; + Fs->CurrentSector.Sector[86] = '2'; + Fs->CurrentSector.Sector[87] = ' '; + Fs->CurrentSector.Sector[88] = ' '; + Fs->CurrentSector.Sector[89] = ' '; + + // Signature + Fs->CurrentSector.Sector[510] = 0x55; + Fs->CurrentSector.Sector[511] = 0xAA; + } + + Status = Fs->DiskIo->WriteDisk ( + Fs->DiskIo, Fs->BlockIo->Media->MediaId, + MultU64x32 (Fs->StartingLBA, Fs->BlockIo->Media->BlockSize) + + MultU64x32 (Fs->FatBeginLba, FAT_SECTOR_SIZE), + FAT_SECTOR_SIZE, + Fs->CurrentSector.Sector); + + return Status; +} +/** + Create the FSinfo sector for FAT32 + + @param Fs Pointer to the FAT_FS structure + @param SectorLba LBA of the FSInfo sector + + @return EFI_SUCCESS FAT32 FSInfo sector successfully created + @return other Failed to create the FAT32 FSInfo sector + +**/ +STATIC +EFI_STATUS +CreateFsinfoSector ( + IN FATFS *Fs, + IN EFI_LBA SectorLba + ) +{ + EFI_STATUS Status; + + // Zero Sector initially + ZeroMem (Fs->CurrentSector.Sector, FAT_SECTOR_SIZE); + + // FSI_LeadSig + Fs->CurrentSector.Sector[0] = 0x52; + Fs->CurrentSector.Sector[1] = 0x52; + Fs->CurrentSector.Sector[2] = 0x61; + Fs->CurrentSector.Sector[3] = 0x41; + + // FSI_StrucSig + Fs->CurrentSector.Sector[484] = 0x72; + Fs->CurrentSector.Sector[485] = 0x72; + Fs->CurrentSector.Sector[486] = 0x41; + Fs->CurrentSector.Sector[487] = 0x61; + + // FSI_Free_Count + Fs->CurrentSector.Sector[488] = 0xFF; + Fs->CurrentSector.Sector[489] = 0xFF; + Fs->CurrentSector.Sector[490] = 0xFF; + Fs->CurrentSector.Sector[491] = 0xFF; + + // FSI_Nxt_Free + Fs->CurrentSector.Sector[492] = 0xFF; + Fs->CurrentSector.Sector[493] = 0xFF; + Fs->CurrentSector.Sector[494] = 0xFF; + Fs->CurrentSector.Sector[495] = 0xFF; + + // Signature + Fs->CurrentSector.Sector[510] = 0x55; + Fs->CurrentSector.Sector[511] = 0xAA; + + Status = Fs->DiskIo->WriteDisk ( + Fs->DiskIo, Fs->BlockIo->Media->MediaId, + MultU64x32 (Fs->StartingLBA, Fs->BlockIo->Media->BlockSize) + + MultU64x32 (SectorLba, FAT_SECTOR_SIZE), + FAT_SECTOR_SIZE, + Fs->CurrentSector.Sector); + + return Status; +} + +/** + Erase FAT table + + @param Fs Pointer to the FAT_FS structure + @param IsFat32 Whether FAT32 is to be used + + @return EFI_SUCCESS Successfully erased FAT + @return other Failed to erase FAT + +**/ +STATIC +EFI_STATUS +EraseFat ( + IN FATFS *Fs, + IN BOOLEAN IsFat32) +{ + UINTN Index; + EFI_STATUS Status; + + // Zero Sector initially + ZeroMem (Fs->CurrentSector.Sector, FAT_SECTOR_SIZE); + + // Initialise default allocate / reserved clusters + if (!IsFat32) { + SET_16BIT_WORD (Fs->CurrentSector.Sector, 0, 0xFFF8); + SET_16BIT_WORD (Fs->CurrentSector.Sector, 2, 0xFFFF); + } else { + SET_32BIT_WORD (Fs->CurrentSector.Sector, 0, 0x0FFFFFF8); + SET_32BIT_WORD (Fs->CurrentSector.Sector, 4, 0xFFFFFFFF); + SET_32BIT_WORD (Fs->CurrentSector.Sector, 8, 0x0FFFFFFF); + } + + Status = Fs->DiskIo->WriteDisk ( + Fs->DiskIo, Fs->BlockIo->Media->MediaId, + MultU64x32 (Fs->StartingLBA, Fs->BlockIo->Media->BlockSize) + + MultU64x32 (Fs->FatBeginLba, FAT_SECTOR_SIZE), + FAT_SECTOR_SIZE, + Fs->CurrentSector.Sector); + if (EFI_ERROR (Status)) { + return Status; + } + + // Zero remaining FAT sectors + ZeroMem (Fs->CurrentSector.Sector, FAT_SECTOR_SIZE); + for (Index = 1; Index < Fs->FatSectors * Fs->NumOfFats; Index++) { + Status = Fs->DiskIo->WriteDisk ( + Fs->DiskIo, + Fs->BlockIo->Media->MediaId, + MultU64x32 (Fs->StartingLBA, Fs->BlockIo->Media->BlockSize) + + MultU64x32 ((Fs->FatBeginLba + Index), FAT_SECTOR_SIZE), + FAT_SECTOR_SIZE, + Fs->CurrentSector.Sector); + if (EFI_ERROR (Status)) { + return Status; + } + } + return Status; +} + +/** + Format a FAT16 partition + + @param Fs Pointer to FAT_FS structure + @param VolumeSectors Number of sectors in the volume + @param Name Volume label + + @return EFI_SUCCESS FAT16 partition was formatted successfully + @return other Failed to format FAT16 partition + +**/ +STATIC +EFI_STATUS +FormatFat16 ( + IN FATFS *Fs, + IN UINT64 VolumeSectors, + IN CONST CHAR8 *Name + ) +{ + EFI_STATUS Status; + + Fs->CurrentSector.Address = FAT32_INVALID_CLUSTER; + Fs->CurrentSector.Dirty = 0; + + Fs->NextFreeCluster = 0; // Invalid + + // Volume is FAT16 + Fs->FatType = FAT_TYPE_16; + + // Not valid for FAT16 + Fs->FsInfoSector = 0; + Fs->RootdirFirstCluster = 0; + + Fs->LbaBegin = 0; + Status = CreateBootSector (Fs, Fs->LbaBegin, VolumeSectors, Name, 0); + if (EFI_ERROR (Status)) { + return Status; + } + + // For FAT16 (which this may be), RootdirFirstCluster is actuall RootdirFirstSector + Fs->RootdirFirstSector = Fs->ReservedSectors + (Fs->NumOfFats * Fs->FatSectors); + Fs->RootdirSectors = ((Fs->RootEntryCount * 32) + + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE; + + // First FAT LBA Address + Fs->FatBeginLba = Fs->LbaBegin + Fs->ReservedSectors; + + // The Address of the first data cluster on this volume + Fs->ClusterBeginLba = Fs->FatBeginLba + + (Fs->NumOfFats * Fs->FatSectors); + + // Initialise FAT sectors + Status = EraseFat (Fs, 0); + if (EFI_ERROR (Status)) { + return Status; + } + + // Erase Root directory + Status = EraseSectors ( + Fs, + Fs->LbaBegin + Fs->RootdirFirstSector, + Fs->RootdirSectors); + + return Status; +} + +/** + Format a FAT32 partition + + @param Fs Pointer to FAT_FS structure + @param VolumeSectors Number of sectors in the volume + @param Name Volume label + + @return EFI_SUCCESS FAT32 partition was formatted successfully + @return other Failed to format FAT32 partition + +**/ +STATIC +EFI_STATUS +FormatFat32 ( + IN FATFS *Fs, + IN UINTN VolumeSectors, + IN CONST CHAR8 *Name + ) +{ + EFI_STATUS Status; + + Fs->CurrentSector.Address = FAT32_INVALID_CLUSTER; + Fs->CurrentSector.Dirty = 0; + + Fs->NextFreeCluster = 0; // Invalid + + // Volume is FAT32 + Fs->FatType = FAT_TYPE_32; + + // Basic defaults for normal FAT32 partitions + Fs->FsInfoSector = 1; + Fs->RootdirFirstCluster = 2; + + // Sector 0: Boot Sector + // NOTE: We don't need an MBR, it is a waste of a good Sector! + Fs->LbaBegin = 0; + Status = CreateBootSector (Fs, Fs->LbaBegin, VolumeSectors, Name, 1); + if (EFI_ERROR (Status)) { + return Status; + } + + // First FAT LBA address + Fs->FatBeginLba = Fs->LbaBegin + Fs->ReservedSectors; + + // The address of the first data cluster on this volume + Fs->ClusterBeginLba = Fs->FatBeginLba + (Fs->NumOfFats * Fs->FatSectors); + + // Initialise FSInfo sector + Status = CreateFsinfoSector (Fs, Fs->FsInfoSector); + if (EFI_ERROR (Status)) { + return Status; + } + + // Initialise FAT sectors + Status = EraseFat (Fs, 1); + if (EFI_ERROR (Status)) { + return Status; + } + + // Erase Root directory + Status = EraseSectors ( + Fs, + LbaOfCluster (Fs, Fs->RootdirFirstCluster), + Fs->SectorsPerCluster); + + return Status; +} + +/** + Format a partition either to FAT16, or FAT32 + + @param StartingLBA Starting LBA of the partition + @param EndingLBA Ending LBA of the partition + @param BlockIo Pointer to the BlockIo protocol + @param DiskIo Pointer to the DiskIo protocol + @param ForceFat32 Force FAT32 format (even if device can be formatted as FAT16) + + @return EFI_SUCCESS Format succeeded + @return other Format failed + +**/ +EFI_STATUS +FatFormat ( + IN EFI_LBA StartingLBA, + IN EFI_LBA EndingLBA, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN CHAR8 *VolumeName, + IN BOOLEAN ForceFat32 + ) +{ + FATFS Fs; + UINT64 VolumeSectors; + + if ((StartingLBA > EndingLBA) || + (BlockIo == NULL) || (DiskIo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + VolumeSectors = DivU64x32 ( + MultU64x32 ( + EndingLBA - StartingLBA + 1, BlockIo->Media->BlockSize), + FAT_SECTOR_SIZE); + + if (VolumeName == NULL) { + VolumeName = DEFAULT_FAT_LABEL_NAME; + } + + ZeroMem (&Fs, sizeof (Fs)); + + Fs.StartingLBA = StartingLBA; + Fs.EndingLBA = EndingLBA; + Fs.BlockIo = BlockIo; + Fs.DiskIo = DiskIo; + // 2GB - 32K limit for safe behaviour for FAT16 + if ((VolumeSectors <= FAT16_MAX_SECTORS) && (!ForceFat32)) { + return FormatFat16 (&Fs, VolumeSectors, VolumeName); + } else { + return FormatFat32 (&Fs, VolumeSectors, VolumeName); + } +} diff --git a/ShellPkg/Library/UefiShellGptCommandLib/FatFormat.h b/ShellPkg/Library/UefiShellGptCommandLib/FatFormat.h new file mode 100644 index 000000000000..213ab4df8ebc --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/FatFormat.h @@ -0,0 +1,115 @@ +/** @file + + Copyright (c) 2003 - 2012, Rob Riglar, Ultra-Embedded.com. All rights reserved
+ Copyright (c) 2016, Broadcom. All rights reserved
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __FAT_FORMAT_H__ +#define __FAT_FORMAT_H__ + +#include "GptWorker.h" + +#define FAT_SECTOR_SIZE 512 +#define FAT_BUFFER_SECTORS 1 +#define FAT16_MAX_SECTORS 4194304 +#define FAT32_LAST_CLUSTER 0xFFFFFFFF +#define FAT32_INVALID_CLUSTER 0xFFFFFFFF +#define FAT_DIR_ENTRY_SIZE 32 +#define FAT_BUFFERS 1 +#define DEFAULT_FAT_LABEL_NAME "EFIVOL" +#define FAT_LABEL_LENGTH 11 + +#define FAT32_FORMAT L"FAT32" +#define FAT16_FORMAT L"FAT16" + +#define GET_32BIT_WORD(Buffer, Location) ( ((UINT32)Buffer[Location + 3] << 24) + \ + ((UINT32)Buffer[Location + 2] <<16 ) + \ + ((UINT32)Buffer[Location + 1] << 8) + \ + (UINT32)Buffer[Location+0] ) + +#define GET_16BIT_WORD(Buffer, Location) ( ((UINT16)Buffer[Location + 1] << 8) + \ + (UINT16)Buffer[Location+0]) + +#define SET_32BIT_WORD(Buffer, Location, Value) { Buffer[Location + 0] = (UINT8)((Value) & 0xFF); \ + Buffer[Location + 1] = (UINT8)((Value >> 8) & 0xFF); \ + Buffer[Location + 2] = (UINT8)((Value >> 16) & 0xFF); \ + Buffer[Location + 3] = (UINT8)((Value >> 24) & 0xFF); } + +#define SET_16BIT_WORD(Buffer, Location, Value) { Buffer[Location + 0] = (UINT8)((Value) & 0xFF); \ + Buffer[Location + 1] = (UINT8)((Value >> 8) & 0xFF); } + +typedef enum eFatType +{ + FAT_TYPE_16, + FAT_TYPE_32 +} FAT_FS_TYPE; + + +// Forward declaration +typedef struct _FAT_BUFFER FAT_BUFFER; + +struct _FAT_BUFFER { + UINT8 Sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS]; + UINTN Address; + BOOLEAN Dirty; + UINT8 *Ptr; + + // Next in chain of sector buffers + struct FAT_BUFFER *NextBuf; +}; + +typedef struct +{ + // Filesystem globals + UINT8 SectorsPerCluster; + EFI_LBA ClusterBeginLba; + UINTN RootdirFirstCluster; + UINTN RootdirFirstSector; + UINTN RootdirSectors; + EFI_LBA FatBeginLba; + UINT16 FsInfoSector; + EFI_LBA LbaBegin; + UINTN FatSectors; + UINTN NextFreeCluster; + UINT16 RootEntryCount; + UINT16 ReservedSectors; + UINT8 NumOfFats; + FAT_FS_TYPE FatType; + + // Working buffer + FAT_BUFFER CurrentSector; + // FAT Buffer + FAT_BUFFER *FatBufferHead; + FAT_BUFFER FatBuffers[FAT_BUFFERS]; + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + +} FATFS; + + + +//----------------------------------------------------------------------------- +// Prototypes +//----------------------------------------------------------------------------- +EFI_STATUS +FatFormat ( + IN EFI_LBA StartingLBA, + IN EFI_LBA EndingLBA, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN CHAR8 *VolumeName, + IN BOOLEAN ForceFat32 + ); + +#endif diff --git a/ShellPkg/Library/UefiShellGptCommandLib/GptWorker.c b/ShellPkg/Library/UefiShellGptCommandLib/GptWorker.c new file mode 100644 index 000000000000..31581b3d6172 --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/GptWorker.c @@ -0,0 +1,1922 @@ +/** @file +Copyright (c) 1999 - 2013, Intel Corporation. All rights reserved.
+Copyright (c) 2016, Broadcom. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "GptWorker.h" + +#define GPT_DEBUG_LEVEL 0 + +STATIC EFI_PARTITION_TABLE_HEADER *PrimaryHeader = NULL; +STATIC EFI_PARTITION_TABLE_HEADER *BackupHeader = NULL; +STATIC EFI_PARTITION_ENTRY *PartEntry = NULL; +STATIC EFI_PARTITION_ENTRY_STATUS *PEntryStatus = NULL; + +STATIC EFI_BLOCK_IO_PROTOCOL *BlockIo; +STATIC EFI_DISK_IO_PROTOCOL *DiskIo; + +STATIC BOOLEAN MbrValid; +STATIC BOOLEAN GptValid; + +STATIC EFI_KNOWN_PARTITION_TYPE PartitionTypes[] = +{ + // Known Partition type GUIDs + // Expand this table as needed. + // Starting with EFI System partition + { { 0xC12A7328L, 0xF81F, 0x11D2, { 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B } }, L"EFI System" }, + // Known Windows partition types + { { 0xE3C9E316L, 0x0B5C, 0x4DB8, { 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE } }, L"MS Windows Reserved (MSR)" }, + { { 0xEBD0A0A2L, 0xB9E5, 0x4433, { 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 } }, L"MS Windows Basic Data" }, + { { 0x5808C8AAL, 0x7E8F, 0x42E0, { 0x85, 0xD2, 0xE1, 0xE9, 0x04, 0x34, 0xCF, 0xB3 } }, L"MS Windows LDM Metadata" }, + { { 0xAF9B60A0L, 0x1431, 0x4F62, { 0xBC, 0x68, 0x33, 0x11, 0x71, 0x4A, 0x69, 0xAD } }, L"MS Windows LDM Data" }, + { { 0xDE94BBA4L, 0x06D1, 0x4D40, { 0xA1, 0x6A, 0xBF, 0xD5, 0x01, 0x79, 0xD6, 0xAC } }, L"MS Windows Recovery Environment" }, + { { 0xE75CAF8FL, 0xF680, 0x4CEE, { 0xAF, 0xA3, 0xB0, 0x01, 0xE5, 0x6E, 0xFC, 0x2D } }, L"MS Windows Storage Spaces" }, + // Known Linux partition types + { { 0x0FC63DAFL, 0x8483, 0x4772, { 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 } }, L"Linux Filesystem Data" }, + { { 0xA19D880FL, 0x05FC, 0x4D3B, { 0xA0, 0x06, 0x74, 0x3F, 0x0F, 0x84, 0x91, 0x1E } }, L"Linux RAID" }, + { { 0x44479540L, 0xF297, 0x41B2, { 0x9A, 0xF7, 0xD1, 0x31, 0xD5, 0xF0, 0x45, 0x8A } }, L"Linux Root (x86)" }, + { { 0x4F68BCE3L, 0xE8CD, 0x4DB1, { 0x96, 0xE7, 0xFB, 0xCA, 0xF9, 0x84, 0xB7, 0x09 } }, L"Linux Root (x86-64)" }, + { { 0x69DAD710L, 0x2CE4, 0x4E3C, { 0xB1, 0x6C, 0x21, 0xA1, 0xD4, 0x9A, 0xBe, 0xD3 } }, L"Linux Root (ARM 32-bit)" }, + { { 0xB921B045L, 0x1DF0, 0x41C3, { 0xAF, 0x44, 0x4C, 0x6F, 0x28, 0x0D, 0x3F, 0xAE } }, L"Linux Root (ARM 64-bit/AARCH64)" }, + { { 0x0657FD6DL, 0xA4AB, 0x43C4, { 0x84, 0xE5, 0x09, 0x33, 0xC8, 0x4B, 0x4F, 0x4F } }, L"Linux Swap" }, + { { 0xE6D6D379L, 0xF507, 0x44C2, { 0xA2, 0x3C, 0x23, 0x8F, 0x2A, 0x3D, 0xF9, 0x28 } }, L"Linux Logical Volume Manager (LVM)" }, + { { 0x933AC7E1L, 0x2EB4, 0x4F13, { 0xB8, 0x44, 0x0E, 0x14, 0xE2, 0xAE, 0xF9, 0x15 } }, L"Linux /home" }, + { { 0x3B8F8425L, 0x20E0, 0x4F3B, { 0x90, 0x7F, 0x1A, 0x25, 0xA7, 0x6F, 0x98, 0xE8 } }, L"Linux /srv (server data)" }, + { { 0x7FFEC5C9L, 0x2D00, 0x49B7, { 0x89, 0x41, 0x3E, 0xA1, 0x0A, 0x55, 0x86, 0xB7 } }, L"Linux Plain dm-crypt" }, + { { 0xCA7D7CCBL, 0x63ED, 0x4C53, { 0x86, 0x1C, 0x17, 0x42, 0x53, 0x60, 0x59, 0xCC } }, L"Linux LUKS" }, + { { 0x8DA63339L, 0x0007, 0x60C0, { 0xC4, 0x36, 0x08, 0x3A, 0xC8, 0x23, 0x09, 0x08 } }, L"Linux Reserved" }, +}; + +MASTER_BOOT_RECORD ProtectiveMbrTemplate = { + { 0 }, // BoostStrapCode [440] + { 0 }, // UniqueMbrSignature[4] (unused) + { 0 }, // Unknown[2] + + // PARTITIONS + { + // MBR_PARTITION_RECORD + { + 0, // BootIndicator + 0, // StartHead + 0x1, // StartSector + 0x1, // StartTrack + PMBR_GPT_PARTITION, // OSIndicator + 0xff, // EndHead + 0xff, // EndSector + 0xff, // EndTrack + { 0x1, 0x0, 0x0, 0x0 }, // StartingLba[4] + { 0xff, 0xff, 0xff, 0xff }, // SizeInLba[4] + }, + { 0 }, { 0 }, { 0 }, // Unused partitions + }, + + MBR_SIGNATURE // Signature +}; + +STATIC UINT32 CurRand = 1; + +/** + Get random seed based on the RTC + +**/ + +STATIC +VOID +Srand (VOID) +{ + EFI_TIME Time; + UINT32 Seed; + UINT64 MonotonicCount; + + gRT->GetTime (&Time, NULL); + Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second); + Seed ^= Time.Nanosecond; + Seed ^= Time.Year << 7; + + gBS->GetNextMonotonicCount (&MonotonicCount); + Seed += (UINT32)MonotonicCount; + + /* Store this seed */ + CurRand = (UINT32)Seed; +} + +/** + Get a pseudo-random number + + @retval A pseudo-random number in the range 0 - 0x7fff +**/ + +STATIC UINTN Rand ( + VOID + ) +{ + /* return a pseudo-random in range 0 - 0x7fff */ + return ((CurRand = CurRand * 214013L + 2531011L) >> 16) & 0x7fff; +} + +/** + Generate a GUID + + @param[in] Guid A pointer to a GUID receiving a generated value +**/ + +STATIC VOID +GenerateGuid ( + OUT GUID *Guid + ) +{ + UINTN Index; + UINT16 Buffer[sizeof (GUID)]; + + /* Generates 128 random bits for new UUID */ + for (Index = 0; Index < sizeof (GUID); Index++) { + UINTN V; + + V = Rand () >> 7; + Buffer[Index] = (UINT16)V; + } + /* set variant 10x and version 4 as required by RFC 4122 */ + Buffer[8] = 0x80 | (Buffer[8] & 0x3f); + Buffer[6] = 0x40 | (Buffer[6] & 0xf); + CopyGuid (Guid, (CONST GUID *)Buffer); +} + +/** + Clean up globals +**/ +VOID +GptCleanupGlobals ( + VOID + ) +{ + SHELL_FREE_NON_NULL (PrimaryHeader); + SHELL_FREE_NON_NULL (BackupHeader); + SHELL_FREE_NON_NULL (PartEntry); + SHELL_FREE_NON_NULL (PEntryStatus); + GptValid = FALSE; + MbrValid = FALSE; +} + +/** + Caution: This function may receive untrusted input. + The GPT partition table header is external input, so this routine + will do basic validation for GPT partition table header before return. + + @param[in] Lba The starting Lba of the Partition Table + @param[out] PartHeader Stores the partition table that is read + + @retval EFI_SUCCESS The partition table is valid + @retval ERROR The partition table is not valid + +**/ +STATIC +EFI_STATUS +PartitionValidGptTable ( + IN EFI_LBA Lba, + OUT EFI_PARTITION_TABLE_HEADER *PartHeader + ); + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +STATIC +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ); + + +/** + Restore Partition Table to its alternate place + (Primary -> Backup or Backup -> Primary). + + @param[in] PartHeader Partition table header structure. + + @retval TRUE Restoring succeeds + @retval FALSE Restoring failed + +**/ +STATIC +BOOLEAN +PartitionRestoreGptTable ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ); + + +/** + This routine will check GPT partition entry and return entry status. + + Caution: This function may receive untrusted input. + The GPT partition entry is external input, so this routine + will do basic validation for GPT partition entry and report status. + + @param[in] PartHeader Partition table header structure + @param[in] PartEntry The partition entry array + @param[out] PEntryStatus the partition entry status array + recording the status of each partition + +**/ +STATIC +VOID +PartitionCheckGptEntry ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry, + OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus + ); + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Size The size of the table + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrcAltSize ( + IN UINTN MaxSize, + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrc ( + IN UINTN MaxSize, + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Updates the CRC32 value in the table header. + + @param Size The size of the table + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrcAltSize ( + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Updates the CRC32 value in the table header. + + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Get GPT tables. + + Caution: This function may receive untrusted input. + The GPT partition table is external input, so this routine + will do basic validation for GPT partition table before install + child handle for each GPT partition. + + @param[in] DiskIoProt DiskIo interface. + @param[in] BlockIoProt BlockIo interface. + + @retval EFI_SUCCESS Valid GPT disk. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval other Not a valid GPT disk. + +**/ +EFI_STATUS +PartitionGetGptTables ( + IN EFI_DISK_IO_PROTOCOL *DiskIoProt, + IN EFI_BLOCK_IO_PROTOCOL *BlockIoProt + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_LBA LastBlock; + UINTN Index; + EFI_STATUS GptValidStatus; + UINT32 MediaId; + MASTER_BOOT_RECORD *ProtectiveMbr; + + // Clear leftovers + GptCleanupGlobals (); + + MbrValid = FALSE; + GptValid = FALSE; + + ProtectiveMbr = NULL; + + BlockIo = BlockIoProt; + DiskIo = DiskIoProt; + + if (CurRand == 1) { + Srand (); + } + + BlockSize = BlockIo->Media->BlockSize; + LastBlock = BlockIo->Media->LastBlock; + MediaId = BlockIo->Media->MediaId; + + DEBUG ((EFI_D_VERBOSE, " BlockSize : %d \n", BlockSize)); + DEBUG ((EFI_D_VERBOSE, " LastBlock : %lx \n", LastBlock)); + + GptValidStatus = EFI_NOT_FOUND; + GptValid = FALSE; + MbrValid = FALSE; + + // + // Allocate a buffer for the Protective MBR + // + ProtectiveMbr = AllocatePool (BlockSize); + if (ProtectiveMbr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Read the Protective MBR from LBA #0 + // + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + 0, + BlockSize, + ProtectiveMbr + ); + if (EFI_ERROR (Status)) { + GptValidStatus = Status; + goto Done; + } + + // + // Verify that the Protective MBR is valid + // + for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { + if (ProtectiveMbr->Partition[Index].BootIndicator == 0x00 && + ProtectiveMbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION && + UNPACK_UINT32 (ProtectiveMbr->Partition[Index].StartingLBA) == 1 + ) { + break; + } + } + if (Index == MAX_MBR_PARTITIONS) { + goto Done; + } + + MbrValid = TRUE; + + // + // Allocate the GPT structures + // + PrimaryHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER)); + if (PrimaryHeader == NULL) { + goto Done; + } + + BackupHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER)); + if (BackupHeader == NULL) { + goto Done; + } + + // + // Check primary and backup partition tables + // + Status = PartitionValidGptTable (PRIMARY_PART_HEADER_LBA, PrimaryHeader); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, " Not Valid primary partition table\n")); + + Status = PartitionValidGptTable (LastBlock, BackupHeader); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, " Not Valid backup partition table\n")); + goto Done; + } else { + DEBUG ((EFI_D_INFO, " Valid backup partition table\n")); + DEBUG ((EFI_D_INFO, " Restore primary partition table by the backup\n")); + if (!PartitionRestoreGptTable (BackupHeader)) { + DEBUG ((EFI_D_INFO, " Restore primary partition table error\n")); + } + + Status = PartitionValidGptTable (BackupHeader->AlternateLBA, PrimaryHeader); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, " Restore backup partition table success\n")); + } + } + } else if (EFI_ERROR (PartitionValidGptTable (PrimaryHeader->AlternateLBA, BackupHeader))) { + DEBUG ((EFI_D_INFO, " Valid primary and !Valid backup partition table\n")); + DEBUG ((EFI_D_INFO, " Restore backup partition table by the primary\n")); + if (!PartitionRestoreGptTable (PrimaryHeader)) { + DEBUG ((EFI_D_INFO, " Restore backup partition table error\n")); + } + + Status = PartitionValidGptTable (PrimaryHeader->AlternateLBA, BackupHeader); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, " Restore backup partition table success\n")); + } + } + + DEBUG ((EFI_D_VERBOSE, " Valid primary and Valid backup partition table\n")); + + // + // Read the EFI Partition Entries + // + PartEntry = AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry); + if (PartEntry == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + goto Done; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockSize), + PrimaryHeader->NumberOfPartitionEntries * (PrimaryHeader->SizeOfPartitionEntry), + PartEntry + ); + if (EFI_ERROR (Status)) { + GptValidStatus = Status; + DEBUG ((EFI_D_ERROR, " Partition Entry ReadDisk error\n")); + goto Done; + } + + DEBUG ((EFI_D_VERBOSE, " Partition entries read block success\n")); + + DEBUG ((EFI_D_VERBOSE, " Number of partition entries: %d\n", PrimaryHeader->NumberOfPartitionEntries)); + + PEntryStatus = AllocateZeroPool (PrimaryHeader->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)); + if (PEntryStatus == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + goto Done; + } + + // + // Check the integrity of partition entries + // + PartitionCheckGptEntry (PrimaryHeader, PartEntry, PEntryStatus); + + // + // If we got this far the GPT layout of the disk is valid and we should return true + // + GptValidStatus = EFI_SUCCESS; + + Done: + SHELL_FREE_NON_NULL (ProtectiveMbr); + if (EFI_ERROR (GptValidStatus)) { + SHELL_FREE_NON_NULL (PrimaryHeader); + SHELL_FREE_NON_NULL (BackupHeader); + SHELL_FREE_NON_NULL (PartEntry); + SHELL_FREE_NON_NULL (PEntryStatus); + } else { + GptValid = TRUE; + } + + return GptValidStatus; +} + +/** + This routine will read GPT partition table header and return it. + + Caution: This function may receive untrusted input. + The GPT partition table header is external input, so this routine + will do basic validation for GPT partition table header before return. + + @param[in] Lba The starting Lba of the Partition Table + @param[out] PartHeader Stores the partition table that is read + + @retval TRUE The partition table is valid + @retval FALSE The partition table is not valid + +**/ +STATIC +EFI_STATUS +PartitionValidGptTable ( + IN EFI_LBA Lba, + OUT EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_PARTITION_TABLE_HEADER *PartHdr; + UINT32 MediaId; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + PartHdr = AllocateZeroPool (BlockSize); + + if (PartHdr == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + return EFI_OUT_OF_RESOURCES; + } + // + // Read the EFI Partition Table Header + // + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (Lba, BlockSize), + BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + FreePool (PartHdr); + return Status; + } + + if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) || + !PartitionCheckCrc (BlockSize, &PartHdr->Header) || + PartHdr->MyLBA != Lba || + (PartHdr->SizeOfPartitionEntry < sizeof (EFI_PARTITION_ENTRY)) + ) { + DEBUG ((EFI_D_ERROR, "Invalid efi partition table header\n")); + FreePool (PartHdr); + return EFI_VOLUME_CORRUPTED; + } + + // + // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow. + // + if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) { + FreePool (PartHdr); + return EFI_VOLUME_CORRUPTED; + } + + CopyMem (PartHeader, PartHdr, sizeof (EFI_PARTITION_TABLE_HEADER)); + if (!PartitionCheckGptEntryArrayCRC (PartHeader)) { + FreePool (PartHdr); + return EFI_VOLUME_CORRUPTED; + } + + DEBUG ((EFI_D_VERBOSE, " Valid efi partition table header\n")); + FreePool (PartHdr); + + return EFI_SUCCESS; +} + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +STATIC +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINT32 Crc; + UINTN Size; + + // + // Read the EFI Partition Entries + // + Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); + if (Ptr == NULL) { + DEBUG ((EFI_D_ERROR, " Allocate pool error\n")); + return FALSE; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (PartHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Ptr); + return FALSE; + } + + Size = PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry; + + Status = gBS->CalculateCrc32 (Ptr, Size, &Crc); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "CheckPEntryArrayCRC: Crc calculation failed\n")); + FreePool (Ptr); + return FALSE; + } + + FreePool (Ptr); + + return (BOOLEAN)(PartHeader->PartitionEntryArrayCRC32 == Crc); +} + + +/** + Restore Partition Table to its alternate place + (Primary -> Backup or Backup -> Primary). + + @param[in] PartHeader Partition table header structure. + + @retval TRUE Restoring succeeds + @retval FALSE Restoring failed + +**/ +STATIC +BOOLEAN +PartitionRestoreGptTable ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINTN BlockSize; + EFI_PARTITION_TABLE_HEADER *PartHdr; + EFI_LBA PEntryLBA; + UINT8 *Ptr; + UINT32 MediaId; + + PartHdr = NULL; + Ptr = NULL; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + + PartHdr = AllocateZeroPool (BlockSize); + + if (PartHdr == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + return FALSE; + } + + PEntryLBA = (PartHeader->MyLBA == PRIMARY_PART_HEADER_LBA) ?\ + (PartHeader->LastUsableLBA + 1) :\ + (PRIMARY_PART_HEADER_LBA + 1); + + CopyMem (PartHdr, PartHeader, sizeof (EFI_PARTITION_TABLE_HEADER)); + + PartHdr->MyLBA = PartHeader->AlternateLBA; + PartHdr->AlternateLBA = PartHeader->MyLBA; + PartHdr->PartitionEntryLBA = PEntryLBA; + PartitionSetCrc ((EFI_TABLE_HEADER *)PartHdr); + + Status = DiskIo->WriteDisk ( + DiskIo, + MediaId, + MultU64x32 (PartHdr->MyLBA, (UINT32)BlockSize), + BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); + if (Ptr == NULL) { + DEBUG ((EFI_D_ERROR, " Allocate pool error\n")); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (PartHeader->PartitionEntryLBA, (UINT32)BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = DiskIo->WriteDisk ( + DiskIo, + MediaId, + MultU64x32 (PEntryLBA, (UINT32)BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + + Done: + FreePool (PartHdr); + + if (Ptr != NULL) { + FreePool (Ptr); + } + + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + This routine will check GPT partition entry and return entry status. + + Caution: This function may receive untrusted input. + The GPT partition entry is external input, so this routine + will do basic validation for GPT partition entry and report status. + + @param[in] PartHeader Partition table header structure + @param[in] PartEntry The partition entry array + @param[out] PEntryStatus the partition entry status array + recording the status of each partition + +**/ +STATIC +VOID +PartitionCheckGptEntry ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry, + OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus + ) +{ + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + EFI_PARTITION_ENTRY *Entry; + UINTN Index1; + UINTN Index2; + + DEBUG ((EFI_D_VERBOSE, " start check partition entries\n")); + for (Index1 = 0; Index1 < PartHeader->NumberOfPartitionEntries; Index1++) { + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index1 * PartHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + StartingLBA = Entry->StartingLBA; + EndingLBA = Entry->EndingLBA; + if (StartingLBA > EndingLBA || + StartingLBA < PartHeader->FirstUsableLBA || + StartingLBA > PartHeader->LastUsableLBA || + EndingLBA < PartHeader->FirstUsableLBA || + EndingLBA > PartHeader->LastUsableLBA + ) { + PEntryStatus[Index1].OutOfRange = TRUE; + continue; + } + + if ((Entry->Attributes & BIT1) != 0) { + // + // If Bit 1 is set, this indicate that this is an OS specific GUID partition. + // + PEntryStatus[Index1].OsSpecific = TRUE; + } + + for (Index2 = Index1 + 1; Index2 < PartHeader->NumberOfPartitionEntries; Index2++) { + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index2 * PartHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + if (Entry->EndingLBA >= StartingLBA && Entry->StartingLBA <= EndingLBA) { + // + // This region overlaps with the Index1'th region + // + PEntryStatus[Index1].Overlap = TRUE; + PEntryStatus[Index2].Overlap = TRUE; + continue; + } + } + } + + DEBUG ((EFI_D_VERBOSE, " End check partition entries\n")); +} + + +/** + Updates the CRC32 value in the table header. + + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + PartitionSetCrcAltSize (Hdr->HeaderSize, Hdr); +} + + +/** + Updates the CRC32 value in the table header. + + @param Size The size of the table + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrcAltSize ( + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + + Hdr->CRC32 = 0; + gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); + Hdr->CRC32 = Crc; +} + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrc ( + IN UINTN MaxSize, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + return PartitionCheckCrcAltSize (MaxSize, Hdr->HeaderSize, Hdr); +} + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Size The size of the table + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrcAltSize ( + IN UINTN MaxSize, + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + UINT32 OrgCrc; + EFI_STATUS Status; + + Crc = 0; + + if (Size == 0) { + // + // If header size is 0 CRC will pass so return FALSE here + // + return FALSE; + } + + if ((MaxSize != 0) && (Size > MaxSize)) { + DEBUG ((EFI_D_ERROR, "CheckCrc32: Size > MaxSize\n")); + return FALSE; + } + // + // clear old crc from header + // + OrgCrc = Hdr->CRC32; + Hdr->CRC32 = 0; + + Status = gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "CheckCrc32: Crc calculation failed\n")); + return FALSE; + } + // + // set results + // + Hdr->CRC32 = Crc; + + // + // return status + // + DEBUG_CODE_BEGIN (); + if (OrgCrc != Crc) { + DEBUG ((EFI_D_ERROR, "CheckCrc32: Crc check failed\n")); + } + DEBUG_CODE_END (); + + return (BOOLEAN)(OrgCrc == Crc); +} + +/** + Converts a GUID into a unicode string. + + @param [in] Guid A GUID to be converted + @param [out] Buffer A pointer to a buffer receiving the string + @param [in] BufferSize Size of the buffer + + @return EFI_SUCCESS Successful conversion + @return EFI_INVALID_PARAMETER Conversion failed + +**/ + +STATIC +EFI_STATUS +GuidToString ( + IN EFI_GUID *Guid, + OUT CHAR16 *Buffer, + IN UINTN BufferSize + ) +{ + UINTN Size; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + Size = UnicodeSPrint ( + Buffer, + BufferSize, + L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (UINTN)Guid->Data1, + (UINTN)Guid->Data2, + (UINTN)Guid->Data3, + (UINTN)Guid->Data4[0], + (UINTN)Guid->Data4[1], + (UINTN)Guid->Data4[2], + (UINTN)Guid->Data4[3], + (UINTN)Guid->Data4[4], + (UINTN)Guid->Data4[5], + (UINTN)Guid->Data4[6], + (UINTN)Guid->Data4[7] + ); + if (!Size) { + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** + Get a string representation of a known + partition type GUID. If partition GUID is not + known, gets a string representation of the GUID + + @param GUID Guid to be searched for + @param PartTypeStr Buffer receiving the string + @param NoGuidStr Do not convert GUID to string + for an unknown partition type + + @return EFI_SUCCESS GUID type is known + @return EFI_NOT_FOUND Unknown GUID partition type + +**/ +EFI_STATUS +GetPartitionTypeStr ( + IN EFI_GUID Guid, + OUT CHAR16 *PartTypeStr, + IN BOOLEAN NoGuidStr) +{ + UINTN Index; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + for (Index = 0; Index < ARRAY_SIZE (PartitionTypes); Index++) { + if (CompareGuid (&Guid, &PartitionTypes[Index].TypeGuid)) { + StrnCpy (PartTypeStr, PartitionTypes[Index].TypeName, MAX_PARTITION_NAME_LENGTH); + return Status; + } + } + if (!NoGuidStr) { + GuidToString (&Guid, PartTypeStr, (MAX_PARTITION_NAME_LENGTH + 1) * sizeof (CHAR16)); + } + return EFI_NOT_FOUND; +} + +/** + Lists partitions on a block device. + + @return Number of partitions used +**/ + +UINTN +PartitionListGptEntries (VOID) +{ + IN UINTN Index; + IN EFI_PARTITION_ENTRY *Entry; + IN EFI_LBA Length; + IN UINTN NumEntries; // Used entries + IN UINTN BlockSize; + IN BOOLEAN FirstTime; + IN UINT64 BlocksOccupied; + + NumEntries = 0; + BlockSize = BlockIo->Media->BlockSize; + FirstTime = TRUE; + + if (!GptValid) { + return 0; + } + + BlocksOccupied = 0; + + for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) { + + BOOLEAN Specific; + CHAR16 PartTypeStr[MAX_PARTITION_NAME_LENGTH + 1]; + + Specific = FALSE; + + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index * PrimaryHeader->SizeOfPartitionEntry); + + Length = Entry->EndingLBA - Entry->StartingLBA + 1; + + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + if (FirstTime) { + Print (L" No \t%-36s\tStart (LBA)\tEnd (LBA)\tSize (MiB)\t Partition Type\r\n", L"Name"); + Print (L" ---- -------------------------------------- ----------- ----------- ---------- -------------------------------\r\n"); + FirstTime = FALSE; + } + NumEntries++; + if (PEntryStatus[Index].OutOfRange || + PEntryStatus[Index].Overlap || + PEntryStatus[Index].OsSpecific) { + Specific = TRUE; + } + + BlocksOccupied += Length; + + GetPartitionTypeStr (Entry->PartitionTypeGUID, (CHAR16 *)&PartTypeStr, FALSE); + + Print (L"%1s %-3d\t%-36s\t0x%09llx\t0x%09llx\t%06llu\t %s\r\n", + Specific ? L"S" : L"", + Index + 1, + Entry->PartitionName, + (UINT64)Entry->StartingLBA, + (UINT64)Entry->EndingLBA, + (MultU64x32 (Length, BlockSize)) >> 20, + PartTypeStr); + } + + // This code block is to provide additional info + { + UINT64 DiskSizeMiB; + UINT64 SizeOccupiedMiB; + + DiskSizeMiB = MultU64x32 (PrimaryHeader->LastUsableLBA - PrimaryHeader->FirstUsableLBA + 1, BlockIo->Media->BlockSize) >> 20; + SizeOccupiedMiB = MultU64x32 (BlocksOccupied, BlockIo->Media->BlockSize) >> 20; + Print (L"\r\n%d partition(s) used out of %d\r\n", NumEntries, PrimaryHeader->NumberOfPartitionEntries); + Print (L"Total device capacity (minus GPT service blocks): %llu MiB\r\nPartitioned space: %llu MiB\r\n", + DiskSizeMiB, + SizeOccupiedMiB); + Print (L"Unpartitioned space available: %llu MiB (%d%%)\n", + DiskSizeMiB - SizeOccupiedMiB, + (100 * (DiskSizeMiB - SizeOccupiedMiB)) / DiskSizeMiB); + } + + return NumEntries; +} + +/** + Locate a partition by criteria + + @param Name Partition name (or ordinal represented a string) + @param StartLba StartLba of the partition to search for + @param EndingLba EndingLba of the partition to search for + @param SearchType A combination (OR) of search options. + Options are: + SRC_BY_NAME = search by partition name + SRC_BY_LBA = search by Start/Ending lba + SRC_ANY = returns a first used partition + SRC_BY_NUM = search by ordinal + + + @return Non-NULL Pointer to the partition found + @return NULL No partition found + +**/ +EFI_PARTITION_ENTRY * +PartitionFindPartitionByCriteria ( + IN OPTIONAL CONST CHAR16 *Name, + IN OPTIONAL EFI_LBA StartLba, + IN OPTIONAL EFI_LBA EndingLba, + IN SEARCH_TYPE SearchType + ) +{ + UINTN Index; + EFI_PARTITION_ENTRY *Entry; + + if (!GptValid) { + return NULL; + } + + if (!Name && (SearchType & SRC_BY_NAME)) { + return NULL; + } + + for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) { + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index * PrimaryHeader->SizeOfPartitionEntry); + + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + if (SearchType & SRC_ANY) { + return Entry; + } + + if (SearchType & SRC_BY_NAME) { + if (!StrCmp (Name, Entry->PartitionName)) { + return Entry; + } + } + if (SearchType & SRC_BY_NUM) { + UINTN Ordinal; + + Ordinal = ShellStrToUintn (Name); + if (Index == (Ordinal - 1)) { + return Entry; + } + } + if (SearchType & SRC_BY_LBA) { + if ( + ((StartLba >= Entry->StartingLBA) && + (StartLba <= Entry->EndingLBA) + ) || + ((EndingLba >= Entry->StartingLBA) && + (EndingLba <= Entry->EndingLBA) + ) + ) { + return Entry; + } + } + } + return NULL; +} + +/** + Prints information on a given partition. + + @param Entry Pointer to the Partition of interest +**/ + +VOID +PartitionPrintGptPartInfo ( + IN EFI_PARTITION_ENTRY *Entry + ) +{ + CONST CHAR16 PartStr[MAX_PARTITION_NAME_LENGTH + 1]; + CONST CHAR16 *StrUnknown; + EFI_STATUS Status; + EFI_LBA Length; + + ASSERT (Entry); + + ZeroMem ((VOID *)PartStr, sizeof (PartStr)); + StrUnknown = GPT_PART_UNKNOWN_STR; + + Status = GetPartitionTypeStr (Entry->PartitionTypeGUID, (CHAR16 *)&PartStr, TRUE); + if (EFI_ERROR (Status)) { + StrCpy ((CHAR16 *)&PartStr, StrUnknown); + } + + Length = Entry->EndingLBA - Entry->StartingLBA + 1; + + Print (L"Partition name: %s\r\n", Entry->PartitionName); + Print (L"Starting LBA : 0x%09llx\r\nEnding LBA : 0x%09llx\r\n", + (UINT64)Entry->StartingLBA, (UINT64)Entry->EndingLBA); + Print (L"Partition Size: %llu MiB (0x%llx blocks)\n", + (MultU64x32 (Length, BlockIo->Media->BlockSize)) >> 20, Length); + Print (L"Attributes : 0x%09llx\r\n", Entry->Attributes); + Print (L"Type/GUID : %s/%g\r\n", PartStr, &Entry->PartitionTypeGUID); + Print (L"Unique GUID : %g\n", &Entry->UniquePartitionGUID); +} + +/** + Writes protective MBR to a block device. + + @return EFI_SUCCESS MBR written successfully + @return other Failed to write an MBR + +**/ + +STATIC +EFI_STATUS +WriteProtectiveMbr ( + VOID + ) +{ + UINT32 BlockSize; + UINT64 DiskSize; + EFI_STATUS Status; + MBR_PARTITION_RECORD *Partition; + MASTER_BOOT_RECORD *ProtectiveMbr; + + BlockSize = BlockIo->Media->BlockSize; + + ProtectiveMbr = NULL; + ProtectiveMbr = AllocateZeroPool (BlockSize); + if (ProtectiveMbr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DiskSize = BlockIo->Media->LastBlock + 1; + if (DiskSize > 0xffffffff) { + DiskSize = 0xffffffff; + } + + CopyMem (ProtectiveMbr, &ProtectiveMbrTemplate, sizeof (MASTER_BOOT_RECORD)); + + Partition = &ProtectiveMbr->Partition[0]; + + Partition->BootIndicator = 0; + Partition->StartSector = 1; + + // + // We don't actually know this data, so we'll make up + // something that seems likely. + // + + // + // Old software is expecting the Partition to start on + // a Track boundary, so we'll set track to 1 to avoid "overlay" + // with the MBR + // + + Partition->StartTrack = 1; + + Status = DiskIo->WriteDisk ( + DiskIo, + BlockIo->Media->MediaId, + 0, + BlockSize, + ProtectiveMbr + ); + + MbrValid = !EFI_ERROR (Status); + + SHELL_FREE_NON_NULL (ProtectiveMbr); + + return Status; +} + +/** + Write GPT tables to the block device. + + @return EFI_SUCCESS GPT tables were successfully written/updated + @return other Failed to write/update GPT tables + +**/ + +STATIC +EFI_STATUS +WriteGPT ( + VOID + ) +/* + CALLER is expected to fill in: + FirstUseableLBA + LastUseableLBA + EntryCount + DiskGUID + + We fill in the rest, and blast it out. + + Returns a status. + +*/ +{ + UINT32 BlockSize; + UINT32 TableSize; + EFI_STATUS Status = EFI_SUCCESS; + + BlockSize = BlockIo->Media->BlockSize; + TableSize = PrimaryHeader->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY); + + if (!MbrValid) { + WriteProtectiveMbr (); + } + // + // Write out the primary header... + // + PrimaryHeader->Header.Signature = EFI_PTAB_HEADER_ID; + PrimaryHeader->Header.Revision = GPT_REVISION_1_0; + PrimaryHeader->Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER); + + PrimaryHeader->AlternateLBA = BackupHeader->MyLBA; + + PrimaryHeader->SizeOfPartitionEntry = sizeof (EFI_PARTITION_ENTRY); + + Status = gBS->CalculateCrc32 ((UINT8 *)PartEntry, TableSize, &PrimaryHeader->PartitionEntryArrayCRC32); + if (EFI_ERROR (Status)) { + return Status; + } + + // Write primary header + PartitionSetCrc (&PrimaryHeader->Header); + + Status = DiskIo->WriteDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (PrimaryHeader->MyLBA, (UINT32)BlockSize), + BlockSize, + PrimaryHeader + ); + + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Write out the primary table ... + // + Status = DiskIo->WriteDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (PrimaryHeader->PartitionEntryLBA, (UINT32)BlockSize), + TableSize, + PartEntry + ); + + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Write out the secondary header and table by calling restore + // + + if (!PartitionRestoreGptTable (PrimaryHeader)) { + return EFI_VOLUME_CORRUPTED; + } + BlockIo->FlushBlocks (BlockIo); + GptValid = !EFI_ERROR (Status); + + return Status; +} + +/** + (Re)initialize GPT tables on the block device + + @return EFI_SUCCESS Successfully (re)initialized GPT Tables + @return other Failed to (re)initialize GPT tables + +**/ +STATIC +EFI_STATUS +TableCreateEmptyGpt ( + VOID + ) +{ + UINTN EntryCount; + UINTN BlockFit; + UINTN BlockSize; + UINTN EntryBlocks; + UINT64 DiskSize; + UINTN TableSize; + EFI_LBA Header1_LBA; + EFI_LBA Table1_LBA; + EFI_LBA Header2_LBA; + EFI_LBA Table2_LBA; + EFI_LBA FirstUsableLBA; + EFI_LBA LastUsableLBA; + EFI_STATUS Status; + + EntryCount = ENTRY_DEFAULT; + BlockSize = BlockIo->Media->BlockSize; + BlockFit = BlockSize / sizeof (EFI_PARTITION_ENTRY); + + if (BlockFit > ENTRY_DEFAULT) { + EntryCount = BlockFit; + } + EntryBlocks = EntryCount / BlockFit; + + if ((EntryBlocks * BlockFit) != EntryCount) { + Status = EFI_VOLUME_CORRUPTED; + PrintErr (L"Invalid Entry blocks and Entry count combination\n", Status); + return Status; + } + + DiskSize = BlockIo->Media->LastBlock + 1; + + SHELL_FREE_NON_NULL (PrimaryHeader); + SHELL_FREE_NON_NULL (BackupHeader); + SHELL_FREE_NON_NULL (PartEntry); + + PrimaryHeader = AllocateZeroPool (BlockSize); + if (PrimaryHeader == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BackupHeader = AllocateZeroPool (BlockSize); + if (BackupHeader == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Header1_LBA = 1; + Table1_LBA = 2; + FirstUsableLBA = Table1_LBA + EntryBlocks; + + Header2_LBA = DiskSize - 1; + Table2_LBA = Header2_LBA - EntryBlocks; + LastUsableLBA = Table2_LBA - 1; + + TableSize = EntryBlocks * BlockSize; + + if (TableSize != (EntryCount * sizeof (EFI_PARTITION_ENTRY))) { + Status = EFI_VOLUME_CORRUPTED; + PrintErr (L"Invalid Table size and Entry count combination\n", Status); + return Status; + } + + if (GPT_DEBUG_LEVEL) { + Print (L"DiskSize = %lx\n", DiskSize); + Print (L"BlockSize = %x\n", BlockSize); + Print (L"Header1_LBA = %lx\n", Header1_LBA); + Print (L"Table1_LBA = %lx\n", Table1_LBA); + Print (L"FirstUsableLBA = %lx\n", FirstUsableLBA); + Print (L"Header2_LBA = %lx\n", Header2_LBA); + Print (L"Table2_LBA = %lx\n", Table2_LBA); + Print (L"LastUsableLBA = %lx\n", LastUsableLBA); + Print (L"EntryCount = %x\n", EntryCount); + Print (L"EntryBlocks = %x\n", EntryBlocks); + } + + // + // Since we're making empty tables, we just write zeros... + // + + PartEntry = AllocateZeroPool (TableSize); + if (PartEntry == NULL) { + SHELL_FREE_NON_NULL (PrimaryHeader); + SHELL_FREE_NON_NULL (BackupHeader); + return EFI_OUT_OF_RESOURCES; + } + + PEntryStatus = AllocateZeroPool (TableSize); + + PrimaryHeader->FirstUsableLBA = FirstUsableLBA; + PrimaryHeader->LastUsableLBA = LastUsableLBA; + PrimaryHeader->NumberOfPartitionEntries = (UINT32)EntryCount; + GenerateGuid (&PrimaryHeader->DiskGUID); + + PrimaryHeader->MyLBA = Header1_LBA; + BackupHeader->MyLBA = Header2_LBA; + PrimaryHeader->PartitionEntryLBA = Table1_LBA; + BackupHeader->PartitionEntryLBA = Table2_LBA; + + Status = WriteGPT (); + + return Status; +} + +/** + Clear GPT partitions. + + @return EFI_SUCCESS Cleared successfully + @return FALSE Failed to clear + +**/ + +EFI_STATUS +PartitionGptClearAll ( + VOID + ) +{ + GptCleanupGlobals (); + GptValid = FALSE; + MbrValid = FALSE; + return TableCreateEmptyGpt (); +} + +/** + Create a GPT partition. + + @param PartName Partition Name + @param StartLba Starting LBA of the partition. + if zero, will be calculated + @param SizeInMegaBytes Size of the partition in MB + @param Attributes Partition attributes + @param PartTypeGuid a Type GUID to be assigned to the partition (not a partition unique GUID) + + + @return EFI_SUCCESS Partition successfully created + @return EFI_INVALID_PARAMETER Either partition exists, or wrong parameters specified + @return other Failed to create a partition +**/ +EFI_STATUS +PartitionGptCreatePartition ( + IN CONST CHAR16 *PartName, + IN EFI_LBA StartLba, + IN UINT64 SizeInMegabytes, + IN UINT64 Attributes, + IN EFI_GUID PartTypeGuid + ) +{ + EFI_GUID Guid; + EFI_GUID PartitionIdGuid; + EFI_STATUS Status; + UINT64 StartBlock; + UINT64 EndBlock; + UINT64 SizeInBytes; + UINT32 BlockSize; + UINT64 DiskSizeBlocks; + UINT8 *p; + BOOLEAN OffsetSpecified; + BOOLEAN AllZeros; + INTN AllZeroEntry; + INTN OldFreeEntry; + UINT64 AvailBlocks; + UINT64 BlocksToAllocate; + UINT64 HighSeen; + UINTN Slot; + UINT64 LowestAlignedLba; + UINT32 OptimalTransferBlocks; + UINTN i; + UINTN j; + CHAR16 PartNameUsed[MAX_PARTITION_NAME_LENGTH + 1]; + EFI_PARTITION_ENTRY *Entry; + + SizeInBytes = 0; + OffsetSpecified = FALSE; + ZeroMem (PartNameUsed, sizeof (PartNameUsed)); + LowestAlignedLba = 0; + OptimalTransferBlocks = 1; + + AllZeroEntry = -1; + OldFreeEntry = -1; + + BlockSize = BlockIo->Media->BlockSize; + OffsetSpecified = (StartLba != 0); + CopyMem (&PartNameUsed, PartName, sizeof (CHAR16) * StrSize (PartName)); + + GenerateGuid (&Guid); + + // Creating a new partition + if (!GptValid) { + // Creating a GPT for the first time + Status = TableCreateEmptyGpt (); + if (!EFI_ERROR (Status)) { + // Fill in the structures + Status = PartitionGetGptTables (DiskIo, BlockIo); + } + if (EFI_ERROR (Status)) { + return Status; + } + } + + Entry = PartitionFindPartitionByCriteria (PartName, 0, 0, SRC_BY_NAME); + if (Entry) { + Status = EFI_INVALID_PARAMETER; + PrintErr (L"Partition with this name already exists", Status); + return Status; + } + HighSeen = PrimaryHeader->FirstUsableLBA - 1; + + if (StartLba) { + // + // if offset is specified, compute the start and end blocks + // + StartBlock = StartLba; + // + // StartBlock should be aligned to OptimalTransferBlocks, the least common multiple of: + // a). the physical block boundary, if any + // b). the optimal transfer length granularity, if any + // + if (StartBlock < LowestAlignedLba) { + StartBlock = LowestAlignedLba; + } else { + while (((StartBlock - LowestAlignedLba) % OptimalTransferBlocks) != 0) { + StartBlock++; + } + } + + if (StartBlock < PrimaryHeader->FirstUsableLBA || + StartBlock > PrimaryHeader->LastUsableLBA) { + // + // Offset specified is too large + // + Status = EFI_INVALID_PARAMETER; + PrintErr (L"Specified offset is too large", EFI_INVALID_PARAMETER); + 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); + EndBlock = StartBlock + BlocksToAllocate - 1; + if (EndBlock > PrimaryHeader->LastUsableLBA) { + EndBlock = PrimaryHeader->LastUsableLBA; + BlocksToAllocate = EndBlock - StartBlock + 1; + } + } + } + + for (i = 0; i < PrimaryHeader->NumberOfPartitionEntries; i++) { + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + i * PrimaryHeader->SizeOfPartitionEntry); + if (!CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + + // + // Type not null, so it's allocated + // + if (Entry->EndingLBA > HighSeen) { + HighSeen = Entry->EndingLBA; + } + if (OffsetSpecified) { + // + // make sure new partition does not overlap with existing partitions + // + if (Entry->StartingLBA <= StartBlock && + StartBlock <= Entry->EndingLBA) { + // + // starting block is inside an existing partition + // + Status = EFI_INVALID_PARAMETER; + PrintErr (L"Starting block is inside an existing partition", Status); + goto Exit; + } + if ((Entry->StartingLBA <= EndBlock && + EndBlock <= Entry->EndingLBA) || + (StartBlock <= Entry->StartingLBA && + Entry->StartingLBA <= EndBlock) || + (StartBlock <= Entry->EndingLBA && + Entry->EndingLBA <= EndBlock)) { + // + // new partition overlaps with an existing partition + // readjust new partition size to avoid overlapping + // + EndBlock = Entry->StartingLBA - 1; + if (EndBlock < StartBlock) { + Status = EFI_INVALID_PARAMETER; + PrintErr (L"Cannot readjust new partition size - overlapping", Status); + goto Exit; + } else { + BlocksToAllocate = EndBlock - StartBlock + 1; + } + } + } + } else { + p = (UINT8 *)(Entry); + AllZeros = TRUE; + for (j = 0; j < sizeof (EFI_PARTITION_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; + PrintErr (L"Table is full", Status); + 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 = PrimaryHeader->LastUsableLBA; + BlocksToAllocate = EndBlock - StartBlock + 1; + } + } else { + // + // Because HighSeen is the last LBA of the used blocks, let HighSeen align to the least common multiple of: + // a). the physical block boundary, if any + // b). the optimal transfer length granularity, if any + // + if (HighSeen + 1 < LowestAlignedLba) { + HighSeen = LowestAlignedLba - 1; + } else { + while (((HighSeen + 1 - LowestAlignedLba) % OptimalTransferBlocks) != 0) { + HighSeen++; + } + } + + if (PrimaryHeader->LastUsableLBA <= HighSeen) { + Status = EFI_OUT_OF_RESOURCES; + PrintErr (L"Disk has no free blocks (FULL) cannot create", Status); + goto Exit; + } + // + // [HighSeen+1 ... LastUsableLBA] is available... + // avail = (LastUsableLBA - (HighSeen+1)) + 1 => LastUsabbleLBA - HighSeen + // + AvailBlocks = PrimaryHeader->LastUsableLBA - HighSeen; + + 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); + + } + } + + // + // 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; + PrintErr (L"Partition is too small to be created", Status); + goto Exit; + } + + if (GPT_DEBUG_LEVEL) { + 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; + } + + GenerateGuid (&PartitionIdGuid); + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Slot * PrimaryHeader->SizeOfPartitionEntry); + + CopyMem (&Entry->PartitionTypeGUID, &PartTypeGuid, sizeof (EFI_GUID)); + CopyMem (&Entry->UniquePartitionGUID, &PartitionIdGuid, sizeof (EFI_GUID)); + if (OffsetSpecified) { + PartEntry[Slot].StartingLBA = StartBlock; + PartEntry[Slot].EndingLBA = EndBlock; + } else { + PartEntry[Slot].StartingLBA = HighSeen + 1; + PartEntry[Slot].EndingLBA = HighSeen + BlocksToAllocate; + } + + if (!(((Entry->EndingLBA - Entry->StartingLBA) + 1) == BlocksToAllocate)) { + PrintErr (L"Wrong Size for new partiton", EFI_INVALID_PARAMETER); + goto Exit; + } + + if ((Entry->StartingLBA < PrimaryHeader->FirstUsableLBA) || + (Entry->EndingLBA > PrimaryHeader->LastUsableLBA)) { + PrintErr (L"New Partition out of bounds", EFI_INVALID_PARAMETER); + goto Exit; + } + + Entry->Attributes = Attributes; + CopyMem (&(Entry->PartitionName[0]), PartName, MAX_PARTITION_NAME_LENGTH * sizeof (CHAR16)); + + DiskSizeBlocks = BlockIo->Media->LastBlock + 1; + if (DiskSizeBlocks > 0xffffffff) { + DiskSizeBlocks = 0xffffffff; + } + + Status = WriteGPT (); + + if (EFI_ERROR (Status)) { + PrintErr (L"Attempt to Write out partition table failed", Status); + } + + Exit: + return Status; +} + +/** + Modifiy a partition based on parameters. + + @param Entry Pointer to an existing partition + @param Params Parameters to be modified + @param Flags Which parameter is to be modified + + @return EFI_SUCCESS Successfully modified the partition + @return other Failed to modify a partition + +**/ + +EFI_STATUS +PartitionGptModifyPartition ( + IN EFI_PARTITION_ENTRY *Entry, + IN MOD_PARAMS *Params, + IN MOD_FLAGS Flags + ) +{ + EFI_STATUS Status; + + ASSERT (Entry); + + Status = EFI_INVALID_PARAMETER; + + switch (Flags) { + case MOD_DELETE: + ZeroMem (Entry, sizeof (EFI_PARTITION_ENTRY)); + break; + case MOD_ATTR: + Entry->Attributes = Params->Attributes; + break; + case MOD_TYPE: + Entry->PartitionTypeGUID = Params->PartTypeGuid; + break; + case MOD_RENAME: + StrCpy (Entry->PartitionName, Params->NewName); + break; + default: + PrintErr (L"Unknown modification flag(s)", Status); + return Status; + } + + return WriteGPT (); +} + +EFI_KNOWN_PARTITION_TYPE *PartitionGetKnownType ( + IN UINTN Index, + IN OUT OPTIONAL UINTN *NumEntries + ) +{ + if (NumEntries) { + *NumEntries = ARRAY_SIZE (PartitionTypes); + } + if (Index > ARRAY_SIZE (PartitionTypes)) { + return NULL; + } + + return &PartitionTypes[Index]; +} + diff --git a/ShellPkg/Library/UefiShellGptCommandLib/GptWorker.h b/ShellPkg/Library/UefiShellGptCommandLib/GptWorker.h new file mode 100644 index 000000000000..048377d883e7 --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/GptWorker.h @@ -0,0 +1,188 @@ +/* + * BSD LICENSE + * + * Copyright(c) 2016 Broadcom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Broadcom nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* GPT partitioner header file */ + +#ifndef _GPTWORKER_H_ +#define _GPTWORKER_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + MOD_NAME = BIT0, + MOD_ATTR = BIT1, + MOD_TYPE = BIT2, + MOD_DELETE = BIT3, + MOD_RENAME = BIT4 +} MOD_FLAGS; + +typedef struct { + UINTN Attributes; + EFI_GUID PartTypeGuid; + CONST CHAR16 *PartName; + CONST CHAR16 *NewName; +} MOD_PARAMS; + +typedef enum { + SRC_BY_NAME = BIT0, + SRC_BY_LBA = BIT1, + SRC_BY_NUM = BIT2, + SRC_ANY = BIT3 +} SEARCH_TYPE; + +#define MAX_PARTITION_NAME_LENGTH 36 +#define ENTRY_DEFAULT 128 +#define GPT_REVISION_1_0 0x00010000 + +#define ARRAY_SIZE(x) \ + (sizeof(x) / sizeof((x)[0])) + +// +// Extract INT32 from char array +// +#define UNPACK_INT32(a) (INT32)( (((UINT8 *) a)[0] << 0) | \ + (((UINT8 *) a)[1] << 8) | \ + (((UINT8 *) a)[2] << 16) | \ + (((UINT8 *) a)[3] << 24) ) + +// +// Extract UINT32 from char array +// +#define UNPACK_UINT32(a) (UINT32)( (((UINT8 *) a)[0] << 0) | \ + (((UINT8 *) a)[1] << 8) | \ + (((UINT8 *) a)[2] << 16) | \ + (((UINT8 *) a)[3] << 24) ) +#define GPT_PART_UNKNOWN_STR L"Unknown"; + +// +// GPT Partition Entry Status +// +typedef struct { + BOOLEAN OutOfRange; + BOOLEAN Overlap; + BOOLEAN OsSpecific; +} EFI_PARTITION_ENTRY_STATUS; + +typedef struct { + CONST EFI_GUID TypeGuid; + CONST CHAR16 *TypeName; + +} EFI_KNOWN_PARTITION_TYPE; + +EFI_KNOWN_PARTITION_TYPE *PartitionGetKnownType ( + IN UINTN Index, + IN OUT OPTIONAL UINTN *NumEntries + ); + +EFI_STATUS +PartitionGetGptTables ( + IN EFI_DISK_IO_PROTOCOL *DiskIoProt, + IN EFI_BLOCK_IO_PROTOCOL *BlockIoProt + ); + +UINTN +PartitionListGptEntries ( + VOID + ); + +VOID +PartitionPrintGptPartInfo ( + IN EFI_PARTITION_ENTRY *Entry + ); + +EFI_STATUS +GetPartitionTypeStr ( + IN EFI_GUID Guid, + OUT CHAR16 *PartTypeStr, + IN BOOLEAN NoGuidStr + ); + + +EFI_STATUS +PartitionGptClearAll ( + VOID + ); + +EFI_STATUS +PartitionGptCreatePartition ( + IN CONST CHAR16 *PartName, + IN EFI_LBA StartLba, + IN EFI_LBA PartitionSize, + IN UINT64 Attributes, + IN EFI_GUID PartTypeGuid); + +EFI_STATUS +PartitionGptModifyPartition ( + IN EFI_PARTITION_ENTRY *Entry, + IN MOD_PARAMS *Params, + IN MOD_FLAGS Flags + ); + +VOID +GptCleanupGlobals ( + VOID + ); + +EFI_PARTITION_ENTRY * +PartitionFindPartitionByCriteria ( + IN CONST CHAR16 *Name, + IN EFI_LBA StartLba, + IN EFI_LBA EndingLba, + IN SEARCH_TYPE SearchType); + +VOID +PrintErr ( + IN CONST CHAR16 *Message, + IN EFI_STATUS Status + ); + +#endif //_GPTWORKER_H_ diff --git a/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.c b/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.c new file mode 100644 index 000000000000..b2d562443ae3 --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.c @@ -0,0 +1,1267 @@ +/******************************************************************************* +Copyright (C) 2016, Marvell International Ltd. All rights reserved. +Copyright (C) 2016, Broadcom. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#include "FatFormat.h" +#include +#include + +CONST CHAR16 gShellGptFileName[] = L"ShellCommand"; +STATIC CONST CHAR16 gAppName[] = L"gpt"; +EFI_HANDLE gShellGptHiiHandle = NULL; + +STATIC CONST SHELL_PARAM_ITEM ParamList[] = { + // Partition-related operations + { L"clear", TypeFlag }, // Clears all partitions + { L"create", TypeFlag }, // Creates a partition + { L"delete", TypeFlag }, // Deletes a partition + { L"list", TypeFlag }, // Lists all partitions + { L"rename", TypeFlag }, // Renames a partition + { L"setattr", TypeFlag }, // Set attributes for a partition in partition table + { L"settype", TypeFlag }, + { L"sync", TypeFlag }, // Synchronizes either master or alternative partition table + { L"typesinfo", TypeFlag }, // Verifies correctness of master and alternative partition tables + // BlockIo-related operations + { L"read", TypeFlag }, // Reads n bytes into memory address from a partition starting from a certain lba + { L"readfile", TypeFlag }, // Same as above, but instead of store it in memory saves into a file system + { L"write", TypeFlag }, // Writes n bytes from memory into a partition starting from a certain lba + { L"writefile", TypeFlag }, // Same as above, but instead of getting data from memory reads a file + { L"info", TypeFlag }, // Get information on a certain partition (startLba, lastLba, attributes) + { L"fatformat", TypeFlag }, // FAT format of a partition + { L"-type", TypeValue }, // Set GUID type to either an arbitrary GUID, or to a GUID from a know partition type + { L"-yes", TypeFlag }, // Warn, but do not prompt on potentially destructive operations, + // assume all YES. Potentially dangerous + { L"-verbose", TypeFlag }, // Print additional information on the operation + { L"-fat16", TypeFlag }, // Do FAT16 format if possible (by default format is FAT32) + { NULL, TypeMax } +}; + +typedef enum { + // Not requires presence + CLEAR = BIT0, + CREATE = BIT1, + LIST = BIT2, + SYNC = BIT3, + TYPES_INFO = BIT4, + + // Requires presence + DELETE = BIT5, + INFO = BIT6, + READ = BIT7, + READ_FILE = BIT8, + RENAME = BIT9, + SETATTR = BIT10, + SETTYPE = BIT11, + WRITE = BIT12, + WRITE_FILE = BIT13, + FAT_FORMAT = BIT14, +} Flags; + +/** + Return the file name of the help text file if not using HII. + + @return The string pointer to the file name. +**/ +CONST CHAR16 * +EFIAPI +ShellCommandGetManFileNameGpt ( + VOID + ) +{ + + return gShellGptFileName; +} + +/** + Open a file for read or write + + @param [in] FilePath The path to the file to be opened/created + @param [out] FileHandle The handle of the file on successful open/create + @param [in] WriteNeeded Indicates that the file is being opened for write + + @return EFI_SUCCESS File opened/created successfully + @return other Failed to open/create the file + +**/ +STATIC +EFI_STATUS +OpenAndPrepareFile ( + IN CHAR16 *FilePath, + OUT SHELL_FILE_HANDLE *FileHandle, + IN BOOLEAN WriteNeeded + ) +{ + EFI_STATUS Status; + UINT64 OpenMode; + + OpenMode = EFI_FILE_MODE_READ; + + if (WriteNeeded) { + OpenMode |= EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE; + } + + Status = ShellOpenFileByName (FilePath, FileHandle, OpenMode, 0); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_ERROR), + gShellGptHiiHandle, + gAppName, + Status, + L"Cannot open file" + ); + return Status; + } + + Status = FileHandleSetPosition (*FileHandle, 0); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_ERROR), + gShellGptHiiHandle, + gAppName, + Status, + "Cannot set file position to the first byte" + ); + + ShellCloseFile (FileHandle); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Print error using generic HII format string + + @param [in] Message Error message to be displayed + @param [in] Status Status from the previous operation + +**/ +VOID +PrintErr ( + IN CONST CHAR16 *Message, + IN EFI_STATUS Status + ) +{ + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_ERROR), + gShellGptHiiHandle, + gAppName, + Status, + Message + ); +} + +/** + Find out if this is a partitionable device by Device Path + + @param [in] DevicePath Device path of the block device + + @return TRUE Block device is partitionable + @return FALSE Block device is not partitionable + +**/ + +STATIC +BOOLEAN +IsPartitionableDevicePath ( + IN EFI_DEVICE_PATH *DevicePath + ) +{ + UINTN PathSize; + EFI_DEVICE_PATH *PathInstance; + BOOLEAN Partitionable; + + Partitionable = TRUE; + while (DevicePath != NULL) { + PathInstance = GetNextDevicePathInstance (&DevicePath, &PathSize); + + while (!IsDevicePathEnd (PathInstance)) { + if ((DevicePathType (PathInstance) == MEDIA_DEVICE_PATH)) { + Partitionable = FALSE; + } + + PathInstance = NextDevicePathNode (PathInstance); + } + } + return Partitionable; +} + +/** + Prompt a user with HII prompt and get answer (Yes, or No) + + @param [in] HiiFormatStringId ID of the HII string in the .uni file + + @return EFI_SUCCESS A user answered "Yes" + @return EFI_ABORTED A user answered "No" + +**/ +STATIC +EFI_STATUS +GptPromptYesNo ( + IN CONST EFI_STRING_ID HiiFormatStringId + ) +{ + EFI_STATUS Status; + VOID *Response; + + Status = ShellPromptForResponseHii (ShellPromptResponseTypeYesNo, HiiFormatStringId, gShellGptHiiHandle, &Response); + if ((EFI_ERROR (Status)) || (*(SHELL_PROMPT_RESPONSE *)Response) != ShellPromptResponseYes) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Format a size in human-readable format + + @param [in] Size The size to be formatted + @param [out] Buffer Buffer receiving the format string + + The size is formatted so that it represents a unit and a metric. + Thus, for 1967128576 bytes it would be 1.8G; + for 33554432 bytes it would be 32M, etc. + +**/ +STATIC +VOID FormatSize ( + IN UINT64 Size, + IN CHAR16 *Buffer + ) +{ +#define MAX_SIZE_BUF_SIZE 32 + UINT64 Base; + UINT64 Frac; + CHAR16 Metric; + + Metric = L'B'; + Frac = 0; + if (Size < SIZE_1KB) { + Base = Size; + } else if (Size < SIZE_1MB) { + Base = Size / SIZE_1KB; + Frac = ((Size % SIZE_1KB) * 10) >> 10; + Metric = L'K'; + } else if (Size < SIZE_1GB) { + Base = Size / SIZE_1MB; + Frac = ((Size % SIZE_1MB) * 10) >> 20; + Metric = L'M'; + } else if (Size < SIZE_1TB) { + Base = Size / SIZE_1GB; + Frac = ((Size % SIZE_1GB) * 10) >> 30; + Metric = L'G'; + } else { + Base = Size / SIZE_1TB; + Frac = ((Size % SIZE_1TB) * 10) >> 40; + Metric = L'T'; + } + if (Frac) { + UnicodeSPrint (Buffer, MAX_SIZE_BUF_SIZE, L"%d.%d%c", Base, Frac, Metric); + } else { + UnicodeSPrint (Buffer, MAX_SIZE_BUF_SIZE, L"%d%c", Base, Metric); + } +} + +/** + Find and print a table of all partitionable devices + + @return EFI_SUCCESS Successfully obtained a list + @return EFI_OUT_OF_RESOURCES Could not allocate memory + @return other Error obtaining the list of devices + +**/ + +STATIC +EFI_STATUS +FindAndPrintPartitionableDevices ( + VOID + ) +{ + UINTN Index; + IN EFI_HANDLE *HandlePointer; + UINTN HandleCount; + UINTN DevCount; + EFI_STATUS Status; + BOOLEAN FirstTime; + CHAR16 *BufferForSize; + + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &HandleCount, &HandlePointer); + if (EFI_ERROR (Status)) { + return Status; + } + + DevCount = 0; + FirstTime = TRUE; + BufferForSize = NULL; + + for (Index = 0; Index < HandleCount; Index++) { + EFI_DEVICE_PATH *DevicePath; + CONST CHAR16 *MapPath; + CHAR16 *Match; + EFI_BLOCK_IO *BlkIo; + EFI_DISK_IO *DiskIo; + + Status = gBS->HandleProtocol (HandlePointer[Index], &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo); + if (EFI_ERROR (Status)) { + continue; + } + + DevicePath = DevicePathFromHandle (HandlePointer[Index]); + if (!IsPartitionableDevicePath (DevicePath)) { + continue; + } + MapPath = gEfiShellProtocol->GetMapFromDevicePath (&DevicePath); + if (MapPath == NULL) { + continue; + } + + Status = gBS->HandleProtocol (HandlePointer[Index], &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo); + if (EFI_ERROR (Status)) { + continue; + } + + Match = StrStr (MapPath, L";BLK"); + if (Match) { + MapPath = Match; + MapPath++; + } + + if (FirstTime) { + BufferForSize = AllocateZeroPool (MAX_SIZE_BUF_SIZE * sizeof (CHAR16)); + if (BufferForSize == NULL) { + Status = EFI_OUT_OF_RESOURCES; + PrintErr (L"Could not allocate memory for size buffer\n", Status); + return Status; + } + Print (L" Device\t Size Comments\n"); + Print (L" ------ ------- ------------------------------------------------------------\n"); + FirstTime = FALSE; + } + GptCleanupGlobals (); + FormatSize (MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize), BufferForSize); + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_LIST_DEVS), + gShellGptHiiHandle, + MapPath, + BufferForSize); + if (BlkIo->Media->ReadOnly) { + Print (L"Read-Only! "); + } + if (!BlkIo->Media->MediaPresent) { + Print (L"No Media! "); + } + if (!EFI_ERROR (PartitionGetGptTables (DiskIo, BlkIo))) { + Print (L"Valid GPT. "); + } + if (BlkIo->Media->RemovableMedia) { + Print (L"Removable device."); + } + Print (L"\n"); + DevCount++; + } + Print (L"\r\n"); + if (DevCount) { + Print (L"%d potentially partitionable device(s) found\n", DevCount); + } else { + Print (L"No potentially partitionable device(s) found\n"); + } + + GptCleanupGlobals (); + SHELL_FREE_NON_NULL (BufferForSize); + + return EFI_SUCCESS; +} + +SHELL_STATUS +EFIAPI +ShellCommandRunGpt ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + LIST_ENTRY *CheckPackage; + EFI_PHYSICAL_ADDRESS Address; + EFI_PHYSICAL_ADDRESS Offset; + UINT64 PartAttributes; + EFI_GUID PartTypeGuid; + SHELL_FILE_HANDLE FileHandle; + UINT64 ByteCount; + UINT64 FileSize; + UINTN I; + UINT8 *Buffer; + UINT8 *FileBuffer; + + CHAR16 *ProblemParam; + CHAR16 *FilePath; + CONST CHAR16 *AddressStr; + CONST CHAR16 *OffsetStr; + CONST CHAR16 *PartName; + CONST CHAR16 *NewPartName; + CONST CHAR16 *AttrStr; + CONST CHAR16 *GuidStr; + CONST CHAR16 *VolumeName; + CONST CHAR16 *LengthStr; + CONST CHAR16 *FileStr; + + BOOLEAN AddrFlag; + BOOLEAN LengthFlag; + BOOLEAN FileFlag; + BOOLEAN GuidFlag; + BOOLEAN OffsetFlag; + BOOLEAN PartNameFlag; + BOOLEAN NewPartNameFlag; + BOOLEAN AttrFlag; + + UINTN Flag; + UINTN CheckFlag; + + CONST CHAR16 *BlockName; + + EFI_DEVICE_PATH_PROTOCOL *DevPath; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_HANDLE BlockIoHandle; + MOD_PARAMS ModParams; + EFI_PARTITION_ENTRY *Entry; + + UINTN NumKnownPartTypesEntries; + + BOOLEAN TableNotEmpty; + BOOLEAN GptIsValid; + BOOLEAN NoPrompt; + BOOLEAN Quiet; + BOOLEAN ForceFat16; + + UINT64 TimeStampB; + UINT64 TimeStampE; + UINT64 SpeedKB; + UINT64 Freq; + + // Initialization + Address = Offset = 0; + PartAttributes = Flag = 0; + ZeroMem (&PartTypeGuid, sizeof (EFI_GUID)); + FileHandle = NULL; + Buffer = FileBuffer = NULL; + AddressStr = OffsetStr = PartName = NewPartName = AttrStr = GuidStr = + VolumeName = LengthStr = FileStr = NULL; + AddrFlag = FileFlag = GuidFlag = NewPartNameFlag = AttrFlag = FALSE; + LengthFlag = OffsetFlag = PartNameFlag = TRUE; + NoPrompt = ForceFat16 = FALSE; + Quiet = TRUE; + TimeStampB = 0; + TimeStampE = 0; + GptIsValid = FALSE; + + // Parse Shell command line + Status = ShellInitialize (); + if (EFI_ERROR (Status)) { + PrintErr (L"Cannot initialize Shell", Status); + ASSERT_EFI_ERROR (Status); + return SHELL_ABORTED; + } + + Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE); + if (EFI_ERROR (Status)) { + PrintErr (L"Error while parsing command line", Status); + return SHELL_ABORTED; + } + + NoPrompt = ShellCommandLineGetFlag (CheckPackage, L"-yes"); + Quiet = !ShellCommandLineGetFlag (CheckPackage, L"-verbose"); + ForceFat16 = ShellCommandLineGetFlag (CheckPackage, L"-fat16"); + + Freq = GetPerformanceCounterProperties (NULL, NULL); + + // Check flags provided by user + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"clear") << 0); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"create") << 1); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"list") << 2); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"sync") << 3); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"typesinfo") << 4); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"delete") << 5); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"info") << 6); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"read") << 7); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"readfile") << 8); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"rename") << 9); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"setattr") << 10); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"settype") << 11); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"write") << 12); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"writefile") << 13); + Flag |= (ShellCommandLineGetFlag (CheckPackage, L"fatformat") << 14); + + PartitionGetKnownType ((UINTN)(-1), &NumKnownPartTypesEntries); + + if (Flag & TYPES_INFO) { + UINTN Index; + + Print (L" No\t%-36s\tGUID\r\n", L"Type name"); + Print (L" ---- ---------------------------------- ------------------------------------\n"); + for (Index = 0; Index < NumKnownPartTypesEntries; Index++) { + EFI_KNOWN_PARTITION_TYPE *PartType; + + PartType = PartitionGetKnownType (Index, NULL); + if (PartType == NULL) { + break; + } + Print (L" %3d\t%-36s\t%g\n", Index, PartType->TypeName, &PartType->TypeGuid); + } + if (Flag == TYPES_INFO) { + return SHELL_SUCCESS; + } + } + + // Start parsing the command. + BlockName = ShellCommandLineGetRawValue (CheckPackage, 1); + if (BlockName == NULL) { + if (Flag & LIST) { + Status = FindAndPrintPartitionableDevices (); + if (EFI_ERROR (Status)) { + PrintErr (L"Error getting list of partitionable devices", Status); + goto CleanUp; + } + return Status; + } + PrintErr (L"Missing block device name", EFI_INVALID_PARAMETER); + return SHELL_INVALID_PARAMETER; + } + + // Find device handle by mapped name + if (gEfiShellProtocol->GetDevicePathFromMap (BlockName) == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellGptHiiHandle, gAppName, BlockName); + Status = SHELL_INVALID_PARAMETER; + } else { + DevPath = (EFI_DEVICE_PATH_PROTOCOL *)gEfiShellProtocol->GetDevicePathFromMap (BlockName); + if (gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &DevPath, NULL) == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_MAP_PROTOCOL), gShellGptHiiHandle, gAppName, BlockName, L"BlockIo"); + Status = SHELL_INVALID_PARAMETER; + } + } + + if (Status) { + return SHELL_INVALID_PARAMETER; + } + + BlockIoHandle = 0; + + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, (EFI_DEVICE_PATH_PROTOCOL **)&DevPath, &BlockIoHandle); + if (EFI_ERROR (Status)) { + goto CleanUp; + } + + Status = gBS->OpenProtocol (BlockIoHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_MAP_PROTOCOL), gShellGptHiiHandle, gAppName, BlockName, L"BlockIo"); + goto CleanUp; + } + + Status = gBS->HandleProtocol ( + BlockIoHandle, + &gEfiDiskIoProtocolGuid, + (VOID **)&DiskIo + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_MAP_PROTOCOL), gShellGptHiiHandle, gAppName, BlockName, L"DiskIo"); + goto CleanUp; + } + + CheckFlag = Flag; + for (I = 0; CheckFlag; CheckFlag >>= 1) { + I += CheckFlag & 1; + if (I > 1) { + PrintErr (L"Too many flags", EFI_INVALID_PARAMETER); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + } + + if (Flag & SYNC) { + // Let the Partition table driver know that + // we want to reread the tables + Status = gBS->ReinstallProtocolInterface ( + BlockIoHandle, + &gEfiBlockIoProtocolGuid, + BlockIo, + BlockIo + ); + Status = SHELL_SUCCESS; + goto CleanUp; + } + + if (!IsPartitionableDevicePath (DevicePathFromHandle (BlockIoHandle))) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellGptHiiHandle, gAppName, BlockName); + Print (L"%s is not a raw block device\n", BlockName); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + + if (!BlockIo->Media->MediaPresent) { + PrintErr (L"Media is not present!\n", EFI_NO_MEDIA); + Status = EFI_NO_MEDIA; + goto CleanUp; + } + + // Preload GPT tables with validation + Status = PartitionGetGptTables (DiskIo, BlockIo); + if (EFI_ERROR (Status)) { + BOOLEAN CanContinue; + + CanContinue = (Flag & CREATE) || (Flag & CLEAR) || (Flag & FAT_FORMAT); + if (Status != EFI_NOT_FOUND) { + PrintErr (L"Unexpected error getting GPT tables", Status); + goto CleanUp; + } else { + if (!CanContinue) { + PrintErr (L"No GPT table found. Create first", Status); + goto CleanUp; + } + } + } else { + GptIsValid = TRUE; + } + + // Do we have any partitions already? + TableNotEmpty = (PartitionFindPartitionByCriteria (NULL, 0, 0, SRC_ANY) != NULL); + + Status = SHELL_INVALID_PARAMETER; + + if ((Flag < LIST) || + (Flag & DELETE) || + (Flag > READ_FILE) + ) { + if (BlockIo->Media->ReadOnly) { + PrintErr (L"Cannot write to a read-only device", EFI_INVALID_PARAMETER); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + } + + if (BlockIo->Media->RemovableMedia) { + Print (L"%s is a removable device. Just a note\n", BlockName); + } + + switch (Flag) { + case INFO: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + OffsetFlag = FALSE; + LengthFlag = FALSE; + break; + case LIST: + { + UINTN NumEntries; + + NumEntries = PartitionListGptEntries (); + if (NumEntries == 0) { + Print (L"GPT is valid on %s, but no partition(s) defined yet\r\nUse create command to create a GPT partition\n", BlockName); + } + Status = SHELL_SUCCESS; + goto CleanUp; + break; + } + case CLEAR: + if (TableNotEmpty) { + // Tell the user what he/she is doing... + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_NOT_EMPTY), + gShellGptHiiHandle, + BlockName + ); + } + + // Even if GPT tables do not exist, there might be something. + // Warn the user and double sure it is the intention, + // to prevent a user from bricking a device (JTAG would be needed to recover) + // by overwriting an ATF boot device. However with NoPrompt on, the user is + // responsible for operation because there is no confirmation (assuming yes on all queries). + if ((NoPrompt) || (!GptPromptYesNo (STRING_TOKEN (STR_GPT_CLEAR_SURE)) && + (!GptPromptYesNo (STRING_TOKEN (STR_GPT_ABSOLUTELY_SURE)))) + ) { + PartitionGptClearAll (); + } + Status = SHELL_SUCCESS; + goto CleanUp; + break; + case CREATE: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 3); + LengthStr = ShellCommandLineGetRawValue (CheckPackage, 4); + AttrStr = ShellCommandLineGetRawValue (CheckPackage, 5); + GuidStr = ShellCommandLineGetValue (CheckPackage, L"-type"); + GuidFlag = TRUE; + break; + case DELETE: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + LengthFlag = FALSE; + OffsetFlag = FALSE; + break; + case RENAME: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + NewPartName = ShellCommandLineGetRawValue (CheckPackage, 3); + NewPartNameFlag = TRUE; + LengthFlag = FALSE; + OffsetFlag = FALSE; + break; + case SETATTR: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + AttrStr = ShellCommandLineGetRawValue (CheckPackage, 3); + AttrFlag = TRUE; + LengthFlag = FALSE; + OffsetFlag = FALSE; + break; + case SETTYPE: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + GuidStr = ShellCommandLineGetValue (CheckPackage, L"-type"); + GuidFlag = TRUE; + LengthFlag = FALSE; + OffsetFlag = FALSE; + break; + case FAT_FORMAT: + PartName = ShellCommandLineGetRawValue (CheckPackage, 2); + VolumeName = ShellCommandLineGetRawValue (CheckPackage, 3); + LengthFlag = FALSE; + OffsetFlag = FALSE; + PartNameFlag = (PartName != NULL); + if (!PartNameFlag && TableNotEmpty) { + // Tell the user what he/she is doing... + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_NOT_EMPTY), + gShellGptHiiHandle, + BlockName + ); + } + break; + // Fall through + case READ: + case WRITE: + AddressStr = ShellCommandLineGetRawValue (CheckPackage, 2); + PartName = ShellCommandLineGetRawValue (CheckPackage, 3); + OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 4); + LengthStr = ShellCommandLineGetRawValue (CheckPackage, 5); + AddrFlag = TRUE; + break; + case READ_FILE: + FileStr = ShellCommandLineGetRawValue (CheckPackage, 2); + PartName = ShellCommandLineGetRawValue (CheckPackage, 3); + OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 4); + LengthStr = ShellCommandLineGetRawValue (CheckPackage, 5); + FileFlag = TRUE; + break; + case WRITE_FILE: + FileStr = ShellCommandLineGetRawValue (CheckPackage, 2); + PartName = ShellCommandLineGetRawValue (CheckPackage, 3); + OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 4); + LengthFlag = FALSE; + FileFlag = TRUE; + break; + default: + PrintErr (L"Unsupported or missing command. Try \"help gpt\"", EFI_INVALID_PARAMETER); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + + // Read address parameter + if ((AddressStr == NULL) & AddrFlag) { + PrintErr (L"No address parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (AddrFlag) { + Address = ShellHexStrToUintn (AddressStr); + if (Address == (UINTN)(-1)) { + PrintErr (L"Wrong address parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } + } + + if ((PartName == NULL) & PartNameFlag) { + PrintErr (L"Missing partition name", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (PartNameFlag) { + if (StrSize (PartName) > MAX_PARTITION_NAME_LENGTH) { + PrintErr (L"Partition name is too long (max 36 chars)", EFI_INVALID_PARAMETER); + goto CleanUp; + } + } + + // Read offset parameter + if ((OffsetStr == NULL) & OffsetFlag) { + PrintErr (L"No offset Parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (OffsetFlag) { + Offset = ShellHexStrToUintn (OffsetStr); + if (Offset < 0) { + Print (L"%s: Wrong offset parameter: %s\n", gAppName, OffsetStr); + goto CleanUp; + } + } + + // Read length parameter + if ((LengthStr == NULL) & LengthFlag) { + PrintErr (L"No length parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (LengthFlag) { + ByteCount = (UINT64)ShellStrToUintn (LengthStr); + if (ByteCount < 0) { + Print (L"%s: Wrong length parameter %s!\n", gAppName, LengthStr); + goto CleanUp; + } + } + + if ((NewPartName == NULL) & NewPartNameFlag) { + PrintErr (L"Missing name to be assigned to partition", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (NewPartNameFlag) { + if (StrSize (NewPartName) > MAX_PARTITION_NAME_LENGTH) { + PrintErr (L"Partition name is too long (max 36 chars)", EFI_INVALID_PARAMETER); + goto CleanUp; + } + } + + if ((AttrStr == NULL) & AttrFlag) { + PrintErr (L"Missing attributes parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (AttrStr) { + PartAttributes = (UINT64)ShellStrToUintn (AttrStr); + } + + if ((GuidStr == NULL) & GuidFlag) { + PrintErr (L"Missing partition type GUID parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } else if (GuidFlag) { + Status = SHELL_INVALID_PARAMETER; + if (InternalShellIsHexOrDecimalNumber (GuidStr, FALSE, TRUE, FALSE)) { + UINTN Ordinal; + + Ordinal = ShellStrToUintn (GuidStr); + if (Ordinal < NumKnownPartTypesEntries) { + PartTypeGuid = PartitionGetKnownType (Ordinal, NULL)->TypeGuid; + Status = SHELL_SUCCESS; + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellGptHiiHandle, gAppName, GuidStr); + goto CleanUp; + } + } else { + Status = ConvertStringToGuid (GuidStr, &PartTypeGuid); + if ((EFI_ERROR (Status)) || (IsZeroGuid (&PartTypeGuid))) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellGptHiiHandle, gAppName, GuidStr); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + } + } + + if (FileFlag) { + // Read FilePath parameter + if (FileStr == NULL) { + PrintErr (L"No FilePath parameter", EFI_INVALID_PARAMETER); + goto CleanUp; + } else { + FilePath = (CHAR16 *)FileStr; + Status = ShellIsFile (FilePath); + // When read file into flash, file doesn't have to exist + if (EFI_ERROR (Status && !(Flag & READ_FILE))) { + PrintErr (L"Wrong FilePath parameter", Status); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + } + + Status = OpenAndPrepareFile (FilePath, &FileHandle, ((Flag & READ_FILE) != 0)); + if (EFI_ERROR (Status)) { + Print (L"Error %r while preparing file %s", Status, FilePath); + goto CleanUp; + } + + // Get file size in order to check correctness at the end of transfer + if (Flag & (WRITE_FILE)) { + Status = FileHandleGetSize (FileHandle, &FileSize); + if (EFI_ERROR (Status)) { + PrintErr (L"Cannot get file size", Status); + goto CleanUp; + } + ByteCount = (UINT64)FileSize; + } + + FileBuffer = AllocateZeroPool ((UINTN)ByteCount); + if (FileBuffer == NULL) { + PrintErr (L"Cannot allocate memory", EFI_OUT_OF_RESOURCES); + Status = SHELL_OUT_OF_RESOURCES; + goto Error_Close_File; + } + + // Read file content and store it in FileBuffer + if (Flag & (WRITE_FILE)) { + if (!Quiet) { + Print (L"Reading %s...\r", FilePath); + } + Status = FileHandleRead (FileHandle, &ByteCount, FileBuffer); + if (EFI_ERROR (Status)) { + PrintErr (L"Read from file error", Status); + goto Error_Free_Buffer; + } else if (ByteCount != (UINTN)FileSize) { + PrintErr (L"Not whole file read. Abort", EFI_DEVICE_ERROR); + Status = SHELL_DEVICE_ERROR; + goto Error_Free_Buffer; + } + if (!Quiet) { + Print (L"Writing %s into device %s, partition %s...\n", FilePath, BlockName, PartName); + } + } + } + + Buffer = (UINT8 *)Address; + if (FileFlag) { + Buffer = FileBuffer; + } + + if (Flag > TYPES_INFO) { + Entry = PartitionFindPartitionByCriteria (PartName, 0, 0, SRC_BY_NAME); + if (!Entry && PartName && (InternalShellIsHexOrDecimalNumber (PartName, FALSE, TRUE, FALSE))) { + Entry = PartitionFindPartitionByCriteria (PartName, 0, 0, SRC_BY_NUM); + } + if ((!Entry) && (PartNameFlag)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellGptHiiHandle, gAppName, PartName); + Print (L"Could not find partition %s (case-sensitive). Make sure the name is spelled properly\n", PartName); + Status = SHELL_NOT_FOUND; + goto CleanUp; + } + } + + switch (Flag) { + case CREATE: + if (!TableNotEmpty && !GptIsValid) { + if (!NoPrompt) { + // Ask a user to confirm re-init of the GPT if + // It was not present before create was invoked + if ((GptPromptYesNo (STRING_TOKEN (STR_GPT_CLEAR_SURE))) || + (GptPromptYesNo (STRING_TOKEN (STR_GPT_ABSOLUTELY_SURE)))) { + goto CleanUp; + } + } + } + + Status = PartitionGptCreatePartition ( + PartName, + Offset, // Lba. If 0, the next available is assumed + ByteCount, // in MegaBytes. If 0 the whole remaining space is assumed + PartAttributes, + PartTypeGuid); + break; + case DELETE: + ModParams.PartName = PartName; + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_DELETE_WARNING), + gShellGptHiiHandle, + Entry->PartitionName + ); + if ((NoPrompt) || (!GptPromptYesNo (STRING_TOKEN (STR_GPT_ABSOLUTELY_SURE)))) { + Status = PartitionGptModifyPartition ( + Entry, &ModParams, MOD_DELETE); + if (EFI_ERROR (Status)) { + PrintErr (L"Error deleting the partition", Status); + goto CleanUp; + } + Status = SHELL_SUCCESS; + } else { + Status = SHELL_ABORTED; + } + break; + case FAT_FORMAT: + { + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + CHAR8 LabelName[12]; + CHAR16 *FormatType; + + if (VolumeName) { + if (StrLen (VolumeName) > FAT_LABEL_LENGTH) { + Status = EFI_INVALID_PARAMETER; + PrintErr (L"The volume label is too long", Status); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + UnicodeStrToAsciiStr (VolumeName, (CHAR8 *)LabelName); + } + ShellPrintHiiEx (-1, -1, + NULL, STRING_TOKEN (STR_GPT_FORMAT_WARNING), + gShellGptHiiHandle, + Entry ? Entry->PartitionName : BlockName + ); + + if ((NoPrompt) || (!GptPromptYesNo (STRING_TOKEN (STR_GPT_FORMAT_SURE)) && + (!GptPromptYesNo (STRING_TOKEN (STR_GPT_ABSOLUTELY_SURE)))) + ) { + StartingLBA = 0; + EndingLBA = BlockIo->Media->LastBlock; + if (Entry) { + StartingLBA = Entry->StartingLBA; + EndingLBA = Entry->EndingLBA; + } + FormatType = FAT32_FORMAT; + if (ForceFat16) { + UINT64 VolumeSectors; + + FormatType = FAT16_FORMAT; + VolumeSectors = DivU64x32 ( + MultU64x32 ( + EndingLBA - StartingLBA + 1, BlockIo->Media->BlockSize), + FAT_SECTOR_SIZE); + if (VolumeSectors > FAT16_MAX_SECTORS) { + FormatType = FAT32_FORMAT; + Print (L"Note: this volume will be formatted as FAT32 (too many sectors for FAT16)\n"); + } + } + if (!Quiet) { + Print (L"Formatting %s to %s...\r", PartNameFlag ? Entry->PartitionName : BlockName, FormatType); + } + Status = FatFormat (StartingLBA, EndingLBA, BlockIo, DiskIo, VolumeName ? LabelName : NULL, !ForceFat16); + if (EFI_ERROR (Status)) { + PrintErr (L"Error formatting the partition ", Status); + goto CleanUp; + } else if (!Quiet) { + Print (L"%s successfully formatted to %s. Formatted size %llu MiB\n", + PartNameFlag ? Entry->PartitionName : BlockName, + FormatType, + MultU64x32 (EndingLBA - StartingLBA + 1, BlockIo->Media->BlockSize) >> 20 + ); + } + } else { + Status = SHELL_ABORTED; + } + } + break; + case RENAME: + ModParams.PartName = PartName; + ModParams.NewName = NewPartName; + Status = PartitionGptModifyPartition ( + Entry, &ModParams, MOD_RENAME); + break; + case SETATTR: + ModParams.Attributes = PartAttributes; + Status = PartitionGptModifyPartition ( + Entry, &ModParams, MOD_ATTR); + break; + case SETTYPE: + ModParams.PartTypeGuid = PartTypeGuid; + Status = PartitionGptModifyPartition ( + Entry, &ModParams, MOD_TYPE); + break; + case INFO: + PartitionPrintGptPartInfo (Entry); + Status = SHELL_SUCCESS; + break; + case READ: + case READ_FILE: + case WRITE: + case WRITE_FILE: + { + UINT64 MaxBytes; + BOOLEAN OpRead; + + OpRead = ((Flag & READ) || (Flag & READ_FILE)); + MaxBytes = MultU64x32 ( + Entry->EndingLBA - Entry->StartingLBA, + BlockIo->Media->BlockSize) + + BlockIo->Media->BlockSize - + MultU64x32 (Offset, BlockIo->Media->BlockSize); + if (ByteCount > MaxBytes) { + Status = EFI_INVALID_PARAMETER; + ShellPrintHiiEx (-1, -1, + NULL, (OpRead) ? + STRING_TOKEN (STR_GPT_READ_BOUNDARY) : + STRING_TOKEN (STR_GPT_WRITE_BOUNDARY), + gShellGptHiiHandle, + gAppName, + Entry->PartitionName, + MaxBytes, + ByteCount + ); + Status = SHELL_INVALID_PARAMETER; + goto CleanUp; + } + + TimeStampB = GetPerformanceCounter (); + if (OpRead) { + Status = DiskIo->ReadDisk (DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (Offset + Entry->StartingLBA, BlockIo->Media->BlockSize), ByteCount, Buffer); + } else { + Status = DiskIo->WriteDisk (DiskIo, BlockIo->Media->MediaId, + MultU64x32 (Offset + Entry->StartingLBA, BlockIo->Media->BlockSize), ByteCount, Buffer); + } + } + break; + default: + Status = SHELL_INVALID_PARAMETER; + PrintErr (L"Unknown command. Try \"help gpt\"", EFI_INVALID_PARAMETER); + goto CleanUp; + } + + if (EFI_ERROR (Status)) { + PrintErr (L"Error while performing transfer\n", Status); + goto CleanUp; + } + + TimeStampE = ((GetPerformanceCounter () - TimeStampB) * 1000) / Freq; + SpeedKB = TimeStampE ? (ByteCount / (TimeStampE / 1000)) >> 10 : 0; + + switch (Flag) { + case WRITE: + case WRITE_FILE: + if (!Quiet) { + ShellPrintHiiEx (-1, -1, + NULL, + STRING_TOKEN (STR_GPT_WRITE_OK), + gShellGptHiiHandle, + ByteCount, + Offset, + Entry->PartitionName, + TimeStampE, + SpeedKB + ); + } + break; + case READ: + if (!Quiet) { + ShellPrintHiiEx (-1, -1, + NULL, + STRING_TOKEN (STR_GPT_READ_OK), + gShellGptHiiHandle, + ByteCount, + Offset, + Entry->PartitionName, + TimeStampE, + SpeedKB + ); + } + break; + case READ_FILE: + Status = FileHandleWrite (FileHandle, &ByteCount, FileBuffer); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, + NULL, + STRING_TOKEN (STR_GPT_FILE_WRITE_FAIL), + gShellGptHiiHandle, + gAppName, + FilePath, + Status + ); + Status = SHELL_DEVICE_ERROR; + goto Error_Free_Buffer; + } + + if (!Quiet) { + ShellPrintHiiEx (-1, -1, + NULL, + STRING_TOKEN (STR_GPT_READFILE_OK), + gShellGptHiiHandle, + ByteCount, + Offset, + Entry->PartitionName, + FilePath, + TimeStampE, + SpeedKB + ); + } + break; + } + + if (FileFlag) { + SHELL_FREE_NON_NULL (FileBuffer); + if (FileHandle != NULL) { + ShellCloseFile (&FileHandle); + FileHandle = NULL; + } + } + + Status = SHELL_SUCCESS; + + Error_Free_Buffer: + SHELL_FREE_NON_NULL (FileBuffer); + Error_Close_File: + if (FileHandle) { + ShellCloseFile (&FileHandle); + } + CleanUp: + if (BlockIoHandle) { + // By UEFI Spec blocks must be flushed + BlockIo->FlushBlocks (BlockIo); + gBS->CloseProtocol (BlockIoHandle, &gEfiBlockIoProtocolGuid, gImageHandle, NULL); + } + + GptCleanupGlobals (); + + ShellCommandLineFreeVarList (CheckPackage); + + if (EFI_ERROR (Status)) { + Status = SHELL_ABORTED; + } + + return Status; +} + +EFI_STATUS +EFIAPI +ShellGptLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + gShellGptHiiHandle = NULL; + + gShellGptHiiHandle = HiiAddPackages ( + &gShellGptHiiGuid, gImageHandle, + UefiShellGptCommandLibStrings, NULL + ); + if (gShellGptHiiHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + ShellCommandRegisterCommandName ( + gAppName, ShellCommandRunGpt, ShellCommandGetManFileNameGpt, 0, + gAppName, TRUE, gShellGptHiiHandle, STRING_TOKEN (STR_GET_HELP_GPT) + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +ShellGptLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + + if (gShellGptHiiHandle != NULL) { + HiiRemovePackages (gShellGptHiiHandle); + } + return EFI_SUCCESS; +} diff --git a/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.inf b/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.inf new file mode 100644 index 000000000000..9b3a6f56c9b7 --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.inf @@ -0,0 +1,74 @@ +# +# Copyright (C) 2016, Marvell International Ltd. All rights reserved. +# Copyright (C) 2016, Broadcom. All rights reserved +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Marvell nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = UefiShellGptCommandLib + FILE_GUID = F62ACF25-0D15-22F5-E642-FFB6515E00D7 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 0.1 + LIBRARY_CLASS = NULL|UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = ShellGptLibConstructor + DESTRUCTOR = ShellGptLibDestructor + +[Sources] + FatFormat.c + GptWorker.c + UefiShellGptCommandLib.c + UefiShellGptCommandLib.uni + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + FileHandleLib + HiiLib + MemoryAllocationLib + PcdLib + ShellCommandLib + ShellLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiLib + +[Protocols] + gEfiBlockIoProtocolGuid + gEfiDevicePathProtocolGuid + gEfiDiskIoProtocolGuid + +[Guids] + gShellGptHiiGuid + gEfiPartTypeUnusedGuid ## SOMETIMES_CONSUMES ## GUID diff --git a/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.uni b/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.uni new file mode 100644 index 000000000000..c0ad20a7f7cd --- /dev/null +++ b/ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.uni @@ -0,0 +1,113 @@ +/******************************************************************************* +Copyright (C) 2016 Marvell International Ltd. All rights reserved +Copyright (C) 2016, Broadcom. All rights reserved */ + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +/=# + +#langdef en-US "english" + +#string STR_GEN_PARAM_INV #language en-US "%H%s%N: Invalid argument - '%H%s%N'\r\n" +#string STR_GEN_MAP_PROTOCOL #language en-US "%H%s%N: Mapped device '%B%s%N' does not have protocol %B%s%N\r\n" + +#string STR_GPT_ERROR #language en-US "%H%s%N: %r - %s\r\n" +#string STR_GPT_NOT_EMPTY #language en-US "%H%s%N: WARNING!!! This device has valid GPT partition tables!\r\n" +#string STR_GPT_CLEAR_SURE #language en-US "Are you sure you want to (re)inintialize GPT tables on this device? %BY%Nes, %BN%No " +#string STR_GPT_ABSOLUTELY_SURE #language en-US "\r\nAre you *** ABSOLUTELY SURE *** you want to perform this operation ? %BY%Nes, %BN%No " +#string STR_GPT_FORMAT_WARNING #language en-US "%H%s%N: WARNING!!! Formatting of this partition will destroy all data on it!\r\n" +#string STR_GPT_FORMAT_SURE #language en-US "Are you sure you want to format this partition and destroy all data on it ? %BY%Nes, %BN%No " +#string STR_GPT_DELETE_WARNING #language en-US "%H%s%N: Deleting of this partition will make data on it unreachable!\r\n" +#string STR_GPT_READ_BOUNDARY #language en-US "%H%s%N: Attempt to read beyond %H%s%N partition boundary (can read upto %llu bytes from the given offset, requested %llu bytes)\r\n" +#string STR_GPT_WRITE_BOUNDARY #language en-US "%H%s%N: Attempt to write beyond %H%s%N partition boundary (can write upto %llu bytes from the given offset, requested %llu bytes)\r\n" +#string STR_GPT_FILE_WRITE_FAIL #language en-US "%H%s%N: Failed to write to the file %s, error %r\r\n" + +#string STR_GPT_WRITE_OK #language en-US "Written %llu bytes at offset 0x%x, partition %s. Elapsed time %llums (%llu KB/s)\r\n" +#string STR_GPT_READ_OK #language en-US "Read %llu bytes from offset 0x%x, partition %s. Elapsed time %llums (%llu KB/s)\r\n" +#string STR_GPT_READFILE_OK #language en-US "Read %llu bytes from offset 0x%x, partition %s into file %s. Elapsed time %llums (%llu KB/s)\r\n" +#string STR_GPT_LIST_DEVS #language en-US " %H%+6s%N %+8s " + +#string STR_GET_HELP_GPT #language en-US "" +".TH gpt 0 "GPT partition manager."\r\n" +".SH NAME\r\n" +"Manages GPT partitions on a block device.\r\n" +".SH SYNOPSIS\r\n" +" \r\n" +"gpt [read | readfile | write | writefile | list | info | clear |\r\n" +" create | delete | rename | setattrs | sync | fatformat | -typesinfo | -yes] \r\n" +"This is a complex utility. Please see examples for usage info\r\n" +".SH OPTIONS\r\n" +" \r\n" +" Device - Block device to be used for the operation\r\n" +" Length - Number of bytes to transfer (for read/write))\r\n" +" Address - Address in RAM to store/load data\r\n" +" Offset - Offset (in blocks) from beggining of the specifie partition to store/load data\r\n" +" FilePath - Path to file to read data into or write/update data from\r\n" +" -yes - Assume yes for all queries, do not prompt\r\n +" -verbose - Print additional information on operation\r\n" +" -fat16 - (For format only, has no effect for anything else) - Format to FAT16 if possible\r\n\r\n" +".SH EXAMPLES\r\n" +" \r\n" +"EXAMPLES:\r\n" +"Get info on the particular partition with name PartitionName on the block device blk0:\r\n" +" gpt %Hinfo%N blk0: PartitionName\r\n" +"List all available GPT partitions on the block device blk0:\r\n" +" gpt %Hlist%N blk0:\r\n" +"Note: the ordinal number shown by this command can be used as a partition name in any command requiring partition name\r\n" +"Thus gpt info blk0: 1 is valid if there is a partition with ordinal 1 present in the output of gpt list command\r\n" +"Get information on all recognized partition types\r\n" +" gpt %Htypesinfo%N\r\n" +"Clear partitions information and install empty GPT tables for a block device blk0:\r\n" +" gpt %Hclear%N blk0:\r\n" +"Create a GPT partition with name PartitionName and type EFI SYSTEM in the GPT table, using the next available LBA, with size\r\n" +"64MiB, with system attribute, on block device blk0:\r\n" +" gpt %Hcreate%N blk0: PartitionName 0 64 1 -type 0\r\n" +"Same as above, but now the partition type is not known to the gpt utility, so use some GUID known to a 3rd party\r\n" +" gpt create blk0: PartitionName 0 64 1 -type 44581A4A-C834-D1A6-2602-9D522A8F2307\r\n" +"Rename the GPT partition PartitionName on blk0: to NewPartitionName\r\n" +" gpt %Hrename%N blk0: PartitionName NewPartitionName\r\n" +"Have the PartitionDxe driver to re-read GPT tables on a block device blk0:(after they were updated with gpt utility)\r\n" +" gpt %Hsync%N blk0:\r\n" +"Read 4K from block offset 0x0e000 in Partition named PartitionName of the block device at blk0: into RAM at address 0x100000\r\n" +" gpt %Hread%N blk0: 0x100000 PartitionName 0xe000 4096\r\n" +"Write 512 bytes from 0x200000 at RAM into the block device at blk0: partition PartitionName at offset 0x1000\r\n" +" gpt %Hwrite%N blk0: 0x200000 PartitionName 0x1000 512\r\n" +"Read 0x3000 bytes from 0x0 offset of Partition PartitionName at the block device blk0: into file fs2:file.bin\r\n" +" gpt %Hreadfile%N blk0: fs2:file.bin PartitionName 0x0 0x3000\r\n" +"Write contents of file fs2:file.bin into partition named PartitionName with offset (in lba) 0x10 on a block device blk0:\r\n" +" gpt %Hwritefile%N blk0: fs2:file.bin PartitionName 0x10\r\n" +"FAT Format the partition PartitionName on block device blk0:\r\n" +" gpt %Hfatformat%N blk0: PartitionName\r\n" +"FAT Format the whole device blk0:\r\n" +" gpt fatformat blk0:\r\n" + +".SH RETURNVALUES\r\n" +" \r\n" +"RETURN VALUES:\r\n" +" SHELL_SUCCESS The action was completed as requested.\r\n" +" Specific Shell error Error while processing command\r\n" diff --git a/ShellPkg/ShellPkg.dec b/ShellPkg/ShellPkg.dec index bb31c2df8cb3..5f73379f9eec 100644 --- a/ShellPkg/ShellPkg.dec +++ b/ShellPkg/ShellPkg.dec @@ -56,6 +56,7 @@ [Guids] gShellNetwork2HiiGuid = {0x174b2b5, 0xf505, 0x4b12, {0xaa, 0x60, 0x59, 0xdf, 0xf8, 0xd6, 0xea, 0x37}} gShellTftpHiiGuid = {0x738a9314, 0x82c1, 0x4592, {0x8f, 0xf7, 0xc1, 0xbd, 0xf1, 0xb2, 0x0e, 0xd4}} gShellBcfgHiiGuid = {0x5f5f605d, 0x1583, 0x4a2d, {0xa6, 0xb2, 0xeb, 0x12, 0xda, 0xb4, 0xa2, 0xb6}} + gShellGptHiiGuid = {0x5a1ed739, 0x5ef1, 0x429a, {0x8d, 0xf8, 0x28, 0xc9, 0x92, 0x64, 0xd7, 0xf8}} [Protocols] gEfiShellEnvironment2Guid = {0x47c7b221, 0xc42a, 0x11d2, {0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}} diff --git a/ShellPkg/ShellPkg.dsc b/ShellPkg/ShellPkg.dsc index 6b7864bac395..b6a8072dedb5 100644 --- a/ShellPkg/ShellPkg.dsc +++ b/ShellPkg/ShellPkg.dsc @@ -91,6 +91,7 @@ [Components] ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf + ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.inf ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf @@ -121,6 +122,9 @@ [Components] !ifdef $(INCLUDE_TFTP_COMMAND) NULL|ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.inf !endif #$(INCLUDE_TFTP_COMMAND) +!ifdef $(INCLUDE_GPT_COMMAND) + NULL|ShellPkg/Library/UefiShellGptCommandLib/UefiShellGptCommandLib.inf +!endif #$(INCLUDE_GPT_COMMAND) !endif #$(NO_SHELL_PROFILES) } -- 1.9.1