// SPDX-License-Identifier: GPL-2.0+ // Copyright 2018 NXP // Copyright (c) 2012-2013 by Tensilica Inc. #include #include #include #include "fsl_dsp.h" #include "fsl_dsp_library_load.h" static Elf32_Half xtlib_host_half(Elf32_Half v, int byteswap) { return (byteswap) ? (v >> 8) | (v << 8) : v; } static Elf32_Word xtlib_host_word(Elf32_Word v, int byteswap) { if (byteswap) { v = ((v & 0x00FF00FF) << 8) | ((v & 0xFF00FF00) >> 8); v = (v >> 16) | (v << 16); } return v; } static int xtlib_verify_magic(Elf32_Ehdr *header, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Byte magic_no; magic_no = header->e_ident[EI_MAG0]; if (magic_no != 0x7f) return -1; magic_no = header->e_ident[EI_MAG1]; if (magic_no != 'E') return -1; magic_no = header->e_ident[EI_MAG2]; if (magic_no != 'L') return -1; magic_no = header->e_ident[EI_MAG3]; if (magic_no != 'F') return -1; if (header->e_ident[EI_CLASS] != ELFCLASS32) return -1; { /* determine byte order */ union { short s; char c[sizeof(short)]; } u; u.s = 1; if (header->e_ident[EI_DATA] == ELFDATA2LSB) xtlib_globals->byteswap = u.c[sizeof(short) - 1] == 1; else if (header->e_ident[EI_DATA] == ELFDATA2MSB) xtlib_globals->byteswap = u.c[0] == 1; else return -1; } return 0; } static void xtlib_load_seg(Elf32_Phdr *pheader, void *src_addr, xt_ptr dst_addr, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Word bytes_to_copy = xtlib_host_word(pheader->p_filesz, xtlib_globals->byteswap); Elf32_Word bytes_to_zero = xtlib_host_word(pheader->p_memsz, xtlib_globals->byteswap) - bytes_to_copy; unsigned int i; char *src_back, *dst_back; void *zero_addr = (void *)dst_addr + bytes_to_copy; if (bytes_to_copy > 0) { // memcpy((void *)(dst_addr), src_addr, bytes_to_copy); src_back = (char *)src_addr; dst_back = (char *)dst_addr; for (i = 0; i < bytes_to_copy; i++) *dst_back++ = *src_back++; } if (bytes_to_zero > 0) { // memset(zero_addr, 0, bytes_to_zero); dst_back = (char *)zero_addr; for (i = 0; i < bytes_to_zero; i++) *dst_back++ = 0; } } #define xtlib_xt_half xtlib_host_half #define xtlib_xt_word xtlib_host_word static xt_ptr align_ptr(xt_ptr ptr, xt_uint align) { return (xt_ptr)(((xt_uint)ptr + align - 1) & ~(align - 1)); } static xt_ptr xt_ptr_offs(xt_ptr base, Elf32_Word offs, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; return (xt_ptr)xtlib_xt_word((xt_uint)base + xtlib_host_word(offs, xtlib_globals->byteswap), xtlib_globals->byteswap); } static Elf32_Dyn *find_dynamic_info(Elf32_Ehdr *eheader, struct lib_info *lib_info) { char *base_addr = (char *)eheader; struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Phdr *pheader = (Elf32_Phdr *)(base_addr + xtlib_host_word(eheader->e_phoff, xtlib_globals->byteswap)); int seg = 0; int num = xtlib_host_half(eheader->e_phnum, xtlib_globals->byteswap); while (seg < num) { if (xtlib_host_word(pheader[seg].p_type, xtlib_globals->byteswap) == PT_DYNAMIC) { return (Elf32_Dyn *)(base_addr + xtlib_host_word(pheader[seg].p_offset, xtlib_globals->byteswap)); } seg++; } return 0; } static int find_align(Elf32_Ehdr *header, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Shdr *sheader = (Elf32_Shdr *) (((char *)header) + xtlib_host_word(header->e_shoff, xtlib_globals->byteswap)); int sec = 0; int num = xtlib_host_half(header->e_shnum, xtlib_globals->byteswap); int align = 0; while (sec < num) { if (sheader[sec].sh_type != SHT_NULL && xtlib_host_word(sheader[sec].sh_size, xtlib_globals->byteswap) > 0) { int sec_align = xtlib_host_word(sheader[sec].sh_addralign, xtlib_globals->byteswap); if (sec_align > align) align = sec_align; } sec++; } return align; } static int validate_dynamic(Elf32_Ehdr *header, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; if (xtlib_verify_magic(header, lib_info) != 0) return XTLIB_NOT_ELF; if (xtlib_host_half(header->e_type, xtlib_globals->byteswap) != ET_DYN) return XTLIB_NOT_DYNAMIC; return XTLIB_NO_ERR; } static int validate_dynamic_splitload(Elf32_Ehdr *header, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Phdr *pheader; int err = validate_dynamic(header, lib_info); if (err != XTLIB_NO_ERR) return err; /* make sure it's split load pi library, expecting three headers, * code, data and dynamic, for example: * *LOAD off 0x00000094 vaddr 0x00000000 paddr 0x00000000 align 2**0 * filesz 0x00000081 memsz 0x00000081 flags r-x *LOAD off 0x00000124 vaddr 0x00000084 paddr 0x00000084 align 2**0 * filesz 0x000001ab memsz 0x000011bc flags rwx *DYNAMIC off 0x00000124 vaddr 0x00000084 paddr 0x00000084 align 2**2 * filesz 0x000000a0 memsz 0x000000a0 flags rw- */ if (xtlib_host_half(header->e_phnum, xtlib_globals->byteswap) != 3) return XTLIB_NOT_SPLITLOAD; pheader = (Elf32_Phdr *)((char *)header + xtlib_host_word(header->e_phoff, xtlib_globals->byteswap)); /* LOAD R-X */ if (xtlib_host_word(pheader[0].p_type, xtlib_globals->byteswap) != PT_LOAD || (xtlib_host_word(pheader[0].p_flags, xtlib_globals->byteswap) & (PF_R | PF_W | PF_X)) != (PF_R | PF_X)) return XTLIB_NOT_SPLITLOAD; /* LOAD RWX */ if (xtlib_host_word(pheader[1].p_type, xtlib_globals->byteswap) != PT_LOAD || (xtlib_host_word(pheader[1].p_flags, xtlib_globals->byteswap) & (PF_R | PF_W | PF_X)) != (PF_R | PF_W | PF_X)) return XTLIB_NOT_SPLITLOAD; /* DYNAMIC RW- */ if (xtlib_host_word(pheader[2].p_type, xtlib_globals->byteswap) != PT_DYNAMIC || (xtlib_host_word(pheader[2].p_flags, xtlib_globals->byteswap) & (PF_R | PF_W | PF_X)) != (PF_R | PF_W)) return XTLIB_NOT_SPLITLOAD; return XTLIB_NO_ERR; } static unsigned int xtlib_split_pi_library_size(struct xtlib_packaged_library *library, unsigned int *code_size, unsigned int *data_size, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Phdr *pheader; Elf32_Ehdr *header = (Elf32_Ehdr *)library; int align; int err = validate_dynamic_splitload(header, lib_info); if (err != XTLIB_NO_ERR) { xtlib_globals->err = err; return err; } align = find_align(header, lib_info); pheader = (Elf32_Phdr *)((char *)library + xtlib_host_word(header->e_phoff, xtlib_globals->byteswap)); *code_size = xtlib_host_word(pheader[0].p_memsz, xtlib_globals->byteswap) + align; *data_size = xtlib_host_word(pheader[1].p_memsz, xtlib_globals->byteswap) + align; return XTLIB_NO_ERR; } static int get_dyn_info(Elf32_Ehdr *eheader, xt_ptr dst_addr, xt_uint src_offs, xt_ptr dst_data_addr, xt_uint src_data_offs, struct xtlib_pil_info *info, struct lib_info *lib_info) { unsigned int jmprel = 0; unsigned int pltrelsz = 0; struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Dyn *dyn_entry = find_dynamic_info(eheader, lib_info); if (dyn_entry == 0) return XTLIB_NO_DYNAMIC_SEGMENT; info->dst_addr = (xt_uint)xtlib_xt_word((Elf32_Word)dst_addr, xtlib_globals->byteswap); info->src_offs = xtlib_xt_word(src_offs, xtlib_globals->byteswap); info->dst_data_addr = (xt_uint)xtlib_xt_word( (Elf32_Word)dst_data_addr + src_data_offs, xtlib_globals->byteswap); info->src_data_offs = xtlib_xt_word(src_data_offs, xtlib_globals->byteswap); dst_addr -= src_offs; dst_data_addr = dst_data_addr + src_data_offs - src_data_offs; info->start_sym = xt_ptr_offs(dst_addr, eheader->e_entry, lib_info); info->align = xtlib_xt_word(find_align(eheader, lib_info), xtlib_globals->byteswap); info->text_addr = 0; while (dyn_entry->d_tag != DT_NULL) { switch ((Elf32_Sword) xtlib_host_word( (Elf32_Word)dyn_entry->d_tag, xtlib_globals->byteswap)) { case DT_RELA: info->rel = xt_ptr_offs(dst_data_addr, dyn_entry->d_un.d_ptr, lib_info); break; case DT_RELASZ: info->rela_count = xtlib_xt_word( xtlib_host_word(dyn_entry->d_un.d_val, xtlib_globals->byteswap) / sizeof(Elf32_Rela), xtlib_globals->byteswap); break; case DT_INIT: info->init = xt_ptr_offs(dst_addr, dyn_entry->d_un.d_ptr, lib_info); break; case DT_FINI: info->fini = xt_ptr_offs(dst_addr, dyn_entry->d_un.d_ptr, lib_info); break; case DT_HASH: info->hash = xt_ptr_offs(dst_data_addr, dyn_entry->d_un.d_ptr, lib_info); break; case DT_SYMTAB: info->symtab = xt_ptr_offs(dst_data_addr, dyn_entry->d_un.d_ptr, lib_info); break; case DT_STRTAB: info->strtab = xt_ptr_offs(dst_data_addr, dyn_entry->d_un.d_ptr, lib_info); break; case DT_JMPREL: jmprel = dyn_entry->d_un.d_val; break; case DT_PLTRELSZ: pltrelsz = dyn_entry->d_un.d_val; break; case DT_LOPROC + 2: info->text_addr = xt_ptr_offs(dst_addr, dyn_entry->d_un.d_ptr, lib_info); break; default: /* do nothing */ break; } dyn_entry++; } return XTLIB_NO_ERR; } static xt_ptr xtlib_load_split_pi_library_common(struct xtlib_packaged_library *library, xt_ptr destination_code_address, xt_ptr destination_data_address, struct xtlib_pil_info *info, struct lib_info *lib_info) { struct xtlib_loader_globals *xtlib_globals = &lib_info->xtlib_globals; Elf32_Ehdr *header = (Elf32_Ehdr *)library; Elf32_Phdr *pheader; unsigned int align; int err = validate_dynamic_splitload(header, lib_info); xt_ptr destination_code_address_back; xt_ptr destination_data_address_back; if (err != XTLIB_NO_ERR) { xtlib_globals->err = err; return 0; } align = find_align(header, lib_info); destination_code_address_back = destination_code_address; destination_data_address_back = destination_data_address; destination_code_address = align_ptr(destination_code_address, align); destination_data_address = align_ptr(destination_data_address, align); lib_info->code_buf_virt += (destination_code_address - destination_code_address_back); lib_info->data_buf_virt += (destination_data_address - destination_data_address_back); pheader = (Elf32_Phdr *)((char *)library + xtlib_host_word(header->e_phoff, xtlib_globals->byteswap)); err = get_dyn_info(header, destination_code_address, xtlib_host_word(pheader[0].p_paddr, xtlib_globals->byteswap), destination_data_address, xtlib_host_word(pheader[1].p_paddr, xtlib_globals->byteswap), info, lib_info); if (err != XTLIB_NO_ERR) { xtlib_globals->err = err; return 0; } /* loading code */ xtlib_load_seg(&pheader[0], (char *)library + xtlib_host_word(pheader[0].p_offset, xtlib_globals->byteswap), (xt_ptr)lib_info->code_buf_virt, lib_info); if (info->text_addr == 0) info->text_addr = (xt_ptr)xtlib_xt_word((Elf32_Word)destination_code_address, xtlib_globals->byteswap); /* loading data */ xtlib_load_seg(&pheader[1], (char *)library + xtlib_host_word(pheader[1].p_offset, xtlib_globals->byteswap), (xt_ptr)lib_info->data_buf_virt + xtlib_host_word(pheader[1].p_paddr, xtlib_globals->byteswap), lib_info); return (xt_ptr)xtlib_host_word((Elf32_Word)info->start_sym, xtlib_globals->byteswap); } static xt_ptr xtlib_host_load_split_pi_library(struct xtlib_packaged_library *library, xt_ptr destination_code_address, xt_ptr destination_data_address, struct xtlib_pil_info *info, struct lib_info *lib_info) { return xtlib_load_split_pi_library_common(library, destination_code_address, destination_data_address, info, lib_info); } static long load_dpu_with_library(struct xf_client *client, struct xf_proxy *proxy, struct lib_info *lib_info) { struct fsl_dsp *dsp_priv = container_of(proxy, struct fsl_dsp, proxy); unsigned char *srambuf; struct lib_dnld_info_t dpulib; struct file *file; struct xf_buffer *buf; Elf32_Phdr *pheader; Elf32_Ehdr *header; loff_t pos = 0; unsigned int align; int filesize = 0; long ret_val = 0; file = filp_open(lib_info->filename, O_RDONLY, 0); if (IS_ERR(file)) return PTR_ERR(file); vfs_llseek(file, 0, SEEK_END); filesize = (int)file->f_pos; srambuf = kmalloc(filesize, GFP_KERNEL); if (!srambuf) return -ENOMEM; vfs_llseek(file, 0, SEEK_SET); ret_val = kernel_read(file, srambuf, filesize, &pos); if (ret_val < 0) return ret_val; filp_close(file, NULL); ret_val = xtlib_split_pi_library_size( (struct xtlib_packaged_library *)(srambuf), (unsigned int *)&dpulib.size_code, (unsigned int *)&dpulib.size_data, lib_info); if (ret_val != XTLIB_NO_ERR) return -EINVAL; lib_info->code_buf_size = dpulib.size_code; lib_info->data_buf_size = dpulib.size_data; header = (Elf32_Ehdr *)srambuf; pheader = (Elf32_Phdr *)((char *)srambuf + xtlib_host_word(header->e_phoff, lib_info->xtlib_globals.byteswap)); align = find_align(header, lib_info); ret_val = xf_pool_alloc(client, proxy, 1, dpulib.size_code + align, XF_POOL_AUX, &lib_info->code_section_pool); if (ret_val) { kfree(srambuf); pr_err("Allocation failure for loading code section\n"); return -ENOMEM; } ret_val = xf_pool_alloc(client, proxy, 1, dpulib.size_data + pheader[1].p_paddr + align, XF_POOL_AUX, &lib_info->data_section_pool); if (ret_val) { kfree(srambuf); pr_err("Allocation failure for loading data section\n"); return -ENOMEM; } buf = xf_buffer_get(lib_info->code_section_pool); lib_info->code_buf_virt = xf_buffer_data(buf); lib_info->code_buf_phys = ((u64)xf_buffer_data(buf) - (u64)dsp_priv->scratch_buf_virt) + dsp_priv->scratch_buf_phys; lib_info->code_buf_size = dpulib.size_code + align; xf_buffer_put(buf); buf = xf_buffer_get(lib_info->data_section_pool); lib_info->data_buf_virt = xf_buffer_data(buf); lib_info->data_buf_phys = ((u64)xf_buffer_data(buf) - (u64)dsp_priv->scratch_buf_virt) + dsp_priv->scratch_buf_phys; lib_info->data_buf_size = dpulib.size_data + align + pheader[1].p_paddr; xf_buffer_put(buf); dpulib.pbuf_code = (unsigned long)lib_info->code_buf_phys; dpulib.pbuf_data = (unsigned long)lib_info->data_buf_phys; dpulib.ppil_inf = &lib_info->pil_info; xtlib_host_load_split_pi_library((struct xtlib_packaged_library *)srambuf, (xt_ptr)(dpulib.pbuf_code), (xt_ptr)(dpulib.pbuf_data), (struct xtlib_pil_info *)dpulib.ppil_inf, (void *)lib_info); kfree(srambuf); return ret_val; } static long unload_dpu_with_library(struct xf_client *client, struct xf_proxy *proxy, struct lib_info *lib_info) { xf_pool_free(client, lib_info->code_section_pool); xf_pool_free(client, lib_info->data_section_pool); return 0; } long xf_load_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info) { void *b = xf_handle_aux(handle); struct icm_xtlib_pil_info icm_info; struct xf_proxy *proxy = handle->proxy; struct xf_message msg; struct xf_message *rmsg; long ret_val; ret_val = load_dpu_with_library(client, proxy, lib_info); if (ret_val) return ret_val; memcpy((void *)(&icm_info.pil_info), (void *)(&lib_info->pil_info), sizeof(struct xtlib_pil_info)); icm_info.lib_type = lib_info->lib_type; /* ...set message parameters */ msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(handle->id, 0)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_LOAD_LIB; msg.buffer = b; msg.length = sizeof(struct icm_xtlib_pil_info); msg.ret = 0; /* ...copy lib info */ memcpy(b, (void *)&icm_info, xf_buffer_length(handle->aux)); /* ...execute command synchronously */ rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, msg.buffer, msg.length, &client->work, &client->compr_complete); if (IS_ERR(rmsg)) return PTR_ERR(rmsg); // xf_msg_free(proxy, rmsg); // xf_unlock(&proxy->lock); return 0; } long xf_unload_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info) { void *b = xf_handle_aux(handle); struct xf_proxy *proxy = handle->proxy; struct xf_message msg; struct xf_message *rmsg; struct icm_xtlib_pil_info icm_info; memset((void *)&icm_info, 0, sizeof(struct icm_xtlib_pil_info)); icm_info.lib_type = lib_info->lib_type; /* ...set message parameters */ msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),__XF_PORT_SPEC2(handle->id, 0)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_UNLOAD_LIB; msg.buffer = b; msg.length = sizeof(struct icm_xtlib_pil_info); msg.ret = 0; /* ...copy lib info */ memcpy(b, (void *)&icm_info, xf_buffer_length(handle->aux)); /* ...execute command synchronously */ rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode, msg.buffer, msg.length, &client->work, &client->compr_complete); if (IS_ERR(rmsg)) return PTR_ERR(rmsg); // xf_msg_free(proxy, rmsg); // xf_unlock(&proxy->lock); return unload_dpu_with_library(client, proxy, lib_info); }