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