blob: d517d686c33001274e22e6b702aaad230fa563f8 [file] [log] [blame]
Ilias Apalodimasec80b472020-02-21 09:55:45 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2020, Linaro Limited
4 */
5
6#include <common.h>
7#include <env.h>
8#include <malloc.h>
9#include <mapmem.h>
10#include <dm.h>
11#include <fs.h>
12#include <efi_loader.h>
13#include <efi_load_initrd.h>
14
15static const efi_guid_t efi_guid_load_file2_protocol =
16 EFI_LOAD_FILE2_PROTOCOL_GUID;
17
18static efi_status_t EFIAPI
19efi_load_file2_initrd(struct efi_load_file_protocol *this,
20 struct efi_device_path *file_path, bool boot_policy,
21 efi_uintn_t *buffer_size, void *buffer);
22
23static const struct efi_load_file_protocol efi_lf2_protocol = {
24 .load_file = efi_load_file2_initrd,
25};
26
27/*
28 * Device path defined by Linux to identify the handle providing the
29 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
30 */
31static const struct efi_initrd_dp dp = {
32 .vendor = {
33 {
34 DEVICE_PATH_TYPE_MEDIA_DEVICE,
35 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
36 sizeof(dp.vendor),
37 },
38 EFI_INITRD_MEDIA_GUID,
39 },
40 .end = {
41 DEVICE_PATH_TYPE_END,
42 DEVICE_PATH_SUB_TYPE_END,
43 sizeof(dp.end),
44 }
45};
46
47/**
48 * get_file_size() - retrieve the size of initramfs, set efi status on error
49 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020050 * @dev: device to read from, e.g. "mmc"
51 * @part: device partition, e.g. "0:1"
52 * @file: name of file
Ilias Apalodimasec80b472020-02-21 09:55:45 +020053 * @status: EFI exit code in case of failure
54 *
55 * Return: size of file
56 */
57static loff_t get_file_size(const char *dev, const char *part, const char *file,
58 efi_status_t *status)
59{
60 loff_t sz = 0;
61 int ret;
62
63 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
64 if (ret) {
65 *status = EFI_NO_MEDIA;
66 goto out;
67 }
68
69 ret = fs_size(file, &sz);
70 if (ret) {
71 sz = 0;
72 *status = EFI_NOT_FOUND;
73 goto out;
74 }
75
76out:
77 return sz;
78}
79
80/**
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020081 * efi_load_file2initrd() - load initial RAM disk
Ilias Apalodimasec80b472020-02-21 09:55:45 +020082 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020083 * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
84 * in order to load an initial RAM disk requested by the Linux kernel stub.
85 *
Ilias Apalodimasec80b472020-02-21 09:55:45 +020086 * See the UEFI spec for details.
87 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020088 * @this: EFI_LOAD_FILE2_PROTOCOL instance
89 * @file_path: media device path of the file, "" in this case
90 * @boot_policy: must be false
Ilias Apalodimasec80b472020-02-21 09:55:45 +020091 * @buffer_size: size of allocated buffer
92 * @buffer: buffer to load the file
93 *
94 * Return: status code
95 */
96static efi_status_t EFIAPI
97efi_load_file2_initrd(struct efi_load_file_protocol *this,
98 struct efi_device_path *file_path, bool boot_policy,
99 efi_uintn_t *buffer_size, void *buffer)
100{
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200101 char *filespec;
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200102 efi_status_t status = EFI_NOT_FOUND;
103 loff_t file_sz = 0, read_sz = 0;
104 char *dev, *part, *file;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200105 char *pos;
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200106 int ret;
107
108 EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
109 buffer_size, buffer);
110
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200111 filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
112 if (!filespec)
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200113 goto out;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200114 pos = filespec;
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200115
116 if (!this || this != &efi_lf2_protocol ||
117 !buffer_size) {
118 status = EFI_INVALID_PARAMETER;
119 goto out;
120 }
121
122 if (file_path->type != dp.end.type ||
123 file_path->sub_type != dp.end.sub_type) {
124 status = EFI_INVALID_PARAMETER;
125 goto out;
126 }
127
128 if (boot_policy) {
129 status = EFI_UNSUPPORTED;
130 goto out;
131 }
132
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200133 /*
134 * expect a string with three space separated parts:
135 *
136 * * a block device type, e.g. "mmc"
137 * * a device and partition identifier, e.g. "0:1"
138 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
139 */
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200140 dev = strsep(&pos, " ");
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200141 if (!dev)
142 goto out;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200143 part = strsep(&pos, " ");
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200144 if (!part)
145 goto out;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200146 file = strsep(&pos, " ");
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200147 if (!file)
148 goto out;
149
150 file_sz = get_file_size(dev, part, file, &status);
151 if (!file_sz)
152 goto out;
153
154 if (!buffer || *buffer_size < file_sz) {
155 status = EFI_BUFFER_TOO_SMALL;
156 *buffer_size = file_sz;
157 } else {
158 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
159 if (ret) {
160 status = EFI_NO_MEDIA;
161 goto out;
162 }
163
164 ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
165 &read_sz);
166 if (ret || read_sz != file_sz)
167 goto out;
168 *buffer_size = read_sz;
169
170 status = EFI_SUCCESS;
171 }
172
173out:
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200174 free(filespec);
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200175 return EFI_EXIT(status);
176}
177
178/**
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200179 * efi_initrd_register() - create handle for loading initial RAM disk
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200180 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200181 * This function creates a new handle and installs a Linux specific vendor
182 * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
183 * to identify the handle and then calls the LoadFile service of the
184 * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200185 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200186 * Return: status code
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200187 */
188efi_status_t efi_initrd_register(void)
189{
190 efi_handle_t efi_initrd_handle = NULL;
191 efi_status_t ret;
192
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200193 ret = EFI_CALL(efi_install_multiple_protocol_interfaces
194 (&efi_initrd_handle,
195 /* initramfs */
196 &efi_guid_device_path, &dp,
197 /* LOAD_FILE2 */
198 &efi_guid_load_file2_protocol,
199 (void *)&efi_lf2_protocol,
200 NULL));
201
202 return ret;
203}