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