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 #include <sys/param.h> 29 #include <sys/gpt.h> 30 31 #ifndef LITTLE_ENDIAN 32 #error gpt.c works only for little endian architectures 33 #endif 34 35 #include "stand.h" 36 #include "zlib.h" 37 #include "drv.h" 38 #include "gpt.h" 39 40 static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; 41 static uint64_t hdr_primary_lba, hdr_backup_lba; 42 static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; 43 static struct gpt_ent *gpttable; 44 static int curent, bootonce; 45 46 /* 47 * Buffer below 64kB passed on gptread(), which can hold at least 48 * one sector of data (512 bytes). 49 */ 50 static char *secbuf; 51 52 static void 53 gptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 54 struct gpt_ent *table) 55 { 56 int entries_per_sec, firstent; 57 daddr_t slba; 58 59 /* 60 * We need to update the following for both primary and backup GPT: 61 * 1. Sector on disk that contains current partition. 62 * 2. Partition table checksum. 63 * 3. Header checksum. 64 * 4. Header on disk. 65 */ 66 67 entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 68 slba = curent / entries_per_sec; 69 firstent = slba * entries_per_sec; 70 bcopy(&table[firstent], secbuf, DEV_BSIZE); 71 slba += hdr->hdr_lba_table; 72 if (drvwrite(dskp, secbuf, slba, 1)) { 73 printf("%s: unable to update %s GPT partition table\n", 74 BOOTPROG, which); 75 return; 76 } 77 hdr->hdr_crc_table = crc32(0, Z_NULL, 0); 78 hdr->hdr_crc_table = crc32(hdr->hdr_crc_table, (const Bytef *)table, 79 hdr->hdr_entries * hdr->hdr_entsz); 80 hdr->hdr_crc_self = crc32(0, Z_NULL, 0); 81 hdr->hdr_crc_self = crc32(hdr->hdr_crc_self, (const Bytef *)hdr, 82 hdr->hdr_size); 83 bzero(secbuf, DEV_BSIZE); 84 bcopy(hdr, secbuf, hdr->hdr_size); 85 if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) { 86 printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 87 return; 88 } 89 } 90 91 int 92 gptfind(const uuid_t *uuid, struct dsk *dskp, int part) 93 { 94 struct gpt_ent *ent; 95 int firsttry; 96 97 if (part >= 0) { 98 if (part == 0 || part > gpthdr->hdr_entries) { 99 printf("%s: invalid partition index\n", BOOTPROG); 100 return (-1); 101 } 102 ent = &gpttable[part - 1]; 103 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) { 104 printf("%s: specified partition is not UFS\n", 105 BOOTPROG); 106 return (-1); 107 } 108 curent = part - 1; 109 goto found; 110 } 111 112 firsttry = (curent == -1); 113 curent++; 114 if (curent >= gpthdr->hdr_entries) { 115 curent = gpthdr->hdr_entries; 116 return (-1); 117 } 118 if (bootonce) { 119 /* 120 * First look for partition with both GPT_ENT_ATTR_BOOTME and 121 * GPT_ENT_ATTR_BOOTONCE flags. 122 */ 123 for (; curent < gpthdr->hdr_entries; curent++) { 124 ent = &gpttable[curent]; 125 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 126 continue; 127 if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 128 continue; 129 if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)) 130 continue; 131 /* Ok, found one. */ 132 goto found; 133 } 134 bootonce = 0; 135 curent = 0; 136 } 137 for (; curent < gpthdr->hdr_entries; curent++) { 138 ent = &gpttable[curent]; 139 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 140 continue; 141 if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 142 continue; 143 if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) 144 continue; 145 /* Ok, found one. */ 146 goto found; 147 } 148 if (firsttry) { 149 /* 150 * No partition with BOOTME flag was found, try to boot from 151 * first UFS partition. 152 */ 153 for (curent = 0; curent < gpthdr->hdr_entries; curent++) { 154 ent = &gpttable[curent]; 155 if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 156 continue; 157 /* Ok, found one. */ 158 goto found; 159 } 160 } 161 return (-1); 162 found: 163 dskp->part = curent + 1; 164 ent = &gpttable[curent]; 165 dskp->start = ent->ent_lba_start; 166 if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) { 167 /* 168 * Clear BOOTME, but leave BOOTONCE set before trying to 169 * boot from this partition. 170 */ 171 if (hdr_primary_lba > 0) { 172 table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 173 gptupdate("primary", dskp, &hdr_primary, table_primary); 174 } 175 if (hdr_backup_lba > 0) { 176 table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 177 gptupdate("backup", dskp, &hdr_backup, table_backup); 178 } 179 } 180 return (0); 181 } 182 183 static int 184 gptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 185 uint64_t hdrlba) 186 { 187 uint32_t crc; 188 189 if (drvread(dskp, secbuf, hdrlba, 1)) { 190 printf("%s: unable to read %s GPT header\n", BOOTPROG, which); 191 return (-1); 192 } 193 bcopy(secbuf, hdr, sizeof(*hdr)); 194 if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || 195 hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 || 196 hdr->hdr_entsz < sizeof(struct gpt_ent) || 197 hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) { 198 printf("%s: invalid %s GPT header\n", BOOTPROG, which); 199 return (-1); 200 } 201 crc = hdr->hdr_crc_self; 202 hdr->hdr_crc_self = crc32(0, Z_NULL, 0); 203 if (crc32(hdr->hdr_crc_self, (const Bytef *)hdr, hdr->hdr_size) != 204 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 213 void 214 gptbootfailed(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 232 static void 233 gptbootconv(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(0, Z_NULL, 0); 271 hdr->hdr_crc_table = crc32(hdr->hdr_crc_table, (const Bytef *)table, 272 hdr->hdr_entries * hdr->hdr_entsz); 273 hdr->hdr_crc_self = crc32(0, Z_NULL, 0); 274 hdr->hdr_crc_self = crc32(hdr->hdr_crc_self, (const Bytef *)hdr, 275 hdr->hdr_size); 276 bzero(secbuf, DEV_BSIZE); 277 bcopy(hdr, secbuf, hdr->hdr_size); 278 if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) 279 printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 280 } 281 282 static int 283 gptread_table(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 284 struct gpt_ent *table) 285 { 286 struct gpt_ent *ent; 287 int entries_per_sec; 288 int part, nent; 289 daddr_t slba; 290 291 if (hdr->hdr_entries == 0) 292 return (0); 293 294 entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 295 slba = hdr->hdr_lba_table; 296 nent = 0; 297 for (;;) { 298 if (drvread(dskp, secbuf, slba, 1)) { 299 printf("%s: unable to read %s GPT partition table\n", 300 BOOTPROG, which); 301 return (-1); 302 } 303 ent = (struct gpt_ent *)secbuf; 304 for (part = 0; part < entries_per_sec; part++, ent++) { 305 bcopy(ent, &table[nent], sizeof(table[nent])); 306 if (++nent >= hdr->hdr_entries) 307 break; 308 } 309 if (nent >= hdr->hdr_entries) 310 break; 311 slba++; 312 } 313 if (crc32(0, (const Bytef *)table, nent * hdr->hdr_entsz) != 314 hdr->hdr_crc_table) { 315 printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which); 316 return (-1); 317 } 318 return (0); 319 } 320 321 int 322 gptread(struct dsk *dskp, char *buf) 323 { 324 uint64_t altlba; 325 326 /* 327 * Read and verify both GPT headers: primary and backup. 328 */ 329 330 secbuf = buf; 331 hdr_primary_lba = hdr_backup_lba = 0; 332 curent = -1; 333 bootonce = 1; 334 dskp->start = 0; 335 336 if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 && 337 gptread_table("primary", dskp, &hdr_primary, table_primary) == 0) { 338 hdr_primary_lba = hdr_primary.hdr_lba_self; 339 gpthdr = &hdr_primary; 340 gpttable = table_primary; 341 } 342 343 if (hdr_primary_lba > 0) { 344 /* 345 * If primary header is valid, we can get backup 346 * header location from there. 347 */ 348 altlba = hdr_primary.hdr_lba_alt; 349 } else { 350 altlba = drvsize(dskp); 351 if (altlba > 0) 352 altlba--; 353 } 354 if (altlba == 0) 355 printf("%s: unable to locate backup GPT header\n", BOOTPROG); 356 else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 && 357 gptread_table("backup", dskp, &hdr_backup, table_backup) == 0) { 358 hdr_backup_lba = hdr_backup.hdr_lba_self; 359 if (hdr_primary_lba == 0) { 360 gpthdr = &hdr_backup; 361 gpttable = table_backup; 362 printf("%s: using backup GPT\n", BOOTPROG); 363 } 364 } 365 366 /* 367 * Convert all BOOTONCE without BOOTME flags into BOOTFAILED. 368 * BOOTONCE without BOOTME means that we tried to boot from it, 369 * but failed after leaving gptboot and machine was rebooted. 370 * We don't want to leave partitions marked as BOOTONCE only, 371 * because when we boot successfully start-up scripts should 372 * find at most one partition with only BOOTONCE flag and this 373 * will mean that we booted from that partition. 374 */ 375 if (hdr_primary_lba != 0) 376 gptbootconv("primary", dskp, &hdr_primary, table_primary); 377 if (hdr_backup_lba != 0) 378 gptbootconv("backup", dskp, &hdr_backup, table_backup); 379 380 if (hdr_primary_lba == 0 && hdr_backup_lba == 0) 381 return (-1); 382 return (0); 383 } 384