git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@205 0109f412-320b-0410-ab79-c3e0c5ffbbe6
250 lines
6.6 KiB
C++
250 lines
6.6 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Special CPU yielding routines for Netware
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 2000-2003,2005-2006 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of version 2 of the GNU General Public
|
|
// License 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.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, contact Novell, Inc.
|
|
//
|
|
// To contact Novell about this file by physical or electronic mail,
|
|
// you may find current contact information at www.novell.com
|
|
//
|
|
// $Id: nwyield.cpp 12315 2006-01-19 15:16:37 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
#ifdef FLM_NLM
|
|
|
|
/****************************************************************************/
|
|
/* Procedures imported from the Kernel */
|
|
/****************************************************************************/
|
|
|
|
extern "C"
|
|
{
|
|
|
|
void StopBell(void);
|
|
|
|
unsigned long kQueCount(
|
|
unsigned long uiQueHandle);
|
|
|
|
unsigned long CEvaluateExpression(
|
|
unsigned char ** commandLine,
|
|
unsigned long * stackFrame,
|
|
unsigned long * number);
|
|
|
|
/****************************************************************************
|
|
Data items imported from the kernel
|
|
****************************************************************************/
|
|
|
|
extern unsigned long CpuCurrentProcessor;
|
|
extern unsigned long WorkToDoListHead;
|
|
|
|
/****************************************************************************/
|
|
|
|
struct OldPerCpuStruct
|
|
{
|
|
unsigned long reserved0[24];
|
|
|
|
/* offset 0x60 */
|
|
unsigned long reserved1[3];
|
|
unsigned long PSD_ThreadStartClocks;
|
|
unsigned long reserved2[4];
|
|
|
|
unsigned long reserved3[40];
|
|
|
|
/* offset 0x120 */
|
|
unsigned long PSD_LocalWTDHead;
|
|
};
|
|
|
|
/****************************************************************************/
|
|
#if !defined( __MWERKS__)
|
|
extern unsigned long ReadInternalClock(void);
|
|
#else
|
|
unsigned long ReadInternalClock(void);
|
|
#endif
|
|
|
|
// Local function prototypes
|
|
|
|
FSTATIC int LocalYieldInit( void);
|
|
|
|
FSTATIC void OldOSCompatibleYield(void);
|
|
|
|
|
|
} // extern "C"
|
|
|
|
#if !defined( __MWERKS__)
|
|
#pragma aux ReadInternalClock = \
|
|
0x0F 0x31 /* RDTSC */\
|
|
modify exact [EAX EDX];
|
|
#else
|
|
unsigned long ReadInternalClock(void)
|
|
{
|
|
__asm
|
|
{
|
|
rdtsc
|
|
ret
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************/
|
|
|
|
static unsigned long yieldTimeSlice = 0;
|
|
static unsigned long stackFrame[0x100] = {0};
|
|
static unsigned long * ThreadFeederQue = 0;
|
|
static struct OldPerCpuStruct * pPerCpuDataArea = 0;
|
|
|
|
/****************************************************************************
|
|
Desc: This API is called once the first time NWYieldIfTime() is called if
|
|
kYieldIfTimeSliceUp isn't supported by the host OS.
|
|
****************************************************************************/
|
|
FSTATIC int LocalYieldInit(void)
|
|
{
|
|
unsigned char * buffer;
|
|
unsigned char * pointer;
|
|
unsigned long ccode;
|
|
unsigned long address;
|
|
unsigned long (*ConvertMicroSecondsToClocks)(unsigned long data);
|
|
|
|
// Fixup ThreadFeededQue
|
|
|
|
buffer = (unsigned char *)"SERVER.NLM|ThreadFeederQue";
|
|
|
|
// Get the address from the debugger
|
|
|
|
ccode = CEvaluateExpression( &buffer, &stackFrame[0], &address);
|
|
if (ccode != 0)
|
|
{
|
|
return( -1);
|
|
}
|
|
|
|
ThreadFeederQue = (unsigned long *)address;
|
|
|
|
// Setup yieldTimeSlice
|
|
// First get a pointer to ConvertMicroSecondsToClocks and then verify it
|
|
|
|
pointer = (unsigned char *)(&StopBell);
|
|
pointer = pointer + 0x1ea;
|
|
if (pointer[0] != 0x8b ||
|
|
pointer[1] != 0x44 ||
|
|
pointer[2] != 0x24 ||
|
|
pointer[3] != 0x04 ||
|
|
pointer[4] != 0x33 ||
|
|
pointer[5] != 0xd2 ||
|
|
pointer[6] != 0xf7)
|
|
{
|
|
return( -1);
|
|
}
|
|
|
|
*(unsigned long *)(&ConvertMicroSecondsToClocks) = (unsigned long)pointer;
|
|
|
|
// Now calculate our desired time slice in clocks
|
|
|
|
yieldTimeSlice = (*ConvertMicroSecondsToClocks)(200);
|
|
|
|
// Get a pointer to the per-CPU data area
|
|
|
|
buffer = (unsigned char *)"LOADER.NLM|PerCpuDataArea";
|
|
ccode = CEvaluateExpression( &buffer, &stackFrame[0], &address);
|
|
if (ccode != 0)
|
|
{
|
|
return( -1);
|
|
}
|
|
|
|
pPerCpuDataArea = (struct OldPerCpuStruct *)address;
|
|
return( 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine does everything that kYieldIfTimeSliceUp() does, and it is
|
|
compatible with 5.0 SP4 and 5.1 release. It shouldn't be used on host
|
|
OSs that support kYieldIfTimeSliceUp().
|
|
****************************************************************************/
|
|
FSTATIC void OldOSCompatibleYield(void)
|
|
{
|
|
unsigned long timeStamp;
|
|
unsigned long threadStartTime;
|
|
|
|
if ((CpuCurrentProcessor == 0) && (WorkToDoListHead != 0))
|
|
{
|
|
|
|
// If we are P0 and there is legacy WTD waiting, then yield
|
|
|
|
kYieldThread();
|
|
return;
|
|
}
|
|
|
|
if ((pPerCpuDataArea->PSD_LocalWTDHead != 0) ||
|
|
(kQueCount( ThreadFeederQue[CpuCurrentProcessor]) != 0))
|
|
{
|
|
|
|
// If there is MPK WTD or a feederQ thread waiting, then yield
|
|
|
|
kYieldThread();
|
|
return;
|
|
}
|
|
|
|
timeStamp = ReadInternalClock();
|
|
threadStartTime = pPerCpuDataArea->PSD_ThreadStartClocks;
|
|
|
|
// Note 32 bit arithmetic is sufficient and less overhead
|
|
|
|
if ((timeStamp - threadStartTime) > yieldTimeSlice)
|
|
{
|
|
kYieldThread();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This is the routine to call for time sensitive yielding. The first
|
|
time it is called, it will change itself to a JMP to the appropriate
|
|
yield procedure. If it is on 5.1 SP1 or greater, it will jump to
|
|
kYieldIfTimeSliceUp(). If it is on 5.0 SP4 or 5.1 release, it will
|
|
jump to OldOSCompatibleYield(). Otherwise it will jump to
|
|
kYieldThread().
|
|
****************************************************************************/
|
|
void NWYieldIfTime(void)
|
|
{
|
|
unsigned char * fixup;
|
|
unsigned long address;
|
|
|
|
*(unsigned long *)(&address) = ImportPublicSymbol(
|
|
(unsigned long)f_getNLMHandle(),
|
|
(unsigned char *)"\x13" "kYieldIfTimeSliceUp");
|
|
|
|
fixup = (unsigned char *)(&NWYieldIfTime);
|
|
|
|
if (address == 0)
|
|
{
|
|
// We couldn't import the procedure, so see if our local routine
|
|
// is compatible.
|
|
|
|
if (LocalYieldInit() == 0)
|
|
{
|
|
address = (unsigned long)(&OldOSCompatibleYield);
|
|
}
|
|
else
|
|
{
|
|
address = (unsigned long)(&kYieldThread);
|
|
}
|
|
}
|
|
|
|
fixup[0] = 0xE9;
|
|
++fixup;
|
|
address = address - (4 + (unsigned long)fixup);
|
|
*(unsigned long *)fixup = address;
|
|
}
|
|
|
|
#endif // FLM_NLM
|