1*5193a33dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 2*5193a33dSArd Biesheuvel /* 3*5193a33dSArd Biesheuvel * Helper functions used by the EFI stub on multiple 4*5193a33dSArd Biesheuvel * architectures. This should be #included by the EFI stub 5*5193a33dSArd Biesheuvel * implementation files. 6*5193a33dSArd Biesheuvel * 7*5193a33dSArd Biesheuvel * Copyright 2011 Intel Corporation; author Matt Fleming 8*5193a33dSArd Biesheuvel */ 9*5193a33dSArd Biesheuvel 10*5193a33dSArd Biesheuvel #include <linux/efi.h> 11*5193a33dSArd Biesheuvel #include <asm/efi.h> 12*5193a33dSArd Biesheuvel 13*5193a33dSArd Biesheuvel #include "efistub.h" 14*5193a33dSArd Biesheuvel 15*5193a33dSArd Biesheuvel /* 16*5193a33dSArd Biesheuvel * Some firmware implementations have problems reading files in one go. 17*5193a33dSArd Biesheuvel * A read chunk size of 1MB seems to work for most platforms. 18*5193a33dSArd Biesheuvel * 19*5193a33dSArd Biesheuvel * Unfortunately, reading files in chunks triggers *other* bugs on some 20*5193a33dSArd Biesheuvel * platforms, so we provide a way to disable this workaround, which can 21*5193a33dSArd Biesheuvel * be done by passing "efi=nochunk" on the EFI boot stub command line. 22*5193a33dSArd Biesheuvel * 23*5193a33dSArd Biesheuvel * If you experience issues with initrd images being corrupt it's worth 24*5193a33dSArd Biesheuvel * trying efi=nochunk, but chunking is enabled by default on x86 because 25*5193a33dSArd Biesheuvel * there are far more machines that require the workaround than those that 26*5193a33dSArd Biesheuvel * break with it enabled. 27*5193a33dSArd Biesheuvel */ 28*5193a33dSArd Biesheuvel #define EFI_READ_CHUNK_SIZE SZ_1M 29*5193a33dSArd Biesheuvel 30*5193a33dSArd Biesheuvel struct file_info { 31*5193a33dSArd Biesheuvel efi_file_protocol_t *handle; 32*5193a33dSArd Biesheuvel u64 size; 33*5193a33dSArd Biesheuvel }; 34*5193a33dSArd Biesheuvel 35*5193a33dSArd Biesheuvel static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, 36*5193a33dSArd Biesheuvel void **handle, u64 *file_sz) 37*5193a33dSArd Biesheuvel { 38*5193a33dSArd Biesheuvel efi_file_protocol_t *h, *fh = __fh; 39*5193a33dSArd Biesheuvel efi_file_info_t *info; 40*5193a33dSArd Biesheuvel efi_status_t status; 41*5193a33dSArd Biesheuvel efi_guid_t info_guid = EFI_FILE_INFO_ID; 42*5193a33dSArd Biesheuvel unsigned long info_sz; 43*5193a33dSArd Biesheuvel 44*5193a33dSArd Biesheuvel status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, 0); 45*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) { 46*5193a33dSArd Biesheuvel efi_printk("Failed to open file: "); 47*5193a33dSArd Biesheuvel efi_char16_printk(filename_16); 48*5193a33dSArd Biesheuvel efi_printk("\n"); 49*5193a33dSArd Biesheuvel return status; 50*5193a33dSArd Biesheuvel } 51*5193a33dSArd Biesheuvel 52*5193a33dSArd Biesheuvel *handle = h; 53*5193a33dSArd Biesheuvel 54*5193a33dSArd Biesheuvel info_sz = 0; 55*5193a33dSArd Biesheuvel status = h->get_info(h, &info_guid, &info_sz, NULL); 56*5193a33dSArd Biesheuvel if (status != EFI_BUFFER_TOO_SMALL) { 57*5193a33dSArd Biesheuvel efi_printk("Failed to get file info size\n"); 58*5193a33dSArd Biesheuvel return status; 59*5193a33dSArd Biesheuvel } 60*5193a33dSArd Biesheuvel 61*5193a33dSArd Biesheuvel grow: 62*5193a33dSArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, info_sz, 63*5193a33dSArd Biesheuvel (void **)&info); 64*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) { 65*5193a33dSArd Biesheuvel efi_printk("Failed to alloc mem for file info\n"); 66*5193a33dSArd Biesheuvel return status; 67*5193a33dSArd Biesheuvel } 68*5193a33dSArd Biesheuvel 69*5193a33dSArd Biesheuvel status = h->get_info(h, &info_guid, &info_sz, info); 70*5193a33dSArd Biesheuvel if (status == EFI_BUFFER_TOO_SMALL) { 71*5193a33dSArd Biesheuvel efi_bs_call(free_pool, info); 72*5193a33dSArd Biesheuvel goto grow; 73*5193a33dSArd Biesheuvel } 74*5193a33dSArd Biesheuvel 75*5193a33dSArd Biesheuvel *file_sz = info->file_size; 76*5193a33dSArd Biesheuvel efi_bs_call(free_pool, info); 77*5193a33dSArd Biesheuvel 78*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) 79*5193a33dSArd Biesheuvel efi_printk("Failed to get initrd info\n"); 80*5193a33dSArd Biesheuvel 81*5193a33dSArd Biesheuvel return status; 82*5193a33dSArd Biesheuvel } 83*5193a33dSArd Biesheuvel 84*5193a33dSArd Biesheuvel static efi_status_t efi_file_read(efi_file_protocol_t *handle, 85*5193a33dSArd Biesheuvel unsigned long *size, void *addr) 86*5193a33dSArd Biesheuvel { 87*5193a33dSArd Biesheuvel return handle->read(handle, size, addr); 88*5193a33dSArd Biesheuvel } 89*5193a33dSArd Biesheuvel 90*5193a33dSArd Biesheuvel static efi_status_t efi_file_close(efi_file_protocol_t *handle) 91*5193a33dSArd Biesheuvel { 92*5193a33dSArd Biesheuvel return handle->close(handle); 93*5193a33dSArd Biesheuvel } 94*5193a33dSArd Biesheuvel 95*5193a33dSArd Biesheuvel static efi_status_t efi_open_volume(efi_loaded_image_t *image, 96*5193a33dSArd Biesheuvel efi_file_protocol_t **__fh) 97*5193a33dSArd Biesheuvel { 98*5193a33dSArd Biesheuvel efi_simple_file_system_protocol_t *io; 99*5193a33dSArd Biesheuvel efi_file_protocol_t *fh; 100*5193a33dSArd Biesheuvel efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; 101*5193a33dSArd Biesheuvel efi_status_t status; 102*5193a33dSArd Biesheuvel efi_handle_t handle = image->device_handle; 103*5193a33dSArd Biesheuvel 104*5193a33dSArd Biesheuvel status = efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io); 105*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) { 106*5193a33dSArd Biesheuvel efi_printk("Failed to handle fs_proto\n"); 107*5193a33dSArd Biesheuvel return status; 108*5193a33dSArd Biesheuvel } 109*5193a33dSArd Biesheuvel 110*5193a33dSArd Biesheuvel status = io->open_volume(io, &fh); 111*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) 112*5193a33dSArd Biesheuvel efi_printk("Failed to open volume\n"); 113*5193a33dSArd Biesheuvel else 114*5193a33dSArd Biesheuvel *__fh = fh; 115*5193a33dSArd Biesheuvel 116*5193a33dSArd Biesheuvel return status; 117*5193a33dSArd Biesheuvel } 118*5193a33dSArd Biesheuvel 119*5193a33dSArd Biesheuvel /* 120*5193a33dSArd Biesheuvel * Check the cmdline for a LILO-style file= arguments. 121*5193a33dSArd Biesheuvel * 122*5193a33dSArd Biesheuvel * We only support loading a file from the same filesystem as 123*5193a33dSArd Biesheuvel * the kernel image. 124*5193a33dSArd Biesheuvel */ 125*5193a33dSArd Biesheuvel efi_status_t handle_cmdline_files(efi_loaded_image_t *image, 126*5193a33dSArd Biesheuvel char *cmd_line, char *option_string, 127*5193a33dSArd Biesheuvel unsigned long max_addr, 128*5193a33dSArd Biesheuvel unsigned long *load_addr, 129*5193a33dSArd Biesheuvel unsigned long *load_size) 130*5193a33dSArd Biesheuvel { 131*5193a33dSArd Biesheuvel unsigned long efi_chunk_size = ULONG_MAX; 132*5193a33dSArd Biesheuvel struct file_info *files; 133*5193a33dSArd Biesheuvel unsigned long file_addr; 134*5193a33dSArd Biesheuvel u64 file_size_total; 135*5193a33dSArd Biesheuvel efi_file_protocol_t *fh = NULL; 136*5193a33dSArd Biesheuvel efi_status_t status; 137*5193a33dSArd Biesheuvel int nr_files; 138*5193a33dSArd Biesheuvel char *str; 139*5193a33dSArd Biesheuvel int i, j, k; 140*5193a33dSArd Biesheuvel 141*5193a33dSArd Biesheuvel if (IS_ENABLED(CONFIG_X86) && !nochunk()) 142*5193a33dSArd Biesheuvel efi_chunk_size = EFI_READ_CHUNK_SIZE; 143*5193a33dSArd Biesheuvel 144*5193a33dSArd Biesheuvel file_addr = 0; 145*5193a33dSArd Biesheuvel file_size_total = 0; 146*5193a33dSArd Biesheuvel 147*5193a33dSArd Biesheuvel str = cmd_line; 148*5193a33dSArd Biesheuvel 149*5193a33dSArd Biesheuvel j = 0; /* See close_handles */ 150*5193a33dSArd Biesheuvel 151*5193a33dSArd Biesheuvel if (!load_addr || !load_size) 152*5193a33dSArd Biesheuvel return EFI_INVALID_PARAMETER; 153*5193a33dSArd Biesheuvel 154*5193a33dSArd Biesheuvel *load_addr = 0; 155*5193a33dSArd Biesheuvel *load_size = 0; 156*5193a33dSArd Biesheuvel 157*5193a33dSArd Biesheuvel if (!str || !*str) 158*5193a33dSArd Biesheuvel return EFI_SUCCESS; 159*5193a33dSArd Biesheuvel 160*5193a33dSArd Biesheuvel for (nr_files = 0; *str; nr_files++) { 161*5193a33dSArd Biesheuvel str = strstr(str, option_string); 162*5193a33dSArd Biesheuvel if (!str) 163*5193a33dSArd Biesheuvel break; 164*5193a33dSArd Biesheuvel 165*5193a33dSArd Biesheuvel str += strlen(option_string); 166*5193a33dSArd Biesheuvel 167*5193a33dSArd Biesheuvel /* Skip any leading slashes */ 168*5193a33dSArd Biesheuvel while (*str == '/' || *str == '\\') 169*5193a33dSArd Biesheuvel str++; 170*5193a33dSArd Biesheuvel 171*5193a33dSArd Biesheuvel while (*str && *str != ' ' && *str != '\n') 172*5193a33dSArd Biesheuvel str++; 173*5193a33dSArd Biesheuvel } 174*5193a33dSArd Biesheuvel 175*5193a33dSArd Biesheuvel if (!nr_files) 176*5193a33dSArd Biesheuvel return EFI_SUCCESS; 177*5193a33dSArd Biesheuvel 178*5193a33dSArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, 179*5193a33dSArd Biesheuvel nr_files * sizeof(*files), (void **)&files); 180*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) { 181*5193a33dSArd Biesheuvel pr_efi_err("Failed to alloc mem for file handle list\n"); 182*5193a33dSArd Biesheuvel goto fail; 183*5193a33dSArd Biesheuvel } 184*5193a33dSArd Biesheuvel 185*5193a33dSArd Biesheuvel str = cmd_line; 186*5193a33dSArd Biesheuvel for (i = 0; i < nr_files; i++) { 187*5193a33dSArd Biesheuvel struct file_info *file; 188*5193a33dSArd Biesheuvel efi_char16_t filename_16[256]; 189*5193a33dSArd Biesheuvel efi_char16_t *p; 190*5193a33dSArd Biesheuvel 191*5193a33dSArd Biesheuvel str = strstr(str, option_string); 192*5193a33dSArd Biesheuvel if (!str) 193*5193a33dSArd Biesheuvel break; 194*5193a33dSArd Biesheuvel 195*5193a33dSArd Biesheuvel str += strlen(option_string); 196*5193a33dSArd Biesheuvel 197*5193a33dSArd Biesheuvel file = &files[i]; 198*5193a33dSArd Biesheuvel p = filename_16; 199*5193a33dSArd Biesheuvel 200*5193a33dSArd Biesheuvel /* Skip any leading slashes */ 201*5193a33dSArd Biesheuvel while (*str == '/' || *str == '\\') 202*5193a33dSArd Biesheuvel str++; 203*5193a33dSArd Biesheuvel 204*5193a33dSArd Biesheuvel while (*str && *str != ' ' && *str != '\n') { 205*5193a33dSArd Biesheuvel if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) 206*5193a33dSArd Biesheuvel break; 207*5193a33dSArd Biesheuvel 208*5193a33dSArd Biesheuvel if (*str == '/') { 209*5193a33dSArd Biesheuvel *p++ = '\\'; 210*5193a33dSArd Biesheuvel str++; 211*5193a33dSArd Biesheuvel } else { 212*5193a33dSArd Biesheuvel *p++ = *str++; 213*5193a33dSArd Biesheuvel } 214*5193a33dSArd Biesheuvel } 215*5193a33dSArd Biesheuvel 216*5193a33dSArd Biesheuvel *p = '\0'; 217*5193a33dSArd Biesheuvel 218*5193a33dSArd Biesheuvel /* Only open the volume once. */ 219*5193a33dSArd Biesheuvel if (!i) { 220*5193a33dSArd Biesheuvel status = efi_open_volume(image, &fh); 221*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) 222*5193a33dSArd Biesheuvel goto free_files; 223*5193a33dSArd Biesheuvel } 224*5193a33dSArd Biesheuvel 225*5193a33dSArd Biesheuvel status = efi_file_size(fh, filename_16, (void **)&file->handle, 226*5193a33dSArd Biesheuvel &file->size); 227*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) 228*5193a33dSArd Biesheuvel goto close_handles; 229*5193a33dSArd Biesheuvel 230*5193a33dSArd Biesheuvel file_size_total += file->size; 231*5193a33dSArd Biesheuvel } 232*5193a33dSArd Biesheuvel 233*5193a33dSArd Biesheuvel if (file_size_total) { 234*5193a33dSArd Biesheuvel unsigned long addr; 235*5193a33dSArd Biesheuvel 236*5193a33dSArd Biesheuvel /* 237*5193a33dSArd Biesheuvel * Multiple files need to be at consecutive addresses in memory, 238*5193a33dSArd Biesheuvel * so allocate enough memory for all the files. This is used 239*5193a33dSArd Biesheuvel * for loading multiple files. 240*5193a33dSArd Biesheuvel */ 241*5193a33dSArd Biesheuvel status = efi_allocate_pages(file_size_total, &file_addr, max_addr); 242*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) { 243*5193a33dSArd Biesheuvel pr_efi_err("Failed to alloc highmem for files\n"); 244*5193a33dSArd Biesheuvel goto close_handles; 245*5193a33dSArd Biesheuvel } 246*5193a33dSArd Biesheuvel 247*5193a33dSArd Biesheuvel /* We've run out of free low memory. */ 248*5193a33dSArd Biesheuvel if (file_addr > max_addr) { 249*5193a33dSArd Biesheuvel pr_efi_err("We've run out of free low memory\n"); 250*5193a33dSArd Biesheuvel status = EFI_INVALID_PARAMETER; 251*5193a33dSArd Biesheuvel goto free_file_total; 252*5193a33dSArd Biesheuvel } 253*5193a33dSArd Biesheuvel 254*5193a33dSArd Biesheuvel addr = file_addr; 255*5193a33dSArd Biesheuvel for (j = 0; j < nr_files; j++) { 256*5193a33dSArd Biesheuvel unsigned long size; 257*5193a33dSArd Biesheuvel 258*5193a33dSArd Biesheuvel size = files[j].size; 259*5193a33dSArd Biesheuvel while (size) { 260*5193a33dSArd Biesheuvel unsigned long chunksize; 261*5193a33dSArd Biesheuvel 262*5193a33dSArd Biesheuvel if (size > efi_chunk_size) 263*5193a33dSArd Biesheuvel chunksize = efi_chunk_size; 264*5193a33dSArd Biesheuvel else 265*5193a33dSArd Biesheuvel chunksize = size; 266*5193a33dSArd Biesheuvel 267*5193a33dSArd Biesheuvel status = efi_file_read(files[j].handle, 268*5193a33dSArd Biesheuvel &chunksize, 269*5193a33dSArd Biesheuvel (void *)addr); 270*5193a33dSArd Biesheuvel if (status != EFI_SUCCESS) { 271*5193a33dSArd Biesheuvel pr_efi_err("Failed to read file\n"); 272*5193a33dSArd Biesheuvel goto free_file_total; 273*5193a33dSArd Biesheuvel } 274*5193a33dSArd Biesheuvel addr += chunksize; 275*5193a33dSArd Biesheuvel size -= chunksize; 276*5193a33dSArd Biesheuvel } 277*5193a33dSArd Biesheuvel 278*5193a33dSArd Biesheuvel efi_file_close(files[j].handle); 279*5193a33dSArd Biesheuvel } 280*5193a33dSArd Biesheuvel 281*5193a33dSArd Biesheuvel } 282*5193a33dSArd Biesheuvel 283*5193a33dSArd Biesheuvel efi_bs_call(free_pool, files); 284*5193a33dSArd Biesheuvel 285*5193a33dSArd Biesheuvel *load_addr = file_addr; 286*5193a33dSArd Biesheuvel *load_size = file_size_total; 287*5193a33dSArd Biesheuvel 288*5193a33dSArd Biesheuvel return status; 289*5193a33dSArd Biesheuvel 290*5193a33dSArd Biesheuvel free_file_total: 291*5193a33dSArd Biesheuvel efi_free(file_size_total, file_addr); 292*5193a33dSArd Biesheuvel 293*5193a33dSArd Biesheuvel close_handles: 294*5193a33dSArd Biesheuvel for (k = j; k < i; k++) 295*5193a33dSArd Biesheuvel efi_file_close(files[k].handle); 296*5193a33dSArd Biesheuvel free_files: 297*5193a33dSArd Biesheuvel efi_bs_call(free_pool, files); 298*5193a33dSArd Biesheuvel fail: 299*5193a33dSArd Biesheuvel *load_addr = 0; 300*5193a33dSArd Biesheuvel *load_size = 0; 301*5193a33dSArd Biesheuvel 302*5193a33dSArd Biesheuvel return status; 303*5193a33dSArd Biesheuvel } 304