433 lines
10 KiB
C
Executable file
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;
|
|
}
|
|
}
|