1213136Spjd/*- 2213136Spjd * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3213136Spjd * All rights reserved. 4213136Spjd * 5213136Spjd * Redistribution and use in source and binary forms, with or without 6213136Spjd * modification, are permitted provided that the following conditions 7213136Spjd * are met: 8213136Spjd * 1. Redistributions of source code must retain the above copyright 9213136Spjd * notice, this list of conditions and the following disclaimer. 10213136Spjd * 2. Redistributions in binary form must reproduce the above copyright 11213136Spjd * notice, this list of conditions and the following disclaimer in the 12213136Spjd * documentation and/or other materials provided with the distribution. 13213136Spjd * 14213136Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15213136Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16213136Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17213136Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18213136Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19213136Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20213136Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21213136Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22213136Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213136Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213136Spjd * SUCH DAMAGE. 25213136Spjd */ 26213136Spjd 27213136Spjd#include <sys/cdefs.h> 28213136Spjd__FBSDID("$FreeBSD: stable/11/stand/libsa/gpt.c 329175 2018-02-12 17:44:35Z kevans $"); 29213136Spjd 30213136Spjd#include <sys/param.h> 31213136Spjd#include <sys/gpt.h> 32213136Spjd 33213136Spjd#ifndef LITTLE_ENDIAN 34213136Spjd#error gpt.c works only for little endian architectures 35213136Spjd#endif 36213136Spjd 37329175Skevans#include "stand.h" 38213136Spjd#include "crc32.h" 39213136Spjd#include "drv.h" 40213136Spjd#include "gpt.h" 41213136Spjd 42213136Spjdstatic struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; 43213136Spjdstatic uint64_t hdr_primary_lba, hdr_backup_lba; 44213136Spjdstatic struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; 45213136Spjdstatic struct gpt_ent *gpttable; 46213136Spjdstatic int curent, bootonce; 47213136Spjd 48213136Spjd/* 49213136Spjd * Buffer below 64kB passed on gptread(), which can hold at least 50214114Spjd * one sector of data (512 bytes). 51213136Spjd */ 52213136Spjdstatic char *secbuf; 53213136Spjd 54213136Spjdstatic void 55213136Spjdgptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 56213136Spjd struct gpt_ent *table) 57213136Spjd{ 58213136Spjd int entries_per_sec, firstent; 59213136Spjd daddr_t slba; 60213136Spjd 61213136Spjd /* 62213136Spjd * We need to update the following for both primary and backup GPT: 63214114Spjd * 1. Sector on disk that contains current partition. 64213136Spjd * 2. Partition table checksum. 65213136Spjd * 3. Header checksum. 66213136Spjd * 4. Header on disk. 67213136Spjd */ 68213136Spjd 69213136Spjd entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 70213136Spjd slba = curent / entries_per_sec; 71213136Spjd firstent = slba * entries_per_sec; 72219083Spjd bcopy(&table[firstent], secbuf, DEV_BSIZE); 73213136Spjd slba += hdr->hdr_lba_table; 74213136Spjd if (drvwrite(dskp, secbuf, slba, 1)) { 75213136Spjd printf("%s: unable to update %s GPT partition table\n", 76213136Spjd BOOTPROG, which); 77213136Spjd return; 78213136Spjd } 79213136Spjd hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); 80213136Spjd hdr->hdr_crc_self = 0; 81213136Spjd hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); 82213136Spjd bzero(secbuf, DEV_BSIZE); 83219083Spjd bcopy(hdr, secbuf, hdr->hdr_size); 84213136Spjd if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) { 85213136Spjd printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 86213136Spjd return; 87213136Spjd } 88213136Spjd} 89213136Spjd 90213136Spjdint 91213136Spjdgptfind(const uuid_t *uuid, struct dsk *dskp, int part) 92213136Spjd{ 93213136Spjd struct gpt_ent *ent; 94213136Spjd int firsttry; 95213136Spjd 96213136Spjd if (part >= 0) { 97213136Spjd if (part == 0 || part > gpthdr->hdr_entries) { 98213136Spjd printf("%s: invalid partition index\n", BOOTPROG); 99213136Spjd return (-1); 100213136Spjd } 101213136Spjd ent = &gpttable[part - 1]; 102213136Spjd if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) { 103213136Spjd printf("%s: specified partition is not UFS\n", 104213136Spjd BOOTPROG); 105213136Spjd return (-1); 106213136Spjd } 107213136Spjd curent = part - 1; 108213136Spjd goto found; 109213136Spjd } 110213136Spjd 111213136Spjd firsttry = (curent == -1); 112213136Spjd curent++; 113213136Spjd if (curent >= gpthdr->hdr_entries) { 114213136Spjd curent = gpthdr->hdr_entries; 115213136Spjd return (-1); 116213136Spjd } 117213136Spjd if (bootonce) { 118213136Spjd /* 119213136Spjd * First look for partition with both GPT_ENT_ATTR_BOOTME and 120213136Spjd * GPT_ENT_ATTR_BOOTONCE flags. 121213136Spjd */ 122213136Spjd for (; curent < gpthdr->hdr_entries; curent++) { 123213136Spjd ent = &gpttable[curent]; 124213136Spjd if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 125213136Spjd continue; 126213136Spjd if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 127213136Spjd continue; 128213136Spjd if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)) 129213136Spjd continue; 130213136Spjd /* Ok, found one. */ 131213136Spjd goto found; 132213136Spjd } 133213136Spjd bootonce = 0; 134213136Spjd curent = 0; 135213136Spjd } 136213136Spjd for (; curent < gpthdr->hdr_entries; curent++) { 137213136Spjd ent = &gpttable[curent]; 138213136Spjd if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 139213136Spjd continue; 140213136Spjd if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) 141213136Spjd continue; 142213136Spjd if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) 143213136Spjd continue; 144213136Spjd /* Ok, found one. */ 145213136Spjd goto found; 146213136Spjd } 147213136Spjd if (firsttry) { 148213136Spjd /* 149213136Spjd * No partition with BOOTME flag was found, try to boot from 150213136Spjd * first UFS partition. 151213136Spjd */ 152213136Spjd for (curent = 0; curent < gpthdr->hdr_entries; curent++) { 153213136Spjd ent = &gpttable[curent]; 154213136Spjd if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) 155213136Spjd continue; 156213136Spjd /* Ok, found one. */ 157213136Spjd goto found; 158213136Spjd } 159213136Spjd } 160213136Spjd return (-1); 161213136Spjdfound: 162213136Spjd dskp->part = curent + 1; 163213136Spjd ent = &gpttable[curent]; 164213136Spjd dskp->start = ent->ent_lba_start; 165213136Spjd if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) { 166213136Spjd /* 167213136Spjd * Clear BOOTME, but leave BOOTONCE set before trying to 168213136Spjd * boot from this partition. 169213136Spjd */ 170213136Spjd if (hdr_primary_lba > 0) { 171213136Spjd table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 172213136Spjd gptupdate("primary", dskp, &hdr_primary, table_primary); 173213136Spjd } 174213136Spjd if (hdr_backup_lba > 0) { 175213136Spjd table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; 176213136Spjd gptupdate("backup", dskp, &hdr_backup, table_backup); 177213136Spjd } 178213136Spjd } 179213136Spjd return (0); 180213136Spjd} 181213136Spjd 182213136Spjdstatic int 183213136Spjdgptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 184213136Spjd uint64_t hdrlba) 185213136Spjd{ 186213136Spjd uint32_t crc; 187213136Spjd 188213136Spjd if (drvread(dskp, secbuf, hdrlba, 1)) { 189213136Spjd printf("%s: unable to read %s GPT header\n", BOOTPROG, which); 190213136Spjd return (-1); 191213136Spjd } 192219083Spjd bcopy(secbuf, hdr, sizeof(*hdr)); 193213136Spjd if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || 194213136Spjd hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 || 195213136Spjd hdr->hdr_entsz < sizeof(struct gpt_ent) || 196213136Spjd hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) { 197213136Spjd printf("%s: invalid %s GPT header\n", BOOTPROG, which); 198213136Spjd return (-1); 199213136Spjd } 200213136Spjd crc = hdr->hdr_crc_self; 201213136Spjd hdr->hdr_crc_self = 0; 202213136Spjd if (crc32(hdr, hdr->hdr_size) != crc) { 203213136Spjd printf("%s: %s GPT header checksum mismatch\n", BOOTPROG, 204213136Spjd which); 205213136Spjd return (-1); 206213136Spjd } 207213136Spjd hdr->hdr_crc_self = crc; 208213136Spjd return (0); 209213136Spjd} 210213136Spjd 211213136Spjdvoid 212213136Spjdgptbootfailed(struct dsk *dskp) 213213136Spjd{ 214213136Spjd 215213136Spjd if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE)) 216213136Spjd return; 217213136Spjd 218213136Spjd if (hdr_primary_lba > 0) { 219213136Spjd table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 220213136Spjd table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 221213136Spjd gptupdate("primary", dskp, &hdr_primary, table_primary); 222213136Spjd } 223213136Spjd if (hdr_backup_lba > 0) { 224213136Spjd table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 225213136Spjd table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 226213136Spjd gptupdate("backup", dskp, &hdr_backup, table_backup); 227213136Spjd } 228213136Spjd} 229213136Spjd 230213136Spjdstatic void 231213136Spjdgptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, 232213136Spjd struct gpt_ent *table) 233213136Spjd{ 234213136Spjd struct gpt_ent *ent; 235213136Spjd daddr_t slba; 236213136Spjd int table_updated, sector_updated; 237213136Spjd int entries_per_sec, nent, part; 238213136Spjd 239213136Spjd table_updated = 0; 240213136Spjd entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 241213136Spjd for (nent = 0, slba = hdr->hdr_lba_table; 242213136Spjd slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; 243213136Spjd slba++, nent += entries_per_sec) { 244213136Spjd sector_updated = 0; 245213136Spjd for (part = 0; part < entries_per_sec; part++) { 246213136Spjd ent = &table[nent + part]; 247213136Spjd if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME | 248213136Spjd GPT_ENT_ATTR_BOOTONCE | 249213136Spjd GPT_ENT_ATTR_BOOTFAILED)) != 250213136Spjd GPT_ENT_ATTR_BOOTONCE) { 251213136Spjd continue; 252213136Spjd } 253213136Spjd ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; 254213136Spjd ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED; 255213136Spjd table_updated = 1; 256213136Spjd sector_updated = 1; 257213136Spjd } 258213136Spjd if (!sector_updated) 259213136Spjd continue; 260219083Spjd bcopy(&table[nent], secbuf, DEV_BSIZE); 261213136Spjd if (drvwrite(dskp, secbuf, slba, 1)) { 262213136Spjd printf("%s: unable to update %s GPT partition table\n", 263213136Spjd BOOTPROG, which); 264213136Spjd } 265213136Spjd } 266213136Spjd if (!table_updated) 267213136Spjd return; 268213136Spjd hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); 269213136Spjd hdr->hdr_crc_self = 0; 270213136Spjd hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); 271213136Spjd bzero(secbuf, DEV_BSIZE); 272219083Spjd bcopy(hdr, secbuf, hdr->hdr_size); 273213136Spjd if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) 274213136Spjd printf("%s: unable to update %s GPT header\n", BOOTPROG, which); 275213136Spjd} 276213136Spjd 277213136Spjdstatic int 278213136Spjdgptread_table(const char *which, const uuid_t *uuid, struct dsk *dskp, 279213136Spjd struct gpt_hdr *hdr, struct gpt_ent *table) 280213136Spjd{ 281213136Spjd struct gpt_ent *ent; 282213136Spjd int entries_per_sec; 283213136Spjd int part, nent; 284213136Spjd daddr_t slba; 285213136Spjd 286213136Spjd if (hdr->hdr_entries == 0) 287213136Spjd return (0); 288213136Spjd 289213136Spjd entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; 290213136Spjd slba = hdr->hdr_lba_table; 291213136Spjd nent = 0; 292213136Spjd for (;;) { 293213136Spjd if (drvread(dskp, secbuf, slba, 1)) { 294213136Spjd printf("%s: unable to read %s GPT partition table\n", 295213136Spjd BOOTPROG, which); 296213136Spjd return (-1); 297213136Spjd } 298213136Spjd ent = (struct gpt_ent *)secbuf; 299213136Spjd for (part = 0; part < entries_per_sec; part++, ent++) { 300219083Spjd bcopy(ent, &table[nent], sizeof(table[nent])); 301213136Spjd if (++nent >= hdr->hdr_entries) 302213136Spjd break; 303213136Spjd } 304213136Spjd if (nent >= hdr->hdr_entries) 305213136Spjd break; 306213136Spjd slba++; 307213136Spjd } 308213136Spjd if (crc32(table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) { 309213136Spjd printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which); 310213136Spjd return (-1); 311213136Spjd } 312213136Spjd return (0); 313213136Spjd} 314213136Spjd 315213136Spjdint 316213136Spjdgptread(const uuid_t *uuid, struct dsk *dskp, char *buf) 317213136Spjd{ 318213136Spjd uint64_t altlba; 319213136Spjd 320213136Spjd /* 321213136Spjd * Read and verify both GPT headers: primary and backup. 322213136Spjd */ 323213136Spjd 324213136Spjd secbuf = buf; 325213136Spjd hdr_primary_lba = hdr_backup_lba = 0; 326213136Spjd curent = -1; 327213136Spjd bootonce = 1; 328213136Spjd dskp->start = 0; 329213136Spjd 330213136Spjd if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 && 331213136Spjd gptread_table("primary", uuid, dskp, &hdr_primary, 332213136Spjd table_primary) == 0) { 333213136Spjd hdr_primary_lba = hdr_primary.hdr_lba_self; 334213136Spjd gpthdr = &hdr_primary; 335213136Spjd gpttable = table_primary; 336213136Spjd } 337213136Spjd 338234176Sae if (hdr_primary_lba > 0) { 339213136Spjd /* 340234176Sae * If primary header is valid, we can get backup 341234176Sae * header location from there. 342213136Spjd */ 343213136Spjd altlba = hdr_primary.hdr_lba_alt; 344234176Sae } else { 345234176Sae altlba = drvsize(dskp); 346234176Sae if (altlba > 0) 347234176Sae altlba--; 348213136Spjd } 349213136Spjd if (altlba == 0) 350213136Spjd printf("%s: unable to locate backup GPT header\n", BOOTPROG); 351213136Spjd else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 && 352213136Spjd gptread_table("backup", uuid, dskp, &hdr_backup, 353213136Spjd table_backup) == 0) { 354213136Spjd hdr_backup_lba = hdr_backup.hdr_lba_self; 355213136Spjd if (hdr_primary_lba == 0) { 356213136Spjd gpthdr = &hdr_backup; 357213136Spjd gpttable = table_backup; 358213136Spjd printf("%s: using backup GPT\n", BOOTPROG); 359213136Spjd } 360213136Spjd } 361213136Spjd 362213136Spjd /* 363213136Spjd * Convert all BOOTONCE without BOOTME flags into BOOTFAILED. 364213136Spjd * BOOTONCE without BOOTME means that we tried to boot from it, 365213136Spjd * but failed after leaving gptboot and machine was rebooted. 366213136Spjd * We don't want to leave partitions marked as BOOTONCE only, 367213136Spjd * because when we boot successfully start-up scripts should 368213136Spjd * find at most one partition with only BOOTONCE flag and this 369213136Spjd * will mean that we booted from that partition. 370213136Spjd */ 371213136Spjd if (hdr_primary_lba != 0) 372213136Spjd gptbootconv("primary", dskp, &hdr_primary, table_primary); 373213136Spjd if (hdr_backup_lba != 0) 374213136Spjd gptbootconv("backup", dskp, &hdr_backup, table_backup); 375213136Spjd 376213136Spjd if (hdr_primary_lba == 0 && hdr_backup_lba == 0) 377213136Spjd return (-1); 378213136Spjd return (0); 379213136Spjd} 380