xref: /freebsd/tools/boot/gptattr.c (revision d8acc2a9a431b4d57ef79b2ca3113ecf33b95d8d)
1*d8acc2a9SWarner Losh /*
2*d8acc2a9SWarner Losh  *
3*d8acc2a9SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
4*d8acc2a9SWarner Losh  */
5*d8acc2a9SWarner Losh 
6*d8acc2a9SWarner Losh /*
7*d8acc2a9SWarner Losh  * Set a GPT attribute on a partition of a disk image file.
8*d8acc2a9SWarner Losh  *
9*d8acc2a9SWarner Losh  * gpart(8) does the same thing, but only on a real device and only as root.
10*d8acc2a9SWarner Losh  * boot-test.sh builds every image as an unprivileged user, so it needs to
11*d8acc2a9SWarner Losh  * edit the on-disk GPT itself.  Both the primary and the secondary entry
12*d8acc2a9SWarner Losh  * table are updated: the loader only reads the primary, but the kernel
13*d8acc2a9SWarner Losh  * checks both, and a mismatch would be reported as a corrupt GPT during the
14*d8acc2a9SWarner Losh  * boot under test.
15*d8acc2a9SWarner Losh  *
16*d8acc2a9SWarner Losh  * Usage: gptattr <image> <index> <attribute-bit>
17*d8acc2a9SWarner Losh  * where <index> is 1-based, matching gpart(8) partition numbering.
18*d8acc2a9SWarner Losh  */
19*d8acc2a9SWarner Losh 
20*d8acc2a9SWarner Losh #include <sys/param.h>
21*d8acc2a9SWarner Losh #include <sys/disk/gpt.h>
22*d8acc2a9SWarner Losh #include <sys/endian.h>
23*d8acc2a9SWarner Losh 
24*d8acc2a9SWarner Losh #include <err.h>
25*d8acc2a9SWarner Losh #include <errno.h>
26*d8acc2a9SWarner Losh #include <fcntl.h>
27*d8acc2a9SWarner Losh #include <stdint.h>
28*d8acc2a9SWarner Losh #include <stdio.h>
29*d8acc2a9SWarner Losh #include <stdlib.h>
30*d8acc2a9SWarner Losh #include <string.h>
31*d8acc2a9SWarner Losh #include <unistd.h>
32*d8acc2a9SWarner Losh #include <zlib.h>
33*d8acc2a9SWarner Losh 
34*d8acc2a9SWarner Losh #define	SECSZ	512
35*d8acc2a9SWarner Losh 
36*d8acc2a9SWarner Losh static uint8_t *image;
37*d8acc2a9SWarner Losh static size_t imagesz;
38*d8acc2a9SWarner Losh 
39*d8acc2a9SWarner Losh /*
40*d8acc2a9SWarner Losh  * Apply the attribute to one entry table and refresh the CRCs of the header
41*d8acc2a9SWarner Losh  * at hdroff that describes it.
42*d8acc2a9SWarner Losh  */
43*d8acc2a9SWarner Losh static void
patch_table(uint64_t hdroff,u_int index,uint64_t attr)44*d8acc2a9SWarner Losh patch_table(uint64_t hdroff, u_int index, uint64_t attr)
45*d8acc2a9SWarner Losh {
46*d8acc2a9SWarner Losh 	struct gpt_hdr *hdr;
47*d8acc2a9SWarner Losh 	struct gpt_ent *ent;
48*d8acc2a9SWarner Losh 	uint64_t tbloff;
49*d8acc2a9SWarner Losh 	uint32_t entries, entsz, hdrsz;
50*d8acc2a9SWarner Losh 
51*d8acc2a9SWarner Losh 	if (hdroff + SECSZ > imagesz)
52*d8acc2a9SWarner Losh 		errx(1, "GPT header at offset %ju is past end of image",
53*d8acc2a9SWarner Losh 		    (uintmax_t)hdroff);
54*d8acc2a9SWarner Losh 
55*d8acc2a9SWarner Losh 	hdr = (struct gpt_hdr *)(image + hdroff);
56*d8acc2a9SWarner Losh 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0)
57*d8acc2a9SWarner Losh 		errx(1, "no GPT header at offset %ju", (uintmax_t)hdroff);
58*d8acc2a9SWarner Losh 
59*d8acc2a9SWarner Losh 	hdrsz = le32toh(hdr->hdr_size);
60*d8acc2a9SWarner Losh 	entries = le32toh(hdr->hdr_entries);
61*d8acc2a9SWarner Losh 	entsz = le32toh(hdr->hdr_entsz);
62*d8acc2a9SWarner Losh 	tbloff = le64toh(hdr->hdr_lba_table) * SECSZ;
63*d8acc2a9SWarner Losh 
64*d8acc2a9SWarner Losh 	if (index < 1 || index > entries)
65*d8acc2a9SWarner Losh 		errx(1, "partition index %u out of range (1..%u)", index,
66*d8acc2a9SWarner Losh 		    entries);
67*d8acc2a9SWarner Losh 	if (tbloff + (uint64_t)entries * entsz > imagesz)
68*d8acc2a9SWarner Losh 		errx(1, "GPT entry table is past end of image");
69*d8acc2a9SWarner Losh 
70*d8acc2a9SWarner Losh 	ent = (struct gpt_ent *)(image + tbloff + (uint64_t)(index - 1) * entsz);
71*d8acc2a9SWarner Losh 	le64enc(&ent->ent_attr, le64toh(ent->ent_attr) | attr);
72*d8acc2a9SWarner Losh 
73*d8acc2a9SWarner Losh 	/*
74*d8acc2a9SWarner Losh 	 * The table CRC covers the entries, and the header CRC covers the
75*d8acc2a9SWarner Losh 	 * header including that table CRC, so recompute them in that order.
76*d8acc2a9SWarner Losh 	 * hdr_crc_self is zeroed while it is being computed over itself.
77*d8acc2a9SWarner Losh 	 */
78*d8acc2a9SWarner Losh 	le32enc(&hdr->hdr_crc_table, crc32(0, image + tbloff,
79*d8acc2a9SWarner Losh 	    entries * entsz));
80*d8acc2a9SWarner Losh 	hdr->hdr_crc_self = 0;
81*d8acc2a9SWarner Losh 	le32enc(&hdr->hdr_crc_self, crc32(0, (const uint8_t *)hdr, hdrsz));
82*d8acc2a9SWarner Losh }
83*d8acc2a9SWarner Losh 
84*d8acc2a9SWarner Losh int
main(int argc,char * argv[])85*d8acc2a9SWarner Losh main(int argc, char *argv[])
86*d8acc2a9SWarner Losh {
87*d8acc2a9SWarner Losh 	FILE *fp;
88*d8acc2a9SWarner Losh 	char *end;
89*d8acc2a9SWarner Losh 	uint64_t attr;
90*d8acc2a9SWarner Losh 	u_long bit, index;
91*d8acc2a9SWarner Losh 
92*d8acc2a9SWarner Losh 	if (argc != 4) {
93*d8acc2a9SWarner Losh 		fprintf(stderr,
94*d8acc2a9SWarner Losh 		    "usage: %s <image> <index> <attribute-bit>\n", argv[0]);
95*d8acc2a9SWarner Losh 		return (1);
96*d8acc2a9SWarner Losh 	}
97*d8acc2a9SWarner Losh 
98*d8acc2a9SWarner Losh 	errno = 0;
99*d8acc2a9SWarner Losh 	index = strtoul(argv[2], &end, 0);
100*d8acc2a9SWarner Losh 	if (errno != 0 || *end != '\0' || index == 0)
101*d8acc2a9SWarner Losh 		errx(1, "bad partition index: %s", argv[2]);
102*d8acc2a9SWarner Losh 	bit = strtoul(argv[3], &end, 0);
103*d8acc2a9SWarner Losh 	if (errno != 0 || *end != '\0' || bit > 63)
104*d8acc2a9SWarner Losh 		errx(1, "bad attribute bit: %s", argv[3]);
105*d8acc2a9SWarner Losh 	attr = (uint64_t)1 << bit;
106*d8acc2a9SWarner Losh 
107*d8acc2a9SWarner Losh 	if ((fp = fopen(argv[1], "r+")) == NULL)
108*d8acc2a9SWarner Losh 		err(1, "%s", argv[1]);
109*d8acc2a9SWarner Losh 	if (fseek(fp, 0, SEEK_END) != 0)
110*d8acc2a9SWarner Losh 		err(1, "%s", argv[1]);
111*d8acc2a9SWarner Losh 	imagesz = (size_t)ftell(fp);
112*d8acc2a9SWarner Losh 	rewind(fp);
113*d8acc2a9SWarner Losh 	if (imagesz < 2 * SECSZ)
114*d8acc2a9SWarner Losh 		errx(1, "%s is too small to hold a GPT", argv[1]);
115*d8acc2a9SWarner Losh 	if ((image = malloc(imagesz)) == NULL)
116*d8acc2a9SWarner Losh 		err(1, "malloc");
117*d8acc2a9SWarner Losh 	if (fread(image, 1, imagesz, fp) != imagesz)
118*d8acc2a9SWarner Losh 		err(1, "%s: read", argv[1]);
119*d8acc2a9SWarner Losh 
120*d8acc2a9SWarner Losh 	/* Primary header is always at LBA 1; it names the secondary. */
121*d8acc2a9SWarner Losh 	patch_table(SECSZ, index, attr);
122*d8acc2a9SWarner Losh 	patch_table(le64toh(((struct gpt_hdr *)(image + SECSZ))->hdr_lba_alt) *
123*d8acc2a9SWarner Losh 	    SECSZ, index, attr);
124*d8acc2a9SWarner Losh 
125*d8acc2a9SWarner Losh 	rewind(fp);
126*d8acc2a9SWarner Losh 	if (fwrite(image, 1, imagesz, fp) != imagesz)
127*d8acc2a9SWarner Losh 		err(1, "%s: write", argv[1]);
128*d8acc2a9SWarner Losh 	if (fclose(fp) != 0)
129*d8acc2a9SWarner Losh 		err(1, "%s: close", argv[1]);
130*d8acc2a9SWarner Losh 	free(image);
131*d8acc2a9SWarner Losh 
132*d8acc2a9SWarner Losh 	return (0);
133*d8acc2a9SWarner Losh }
134