/* Copyright 2018 Canaan Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <atomic.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <uarths.h>
#include "dump.h"
#include "interrupt.h"

#define DUMP_PRINTF printk

static corelock_t s_dump_lock = CORELOCK_INIT;

void dump_core(const char *reason, uintptr_t *regs, uintptr_t cause)
{
    static const char* const reg_usage[][2] = {
        {"mpec ", "Machine exception program counter"},
        {"ra   ", "Return address"},
        {"sp   ", "Stack pointer"},
        {"gp   ", "Global pointer"},
        {"tp   ", "Thread pointer"},
        {"t0   ", "Temporaries Caller"},
        {"t1   ", "Temporaries Caller"},
        {"t2   ", "Temporaries Caller"},
        {"s0/fp", "Saved register/frame pointer"},
        {"s1   ", "Saved register"},
        {"a0   ", "Function arguments/return values"},
        {"a1   ", "Function arguments/return values"},
        {"a2   ", "Function arguments values"},
        {"a3   ", "Function arguments values"},
        {"a4   ", "Function arguments values"},
        {"a5   ", "Function arguments values"},
        {"a6   ", "Function arguments values"},
        {"a7   ", "Function arguments values"},
        {"s2   ", "Saved registers"},
        {"s3   ", "Saved registers"},
        {"s4   ", "Saved registers"},
        {"s5   ", "Saved registers"},
        {"s6   ", "Saved registers"},
        {"s7   ", "Saved registers"},
        {"s8   ", "Saved registers"},
        {"s9   ", "Saved registers"},
        {"s10  ", "Saved registers"},
        {"s11  ", "Saved registers"},
        {"t3   ", "Temporaries Caller"},
        {"t4   ", "Temporaries Caller"},
        {"t5   ", "Temporaries Caller"},
        {"t6   ", "Temporaries Caller"},
    };

    static const char* const regf_usage[][2] = {
        {"ft0 ", "FP temporaries"},
        {"ft1 ", "FP temporaries"},
        {"ft2 ", "FP temporaries"},
        {"ft3 ", "FP temporaries"},
        {"ft4 ", "FP temporaries"},
        {"ft5 ", "FP temporaries"},
        {"ft6 ", "FP temporaries"},
        {"ft7 ", "FP temporaries"},
        {"fs0 ", "FP saved registers"},
        {"fs1 ", "FP saved registers"},
        {"fa0 ", "FP arguments/return values"},
        {"fa1 ", "FP arguments/return values"},
        {"fa2 ", "FP arguments values"},
        {"fa3 ", "FP arguments values"},
        {"fa4 ", "FP arguments values"},
        {"fa5 ", "FP arguments values"},
        {"fa6 ", "FP arguments values"},
        {"fa7 ", "FP arguments values"},
        {"fs2 ", "FP Saved registers"},
        {"fs3 ", "FP Saved registers"},
        {"fs4 ", "FP Saved registers"},
        {"fs5 ", "FP Saved registers"},
        {"fs6 ", "FP Saved registers"},
        {"fs7 ", "FP Saved registers"},
        {"fs8 ", "FP Saved registers"},
        {"fs9 ", "FP Saved registers"},
        {"fs10", "FP Saved registers"},
        {"fs11", "FP Saved registers"},
        {"ft8 ", "FP Temporaries Caller"},
        {"ft9 ", "FP Temporaries Caller"},
        {"ft10", "FP Temporaries Caller"},
        {"ft11", "FP Temporaries Caller"},
    };

    if (CONFIG_LOG_LEVEL >= LOG_ERROR)
    {
        corelock_lock(&s_dump_lock);

        const char unknown_reason[] = "unknown";

        if (!reason)
            reason = unknown_reason;

        uintptr_t *fregs = regs + 32;

        DUMP_PRINTF("core %d, core dump: %s\n", (int)read_csr(mhartid), reason);
        DUMP_PRINTF("Cause 0x%016lx\n", cause);

        int i = 0;

        for (i = 0; i < 32 / 2; i++)
        {
            DUMP_PRINTF(
                "reg[%02d](%s) = 0x%016lx, reg[%02d](%s) = 0x%016lx\n",
                i * 2, reg_usage[i * 2][0], regs[i * 2],
                i * 2 + 1, reg_usage[i * 2 + 1][0], regs[i * 2 + 1]);
        }

        for (i = 0; i < 32 / 2; i++)
        {
            DUMP_PRINTF(
                "freg[%02d](%s) = 0x%016lx, freg[%02d](%s) = 0x%016lx\n",
                i * 2, regf_usage[i * 2][0], fregs[i * 2],
                i * 2 + 1, regf_usage[i * 2 + 1][0], fregs[i * 2 + 1]);
        }

        corelock_unlock(&s_dump_lock);
    }
}
