1 /* $NetBSD: bootvar.c,v 1.3 2025/03/02 01:07:11 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.3 2025/03/02 01:07:11 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, 172 sizeof(pp->PartitionSignature)); 173 pp->PartitionFormat = PARTITION_FORMAT_GPT; 174 pp->SignatureType = SIGNATURE_TYPE_GUID; 175 176 return bb; 177 } 178 179 static boot_blk_t * 180 create_devpath_media_pathname(const char *loader) 181 { 182 struct { 183 devpath_t hdr; 184 uint16_t PathName[]; 185 } __packed *pn; 186 size_t len, path_len; 187 boot_blk_t *bb; 188 189 path_len = utf8_to_ucs2_size(loader); 190 len = sizeof(pn->hdr) + path_len; 191 192 bb = new_blk(DEVPATH_TYPE_MEDIA, 4, (uint16_t)len); 193 194 pn = bb->u.vp; 195 (void)utf8_to_ucs2(loader, strlen(loader) + 1, pn->PathName, 196 &path_len); 197 198 return bb; 199 } 200 201 static boot_blk_t * 202 create_devpath_end(void) 203 { 204 boot_blk_t *bb; 205 206 bb = new_blk(DEVPATH_TYPE_END, 0xff, sizeof(*bb->u.path)); 207 208 return bb; 209 } 210 211 static boot_blk_t * 212 create_optdata(const char *fname) 213 { 214 boot_blk_t *bb; 215 216 bb = new_blk(0, 0, 0); 217 bb->u.vp = read_file(fname, &bb->size); 218 return bb; 219 } 220 221 PUBLIC void * 222 make_bootvar_data(const char *dev, uint partnum, uint32_t attrib, 223 const char *label, const char *loader, const char *fname, 224 size_t *datasize) 225 { 226 boothead_t head = SIMPLEQ_HEAD_INITIALIZER(head); 227 boot_blk_t *bb; 228 size_t FilePathListLength, OptDataLength; 229 230 bb = create_bootbody(label, attrib); 231 SIMPLEQ_INSERT_TAIL(&head, bb, entry); 232 FilePathListLength = 0; 233 234 bb = create_devpath_media_hd(dev, partnum); 235 SIMPLEQ_INSERT_TAIL(&head, bb, entry); 236 FilePathListLength += bb->size; 237 238 bb = create_devpath_media_pathname(loader); 239 SIMPLEQ_INSERT_TAIL(&head, bb, entry); 240 FilePathListLength += bb->size; 241 242 bb = create_devpath_end(); 243 SIMPLEQ_INSERT_TAIL(&head, bb, entry); 244 FilePathListLength += bb->size; 245 246 if (fname == NULL) { 247 OptDataLength = 0; 248 } 249 else { 250 bb = create_optdata(fname); 251 SIMPLEQ_INSERT_TAIL(&head, bb, entry); 252 OptDataLength = bb->size; 253 } 254 255 bb = SIMPLEQ_FIRST(&head); 256 bb->u.body->FilePathListLength = (uint16_t)FilePathListLength; 257 258 *datasize = bb->size + FilePathListLength + OptDataLength; 259 return collapse_list(&head, *datasize); 260 } 261 262 PUBLIC int 263 find_new_bootvar(efi_var_t **var_array, size_t var_cnt, const char *target) 264 { 265 int idx, lastidx; 266 int rstatus; 267 size_t n; 268 269 assert(target != NULL); 270 271 n = strlen(target); 272 lastidx = -1; 273 for (size_t i = 0; i < var_cnt; i++) { 274 idx = (int)strtou(var_array[i]->name + n, NULL, 16, 0, 0xffff, 275 &rstatus); 276 if (rstatus != 0) 277 err(EXIT_FAILURE, "strtou: %s", var_array[i]->name); 278 279 // printf("idx: %x\n", idx); 280 assert(idx > lastidx); 281 if (idx != lastidx + 1) { 282 break; 283 } 284 lastidx = idx; 285 } 286 return lastidx + 1; 287 } 288