1*f48485abSMatt Coster // SPDX-License-Identifier: GPL-2.0-only OR MIT 2*f48485abSMatt Coster /* Copyright (c) 2024 Imagination Technologies Ltd. */ 3*f48485abSMatt Coster 4*f48485abSMatt Coster #include "pvr_device.h" 5*f48485abSMatt Coster #include "pvr_fw.h" 6*f48485abSMatt Coster 7*f48485abSMatt Coster #include <drm/drm_device.h> 8*f48485abSMatt Coster #include <drm/drm_print.h> 9*f48485abSMatt Coster 10*f48485abSMatt Coster #include <linux/elf.h> 11*f48485abSMatt Coster #include <linux/string.h> 12*f48485abSMatt Coster #include <linux/types.h> 13*f48485abSMatt Coster 14*f48485abSMatt Coster /** 15*f48485abSMatt Coster * pvr_fw_process_elf_command_stream() - Process ELF firmware image and populate 16*f48485abSMatt Coster * firmware sections 17*f48485abSMatt Coster * @pvr_dev: Device pointer. 18*f48485abSMatt Coster * @fw: Pointer to firmware image. 19*f48485abSMatt Coster * @fw_code_ptr: Pointer to FW code section. 20*f48485abSMatt Coster * @fw_data_ptr: Pointer to FW data section. 21*f48485abSMatt Coster * @fw_core_code_ptr: Pointer to FW coremem code section. 22*f48485abSMatt Coster * @fw_core_data_ptr: Pointer to FW coremem data section. 23*f48485abSMatt Coster * 24*f48485abSMatt Coster * Returns : 25*f48485abSMatt Coster * * 0 on success, or 26*f48485abSMatt Coster * * -EINVAL on any error in ELF command stream. 27*f48485abSMatt Coster */ 28*f48485abSMatt Coster int 29*f48485abSMatt Coster pvr_fw_process_elf_command_stream(struct pvr_device *pvr_dev, const u8 *fw, 30*f48485abSMatt Coster u8 *fw_code_ptr, u8 *fw_data_ptr, 31*f48485abSMatt Coster u8 *fw_core_code_ptr, u8 *fw_core_data_ptr) 32*f48485abSMatt Coster { 33*f48485abSMatt Coster struct elf32_hdr *header = (struct elf32_hdr *)fw; 34*f48485abSMatt Coster struct elf32_phdr *program_header = (struct elf32_phdr *)(fw + header->e_phoff); 35*f48485abSMatt Coster struct drm_device *drm_dev = from_pvr_device(pvr_dev); 36*f48485abSMatt Coster int err; 37*f48485abSMatt Coster 38*f48485abSMatt Coster for (u32 entry = 0; entry < header->e_phnum; entry++, program_header++) { 39*f48485abSMatt Coster void *write_addr; 40*f48485abSMatt Coster 41*f48485abSMatt Coster /* Only consider loadable entries in the ELF segment table */ 42*f48485abSMatt Coster if (program_header->p_type != PT_LOAD) 43*f48485abSMatt Coster continue; 44*f48485abSMatt Coster 45*f48485abSMatt Coster err = pvr_fw_find_mmu_segment(pvr_dev, program_header->p_vaddr, 46*f48485abSMatt Coster program_header->p_memsz, fw_code_ptr, fw_data_ptr, 47*f48485abSMatt Coster fw_core_code_ptr, fw_core_data_ptr, &write_addr); 48*f48485abSMatt Coster if (err) { 49*f48485abSMatt Coster drm_err(drm_dev, 50*f48485abSMatt Coster "Addr 0x%x (size: %d) not found in any firmware segment", 51*f48485abSMatt Coster program_header->p_vaddr, program_header->p_memsz); 52*f48485abSMatt Coster return err; 53*f48485abSMatt Coster } 54*f48485abSMatt Coster 55*f48485abSMatt Coster /* Write to FW allocation only if available */ 56*f48485abSMatt Coster if (write_addr) { 57*f48485abSMatt Coster memcpy(write_addr, fw + program_header->p_offset, 58*f48485abSMatt Coster program_header->p_filesz); 59*f48485abSMatt Coster 60*f48485abSMatt Coster memset((u8 *)write_addr + program_header->p_filesz, 0, 61*f48485abSMatt Coster program_header->p_memsz - program_header->p_filesz); 62*f48485abSMatt Coster } 63*f48485abSMatt Coster } 64*f48485abSMatt Coster 65*f48485abSMatt Coster return 0; 66*f48485abSMatt Coster } 67