2 * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
29 #include <sys/param.h>
33 #error gpt.c works only for little endian architectures
41 #define MAXTBLENTS 128
43 static struct gpt_hdr hdr_primary
, hdr_backup
, *gpthdr
;
44 static uint64_t hdr_primary_lba
, hdr_backup_lba
;
45 static struct gpt_ent table_primary
[MAXTBLENTS
], table_backup
[MAXTBLENTS
];
46 static struct gpt_ent
*gpttable
;
47 static int curent
, bootonce
;
50 * Buffer below 64kB passed on gptread(), which can hold at least
51 * one sector of data (512 bytes).
56 gptupdate(const char *which
, struct dsk
*dskp
, struct gpt_hdr
*hdr
,
57 struct gpt_ent
*table
)
59 int entries_per_sec
, firstent
;
63 * We need to update the following for both primary and backup GPT:
64 * 1. Sector on disk that contains current partition.
65 * 2. Partition table checksum.
70 entries_per_sec
= DEV_BSIZE
/ hdr
->hdr_entsz
;
71 slba
= curent
/ entries_per_sec
;
72 firstent
= slba
* entries_per_sec
;
73 bcopy(&table
[firstent
], secbuf
, DEV_BSIZE
);
74 slba
+= hdr
->hdr_lba_table
;
75 if (drvwrite(dskp
, secbuf
, slba
, 1)) {
76 printf("%s: unable to update %s GPT partition table\n",
80 hdr
->hdr_crc_table
= crc32(0, Z_NULL
, 0);
81 hdr
->hdr_crc_table
= crc32(hdr
->hdr_crc_table
, table
,
82 hdr
->hdr_entries
* hdr
->hdr_entsz
);
83 hdr
->hdr_crc_self
= crc32(0, Z_NULL
, 0);
84 hdr
->hdr_crc_self
= crc32(hdr
->hdr_crc_self
, hdr
, hdr
->hdr_size
);
85 bzero(secbuf
, DEV_BSIZE
);
86 bcopy(hdr
, secbuf
, hdr
->hdr_size
);
87 if (drvwrite(dskp
, secbuf
, hdr
->hdr_lba_self
, 1)) {
88 printf("%s: unable to update %s GPT header\n", BOOTPROG
, which
);
94 gptfind(const uuid_t
*uuid
, struct dsk
*dskp
, int part
)
100 if (part
== 0 || part
> gpthdr
->hdr_entries
) {
101 printf("%s: invalid partition index\n", BOOTPROG
);
104 ent
= &gpttable
[part
- 1];
105 if (bcmp(&ent
->ent_type
, uuid
, sizeof (uuid_t
)) != 0) {
106 printf("%s: specified partition is not UFS\n",
114 firsttry
= (curent
== -1);
116 if (curent
>= gpthdr
->hdr_entries
) {
117 curent
= gpthdr
->hdr_entries
;
122 * First look for partition with both GPT_ENT_ATTR_BOOTME and
123 * GPT_ENT_ATTR_BOOTONCE flags.
125 for (; curent
< gpthdr
->hdr_entries
; curent
++) {
126 ent
= &gpttable
[curent
];
127 if (bcmp(&ent
->ent_type
, uuid
, sizeof (uuid_t
)) != 0)
129 if (!(ent
->ent_attr
& GPT_ENT_ATTR_BOOTME
))
131 if (!(ent
->ent_attr
& GPT_ENT_ATTR_BOOTONCE
))
139 for (; curent
< gpthdr
->hdr_entries
; curent
++) {
140 ent
= &gpttable
[curent
];
141 if (bcmp(&ent
->ent_type
, uuid
, sizeof (uuid_t
)) != 0)
143 if (!(ent
->ent_attr
& GPT_ENT_ATTR_BOOTME
))
145 if (ent
->ent_attr
& GPT_ENT_ATTR_BOOTONCE
)
152 * No partition with BOOTME flag was found, try to boot from
153 * first UFS partition.
155 for (curent
= 0; curent
< gpthdr
->hdr_entries
; curent
++) {
156 ent
= &gpttable
[curent
];
157 if (bcmp(&ent
->ent_type
, uuid
, sizeof (uuid_t
)) != 0)
165 dskp
->part
= curent
+ 1;
166 ent
= &gpttable
[curent
];
167 dskp
->start
= ent
->ent_lba_start
;
168 if (ent
->ent_attr
& GPT_ENT_ATTR_BOOTONCE
) {
170 * Clear BOOTME, but leave BOOTONCE set before trying to
171 * boot from this partition.
173 if (hdr_primary_lba
> 0) {
174 table_primary
[curent
].ent_attr
&= ~GPT_ENT_ATTR_BOOTME
;
175 gptupdate("primary", dskp
, &hdr_primary
, table_primary
);
177 if (hdr_backup_lba
> 0) {
178 table_backup
[curent
].ent_attr
&= ~GPT_ENT_ATTR_BOOTME
;
179 gptupdate("backup", dskp
, &hdr_backup
, table_backup
);
186 gptread_hdr(const char *which
, struct dsk
*dskp
, struct gpt_hdr
*hdr
,
191 if (drvread(dskp
, secbuf
, hdrlba
, 1)) {
192 printf("%s: unable to read %s GPT header\n", BOOTPROG
, which
);
195 bcopy(secbuf
, hdr
, sizeof (*hdr
));
196 if (bcmp(hdr
->hdr_sig
, GPT_HDR_SIG
, sizeof (hdr
->hdr_sig
)) != 0 ||
197 hdr
->hdr_lba_self
!= hdrlba
|| hdr
->hdr_revision
< 0x00010000 ||
198 hdr
->hdr_entsz
< sizeof (struct gpt_ent
) ||
199 hdr
->hdr_entries
> MAXTBLENTS
|| DEV_BSIZE
% hdr
->hdr_entsz
!= 0) {
200 printf("%s: invalid %s GPT header\n", BOOTPROG
, which
);
203 crc
= hdr
->hdr_crc_self
;
204 hdr
->hdr_crc_self
= crc32(0, Z_NULL
, 0);
205 if (crc32(hdr
->hdr_crc_self
, hdr
, hdr
->hdr_size
) != crc
) {
206 printf("%s: %s GPT header checksum mismatch\n", BOOTPROG
,
210 hdr
->hdr_crc_self
= crc
;
215 gptbootfailed(struct dsk
*dskp
)
218 if (!(gpttable
[curent
].ent_attr
& GPT_ENT_ATTR_BOOTONCE
))
221 if (hdr_primary_lba
> 0) {
222 table_primary
[curent
].ent_attr
&= ~GPT_ENT_ATTR_BOOTONCE
;
223 table_primary
[curent
].ent_attr
|= GPT_ENT_ATTR_BOOTFAILED
;
224 gptupdate("primary", dskp
, &hdr_primary
, table_primary
);
226 if (hdr_backup_lba
> 0) {
227 table_backup
[curent
].ent_attr
&= ~GPT_ENT_ATTR_BOOTONCE
;
228 table_backup
[curent
].ent_attr
|= GPT_ENT_ATTR_BOOTFAILED
;
229 gptupdate("backup", dskp
, &hdr_backup
, table_backup
);
234 gptbootconv(const char *which
, struct dsk
*dskp
, struct gpt_hdr
*hdr
,
235 struct gpt_ent
*table
)
239 int table_updated
, sector_updated
;
240 int entries_per_sec
, nent
, part
;
243 entries_per_sec
= DEV_BSIZE
/ hdr
->hdr_entsz
;
244 for (nent
= 0, slba
= hdr
->hdr_lba_table
;
245 slba
< hdr
->hdr_lba_table
+ hdr
->hdr_entries
/ entries_per_sec
;
246 slba
++, nent
+= entries_per_sec
) {
248 for (part
= 0; part
< entries_per_sec
; part
++) {
249 ent
= &table
[nent
+ part
];
250 if ((ent
->ent_attr
& (GPT_ENT_ATTR_BOOTME
|
251 GPT_ENT_ATTR_BOOTONCE
|
252 GPT_ENT_ATTR_BOOTFAILED
)) !=
253 GPT_ENT_ATTR_BOOTONCE
) {
256 ent
->ent_attr
&= ~GPT_ENT_ATTR_BOOTONCE
;
257 ent
->ent_attr
|= GPT_ENT_ATTR_BOOTFAILED
;
263 bcopy(&table
[nent
], secbuf
, DEV_BSIZE
);
264 if (drvwrite(dskp
, secbuf
, slba
, 1)) {
265 printf("%s: unable to update %s GPT partition table\n",
271 hdr
->hdr_crc_table
= crc32(0, Z_NULL
, 0);
272 hdr
->hdr_crc_table
= crc32(hdr
->hdr_crc_table
, table
,
273 hdr
->hdr_entries
* hdr
->hdr_entsz
);
274 hdr
->hdr_crc_self
= crc32(0, Z_NULL
, 0);
275 hdr
->hdr_crc_self
= crc32(hdr
->hdr_crc_self
, hdr
, 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
);
283 gptread_table(const char *which
, const uuid_t
*uuid
, struct dsk
*dskp
,
284 struct gpt_hdr
*hdr
, struct gpt_ent
*table
)
291 if (hdr
->hdr_entries
== 0)
294 entries_per_sec
= DEV_BSIZE
/ hdr
->hdr_entsz
;
295 slba
= hdr
->hdr_lba_table
;
298 if (drvread(dskp
, secbuf
, slba
, 1)) {
299 printf("%s: unable to read %s GPT partition table\n",
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
)
309 if (nent
>= hdr
->hdr_entries
)
313 if (crc32(0, table
, nent
* hdr
->hdr_entsz
) != hdr
->hdr_crc_table
) {
314 printf("%s: %s GPT table checksum mismatch\n", BOOTPROG
, which
);
321 gptread(const uuid_t
*uuid
, struct dsk
*dskp
, char *buf
)
326 * Read and verify both GPT headers: primary and backup.
330 hdr_primary_lba
= hdr_backup_lba
= 0;
335 if (gptread_hdr("primary", dskp
, &hdr_primary
, 1) == 0 &&
336 gptread_table("primary", uuid
, dskp
, &hdr_primary
,
337 table_primary
) == 0) {
338 hdr_primary_lba
= hdr_primary
.hdr_lba_self
;
339 gpthdr
= &hdr_primary
;
340 gpttable
= table_primary
;
343 if (hdr_primary_lba
> 0) {
345 * If primary header is valid, we can get backup
346 * header location from there.
348 altlba
= hdr_primary
.hdr_lba_alt
;
350 altlba
= drvsize(dskp
);
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", uuid
, dskp
, &hdr_backup
,
358 table_backup
) == 0) {
359 hdr_backup_lba
= hdr_backup
.hdr_lba_self
;
360 if (hdr_primary_lba
== 0) {
361 gpthdr
= &hdr_backup
;
362 gpttable
= table_backup
;
363 printf("%s: using backup GPT\n", BOOTPROG
);
368 * Convert all BOOTONCE without BOOTME flags into BOOTFAILED.
369 * BOOTONCE without BOOTME means that we tried to boot from it,
370 * but failed after leaving gptboot and machine was rebooted.
371 * We don't want to leave partitions marked as BOOTONCE only,
372 * because when we boot successfully start-up scripts should
373 * find at most one partition with only BOOTONCE flag and this
374 * will mean that we booted from that partition.
376 if (hdr_primary_lba
!= 0)
377 gptbootconv("primary", dskp
, &hdr_primary
, table_primary
);
378 if (hdr_backup_lba
!= 0)
379 gptbootconv("backup", dskp
, &hdr_backup
, table_backup
);
381 if (hdr_primary_lba
== 0 && hdr_backup_lba
== 0)