]> code.delx.au - refind/blob - refind/gpt.c
Added feature to detect the root (/) Linux filesystem based on
[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 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 protective MBR and header data appear valid,
64 // FALSE otherwise.
65 static BOOLEAN GptHeaderValid(GPT_DATA *GptData) {
66 BOOLEAN IsValid;
67 UINT32 CrcValue, StoredCrcValue;
68 UINTN HeaderSize = sizeof(GPT_HEADER);
69
70 if ((GptData == NULL) || (GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL))
71 return FALSE;
72
73 IsValid = (GptData->ProtectiveMBR->MBRSignature == 0xAA55);
74 IsValid = IsValid && ((GptData->ProtectiveMBR->partitions[0].type == 0xEE) ||
75 (GptData->ProtectiveMBR->partitions[1].type == 0xEE) ||
76 (GptData->ProtectiveMBR->partitions[2].type == 0xEE) ||
77 (GptData->ProtectiveMBR->partitions[3].type == 0xEE));
78
79 IsValid = IsValid && ((GptData->Header->signature == 0x5452415020494645ULL) &&
80 (GptData->Header->spec_revision == 0x00010000) &&
81 (GptData->Header->entry_size == 128));
82
83 // Looks good so far; check CRC value....
84 if (IsValid) {
85 if (GptData->Header->header_size < HeaderSize)
86 HeaderSize = GptData->Header->header_size;
87 StoredCrcValue = GptData->Header->header_crc32;
88 GptData->Header->header_crc32 = 0;
89 CrcValue = crc32(0x0, GptData->Header, HeaderSize);
90 if (CrcValue != StoredCrcValue)
91 IsValid = FALSE;
92 GptData->Header->header_crc32 = StoredCrcValue;
93 } // if
94
95 return IsValid;
96 } // BOOLEAN GptHeaderValid()
97
98 // Read GPT data from Volume and store it in *Data. Note that this function
99 // may be called on a Volume that is not in fact a GPT disk (an MBR disk,
100 // a partition, etc.), in which case it will return EFI_LOAD_ERROR or some
101 // other error condition. In this case, *Data will be left alone.
102 // Note also that this function checks CRCs and does other sanity checks
103 // on the input data, but does NOT resort to using the backup data if the
104 // primary data structures are damaged. The intent is that the function
105 // be very conservative about reading GPT data. Currently (version 0.7.10),
106 // rEFInd uses the data only to provide access to partition names. This is
107 // non-critical data, so it's OK to return nothing, but having the program
108 // hang on reading garbage or return nonsense could be very bad.
109 EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) {
110 EFI_STATUS Status = EFI_SUCCESS;
111 UINT64 BufferSize;
112 UINTN i;
113 GPT_DATA *GptData; // Temporary holding storage; transferred to *Data later
114
115 if ((Volume == NULL) || (Data == NULL))
116 return EFI_INVALID_PARAMETER;
117
118 // get block i/o
119 if ((Status == EFI_SUCCESS) && (Volume->BlockIO == NULL)) {
120 Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO));
121 if (EFI_ERROR(Status)) {
122 Volume->BlockIO = NULL;
123 Print(L"Warning: Can't get BlockIO protocol in ReadGptData().\n");
124 Status = EFI_NOT_READY;
125 }
126 } // if
127
128 if ((Status == EFI_SUCCESS) && ((!Volume->BlockIO->Media->MediaPresent) || (Volume->BlockIO->Media->LogicalPartition)))
129 Status = EFI_NO_MEDIA;
130
131 if (Status == EFI_SUCCESS) {
132 GptData = AllocateGptData(); // Note: All but GptData->Entries
133 if (GptData == NULL) {
134 Status = EFI_OUT_OF_RESOURCES;
135 } // if
136 } // if
137
138 // Read the MBR and store it in GptData->ProtectiveMBR.
139 if (Status == EFI_SUCCESS) {
140 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
141 0, sizeof(MBR_RECORD), (VOID*) GptData->ProtectiveMBR);
142 }
143
144 // Read the GPT header and store it in GptData->Header.
145 if (Status == EFI_SUCCESS) {
146 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
147 1, sizeof(GPT_HEADER), GptData->Header);
148 }
149
150 // If it looks like a valid protective MBR & GPT header, try to do more with it....
151 if (Status == EFI_SUCCESS) {
152 if (GptHeaderValid(GptData)) {
153 // Load actual GPT table....
154 BufferSize = GptData->Header->entry_count * 128;
155 GptData->Entries = AllocatePool(BufferSize);
156 if (GptData->Entries == NULL)
157 Status = EFI_OUT_OF_RESOURCES;
158
159 if (Status == EFI_SUCCESS)
160 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
161 GptData->Header->entry_lba, BufferSize, GptData->Entries);
162
163 // Check CRC status of table
164 if ((Status == EFI_SUCCESS) && (crc32(0x0, GptData->Entries, BufferSize) != GptData->Header->entry_crc32))
165 Status = EFI_CRC_ERROR;
166
167 // Now, ensure that every name is null-terminated....
168 if (Status == EFI_SUCCESS) {
169 for (i = 0; i < GptData->Header->entry_count; i++)
170 GptData->Entries[i].name[35] = '\0';
171 } // if
172 } else {
173 Status = EFI_UNSUPPORTED;
174 } // if/else valid header
175 } // if header read OK
176
177 if (Status == EFI_SUCCESS) {
178 // Everything looks OK, so copy it over
179 ClearGptData(*Data);
180 *Data = GptData;
181 } else {
182 ClearGptData(GptData);
183 } // if/else
184
185 return Status;
186 } // EFI_STATUS ReadGptData()
187
188 // // Look in gPartitions for a partition with the specified Guid. If found, return
189 // // a pointer to that partition's name string. If not found, return a NULL pointer.
190 // // The calling function is responsible for freeing the returned memory.
191 // CHAR16 * PartNameFromGuid(EFI_GUID *Guid) {
192 // UINTN i;
193 // CHAR16 *Found = NULL;
194 // GPT_DATA *GptData;
195 //
196 // if ((Guid == NULL) || (gPartitions == NULL))
197 // return NULL;
198 //
199 // GptData = gPartitions;
200 // while ((GptData != NULL) && (!Found)) {
201 // i = 0;
202 // while ((i < GptData->Header->entry_count) && (!Found)) {
203 // if (GuidsAreEqual((EFI_GUID*) &(GptData->Entries[i].partition_guid), Guid))
204 // Found = StrDuplicate(GptData->Entries[i].name);
205 // else
206 // i++;
207 // } // while(scanning entries)
208 // GptData = GptData->NextEntry;
209 // } // while(scanning GPTs)
210 // return Found;
211 // } // CHAR16 * PartNameFromGuid()
212
213 // Look in gPartitions for a partition with the specified Guid. If found, return
214 // a pointer to that partition's data. If not found, return a NULL pointer.
215 // The calling function is responsible for freeing the returned memory.
216 GPT_ENTRY * FindPartWithGuid(EFI_GUID *Guid) {
217 UINTN i;
218 GPT_ENTRY *Found = NULL;
219 GPT_DATA *GptData;
220
221 if ((Guid == NULL) || (gPartitions == NULL))
222 return NULL;
223
224 GptData = gPartitions;
225 while ((GptData != NULL) && (!Found)) {
226 i = 0;
227 while ((i < GptData->Header->entry_count) && (!Found)) {
228 if (GuidsAreEqual((EFI_GUID*) &(GptData->Entries[i].partition_guid), Guid)) {
229 Found = AllocateZeroPool(sizeof(GPT_ENTRY));
230 CopyMem(Found, &GptData->Entries[i], sizeof(GPT_ENTRY));
231 } else {
232 i++;
233 } // if/else
234 } // while(scanning entries)
235 GptData = GptData->NextEntry;
236 } // while(scanning GPTs)
237 return Found;
238 } // GPT_ENTRY * FindPartWithGuid()
239
240 // Erase the gPartitions linked-list data structure
241 VOID ForgetPartitionTables(VOID) {
242 GPT_DATA *Next;
243
244 while (gPartitions != NULL) {
245 Next = gPartitions->NextEntry;
246 ClearGptData(gPartitions);
247 gPartitions = Next;
248 } // while
249 } // VOID ForgetPartitionTables()
250
251 // If Volume points to a whole disk with a GPT, add it to the gPartitions
252 // linked list of GPTs.
253 VOID AddPartitionTable(REFIT_VOLUME *Volume) {
254 GPT_DATA *GptData = NULL, *GptList;
255 EFI_STATUS Status;
256 UINTN NumTables = 1;
257
258 Status = ReadGptData(Volume, &GptData);
259 if (Status == EFI_SUCCESS) {
260 if (gPartitions == NULL) {
261 gPartitions = GptData;
262 } else {
263 GptList = gPartitions;
264 while (GptList->NextEntry != NULL) {
265 GptList = GptList->NextEntry;
266 NumTables++;
267 } // while
268 GptList->NextEntry = GptData;
269 NumTables++;
270 } // if/else
271 } else if (GptData != NULL) {
272 ClearGptData(GptData);
273 NumTables = 0;
274 } // if/else
275 } // VOID AddPartitionTable()
276