/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */
#ifndef _DMC_SHARE_LOG_H_
#define _DMC_SHARE_LOG_H_

#ifdef CFG_FEATURE_SHARE_LOG
#include "securec.h"
#if ((!defined(LOG_UT)) && (!defined(EMU_ST)) && (!defined(EVENT_SCHED_UT)) && (!defined(TSDRV_UT)) && \
    (!defined(DRV_UT)) && (!defined(DEVDRV_UT)))
#include "ka_task_pub.h"
#endif
#include "ka_memory_pub.h"
#include "kernel_version_adapt.h"
#include "ka_system_pub.h"

#define SHARE_LOG_PAGE_WRITE      1
#define SHARE_LOG_MAGIC_LENGTH    24
#define SHARE_LOG_RECORD_OFFSET   100
#define SHARE_LOG_MAX_SIZE        (4 * 1024) /* only support 1 page, don't change this value */
#define SHARE_LOG_RECORD_SIZE     (SHARE_LOG_MAX_SIZE - SHARE_LOG_RECORD_OFFSET)
#define SHARE_LOG_PAGE_NUM        (roundup(SHARE_LOG_MAX_SIZE, KA_MM_PAGE_SIZE) / KA_MM_PAGE_SIZE)
#define SHARE_LOG_MAGIC           "drvshartlogab90cd78ef56"

struct share_log_info {
    char magic[SHARE_LOG_MAGIC_LENGTH];
    int total_size;
    void *record_base;
    int record_size;
    int read;
    int write;
};

void __attribute__((weak)) share_log_formatting(struct share_log_info *info, u32 *tmp_write, u32 *remain_size);
void __attribute__((weak)) share_log_update_write(struct share_log_info *info, u32 tmp_write, u32 size, const char* fmt, ka_va_list arg);
void __attribute__((weak)) share_log_record_sub(struct share_log_info *info, const char* fmt, ka_va_list arg);
ka_rw_semaphore_t* __attribute__((weak)) share_log_sem(ka_mm_struct_t *mm);
void __attribute__((weak)) share_log_record(ka_page_t *page, unsigned long start, const char* fmt,ka_mm_struct_t *mm, ka_va_list arg);
void __attribute__((weak)) _share_log_no_kthread_or_no_interrupt_record(unsigned long start, const char* fmt, ka_va_list arg);
void __attribute__((weak)) share_log_no_kthread_or_no_interrupt_record(unsigned long start, const char* fmt, ...);
long __attribute__((weak)) _log_get_user_pages_remote(ka_task_struct_t *tsk, ka_mm_struct_t *mm, u64 va, u32 num, ka_page_t **pages);
int __attribute__((weak)) log_get_user_pages_remote(ka_task_struct_t *tsk, ka_mm_struct_t *mm,u64 va, u32 num, ka_page_t **pages);
void __attribute__((weak)) _share_log_record_by_pid(unsigned long start, u32 pid, const char* fmt, ka_va_list arg);
void __attribute__((weak)) share_log_record_by_pid(unsigned long start, u32 pid, const char* fmt, ...);
void __attribute__((weak)) share_log_err_by_pid_ex(unsigned long start, u32 pid, const char* fmt, ka_va_list arg);
void __attribute__((weak)) share_log_run_info_by_pid_ex(unsigned long start, u32 pid, const char* fmt, ka_va_list arg);
void __attribute__((weak)) share_log_err_ex(unsigned long start, const char* fmt, ka_va_list arg);
void __attribute__((weak)) share_log_run_info_ex(unsigned long start, const char* fmt, ka_va_list arg);

void __attribute__((weak)) share_log_formatting(struct share_log_info *info, u32 *tmp_write, u32 *remain_size)
{
    if (info->read >= (int)*tmp_write) {
        info->read = 0;
        info->write = 0;
        *tmp_write = 0;
        *remain_size = SHARE_LOG_RECORD_SIZE;
    }
}

void __attribute__((weak)) share_log_update_write(struct share_log_info *info, u32 tmp_write, u32 size,
    const char* fmt, ka_va_list arg)
{
    int write_offset = vsnprintf_s((u8 *)info + SHARE_LOG_RECORD_OFFSET + tmp_write, size, size - 1, fmt, arg);
    if (write_offset > 0) {
        info->write += write_offset;
    } else {
        info->write = 0;
        info->read = 0;
    }
}

void __attribute__((weak)) share_log_record_sub(struct share_log_info *info, const char* fmt, ka_va_list arg)
{
    u32 tmp_write = (u32)info->write;
    u32 remain_size = (u32)info->record_size - tmp_write;

    if (ka_base_strcmp(info->magic, SHARE_LOG_MAGIC) == 0 && info->total_size == SHARE_LOG_MAX_SIZE &&
        tmp_write <= SHARE_LOG_RECORD_SIZE && remain_size <= SHARE_LOG_RECORD_SIZE  &&
        (tmp_write + remain_size) <= SHARE_LOG_RECORD_SIZE) {
        share_log_formatting(info, &tmp_write, &remain_size);
        share_log_update_write(info, tmp_write, remain_size, fmt, arg);
    }
}

ka_rw_semaphore_t* __attribute__((weak)) share_log_sem(ka_mm_struct_t *mm)
{
    return get_mmap_sem(mm);
}

void __attribute__((weak)) share_log_record(ka_page_t *page, unsigned long start, const char* fmt,
    ka_mm_struct_t *mm, ka_va_list arg)
{
    ka_vm_area_struct_t *vma = NULL;
    /*
     * The use of mmap_sem is to ensure that the log records are serialized when multi-threaded,
     * and no logging is performed when find_vma is NULL
     */
    if (ka_task_down_write_trylock(share_log_sem(mm)) != 0) {
        vma = ka_mm_find_vma(mm, start);
        if ((vma != NULL) && (vma->vm_start <= start)) {
            struct share_log_info *sha_info = ka_mm_page_address(page);
            share_log_record_sub(sha_info, fmt, arg);
        }
        ka_task_up_write(share_log_sem(mm));
    }
}

/*
 * share_log will not record logs if the interface is called in mmap, unmap or release process, because :
 * 1.mmap, unmap : os first performs down_write and then calls the mmap interface registered by each module,
 *   which will cause down_write or down_read to fail if the share_log_err call occurs;
 * 2.release : current->mm is null.
 */
void __attribute__((weak)) _share_log_no_kthread_or_no_interrupt_record(unsigned long start, const char* fmt, ka_va_list arg)
{
    if ((ka_task_get_current()->flags & KA_TASK_PF_KTHREAD) == 0 && ka_system_in_interrupt() == 0 && ka_task_get_current()->mm != NULL &&
        ka_task_down_write_trylock(share_log_sem(ka_task_get_current()->mm)) != 0) { /* Prevent read_lock nested */
        ka_page_t *sha_page = NULL;
        ka_task_up_write(share_log_sem(ka_task_get_current()->mm));
        if (ka_mm_get_user_pages_fast(start, SHARE_LOG_PAGE_NUM, KA_FOLL_WRITE, &sha_page) == (int)SHARE_LOG_PAGE_NUM) {
            share_log_record(sha_page, start, fmt, ka_task_get_current()->mm, arg);
            ka_mm_put_page(sha_page);
        }
    }
}

void __attribute__((weak)) share_log_no_kthread_or_no_interrupt_record(unsigned long start, const char* fmt, ...)
{
    ka_va_list arg;
    va_start(arg, fmt);
    _share_log_no_kthread_or_no_interrupt_record(start, fmt, arg);
    va_end(arg);
}

#define TSDRV_SHARE_LOG_START   (0xE0000000000ULL)
#define DEVMM_SHARE_LOG_START   (0xE0000080000ULL)
#define DEVMNG_SHARE_LOG_START  (0xE0000100000ULL)
#define HDC_SHARE_LOG_START     (0xE0000180000ULL)
#define ESCHED_SHARE_LOG_START  (0xE0000200000ULL)
#define XSMEM_SHARE_LOG_START   (0xE0000280000ULL)
#define QUEUE_SHARE_LOG_START   (0xE0000300000ULL)
#define COMMON_SHARE_LOG_START  (0xE0000380000ULL)
#define APM_SHARE_LOG_START     (0xE0000400000ULL)

#define TSDRV_SHARE_LOG_RUNINFO_START  (0xE0000040000ULL)
#define DEVMM_SHARE_LOG_RUNINFO_START  (0xE00000C0000ULL)
#define DEVMNG_SHARE_LOG_RUNINFO_START (0xE0000140000ULL)
#define HDC_SHARE_LOG_RUNINFO_START    (0xE00001C0000ULL)
#define ESCHED_SHARE_LOG_RUNINFO_START (0xE0000240000ULL)
#define XSMEM_SHARE_LOG_RUNINFO_START  (0xE00002C0000ULL)
#define QUEUE_SHARE_LOG_RUNINFO_START  (0xE0000340000ULL)
#define COMMON_SHARE_LOG_RUNINFO_START (0xE00003C0000ULL)
#define APM_SHARE_LOG_RUNINFO_START    (0xE0000440000ULL)

long __attribute__((weak)) _log_get_user_pages_remote(ka_task_struct_t *tsk, ka_mm_struct_t *mm,
    u64 va, u32 num, ka_page_t **pages)
{
    long got_num = -1;

    if (ka_task_down_write_trylock(share_log_sem(mm)) == 0) { /* Prevent read_lock nested */
        return got_num;
    }
    ka_task_up_write(share_log_sem(mm));

    if (ka_task_down_read_trylock(share_log_sem(mm)) == 0) {
        return got_num;
    }
    got_num = ka_mm_get_user_pages_remote(tsk, mm, va, KA_FOLL_WRITE, num, pages);
    ka_task_up_read(share_log_sem(mm));
    return got_num;
}

/*
 * num is 1, when get page failed ,directly return
 */
int __attribute__((weak)) log_get_user_pages_remote(ka_task_struct_t *tsk, ka_mm_struct_t *mm,
    u64 va, u32 num, ka_page_t **pages)
{
    return (_log_get_user_pages_remote(tsk, mm, va, num, pages) == num) ? 0 : -ENOMEM;
}

/*
 * in_interrupt is not allow
 */
void __attribute__((weak)) _share_log_record_by_pid(unsigned long start, u32 pid, const char* fmt, ka_va_list arg)
{
    ka_task_struct_t *task = NULL;
    ka_page_t *sha_page = NULL;
    ka_mm_struct_t *mm = NULL;
    ka_struct_pid_t *pro_id = NULL;
    int ret;

    if (ka_system_in_interrupt() != 0) {
        return;
    }

    pro_id = ka_task_find_get_pid((int)pid);
    if (pro_id == NULL) {
        return;
    }

    task = ka_task_get_pid_task(pro_id, KA_PIDTYPE_PID);
    if (task == NULL) {
        ka_task_put_pid(pro_id);
        return;
    }
    mm = ka_task_get_task_mm(task);
    if (mm == NULL) {
        ka_task_put_task_struct(task);
        ka_task_put_pid(pro_id);
        return;
    }
    ret = log_get_user_pages_remote(task, mm, start, (u32)SHARE_LOG_PAGE_NUM, &sha_page);
    if (ret != 0) {
        ka_mm_mmput(mm);
        ka_task_put_task_struct(task);
        ka_task_put_pid(pro_id);
        return;
    }
    share_log_record(sha_page, start, fmt, mm, arg);
    ka_mm_put_page(sha_page);
    ka_mm_mmput(mm);
    ka_task_put_task_struct(task);
    ka_task_put_pid(pro_id);
}

void __attribute__((weak)) share_log_record_by_pid(unsigned long start, u32 pid, const char* fmt, ...)
{
    ka_va_list arg;
    va_start(arg, fmt);
    _share_log_record_by_pid(start, pid, fmt, arg);
    va_end(arg);
}

void __attribute__((weak)) share_log_err_by_pid_ex(unsigned long start, u32 pid, const char* fmt, ka_va_list arg)
{
    _share_log_record_by_pid(start, pid, fmt, arg);
}

void __attribute__((weak)) share_log_run_info_by_pid_ex(unsigned long start, u32 pid, const char* fmt, ka_va_list arg)
{
    _share_log_record_by_pid(start, pid, fmt, arg);
}

void __attribute__((weak)) share_log_err_ex(unsigned long start, const char* fmt, ka_va_list arg)
{
    _share_log_no_kthread_or_no_interrupt_record(start, fmt, arg);
}

void __attribute__((weak)) share_log_run_info_ex(unsigned long start, const char* fmt, ka_va_list arg)
{
    _share_log_no_kthread_or_no_interrupt_record(start, fmt, arg);
}

#define share_log_err_by_pid(start, pid, fmt, ...) \
    share_log_record_by_pid(start, pid, fmt, ##__VA_ARGS__)

#define share_log_err(start, fmt, ...) \
    share_log_no_kthread_or_no_interrupt_record(start, fmt, ##__VA_ARGS__)

#define share_log_run_info_by_pid(start, pid, fmt, ...) \
    share_log_record_by_pid(start, pid, fmt, ##__VA_ARGS__)

#define share_log_run_info(start, fmt, ...) \
    share_log_no_kthread_or_no_interrupt_record(start, fmt, ##__VA_ARGS__)
#else
#define TSDRV_SHARE_LOG_START
#define ESCHED_SHARE_LOG_START
#define share_log_err(start, fmt, ...)
#define share_log_err_by_pid(start, pid, fmt, ...)
#endif
#endif /* _DMC_SHARE_LOG_H_ */