Home | History | Annotate | Line # | Download | only in efi
      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