]> code.delx.au - refind/blob - refind/gpt.c
Improved GPT support; uses CRCs to minimize the risk of damaged data wreaking
[refind] / refind / gpt.c
1 /*
2 * refind/gpt.c
3 * Functions related to GPT data structures
4 *
5 * Copyright (c) 2014 Roderick W. Smith
6 * All rights reserved.
7 *
8 * This program is distributed under the terms of the GNU General Public
9 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
10 * with this source code or binaries made from it.
11 *
12 */
13
14 #include "gpt.h"
15 #include "lib.h"
16 #include "screen.h"
17 #include "crc32.h"
18 #include "../include/refit_call_wrapper.h"
19
20 #ifdef __MAKEWITH_TIANO
21 #define BlockIoProtocol gEfiBlockIoProtocolGuid
22 #endif
23
24 extern GPT_DATA *gPartitions;
25
26 // Allocate data for the main GPT_DATA structure, as well as the ProtectiveMBR
27 // and Header structures it contains. This function does *NOT*, however,
28 // allocate memory for the Entries data structure, since its size is variable
29 // and is determined by the contents of Header.
30 GPT_DATA * AllocateGptData(VOID) {
31 GPT_DATA *GptData;
32
33 GptData = AllocateZeroPool(sizeof(GPT_DATA));
34 if (GptData != NULL) {
35 GptData->ProtectiveMBR = AllocateZeroPool(sizeof(MBR_RECORD));
36 GptData->Header = AllocateZeroPool(sizeof(GPT_HEADER));
37 if ((GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL)) {
38 MyFreePool(GptData->ProtectiveMBR);
39 MyFreePool(GptData->Header);
40 MyFreePool(GptData);
41 GptData = NULL;
42 } // if
43 } // if
44 return GptData;
45 } // GPT_DATA * AllocateGptData()
46
47 // Unallocate a single GPT_DATA structure. This does NOT follow the
48 // linked list, though.
49 VOID ClearGptData(GPT_DATA *Data) {
50 if (Data) {
51 if (Data->ProtectiveMBR)
52 MyFreePool(Data->ProtectiveMBR);
53 if (Data->Header)
54 MyFreePool(Data->Header);
55 if (Data->Entries)
56 MyFreePool(Data->Entries);
57 MyFreePool(Data);
58 } // if
59 } // VOID ClearGptData()
60
61 // TODO: Make this work on big-endian systems; at the moment, it contains
62 // little-endian assumptions!
63 // Returns TRUE if the GPT header data appear valid, FALSE otherwise.
64 static BOOLEAN GptHeaderValid(GPT_DATA *GptData) {
65 BOOLEAN IsValid;
66 UINT32 CrcValue, StoredCrcValue;
67 UINTN HeaderSize = sizeof(GPT_HEADER);
68
69 IsValid = ((GptData != NULL) && (GptData->ProtectiveMBR != NULL) && (GptData->Header != NULL));
70 IsValid = IsValid && (GptData->ProtectiveMBR->MBRSignature == 0xAA55);
71 IsValid = IsValid && ((GptData->ProtectiveMBR->partitions[0].type == 0xEE) ||
72 (GptData->ProtectiveMBR->partitions[1].type == 0xEE) ||
73 (GptData->ProtectiveMBR->partitions[2].type == 0xEE) ||
74 (GptData->ProtectiveMBR->partitions[3].type == 0xEE));
75 IsValid = IsValid && (GptData->Header->signature == 0x5452415020494645ULL);
76
77 // Looks good so far; check CRC value....
78 if (IsValid) {
79 if (GptData->Header->header_size < HeaderSize)
80 HeaderSize = GptData->Header->header_size;
81 StoredCrcValue = GptData->Header->header_crc32;
82 GptData->Header->header_crc32 = 0;
83 CrcValue = crc32(0x0, GptData->Header, HeaderSize);
84 if (CrcValue != StoredCrcValue)
85 IsValid = FALSE;
86 GptData->Header->header_crc32 = StoredCrcValue;
87 } // if
88
89 return IsValid;
90 } // BOOLEAN GptHeaderValid()
91
92 // Read GPT data from Volume and store it in Data. Note that this function
93 // may be called on a Volume that is not in fact a GPT disk (an MBR disk,
94 // a partition, etc.), in which case it will return EFI_LOAD_ERROR or some
95 // other error condition. In this case, *Data will be left alone.
96 // Note also that this function checks CRCs and does other sanity checks
97 // on the input data, but does NOT resort to using the backup data if the
98 // primary data structures are damaged.
99 EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) {
100 EFI_STATUS Status = EFI_SUCCESS;
101 UINT64 BufferSize;
102 UINT32 TableCrc;
103 UINTN i;
104 GPT_DATA *GptData; // Temporary holding storage; transferred to *Data later
105
106 if ((Volume == NULL) || (Data == NULL))
107 Status = EFI_INVALID_PARAMETER;
108
109 // get block i/o
110 if ((Status == EFI_SUCCESS) && (Volume->BlockIO == NULL)) {
111 Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO));
112 if (EFI_ERROR(Status)) {
113 Volume->BlockIO = NULL;
114 Print(L"Warning: Can't get BlockIO protocol.\n");
115 Status = EFI_NOT_READY;
116 }
117 } // if
118
119 if ((Status == EFI_SUCCESS) && ((!Volume->BlockIO->Media->MediaPresent) || (Volume->BlockIO->Media->LogicalPartition)))
120 Status = EFI_NO_MEDIA;
121
122 if (Status == EFI_SUCCESS) {
123 GptData = AllocateGptData(); // Note: All but GptData->Entries
124 if (GptData == NULL) {
125 Status = EFI_OUT_OF_RESOURCES;
126 } // if
127 } // if
128
129 // Read the MBR and store it in GptData->ProtectiveMBR.
130 if (Status == EFI_SUCCESS) {
131 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
132 0, 512, (VOID*) GptData->ProtectiveMBR);
133 }
134
135 // Read the GPT header and store it in GptData->Header.
136 if (Status == EFI_SUCCESS) {
137 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
138 1, sizeof(GPT_HEADER), GptData->Header);
139 }
140
141 // If it looks like a valid protective MBR, try to do more with it....
142 if (Status == EFI_SUCCESS) {
143 if (GptHeaderValid(GptData)) {
144 // Load actual GPT table....
145 BufferSize = GptData->Header->entry_count * 128;
146 if (GptData->Entries != NULL)
147 MyFreePool(GptData->Entries);
148 GptData->Entries = AllocatePool(BufferSize);
149 if (GptData->Entries == NULL) {
150 Status = EFI_OUT_OF_RESOURCES;
151 } // if
152
153 if (Status == EFI_SUCCESS) {
154 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
155 GptData->Header->entry_lba, BufferSize, GptData->Entries);
156 } // if
157
158 // Check CRC status of table
159 if (Status == EFI_SUCCESS) {
160 TableCrc = crc32(0x0, GptData->Entries, BufferSize);
161 if (TableCrc != GptData->Header->entry_crc32)
162 Status = EFI_CRC_ERROR;
163 } // if
164
165 // Now, ensure that every name is null-terminated....
166 if (Status == EFI_SUCCESS) {
167 for (i = 0; i < GptData->Header->entry_count; i++)
168 GptData->Entries[i].name[35] = '\0';
169 } // if
170 } else {
171 Status = EFI_UNSUPPORTED;
172 } // if/else valid header
173 } // if header read OK
174
175 if (Status == EFI_SUCCESS) {
176 // Everything looks OK, so copy it over
177 ClearGptData(*Data);
178 *Data = GptData;
179 } else {
180 ClearGptData(GptData);
181 } // if/else
182
183 return Status;
184 } // EFI_STATUS ReadGptData()
185
186 // Look in gPartitions for a partition with the specified Guid. If found, return
187 // a pointer to that partition's name string. If not found, return a NULL pointer.
188 // The calling function is responsible for freeing the returned memory.
189 CHAR16 * PartNameFromGuid(EFI_GUID *Guid) {
190 UINTN i;
191 CHAR16 *Found = NULL;
192 GPT_DATA *GptData;
193
194 if ((Guid == NULL) || (gPartitions == NULL))
195 return NULL;
196
197 GptData = gPartitions;
198 while ((GptData != NULL) && (!Found)) {
199 i = 0;
200 while ((i < GptData->Header->entry_count) && (!Found)) {
201 if (GuidsAreEqual((EFI_GUID*) &(GptData->Entries[i].partition_guid), Guid))
202 Found = StrDuplicate(GptData->Entries[i].name);
203 else
204 i++;
205 } // while(scanning entries)
206 GptData = GptData->NextEntry;
207 } // while(scanning GPTs)
208 return Found;
209 } // CHAR16 * PartNameFromGuid()
210
211 // Erase the gPartitions linked-list data structure
212 VOID ForgetPartitionTables(VOID) {
213 GPT_DATA *Next;
214
215 while (gPartitions != NULL) {
216 Next = gPartitions->NextEntry;
217 ClearGptData(gPartitions);
218 gPartitions = Next;
219 } // while
220 } // VOID ForgetPartitionTables()
221
222 // If Volume points to a whole disk with a GPT, add it to the gPartitions
223 // linked list of GPTs.
224 VOID AddPartitionTable(REFIT_VOLUME *Volume) {
225 GPT_DATA *GptData = NULL, *GptList;
226 EFI_STATUS Status;
227 UINTN NumTables = 1;
228
229 Status = ReadGptData(Volume, &GptData);
230 if (Status == EFI_SUCCESS) {
231 if (gPartitions == NULL) {
232 gPartitions = GptData;
233 } else {
234 GptList = gPartitions;
235 while (GptList->NextEntry != NULL) {
236 GptList = GptList->NextEntry;
237 NumTables++;
238 } // while
239 GptList->NextEntry = GptData;
240 NumTables++;
241 } // if/else
242 } else if (GptData != NULL) {
243 ClearGptData(GptData);
244 NumTables = 0;
245 } // if/else
246 Print(L"In AddPartitionTable(), total number of tables is %d\n", NumTables);
247 PauseForKey();
248 } // VOID AddPartitionTable()
249