file.c (6341B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Helper functions used by the EFI stub on multiple 4 * architectures. This should be #included by the EFI stub 5 * implementation files. 6 * 7 * Copyright 2011 Intel Corporation; author Matt Fleming 8 */ 9 10#include <linux/efi.h> 11#include <asm/efi.h> 12 13#include "efistub.h" 14 15#define MAX_FILENAME_SIZE 256 16 17/* 18 * Some firmware implementations have problems reading files in one go. 19 * A read chunk size of 1MB seems to work for most platforms. 20 * 21 * Unfortunately, reading files in chunks triggers *other* bugs on some 22 * platforms, so we provide a way to disable this workaround, which can 23 * be done by passing "efi=nochunk" on the EFI boot stub command line. 24 * 25 * If you experience issues with initrd images being corrupt it's worth 26 * trying efi=nochunk, but chunking is enabled by default on x86 because 27 * there are far more machines that require the workaround than those that 28 * break with it enabled. 29 */ 30#define EFI_READ_CHUNK_SIZE SZ_1M 31 32struct finfo { 33 efi_file_info_t info; 34 efi_char16_t filename[MAX_FILENAME_SIZE]; 35}; 36 37static efi_status_t efi_open_file(efi_file_protocol_t *volume, 38 struct finfo *fi, 39 efi_file_protocol_t **handle, 40 unsigned long *file_size) 41{ 42 efi_guid_t info_guid = EFI_FILE_INFO_ID; 43 efi_file_protocol_t *fh; 44 unsigned long info_sz; 45 efi_status_t status; 46 47 status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); 48 if (status != EFI_SUCCESS) { 49 efi_err("Failed to open file: %ls\n", fi->filename); 50 return status; 51 } 52 53 info_sz = sizeof(struct finfo); 54 status = fh->get_info(fh, &info_guid, &info_sz, fi); 55 if (status != EFI_SUCCESS) { 56 efi_err("Failed to get file info\n"); 57 fh->close(fh); 58 return status; 59 } 60 61 *handle = fh; 62 *file_size = fi->info.file_size; 63 return EFI_SUCCESS; 64} 65 66static efi_status_t efi_open_volume(efi_loaded_image_t *image, 67 efi_file_protocol_t **fh) 68{ 69 efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; 70 efi_simple_file_system_protocol_t *io; 71 efi_status_t status; 72 73 status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto, 74 (void **)&io); 75 if (status != EFI_SUCCESS) { 76 efi_err("Failed to handle fs_proto\n"); 77 return status; 78 } 79 80 status = io->open_volume(io, fh); 81 if (status != EFI_SUCCESS) 82 efi_err("Failed to open volume\n"); 83 84 return status; 85} 86 87static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, 88 const efi_char16_t *prefix, int prefix_size, 89 efi_char16_t *result, int result_len) 90{ 91 int prefix_len = prefix_size / 2; 92 bool found = false; 93 int i; 94 95 for (i = prefix_len; i < cmdline_len; i++) { 96 if (!memcmp(&cmdline[i - prefix_len], prefix, prefix_size)) { 97 found = true; 98 break; 99 } 100 } 101 102 if (!found) 103 return 0; 104 105 /* Skip any leading slashes */ 106 while (i < cmdline_len && (cmdline[i] == L'/' || cmdline[i] == L'\\')) 107 i++; 108 109 while (--result_len > 0 && i < cmdline_len) { 110 efi_char16_t c = cmdline[i++]; 111 112 if (c == L'\0' || c == L'\n' || c == L' ') 113 break; 114 else if (c == L'/') 115 /* Replace UNIX dir separators with EFI standard ones */ 116 *result++ = L'\\'; 117 else 118 *result++ = c; 119 } 120 *result = L'\0'; 121 return i; 122} 123 124/* 125 * Check the cmdline for a LILO-style file= arguments. 126 * 127 * We only support loading a file from the same filesystem as 128 * the kernel image. 129 */ 130efi_status_t handle_cmdline_files(efi_loaded_image_t *image, 131 const efi_char16_t *optstr, 132 int optstr_size, 133 unsigned long soft_limit, 134 unsigned long hard_limit, 135 unsigned long *load_addr, 136 unsigned long *load_size) 137{ 138 const efi_char16_t *cmdline = image->load_options; 139 int cmdline_len = image->load_options_size; 140 unsigned long efi_chunk_size = ULONG_MAX; 141 efi_file_protocol_t *volume = NULL; 142 efi_file_protocol_t *file; 143 unsigned long alloc_addr; 144 unsigned long alloc_size; 145 efi_status_t status; 146 int offset; 147 148 if (!load_addr || !load_size) 149 return EFI_INVALID_PARAMETER; 150 151 efi_apply_loadoptions_quirk((const void **)&cmdline, &cmdline_len); 152 cmdline_len /= sizeof(*cmdline); 153 154 if (IS_ENABLED(CONFIG_X86) && !efi_nochunk) 155 efi_chunk_size = EFI_READ_CHUNK_SIZE; 156 157 alloc_addr = alloc_size = 0; 158 do { 159 struct finfo fi; 160 unsigned long size; 161 void *addr; 162 163 offset = find_file_option(cmdline, cmdline_len, 164 optstr, optstr_size, 165 fi.filename, ARRAY_SIZE(fi.filename)); 166 167 if (!offset) 168 break; 169 170 cmdline += offset; 171 cmdline_len -= offset; 172 173 if (!volume) { 174 status = efi_open_volume(image, &volume); 175 if (status != EFI_SUCCESS) 176 return status; 177 } 178 179 status = efi_open_file(volume, &fi, &file, &size); 180 if (status != EFI_SUCCESS) 181 goto err_close_volume; 182 183 /* 184 * Check whether the existing allocation can contain the next 185 * file. This condition will also trigger naturally during the 186 * first (and typically only) iteration of the loop, given that 187 * alloc_size == 0 in that case. 188 */ 189 if (round_up(alloc_size + size, EFI_ALLOC_ALIGN) > 190 round_up(alloc_size, EFI_ALLOC_ALIGN)) { 191 unsigned long old_addr = alloc_addr; 192 193 status = EFI_OUT_OF_RESOURCES; 194 if (soft_limit < hard_limit) 195 status = efi_allocate_pages(alloc_size + size, 196 &alloc_addr, 197 soft_limit); 198 if (status == EFI_OUT_OF_RESOURCES) 199 status = efi_allocate_pages(alloc_size + size, 200 &alloc_addr, 201 hard_limit); 202 if (status != EFI_SUCCESS) { 203 efi_err("Failed to allocate memory for files\n"); 204 goto err_close_file; 205 } 206 207 if (old_addr != 0) { 208 /* 209 * This is not the first time we've gone 210 * around this loop, and so we are loading 211 * multiple files that need to be concatenated 212 * and returned in a single buffer. 213 */ 214 memcpy((void *)alloc_addr, (void *)old_addr, alloc_size); 215 efi_free(alloc_size, old_addr); 216 } 217 } 218 219 addr = (void *)alloc_addr + alloc_size; 220 alloc_size += size; 221 222 while (size) { 223 unsigned long chunksize = min(size, efi_chunk_size); 224 225 status = file->read(file, &chunksize, addr); 226 if (status != EFI_SUCCESS) { 227 efi_err("Failed to read file\n"); 228 goto err_close_file; 229 } 230 addr += chunksize; 231 size -= chunksize; 232 } 233 file->close(file); 234 } while (offset > 0); 235 236 *load_addr = alloc_addr; 237 *load_size = alloc_size; 238 239 if (volume) 240 volume->close(volume); 241 return EFI_SUCCESS; 242 243err_close_file: 244 file->close(file); 245 246err_close_volume: 247 volume->close(volume); 248 efi_free(alloc_size, alloc_addr); 249 return status; 250}