14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 281a0bc39SRoy Franz /* 381a0bc39SRoy Franz * Copyright (C) 2013 Linaro Ltd; <roy.franz@linaro.org> 481a0bc39SRoy Franz */ 581a0bc39SRoy Franz #include <linux/efi.h> 681a0bc39SRoy Franz #include <asm/efi.h> 781a0bc39SRoy Franz 8eeff7d63SArd Biesheuvel #include "efistub.h" 9eeff7d63SArd Biesheuvel 102a55280aSArd Biesheuvel static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID; 112a55280aSArd Biesheuvel 122a55280aSArd Biesheuvel struct efi_arm_entry_state *efi_entry_state; 132a55280aSArd Biesheuvel 142a55280aSArd Biesheuvel static void get_cpu_state(u32 *cpsr, u32 *sctlr) 152a55280aSArd Biesheuvel { 162a55280aSArd Biesheuvel asm("mrs %0, cpsr" : "=r"(*cpsr)); 172a55280aSArd Biesheuvel if ((*cpsr & MODE_MASK) == HYP_MODE) 182a55280aSArd Biesheuvel asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr)); 192a55280aSArd Biesheuvel else 202a55280aSArd Biesheuvel asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr)); 212a55280aSArd Biesheuvel } 222a55280aSArd Biesheuvel 23cd33a5c1SArd Biesheuvel efi_status_t check_platform_features(void) 242ec0f0a3SArd Biesheuvel { 252a55280aSArd Biesheuvel efi_status_t status; 262a55280aSArd Biesheuvel u32 cpsr, sctlr; 272ec0f0a3SArd Biesheuvel int block; 282ec0f0a3SArd Biesheuvel 292a55280aSArd Biesheuvel get_cpu_state(&cpsr, &sctlr); 302a55280aSArd Biesheuvel 312a55280aSArd Biesheuvel efi_info("Entering in %s mode with MMU %sabled\n", 322a55280aSArd Biesheuvel ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC", 332a55280aSArd Biesheuvel (sctlr & 1) ? "en" : "dis"); 342a55280aSArd Biesheuvel 352a55280aSArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, 362a55280aSArd Biesheuvel sizeof(*efi_entry_state), 372a55280aSArd Biesheuvel (void **)&efi_entry_state); 382a55280aSArd Biesheuvel if (status != EFI_SUCCESS) { 392a55280aSArd Biesheuvel efi_err("allocate_pool() failed\n"); 402a55280aSArd Biesheuvel return status; 412a55280aSArd Biesheuvel } 422a55280aSArd Biesheuvel 432a55280aSArd Biesheuvel efi_entry_state->cpsr_before_ebs = cpsr; 442a55280aSArd Biesheuvel efi_entry_state->sctlr_before_ebs = sctlr; 452a55280aSArd Biesheuvel 462a55280aSArd Biesheuvel status = efi_bs_call(install_configuration_table, &cpu_state_guid, 472a55280aSArd Biesheuvel efi_entry_state); 482a55280aSArd Biesheuvel if (status != EFI_SUCCESS) { 492a55280aSArd Biesheuvel efi_err("install_configuration_table() failed\n"); 502a55280aSArd Biesheuvel goto free_state; 512a55280aSArd Biesheuvel } 522a55280aSArd Biesheuvel 532ec0f0a3SArd Biesheuvel /* non-LPAE kernels can run anywhere */ 542ec0f0a3SArd Biesheuvel if (!IS_ENABLED(CONFIG_ARM_LPAE)) 552ec0f0a3SArd Biesheuvel return EFI_SUCCESS; 562ec0f0a3SArd Biesheuvel 572ec0f0a3SArd Biesheuvel /* LPAE kernels need compatible hardware */ 582ec0f0a3SArd Biesheuvel block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0); 592ec0f0a3SArd Biesheuvel if (block < 5) { 60793473c2SArvind Sankar efi_err("This LPAE kernel is not supported by your CPU\n"); 612a55280aSArd Biesheuvel status = EFI_UNSUPPORTED; 622a55280aSArd Biesheuvel goto drop_table; 632ec0f0a3SArd Biesheuvel } 642ec0f0a3SArd Biesheuvel return EFI_SUCCESS; 652a55280aSArd Biesheuvel 662a55280aSArd Biesheuvel drop_table: 672a55280aSArd Biesheuvel efi_bs_call(install_configuration_table, &cpu_state_guid, NULL); 682a55280aSArd Biesheuvel free_state: 692a55280aSArd Biesheuvel efi_bs_call(free_pool, efi_entry_state); 702a55280aSArd Biesheuvel return status; 712a55280aSArd Biesheuvel } 722a55280aSArd Biesheuvel 732a55280aSArd Biesheuvel void efi_handle_post_ebs_state(void) 742a55280aSArd Biesheuvel { 752a55280aSArd Biesheuvel get_cpu_state(&efi_entry_state->cpsr_after_ebs, 762a55280aSArd Biesheuvel &efi_entry_state->sctlr_after_ebs); 772ec0f0a3SArd Biesheuvel } 782ec0f0a3SArd Biesheuvel 79801820beSArd Biesheuvel static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID; 80801820beSArd Biesheuvel 81cd33a5c1SArd Biesheuvel struct screen_info *alloc_screen_info(void) 82801820beSArd Biesheuvel { 83801820beSArd Biesheuvel struct screen_info *si; 84801820beSArd Biesheuvel efi_status_t status; 85801820beSArd Biesheuvel 86801820beSArd Biesheuvel /* 87801820beSArd Biesheuvel * Unlike on arm64, where we can directly fill out the screen_info 88801820beSArd Biesheuvel * structure from the stub, we need to allocate a buffer to hold 89801820beSArd Biesheuvel * its contents while we hand over to the kernel proper from the 90801820beSArd Biesheuvel * decompressor. 91801820beSArd Biesheuvel */ 92966291f6SArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, 93801820beSArd Biesheuvel sizeof(*si), (void **)&si); 94801820beSArd Biesheuvel 95801820beSArd Biesheuvel if (status != EFI_SUCCESS) 96801820beSArd Biesheuvel return NULL; 97801820beSArd Biesheuvel 98966291f6SArd Biesheuvel status = efi_bs_call(install_configuration_table, 99801820beSArd Biesheuvel &screen_info_guid, si); 100801820beSArd Biesheuvel if (status == EFI_SUCCESS) 101801820beSArd Biesheuvel return si; 102801820beSArd Biesheuvel 103966291f6SArd Biesheuvel efi_bs_call(free_pool, si); 104801820beSArd Biesheuvel return NULL; 105801820beSArd Biesheuvel } 106801820beSArd Biesheuvel 107cd33a5c1SArd Biesheuvel void free_screen_info(struct screen_info *si) 108801820beSArd Biesheuvel { 109801820beSArd Biesheuvel if (!si) 110801820beSArd Biesheuvel return; 111801820beSArd Biesheuvel 112966291f6SArd Biesheuvel efi_bs_call(install_configuration_table, &screen_info_guid, NULL); 113966291f6SArd Biesheuvel efi_bs_call(free_pool, si); 114801820beSArd Biesheuvel } 115801820beSArd Biesheuvel 116cd33a5c1SArd Biesheuvel efi_status_t handle_kernel_image(unsigned long *image_addr, 117318532bfSArd Biesheuvel unsigned long *image_size, 118318532bfSArd Biesheuvel unsigned long *reserve_addr, 119318532bfSArd Biesheuvel unsigned long *reserve_size, 120318532bfSArd Biesheuvel efi_loaded_image_t *image) 121318532bfSArd Biesheuvel { 122762cd288SArd Biesheuvel const int slack = TEXT_OFFSET - 5 * PAGE_SIZE; 123762cd288SArd Biesheuvel int alloc_size = MAX_UNCOMP_KERNEL_SIZE + EFI_PHYS_ALIGN; 124762cd288SArd Biesheuvel unsigned long alloc_base, kernel_base; 125318532bfSArd Biesheuvel efi_status_t status; 126318532bfSArd Biesheuvel 12741cd96faSArd Biesheuvel /* 128762cd288SArd Biesheuvel * Allocate space for the decompressed kernel as low as possible. 129762cd288SArd Biesheuvel * The region should be 16 MiB aligned, but the first 'slack' bytes 130762cd288SArd Biesheuvel * are not used by Linux, so we allow those to be occupied by the 131762cd288SArd Biesheuvel * firmware. 13241cd96faSArd Biesheuvel */ 133762cd288SArd Biesheuvel status = efi_low_alloc_above(alloc_size, EFI_PAGE_SIZE, &alloc_base, 0x0); 134318532bfSArd Biesheuvel if (status != EFI_SUCCESS) { 135793473c2SArvind Sankar efi_err("Unable to allocate memory for uncompressed kernel.\n"); 13681a0bc39SRoy Franz return status; 13781a0bc39SRoy Franz } 13881a0bc39SRoy Franz 139762cd288SArd Biesheuvel if ((alloc_base % EFI_PHYS_ALIGN) > slack) { 140762cd288SArd Biesheuvel /* 141762cd288SArd Biesheuvel * More than 'slack' bytes are already occupied at the base of 142762cd288SArd Biesheuvel * the allocation, so we need to advance to the next 16 MiB block. 143762cd288SArd Biesheuvel */ 144762cd288SArd Biesheuvel kernel_base = round_up(alloc_base, EFI_PHYS_ALIGN); 145762cd288SArd Biesheuvel efi_info("Free memory starts at 0x%lx, setting kernel_base to 0x%lx\n", 146762cd288SArd Biesheuvel alloc_base, kernel_base); 147762cd288SArd Biesheuvel } else { 148762cd288SArd Biesheuvel kernel_base = round_down(alloc_base, EFI_PHYS_ALIGN); 149762cd288SArd Biesheuvel } 150762cd288SArd Biesheuvel 151762cd288SArd Biesheuvel *reserve_addr = kernel_base + slack; 152762cd288SArd Biesheuvel *reserve_size = MAX_UNCOMP_KERNEL_SIZE; 153762cd288SArd Biesheuvel 154762cd288SArd Biesheuvel /* now free the parts that we will not use */ 155762cd288SArd Biesheuvel if (*reserve_addr > alloc_base) { 156762cd288SArd Biesheuvel efi_bs_call(free_pages, alloc_base, 157762cd288SArd Biesheuvel (*reserve_addr - alloc_base) / EFI_PAGE_SIZE); 158762cd288SArd Biesheuvel alloc_size -= *reserve_addr - alloc_base; 159762cd288SArd Biesheuvel } 160762cd288SArd Biesheuvel efi_bs_call(free_pages, *reserve_addr + MAX_UNCOMP_KERNEL_SIZE, 161762cd288SArd Biesheuvel (alloc_size - MAX_UNCOMP_KERNEL_SIZE) / EFI_PAGE_SIZE); 162762cd288SArd Biesheuvel 163762cd288SArd Biesheuvel *image_addr = kernel_base + TEXT_OFFSET; 16481a0bc39SRoy Franz *image_size = 0; 165762cd288SArd Biesheuvel 166762cd288SArd Biesheuvel efi_debug("image addr == 0x%lx, reserve_addr == 0x%lx\n", 167762cd288SArd Biesheuvel *image_addr, *reserve_addr); 168762cd288SArd Biesheuvel 16981a0bc39SRoy Franz return EFI_SUCCESS; 17081a0bc39SRoy Franz } 171