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