//
// GPT (GUID Partition Table) declarations.
//


//
// DO WE EVER ALLOW GPT_TABLEs TO NOT BE PACKED UP AGAINST THEIR HEADER?
// IF NOT, DO WE ALLOW PEOPLE TO MAKE ASSUMPTIONS ABOUT THEIR LOCATION?
// DO WE NEED THE POINTER?
//

//
// Rules:
//      None of these structures ever appear at LBA 0, because
//      we put a "fake" MBR there (the legacy defense MBR).
//      Therefore, LBA of 0 is useable as NULL.
//
//      For All Entry's, StartingLBA must be >= FirstUsableLBA.
//      For All Entry's, EndingLBA must be <= LastUsableLBA.
//
//      0 is not a valid GUID.  Therefore, emtpy GPT_ENTRY's will
//      have a PartitionType of 0.
//      However, if an entry is otherwise valid, but has a PartitionID
//      of 0, this means a GUID needs to be generated and placed there.
//
//      LBA = Logical Block Address == Sector Number.  Always count from 0.
//
//      Block size (sector size) could be any number >= sizeof(GPT_ENTRY)
//      AND >= sizeof(GPT_HEADER).  In practice, always >= 512 bytes.
//
//      A block, B, is free for allocation to a partition if and only if
//      it is in the range FirstUsableLBA <= B <= LastUsableLBA AND it
//      is not already allocated to some other parition.
//
//      GPT partitions are always contiguous arrays of blocks.  however,
//      they need NOT be packed on the disk, their order in the GPT need
//      NOT match their order on the disk, there may be blank entries
//      in the GPT table, etc.  Building an accurate view of the parititon
//      *requires* reading the entire GPT_TABLE into memory.  In practice,
//      it will always be small enough for this to be easy.
//

#pragma pack (4)

//
// Each partition is described by a GPT_ENTRY.
//
#define PART_NAME_LEN       36

typedef struct {
    EFI_GUID    PartitionType;  // declartion of this partition's type
    EFI_GUID    PartitionID;    // Unique ID for this particular partition
                                // (unique to this instance)
    EFI_LBA     StartingLBA;    // 0 based block (sector) address of the
                                // first block included in the partition.
    EFI_LBA     EndingLBA;      // 0 based block (sector) address of the
                                // last block included in the partition.
                                // If StartingLBA == EndingLBA then the
                                // partition is 1 block long.  this is legal.
    UINT64      Attributes;     // Always ZERO for now
    CHAR16      PartitionName[PART_NAME_LEN];  // 36 unicode characters of name
} GPT_ENTRY, *PGPT_ENTRY;

C_ASSERT (sizeof (GPT_ENTRY) == 128);
//
// All of the GPT_ENTRY's are gathered into a GPT_TABLE, which
// is stored as a linear array of blocks on the disk.
//
typedef struct {
    GPT_ENTRY   Entry[1];       // Always an integer number of Entry's
                                // per sector.  Always at least 1 sector.
                                // Can be any number of sectors...
} GPT_TABLE, *PGPT_TABLE;

//
// A main and a backup header each describe the disk, and each points
// to it's own copy of the GPT_TABLE...
//
typedef struct {
    UINT64      Signature;      // GPT PART
    UINT32      Revision;
    UINT32      HeaderSize;
    UINT32      HeaderCRC32;    // computed using 0 for own init value
    UINT32      Reserved0;
    EFI_LBA     MyLBA;          // 0 based sector number of the first
                                // sector of this structure
    EFI_LBA     AlternateLBA;   // 0 based sector (block) number of the
                                // first sector of the secondary
                                // GPT_HEADER, or 0 if this is the
                                // secondary.
    EFI_LBA     FirstUsableLBA; // 0 based sector number of the first
                                // sector that may be included in a partition.
    EFI_LBA     LastUsableLBA;  // last legal LBA, inclusive.
    EFI_GUID    DiskGUID;       // The unique ID of this LUN/spindle/disk
    EFI_LBA     TableLBA;       // The start of the table of entries...
    UINT32      EntriesAllocated; // Number of entries in the table, this is
                                  // how many allocated, NOT how many used.
    UINT32      SizeOfGPT_ENTRY;    // sizeof(GPT_ENTRY) always mult. of 8
    UINT32      TableCRC32;      // CRC32 of the table.
    // Reserved and zeros to the end of the block
    // Don't declare an array or sizeof() gives a nonsense answer..
} GPT_HEADER, *PGPT_HEADER;

C_ASSERT (sizeof (GPT_HEADER) == 92);

#define GPT_HEADER_SIGNATURE    0x5452415020494645
#define GPT_REVISION_1_0        0x00010000

#define ENTRY_DEFAULT       128
//#define ENTRY_DEFAULT       8           // TESTING ONLY
#define ENTRY_SANITY_LIMIT  1024


//
// GPT Disk Layout
//
/*

        +---------------------------------------------------+
LBA=0   | "Fake" MBR to ward off legacy parition apps       |
        +---------------------------------------------------+
LBA=1   | Primary GPT_HEADER                                |
        +---------------------------------------------------+
LBA=2   | Primary GPT_TABLE starts                          |
        ...             ...                                 ...
LBA=n   | Primary GPT_TABLE ends                            |
        +---------------------------------------------------+
LBA=n+1 | FirstUsableLBA = this block                       |
        ...             ...                                 ...
LBA=x   | LastUsableLBA = this block                        |
        +---------------------------------------------------+
LBA=x+1 | Secondary GPT_TABLE starts                        |
        ...             ...                                 ...
LBA=z   | Secondary GPT_TABLE ends                          |
        +---------------------------------------------------+
LBA=z+n | Secondary GPT_HEADER starts                       |
        ...             ...                                 ...
LAST    | Secondary GPT_HEADER ends at last sector of disk  |
        +---------------------------------------------------+

SO:
    Primary GPT_HEADER is always at LBA=1
    Secondary GPT_HEADER is at LBA=Last so long as GPT_HEADER fits
    in 1 sector, which we require.

    Primary Table is stacked up after the primary header,
    which points to it anyway.

    Secondary Table is stacked up before the secondary header,
    which points to it anyway.

*/


//
// ------------------ Functions To Manipulate GPT ---------------
//
typedef struct _LBA_BLOCK {
    EFI_LBA     Header1_LBA;
    EFI_LBA     Table1_LBA;
    EFI_LBA     Header2_LBA;
    EFI_LBA     Table2_LBA;
} LBA_BLOCK, *PLBA_BLOCK;


EFI_STATUS
ReadGPT(
    EFI_HANDLE      DiskHandle,
    PGPT_HEADER     *Header,
    PGPT_TABLE      *Table,
    PLBA_BLOCK      *LbaBlock,
    UINTN           *DiskType
    );

EFI_STATUS
WriteGPT(
    EFI_HANDLE      DiskHandle,
    PGPT_HEADER     Header,
    PGPT_TABLE      Table,
    PLBA_BLOCK      LbaBlock
    );

EFI_STATUS
CreateGPT(
    EFI_HANDLE  DiskHandle,
    UINTN       EntryRequest
    );

#pragma pack ()