Home | History | Annotate | Download | only in efi_loader
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  *  EFI utils
      4  *
      5  *  Copyright (c) 2017 Rob Clark
      6  */
      7 
      8 #include <common.h>
      9 #include <charset.h>
     10 #include <efi_loader.h>
     11 #include <malloc.h>
     12 #include <fs.h>
     13 
     14 /* GUID for file system information */
     15 const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
     16 
     17 struct file_system {
     18 	struct efi_simple_file_system_protocol base;
     19 	struct efi_device_path *dp;
     20 	struct blk_desc *desc;
     21 	int part;
     22 };
     23 #define to_fs(x) container_of(x, struct file_system, base)
     24 
     25 struct file_handle {
     26 	struct efi_file_handle base;
     27 	struct file_system *fs;
     28 	loff_t offset;       /* current file position/cursor */
     29 	int isdir;
     30 
     31 	/* for reading a directory: */
     32 	struct fs_dir_stream *dirs;
     33 	struct fs_dirent *dent;
     34 
     35 	char path[0];
     36 };
     37 #define to_fh(x) container_of(x, struct file_handle, base)
     38 
     39 static const struct efi_file_handle efi_file_handle_protocol;
     40 
     41 static char *basename(struct file_handle *fh)
     42 {
     43 	char *s = strrchr(fh->path, '/');
     44 	if (s)
     45 		return s + 1;
     46 	return fh->path;
     47 }
     48 
     49 static int set_blk_dev(struct file_handle *fh)
     50 {
     51 	return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
     52 }
     53 
     54 static int is_dir(struct file_handle *fh)
     55 {
     56 	struct fs_dir_stream *dirs;
     57 
     58 	set_blk_dev(fh);
     59 	dirs = fs_opendir(fh->path);
     60 	if (!dirs)
     61 		return 0;
     62 
     63 	fs_closedir(dirs);
     64 
     65 	return 1;
     66 }
     67 
     68 /*
     69  * Normalize a path which may include either back or fwd slashes,
     70  * double slashes, . or .. entries in the path, etc.
     71  */
     72 static int sanitize_path(char *path)
     73 {
     74 	char *p;
     75 
     76 	/* backslash to slash: */
     77 	p = path;
     78 	while ((p = strchr(p, '\\')))
     79 		*p++ = '/';
     80 
     81 	/* handle double-slashes: */
     82 	p = path;
     83 	while ((p = strstr(p, "//"))) {
     84 		char *src = p + 1;
     85 		memmove(p, src, strlen(src) + 1);
     86 	}
     87 
     88 	/* handle extra /.'s */
     89 	p = path;
     90 	while ((p = strstr(p, "/."))) {
     91 		/*
     92 		 * You'd be tempted to do this *after* handling ".."s
     93 		 * below to avoid having to check if "/." is start of
     94 		 * a "/..", but that won't have the correct results..
     95 		 * for example, "/foo/./../bar" would get resolved to
     96 		 * "/foo/bar" if you did these two passes in the other
     97 		 * order
     98 		 */
     99 		if (p[2] == '.') {
    100 			p += 2;
    101 			continue;
    102 		}
    103 		char *src = p + 2;
    104 		memmove(p, src, strlen(src) + 1);
    105 	}
    106 
    107 	/* handle extra /..'s: */
    108 	p = path;
    109 	while ((p = strstr(p, "/.."))) {
    110 		char *src = p + 3;
    111 
    112 		p--;
    113 
    114 		/* find beginning of previous path entry: */
    115 		while (true) {
    116 			if (p < path)
    117 				return -1;
    118 			if (*p == '/')
    119 				break;
    120 			p--;
    121 		}
    122 
    123 		memmove(p, src, strlen(src) + 1);
    124 	}
    125 
    126 	return 0;
    127 }
    128 
    129 /* NOTE: despite what you would expect, 'file_name' is actually a path.
    130  * With windoze style backlashes, ofc.
    131  */
    132 static struct efi_file_handle *file_open(struct file_system *fs,
    133 		struct file_handle *parent, s16 *file_name, u64 mode)
    134 {
    135 	struct file_handle *fh;
    136 	char f0[MAX_UTF8_PER_UTF16] = {0};
    137 	int plen = 0;
    138 	int flen = 0;
    139 
    140 	if (file_name) {
    141 		utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1);
    142 		flen = utf16_strlen((u16 *)file_name);
    143 	}
    144 
    145 	/* we could have a parent, but also an absolute path: */
    146 	if (f0[0] == '\\') {
    147 		plen = 0;
    148 	} else if (parent) {
    149 		plen = strlen(parent->path) + 1;
    150 	}
    151 
    152 	/* +2 is for null and '/' */
    153 	fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
    154 
    155 	fh->base = efi_file_handle_protocol;
    156 	fh->fs = fs;
    157 
    158 	if (parent) {
    159 		char *p = fh->path;
    160 
    161 		if (plen > 0) {
    162 			strcpy(p, parent->path);
    163 			p += plen - 1;
    164 			*p++ = '/';
    165 		}
    166 
    167 		utf16_to_utf8((u8 *)p, (u16 *)file_name, flen);
    168 
    169 		if (sanitize_path(fh->path))
    170 			goto error;
    171 
    172 		/* check if file exists: */
    173 		if (set_blk_dev(fh))
    174 			goto error;
    175 
    176 		if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path)))
    177 			goto error;
    178 
    179 		/* figure out if file is a directory: */
    180 		fh->isdir = is_dir(fh);
    181 	} else {
    182 		fh->isdir = 1;
    183 		strcpy(fh->path, "");
    184 	}
    185 
    186 	return &fh->base;
    187 
    188 error:
    189 	free(fh);
    190 	return NULL;
    191 }
    192 
    193 static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
    194 		struct efi_file_handle **new_handle,
    195 		s16 *file_name, u64 open_mode, u64 attributes)
    196 {
    197 	struct file_handle *fh = to_fh(file);
    198 
    199 	EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name,
    200 		  open_mode, attributes);
    201 
    202 	*new_handle = file_open(fh->fs, fh, file_name, open_mode);
    203 	if (!*new_handle)
    204 		return EFI_EXIT(EFI_NOT_FOUND);
    205 
    206 	return EFI_EXIT(EFI_SUCCESS);
    207 }
    208 
    209 static efi_status_t file_close(struct file_handle *fh)
    210 {
    211 	fs_closedir(fh->dirs);
    212 	free(fh);
    213 	return EFI_SUCCESS;
    214 }
    215 
    216 static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
    217 {
    218 	struct file_handle *fh = to_fh(file);
    219 	EFI_ENTRY("%p", file);
    220 	return EFI_EXIT(file_close(fh));
    221 }
    222 
    223 static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
    224 {
    225 	struct file_handle *fh = to_fh(file);
    226 	EFI_ENTRY("%p", file);
    227 	file_close(fh);
    228 	return EFI_EXIT(EFI_WARN_DELETE_FAILURE);
    229 }
    230 
    231 static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
    232 		void *buffer)
    233 {
    234 	loff_t actread;
    235 
    236 	if (fs_read(fh->path, (ulong)buffer, fh->offset,
    237 		    *buffer_size, &actread))
    238 		return EFI_DEVICE_ERROR;
    239 
    240 	*buffer_size = actread;
    241 	fh->offset += actread;
    242 
    243 	return EFI_SUCCESS;
    244 }
    245 
    246 static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
    247 		void *buffer)
    248 {
    249 	struct efi_file_info *info = buffer;
    250 	struct fs_dirent *dent;
    251 	unsigned int required_size;
    252 
    253 	if (!fh->dirs) {
    254 		assert(fh->offset == 0);
    255 		fh->dirs = fs_opendir(fh->path);
    256 		if (!fh->dirs)
    257 			return EFI_DEVICE_ERROR;
    258 	}
    259 
    260 	/*
    261 	 * So this is a bit awkward.  Since fs layer is stateful and we
    262 	 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
    263 	 * we might have to return without consuming the dent.. so we
    264 	 * have to stash it for next call.
    265 	 */
    266 	if (fh->dent) {
    267 		dent = fh->dent;
    268 		fh->dent = NULL;
    269 	} else {
    270 		dent = fs_readdir(fh->dirs);
    271 	}
    272 
    273 
    274 	if (!dent) {
    275 		/* no more files in directory: */
    276 		/* workaround shim.efi bug/quirk.. as find_boot_csv()
    277 		 * loops through directory contents, it initially calls
    278 		 * read w/ zero length buffer to find out how much mem
    279 		 * to allocate for the EFI_FILE_INFO, then allocates,
    280 		 * and then calls a 2nd time.  If we return size of
    281 		 * zero the first time, it happily passes that to
    282 		 * AllocateZeroPool(), and when that returns NULL it
    283 		 * thinks it is EFI_OUT_OF_RESOURCES.  So on first
    284 		 * call return a non-zero size:
    285 		 */
    286 		if (*buffer_size == 0)
    287 			*buffer_size = sizeof(*info);
    288 		else
    289 			*buffer_size = 0;
    290 		return EFI_SUCCESS;
    291 	}
    292 
    293 	/* check buffer size: */
    294 	required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1);
    295 	if (*buffer_size < required_size) {
    296 		*buffer_size = required_size;
    297 		fh->dent = dent;
    298 		return EFI_BUFFER_TOO_SMALL;
    299 	}
    300 
    301 	*buffer_size = required_size;
    302 	memset(info, 0, required_size);
    303 
    304 	info->size = required_size;
    305 	info->file_size = dent->size;
    306 	info->physical_size = dent->size;
    307 
    308 	if (dent->type == FS_DT_DIR)
    309 		info->attribute |= EFI_FILE_DIRECTORY;
    310 
    311 	ascii2unicode((u16 *)info->file_name, dent->name);
    312 
    313 	fh->offset++;
    314 
    315 	return EFI_SUCCESS;
    316 }
    317 
    318 static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
    319 					 efi_uintn_t *buffer_size, void *buffer)
    320 {
    321 	struct file_handle *fh = to_fh(file);
    322 	efi_status_t ret = EFI_SUCCESS;
    323 	u64 bs;
    324 
    325 	EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
    326 
    327 	if (!buffer_size || !buffer) {
    328 		ret = EFI_INVALID_PARAMETER;
    329 		goto error;
    330 	}
    331 
    332 	if (set_blk_dev(fh)) {
    333 		ret = EFI_DEVICE_ERROR;
    334 		goto error;
    335 	}
    336 
    337 	bs = *buffer_size;
    338 	if (fh->isdir)
    339 		ret = dir_read(fh, &bs, buffer);
    340 	else
    341 		ret = file_read(fh, &bs, buffer);
    342 	if (bs <= SIZE_MAX)
    343 		*buffer_size = bs;
    344 	else
    345 		*buffer_size = SIZE_MAX;
    346 
    347 error:
    348 	return EFI_EXIT(ret);
    349 }
    350 
    351 static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
    352 					  efi_uintn_t *buffer_size,
    353 					  void *buffer)
    354 {
    355 	struct file_handle *fh = to_fh(file);
    356 	efi_status_t ret = EFI_SUCCESS;
    357 	loff_t actwrite;
    358 
    359 	EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
    360 
    361 	if (set_blk_dev(fh)) {
    362 		ret = EFI_DEVICE_ERROR;
    363 		goto error;
    364 	}
    365 
    366 	if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size,
    367 		     &actwrite)) {
    368 		ret = EFI_DEVICE_ERROR;
    369 		goto error;
    370 	}
    371 
    372 	*buffer_size = actwrite;
    373 	fh->offset += actwrite;
    374 
    375 error:
    376 	return EFI_EXIT(ret);
    377 }
    378 
    379 static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
    380 					   efi_uintn_t *pos)
    381 {
    382 	struct file_handle *fh = to_fh(file);
    383 
    384 	EFI_ENTRY("%p, %p", file, pos);
    385 
    386 	if (fh->offset <= SIZE_MAX) {
    387 		*pos = fh->offset;
    388 		return EFI_EXIT(EFI_SUCCESS);
    389 	} else {
    390 		return EFI_EXIT(EFI_DEVICE_ERROR);
    391 	}
    392 }
    393 
    394 static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
    395 		efi_uintn_t pos)
    396 {
    397 	struct file_handle *fh = to_fh(file);
    398 	efi_status_t ret = EFI_SUCCESS;
    399 
    400 	EFI_ENTRY("%p, %zu", file, pos);
    401 
    402 	if (fh->isdir) {
    403 		if (pos != 0) {
    404 			ret = EFI_UNSUPPORTED;
    405 			goto error;
    406 		}
    407 		fs_closedir(fh->dirs);
    408 		fh->dirs = NULL;
    409 	}
    410 
    411 	if (pos == ~0ULL) {
    412 		loff_t file_size;
    413 
    414 		if (set_blk_dev(fh)) {
    415 			ret = EFI_DEVICE_ERROR;
    416 			goto error;
    417 		}
    418 
    419 		if (fs_size(fh->path, &file_size)) {
    420 			ret = EFI_DEVICE_ERROR;
    421 			goto error;
    422 		}
    423 
    424 		pos = file_size;
    425 	}
    426 
    427 	fh->offset = pos;
    428 
    429 error:
    430 	return EFI_EXIT(ret);
    431 }
    432 
    433 static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
    434 					    const efi_guid_t *info_type,
    435 					    efi_uintn_t *buffer_size,
    436 					    void *buffer)
    437 {
    438 	struct file_handle *fh = to_fh(file);
    439 	efi_status_t ret = EFI_SUCCESS;
    440 
    441 	EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer);
    442 
    443 	if (!guidcmp(info_type, &efi_file_info_guid)) {
    444 		struct efi_file_info *info = buffer;
    445 		char *filename = basename(fh);
    446 		unsigned int required_size;
    447 		loff_t file_size;
    448 
    449 		/* check buffer size: */
    450 		required_size = sizeof(*info) + 2 * (strlen(filename) + 1);
    451 		if (*buffer_size < required_size) {
    452 			*buffer_size = required_size;
    453 			ret = EFI_BUFFER_TOO_SMALL;
    454 			goto error;
    455 		}
    456 
    457 		if (set_blk_dev(fh)) {
    458 			ret = EFI_DEVICE_ERROR;
    459 			goto error;
    460 		}
    461 
    462 		if (fs_size(fh->path, &file_size)) {
    463 			ret = EFI_DEVICE_ERROR;
    464 			goto error;
    465 		}
    466 
    467 		memset(info, 0, required_size);
    468 
    469 		info->size = required_size;
    470 		info->file_size = file_size;
    471 		info->physical_size = file_size;
    472 
    473 		if (fh->isdir)
    474 			info->attribute |= EFI_FILE_DIRECTORY;
    475 
    476 		ascii2unicode((u16 *)info->file_name, filename);
    477 	} else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
    478 		struct efi_file_system_info *info = buffer;
    479 		disk_partition_t part;
    480 		efi_uintn_t required_size;
    481 		int r;
    482 
    483 		if (fh->fs->part >= 1)
    484 			r = part_get_info(fh->fs->desc, fh->fs->part, &part);
    485 		else
    486 			r = part_get_info_whole_disk(fh->fs->desc, &part);
    487 		if (r < 0) {
    488 			ret = EFI_DEVICE_ERROR;
    489 			goto error;
    490 		}
    491 		required_size = sizeof(info) + 2 *
    492 				(strlen((const char *)part.name) + 1);
    493 		if (*buffer_size < required_size) {
    494 			*buffer_size = required_size;
    495 			ret = EFI_BUFFER_TOO_SMALL;
    496 			goto error;
    497 		}
    498 
    499 		memset(info, 0, required_size);
    500 
    501 		info->size = required_size;
    502 		info->read_only = true;
    503 		info->volume_size = part.size * part.blksz;
    504 		info->free_space = 0;
    505 		info->block_size = part.blksz;
    506 		/*
    507 		 * TODO: The volume label is not available in U-Boot.
    508 		 * Use the partition name as substitute.
    509 		 */
    510 		ascii2unicode((u16 *)info->volume_label,
    511 			      (const char *)part.name);
    512 	} else {
    513 		ret = EFI_UNSUPPORTED;
    514 	}
    515 
    516 error:
    517 	return EFI_EXIT(ret);
    518 }
    519 
    520 static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
    521 					    const efi_guid_t *info_type,
    522 					    efi_uintn_t buffer_size,
    523 					    void *buffer)
    524 {
    525 	EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
    526 
    527 	return EFI_EXIT(EFI_UNSUPPORTED);
    528 }
    529 
    530 static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
    531 {
    532 	EFI_ENTRY("%p", file);
    533 	return EFI_EXIT(EFI_SUCCESS);
    534 }
    535 
    536 static const struct efi_file_handle efi_file_handle_protocol = {
    537 	.rev = EFI_FILE_PROTOCOL_REVISION,
    538 	.open = efi_file_open,
    539 	.close = efi_file_close,
    540 	.delete = efi_file_delete,
    541 	.read = efi_file_read,
    542 	.write = efi_file_write,
    543 	.getpos = efi_file_getpos,
    544 	.setpos = efi_file_setpos,
    545 	.getinfo = efi_file_getinfo,
    546 	.setinfo = efi_file_setinfo,
    547 	.flush = efi_file_flush,
    548 };
    549 
    550 struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
    551 {
    552 	struct efi_simple_file_system_protocol *v;
    553 	struct efi_file_handle *f;
    554 	efi_status_t ret;
    555 
    556 	v = efi_fs_from_path(fp);
    557 	if (!v)
    558 		return NULL;
    559 
    560 	EFI_CALL(ret = v->open_volume(v, &f));
    561 	if (ret != EFI_SUCCESS)
    562 		return NULL;
    563 
    564 	/* skip over device-path nodes before the file path: */
    565 	while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
    566 		fp = efi_dp_next(fp);
    567 
    568 	while (fp) {
    569 		struct efi_device_path_file_path *fdp =
    570 			container_of(fp, struct efi_device_path_file_path, dp);
    571 		struct efi_file_handle *f2;
    572 
    573 		if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
    574 			printf("bad file path!\n");
    575 			f->close(f);
    576 			return NULL;
    577 		}
    578 
    579 		EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str,
    580 				       EFI_FILE_MODE_READ, 0));
    581 		if (ret != EFI_SUCCESS)
    582 			return NULL;
    583 
    584 		fp = efi_dp_next(fp);
    585 
    586 		EFI_CALL(f->close(f));
    587 		f = f2;
    588 	}
    589 
    590 	return f;
    591 }
    592 
    593 static efi_status_t EFIAPI
    594 efi_open_volume(struct efi_simple_file_system_protocol *this,
    595 		struct efi_file_handle **root)
    596 {
    597 	struct file_system *fs = to_fs(this);
    598 
    599 	EFI_ENTRY("%p, %p", this, root);
    600 
    601 	*root = file_open(fs, NULL, NULL, 0);
    602 
    603 	return EFI_EXIT(EFI_SUCCESS);
    604 }
    605 
    606 struct efi_simple_file_system_protocol *
    607 efi_simple_file_system(struct blk_desc *desc, int part,
    608 		       struct efi_device_path *dp)
    609 {
    610 	struct file_system *fs;
    611 
    612 	fs = calloc(1, sizeof(*fs));
    613 	fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
    614 	fs->base.open_volume = efi_open_volume;
    615 	fs->desc = desc;
    616 	fs->part = part;
    617 	fs->dp = dp;
    618 
    619 	return &fs->base;
    620 }
    621