|
| 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 | +staticconstefi_guid_tefi_guid_load_file2_protocol= |
| 16 | +EFI_LOAD_FILE2_PROTOCOL_GUID; |
| 17 | + |
| 18 | +staticefi_status_tEFIAPI |
| 19 | +efi_load_file2_initrd(structefi_load_file_protocol*this, |
| 20 | +structefi_device_path*file_path,boolboot_policy, |
| 21 | +efi_uintn_t*buffer_size,void*buffer); |
| 22 | + |
| 23 | +staticconststructefi_load_file_protocolefi_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 | +staticconststructefi_initrd_dpdp= { |
| 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 | +staticloff_tget_file_size(constchar*dev,constchar*part,constchar*file, |
| 58 | +efi_status_t*status) |
| 59 | +{ |
| 60 | +loff_tsz=0; |
| 61 | +intret; |
| 62 | + |
| 63 | +ret=fs_set_blk_dev(dev,part,FS_TYPE_ANY); |
| 64 | +if (ret) { |
| 65 | +*status=EFI_NO_MEDIA; |
| 66 | +gotoout; |
| 67 | +} |
| 68 | + |
| 69 | +ret=fs_size(file,&sz); |
| 70 | +if (ret) { |
| 71 | +sz=0; |
| 72 | +*status=EFI_NOT_FOUND; |
| 73 | +gotoout; |
| 74 | +} |
| 75 | + |
| 76 | +out: |
| 77 | +returnsz; |
| 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 | +staticefi_status_tEFIAPI |
| 96 | +efi_load_file2_initrd(structefi_load_file_protocol*this, |
| 97 | +structefi_device_path*file_path,boolboot_policy, |
| 98 | +efi_uintn_t*buffer_size,void*buffer) |
| 99 | +{ |
| 100 | +constchar*filespec=CONFIG_EFI_INITRD_FILESPEC; |
| 101 | +efi_status_tstatus=EFI_NOT_FOUND; |
| 102 | +loff_tfile_sz=0,read_sz=0; |
| 103 | +char*dev,*part,*file; |
| 104 | +char*s; |
| 105 | +intret; |
| 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 | +gotoout; |
| 113 | + |
| 114 | +if (!this||this!=&efi_lf2_protocol|| |
| 115 | + !buffer_size) { |
| 116 | +status=EFI_INVALID_PARAMETER; |
| 117 | +gotoout; |
| 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 | +gotoout; |
| 124 | +} |
| 125 | + |
| 126 | +if (boot_policy) { |
| 127 | +status=EFI_UNSUPPORTED; |
| 128 | +gotoout; |
| 129 | +} |
| 130 | + |
| 131 | +/* expect something like 'mmc 0:1 initrd.cpio.gz' */ |
| 132 | +dev=strsep(&s," "); |
| 133 | +if (!dev) |
| 134 | +gotoout; |
| 135 | +part=strsep(&s," "); |
| 136 | +if (!part) |
| 137 | +gotoout; |
| 138 | +file=strsep(&s," "); |
| 139 | +if (!file) |
| 140 | +gotoout; |
| 141 | + |
| 142 | +file_sz=get_file_size(dev,part,file,&status); |
| 143 | +if (!file_sz) |
| 144 | +gotoout; |
| 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 | +gotoout; |
| 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 | +gotoout; |
| 160 | +*buffer_size=read_sz; |
| 161 | + |
| 162 | +status=EFI_SUCCESS; |
| 163 | +} |
| 164 | + |
| 165 | +out: |
| 166 | +free(s); |
| 167 | +returnEFI_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_tefi_initrd_register(void) |
| 180 | +{ |
| 181 | +efi_handle_tefi_initrd_handle=NULL; |
| 182 | +efi_status_tret; |
| 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 | +returnret; |
| 198 | +} |