1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <efi/protocol/loaded-image.h>
6#include <efi/protocol/simple-file-system.h>
7
8#include <xefi.h>
9#include <stdio.h>
10
11#ifndef VERBOSE
12#define xprintf(...) do {} while (0);
13#else
14#define xprintf(fmt...) printf(fmt)
15#endif
16
17efi_file_protocol* xefi_open_file(char16_t* filename) {
18    efi_loaded_image_protocol* loaded;
19    efi_status r;
20    efi_file_protocol* file = NULL;
21
22    r = xefi_open_protocol(gImg, &LoadedImageProtocol, (void**)&loaded);
23    if (r) {
24        xprintf("LoadFile: Cannot open LoadedImageProtocol (%s)\n", xefi_strerror(r));
25        goto exit0;
26    }
27
28#if 0
29    printf("Img DeviceHandle='%s'\n", HandleToString(loaded->DeviceHandle));
30    printf("Img FilePath='%s'\n", DevicePathToStr(loaded->FilePath));
31    printf("Img Base=%lx Size=%lx\n", loaded->ImageBase, loaded->ImageSize);
32#endif
33
34    efi_simple_file_system_protocol* sfs;
35    r = xefi_open_protocol(loaded->DeviceHandle, &SimpleFileSystemProtocol, (void**)&sfs);
36    if (r) {
37        xprintf("LoadFile: Cannot open SimpleFileSystemProtocol (%s)\n", xefi_strerror(r));
38        goto exit1;
39    }
40
41    efi_file_protocol* root;
42    r = sfs->OpenVolume(sfs, &root);
43    if (r) {
44        xprintf("LoadFile: Cannot open root volume (%s)\n", xefi_strerror(r));
45        goto exit2;
46    }
47
48    r = root->Open(root, &file, filename, EFI_FILE_MODE_READ, 0);
49    if (r) {
50        xprintf("LoadFile: Cannot open file (%s)\n", xefi_strerror(r));
51        goto exit3;
52    }
53
54exit3:
55    root->Close(root);
56exit2:
57    xefi_close_protocol(loaded->DeviceHandle, &SimpleFileSystemProtocol);
58exit1:
59    xefi_close_protocol(gImg, &LoadedImageProtocol);
60exit0:
61    return file;
62}
63
64void* xefi_read_file(efi_file_protocol* file, size_t* _sz, size_t front_bytes) {
65    efi_status r;
66    size_t pages = 0;
67    void* data = NULL;
68
69    char buf[512];
70    size_t sz = sizeof(buf);
71    efi_file_info* finfo = (void*)buf;
72    r = file->GetInfo(file, &FileInfoGuid, &sz, finfo);
73    if (r) {
74        xprintf("LoadFile: Cannot get FileInfo (%s)\n", xefi_strerror(r));
75        return NULL;
76    }
77
78    pages = (finfo->FileSize + front_bytes + 4095) / 4096;
79    r = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages, (efi_physical_addr *)&data);
80    if (r) {
81        xprintf("LoadFile: Cannot allocate buffer (%s)\n", xefi_strerror(r));
82        return NULL;
83    }
84
85    sz = finfo->FileSize;
86    r = file->Read(file, &sz, data + front_bytes);
87    if (r) {
88        xprintf("LoadFile: Error reading file (%s)\n", xefi_strerror(r));
89        gBS->FreePages((efi_physical_addr)data, pages);
90        return NULL;
91    }
92    if (sz != finfo->FileSize) {
93        xprintf("LoadFile: Short read\n");
94        gBS->FreePages((efi_physical_addr)data, pages);
95        return NULL;
96    }
97    *_sz = finfo->FileSize;
98
99    return data + front_bytes;
100}
101
102void* xefi_load_file(char16_t* filename, size_t* _sz, size_t front_bytes) {
103    efi_file_protocol* file = xefi_open_file(filename);
104    if (!file) {
105        return NULL;
106    }
107    void* data = xefi_read_file(file, _sz, front_bytes);
108    file->Close(file);
109    return data;
110}
111