gpt.c revision 219083
150276Speter/*- 2184989Srafan * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 350276Speter * All rights reserved. 450276Speter * 550276Speter * Redistribution and use in source and binary forms, with or without 650276Speter * modification, are permitted provided that the following conditions 750276Speter * are met: 850276Speter * 1. Redistributions of source code must retain the above copyright 950276Speter * notice, this list of conditions and the following disclaimer. 1050276Speter * 2. Redistributions in binary form must reproduce the above copyright 1150276Speter * notice, this list of conditions and the following disclaimer in the 1250276Speter * documentation and/or other materials provided with the distribution. 1350276Speter * 1450276Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 1550276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750276Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 1850276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2250276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450276Speter * SUCH DAMAGE. 2550276Speter */ 2650276Speter 2750276Speter#include <sys/cdefs.h> 2850276Speter__FBSDID("$FreeBSD: head/sys/boot/common/gpt.c 219083 2011-02-27 12:25:47Z pjd $"); 2950276Speter 3050276Speter#include <sys/param.h> 3150276Speter#include <sys/gpt.h> 32166124Srafan 3350276Speter#ifndef LITTLE_ENDIAN 3450276Speter#error gpt.c works only for little endian architectures 3550276Speter#endif 3650276Speter 3750276Speter#include "crc32.h" 3850276Speter#include "drv.h" 3950276Speter#include "util.h" 4050276Speter#include "gpt.h" 4150276Speter 4250276Speter#define MAXTBLENTS 128 4350276Speter 4450276Speterstatic struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; 4550276Speterstatic uint64_t hdr_primary_lba, hdr_backup_lba; 4650276Speterstatic struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; 4750276Speterstatic struct gpt_ent *gpttable; 4850276Speterstatic int curent, bootonce; 4950276Speter 5050276Speter/* 5150276Speter * Buffer below 64kB passed on gptread(), which can hold at least 5250276Speter * one sector of data (512 bytes). 5350276Speter */ 5450276Speterstatic char *secbuf; 5550276Speter 5650276Speterstatic void 5750276Spetergptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 5850276Speter struct gpt_ent *table) 5950276Speter{ 6050276Speter int entries_per_sec, firstent; 6150276Speter daddr_t slba; 6250276Speter 6350276Speter /* 6450276Speter * We need to update the following for both primary and backup GPT: 6550276Speter * 1. Sector on disk that contains current partition. 6650276Speter * 2. Partition table checksum. 6750276Speter * 3. Header checksum. 6850276Speter * 4. Header on disk. 6950276Speter */ 7050276Speter 7150276Speter entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 7250276Speter slba = curent / entries_per_sec; 7350276Speter firstent = slba * entries_per_sec; 7450276Speter bcopy(&table[firstent], secbuf, DEV_BSIZE); 7550276Speter slba += hdr->hdr_lba_table; 76174993Srafan if (drvwrite(dskp, secbuf, slba, 1)) { 7750276Speter printf("%s: unable to update %s GPT partition table\n", 7850276Speter BOOTPROG, which); 7950276Speter return; 8050276Speter } 8150276Speter hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); 8250276Speter hdr->hdr_crc_self = 0; 8350276Speter hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); 8450276Speter bzero(secbuf, DEV_BSIZE); 8550276Speter bcopy(hdr, secbuf, hdr->hdr_size); 8650276Speter if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) { 8750276Speter printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 8850276Speter return; 8950276Speter } 9050276Speter} 9150276Speter 92184989Srafanint 93184989Srafangptfind(const uuid_t *uuid, struct dsk *dskp, int part) 94184989Srafan{ 95184989Srafan struct gpt_ent *ent; 96184989Srafan int firsttry; 97184989Srafan 9850276Speter if (part >= 0) { 9950276Speter if (part == 0 || part > gpthdr->hdr_entries) { 10050276Speter printf("%s: invalid partition index\n", BOOTPROG); 10150276Speter return (-1); 10250276Speter } 10397049Speter ent = &gpttable[part - 1]; 10497049Speter if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) { 10550276Speter printf("%s: specified partition is not UFS\n", 10650276Speter BOOTPROG); 10750276Speter return (-1); 10866963Speter } 10950276Speter curent = part - 1; 110184989Srafan goto found; 11150276Speter } 112184989Srafan 113184989Srafan firsttry = (curent == -1); 114184989Srafan curent++; 115184989Srafan if (curent >= gpthdr->hdr_entries) { 116184989Srafan curent = gpthdr->hdr_entries; 117184989Srafan return (-1); 118184989Srafan } 119184989Srafan if (bootonce) { 120184989Srafan /* 121184989Srafan * First look for partition with both GPT_ENT_ATTR_BOOTME and 122184989Srafan * GPT_ENT_ATTR_BOOTONCE flags. 123184989Srafan */ 124184989Srafan for (; curent < gpthdr->hdr_entries; curent++) { 125184989Srafan ent = &gpttable[curent]; 126184989Srafan if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 127184989Srafan continue; 128184989Srafan if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 129184989Srafan continue; 130184989Srafan if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)) 131184989Srafan continue; 13250276Speter /* Ok, found one. */ 13350276Speter goto found; 13450276Speter } 13550276Speter bootonce = 0; 13650276Speter curent = 0; 13750276Speter } 13850276Speter for (; curent < gpthdr->hdr_entries; curent++) { 13997049Speter ent = &gpttable[curent]; 14050276Speter if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 141166124Srafan continue; 142166124Srafan if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 143166124Srafan continue; 14497049Speter if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) 14566963Speter continue; 14662449Speter /* Ok, found one. */ 14762449Speter goto found; 14862449Speter } 14962449Speter if (firsttry) { 15050276Speter /* 15197049Speter * No partition with BOOTME flag was found, try to boot from 15250276Speter * first UFS partition. 15350276Speter */ 15462449Speter for (curent = 0; curent < gpthdr->hdr_entries; curent++) { 15562449Speter ent = &gpttable[curent]; 15662449Speter if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 15762449Speter continue; 15862449Speter /* Ok, found one. */ 15962449Speter goto found; 16062449Speter } 16162449Speter } 16262449Speter return (-1); 16350276Speterfound: 16450276Speter dskp->part = curent + 1; 16550276Speter ent = &gpttable[curent]; 16697049Speter dskp->start = ent->ent_lba_start; 16797049Speter if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) { 16897049Speter /* 16997049Speter * Clear BOOTME, but leave BOOTONCE set before trying to 17097049Speter * boot from this partition. 17197049Speter */ 172166124Srafan if (hdr_primary_lba > 0) { 17397049Speter table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 17497049Speter gptupdate("primary", dskp, &hdr_primary, table_primary); 17597049Speter } 17697049Speter if (hdr_backup_lba > 0) { 17762449Speter table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 17850276Speter gptupdate("backup", dskp, &hdr_backup, table_backup); 17962449Speter } 18062449Speter } 181166124Srafan return (0); 18262449Speter} 18362449Speter 18497049Speterstatic int 18562449Spetergptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 18650276Speter uint64_t hdrlba) 18750276Speter{ 18850276Speter uint32_t crc; 18950276Speter 19050276Speter if (drvread(dskp, secbuf, hdrlba, 1)) { 19162449Speter printf("%s: unable to read %s GPT header\n", BOOTPROG, which); 192166124Srafan return (-1); 193166124Srafan } 194184989Srafan bcopy(secbuf, hdr, sizeof(*hdr)); 195166124Srafan if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || 196166124Srafan hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 || 197166124Srafan hdr->hdr_entsz < sizeof(struct gpt_ent) || 198166124Srafan hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) { 199166124Srafan printf("%s: invalid %s GPT header\n", BOOTPROG, which); 200166124Srafan return (-1); 20197049Speter } 20262449Speter crc = hdr->hdr_crc_self; 20350276Speter hdr->hdr_crc_self = 0; 20450276Speter if (crc32(hdr, hdr->hdr_size) != crc) { 20550276Speter printf("%s: %s GPT header checksum mismatch\n", BOOTPROG, 20650276Speter which); 20750276Speter return (-1); 20897049Speter } 20997049Speter hdr->hdr_crc_self = crc; 21062449Speter return (0); 21150276Speter} 21297049Speter 21362449Spetervoid 21450276Spetergptbootfailed(struct dsk *dskp) 21597049Speter{ 21697049Speter 21797049Speter if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE)) 21897049Speter return; 21950276Speter 22050276Speter if (hdr_primary_lba > 0) { 22150276Speter table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 22250276Speter table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 22350276Speter gptupdate("primary", dskp, &hdr_primary, table_primary); 22462449Speter } 22550276Speter if (hdr_backup_lba > 0) { 22650276Speter table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 22750276Speter table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 22850276Speter gptupdate("backup", dskp, &hdr_backup, table_backup); 22950276Speter } 23050276Speter} 23162449Speter 23262449Speterstatic void 23350276Spetergptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 23462449Speter struct gpt_ent *table) 235166124Srafan{ 23662449Speter struct gpt_ent *ent; 23762449Speter daddr_t slba; 23897049Speter int table_updated, sector_updated; 23997049Speter int entries_per_sec, nent, part; 24062449Speter 24162449Speter table_updated = 0; 24262449Speter entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 24362449Speter for (nent = 0, slba = hdr->hdr_lba_table; 24462449Speter slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; 24562449Speter slba++, nent += entries_per_sec) { 24662449Speter sector_updated = 0; 24762449Speter for (part = 0; part < entries_per_sec; part++) { 24862449Speter ent = &table[nent + part]; 24962449Speter if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME | 25097049Speter GPT_ENT_ATTR_BOOTONCE | 25197049Speter GPT_ENT_ATTR_BOOTFAILED)) != 25262449Speter GPT_ENT_ATTR_BOOTONCE) { 25362449Speter continue; 25450276Speter } 25550276Speter ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 25662449Speter ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 25762449Speter table_updated = 1; 25862449Speter sector_updated = 1; 25962449Speter } 26062449Speter if (!sector_updated) 26162449Speter continue; 26262449Speter bcopy(&table[nent], secbuf, DEV_BSIZE); 26350276Speter if (drvwrite(dskp, secbuf, slba, 1)) { 26450276Speter printf("%s: unable to update %s GPT partition table\n", 26550276Speter BOOTPROG, which); 26650276Speter } 26750276Speter } 26850276Speter if (!table_updated) 26950276Speter return; 27050276Speter hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); 27150276Speter hdr->hdr_crc_self = 0; 27250276Speter hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); 27350276Speter bzero(secbuf, DEV_BSIZE); 27450276Speter bcopy(hdr, secbuf, hdr->hdr_size); 27550276Speter if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) 27650276Speter printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 27750276Speter} 27850276Speter 27950276Speterstatic int 28062449Spetergptread_table(const char *which, const uuid_t *uuid, struct dsk *dskp, 28162449Speter struct gpt_hdr *hdr, struct gpt_ent *table) 28262449Speter{ 28362449Speter struct gpt_ent *ent; 28466963Speter int entries_per_sec; 28550276Speter int part, nent; 28650276Speter daddr_t slba; 28750276Speter 28850276Speter if (hdr->hdr_entries == 0) 28950276Speter return (0); 29062449Speter 29162449Speter entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 29250276Speter slba = hdr->hdr_lba_table; 29350276Speter nent = 0; 29462449Speter for (;;) { 29562449Speter if (drvread(dskp, secbuf, slba, 1)) { 29662449Speter printf("%s: unable to read %s GPT partition table\n", 29762449Speter BOOTPROG, which); 29862449Speter return (-1); 29962449Speter } 30062449Speter ent = (struct gpt_ent *)secbuf; 30162449Speter for (part = 0; part < entries_per_sec; part++, ent++) { 30262449Speter bcopy(ent, &table[nent], sizeof(table[nent])); 30362449Speter if (++nent >= hdr->hdr_entries) 30462449Speter break; 30562449Speter } 30662449Speter if (nent >= hdr->hdr_entries) 30762449Speter break; 30862449Speter slba++; 30962449Speter } 31062449Speter if (crc32(table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) { 31166963Speter printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which); 31266963Speter return (-1); 31362449Speter } 31466963Speter return (0); 31566963Speter} 31662449Speter 31766963Speterint 31866963Spetergptread(const uuid_t *uuid, struct dsk *dskp, char *buf) 31962449Speter{ 32066963Speter uint64_t altlba; 32166963Speter 32262449Speter /* 32366963Speter * Read and verify both GPT headers: primary and backup. 32450276Speter */ 32562449Speter 32650276Speter secbuf = buf; 32750276Speter hdr_primary_lba = hdr_backup_lba = 0; 32862449Speter curent = -1; 32950276Speter bootonce = 1; 33050276Speter dskp->start = 0; 33150276Speter 33262449Speter if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 && 33350276Speter gptread_table("primary", uuid, dskp, &hdr_primary, 33450276Speter table_primary) == 0) { 33562449Speter hdr_primary_lba = hdr_primary.hdr_lba_self; 33650276Speter gpthdr = &hdr_primary; 33750276Speter gpttable = table_primary; 33850276Speter } 33962449Speter 34050276Speter altlba = drvsize(dskp); 34150276Speter if (altlba > 0) 34262449Speter altlba--; 34350276Speter else if (hdr_primary_lba > 0) { 34450276Speter /* 34562449Speter * If we cannot obtain disk size, but primary header 34650276Speter * is valid, we can get backup header location from 34750276Speter * there. 34862449Speter */ 34950276Speter altlba = hdr_primary.hdr_lba_alt; 35062449Speter } 35150276Speter if (altlba == 0) 35250276Speter printf("%s: unable to locate backup GPT header\n", BOOTPROG); 35350276Speter else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 && 35450276Speter gptread_table("backup", uuid, dskp, &hdr_backup, 35550276Speter table_backup) == 0) { 35662449Speter hdr_backup_lba = hdr_backup.hdr_lba_self; 35762449Speter if (hdr_primary_lba == 0) { 35850276Speter gpthdr = &hdr_backup; 35962449Speter gpttable = table_backup; 36062449Speter printf("%s: using backup GPT\n", BOOTPROG); 36162449Speter } 36250276Speter } 36362449Speter 36462449Speter /* 36562449Speter * Convert all BOOTONCE without BOOTME flags into BOOTFAILED. 36662449Speter * BOOTONCE without BOOTME means that we tried to boot from it, 36750276Speter * but failed after leaving gptboot and machine was rebooted. 36862449Speter * We don't want to leave partitions marked as BOOTONCE only, 36962449Speter * because when we boot successfully start-up scripts should 37062449Speter * find at most one partition with only BOOTONCE flag and this 37162449Speter * will mean that we booted from that partition. 37250276Speter */ 37350276Speter if (hdr_primary_lba != 0) 37450276Speter gptbootconv("primary", dskp, &hdr_primary, table_primary); 37550276Speter if (hdr_backup_lba != 0) 37650276Speter gptbootconv("backup", dskp, &hdr_backup, table_backup); 37750276Speter 37850276Speter if (hdr_primary_lba == 0 && hdr_backup_lba == 0) 37950276Speter return (-1); 38050276Speter return (0); 38150276Speter} 38262449Speter