blob: 4bf3b5ef68440fdc40e7837da3a65b67ff3ee05d [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
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
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 */
28static const struct efi_initrd_dp dp = {
29 .vendor = {
30 {
31 DEVICE_PATH_TYPE_MEDIA_DEVICE,
32 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
33 sizeof(dp.vendor),
34 },
35 EFI_INITRD_MEDIA_GUID,
36 },
37 .end = {
38 DEVICE_PATH_TYPE_END,
39 DEVICE_PATH_SUB_TYPE_END,
40 sizeof(dp.end),
41 }
42};
43
44/**
45 * get_file_size() - retrieve the size of initramfs, set efi status on error
46 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +020047 * @dev: device to read from, e.g. "mmc"
48 * @part: device partition, e.g. "0:1"
49 * @file: name of file
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020050 * @status: EFI exit code in case of failure
51 *
52 * Return: size of file
53 */
54static loff_t get_file_size(const char *dev, const char *part, const char *file,
55 efi_status_t *status)
56{
57 loff_t sz = 0;
58 int ret;
59
60 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
61 if (ret) {
62 *status = EFI_NO_MEDIA;
63 goto out;
64 }
65
66 ret = fs_size(file, &sz);
67 if (ret) {
68 sz = 0;
69 *status = EFI_NOT_FOUND;
70 goto out;
71 }
72
73out:
74 return sz;
75}
76
77/**
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +020078 * efi_load_file2initrd() - load initial RAM disk
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020079 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +020080 * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
81 * in order to load an initial RAM disk requested by the Linux kernel stub.
82 *
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020083 * See the UEFI spec for details.
84 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +020085 * @this: EFI_LOAD_FILE2_PROTOCOL instance
86 * @file_path: media device path of the file, "" in this case
87 * @boot_policy: must be false
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020088 * @buffer_size: size of allocated buffer
89 * @buffer: buffer to load the file
90 *
91 * Return: status code
92 */
93static efi_status_t EFIAPI
94efi_load_file2_initrd(struct efi_load_file_protocol *this,
95 struct efi_device_path *file_path, bool boot_policy,
96 efi_uintn_t *buffer_size, void *buffer)
97{
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +020098 char *filespec;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +020099 efi_status_t status = EFI_NOT_FOUND;
100 loff_t file_sz = 0, read_sz = 0;
101 char *dev, *part, *file;
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200102 char *pos;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200103 int ret;
104
105 EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
106 buffer_size, buffer);
107
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200108 filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
109 if (!filespec)
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200110 goto out;
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200111 pos = filespec;
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200112
113 if (!this || this != &efi_lf2_protocol ||
114 !buffer_size) {
115 status = EFI_INVALID_PARAMETER;
116 goto out;
117 }
118
119 if (file_path->type != dp.end.type ||
120 file_path->sub_type != dp.end.sub_type) {
121 status = EFI_INVALID_PARAMETER;
122 goto out;
123 }
124
125 if (boot_policy) {
126 status = EFI_UNSUPPORTED;
127 goto out;
128 }
129
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200130 /*
131 * expect a string with three space separated parts:
132 *
133 * * a block device type, e.g. "mmc"
134 * * a device and partition identifier, e.g. "0:1"
135 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
136 */
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200137 dev = strsep(&pos, " ");
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200138 if (!dev)
139 goto out;
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200140 part = strsep(&pos, " ");
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200141 if (!part)
142 goto out;
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200143 file = strsep(&pos, " ");
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200144 if (!file)
145 goto out;
146
147 file_sz = get_file_size(dev, part, file, &status);
148 if (!file_sz)
149 goto out;
150
151 if (!buffer || *buffer_size < file_sz) {
152 status = EFI_BUFFER_TOO_SMALL;
153 *buffer_size = file_sz;
154 } else {
155 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
156 if (ret) {
157 status = EFI_NO_MEDIA;
158 goto out;
159 }
160
161 ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
162 &read_sz);
163 if (ret || read_sz != file_sz)
164 goto out;
165 *buffer_size = read_sz;
166
167 status = EFI_SUCCESS;
168 }
169
170out:
Heinrich Schuchardt988b0f52020-10-03 12:50:52 +0200171 free(filespec);
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200172 return EFI_EXIT(status);
173}
174
175/**
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200176 * efi_initrd_register() - create handle for loading initial RAM disk
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200177 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200178 * This function creates a new handle and installs a Linux specific vendor
179 * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
180 * to identify the handle and then calls the LoadFile service of the
181 * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200182 *
Heinrich Schuchardtd1d89332020-10-03 12:44:31 +0200183 * Return: status code
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200184 */
185efi_status_t efi_initrd_register(void)
186{
187 efi_handle_t efi_initrd_handle = NULL;
188 efi_status_t ret;
189
Ilias Apalodimas3510ba72020-02-21 09:55:45 +0200190 ret = EFI_CALL(efi_install_multiple_protocol_interfaces
191 (&efi_initrd_handle,
192 /* initramfs */
193 &efi_guid_device_path, &dp,
194 /* LOAD_FILE2 */
195 &efi_guid_load_file2_protocol,
196 (void *)&efi_lf2_protocol,
197 NULL));
198
199 return ret;
200}