From 54acc0cec519f83002cc0bcc67c9d8f571ad8708 Mon Sep 17 00:00:00 2001 From: srs5694 Date: Sun, 27 Apr 2014 22:13:33 -0400 Subject: [PATCH] New files required for reading and parsing GPT data. --- refind/gpt.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++ refind/gpt.h | 89 ++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 refind/gpt.c create mode 100644 refind/gpt.h diff --git a/refind/gpt.c b/refind/gpt.c new file mode 100644 index 0000000..4f9be22 --- /dev/null +++ b/refind/gpt.c @@ -0,0 +1,203 @@ +/* + * refind/gpt.c + * Functions related to GPT data structures + * + * Copyright (c) 2014 Roderick W. Smith + * All rights reserved. + * + * This program is distributed under the terms of the GNU General Public + * License (GPL) version 3 (GPLv3), a copy of which must be distributed + * with this source code or binaries made from it. + * + */ + +#include "gpt.h" +#include "lib.h" +#include "screen.h" +#include "../include/refit_call_wrapper.h" + +#ifdef __MAKEWITH_TIANO +#define BlockIoProtocol gEfiBlockIoProtocolGuid +#endif + +extern GPT_DATA *gPartitions; + +GPT_DATA * AllocateGptData(VOID) { + GPT_DATA *GptData; + + GptData = AllocateZeroPool(sizeof(GPT_DATA)); + if (GptData != NULL) { + GptData->ProtectiveMBR = AllocateZeroPool(sizeof(MBR_RECORD)); + GptData->Header = AllocateZeroPool(sizeof(GPT_HEADER)); + if ((GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL)) { + MyFreePool(GptData->ProtectiveMBR); + MyFreePool(GptData->Header); + MyFreePool(GptData); + GptData = NULL; + } // if + } // if + return GptData; +} // GPT_DATA * AllocateGptData() + +VOID ClearGptData(GPT_DATA *Data) { + if (Data) { + if (Data->ProtectiveMBR) + MyFreePool(Data->ProtectiveMBR); + if (Data->Header) + MyFreePool(Data->Header); + if (Data->Entries) + MyFreePool(Data->Entries); + MyFreePool(Data); + } // if +} // VOID ClearGptData() + +// TODO: Add more tests, like a CRC check. +// Returns TRUE if the GPT header data appear valid, FALSE otherwise. +static BOOLEAN GptHeaderValid(GPT_HEADER *Header) { + BOOLEAN IsValid = TRUE; + + if ((Header->signature != 0x5452415020494645ULL) || (Header->entry_count > 2048)) + IsValid = FALSE; + + return IsValid; +} // BOOLEAN GptHeaderValid + +// Read GPT data from Volume and store it in Data. Note that this function +// may be called on a Volume that is not in fact the protective MBR of a GPT +// disk, in which case it will return EFI_LOAD_ERROR or some other error +// condition. In this case, *Data will be left alone. +EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) { + EFI_STATUS Status = EFI_SUCCESS; + UINT64 BufferSize; + GPT_DATA *GptData; // Temporary holding storage; transferred to *Data later + + if ((Volume == NULL) || (Data == NULL)) + Status = EFI_INVALID_PARAMETER; + + // get block i/o + if ((Status == EFI_SUCCESS) && (Volume->BlockIO == NULL)) { + Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO)); + if (EFI_ERROR(Status)) { + Volume->BlockIO = NULL; + Print(L"Warning: Can't get BlockIO protocol.\n"); + Status = EFI_INVALID_PARAMETER; + } + } // if + + if ((Status == EFI_SUCCESS) && ((!Volume->BlockIO->Media->MediaPresent) || (Volume->BlockIO->Media->LogicalPartition))) + Status = EFI_NO_MEDIA; + + if (Status == EFI_SUCCESS) { + GptData = AllocateGptData(); + if (GptData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } // if + } // if + + // Read the MBR and store it in GptData->ProtectiveMBR. + if (Status == EFI_SUCCESS) { + Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, + 0, 512, (VOID*) GptData->ProtectiveMBR); + } + + // If it looks like a valid protective MBR, try to do more with it.... + if ((Status == EFI_SUCCESS) && (GptData->ProtectiveMBR->MBRSignature == 0xAA55) && + ((GptData->ProtectiveMBR->partitions[0].type == 0xEE) || (GptData->ProtectiveMBR->partitions[1].type == 0xEE) || + (GptData->ProtectiveMBR->partitions[2].type = 0xEE) || (GptData->ProtectiveMBR->partitions[3].type == 0xEE))) { + Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, + 1, sizeof(GPT_HEADER), GptData->Header); + + // Do basic sanity check on GPT header. + if ((Status == EFI_SUCCESS) && !GptHeaderValid(GptData->Header)) { + Status = EFI_UNSUPPORTED; + } + + // Load actual GPT table.... + if (Status == EFI_SUCCESS) { + BufferSize = GptData->Header->entry_count * 128; + if (GptData->Entries != NULL) + MyFreePool(GptData->Entries); + GptData->Entries = AllocatePool(BufferSize); + if (GptData->Entries == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } // if + } // if + + if (Status == EFI_SUCCESS) { + Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, + GptData->Header->header_lba, BufferSize, GptData->Entries); + } // if + + } else { + Status = EFI_LOAD_ERROR; + } // if/else + + if (Status == EFI_SUCCESS) { + // Everything looks OK, so copy it over + ClearGptData(*Data); + *Data = GptData; + } else { + ClearGptData(GptData); + } // if/else + + return Status; +} // EFI_STATUS ReadGptData() + +// Look in gPartitions for a partition with the specified Guid. If found, return +// a pointer to that partition's name string. If not found, return a NULL pointer. +// The calling function is responsible for freeing the returned memory. +CHAR16 * PartNameFromGuid(EFI_GUID *Guid) { + UINTN i; + CHAR16 *Found = NULL; + GPT_DATA *GptData; + + if ((Guid == NULL) || (gPartitions == NULL)) + return NULL; + + GptData = gPartitions; + while ((GptData != NULL) && (!Found)) { + i = 0; + while ((i < GptData->Header->entry_count) && (!Found)) { + if (GuidsAreEqual((EFI_GUID*) &(GptData->Entries[i].partition_guid), Guid)) + Found = StrDuplicate(GptData->Entries[i].name); + else + i++; + } // while(scanning entries) + GptData = GptData->NextEntry; + } // while(scanning GPTs) + return Found; +} // CHAR16 * PartNameFromGuid() + +// Erase the gPartitions linked-list data structure +VOID ForgetPartitionTables(VOID) { + GPT_DATA *Next; + + while (gPartitions != NULL) { + Next = gPartitions->NextEntry; + ClearGptData(gPartitions); + gPartitions = Next; + } // while +} // VOID ForgetPartitionTables() + +// If Volume points to a whole disk with a GPT, add it to the gPartitions +// linked list of GPTs. +VOID AddPartitionTable(REFIT_VOLUME *Volume) { + GPT_DATA *GptData = NULL, *GptList; + EFI_STATUS Status; + + Status = ReadGptData(Volume, &GptData); + if (Status == EFI_SUCCESS) { + if (gPartitions == NULL) { + gPartitions = GptData; + } else { + GptList = gPartitions; + while (GptList->NextEntry != NULL) { + GptList = GptList->NextEntry; + } // while + GptList->NextEntry = GptData; + } // if/else + } else if (GptData != NULL) { + ClearGptData(GptData); + } // if/else +} // VOID AddPartitionTable() + diff --git a/refind/gpt.h b/refind/gpt.h new file mode 100644 index 0000000..3a6d912 --- /dev/null +++ b/refind/gpt.h @@ -0,0 +1,89 @@ +/* + * refind/gpt.h + * Functions related to GPT data structures + * + * Copyright (c) 2014 Roderick W. Smith + * All rights reserved. + * + * This program is distributed under the terms of the GNU General Public + * License (GPL) version 3 (GPLv3), a copy of which must be distributed + * with this source code or binaries made from it. + * + */ + +#include "global.h" + +#ifndef __GPT_H_ +#define __GPT_H_ + +#ifdef __MAKEWITH_GNUEFI +#include "efi.h" +#include "efilib.h" +#else +#include "../include/tiano_includes.h" +#endif + +#pragma pack(1) +typedef struct { + UINT8 flags; + UINT8 start_chs[3]; + UINT8 type; + UINT8 end_chs[3]; + UINT32 start_lba; + UINT32 size; +} MBR_PART_INFO; + +// A 512-byte data structure into which the MBR can be loaded in one +// go. Also used when loading logical partitions. + +typedef struct { + UINT8 code[440]; + UINT32 diskSignature; + UINT16 nulls; + MBR_PART_INFO partitions[4]; + UINT16 MBRSignature; +} MBR_RECORD; + +typedef struct { + UINT64 signature; + UINT32 spec_revision; + UINT32 header_size; + UINT32 header_crc32; + UINT32 reserved; + UINT64 header_lba; + UINT64 alternate_header_lba; + UINT64 first_usable_lba; + UINT64 last_usable_lba; + UINT8 disk_guid[16]; + UINT64 entry_lba; + UINT32 entry_count; + UINT32 entry_size; + UINT32 entry_crc32; + UINT8 reserved2[420]; +} GPT_HEADER; + +typedef struct { + UINT8 type_guid[16]; + UINT8 partition_guid[16]; + UINT64 start_lba; + UINT64 end_lba; + UINT64 attributes; + CHAR16 name[36]; +} GPT_ENTRY; + +typedef struct _gpt_data { + MBR_RECORD *ProtectiveMBR; + GPT_HEADER *Header; + GPT_ENTRY *Entries; + struct _gpt_data *NextEntry; +} GPT_DATA; + +#pragma pack(0) + +VOID ClearGptData(GPT_DATA *Data); +EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data); +CHAR16 * PartNameFromGuid(EFI_GUID *Guid); +VOID ForgetPartitionTables(VOID); +VOID AddPartitionTable(REFIT_VOLUME *Volume); + +#endif \ No newline at end of file -- 2.39.2