kernel_samsung_a53x/drivers/vision3/dsp/dl/dsp-lib-manager.c
2024-06-15 16:02:09 -03:00

628 lines
14 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Samsung Exynos SoC series dsp driver
*
* Copyright (c) 2019 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*/
#include "dl/dsp-common.h"
#include "dl/dsp-dl-engine.h"
#include "dl/dsp-lib-manager.h"
#include "dl/dsp-pm-manager.h"
#include "dl/dsp-gpt-manager.h"
#include "dl/dsp-dl-out-manager.h"
#include "dl/dsp-xml-parser.h"
static const char *dl_lib_path;
static struct dsp_hash_tab *dsp_lib_hash;
void dsp_lib_init(struct dsp_lib *lib, struct dsp_dl_lib_info *info)
{
lib->name = (char *)dsp_dl_malloc(strlen(info->name) + 1,
"lib name");
strcpy(lib->name, info->name);
DL_DEBUG("lib name : %s\n", lib->name);
lib->elf = NULL;
lib->pm = NULL;
lib->gpt = NULL;
lib->dl_out = NULL;
lib->dl_out_mem = NULL;
lib->link_info = NULL;
lib->ref_cnt = 0;
lib->loaded = 0;
}
void dsp_lib_unload(struct dsp_lib *lib)
{
DL_DEBUG("DL lib unload\n");
if (lib->elf) {
dsp_elf32_free(lib->elf);
dsp_dl_free(lib->elf);
lib->elf = NULL;
}
if (lib->link_info) {
dsp_link_info_free(lib->link_info);
dsp_dl_free(lib->link_info);
lib->link_info = NULL;
}
}
void dsp_lib_free(struct dsp_lib *lib)
{
DL_DEBUG("DL lib free\n");
dsp_dl_free(lib->name);
dsp_lib_unload(lib);
dsp_pm_free(lib);
dsp_gpt_free(lib);
dsp_dl_out_free(lib);
}
void dsp_lib_print(struct dsp_lib *lib)
{
DL_INFO(DL_BORDER);
DL_INFO("Library name(%s) ref_cnt(%u) %s\n",
lib->name, lib->ref_cnt,
(lib->loaded) ? "loaded" : "unloaded");
if (lib->elf) {
DL_DEBUG("\n");
DL_DEBUG("ELF information\n");
dsp_elf32_print(lib->elf);
}
if (lib->pm) {
DL_INFO("\n");
DL_INFO("Program memory\n");
dsp_tlsf_mem_print(lib->pm);
DL_DEBUG("\n");
dsp_pm_print(lib);
}
if (lib->link_info) {
DL_INFO("\n");
DL_INFO("Linking information\n");
dsp_link_info_print(lib->link_info);
}
if (lib->gpt) {
DL_INFO("\n");
DL_INFO("Global pointer\n");
dsp_gpt_print(lib->gpt);
}
if (lib->dl_out_mem) {
DL_INFO("\n");
DL_INFO("Loader output\n");
dsp_tlsf_mem_print(lib->dl_out_mem);
dsp_dl_out_print(lib->dl_out);
}
}
void dsp_lib_manager_init(const char *lib_path)
{
DL_DEBUG("DL lib init\n");
dsp_lib_hash = (struct dsp_hash_tab *)dsp_dl_malloc(
sizeof(struct dsp_hash_tab),
"Dsp lib hash");
dl_lib_path = lib_path;
DL_DEBUG("%s\n", dl_lib_path);
dsp_hash_tab_init(dsp_lib_hash);
}
void dsp_lib_manager_free(void)
{
unsigned int idx;
DL_DEBUG("DL lib_manager free\n");
for (idx = 0; idx < DSP_HASH_MAX; idx++) {
struct dsp_list_node *cur, *next;
cur = (&dsp_lib_hash->list[idx])->next;
while (cur != NULL) {
struct dsp_hash_node *hash_node =
container_of(cur, struct dsp_hash_node, node);
struct dsp_lib *lib =
(struct dsp_lib *)hash_node->value;
next = cur->next;
dsp_lib_free(lib);
cur = next;
}
}
dsp_hash_free(dsp_lib_hash, 1);
dsp_dl_free(dsp_lib_hash);
}
void dsp_lib_manager_print(void)
{
unsigned int idx;
DL_INFO(DL_BORDER);
DL_INFO("Library manager\n");
for (idx = 0; idx < DSP_HASH_MAX; idx++) {
struct dsp_list_node *cur, *next;
cur = (&dsp_lib_hash->list[idx])->next;
while (cur != NULL) {
struct dsp_hash_node *hash_node =
container_of(cur, struct dsp_hash_node, node);
struct dsp_lib *lib =
(struct dsp_lib *)hash_node->value;
next = cur->next;
dsp_lib_print(lib);
cur = next;
}
}
}
struct dsp_lib **dsp_lib_manager_get_libs(struct dsp_dl_lib_info *lib_infos,
size_t lib_infos_size)
{
int ret;
unsigned int idx;
struct dsp_lib **libs = (struct dsp_lib **)dsp_dl_malloc(
sizeof(*libs) * lib_infos_size,
"struct dsp_lib list");
DL_DEBUG("DL lib_manager get libs\n");
for (idx = 0; idx < lib_infos_size; idx++) {
struct dsp_lib *lib;
const char *name = lib_infos[idx].name;
ret = dsp_hash_get(dsp_lib_hash, name, (void **)&lib);
if (ret == -1) {
DL_DEBUG("Create library %s\n", name);
lib = (struct dsp_lib *)dsp_dl_malloc(
sizeof(*lib), "Lib created");
dsp_lib_init(lib, &lib_infos[idx]);
dsp_hash_push(dsp_lib_hash, name, lib);
libs[idx] = lib;
} else {
DL_DEBUG("Check lib duplicate\n");
if (!lib->loaded) {
DL_ERROR("Library(%s) is duplicated\n",
lib->name);
dsp_lib_manager_delete_unloaded_libs(libs, idx);
dsp_dl_free(libs);
return NULL;
}
}
libs[idx] = lib;
}
return libs;
}
void dsp_lib_manager_inc_ref_cnt(struct dsp_lib **libs, size_t libs_size)
{
unsigned int idx;
DL_DEBUG("DL lib_manager inc_ref_cnt\n");
for (idx = 0; idx < libs_size; idx++)
libs[idx]->ref_cnt++;
}
void dsp_lib_manager_dec_ref_cnt(struct dsp_lib **libs, size_t libs_size)
{
unsigned int idx;
DL_DEBUG("DL lib_manager dec_ref_cnt\n");
for (idx = 0; idx < libs_size; idx++)
libs[idx]->ref_cnt--;
}
void dsp_lib_manager_delete_unloaded_libs(struct dsp_lib **libs,
size_t libs_size)
{
unsigned int idx;
DL_DEBUG("DL lib_manager delete unloaded libs\n");
for (idx = 0; idx < libs_size; idx++) {
if (!libs[idx]->loaded)
dsp_lib_manager_delete_lib(libs[idx]);
}
}
void dsp_lib_manager_delete_lib(struct dsp_lib *lib)
{
DL_DEBUG("DL lib-manager delete_lib\n");
dsp_hash_pop(dsp_lib_hash, lib->name, 0);
dsp_lib_free(lib);
dsp_dl_free(lib);
}
int dsp_lib_manager_delete_no_ref(void)
{
unsigned int idx;
int ret = 0;
DL_DEBUG("Delete no ref libs\n");
for (idx = 0; idx < DSP_HASH_MAX; idx++) {
struct dsp_list_node *cur, *next;
cur = (&dsp_lib_hash->list[idx])->next;
while (cur != NULL) {
struct dsp_hash_node *hash_node =
container_of(cur, struct dsp_hash_node, node);
struct dsp_lib *lib =
(struct dsp_lib *)hash_node->value;
if (lib->loaded && lib->ref_cnt == 0) {
DL_DEBUG("Delete lib(%s)\n", lib->name);
ret = 1;
dsp_lib_manager_delete_lib(lib);
}
next = cur->next;
cur = next;
}
}
return ret;
}
static int __dsp_lib_manager_load_kernel_table(struct dsp_lib *lib,
struct dsp_dl_out_section sec)
{
unsigned int ret;
struct dsp_xml_lib *xml_lib;
struct dsp_dl_kernel_table *kernel_table;
unsigned int idx;
ret = dsp_hash_get(&xml_libs->lib_hash, lib->name,
(void **)&xml_lib);
if (ret == -1U) {
DL_ERROR("No Library %s\n", lib->name);
return -1;
}
kernel_table = (struct dsp_dl_kernel_table *)(lib->dl_out->data +
sec.offset);
for (idx = 0; idx < xml_lib->kernel_cnt; idx++) {
struct dsp_xml_kernel_table *kernel = &xml_lib->kernels[idx];
ret = dsp_link_info_get_kernel_addr(lib->link_info,
kernel->pre);
if (ret == (unsigned int) -1) {
DL_ERROR("[%s] pre CHK_ERR\n", __func__);
return -1;
}
kernel_table[idx].pre = ret;
ret = dsp_link_info_get_kernel_addr(lib->link_info,
kernel->exe);
if (ret == (unsigned int) -1) {
DL_ERROR("[%s] exe CHK_ERR\n", __func__);
return -1;
}
kernel_table[idx].exe = ret;
ret = dsp_link_info_get_kernel_addr(lib->link_info,
kernel->post);
if (ret == (unsigned int) -1) {
DL_ERROR("[%s] post CHK_ERR\n", __func__);
return -1;
}
kernel_table[idx].post = ret;
}
return 0;
}
static void __dsp_lib_manager_load_gpt(struct dsp_lib *lib)
{
DL_DEBUG("load gpt\n");
lib->dl_out->gpt_addr = (unsigned int)lib->gpt->addr;
}
static int __dsp_lib_manager_load_pm(struct dsp_lib *lib)
{
int ret;
struct dsp_elf32 *elf = lib->elf;
struct dsp_list_node *node;
DL_DEBUG("load pm\n");
dsp_list_for_each(node, &elf->text.text) {
struct dsp_elf32_idx_node *idx_node =
container_of(node, struct dsp_elf32_idx_node, node);
unsigned int ndx = idx_node->idx;
struct dsp_elf32_shdr *text_hdr = elf->shdr + ndx;
unsigned char *text = (unsigned char *)(elf->data
+ text_hdr->sh_offset);
unsigned char *text_end = text + text_hdr->sh_size;
unsigned char *dest =
(unsigned char *)(dsp_pm_manager_get_pm_start_addr() +
lib->link_info->sec[ndx]);
ret = dsp_elf32_check_range(elf, (size_t)text_hdr->sh_offset +
text_hdr->sh_size);
if (ret) {
DL_ERROR("invalid text range(%u/%u)\n",
text_hdr->sh_offset,
text_hdr->sh_size);
return -1;
}
if (lib->link_info->sec[ndx] + text_hdr->sh_size >
dsp_pm_manager_get_pm_total_size()) {
DL_ERROR("invalid dest range(%lu/%u/%zu)\n",
lib->link_info->sec[ndx],
text_hdr->sh_size,
dsp_pm_manager_get_pm_total_size());
return -1;
}
DL_DEBUG("Dest : %p, Src : %p, Src end : %p, size : %u\n",
dest, text, text_end, text_hdr->sh_size);
for (; text < text_end; text += 4, dest += 4) {
int idx;
for (idx = 0; idx < 4; idx++)
dest[idx] = text[3 - idx];
}
}
return 0;
}
static int __dsp_lib_manager_load_bss_sec(struct dsp_lib *lib,
struct dsp_list_head *head, struct dsp_dl_out_section sec)
{
struct dsp_list_node *node;
struct dsp_elf32 *elf = lib->elf;
DL_DEBUG("load bss sec\n");
dsp_list_for_each(node, head) {
struct dsp_elf32_idx_node *idx_node =
container_of(node, struct dsp_elf32_idx_node, node);
unsigned int ndx = idx_node->idx;
struct dsp_elf32_shdr *mem_hdr = elf->shdr + ndx;
char *dest = lib->dl_out->data + sec.offset +
lib->link_info->sec[ndx];
unsigned long end = (unsigned long)(dest + mem_hdr->sh_size);
unsigned int *addr;
if (sec.offset + lib->link_info->sec[ndx] + mem_hdr->sh_size >
lib->dl_out_data_size) {
DL_ERROR("invalid dest range(%u/%lu/%u/%zu)\n",
sec.offset,
lib->link_info->sec[ndx],
mem_hdr->sh_size,
lib->dl_out_data_size);
return -1;
}
DL_DEBUG("load sec %u\n", ndx);
DL_DEBUG("Dest : %p, size : %u\n", dest, mem_hdr->sh_size);
for (addr = (unsigned int *)dest; (unsigned long)addr < end;
addr++)
*addr = 0;
}
return 0;
}
static int __dsp_lib_manager_load_sec(struct dsp_lib *lib,
struct dsp_list_head *head, struct dsp_dl_out_section sec,
int rev_endian)
{
int ret;
struct dsp_list_node *node;
struct dsp_elf32 *elf = lib->elf;
DL_DEBUG("load sec\n");
dsp_list_for_each(node, head) {
struct dsp_elf32_idx_node *idx_node =
container_of(node, struct dsp_elf32_idx_node, node);
unsigned int ndx = idx_node->idx;
struct dsp_elf32_shdr *mem_hdr = elf->shdr + ndx;
unsigned char *data = (unsigned char *)(elf->data +
mem_hdr->sh_offset);
unsigned char *data_end = data + mem_hdr->sh_size;
unsigned char *dest = lib->dl_out->data + sec.offset +
lib->link_info->sec[ndx];
ret = dsp_elf32_check_range(elf,
(size_t)mem_hdr->sh_offset + mem_hdr->sh_size);
if (ret) {
DL_ERROR("invalid data range(%u/%u)\n",
mem_hdr->sh_offset,
mem_hdr->sh_size);
return -1;
}
if (sec.offset + lib->link_info->sec[ndx] + mem_hdr->sh_size >
lib->dl_out_data_size) {
DL_ERROR("invalid dest range(%u/%lu/%u/%zu)\n",
sec.offset,
lib->link_info->sec[ndx],
mem_hdr->sh_size,
lib->dl_out_data_size);
return -1;
}
DL_DEBUG("load sec %u\n", ndx);
DL_DEBUG("Dest : %p, Src : %p, size : %u\n",
dest, data, mem_hdr->sh_size);
if (rev_endian) {
for (; data < data_end; data += 4, dest += 4) {
int idx;
for (idx = 0; idx < 4; idx++)
dest[idx] = data[3 - idx];
}
} else
memcpy(dest, data, mem_hdr->sh_size);
}
return 0;
}
static int __dsp_lib_manager_load_mem(struct dsp_lib *lib,
struct dsp_elf32_mem *mem, struct dsp_dl_out_section sec,
int rev_endian)
{
int ret;
DL_DEBUG("load mem\n");
DL_DEBUG("Load robss\n");
ret = __dsp_lib_manager_load_bss_sec(lib, &mem->robss, sec);
if (ret) {
DL_ERROR("Failed to load robss\n");
return -1;
}
DL_DEBUG("Load rodata\n");
ret = __dsp_lib_manager_load_sec(lib, &mem->rodata, sec, rev_endian);
if (ret) {
DL_ERROR("Failed to load rodata\n");
return -1;
}
DL_DEBUG("Load bss\n");
ret = __dsp_lib_manager_load_bss_sec(lib, &mem->bss, sec);
if (ret) {
DL_ERROR("Failed to load bss\n");
return -1;
}
DL_DEBUG("Load data\n");
ret = __dsp_lib_manager_load_sec(lib, &mem->data, sec, rev_endian);
if (ret) {
DL_ERROR("Failed to load data\n");
return -1;
}
return 0;
}
int dsp_lib_manager_load_libs(struct dsp_lib **libs, size_t libs_size)
{
int ret;
unsigned int idx;
DL_DEBUG(DL_BORDER);
DL_DEBUG("DL lib_manager load libs\n");
for (idx = 0; idx < libs_size; idx++) {
struct dsp_dl_out *dl_out;
if (libs[idx]->loaded)
continue;
dl_out = libs[idx]->dl_out;
if (dl_out) {
DL_DEBUG("DL out data addr : %pK\n",
dl_out->data);
DL_DEBUG("Load Kernel table\n");
ret = __dsp_lib_manager_load_kernel_table(
libs[idx],
dl_out->kernel_table);
if (ret == -1) {
DL_ERROR("[%s] CHK_ERR\n", __func__);
return -1;
}
}
if (libs[idx]->pm) {
DL_DEBUG("Load PM\n");
ret = __dsp_lib_manager_load_pm(libs[idx]);
if (ret) {
DL_ERROR("Failed to load PM\n");
return -1;
}
}
if (libs[idx]->gpt) {
DL_DEBUG("Load GPT\n");
__dsp_lib_manager_load_gpt(libs[idx]);
}
if (dl_out && libs[idx]->dl_out_mem) {
DL_DEBUG("Load DM\n");
ret = __dsp_lib_manager_load_mem(libs[idx],
&libs[idx]->elf->DMb,
dl_out->DM_sh, 0);
if (ret) {
DL_ERROR("Failed to load DM\n");
return -1;
}
DL_DEBUG("Load DM_local\n");
ret = __dsp_lib_manager_load_mem(libs[idx],
&libs[idx]->elf->DMb_local,
dl_out->DM_local, 0);
if (ret) {
DL_ERROR("Failed to load DM_local\n");
return -1;
}
DL_DEBUG("Load TCM\n");
ret = __dsp_lib_manager_load_mem(libs[idx],
&libs[idx]->elf->TCMb,
dl_out->TCM_sh, 0);
if (ret) {
DL_ERROR("Failed to load TCM\n");
return -1;
}
DL_DEBUG("Load TCM_local\n");
ret = __dsp_lib_manager_load_mem(libs[idx],
&libs[idx]->elf->TCMb_local,
dl_out->TCM_local, 0);
if (ret) {
DL_ERROR("Failed to load TCM_local\n");
return -1;
}
DL_DEBUG("Load Shared mem\n");
ret = __dsp_lib_manager_load_mem(libs[idx],
&libs[idx]->elf->SFRw,
dl_out->sh_mem, 1);
if (ret) {
DL_ERROR("Failed to load Shared mem\n");
return -1;
}
}
libs[idx]->loaded = 1;
}
return 0;
}
void dsp_lib_manager_unload_libs(struct dsp_lib **libs, size_t libs_size)
{
unsigned int idx;
DL_DEBUG("DL lib_manager unload libs\n");
dsp_lib_manager_dec_ref_cnt(libs, libs_size);
for (idx = 0; idx < libs_size; idx++) {
if (libs[idx]->ref_cnt == 0)
dsp_lib_unload(libs[idx]);
}
}