/****************************************************************************
*
*                   SciTech OS Portability Manager Library
*
*  ========================================================================
*
*    The contents of this file are subject to the SciTech MGL Public
*    License Version 1.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.scitechsoft.com/mgl-license.txt
*
*    Software distributed under the License is distributed on an
*    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
*    implied. See the License for the specific language governing
*    rights and limitations under the License.
*
*    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
*
*    The Initial Developer of the Original Code is SciTech Software, Inc.
*    All Rights Reserved.
*
*  ========================================================================
*
* Language:     ANSI C
* Environment:  QNX
*
* Description:  MTRR helper functions module. To make it easier to implement
*               the MTRR support under QNX, we simply put our ring 0 helper
*               functions into stubs that run them at ring 0 using whatever
*               mechanism is available.
*
****************************************************************************/

#include "pmapi.h"
#include <stdio.h>
#include <sys/mman.h>
#include <time.h>
#ifdef __QNXNTO__
#include <sys/neutrino.h>
#include <sys/syspage.h>
#else
#include <i86.h>
#include <sys/irqinfo.h>
#endif

/*--------------------------- Global variables ----------------------------*/

#define R0_FLUSH_TLB    0
#define R0_SAVE_CR4     1
#define R0_RESTORE_CR4  2
#define R0_READ_MSR     3
#define R0_WRITE_MSR    4

typedef struct {
    int     service;
    int     reg;
    ulong   eax;
    ulong   edx;
    } R0_data;

extern volatile R0_data _PM_R0;

/*----------------------------- Implementation ----------------------------*/

#ifdef __QNXNTO__
const struct sigevent * _ASMAPI _PM_ring0_isr(void *arg, int id);
#else
pid_t far _ASMAPI _PM_ring0_isr();
#endif

/****************************************************************************
REMARKS:
Return true if ring 0 (or if we can call the helpers functions at ring 0)
****************************************************************************/
ibool _ASMAPI _MTRR_isRing0(void)
{
#ifdef __QNXNTO__
    return false;   // Not implemented yet!
#else
    return true;
#endif
}

/****************************************************************************
REMARKS:
Function to execute a service at ring 0. This is done using the clock
interrupt handler since the code we attach to it will always run at ring 0.
****************************************************************************/
static void CallRing0(void)
{
#ifdef __QNXNTO__
    uint    clock_intno = SYSPAGE_ENTRY(qtime)->intr;
#else
    uint    clock_intno = 0;    /* clock irq */
#endif
    int     intrid;

#ifdef __QNXNTO__
    mlock((void*)&_PM_R0, sizeof(_PM_R0));
    ThreadCtl(_NTO_TCTL_IO, 0);
#endif
#ifdef __QNXNTO__
    if ((intrid = InterruptAttach(_NTO_INTR_CLASS_EXTERNAL | clock_intno,
        _PM_ring0_isr, (void*)&_PM_R0, sizeof(_PM_R0), _NTO_INTR_FLAGS_END)) == -1) {
#else
    if ((intrid = qnx_hint_attach(clock_intno, _PM_ring0_isr, FP_SEG(&_PM_R0))) == -1) {
#endif
        perror("Attach");
        exit(-1);
        }
    while (_PM_R0.service != -1)
        ;
#ifdef __QNXNTO__
    InterruptDetachId(intrid);
#else
    qnx_hint_detach(intrid);
#endif
}

/****************************************************************************
REMARKS:
Flush the translation lookaside buffer.
****************************************************************************/
void PMAPI PM_flushTLB(void)
{
    _PM_R0.service = R0_FLUSH_TLB;
    CallRing0();
}

/****************************************************************************
REMARKS:
Read and return the value of the CR4 register
****************************************************************************/
ulong _ASMAPI _MTRR_saveCR4(void)
{
    _PM_R0.service = R0_SAVE_CR4;
    CallRing0();
    return _PM_R0.reg;
}

/****************************************************************************
REMARKS:
Restore the value of the CR4 register
****************************************************************************/
void _ASMAPI _MTRR_restoreCR4(ulong cr4Val)
{
    _PM_R0.service = R0_RESTORE_CR4;
    _PM_R0.reg = cr4Val;
    CallRing0();
}

/****************************************************************************
REMARKS:
Read a machine status register for the CPU.
****************************************************************************/
void _ASMAPI _MTRR_readMSR(
    int reg,
    ulong *eax,
    ulong *edx)
{
    _PM_R0.service = R0_READ_MSR;
    _PM_R0.reg = reg;
    CallRing0();
    *eax = _PM_R0.eax;
    *edx = _PM_R0.edx;
}

/****************************************************************************
REMARKS:
Write a machine status register for the CPU.
****************************************************************************/
void _ASMAPI _MTRR_writeMSR(
    int reg,
    ulong eax,
    ulong edx)
{
    _PM_R0.service = R0_WRITE_MSR;
    _PM_R0.reg = reg;
    _PM_R0.eax = eax;
    _PM_R0.edx = edx;
    CallRing0();
}
