blob: b9ee8839054f45aad47ef739a7b63cd6bd736aab [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>
Ilias Apalodimasec80b472020-02-21 09:55:45 +02007#include <efi_loader.h>
8#include <efi_load_initrd.h>
Ilias Apalodimas0c444522020-12-31 12:33:23 +02009#include <fs.h>
10#include <malloc.h>
11#include <mapmem.h>
Ilias Apalodimasec80b472020-02-21 09:55:45 +020012
Ilias Apalodimasec80b472020-02-21 09:55:45 +020013static efi_status_t EFIAPI
14efi_load_file2_initrd(struct efi_load_file_protocol *this,
15 struct efi_device_path *file_path, bool boot_policy,
16 efi_uintn_t *buffer_size, void *buffer);
17
18static const struct efi_load_file_protocol efi_lf2_protocol = {
19 .load_file = efi_load_file2_initrd,
20};
21
22/*
23 * Device path defined by Linux to identify the handle providing the
24 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
25 */
26static const struct efi_initrd_dp dp = {
27 .vendor = {
28 {
29 DEVICE_PATH_TYPE_MEDIA_DEVICE,
30 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
31 sizeof(dp.vendor),
32 },
33 EFI_INITRD_MEDIA_GUID,
34 },
35 .end = {
36 DEVICE_PATH_TYPE_END,
37 DEVICE_PATH_SUB_TYPE_END,
38 sizeof(dp.end),
39 }
40};
41
42/**
43 * get_file_size() - retrieve the size of initramfs, set efi status on error
44 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020045 * @dev: device to read from, e.g. "mmc"
46 * @part: device partition, e.g. "0:1"
47 * @file: name of file
Ilias Apalodimasec80b472020-02-21 09:55:45 +020048 * @status: EFI exit code in case of failure
49 *
50 * Return: size of file
51 */
52static loff_t get_file_size(const char *dev, const char *part, const char *file,
53 efi_status_t *status)
54{
55 loff_t sz = 0;
56 int ret;
57
58 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
59 if (ret) {
60 *status = EFI_NO_MEDIA;
61 goto out;
62 }
63
64 ret = fs_size(file, &sz);
65 if (ret) {
66 sz = 0;
67 *status = EFI_NOT_FOUND;
68 goto out;
69 }
70
71out:
72 return sz;
73}
74
75/**
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020076 * efi_load_file2initrd() - load initial RAM disk
Ilias Apalodimasec80b472020-02-21 09:55:45 +020077 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020078 * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
79 * in order to load an initial RAM disk requested by the Linux kernel stub.
80 *
Ilias Apalodimasec80b472020-02-21 09:55:45 +020081 * See the UEFI spec for details.
82 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +020083 * @this: EFI_LOAD_FILE2_PROTOCOL instance
84 * @file_path: media device path of the file, "" in this case
85 * @boot_policy: must be false
Ilias Apalodimasec80b472020-02-21 09:55:45 +020086 * @buffer_size: size of allocated buffer
87 * @buffer: buffer to load the file
88 *
89 * Return: status code
90 */
91static efi_status_t EFIAPI
92efi_load_file2_initrd(struct efi_load_file_protocol *this,
93 struct efi_device_path *file_path, bool boot_policy,
94 efi_uintn_t *buffer_size, void *buffer)
95{
Heinrich Schuchardte2aff332020-10-03 12:50:52 +020096 char *filespec;
Ilias Apalodimasec80b472020-02-21 09:55:45 +020097 efi_status_t status = EFI_NOT_FOUND;
98 loff_t file_sz = 0, read_sz = 0;
99 char *dev, *part, *file;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200100 char *pos;
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200101 int ret;
102
103 EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
104 buffer_size, buffer);
105
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200106 filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
107 if (!filespec)
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200108 goto out;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200109 pos = filespec;
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200110
111 if (!this || this != &efi_lf2_protocol ||
112 !buffer_size) {
113 status = EFI_INVALID_PARAMETER;
114 goto out;
115 }
116
117 if (file_path->type != dp.end.type ||
118 file_path->sub_type != dp.end.sub_type) {
119 status = EFI_INVALID_PARAMETER;
120 goto out;
121 }
122
123 if (boot_policy) {
124 status = EFI_UNSUPPORTED;
125 goto out;
126 }
127
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200128 /*
129 * expect a string with three space separated parts:
130 *
131 * * a block device type, e.g. "mmc"
132 * * a device and partition identifier, e.g. "0:1"
133 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
134 */
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200135 dev = strsep(&pos, " ");
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200136 if (!dev)
137 goto out;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200138 part = strsep(&pos, " ");
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200139 if (!part)
140 goto out;
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200141 file = strsep(&pos, " ");
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200142 if (!file)
143 goto out;
144
145 file_sz = get_file_size(dev, part, file, &status);
146 if (!file_sz)
147 goto out;
148
149 if (!buffer || *buffer_size < file_sz) {
150 status = EFI_BUFFER_TOO_SMALL;
151 *buffer_size = file_sz;
152 } else {
153 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
154 if (ret) {
155 status = EFI_NO_MEDIA;
156 goto out;
157 }
158
159 ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
160 &read_sz);
161 if (ret || read_sz != file_sz)
162 goto out;
163 *buffer_size = read_sz;
164
165 status = EFI_SUCCESS;
166 }
167
168out:
Heinrich Schuchardte2aff332020-10-03 12:50:52 +0200169 free(filespec);
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200170 return EFI_EXIT(status);
171}
172
173/**
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200174 * efi_initrd_register() - create handle for loading initial RAM disk
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200175 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200176 * This function creates a new handle and installs a Linux specific vendor
177 * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
178 * to identify the handle and then calls the LoadFile service of the
179 * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200180 *
Heinrich Schuchardt5cd28e12020-10-03 12:44:31 +0200181 * Return: status code
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200182 */
183efi_status_t efi_initrd_register(void)
184{
185 efi_handle_t efi_initrd_handle = NULL;
186 efi_status_t ret;
187
Ilias Apalodimasec80b472020-02-21 09:55:45 +0200188 ret = EFI_CALL(efi_install_multiple_protocol_interfaces
189 (&efi_initrd_handle,
190 /* initramfs */
191 &efi_guid_device_path, &dp,
192 /* LOAD_FILE2 */
193 &efi_guid_load_file2_protocol,
194 (void *)&efi_lf2_protocol,
195 NULL));
196
197 return ret;
198}