/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: import/chips/p9/common/pmlib/ppc405lib/ssx_dump.c $           */
/*                                                                        */
/* OpenPOWER HCODE Project                                                */
/*                                                                        */
/* COPYRIGHT 2015,2017                                                    */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/// \file ssx_dump.c
/// \brief Routines for dumping SSX kernel data structures
///
/// \note This is a quick hack to help solve a P8 bringup issue.  This code is
/// PPC405 (OCC) specific, i.e., not "portable" SSX. Ideally this type of dump
/// would be implemented in an external debugging tool as well, however it's
/// simplest to implement here in the execution context.

#include "ssx_dump.h"

#define SSX_DUMP_UNIMPLEMENTED 0x00d86701

static const char* _threadState[] =
{
    0,
    "Suspended Runnable",
    "Mapped",
    "Suspended Blocked",
    "Completed",
    "Deleted",
};

static const char* _threadFlags[] =
{
    0,
    "Semaphore Pend",
    "Timer Pend",
    "Timer Pend | Semaphore Pend",
    "Timed Out",
    "Timed Out | Semaphore Pend",
    "Timed Out | Timer Pend",
    "Timed Out | Timer Pend | Semaphore Pend",
};


static void
_dumpTimer(FILE* stream, SsxTimer* timer)
{
    fprintf(stream,
            "-- Timer @ %p\n"
            "--     Deque.previous = %p\n"
            "--     Deque.next     = %p\n"
            "--     Timeout        = 0x%016llx\n"
            "--     Period         = 0x%016llx\n"
            "--     Callback       = %p\n"
            "--     Arg            = %p\n"
            "--     Options        = 0x%02x\n",
            timer,
            timer->deque.previous,
            timer->deque.next,
            timer->timeout,
            (unsigned long long)(timer->period),
            timer->callback,
            timer->arg,
            timer->options);
}


static void
_dumpThread(FILE* stream, SsxThread* thread)
{
    SsxThreadContext*        threadCtx;
    SsxThreadContextFullIrq* threadCtxIrq;
    uint32_t srr[4], lr, sp;

    fprintf(stream,
            "-- Thread mapped at priority %d (%p)\n"
            "--     Thread state        = %s (%d)\n"
            "--     Thread flags        = %s (0x%02x)\n"
            "--     Saved Stack Pointer = %p\n",
            thread->priority, thread,
            _threadState[thread->state], thread->state,
            _threadFlags[thread->flags], thread->flags,
            (void*)thread->saved_stack_pointer);

    if (thread->flags & SSX_THREAD_FLAG_SEMAPHORE_PEND)
    {

        fprintf(stream,
                "--     Semaphore           = %p\n",
                (void*)thread->semaphore);
    }

    fprintf(stream,
            "---------------------------------------------\n");

    if (thread->flags & SSX_THREAD_FLAG_TIMER_PEND)
    {

        _dumpTimer(stream, &(thread->timer));
        fprintf(stream,
                "---------------------------------------------\n");
    }

    if ((thread == ssx_current()) && !__ssx_kernel_context_any_interrupt())
    {

        fprintf(stream,
                "--    This thread is executing ssx_dump()\n");

    }
    else
    {

        if (thread == ssx_current())
        {

            // This is the interrupted thread, and only has its volatile
            // context saved.  The thread stack pointer is stored in a global
            // kernel variable.

            if (__ssx_kernel_context_critical_interrupt())
            {

                SSX_PANIC(SSX_DUMP_UNIMPLEMENTED);
                srr[0] = srr[1] = srr[2] = srr[3] = lr = sp = 0; /* For GCC */

            }
            else
            {

                threadCtxIrq =
                    (SsxThreadContextFullIrq*)__ssx_saved_sp_noncritical;
                srr[0] = threadCtxIrq->srr0;
                srr[1] = threadCtxIrq->srr1;
                srr[2] = threadCtxIrq->srr2;
                srr[3] = threadCtxIrq->srr3;
                lr = threadCtxIrq->lr;
                sp = threadCtxIrq->r1;
            }
        }
        else
        {

            // This is a fully swapped-out thread.  The context is saved in
            // at the stored stack pointer.

            threadCtx = (SsxThreadContext*)(thread->saved_stack_pointer);
            srr[0] = threadCtx->srr0;
            srr[1] = threadCtx->srr1;
            srr[2] = threadCtx->srr2;
            srr[3] = threadCtx->srr3;
            lr = threadCtx->lr;
            sp = ((uint32_t*)threadCtx->r1)[0];
        }

        fprintf(stream,
                "--    SRR0: 0x%08x  SRR1: 0x%08x  "
                "SRR2: 0x%08x  SRR3: 0x%08x\n"
                "--      LR: 0x%08x\n",
                srr[0], srr[1], srr[2], srr[3],
                lr);

        fprintf(stream,
                "---------------------------------------------\n");

        // Unwind the stack

        while (sp != 0)
        {

            fprintf(stream,
                    "--      SP: 0x%08x  *LR*:0x%08x\n",
                    sp, ((uint32_t*)sp)[1]);
            sp = ((uint32_t*)sp)[0];
        }
    }
}




void
ssx_dump(FILE* stream, int options)
{
    int i, sep;
    SsxThread* thread;

    fprintf(stream,
            "------------------------------------------------------------\n");
    fprintf(stream,
            "-- SSX Kernel Dump @ 0x%016llx\n"
            "--          USPRG0 = 0x%08x\n"
            "-- __ssx_run_queue = 0x%08x\n",
            ssx_timebase_get(),
            mfspr(SPRN_USPRG0),
            __ssx_run_queue);
    fprintf(stream,
            "------------------------------------------------------------\n");

    sep = 0;

    for (i = 0; i < SSX_THREADS; i++)
    {

        ssx_thread_at_priority(i, &thread);

        if (thread)
        {
            if (sep)
            {
                fprintf(stream,
                        "*********************************************\n");
            }

            _dumpThread(stream, thread);
            sep = 1;
        }
    }

    fprintf(stream,
            "------------------------------------------------------------\n");
}
