bootvar.c revision 1.2 1 /* $NetBSD: bootvar.c,v 1.2 2025/03/02 00:03:41 riastradh Exp $ */
2
3 /*
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27 #ifndef lint
28 __RCSID("$NetBSD: bootvar.c,v 1.2 2025/03/02 00:03:41 riastradh Exp $");
29 #endif /* not lint */
30
31 #include <sys/queue.h>
32
33 #include <assert.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <util.h>
43 #include <uuid.h>
44
45 #include "defs.h"
46 #include "efiio.h"
47 #include "bootvar.h"
48 #include "devpath.h"
49 #include "gptsubr.h"
50 #include "map.h"
51 #include "utils.h"
52
53 typedef SIMPLEQ_HEAD(boothead, boot_blk) boothead_t;
54
55 typedef struct boot_blk {
56 size_t size;
57 union {
58 uint8_t *bp;
59 char *cp;
60 void *vp;
61 boot_var_t *body; /* first element */
62 devpath_t *path; /* remaining elements */
63 } u;
64 SIMPLEQ_ENTRY(boot_blk) entry;
65 } boot_blk_t;
66
67 static inline boot_blk_t *
68 new_blk(uint8_t type, uint8_t subtype, uint16_t length)
69 {
70 boot_blk_t *bb;
71
72 bb = ecalloc(sizeof(*bb), 1);
73
74 if (length == 0) /* alloc bb only */
75 return bb;
76
77 bb->u.vp = ecalloc(length, 1);
78 bb->size = length;
79
80 if (type == 0) /* non-devpath */
81 return bb;
82
83 bb->u.path->Type = type;
84 bb->u.path->SubType = subtype;
85 bb->u.path->Length = length;
86
87 return bb;
88 }
89
90 static void *
91 collapse_list(boothead_t *head, size_t datasize)
92 {
93 boot_blk_t *bb;
94 void *data;
95 char *cp;
96
97 data = ecalloc(datasize, 1);
98 cp = data;
99 SIMPLEQ_FOREACH(bb, head, entry) {
100 memcpy(cp, bb->u.vp, bb->size);
101 cp += bb->size;
102 }
103 return data;
104 }
105
106 static boot_blk_t *
107 create_bootbody(const char *label, uint32_t attrib)
108 {
109 boot_blk_t *bb;
110 size_t body_size, desc_size, size;
111
112 desc_size = utf8_to_ucs2_size(label);
113 body_size = sizeof(*bb->u.body) + desc_size;
114
115 bb = new_blk(0, 0, (uint16_t)body_size);
116
117 bb->u.body->Attributes = attrib;
118
119 size = desc_size;
120 utf8_to_ucs2(label, strlen(label) + 1, bb->u.body->Description, &size);
121 assert(size == desc_size);
122
123 return bb;
124 }
125
126 static boot_blk_t *
127 create_devpath_media_hd(const char *dev, uint partnum)
128 {
129 struct {
130 devpath_t hdr; /* Length 42 */
131 uint32_t PartitionNumber;
132 uint64_t PartitionStart;
133 uint64_t PartitionSize;
134 struct uuid PartitionSignature;
135 uint8_t PartitionFormat;
136 #define PARTITION_FORMAT_MBR 0x01
137 #define PARTITION_FORMAT_GPT 0x02
138
139 uint8_t SignatureType;
140 #define SIGNATURE_TYPE_NONE 0x00
141 #define SIGNATURE_TYPE_MBR 0x01
142 #define SIGNATURE_TYPE_GUID 0x02
143 } __packed *pp;
144 assert(sizeof(*pp) == 42);
145 boot_blk_t *bb;
146 struct gpt_ent *ent;
147 map_t m;
148
149 /* Get GPT info for device and partition */
150 m = find_gpt_map(dev, partnum);
151 if (m == NULL)
152 errx(EXIT_FAILURE, "cannot find partition number %u on %s\n",
153 partnum, dev);
154
155 ent = m->map_data;
156 if (m->map_type != MAP_TYPE_GPT_PART)
157 errx(EXIT_FAILURE, "not a MAP_TYPE_GPT_PART: %u\n",
158 m->map_type);
159
160 /* Check that this is an EFI partition? */
161 if (memcmp(ent->ent_type, (void *)&(uuid_t)GPT_ENT_TYPE_EFI,
162 sizeof(ent->ent_type)) != 0)
163 errx(EXIT_FAILURE, "not an EFI partition");
164
165 bb = new_blk(DEVPATH_TYPE_MEDIA, 1, sizeof(*pp));
166
167 pp = bb->u.vp;
168 pp->PartitionNumber = m->map_index;
169 pp->PartitionStart = (uint64_t)m->map_start;
170 pp->PartitionSize = (uint64_t)m->map_size;
171 memcpy(&pp->PartitionSignature, ent->ent_guid, sizeof(pp->PartitionSignature));
172 pp->PartitionFormat = PARTITION_FORMAT_GPT;
173 pp->SignatureType = SIGNATURE_TYPE_GUID;
174
175 return bb;
176 }
177
178 static boot_blk_t *
179 create_devpath_media_pathname(const char *loader)
180 {
181 struct {
182 devpath_t hdr;
183 uint16_t PathName[];
184 } __packed *pn;
185 size_t len, path_len;
186 boot_blk_t *bb;
187
188 path_len = utf8_to_ucs2_size(loader);
189 len = sizeof(pn->hdr) + path_len;
190
191 bb = new_blk(DEVPATH_TYPE_MEDIA, 4, (uint16_t)len);
192
193 pn = bb->u.vp;
194 (void)utf8_to_ucs2(loader, strlen(loader) + 1, pn->PathName,
195 &path_len);
196
197 return bb;
198 }
199
200 static boot_blk_t *
201 create_devpath_end(void)
202 {
203 boot_blk_t *bb;
204
205 bb = new_blk(DEVPATH_TYPE_END, 0xff, sizeof(*bb->u.path));
206
207 return bb;
208 }
209
210 static boot_blk_t *
211 create_optdata(const char *fname)
212 {
213 boot_blk_t *bb;
214
215 bb = new_blk(0, 0, 0);
216 bb->u.vp = read_file(fname, &bb->size);
217 return bb;
218 }
219
220 PUBLIC void *
221 make_bootvar_data(const char *dev, uint partnum, uint32_t attrib,
222 const char *label, const char *loader, const char *fname,
223 size_t *datasize)
224 {
225 boothead_t head = SIMPLEQ_HEAD_INITIALIZER(head);
226 boot_blk_t *bb;
227 size_t FilePathListLength, OptDataLength;
228
229 bb = create_bootbody(label, attrib);
230 SIMPLEQ_INSERT_TAIL(&head, bb, entry);
231 FilePathListLength = 0;
232
233 bb = create_devpath_media_hd(dev, partnum);
234 SIMPLEQ_INSERT_TAIL(&head, bb, entry);
235 FilePathListLength += bb->size;
236
237 bb = create_devpath_media_pathname(loader);
238 SIMPLEQ_INSERT_TAIL(&head, bb, entry);
239 FilePathListLength += bb->size;
240
241 bb = create_devpath_end();
242 SIMPLEQ_INSERT_TAIL(&head, bb, entry);
243 FilePathListLength += bb->size;
244
245 if (fname == NULL) {
246 OptDataLength = 0;
247 }
248 else {
249 bb = create_optdata(fname);
250 SIMPLEQ_INSERT_TAIL(&head, bb, entry);
251 OptDataLength = bb->size;
252 }
253
254 bb = SIMPLEQ_FIRST(&head);
255 bb->u.body->FilePathListLength = (uint16_t)FilePathListLength;
256
257 *datasize = bb->size + FilePathListLength + OptDataLength;
258 return collapse_list(&head, *datasize);
259 }
260
261 PUBLIC int
262 find_new_bootvar(efi_var_t **var_array, size_t var_cnt, const char *target)
263 {
264 int idx, lastidx;
265 int rstatus;
266 size_t n;
267
268 assert(target != NULL);
269
270 n = strlen(target);
271 lastidx = -1;
272 for (size_t i = 0; i < var_cnt; i++) {
273 idx = (int)strtou(var_array[i]->name + n, NULL, 16, 0, 0xffff, &rstatus);
274 if (rstatus != 0)
275 err(EXIT_FAILURE, "strtou: %s", var_array[i]->name);
276
277 // printf("idx: %x\n", idx);
278 assert(idx > lastidx);
279 if (idx != lastidx + 1) {
280 break;
281 }
282 lastidx = idx;
283 }
284 return lastidx + 1;
285 }
286