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