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

433 lines
10 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-dl-out-manager.h"
#include "dl/dsp-elf-loader.h"
#include "dl/dsp-lib-manager.h"
#include "dl/dsp-xml-parser.h"
#define DL_DL_OUT_ALIGN (4)
#define DL_HASH_SIZE (300)
#define DL_HASH_END (0xFFFFFFFF)
static unsigned int *dl_hash;
static struct dsp_tlsf *out_manager;
unsigned int dsp_dl_hash_get_key(char *data)
{
int len;
unsigned int hash, tmp;
int rem;
if (data == NULL)
return 0;
len = strlen(data);
if (len <= 0)
return 0;
hash = len;
rem = len & 3;
len >>= 2;
for (; len > 0; len--) {
hash += get16bits(data);
tmp = (get16bits(data + 2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2 * sizeof(unsigned short);
hash += hash >> 11;
}
switch (rem) {
case 3:
hash += get16bits(data);
hash ^= hash << 16;
hash ^= ((signed char)data[sizeof(unsigned short)]) << 18;
hash += hash >> 11;
break;
case 2:
hash += get16bits(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1:
hash += (signed char)(*data);
hash ^= hash << 10;
hash += hash >> 1;
}
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash % DL_HASH_SIZE;
}
void dsp_dl_hash_init(void)
{
int idx;
unsigned int *node;
for (idx = 0; idx < DL_HASH_SIZE; idx++) {
node = dl_hash + idx;
*node = DL_HASH_END;
}
}
void dsp_dl_hash_push(struct dsp_dl_out *dl_out)
{
unsigned int key = dsp_dl_hash_get_key(dl_out->data);
dl_out->hash_next = dl_hash[key];
dl_hash[key] = (unsigned long)dl_out - (unsigned long)dl_hash;
DL_DEBUG("key : %u, head : %u\n", key, dl_hash[key]);
}
void dsp_dl_hash_pop(char *k)
{
unsigned int key = dsp_dl_hash_get_key(k);
unsigned int *node = dl_hash + key;
struct dsp_dl_out *next;
while (*node != DL_HASH_END) {
next = (struct dsp_dl_out *)((unsigned long)dl_hash + *node);
if (strcmp(next->data, k) == 0) {
*node = next->hash_next;
return;
}
node = &next->hash_next;
}
DL_ERROR("No hash node for %s\n", k);
}
void dsp_dl_hash_print(void)
{
int idx;
unsigned int *node;
struct dsp_dl_out *next;
for (idx = 0; idx < DL_HASH_SIZE; idx++) {
node = dl_hash + idx;
while (*node != DL_HASH_END) {
DL_INFO("\n");
DL_INFO("Hash node: key(%u) offset(%u)\n", idx, *node);
next = (struct dsp_dl_out *)(
(unsigned long)dl_hash + *node);
DL_INFO("Library name: %s\n", next->data);
DL_INFO("Hash next offset: 0x%x\n", next->hash_next);
node = &next->hash_next;
}
}
}
static unsigned int __dsp_dl_out_offset_align(unsigned int offset)
{
return (offset + 0x3) & ~0x3;
}
int dsp_dl_out_create(struct dsp_lib *lib)
{
int ret;
unsigned int offset = 0;
struct dsp_xml_lib *xml_lib;
DL_DEBUG("DL out create\n");
lib->dl_out = (struct dsp_dl_out *)dsp_dl_malloc(
sizeof(*lib->dl_out), "lib dl_out");
lib->dl_out->hash_next = DL_HASH_END;
offset += strlen(lib->name) + 1;
offset = __dsp_dl_out_offset_align(offset);
lib->dl_out->kernel_table.offset = offset;
ret = dsp_hash_get(&xml_libs->lib_hash, lib->name,
(void **)&xml_lib);
if (ret == -1) {
DL_ERROR("No global table for %s\n", lib->name);
dsp_dl_free(lib->dl_out);
lib->dl_out = NULL;
return -1;
}
lib->dl_out->kernel_table.size = xml_lib->kernel_cnt *
sizeof(struct dsp_dl_kernel_table);
offset += lib->dl_out->kernel_table.size;
offset = __dsp_dl_out_offset_align(offset);
lib->dl_out->DM_sh.offset = offset;
lib->dl_out->DM_sh.size = dsp_elf32_get_mem_size(&lib->elf->DMb,
lib->elf);
offset += lib->dl_out->DM_sh.size;
offset = __dsp_dl_out_offset_align(offset);
lib->dl_out->DM_local.offset = offset;
lib->dl_out->DM_local.size = dsp_elf32_get_mem_size(
&lib->elf->DMb_local, lib->elf);
offset += lib->dl_out->DM_local.size;
offset = __dsp_dl_out_offset_align(offset);
lib->dl_out->TCM_sh.offset = offset;
lib->dl_out->TCM_sh.size = dsp_elf32_get_mem_size(&lib->elf->TCMb,
lib->elf);
offset += lib->dl_out->TCM_sh.size;
offset = __dsp_dl_out_offset_align(offset);
lib->dl_out->TCM_local.offset = offset;
lib->dl_out->TCM_local.size = dsp_elf32_get_mem_size(
&lib->elf->TCMb_local, lib->elf);
offset += lib->dl_out->TCM_local.size;
offset = __dsp_dl_out_offset_align(offset);
lib->dl_out->sh_mem.offset = offset;
lib->dl_out->sh_mem.size = dsp_elf32_get_mem_size(&lib->elf->SFRw,
lib->elf);
return 0;
}
size_t dsp_dl_out_get_size(struct dsp_dl_out *dl_out)
{
return sizeof(*dl_out) + dl_out->sh_mem.offset + dl_out->sh_mem.size;
}
static void __dsp_dl_out_cpy_metadata(struct dsp_dl_out *op1,
struct dsp_dl_out *op2)
{
memcpy(op1, op2, sizeof(*op1));
}
static void __dsp_dl_out_print_sec_data(struct dsp_dl_out *dl_out,
struct dsp_dl_out_section sec)
{
unsigned int idx;
unsigned int *data = (unsigned int *)(dl_out->data + sec.offset);
unsigned int sec_end = sec.size / sizeof(unsigned int);
for (idx = 0; idx < sec_end; idx++) {
DL_BUF_STR("0x%08x ", data[idx]);
if ((idx + 1) % 4 == 0 || idx == sec_end - 1) {
DL_BUF_STR("\n");
DL_PRINT_BUF(DEBUG);
}
}
}
static void __dsp_dl_out_print_kernel_table(struct dsp_dl_out *dl_out)
{
struct dsp_dl_out_section sec = dl_out->kernel_table;
unsigned int idx;
unsigned int *data = (unsigned int *)(dl_out->data + sec.offset);
unsigned int sec_end = sec.size / sizeof(unsigned int);
for (idx = 0; idx < sec_end; idx++) {
DL_BUF_STR("0x%08x ", data[idx]);
if ((idx + 1) % 3 == 0 || idx == sec_end - 1) {
DL_BUF_STR("\n");
DL_PRINT_BUF(DEBUG);
}
}
}
static void __dsp_dl_out_print_data(struct dsp_dl_out *dl_out)
{
DL_DEBUG("Kernel address table\n");
DL_DEBUG(" Pre Exe Post\n");
__dsp_dl_out_print_kernel_table(dl_out);
DL_DEBUG("DM shared data\n");
__dsp_dl_out_print_sec_data(dl_out, dl_out->DM_sh);
DL_DEBUG("DM thread local data\n");
__dsp_dl_out_print_sec_data(dl_out, dl_out->DM_local);
DL_DEBUG("TCM shared data\n");
__dsp_dl_out_print_sec_data(dl_out, dl_out->TCM_sh);
DL_DEBUG("TCM thread local data\n");
__dsp_dl_out_print_sec_data(dl_out, dl_out->TCM_local);
DL_DEBUG("Shared memory data\n");
__dsp_dl_out_print_sec_data(dl_out, dl_out->sh_mem);
}
void dsp_dl_out_print(struct dsp_dl_out *dl_out)
{
DL_INFO("Library name: %s\n", dl_out->data);
DL_INFO("Hash next offset: 0x%x\n", dl_out->hash_next);
DL_INFO("Gpt address: 0x%x\n", dl_out->gpt_addr);
DL_INFO("\n");
DL_INFO("Data offsets\n");
DL_INFO("Kernel table: offset(%u) size(%u)\n",
dl_out->kernel_table.offset, dl_out->kernel_table.size);
DL_INFO("DM shared data: offset(%u) size(%u\n",
dl_out->DM_sh.offset, dl_out->DM_sh.size);
DL_INFO("DM thread local data: offset(%u) size(%u)\n",
dl_out->DM_local.offset, dl_out->DM_local.size);
DL_INFO("TCM shared data: offset(%u) size(%u)\n",
dl_out->TCM_sh.offset, dl_out->TCM_sh.size);
DL_INFO("TCM thread local data: offset(%u) size(%u)\n",
dl_out->TCM_local.offset, dl_out->TCM_local.size);
DL_INFO("Shared memory data: offset(%u) size(%u)\n",
dl_out->sh_mem.offset, dl_out->sh_mem.size);
DL_INFO("\n");
DL_DEBUG("Data loaded\n");
__dsp_dl_out_print_data(dl_out);
}
int dsp_dl_out_manager_init(unsigned long start_addr, size_t size)
{
unsigned long mem_str;
size_t mem_size;
DL_DEBUG("DL out manager init\n");
out_manager = (struct dsp_tlsf *)dsp_dl_malloc(
sizeof(struct dsp_tlsf), "Out manager");
if (size <= sizeof(unsigned int) * DL_HASH_SIZE) {
DL_ERROR("Size(%zu) is not enough for DL_out\n", size);
dsp_dl_free(out_manager);
return -1;
}
dl_hash = (unsigned int *)start_addr;
DL_DEBUG("DL hash : 0x%p\n", dl_hash);
dsp_dl_hash_init();
mem_str = (unsigned long)((unsigned int *)start_addr + DL_HASH_SIZE);
mem_size = size - sizeof(unsigned int) * DL_HASH_SIZE;
dsp_tlsf_init(out_manager, mem_str, mem_size, DL_DL_OUT_ALIGN);
return 0;
}
int dsp_dl_out_manager_free(void)
{
dsp_tlsf_delete(out_manager);
dsp_dl_free(out_manager);
return 0;
}
void dsp_dl_out_manager_print(void)
{
DL_INFO(DL_BORDER);
DL_INFO("Dynamic loader output manager\n");
DL_INFO("\n");
dsp_tlsf_print(out_manager);
DL_INFO("\n");
DL_INFO("Output hash table\n");
dsp_dl_hash_print();
}
int dsp_dl_out_manager_alloc_libs(struct dsp_lib **libs, int libs_size,
int *pm_inv)
{
int ret, idx;
DL_DEBUG("Alloc DL out\n");
for (idx = 0; idx < libs_size; idx++) {
if (!libs[idx]->dl_out) {
DL_DEBUG("Alloc DL out for library %s\n",
libs[idx]->name);
ret = dsp_dl_out_alloc(libs[idx], pm_inv);
if (ret == -1) {
DL_ERROR("Alloc DL out failed\n");
return -1;
}
}
}
return 0;
}
static int __dsp_dl_out_alloc_mem(size_t size, struct dsp_lib *lib,
int *pm_inv)
{
int ret;
ret = dsp_tlsf_malloc(size, &lib->dl_out_mem,
out_manager);
if (ret == -1) {
if (dsp_tlsf_can_be_loaded(out_manager, size)) {
dsp_lib_manager_delete_no_ref();
*pm_inv = 1;
__dsp_dl_out_alloc_mem(size, lib, pm_inv);
} else {
DL_ERROR("Can not alloc DL out memory for library %s\n",
lib->name);
return -1;
}
}
return 0;
}
int dsp_dl_out_alloc(struct dsp_lib *lib, int *pm_inv)
{
int ret;
size_t dl_out_size;
int alloc_ret;
unsigned long mem_addr;
DL_DEBUG("DL out alloc\n");
ret = dsp_dl_out_create(lib);
if (ret == -1) {
DL_ERROR("[%s] CHK_ERR\n", __func__);
return -1;
}
dl_out_size = dsp_dl_out_get_size(lib->dl_out);
DL_DEBUG("DL_out_size : %zu\n", dl_out_size);
alloc_ret = __dsp_dl_out_alloc_mem(dl_out_size, lib,
pm_inv);
if (alloc_ret == -1) {
dsp_dl_free(lib->dl_out);
lib->dl_out = NULL;
lib->dl_out_mem = NULL;
return -1;
}
lib->dl_out_mem->lib = lib;
mem_addr = lib->dl_out_mem->start_addr;
__dsp_dl_out_cpy_metadata((struct dsp_dl_out *)mem_addr, lib->dl_out);
dsp_dl_free(lib->dl_out);
lib->dl_out = (struct dsp_dl_out *)mem_addr;
lib->dl_out_data_size = dl_out_size - sizeof(*lib->dl_out);
strcpy(lib->dl_out->data, lib->name);
DL_DEBUG("lib name : %s\n", lib->dl_out->data);
dsp_dl_hash_push(lib->dl_out);
return 0;
}
void dsp_dl_out_free(struct dsp_lib *lib)
{
if (lib->dl_out && lib->dl_out_mem) {
dsp_dl_hash_pop(lib->dl_out->data);
dsp_tlsf_free(lib->dl_out_mem, out_manager);
lib->dl_out = NULL;
lib->dl_out_mem = NULL;
}
}