blob: e2a806302303cbadb1b98d3d4c9c17231575dc46 [file] [log] [blame]
Ilias Apalodimas3510ba72020-02-21 09:55:45 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2020, Linaro Limited
4 */
5
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +02006#define LOG_CATEGORY LOGC_EFI
Ilias Apalodimas3510ba72020-02-21 09:55:45 +02007#include <common.h>
Ilias Apalodimas3510ba72020-02-21 09:55:45 +02008#include <efi_loader.h>
9#include <efi_load_initrd.h>
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020010#include <efi_variable.h>
Ilias Apalodimasdaa924a2020-12-31 12:33:23 +020011#include <fs.h>
12#include <malloc.h>
13#include <mapmem.h>
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020014
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020015static efi_status_t EFIAPI
16efi_load_file2_initrd(struct efi_load_file_protocol *this,
17 struct efi_device_path *file_path, bool boot_policy,
18 efi_uintn_t *buffer_size, void *buffer);
19
20static const struct efi_load_file_protocol efi_lf2_protocol = {
21 .load_file = efi_load_file2_initrd,
22};
23
24/*
25 * Device path defined by Linux to identify the handle providing the
26 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
27 */
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020028static const struct efi_initrd_dp dp_lf2_handle = {
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020029 .vendor = {
30 {
31 DEVICE_PATH_TYPE_MEDIA_DEVICE,
32 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020033 sizeof(dp_lf2_handle.vendor),
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020034 },
35 EFI_INITRD_MEDIA_GUID,
36 },
37 .end = {
38 DEVICE_PATH_TYPE_END,
39 DEVICE_PATH_SUB_TYPE_END,
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020040 sizeof(dp_lf2_handle.end),
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020041 }
42};
43
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020044static efi_handle_t efi_initrd_handle;
45
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020046/**
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020047 * get_initrd_fp() - Get initrd device path from a FilePathList device path
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020048 *
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020049 * @initrd_fp: the final initrd filepath
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020050 *
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020051 * Return: status code. Caller must free initrd_fp
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020052 */
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020053static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020054{
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020055 const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
56 struct efi_device_path *dp = NULL;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020057
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020058 /*
59 * if bootmgr is setup with and initrd, the device path will be
60 * in the FilePathList[] of our load options in Boot####.
61 * The first device path of the multi instance device path will
62 * start with a VenMedia and the initrds will follow.
63 *
64 * If the device path is not found return EFI_INVALID_PARAMETER.
65 * We can then use this specific return value and not install the
66 * protocol, while allowing the boot to continue
67 */
68 dp = efi_get_dp_from_boot(lf2_initrd_guid);
69 if (!dp)
70 return EFI_INVALID_PARAMETER;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020071
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020072 *initrd_fp = dp;
73 return EFI_SUCCESS;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020074}
75
76/**
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020077 * efi_load_file2_initrd() - load initial RAM disk
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020078 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +020079 * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
80 * in order to load an initial RAM disk requested by the Linux kernel stub.
81 *
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020082 * See the UEFI spec for details.
83 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +020084 * @this: EFI_LOAD_FILE2_PROTOCOL instance
85 * @file_path: media device path of the file, "" in this case
86 * @boot_policy: must be false
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020087 * @buffer_size: size of allocated buffer
88 * @buffer: buffer to load the file
89 *
90 * Return: status code
91 */
92static efi_status_t EFIAPI
93efi_load_file2_initrd(struct efi_load_file_protocol *this,
94 struct efi_device_path *file_path, bool boot_policy,
95 efi_uintn_t *buffer_size, void *buffer)
96{
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +020097 struct efi_device_path *initrd_fp = NULL;
98 efi_status_t ret = EFI_NOT_FOUND;
99 struct efi_file_handle *f = NULL;
100 efi_uintn_t bs;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200101
102 EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
103 buffer_size, buffer);
104
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200105 if (!this || this != &efi_lf2_protocol ||
106 !buffer_size) {
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200107 ret = EFI_INVALID_PARAMETER;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200108 goto out;
109 }
110
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200111 if (file_path->type != dp_lf2_handle.end.type ||
112 file_path->sub_type != dp_lf2_handle.end.sub_type) {
113 ret = EFI_INVALID_PARAMETER;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200114 goto out;
115 }
116
117 if (boot_policy) {
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200118 ret = EFI_UNSUPPORTED;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200119 goto out;
120 }
121
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200122 ret = get_initrd_fp(&initrd_fp);
123 if (ret != EFI_SUCCESS)
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200124 goto out;
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200125
126 /* Open file */
127 f = efi_file_from_path(initrd_fp);
128 if (!f) {
129 log_err("Can't find initrd specified in Boot####\n");
130 ret = EFI_NOT_FOUND;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200131 goto out;
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200132 }
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200133
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200134 /* Get file size */
135 ret = efi_file_size(f, &bs);
136 if (ret != EFI_SUCCESS)
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200137 goto out;
138
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200139 if (!buffer || *buffer_size < bs) {
140 ret = EFI_BUFFER_TOO_SMALL;
141 *buffer_size = bs;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200142 } else {
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200143 ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
144 *buffer_size = bs;
145 }
146
147out:
148 efi_free_pool(initrd_fp);
149 if (f)
150 EFI_CALL(f->close(f));
151 return EFI_EXIT(ret);
152}
153
154/**
155 * check_initrd() - Determine if the file defined as an initrd in Boot####
156 * load_options device path is present
157 *
158 * Return: status code
159 */
160static efi_status_t check_initrd(void)
161{
162 struct efi_device_path *initrd_fp = NULL;
163 struct efi_file_handle *f;
164 efi_status_t ret;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200165
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200166 ret = get_initrd_fp(&initrd_fp);
167 if (ret != EFI_SUCCESS)
168 goto out;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200169
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200170 /*
171 * If the file is not found, but the file path is set, return an error
172 * and trigger the bootmgr fallback
173 */
174 f = efi_file_from_path(initrd_fp);
175 if (!f) {
176 log_err("Can't find initrd specified in Boot####\n");
177 ret = EFI_NOT_FOUND;
178 goto out;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200179 }
180
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200181 EFI_CALL(f->close(f));
182
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200183out:
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200184 efi_free_pool(initrd_fp);
185 return ret;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200186}
187
188/**
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200189 * efi_initrd_register() - create handle for loading initial RAM disk
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200190 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200191 * This function creates a new handle and installs a Linux specific vendor
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200192 * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200193 * to identify the handle and then calls the LoadFile service of the
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200194 * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200195 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200196 * Return: status code
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200197 */
198efi_status_t efi_initrd_register(void)
199{
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200200 efi_status_t ret;
201
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200202 /*
203 * Allow the user to continue if Boot#### file path is not set for
204 * an initrd
205 */
206 ret = check_initrd();
207 if (ret == EFI_INVALID_PARAMETER)
208 return EFI_SUCCESS;
209 if (ret != EFI_SUCCESS)
210 return ret;
211
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200212 ret = EFI_CALL(efi_install_multiple_protocol_interfaces
213 (&efi_initrd_handle,
214 /* initramfs */
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200215 &efi_guid_device_path, &dp_lf2_handle,
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200216 /* LOAD_FILE2 */
217 &efi_guid_load_file2_protocol,
218 (void *)&efi_lf2_protocol,
219 NULL));
220
221 return ret;
222}
Ilias Apalodimasb307e3d2021-03-17 21:55:00 +0200223
224/**
225 * efi_initrd_deregister() - delete the handle for loading initial RAM disk
226 *
227 * This will delete the handle containing the Linux specific vendor device
228 * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
229 *
230 * Return: status code
231 */
232void efi_initrd_deregister(void)
233{
234 efi_delete_handle(efi_initrd_handle);
235 efi_initrd_handle = NULL;
236}