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