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

714 lines
19 KiB
C

/*
Gpt - Guid Partition Table routines
*/
#include "diskpart.h"
BOOLEAN Debug = TRUE;
EFI_STATUS WriteShadowMBR(EFI_HANDLE DiskHandle);
EFI_STATUS
ReadGPT(
EFI_HANDLE DiskHandle,
PGPT_HEADER *Header,
PGPT_TABLE *Table,
PLBA_BLOCK *LbaBlock,
UINTN *DiskType
)
/*
*Header, *Table, *LbaBlock will either be NULL or have a pointer.
If they have pointers, caller is expected to free them with DoFree();
RAW and MBR stuff is NOT DONE.
DISK_RAW - no known partition scheme on the disk
DISK_MBR - an MBR/Legacy disk
DISK_GPT - a GPT style disk
DISK_GPT_UPD - a GPT disk with inconsistent partition tables
that need to be fixed up (may also need MBR rewrite)
DISK_GPT_BAD - a GPT disk that is hopeless (or a hopeless disk
that we think is a GPT disk)
*/
{
#define MBR_STATE_RAW 0
#define MBR_STATE_MBR 1
#define MBR_STATE_GPT 2
UINTN MbrState = MBR_STATE_RAW;
UINT32 BlockSize;
UINT64 DiskSize;
VOID *p = NULL;
PGPT_HEADER h1 = NULL;
PGPT_HEADER h2 = NULL;
PGPT_TABLE t1 = NULL;
PGPT_TABLE t2 = NULL;
PLBA_BLOCK lba = NULL;
UINT32 h1crc;
UINT32 h2crc;
UINT32 newcrc;
UINT32 TableSize;
UINT32 TableBlocks;
BOOLEAN PartialGPT = FALSE;
MBR_ENTRY *MbrTable;
UINT16 *MbrSignature;
BOOLEAN H1T1good = TRUE;
BOOLEAN H2T2good = TRUE;
BlockSize = GetBlockSize(DiskHandle);
DiskSize = GetDiskSize(DiskHandle);
//
// Assure that DoFree will notice uninited returns...
//
*Header = NULL;
*Table = NULL;
*LbaBlock = NULL;
*DiskType = DISK_ERROR;
status = EFI_SUCCESS;
p = DoAllocate(BlockSize);
if (p == NULL) goto ErrorMem;
//
// Read the MBR, if we can't read that, assume
// we're in deep trouble (MBR is always block 0, 1 block long)
//
status = ReadBlock(DiskHandle, p, (UINT64)0, BlockSize);
if (EFI_ERROR(status)) goto ErrorRead;
MbrTable = (MBR_ENTRY *)((CHAR8 *)p + MBR_TABLE_OFFSET);
MbrSignature = (UINT16 *)((CHAR8 *)p + MBR_SIGNATURE_OFFSET);
if (*MbrSignature == MBR_SIGNATURE) { // 0xaa55
//
// There's an MBR signature, so assume NOT RAW
//
//
// If we find a type 0xEE in the first slot, we'll assume
// it's a GPT Shadow MBR. Otherwise we think it's an old MBR.
// But code below will account for GPT structures as well
//
if (MbrTable[0].PartitionType == PARTITION_TYPE_GPT_SHADOW) { // 0xEE
//
// Well, that type should never occur anywhere else,
// so assume it's a GPT Shadow regardless of how it's set
//
MbrState = MBR_STATE_GPT;
} else {
//
// It's not RAW (there's a signature) and it's not
// GPT Shadow MBR (no 0xEE for Table[0] type
// So, assume it's an MBR and we're done
//
*DiskType = DISK_MBR;
DoFree(p);
p = NULL;
return EFI_SUCCESS;
}
} else {
*DiskType = DISK_RAW; // if we don't find more...
}
//
// ----- h1/t1 ------------------------------------------------
//
//
// Read Header1. If cannot *read* it, punt.
// First header is always at Block 1, 1 block long
//
h1 = p;
p = NULL;
status = ReadBlock(DiskHandle, h1, 1, BlockSize);
if (EFI_ERROR(status)) goto ErrorRead;
//
// h1 => header1
//
if ( (h1->Signature != GPT_HEADER_SIGNATURE) ||
(h1->Revision != GPT_REVISION_1_0) ||
(h1->HeaderSize != sizeof(GPT_HEADER)) ||
(h1->SizeOfGPT_ENTRY != sizeof(GPT_ENTRY)) )
{
H1T1good = FALSE;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT header 1 is incorrect with status %x\n",
(h1->Signature != GPT_HEADER_SIGNATURE)*1 +
(h1->Revision != GPT_REVISION_1_0)*2 +
(h1->HeaderSize != sizeof(GPT_HEADER))*4 +
(h1->SizeOfGPT_ENTRY != sizeof(GPT_ENTRY))*8);
}
}
h1crc = h1->HeaderCRC32;
h1->HeaderCRC32 = 0;
newcrc = GetCRC32(h1, sizeof(GPT_HEADER));
h1->HeaderCRC32 = h1crc;
if (h1crc != newcrc) {
H1T1good = FALSE;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT header 1 crc is incorrect\n");
}
}
if (H1T1good) {
PartialGPT = TRUE;
}
//
// if header1 is bad, assume that table1 is bad too...
//
if (H1T1good) {
TableSize = sizeof(GPT_ENTRY) * h1->EntriesAllocated;
t1 = DoAllocate(TableSize);
if (t1 == NULL) goto ErrorMem;
//
// OK, so how many BLOCKS long is the table?
//
TableBlocks = TableSize / BlockSize;
//
// if we cannot READ t1, punt...
//
status = ReadBlock(DiskHandle, t1, h1->TableLBA, TableSize);
if (EFI_ERROR(status)) goto ErrorRead;
newcrc = GetCRC32(t1, TableSize);
if (h1->TableCRC32 != newcrc) {
H1T1good = FALSE;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT table 1 crc is incorrect\n");
}
}
}
//
// ----- h2/t2 ------------------------------------------------
//
//
// Read Header2. If cannot *read* it, punt.
//
h2 = DoAllocate(BlockSize);
if (h2 == NULL) goto ErrorMem;
//
// Header2 is always 1 block long, last block on disk
//
status = ReadBlock(DiskHandle, h2, DiskSize-1, BlockSize);
if (EFI_ERROR(status)) goto ErrorRead;
//
// h2 => header2
//
if ( (h2->Signature != GPT_HEADER_SIGNATURE) ||
(h2->Revision != GPT_REVISION_1_0) ||
(h2->HeaderSize != sizeof(GPT_HEADER)) ||
(h2->SizeOfGPT_ENTRY != sizeof(GPT_ENTRY)) )
{
H2T2good = FALSE;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT header 2 is incorrect with status %x\n",
(h2->Signature != GPT_HEADER_SIGNATURE)*1 +
(h2->Revision != GPT_REVISION_1_0)*2 +
(h2->HeaderSize != sizeof(GPT_HEADER))*4 +
(h2->SizeOfGPT_ENTRY != sizeof(GPT_ENTRY))*8);
}
}
h2crc = h2->HeaderCRC32;
h2->HeaderCRC32 = 0;
newcrc = GetCRC32(h2, sizeof(GPT_HEADER));
h2->HeaderCRC32 = h2crc;
if (h2crc != newcrc) {
H2T2good = FALSE;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT header 2 crc is incorrect\n");
}
}
if (H2T2good) {
PartialGPT = TRUE;
}
//
// if header2 is bad, assume that table2 is bad too...
//
if (H2T2good) {
TableSize = sizeof(GPT_ENTRY) * h2->EntriesAllocated;
t2 = DoAllocate(TableSize);
if (t2 == NULL) goto ErrorMem;
//
// OK, so how many BLOCKS long is the table?
//
TableBlocks = TableSize / BlockSize;
//
// if we cannot READ t2, punt...
//
status = ReadBlock(DiskHandle, t2, h2->TableLBA, TableSize);
if (EFI_ERROR(status)) goto ErrorRead;
newcrc = GetCRC32(t2, TableSize);
if (h2->TableCRC32 != newcrc) {
H2T2good = FALSE;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT table 2 crc is incorrect\n");
}
}
}
//
// ------ analysis --------------------------------------------------
//
// since we are here:
// h1 -> header1, t1 -> table1, H1T1good indicates state
// h2 -> header2, t2 -> table2, H2T2good indicates state
//
lba = (PLBA_BLOCK)DoAllocate(sizeof(LBA_BLOCK));
if (lba == NULL) goto ErrorMem;
lba->Header1_LBA = 1;
lba->Table1_LBA = h1->TableLBA;
lba->Header2_LBA = (DiskSize - 1);
lba->Table2_LBA = h2->TableLBA;
if (H1T1good) {
*Header = h1;
*Table = t1;
*LbaBlock = lba;
if ( (H2T2good) &&
(h1->AlternateLBA == (DiskSize-1)) &&
(CompareMem(t1, t2, TableSize) == 0)
)
{
*DiskType = DISK_GPT;
} else {
*DiskType = DISK_GPT_UPD;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT partition table 1 checked out but table 2 is inconsistent with table 1\n");
}
}
DoFree(h2);
h2 = NULL;
DoFree(t2);
t2 = NULL;
status = EFI_SUCCESS;
return status;
}
if (H2T2good) {
// since we're here, H1T1good is FALSE...
*Header = h2;
*Table = t2;
*LbaBlock = lba;
DoFree(h1);
h1 = NULL;
DoFree(t1);
t1 = NULL;
*DiskType = DISK_GPT_UPD;
if (DebugLevel >= DEBUG_ERRPRINT) {
Print(L"GPT partition table 2 checked out but table 1 is not good\n");
}
return EFI_SUCCESS;
}
//
// Since we're HERE, H1T1good AND H2T2good are BOTH false.
// Unless the shadow MBR says it's a GPT disk, claim it's raw.
// If we did see a shadow, or GPT partial is set, say it's a bad GPT
//
if ( (PartialGPT) || (MbrState == MBR_STATE_GPT) ) {
//
// At least one of the headers looked OK,
// OR
// There's an MBR that looks like a GPT shadow MBR
// SO
// Report DISK_GPT_BAD
//
*DiskType = DISK_GPT_BAD;
goto ExitRet;
} else {
//
// It's not an MBR disk, or we wouldn't have gotten here
//
*DiskType = DISK_RAW;
goto ExitRet;
}
ErrorMem:
status = EFI_OUT_OF_RESOURCES;
goto ExitRet;
ErrorRead:
status = EFI_DEVICE_ERROR;
ExitRet:
DoFree(p);
DoFree(h1);
DoFree(t1);
DoFree(h2);
DoFree(t2);
DoFree(lba);
return status;
}
EFI_STATUS
WriteGPT(
EFI_HANDLE DiskHandle,
PGPT_HEADER Header,
PGPT_TABLE Table,
PLBA_BLOCK LbaBlock
)
/*
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;
UINT32 TableBlocks;
status = EFI_SUCCESS;
BlockSize = GetBlockSize(DiskHandle);
TableSize = Header->EntriesAllocated * sizeof(GPT_ENTRY);
WriteShadowMBR(DiskHandle);
//
// Write out the primary header...
//
Header->Signature = GPT_HEADER_SIGNATURE;
Header->Revision = GPT_REVISION_1_0;
Header->HeaderSize = sizeof(GPT_HEADER);
Header->MyLBA = LbaBlock->Header1_LBA;
Header->AlternateLBA = LbaBlock->Header2_LBA;
Header->TableLBA = LbaBlock->Table1_LBA;
Header->SizeOfGPT_ENTRY = sizeof(GPT_ENTRY);
Header->TableCRC32 = GetCRC32(Table, TableSize);
Header->HeaderCRC32 = 0;
Header->HeaderCRC32 = GetCRC32(Header, sizeof(GPT_HEADER));
if (DebugLevel >= DEBUG_ARGPRINT) {
Print(L"\nWriteGPT\n DiskHandle = %8X\n", DiskHandle);
Print(L" Header=%X\n", Header);
Print(L" Signature=%lX\n Revision=%X HeaderSize=%X HeaderCRC=%X\n",
Header->Signature, Header->Revision, Header->HeaderSize, Header->HeaderCRC32);
Print(L" MyLBA=%lX AltLBA=%lX\n", Header->MyLBA, Header->AlternateLBA);
Print(L" FirstUsable=%lX Last=%lX\n", Header->FirstUsableLBA, Header->LastUsableLBA);
Print(L" TableLBA=%lX\n", Header->TableLBA);
}
status = WriteBlock(DiskHandle, Header, LbaBlock->Header1_LBA, BlockSize);
if (EFI_ERROR(status)) return status;
//
// Write out the primary table ...
//
TableBlocks = TableSize / BlockSize;
status = WriteBlock(DiskHandle, Table, LbaBlock->Table1_LBA, TableSize);
if (EFI_ERROR(status)) return status;
//
// Write out the secondary header ...
//
Header->MyLBA = LbaBlock->Header2_LBA;
Header->AlternateLBA = 0;
Header->TableLBA = LbaBlock->Table2_LBA;
Header->HeaderCRC32 = 0;
Header->HeaderCRC32 = GetCRC32(Header, sizeof(GPT_HEADER));
status = WriteBlock(DiskHandle, Header, LbaBlock->Header2_LBA, BlockSize);
if (EFI_ERROR(status)) return status;
//
// write out the secondary table ..
//
TableBlocks = TableSize / BlockSize;
status = WriteBlock(DiskHandle, Table, LbaBlock->Table2_LBA, TableSize);
FlushBlock(DiskHandle);
return status;
}
EFI_STATUS
WriteShadowMBR(
EFI_HANDLE DiskHandle
)
/*
WriteShadowMBR writes out a GPT shadow master boot record,
which means an MBR full of zeros except:
a. It has the 0xaa55 signature at 0x1fe
b. It has a single partition entry of type 'EE'...
*/
{
UINT32 BlockSize;
UINT8 *MbrBlock;
UINT64 DiskSize;
MBR_ENTRY UNALIGNED *MbrEntry = NULL;
UINT16 UNALIGNED *MbrSignature;
BlockSize = GetBlockSize(DiskHandle);
MbrBlock = DoAllocate(BlockSize);
if (MbrBlock == NULL) {
status = EFI_OUT_OF_RESOURCES;
return status;
}
ZeroMem(MbrBlock, BlockSize);
DiskSize = GetDiskSize(DiskHandle);
if (DiskSize > 0xffffffff) {
DiskSize = 0xffffffff;
}
MbrEntry = (MBR_ENTRY *)(MbrBlock + MBR_TABLE_OFFSET);
MbrEntry->ActiveFlag = 0;
MbrEntry->PartitionType = PARTITION_TYPE_GPT_SHADOW;
MbrEntry->StartingSector = 1;
MbrEntry->PartitionLength = (UINT32)DiskSize - MbrEntry->StartingSector;
//
// 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
//
MbrEntry->StartingTrack = 1;
MbrEntry->StartingCylinderLsb = 0;
MbrEntry->StartingCylinderMsb = 0;
MbrEntry->EndingTrack = 0xff;
MbrEntry->EndingCylinderLsb = 0xff;
MbrEntry->EndingCylinderMsb = 0xff;
MbrSignature = (UINT16 *)(MbrBlock + MBR_SIGNATURE_OFFSET);
*MbrSignature = BOOT_RECORD_SIGNATURE;
status = WriteBlock(DiskHandle, MbrBlock, 0, BlockSize);
DoFree(MbrBlock);
return status;
}
EFI_STATUS
CreateGPT(
EFI_HANDLE DiskHandle,
UINTN EntryRequest
)
/*
Write a new GPT table onto a clean disk
When we get here, we assume the disk is clean, and
that the user really wants to do this.
DiskHandle - the disk we are going to write to
EntryRequest - number of entries the user wants, ignored
if less than our minimum, rounded up to number of entries
that fill up the nearest sector. So, the user is
says "at least" this many.
Ignored if > 1024. (At least for now)
NOTE:
Even though the disk is in theory all zeros from
having been cleaned, we actually rewrite all the data,
just in case somebody fooled the detector code...
*/
{
UINTN EntryCount;
UINTN BlockFit;
UINTN BlockSize;
UINTN EntryBlocks;
UINT64 DiskSize;
LBA_BLOCK LbaBlock;
PGPT_HEADER Header;
PGPT_TABLE Table;
UINTN TableSize;
EFI_LBA Header1_LBA;
EFI_LBA Table1_LBA;
EFI_LBA Header2_LBA;
EFI_LBA Table2_LBA;
EFI_LBA FirstUsableLBA;
EFI_LBA LastUsableLBA;
//
// BlockSize is the block/sector size, in bytes.
// It is assumed to be a power of 2 and >= 512
//
BlockSize = GetBlockSize(DiskHandle);
//
// DiskSize is a Count (1 based, not 0 based) of
// software visible blocks on the disk. We assume
// we may address them as 0 to DiskSize-1.
//
DiskSize = GetDiskSize(DiskHandle);
//
// By default, we support the max of 32 entries or
// BlockSize/sizeof(GPT_ENTRY). (Meaning there will
// always be at least 32 entries and always be at least
// enough entries to fill 1 sector)
// If the user asks for more than that, but less than
// the sanity threshold, we give the user what they asked
// for, rounded up to BlockSize/sizeof(GPT_ENTRY)
//
EntryCount = ENTRY_DEFAULT;
BlockFit = BlockSize/sizeof(GPT_ENTRY);
if (BlockFit > ENTRY_DEFAULT) {
EntryCount = BlockFit;
}
if (EntryRequest > EntryCount) {
if (EntryRequest <= ENTRY_SANITY_LIMIT) { // 1024
EntryCount = ((EntryRequest + BlockFit) / BlockFit) * BlockFit;
if ((EntryCount < EntryRequest) ||
(EntryCount < ENTRY_DEFAULT) ||
(EntryCount < BlockFit))
{
TerribleError(L"CreateGPT is terribly confused\n");
}
}
}
EntryBlocks = EntryCount / BlockFit;
if ((EntryBlocks * BlockFit) != EntryCount) {
TerribleError(L"CreateGPT is terribly confused, spot #2\n");
}
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(GPT_ENTRY))) {
TerribleError(L"CreateGPT is terribly confused, spot #3\n");
}
if (DebugLevel >= DEBUG_ARGPRINT) {
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);
}
//
// OK, from this point it's just filling in structures
// and writing them out.
//
Header = (PGPT_HEADER)DoAllocate(BlockSize);
if (Header == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem(Header, BlockSize);
//
// Since we're making empty tables, we just write zeros...
//
Table = (PGPT_TABLE)DoAllocate(TableSize);
if (Table == NULL) {
DoFree(Header);
Header = NULL;
return EFI_OUT_OF_RESOURCES;
}
ZeroMem(Table, TableSize);
//
// Fill in the things that WriteGPT doesn't understand
//
Header->FirstUsableLBA = FirstUsableLBA;
Header->LastUsableLBA = LastUsableLBA;
Header->EntriesAllocated = (UINT32)EntryCount;
Header->DiskGUID = GetGUID();
LbaBlock.Header1_LBA = Header1_LBA;
LbaBlock.Header2_LBA = Header2_LBA;
LbaBlock.Table1_LBA = Table1_LBA;
LbaBlock.Table2_LBA = Table2_LBA;
status = WriteGPT(
DiskHandle,
Header,
Table,
&LbaBlock
);
DoFree(Header);
DoFree(Table);
return status;
}