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