📄 profxsc1.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
#include <windows.h>
#include <string.h>
#include <nkintr.h>
#include <profiler.h>
#include <profxsc1.h>
#include <Xsc1RegisterBitsPMNC.h>
#include <CspPerfRegFuncs.h>
// Current profiler settings
static XSC1_ProfilerControl g_PMUSettings;
// Rollover counters for each register
static DWORD g_dwRolloverCCNT;
static DWORD g_dwRolloverPMN0;
static DWORD g_dwRolloverPMN1;
// Keep track of whether the profiler is running
static BOOL g_fIsPMURunning;
// defined in winceos\coreos\nk\kernel\profiler.c
extern DWORD g_dwProfilerFlags;
//
// Handle IOCTL_HAL_OEM_PROFILER
//
DWORD g_dwDebugFlag = 0;
DWORD CspPerfRegIoctl(LPVOID lpInBuf, DWORD nInBufSize, LPVOID lpOutBuf,
DWORD nOutBufSize, LPDWORD lpBytesReturned, DWORD *lpAction)
{
ProfilerControl *pControl = (ProfilerControl*)lpInBuf;
if (!lpInBuf
|| (nInBufSize != (sizeof(ProfilerControl) + pControl->OEM.dwControlSize)) ||
(NULL == lpAction) ) {
return ERROR_INVALID_PARAMETER;
}
*lpAction = XSC1PROFILERCONTROL_NOACTION;
// Only START, STOP, QUERY are currently supported
if (pControl->dwOptions & (PROFILE_OEM_QUERY | PROFILE_STOP)) {
OEMProfilerData *pOemData;
XSC1_ProfilerData *pHitData;
// Cannot stop if it's not running
if ((pControl->dwOptions & PROFILE_STOP) && !g_fIsPMURunning) {
return ERROR_ALREADY_EXISTS;
}
// Return PMU values as an XSC1_ProfilerData struct in lpOutBuf.
// Allow lpOutBuf to be NULL if the user does not want to query the
// settings on STOP.
if (lpOutBuf) {
// BUGBUG should check caller trust & access rights to lpOutBuf & lpBytesReturned
if (lpBytesReturned) {
*lpBytesReturned = sizeof(OEMProfilerData) + sizeof(XSC1_ProfilerData);
}
if (nOutBufSize < (sizeof(OEMProfilerData) + sizeof(XSC1_ProfilerData))) {
return ERROR_INSUFFICIENT_BUFFER; // Required size is set in lpBytesReturned
}
// Read the registers into the output buffer
pOemData = lpOutBuf;
pOemData->dwBufSize = sizeof(XSC1_ProfilerData);
pHitData = (XSC1_ProfilerData*)pOemData->buf;
pHitData->dwValCCNT = ReadPMURegister(PMUREG_CCNT);
pHitData->dwOverflowCCNT = g_dwRolloverCCNT;
pHitData->dwValPMN0 = ReadPMURegister(PMUREG_PMN0);
pHitData->dwOverflowPMN0 = g_dwRolloverPMN0;
pHitData->dwValPMN1 = ReadPMURegister(PMUREG_PMN1);
pHitData->dwOverflowPMN1 = g_dwRolloverPMN1;
}
if (pControl->dwOptions & PROFILE_STOP) {
DWORD dwPMNC; // PMU control register setting
// Stop PMU
dwPMNC = ReadPMURegister(PMUREG_PMNC);
CLEAR_PMNC(dwPMNC);
PMNC_BIT_P_SET(dwPMNC);
PMNC_BIT_C_SET(dwPMNC);
WritePMURegister(PMUREG_PMNC, dwPMNC);
*lpAction = XSC1PROFILERCONTROL_STOP;
g_fIsPMURunning = FALSE;
}
return NO_ERROR;
} else if (pControl->dwOptions & PROFILE_START) {
XSC1_ProfilerControl *pXSC1Control;
DWORD dwPMNC; // PMU control register setting
// XSC1_ProfilerControl struct is required as input
if (nInBufSize != sizeof(ProfilerControl) + sizeof(XSC1_ProfilerControl)) {
return ERROR_INVALID_PARAMETER;
}
// Cannot start if it's already running
if (g_fIsPMURunning) {
return ERROR_ALREADY_EXISTS; // BUGBUG there must be a better error
}
*lpAction = XSC1PROFILERCONTROL_START;
pXSC1Control = (XSC1_ProfilerControl*)&(pControl->OEM.bHardwareSpecificSettings);
memcpy(&g_PMUSettings, pXSC1Control, sizeof(XSC1_ProfilerControl));
dwPMNC = ReadPMURegister(PMUREG_PMNC);
// clear out read-unpredictable / write-as-zero bits, as well as everything else
CLEAR_PMNC(dwPMNC);
// ensure that clock divisor is not set
PMNC_BIT_D_DIS(dwPMNC);
g_dwRolloverCCNT = 0;
g_dwRolloverPMN0 = 0;
g_dwRolloverPMN1 = 0;
// CCNT, PMN0, and/or PMN1 are being used to generate an irq. Seed the register(s).
if(g_PMUSettings.dwCCNTOverflowInterval) {
WritePMURegister(PMUREG_CCNT, 0xFFFFFFFF - g_PMUSettings.dwCCNTOverflowInterval);
}
else {
WritePMURegister(PMUREG_CCNT, 0);
}
if(g_PMUSettings.dwPMN0OverflowInterval) {
WritePMURegister(PMUREG_PMN0, 0xFFFFFFFF - g_PMUSettings.dwPMN0OverflowInterval);
}
else {
WritePMURegister(PMUREG_PMN0, 0);
}
if(g_PMUSettings.dwPMN1OverflowInterval) {
WritePMURegister(PMUREG_PMN1, 0xFFFFFFFF - g_PMUSettings.dwPMN1OverflowInterval);
}
else {
WritePMURegister(PMUREG_PMN1, 0);
}
// set the events, and what is generating irq's
dwPMNC |= g_PMUSettings.dwPMNCFlags;
WritePMURegister(PMUREG_PMNC, dwPMNC);
g_fIsPMURunning = TRUE;
return NO_ERROR;
} else {
// STARTPAUSED, PAUSE, CONTINUE are not currently supported
return ERROR_NOT_SUPPORTED;
}
// should never get here
return ERROR_BAD_COMMAND;
}
// Read the PMU registers and send the data to the kernel profiler
static VOID LogHit(unsigned int ra)
{
BYTE bTemp[sizeof(OEMProfilerData) + sizeof(XSC1_ProfilerData)];
OEMProfilerData *pOEMData = (OEMProfilerData*)bTemp;
XSC1_ProfilerData *pXSC1Data = (XSC1_ProfilerData*)&(pOEMData->buf);
// Populate the data buffer
pOEMData->ra = ra;
pOEMData->dwBufSize = sizeof(XSC1_ProfilerData);
pXSC1Data->dwValCCNT = ReadPMURegister(PMUREG_CCNT);
pXSC1Data->dwOverflowCCNT = g_dwRolloverCCNT;
pXSC1Data->dwValPMN0 = ReadPMURegister(PMUREG_PMN0);
pXSC1Data->dwOverflowPMN0 = g_dwRolloverPMN0;
pXSC1Data->dwValPMN1 = ReadPMURegister(PMUREG_PMN1);
pXSC1Data->dwOverflowPMN1 = g_dwRolloverPMN1;
ProfilerHitEx(pOEMData);
}
VOID CspPerfRegOverflowHandler(unsigned int ra)
{
DWORD dwPMNC; // PMU control register setting
BOOL fLoggedHit = FALSE; // Did we already send a hit to the kernel via ProfilerHitEx?
// Determine which counter(s) (CCNT, PMN0, or PMN1) rolled over
dwPMNC = ReadPMURegister(PMUREG_PMNC);
if (dwPMNC & PMNC_BIT_CCNT_OVERFLOW)
{
if (0 != g_PMUSettings.dwCCNTOverflowInterval)
{
// Send to kernel profiler
if (!fLoggedHit)
{
LogHit(ra);
fLoggedHit = TRUE;
}
// Set the register up to overflow again
WritePMURegister(PMUREG_CCNT, 0xFFFFFFFF - g_PMUSettings.dwCCNTOverflowInterval);
}
else
{
g_dwRolloverCCNT++;
}
}
if (dwPMNC & PMNC_BIT_PMN0_OVERFLOW)
{
if (0 != g_PMUSettings.dwPMN0OverflowInterval)
{
// Send to kernel profiler
if (!fLoggedHit)
{
LogHit(ra);
fLoggedHit = TRUE;
}
// Set the register up to overflow again
WritePMURegister(PMUREG_PMN0, 0xFFFFFFFF - g_PMUSettings.dwPMN0OverflowInterval);
}
else
{
g_dwRolloverPMN0++;
}
}
if (dwPMNC & PMNC_BIT_PMN1_OVERFLOW)
{
if (0 != g_PMUSettings.dwPMN1OverflowInterval)
{
// Send to kernel profiler
if (!fLoggedHit)
{
LogHit(ra);
fLoggedHit = TRUE;
}
// Set the register up to overflow again
WritePMURegister(PMUREG_PMN1, 0xFFFFFFFF - g_PMUSettings.dwPMN1OverflowInterval);
}
else
{
g_dwRolloverPMN1++;
}
}
// Xscale Dev. Manual lists bits 1 and 2 as being "read-unpredictable",
// so we are manually setting these bits to "0" (no action)
PMNC_BIT_P_DIS(dwPMNC);
PMNC_BIT_C_DIS(dwPMNC);
PMNC_BIT_RESERVED_CLEAR(dwPMNC);
// To clear the bit, we write a 1 to the same bit that informs of overflow condition
// since those bits are set (overflow indicators), we simply write back what we read (neat!)
WritePMURegister(PMUREG_PMNC, dwPMNC);
}
#define WINCEMACRO 1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -