Merge tag 'u-boot-stm32-20200203' of https://gitlab.denx.de/u-boot/custodians/u-boot-stm
[oweals/u-boot.git] / lib / efi_loader / efi_load_initrd.c
1 // 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
15 static const efi_guid_t efi_guid_load_file2_protocol =
16                 EFI_LOAD_FILE2_PROTOCOL_GUID;
17
18 static efi_status_t EFIAPI
19 efi_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
23 static 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  */
31 static 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  *
50  * @dev:                        device to read from. i.e "mmc"
51  * @part:                       device partition. i.e "0:1"
52  * @file:                       name fo file
53  * @status:                     EFI exit code in case of failure
54  *
55  * Return:                      size of file
56  */
57 static 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
76 out:
77         return sz;
78 }
79
80 /**
81  * load_file2() - get information about random number generation
82  *
83  * This function implement the LoadFile2() service in order to load an initram
84  * disk requested by the Linux kernel stub.
85  * See the UEFI spec for details.
86  *
87  * @this:                       loadfile2 protocol instance
88  * @file_path:                  relative path of the file. "" in this case
89  * @boot_policy:                must be false for Loadfile2
90  * @buffer_size:                size of allocated buffer
91  * @buffer:                     buffer to load the file
92  *
93  * Return:                      status code
94  */
95 static efi_status_t EFIAPI
96 efi_load_file2_initrd(struct efi_load_file_protocol *this,
97                       struct efi_device_path *file_path, bool boot_policy,
98                       efi_uintn_t *buffer_size, void *buffer)
99 {
100         const char *filespec = CONFIG_EFI_INITRD_FILESPEC;
101         efi_status_t status = EFI_NOT_FOUND;
102         loff_t file_sz = 0, read_sz = 0;
103         char *dev, *part, *file;
104         char *s;
105         int ret;
106
107         EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
108                   buffer_size, buffer);
109
110         s = strdup(filespec);
111         if (!s)
112                 goto out;
113
114         if (!this || this != &efi_lf2_protocol ||
115             !buffer_size) {
116                 status = EFI_INVALID_PARAMETER;
117                 goto out;
118         }
119
120         if (file_path->type != dp.end.type ||
121             file_path->sub_type != dp.end.sub_type) {
122                 status = EFI_INVALID_PARAMETER;
123                 goto out;
124         }
125
126         if (boot_policy) {
127                 status = EFI_UNSUPPORTED;
128                 goto out;
129         }
130
131         /* expect something like 'mmc 0:1 initrd.cpio.gz' */
132         dev = strsep(&s, " ");
133         if (!dev)
134                 goto out;
135         part = strsep(&s, " ");
136         if (!part)
137                 goto out;
138         file = strsep(&s, " ");
139         if (!file)
140                 goto out;
141
142         file_sz = get_file_size(dev, part, file, &status);
143         if (!file_sz)
144                 goto out;
145
146         if (!buffer || *buffer_size < file_sz) {
147                 status = EFI_BUFFER_TOO_SMALL;
148                 *buffer_size = file_sz;
149         } else {
150                 ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
151                 if (ret) {
152                         status = EFI_NO_MEDIA;
153                         goto out;
154                 }
155
156                 ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
157                               &read_sz);
158                 if (ret || read_sz != file_sz)
159                         goto out;
160                 *buffer_size = read_sz;
161
162                 status = EFI_SUCCESS;
163         }
164
165 out:
166         free(s);
167         return EFI_EXIT(status);
168 }
169
170 /**
171  * efi_initrd_register() - Register a handle and loadfile2 protocol
172  *
173  * This function creates a new handle and installs a linux specific GUID
174  * to handle initram disk loading during boot.
175  * See the UEFI spec for details.
176  *
177  * Return:                      status code
178  */
179 efi_status_t efi_initrd_register(void)
180 {
181         efi_handle_t efi_initrd_handle = NULL;
182         efi_status_t ret;
183
184         /*
185          * Set up the handle with the EFI_LOAD_FILE2_PROTOCOL which Linux may
186          * use to load the initial ramdisk.
187          */
188         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 }