1 /*
2 * Copyright 2019 Frank Hunleth
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gpt.h"
18 #include "crc32.h"
19 #include "util.h"
20
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #define GPT_PARTITION_SIZE 128
29
30 struct gpt_partition {
31 uint32_t block_offset;
32 uint32_t block_count;
33 uint64_t flags; // See spec for meaning of bits
34 uint8_t partition_type[UUID_LENGTH];
35 uint8_t guid[UUID_LENGTH];
36 char name[72]; // Encoded as UTF-16LE
37 bool expand_flag; // true to indicate that fwup can grow this partition
38 bool valid; // true if valid partition
39 uint8_t padding[6];
40 };
41
42 struct gpt_header {
43 uint64_t current_lba;
44 uint64_t backup_lba;
45 uint64_t first_usable_lba;
46 uint64_t last_usable_lba;
47 uint8_t disk_guid[UUID_LENGTH];
48 uint64_t partition_lba;
49
50 int num_partitions;
51 uint32_t partition_crc;
52 };
53
54 /**
55 * @brief gpt_verify check that the specified partitions make sense and don't overlap
56 * @param partitions the partitions
57 * @return 0 if successful
58 */
gpt_verify_partitions(const struct gpt_partition partitions[GPT_MAX_PARTITIONS])59 static int gpt_verify_partitions(const struct gpt_partition partitions[GPT_MAX_PARTITIONS])
60 {
61 bool expanding = false;
62 int i;
63
64 // Check for overlap
65 for (i = 0; i < GPT_MAX_PARTITIONS; i++) {
66 uint32_t ileft = partitions[i].block_offset;
67 uint32_t iright = ileft + partitions[i].block_count;
68
69 // Check if unused.
70 if (!partitions[i].valid)
71 continue;
72
73 if (ileft == iright && !partitions[i].expand_flag)
74 continue;
75
76 // Validate that if expand is used, it has to be the last partition
77 if (expanding)
78 ERR_RETURN("a partition can't be specified after the one with \"expand = true\"");
79
80 if (partitions[i].expand_flag)
81 expanding = true;
82
83 int j;
84 for (j = 0; j < GPT_MAX_PARTITIONS; j++) {
85 if (!partitions[j].valid || j == i)
86 continue;
87
88 uint32_t jleft = partitions[j].block_offset;
89 uint32_t jright = jleft + partitions[j].block_count;
90
91 if ((ileft >= jleft && ileft < jright) ||
92 (iright > jleft && iright <= jright))
93 ERR_RETURN("partitions %d (blocks %u to %u) and %d (blocks %u to %u) overlap",
94 i, ileft, iright, j, jleft, jright);
95 }
96 }
97
98 return 0;
99 }
100
create_protective_mbr(uint8_t * output,uint32_t num_blocks)101 static void create_protective_mbr(uint8_t *output, uint32_t num_blocks)
102 {
103 // First partition covers the entire disk or as much as possible
104 output[446] = 0; // Boot flag
105 output[446 + 2] = 0x02; // Match output of sfdisk?
106 output[446 + 4] = 0xee; // Protective MBR partition
107 output[446 + 5] = 0xff; // "End CHS" = 0xffffff
108 output[446 + 6] = 0xff;
109 output[446 + 7] = 0xff;
110 copy_le32(&output[446 + 8], 1);
111 copy_le32(&output[446 + 12], num_blocks - 1);
112
113 // MBR signature
114 output[510] = 0x55;
115 output[511] = 0xaa;
116 }
117
create_partition(const struct gpt_partition * partition,uint8_t * output,uint32_t num_blocks)118 static void create_partition(const struct gpt_partition *partition, uint8_t *output, uint32_t num_blocks)
119 {
120 uint32_t block_count = partition->block_count;
121
122 // If expanding and we know the total blocks, update this partition to the max
123 if (partition->expand_flag &&
124 num_blocks > (partition->block_offset + partition->block_count + GPT_SIZE_BLOCKS))
125 block_count = num_blocks - GPT_SIZE_BLOCKS - 1 - partition->block_offset;
126
127 uint64_t first_lba = partition->block_offset;
128 uint64_t last_lba = partition->block_offset + block_count - 1;
129
130 memcpy(&output[0], partition->partition_type, UUID_LENGTH);
131 memcpy(&output[16], partition->guid, UUID_LENGTH);
132 copy_le64(&output[32], first_lba);
133 copy_le64(&output[40], last_lba);
134 copy_le64(&output[48], partition->flags);
135 memcpy(&output[56], partition->name, 72);
136 }
137
create_partitions(const struct gpt_partition * partitions,uint32_t num_blocks,uint8_t * output)138 static void create_partitions(const struct gpt_partition *partitions, uint32_t num_blocks, uint8_t *output)
139 {
140 for (int i = 0; i < GPT_MAX_PARTITIONS; i++) {
141 if (partitions[i].valid)
142 create_partition(&partitions[i], output, num_blocks);
143
144 output += GPT_PARTITION_SIZE;
145 }
146 }
147
create_gpt_header(const struct gpt_header * header,uint8_t * output)148 static void create_gpt_header(const struct gpt_header *header,
149 uint8_t *output)
150 {
151 const size_t header_size = 92;
152
153 // Zero out the block per the spec (it should already be zero)
154 memset(output, 0, FWUP_BLOCK_SIZE);
155
156 // Signature
157 memcpy(&output[0], "EFI PART", 8);
158
159 // Revision (GPT 1.0)
160 copy_le32(&output[8], 0x00010000);
161
162 // Header size (92 bytes)
163 copy_le32(&output[12], header_size);
164
165 // CRC32 pre-calculation (offset 16)
166
167 // Reserved (offset 20)
168
169 // Header locations
170 copy_le64(&output[24], header->current_lba);
171 copy_le64(&output[32], header->backup_lba);
172
173 // Usable locations
174 copy_le64(&output[40], header->first_usable_lba);
175 copy_le64(&output[48], header->last_usable_lba);
176
177 // Disk GUID
178 memcpy(&output[56], header->disk_guid, UUID_LENGTH);
179
180 // Partition table location
181 copy_le64(&output[72], header->partition_lba);
182
183 // Number of partition entries
184 copy_le32(&output[80], header->num_partitions);
185
186 // Partition entry size
187 copy_le32(&output[84], GPT_PARTITION_SIZE);
188
189 copy_le32(&output[88], header->partition_crc);
190
191 // Final header CRC32
192 uint32_t header_crc = crc32buf((const char *) output, header_size);
193 copy_le32(&output[16], header_crc);
194 }
195
gpt_cfg_to_partitions(cfg_t * cfg,struct gpt_partition * partitions,int * found_partitions)196 static int gpt_cfg_to_partitions(cfg_t *cfg, struct gpt_partition *partitions, int *found_partitions)
197 {
198 cfg_t *partition;
199 int i = 0;
200 int found = 0;
201
202 memset(partitions, 0, GPT_MAX_PARTITIONS * sizeof(struct gpt_partition));
203
204 while ((partition = cfg_getnsec(cfg, "partition", i++)) != NULL) {
205 unsigned long partition_ix = strtoul(cfg_title(partition), NULL, 0);
206 if (partition_ix >= GPT_MAX_PARTITIONS)
207 ERR_RETURN("partition must be numbered 0 through %d", GPT_MAX_PARTITIONS - 1);
208
209 if (found & (1 << partition_ix))
210 ERR_RETURN("invalid or duplicate partition number found for %d", partition_ix);
211 found = found | (1 << partition_ix);
212
213 const char *unverified_type = cfg_getstr(partition, "type");
214 if (!unverified_type || string_to_uuid_me(unverified_type, partitions[partition_ix].partition_type) < 0)
215 ERR_RETURN("partition %d's type must set to a UUID", partition_ix);
216
217 const char *unverified_guid = cfg_getstr(partition, "guid");
218 if (!unverified_guid || string_to_uuid_me(unverified_guid, partitions[partition_ix].guid) < 0)
219 ERR_RETURN("partition %d must have a valid guid", partition_ix);
220
221 const char *unverified_name = cfg_getstr(partition, "name");
222 size_t name_len = strlen(unverified_name);
223 if (name_len > 36)
224 name_len = 36;
225 ascii_to_utf16le(unverified_name, partitions[partition_ix].name, name_len);
226
227 const char *unverified_block_offset = cfg_getstr(partition, "block-offset");
228 if (!unverified_block_offset || *unverified_block_offset == '\0')
229 ERR_RETURN("partition %d's block_offset is required", partition_ix);
230 char *endptr;
231 unsigned long block_offset = strtoul(unverified_block_offset, &endptr, 0);
232
233 // strtoul returns error by returning ULONG_MAX and setting errno.
234 // Values bigger than 2^32-1 won't fit in the MBR, so report an
235 // error for those too.
236 if ((block_offset == ULONG_MAX && errno != 0) || block_offset >= UINT32_MAX)
237 ERR_RETURN("partition %d's block_offset must be positive and less than 2^32 - 1: '%s'", partition_ix, unverified_block_offset);
238 if (*endptr != '\0')
239 ERR_RETURN("error parsing partition %d's block offset", partition_ix);
240
241 partitions[partition_ix].block_offset = block_offset;
242
243 partitions[partition_ix].block_count = cfg_getint(partition, "block-count");
244 if (partitions[partition_ix].block_count >= INT32_MAX)
245 ERR_RETURN("partition %d's block-count must be specified and less than 2^31 - 1", partition_ix);
246
247 partitions[partition_ix].expand_flag = cfg_getbool(partition, "expand");
248
249 const char *unverified_flags_str = cfg_getstr(partition, "flags");
250 uint64_t flags = 0;
251 if (unverified_flags_str) {
252 uint64_t unverified_flags = strtoull(unverified_flags_str, &endptr, 0);
253 if ((unverified_flags == ULLONG_MAX && errno != 0) || *endptr != '\0')
254 ERR_RETURN("error parsing partition %d's flags", partition_ix);
255 flags = unverified_flags;
256 }
257
258 // Boot for GPT partitions means bit 2 of the attribute flags is set.
259 if (cfg_getbool(partition, "boot")) {
260 flags |= 0x4;
261
262 // Support for "boot" was not added until after 1.6.0. To support
263 // older versions of fwup, update the "flags" field with the bit
264 // on.
265 char new_flags_str[32];
266 sprintf(new_flags_str, "0x%" PRIx64, flags);
267 cfg_setstr(partition, "flags", new_flags_str);
268 cfg_setbool(partition, "boot", cfg_false);
269 }
270
271 partitions[partition_ix].flags = flags;
272 partitions[partition_ix].valid = true;
273 }
274
275 if (found_partitions)
276 *found_partitions = found;
277 return 0;
278 }
279
gpt_verify_cfg(cfg_t * cfg)280 int gpt_verify_cfg(cfg_t *cfg)
281 {
282 uint8_t guid[UUID_LENGTH];
283 const char *unverified_guid = cfg_getstr(cfg, "guid");
284 if (!unverified_guid || string_to_uuid_me(unverified_guid, guid) < 0)
285 ERR_RETURN("GPT must have a valid disk guid");
286
287 int found_partitions = 0;
288 struct gpt_partition partitions[GPT_MAX_PARTITIONS];
289
290 if (gpt_cfg_to_partitions(cfg, partitions, &found_partitions) < 0)
291 return -1;
292 if (found_partitions == 0)
293 ERR_RETURN("empty partition table?");
294
295 return gpt_verify_partitions(partitions);
296 }
297
compute_num_blocks(const struct gpt_partition * partitions)298 static uint32_t compute_num_blocks(const struct gpt_partition *partitions)
299 {
300 uint32_t num_blocks = 0;
301 for (int i = 0; i < GPT_MAX_PARTITIONS; i++) {
302 if (partitions[i].valid) {
303 uint32_t last_block = partitions[i].block_offset + partitions[i].block_count;
304 if (last_block > num_blocks)
305 num_blocks = last_block;
306 }
307 }
308
309 // Add room for secondary GPT
310 num_blocks += GPT_SIZE_BLOCKS + 1;
311
312 return num_blocks;
313 }
314
315 /**
316 * @brief Encode the GPT headers
317 *
318 * After calling this function, the primary_gpt should be written starting at LBA 0 and
319 * the secondary_gpt should be written at byte offset secondary_gpt_offset.
320 *
321 * @param cfg the mbr configuration
322 * @param num_blocks the number of blocks on the destination or 0 if unknown
323 * @param mbr_and_primary_gpt where to store the MBR and primary GPT (must be GPT_SIZE + 512 bytes)
324 * @param secondary_gpt where to store the secondary GPT (must be GPT_SIZE bytes)
325 * @param secondary_gpt_offset where to write the secondary GPT on disk
326 * @return 0 if successful
327 */
gpt_create_cfg(cfg_t * cfg,uint32_t num_blocks,uint8_t * mbr_and_primary_gpt,uint8_t * secondary_gpt,off_t * secondary_gpt_offset)328 int gpt_create_cfg(cfg_t *cfg, uint32_t num_blocks, uint8_t *mbr_and_primary_gpt, uint8_t *secondary_gpt, off_t *secondary_gpt_offset)
329 {
330 struct gpt_header header;
331 struct gpt_partition partitions[GPT_MAX_PARTITIONS];
332
333 if (gpt_cfg_to_partitions(cfg, partitions, NULL) < 0)
334 return -1;
335
336 if (num_blocks == 0)
337 num_blocks = compute_num_blocks(partitions);
338
339 // Clear everything out.
340 memset(mbr_and_primary_gpt, 0, GPT_SIZE + FWUP_BLOCK_SIZE);
341 memset(secondary_gpt, 0, GPT_SIZE);
342
343 // Create the protective MBR
344 create_protective_mbr(mbr_and_primary_gpt, num_blocks);
345
346 // Create the partition table entries
347 create_partitions(partitions, num_blocks, &mbr_and_primary_gpt[FWUP_BLOCK_SIZE * 2]);
348 create_partitions(partitions, num_blocks, &secondary_gpt[0]);
349
350 // Create the GPT headers
351 header.num_partitions = (GPT_PARTITION_TABLE_BLOCKS * FWUP_BLOCK_SIZE / GPT_PARTITION_SIZE);
352 header.partition_crc = crc32buf((const char *) secondary_gpt, header.num_partitions * GPT_PARTITION_SIZE);
353 const char *unverified_guid = cfg_getstr(cfg, "guid");
354 if (!unverified_guid || string_to_uuid_me(unverified_guid, header.disk_guid) < 0)
355 return -1;
356
357 header.first_usable_lba = 1 + GPT_SIZE_BLOCKS;
358 header.last_usable_lba = num_blocks - GPT_SIZE_BLOCKS - 1;
359
360 // Primary GPT
361 header.current_lba = 1;
362 header.backup_lba = num_blocks - 1;
363 header.partition_lba = 2;
364 create_gpt_header(&header, &mbr_and_primary_gpt[FWUP_BLOCK_SIZE]);
365
366 // Secondary GPT
367 header.current_lba = num_blocks - 1;
368 header.backup_lba = 1;
369 header.partition_lba = num_blocks - GPT_SIZE_BLOCKS;
370 create_gpt_header(&header, &secondary_gpt[GPT_SIZE - FWUP_BLOCK_SIZE]);
371
372 *secondary_gpt_offset = header.partition_lba * FWUP_BLOCK_SIZE;
373
374 return 0;
375 }
376