gpt.c revision 219083
1/*- 2 * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/boot/common/gpt.c 219083 2011-02-27 12:25:47Z pjd $"); 29 30#include <sys/param.h> 31#include <sys/gpt.h> 32 33#ifndef LITTLE_ENDIAN 34#error gpt.c works only for little endian architectures 35#endif 36 37#include "crc32.h" 38#include "drv.h" 39#include "util.h" 40#include "gpt.h" 41 42#define MAXTBLENTS 128 43 44static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; 45static uint64_t hdr_primary_lba, hdr_backup_lba; 46static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; 47static struct gpt_ent *gpttable; 48static int curent, bootonce; 49 50/* 51 * Buffer below 64kB passed on gptread(), which can hold at least 52 * one sector of data (512 bytes). 53 */ 54static char *secbuf; 55 56static void 57gptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 58 struct gpt_ent *table) 59{ 60 int entries_per_sec, firstent; 61 daddr_t slba; 62 63 /* 64 * We need to update the following for both primary and backup GPT: 65 * 1. Sector on disk that contains current partition. 66 * 2. Partition table checksum. 67 * 3. Header checksum. 68 * 4. Header on disk. 69 */ 70 71 entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 72 slba = curent / entries_per_sec; 73 firstent = slba * entries_per_sec; 74 bcopy(&table[firstent], secbuf, DEV_BSIZE); 75 slba += hdr->hdr_lba_table; 76 if (drvwrite(dskp, secbuf, slba, 1)) { 77 printf("%s: unable to update %s GPT partition table\n", 78 BOOTPROG, which); 79 return; 80 } 81 hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); 82 hdr->hdr_crc_self = 0; 83 hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); 84 bzero(secbuf, DEV_BSIZE); 85 bcopy(hdr, secbuf, hdr->hdr_size); 86 if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) { 87 printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 88 return; 89 } 90} 91 92int 93gptfind(const uuid_t *uuid, struct dsk *dskp, int part) 94{ 95 struct gpt_ent *ent; 96 int firsttry; 97 98 if (part >= 0) { 99 if (part == 0 || part > gpthdr->hdr_entries) { 100 printf("%s: invalid partition index\n", BOOTPROG); 101 return (-1); 102 } 103 ent = &gpttable[part - 1]; 104 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) { 105 printf("%s: specified partition is not UFS\n", 106 BOOTPROG); 107 return (-1); 108 } 109 curent = part - 1; 110 goto found; 111 } 112 113 firsttry = (curent == -1); 114 curent++; 115 if (curent >= gpthdr->hdr_entries) { 116 curent = gpthdr->hdr_entries; 117 return (-1); 118 } 119 if (bootonce) { 120 /* 121 * First look for partition with both GPT_ENT_ATTR_BOOTME and 122 * GPT_ENT_ATTR_BOOTONCE flags. 123 */ 124 for (; curent < gpthdr->hdr_entries; curent++) { 125 ent = &gpttable[curent]; 126 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 127 continue; 128 if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 129 continue; 130 if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)) 131 continue; 132 /* Ok, found one. */ 133 goto found; 134 } 135 bootonce = 0; 136 curent = 0; 137 } 138 for (; curent < gpthdr->hdr_entries; curent++) { 139 ent = &gpttable[curent]; 140 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 141 continue; 142 if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 143 continue; 144 if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) 145 continue; 146 /* Ok, found one. */ 147 goto found; 148 } 149 if (firsttry) { 150 /* 151 * No partition with BOOTME flag was found, try to boot from 152 * first UFS partition. 153 */ 154 for (curent = 0; curent < gpthdr->hdr_entries; curent++) { 155 ent = &gpttable[curent]; 156 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 157 continue; 158 /* Ok, found one. */ 159 goto found; 160 } 161 } 162 return (-1); 163found: 164 dskp->part = curent + 1; 165 ent = &gpttable[curent]; 166 dskp->start = ent->ent_lba_start; 167 if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) { 168 /* 169 * Clear BOOTME, but leave BOOTONCE set before trying to 170 * boot from this partition. 171 */ 172 if (hdr_primary_lba > 0) { 173 table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 174 gptupdate("primary", dskp, &hdr_primary, table_primary); 175 } 176 if (hdr_backup_lba > 0) { 177 table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 178 gptupdate("backup", dskp, &hdr_backup, table_backup); 179 } 180 } 181 return (0); 182} 183 184static int 185gptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 186 uint64_t hdrlba) 187{ 188 uint32_t crc; 189 190 if (drvread(dskp, secbuf, hdrlba, 1)) { 191 printf("%s: unable to read %s GPT header\n", BOOTPROG, which); 192 return (-1); 193 } 194 bcopy(secbuf, hdr, sizeof(*hdr)); 195 if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || 196 hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 || 197 hdr->hdr_entsz < sizeof(struct gpt_ent) || 198 hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) { 199 printf("%s: invalid %s GPT header\n", BOOTPROG, which); 200 return (-1); 201 } 202 crc = hdr->hdr_crc_self; 203 hdr->hdr_crc_self = 0; 204 if (crc32(hdr, hdr->hdr_size) != crc) { 205 printf("%s: %s GPT header checksum mismatch\n", BOOTPROG, 206 which); 207 return (-1); 208 } 209 hdr->hdr_crc_self = crc; 210 return (0); 211} 212 213void 214gptbootfailed(struct dsk *dskp) 215{ 216 217 if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE)) 218 return; 219 220 if (hdr_primary_lba > 0) { 221 table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 222 table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 223 gptupdate("primary", dskp, &hdr_primary, table_primary); 224 } 225 if (hdr_backup_lba > 0) { 226 table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 227 table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 228 gptupdate("backup", dskp, &hdr_backup, table_backup); 229 } 230} 231 232static void 233gptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 234 struct gpt_ent *table) 235{ 236 struct gpt_ent *ent; 237 daddr_t slba; 238 int table_updated, sector_updated; 239 int entries_per_sec, nent, part; 240 241 table_updated = 0; 242 entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 243 for (nent = 0, slba = hdr->hdr_lba_table; 244 slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; 245 slba++, nent += entries_per_sec) { 246 sector_updated = 0; 247 for (part = 0; part < entries_per_sec; part++) { 248 ent = &table[nent + part]; 249 if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME | 250 GPT_ENT_ATTR_BOOTONCE | 251 GPT_ENT_ATTR_BOOTFAILED)) != 252 GPT_ENT_ATTR_BOOTONCE) { 253 continue; 254 } 255 ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 256 ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 257 table_updated = 1; 258 sector_updated = 1; 259 } 260 if (!sector_updated) 261 continue; 262 bcopy(&table[nent], secbuf, DEV_BSIZE); 263 if (drvwrite(dskp, secbuf, slba, 1)) { 264 printf("%s: unable to update %s GPT partition table\n", 265 BOOTPROG, which); 266 } 267 } 268 if (!table_updated) 269 return; 270 hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); 271 hdr->hdr_crc_self = 0; 272 hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); 273 bzero(secbuf, DEV_BSIZE); 274 bcopy(hdr, secbuf, hdr->hdr_size); 275 if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) 276 printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 277} 278 279static int 280gptread_table(const char *which, const uuid_t *uuid, struct dsk *dskp, 281 struct gpt_hdr *hdr, struct gpt_ent *table) 282{ 283 struct gpt_ent *ent; 284 int entries_per_sec; 285 int part, nent; 286 daddr_t slba; 287 288 if (hdr->hdr_entries == 0) 289 return (0); 290 291 entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 292 slba = hdr->hdr_lba_table; 293 nent = 0; 294 for (;;) { 295 if (drvread(dskp, secbuf, slba, 1)) { 296 printf("%s: unable to read %s GPT partition table\n", 297 BOOTPROG, which); 298 return (-1); 299 } 300 ent = (struct gpt_ent *)secbuf; 301 for (part = 0; part < entries_per_sec; part++, ent++) { 302 bcopy(ent, &table[nent], sizeof(table[nent])); 303 if (++nent >= hdr->hdr_entries) 304 break; 305 } 306 if (nent >= hdr->hdr_entries) 307 break; 308 slba++; 309 } 310 if (crc32(table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) { 311 printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which); 312 return (-1); 313 } 314 return (0); 315} 316 317int 318gptread(const uuid_t *uuid, struct dsk *dskp, char *buf) 319{ 320 uint64_t altlba; 321 322 /* 323 * Read and verify both GPT headers: primary and backup. 324 */ 325 326 secbuf = buf; 327 hdr_primary_lba = hdr_backup_lba = 0; 328 curent = -1; 329 bootonce = 1; 330 dskp->start = 0; 331 332 if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 && 333 gptread_table("primary", uuid, dskp, &hdr_primary, 334 table_primary) == 0) { 335 hdr_primary_lba = hdr_primary.hdr_lba_self; 336 gpthdr = &hdr_primary; 337 gpttable = table_primary; 338 } 339 340 altlba = drvsize(dskp); 341 if (altlba > 0) 342 altlba--; 343 else if (hdr_primary_lba > 0) { 344 /* 345 * If we cannot obtain disk size, but primary header 346 * is valid, we can get backup header location from 347 * there. 348 */ 349 altlba = hdr_primary.hdr_lba_alt; 350 } 351 if (altlba == 0) 352 printf("%s: unable to locate backup GPT header\n", BOOTPROG); 353 else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 && 354 gptread_table("backup", uuid, dskp, &hdr_backup, 355 table_backup) == 0) { 356 hdr_backup_lba = hdr_backup.hdr_lba_self; 357 if (hdr_primary_lba == 0) { 358 gpthdr = &hdr_backup; 359 gpttable = table_backup; 360 printf("%s: using backup GPT\n", BOOTPROG); 361 } 362 } 363 364 /* 365 * Convert all BOOTONCE without BOOTME flags into BOOTFAILED. 366 * BOOTONCE without BOOTME means that we tried to boot from it, 367 * but failed after leaving gptboot and machine was rebooted. 368 * We don't want to leave partitions marked as BOOTONCE only, 369 * because when we boot successfully start-up scripts should 370 * find at most one partition with only BOOTONCE flag and this 371 * will mean that we booted from that partition. 372 */ 373 if (hdr_primary_lba != 0) 374 gptbootconv("primary", dskp, &hdr_primary, table_primary); 375 if (hdr_backup_lba != 0) 376 gptbootconv("backup", dskp, &hdr_backup, table_backup); 377 378 if (hdr_primary_lba == 0 && hdr_backup_lba == 0) 379 return (-1); 380 return (0); 381} 382