2020-09-30 17:17:25 +02:00

4837 lines
146 KiB
C

/*
*
* dmserv.c
*
* The debug monitor server
*
*/
#include "dmp.h"
#include "xboxp.h"
#include "xconfig.h"
#include "av.h"
#include "xlaunch.h"
#undef DeleteFile
#include "d3d8.h"
void ShowIPAddress(ULONG);
DWORD DedicatedServThread(LPVOID lpv);
RTL_CRITICAL_SECTION csEch;
RTL_CRITICAL_SECTION csAccept;
CST rgcst[MAX_CONNECTIONS];
const char rgchHex[] = "0123456789ABCDEF";
char rgchDbgName[256];
ULONG ulIpAddr;
BOOL g_fServShutdown;
BOOL g_fDebugging;
BOOL g_fLockLevel;
BOOL g_fDebugSecureMode;// = TRUE;
ULARGE_INTEGER g_luBoxId;
KEVENT kevtServ;
PETHREAD pthrServ;
extern CHH rgchh[];
extern int cchh;
INITIALIZED_OBJECT_STRING_RDATA(ostDash, "\\Device\\Harddisk0\\Partition1\\DASHBOARD.XBX");
CHAR XdkLauncherPathname[] = "\\Device\\Harddisk0\\Partition2\\xshell.xbe";
/* Extension stuff */
ECH *rgpech[MAX_ECH];
NTSTATUS
WINAPI
XWriteTitleInfoNoReboot(
PCOSTR pszLaunchPath,
PCOSTR pszDDrivePath,
DWORD dwLaunchDataType,
DWORD dwTitleId,
PLAUNCH_DATA pLaunchData
);
BOOL
WINAPI
XapiFormatFATVolume(
POBJECT_STRING VolumePath
);
void InitServ(void)
{
InitializeCriticalSection(&csEch);
InitializeCriticalSection(&csAccept);
KeInitializeEvent(&kevtServ, NotificationEvent, FALSE);
}
void StopServ(void)
{
int icst;
PETHREAD pthr;
/* Loop through the socket list and close each one down */
EnterCriticalSection(&csAccept);
g_fServShutdown = TRUE;
for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
if(rgcst[icst].s != INVALID_SOCKET) {
/* If this socket isn't running on the current thread, it may be
* inside of a select. In that case, we take it off the thread,
* stop the select, and give the thread a little bit of time to
* exit the select */
pthr = rgcst[icst].pthrDedicated;
rgcst[icst].pthrDedicated = NULL;
if(pthr != PsGetCurrentThread()) {
KeAlertThread(&pthr->Tcb, KernelMode);
Sleep(20);
}
shutdown(rgcst[icst].s, SD_SEND);
}
}
LeaveCriticalSection(&csAccept);
/* Wait for the sockets to close */
Sleep(200);
}
int SgnCompareRgch(const char *sz1, const char *sz2, int cch)
{
while(cch-- && *sz1) {
char ch1 = *sz1++;
char ch2 = *sz2++;
if(ch1 >= 'a' && ch2 <= 'z')
ch1 -= 32;
if(ch2 >= 'a' && ch2 <= 'z')
ch2 -= 32;
if(ch1 != ch2)
return ch1 - ch2;
}
if(*sz1)
return *sz1;
return cch < 0 ? 0 : -*sz2;
}
BOOL FEqualRgch(const char *sz1, const char *sz2, int cch)
{
return SgnCompareRgch(sz1, sz2, cch) == 0;
}
DWORD DwHexFromSz(LPCSTR sz, LPCSTR *szOut)
{
DWORD dw = 0;
for(;;) {
if(*sz >= '0' && *sz <= '9')
dw = dw * 16 + (*sz - '0');
else if(*sz >= 'A' && *sz <= 'F')
dw = dw * 16 + (*sz - 'A' + 10);
else if(*sz >= 'a' && *sz <= 'f')
dw = dw * 16 + (*sz - 'a' + 10);
else
break;
++sz;
}
if(szOut)
*szOut = sz;
return dw;
}
DWORD DwFromSz(LPCSTR sz, int *pcchUsed)
{
DWORD dw = 0;
LPCSTR szStart = sz;
/* Process decimal, octal, or hex */
if(*sz == '0') {
++sz;
if(*sz == 'x')
dw = DwHexFromSz(++sz, &sz);
else
while(*sz >= '0' && *sz <= '7')
dw = dw * 8 + (*sz++ - '0');
} else
while(*sz >= '0' && *sz <= '9')
dw = dw * 10 + (*sz++ - '0');
if(pcchUsed)
*pcchUsed = sz - szStart;
return dw;
}
void CloseConn(int icst)
{
KIRQL irql;
ASSERT(rgcst[icst].s != INVALID_SOCKET);
closesocket(rgcst[icst].s);
/* The server thread will attempt to read data from this socket once
* pechDedicated is cleared, and may attempt to reuse this
* connection state as soon as the socket ID is set to INVALID. We
* need to do all of this as one atomic operation */
irql = KeRaiseIrqlToDpcLevel();
rgcst[icst].dwStatus = 0;
rgcst[icst].pechDedicated = NULL;
rgcst[icst].pthrDedicated = NULL;
rgcst[icst].s = INVALID_SOCKET;
KeLowerIrql(irql);
}
void InitDmcc(int icst)
{
rgcst[icst].dmcc.BytesRemaining = -1;
rgcst[icst].dmcc.Buffer = NULL;
rgcst[icst].dmcc.CustomData = &rgcst[icst].ccs;
rgcst[icst].dmcc.HandlingFunction = NULL;
rgcst[icst].dwStatus &= ~(CONN_BINARY | CONN_BINSEND | CONN_MULTILINE);
}
BOOL PrintSockLine(SOCKET s, LPCSTR sz)
{
char szT[1024];
int ich;
for(ich = 0; sz[ich] && ich < sizeof szT - 2; ++ich)
szT[ich] = sz[ich];
if(sz[ich]) {
/* Didn't get the whole string, so we have to send two packets */
if(send(s, sz, strlen(sz), 0) <= 0)
return FALSE;
if(send(s, "\015\012", 2, 0) <= 0)
return FALSE;
} else {
szT[ich++] = '\015';
szT[ich++] = '\012';
if(send(s, szT, ich, 0) <= 0)
return FALSE;
}
return TRUE;
}
BOOL ReadSockLine(SOCKET s, char *sz, int cch)
{
int cb;
int ich = 0;
--cch; /* save room for \0 */
while(ich < cch) {
cb = recv(s, &sz[ich], 1, 0);
if(cb <= 0) {
if(ich == 0)
return FALSE;
sz[ich] = 0;
return TRUE;
}
if(sz[ich] == '\012') {
sz[ich] = 0;
return TRUE;
}
if(sz[ich] != '\015')
++ich;
}
sz[ich] = 0;
return TRUE;
}
__inline BOOL FIsSpace(char ch)
{
return ch == ' ' || ch == '\015' || ch == 0;
}
const char *PchGetParam(LPCSTR szCmd, LPCSTR szKey, BOOL fNeedValue)
{
const char *pchTok;
int cchTok;
BOOL fQuote = FALSE;
/* Skip the command */
for(pchTok = szCmd; !FIsSpace(*pchTok); ++pchTok);
while(*pchTok) {
/* Skip leading spaces */
while(*pchTok && FIsSpace(*pchTok))
++pchTok;
if(!*pchTok)
return NULL;
for(cchTok = 0; !FIsSpace(pchTok[cchTok]); ++cchTok) {
if(pchTok[cchTok] == '=') {
if(FEqualRgch(szKey, pchTok, cchTok))
return pchTok + cchTok + 1; /* Skip the '=' */
break;
}
}
/* If we didn't see the '=' we need to check anyway */
if(!fNeedValue && pchTok[cchTok] != '=' && FEqualRgch(szKey, pchTok,
cchTok))
return pchTok + cchTok;
/* No match, so we need to skip past the value */
pchTok += cchTok;
while(*pchTok && (!FIsSpace(*pchTok) || fQuote))
if(*pchTok++ == '"')
fQuote = !fQuote;
}
return NULL;
}
int CchOfWord(LPCSTR sz)
{
int cch;
for(cch = 0; !FIsSpace(sz[cch]); ++cch);
return cch;
}
void GetParam(LPCSTR szLine, LPSTR szBuf, int cchBuf)
{
int cch = 0;
BOOL fQuote = FALSE;
while(cch < cchBuf-1 && *szLine && (!FIsSpace(*szLine) || fQuote)) {
if(*szLine == '"') {
if(fQuote && szLine[1] == '"') {
/* Double quote inside a string gets copied as a single
* quote */
szBuf[cch++] = '"';
szLine += 2;
} else {
fQuote = !fQuote;
++szLine;
}
} else
szBuf[cch++] = *szLine++;
}
szBuf[cch] = 0;
}
BOOL FGetSzParam(LPCSTR szLine, LPCSTR szKey, LPSTR szBuf, int cchBuf)
{
LPCSTR pch = PchGetParam(szLine, szKey, TRUE);
if(!pch)
return FALSE;
GetParam(pch, szBuf, cchBuf);
return TRUE;
}
BOOL FGetDwParam(LPCSTR szLine, LPCSTR szKey, DWORD *pdw)
{
int cch;
char sz[32];
LPCSTR pch = PchGetParam(szLine, szKey, TRUE);
if(!pch)
return FALSE;
GetParam(pch, sz, sizeof sz);
*pdw = DwFromSz(sz, &cch);
return FIsSpace(sz[cch]);
}
BOOL FGetQwordParam(LPCSTR szLine, LPCSTR szKey, ULARGE_INTEGER *plu)
{
int cch;
char sz[32];
LPCSTR pch;
pch = PchGetParam(szLine, szKey, TRUE);
if(!pch)
return FALSE;
GetParam(pch, sz, sizeof sz - 1);
sz[sizeof sz - 1] = 0;
/* Verify the 0q prefix */
if(sz[0] != '0' || sz[1] != 'q')
return FALSE;
/* Make sure we have a bunch of hex characters */
for(cch = 2; cch < sizeof sz && !FIsSpace(sz[cch]); ++cch) {
if(!(sz[cch] >= '0' && sz[cch] <= '9' ||
sz[cch] >= 'A' && sz[cch] <= 'F' ||
sz[cch] >= 'a' && sz[cch] <= 'f'))
return FALSE;
}
cch -= 2;
if(cch <= 0)
return FALSE;
/* Move the text out to the end of the string and fill the preceding
* characters with zeroes */
memmove(&sz[sizeof sz - 1 - cch], &sz[2], cch);
memset(sz, '0', sizeof sz - 1 - cch);
/* Now parse out the two dwords */
plu->LowPart = DwHexFromSz(&sz[sizeof sz - 9], NULL);
sz[sizeof sz - 9] = 0;
plu->HighPart = DwHexFromSz(&sz[sizeof sz - 17], NULL);
return TRUE;
}
BOOL FGetNamedDwParam(LPCSTR szLine, LPCSTR szKey, DWORD *pdw, LPSTR szResp)
{
if(!FGetDwParam(szLine, szKey, pdw)) {
sprintf(szResp, "missing %s", szKey);
return FALSE;
}
return TRUE;
}
ULONG UlAddrFromSz(LPCSTR sz)
{
ULONG ul;
int ib;
int ich;
for(ib = 0; ib < 4; ++ib) {
BYTE b = 0;
for(ich = 0; ich < 3; ++ich) {
if(sz[ich] < '0' || sz[ich] > '9')
break;
b = 10 * b + (sz[ich] - '0');
}
if(ich == 0 || sz[ich] != (ib == 3 ? 0 : '.'))
return 0;
sz += ich + 1;
((BYTE *)&ul)[ib ^ 3] = b;
}
return ul;
}
LPCSTR SzStdResponse(HRESULT hr)
{
LPCSTR pszResp;
switch(hr) {
case XBDM_NOSUCHFILE:
pszResp = "file not found";
break;
case XBDM_NOMODULE:
pszResp = "no such module";
break;
case XBDM_MEMUNMAPPED:
pszResp = "memory not mapped";
break;
case XBDM_NOTHREAD:
pszResp = "no such thread";
break;
case XBDM_INVALIDCMD:
pszResp = "unknown command";
break;
case XBDM_NOTSTOPPED:
pszResp = "not stopped";
break;
case XBDM_MUSTCOPY:
pszResp = "file must be copied";
break;
case XBDM_ALREADYEXISTS:
pszResp = "file already exists";
break;
case XBDM_DIRNOTEMPTY:
pszResp = "directory not empty";
break;
case XBDM_BADFILENAME:
pszResp = "filename is invalid";
break;
case XBDM_CANNOTCREATE:
pszResp = "file cannot be created";
break;
case XBDM_DEVICEFULL:
pszResp = "no room on device";
break;
case XBDM_MULTIRESPONSE:
pszResp = "multiline response follows";
break;
case XBDM_BINRESPONSE:
pszResp = "binary response follows";
break;
case XBDM_READYFORBIN:
pszResp = "send binary data";
break;
case XBDM_CANNOTACCESS:
pszResp = "access denied";
break;
case XBDM_NOTDEBUGGABLE:
pszResp = "not debuggable";
break;
case XBDM_BADCOUNTTYPE:
pszResp = "type invalid";
break;
case XBDM_COUNTUNAVAILABLE:
pszResp = "data not available";
break;
case XBDM_NOTLOCKED:
pszResp = "box is not locked";
break;
case XBDM_KEYXCHG:
pszResp = "key exchange required";
break;
case XBDM_MUSTBEDEDICATED:
pszResp = "dedicated connection required";
break;
case E_OUTOFMEMORY:
pszResp = "out of memory";
break;
case E_UNEXPECTED:
pszResp = "unexpected error";
break;
case E_INVALIDARG:
pszResp = "bad parameter";
break;
case XBDM_NOERR:
pszResp = "OK";
break;
default:
pszResp = "";
break;
}
return pszResp;
}
HRESULT HrFromStatus(NTSTATUS st, HRESULT hrDefault)
{
switch(st) {
case STATUS_DIRECTORY_NOT_EMPTY:
return XBDM_DIRNOTEMPTY;
case STATUS_OBJECT_NAME_COLLISION:
return XBDM_ALREADYEXISTS;
case STATUS_OBJECT_PATH_NOT_FOUND:
case STATUS_OBJECT_NAME_NOT_FOUND:
return XBDM_NOSUCHFILE;
case STATUS_OBJECT_PATH_INVALID:
case STATUS_OBJECT_NAME_INVALID:
return XBDM_BADFILENAME;
case STATUS_ACCESS_DENIED:
return XBDM_CANNOTACCESS;
case STATUS_DISK_FULL:
return XBDM_DEVICEFULL;
case STATUS_INSUFFICIENT_RESOURCES:
return E_OUTOFMEMORY;
case STATUS_INVALID_HANDLE:
return E_INVALIDARG;
}
return hrDefault;
}
ULONG DmplOfConnection(PDM_CMDCONT pdmcc)
{
if(!pdmcc)
return -1;
return rgcst[(PCST)pdmcc - rgcst].dmplCur;
}
BOOL FDedicateConnection(int icst, PECH pech)
{
PETHREAD pthrExpect;
HANDLE hthrNew;
PVOID pfnCreateThread;
pthrExpect = rgcst[icst].pthrDedicated;
/* We should only be rededicating a connection running on the current
* thread */
if(PsGetCurrentThread() != pthrExpect) {
DbgPrint("dm: rededicating a connection on the wrong thread!\n");
return FALSE;
}
/* Create the new thread */
if(pech) {
pfnCreateThread = pech->pfnCreateThread;
if(!pfnCreateThread)
pfnCreateThread = CreateThread;
hthrNew = (*(HANDLE (*)())pfnCreateThread)(NULL, 0,
DedicatedServThread, (LPVOID)icst, CREATE_SUSPENDED, NULL);
if(!hthrNew)
return FALSE;
} else
pfnCreateThread = NULL;
/* Send the success message */
PrintSockLine(rgcst[icst].s, "205- connection dedicated");
/* Mark the connection state for the new thread. We mark the thread ID
* as NULL; dedicated threads will fill it in when they start */
rgcst[icst].pthrDedicated = NULL;
rgcst[icst].pechDedicated = pech;
/* Wake up the new thread */
if(pfnCreateThread) {
NtResumeThread(hthrNew, NULL);
CloseHandle(hthrNew);
} else {
rgcst[icst].pthrDedicated = pthrServ;
KeAlertThread(&pthrServ->Tcb, KernelMode);
}
return TRUE;
}
void SendHrSzResp(SOCKET s, HRESULT hr, LPCSTR szResp, LPSTR szBuf)
{
/* Make sure we have an error code we like */
if(((hr >> 16) & 0x7fff) != FACILITY_XBDM) {
hr = SUCCEEDED(hr) ? XBDM_NOERR : XBDM_UNDEFINED;
if(!szResp)
szResp = SzStdResponse(E_UNEXPECTED);
} else if((hr & 0xffff) > 0xff)
hr = XBDM_UNDEFINED;
if(FAILED(hr))
szBuf[0] = '4';
else
szBuf[0] = '2';
szBuf[1] = (char) ('0' + (hr & 0xffff) / 10); // overflow?
szBuf[2] = (char) ('0' + (hr & 0xffff) % 10);
szBuf[3] = '-';
szBuf[4] = ' ';
if(szResp != szBuf) {
if(szResp)
strcpy(szBuf + 5, szResp);
else
szBuf[5] = 0;
}
PrintSockLine(s, szBuf);
}
HRESULT HrHandleBuiltinCommand(DWORD dmplCur, LPCSTR sz, int cchCmd,
LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
HRESULT hr;
int ichhMin, ichhMac, ichhMid;
int sgn;
BOOL fPermitted;
ichhMin = 0;
ichhMac = cchh;
while(ichhMin < ichhMac) {
ichhMid = (ichhMin + ichhMac) / 2;
sgn = SgnCompareRgch(rgchh[ichhMid].szCommand, sz, cchCmd);
if(sgn == 0)
break;
else if(sgn < 0)
ichhMin = ichhMid + 1;
else
ichhMac = ichhMid;
}
if(ichhMin < ichhMac) {
/* Check for appropriate permission */
if(rgchh[ichhMid].dmplCombined)
fPermitted = (dmplCur & rgchh[ichhMid].dmplCombined) ==
rgchh[ichhMid].dmplCombined;
else if(rgchh[ichhMid].dmplMask)
fPermitted = dmplCur & rgchh[ichhMid].dmplMask;
else
fPermitted = TRUE;
if(fPermitted) {
/* Make sure we're running on the right thread */
if(!pthrServ || PsGetCurrentThread() == pthrServ ||
(rgchh[ichhMid].dwFlags & CHH_ANYTHREAD))
{
hr = (*rgchh[ichhMid].pfnHandler)(sz, szResp, cchResp, pdmcc);
} else
hr = XBDM_MUSTBEDEDICATED;
} else
hr = XBDM_CANNOTACCESS;
} else
hr = XBDM_INVALIDCMD;
return hr;
}
void DoSendReceive(int icst, BOOL fSend)
{
HRESULT hr;
SOCKET s = rgcst[icst].s;
char szBuf[256];
char szResp[256];
if(!rgcst[icst].dmcc.Buffer) {
*szBuf = 0;
rgcst[icst].dmcc.Buffer = szBuf;
rgcst[icst].dmcc.BufferSize = sizeof szBuf;
}
szResp[0] = 0;
if(!fSend) {
int cb;
/* We need to receive data. First, we attempt to take it from our
* command receive buffer */
cb = rgcst[icst].dwStatus & CONN_RECEIVED;
if(cb) {
int ib;
int ibT;
ib = cb;
if((DWORD)cb > rgcst[icst].dmcc.BytesRemaining)
ib = rgcst[icst].dmcc.BytesRemaining;
if((DWORD)cb > rgcst[icst].dmcc.BufferSize)
ib = rgcst[icst].dmcc.BufferSize;
if(ib) {
rgcst[icst].dmcc.DataSize = ib;
memcpy(rgcst[icst].dmcc.Buffer, rgcst[icst].szBuf, ib);
for(ibT = 0; ib < cb; )
rgcst[icst].szBuf[ibT++] = rgcst[icst].szBuf[ib++];
rgcst[icst].dwStatus = (rgcst[icst].dwStatus &
~CONN_RECEIVED) | ibT;
} else
rgcst[icst].dmcc.DataSize = 0;
} else {
/* Receive as much data as we can */
cb = rgcst[icst].dmcc.BufferSize;
if((DWORD)cb > rgcst[icst].dmcc.BytesRemaining)
cb = rgcst[icst].dmcc.BytesRemaining;
if(cb) {
cb = recv(s, rgcst[icst].dmcc.Buffer, cb, 0);
if(cb <= 0) {
/* Socket is gone, so clean up and flag an error */
cb = 0;
CloseConn(icst);
}
}
rgcst[icst].dmcc.DataSize = cb;
}
}
hr = rgcst[icst].dmcc.HandlingFunction(&rgcst[icst].dmcc, szResp,
sizeof szResp);
if(fSend) {
if(SUCCEEDED(hr)) {
if(rgcst[icst].dmcc.DataSize == -1) {
/* We have a string to send, so send it */
PrintSockLine(s, rgcst[icst].dmcc.Buffer);
} else if(rgcst[icst].dmcc.DataSize) {
/* We have data to send, so send it */
int cbSend = rgcst[icst].dmcc.DataSize;
while(cbSend) {
int cbSent = send(s, (PBYTE)rgcst[icst].dmcc.Buffer +
rgcst[icst].dmcc.DataSize - cbSend, cbSend, 0);
if(cbSent <= 0) {
/* A send error occurred, so close the connection and
* tell the handling function we can accept no more
* data */
rgcst[icst].dmcc.BytesRemaining = 0;
rgcst[icst].dmcc.HandlingFunction(&rgcst[icst].dmcc,
szResp, sizeof szResp);
CloseConn(icst);
break;
} else
cbSend -= cbSent;
}
}
} else if(hr == XBDM_ENDOFLIST) {
/* We're out of data, so send end-of-data if appropriate and go
* back to text mode */
if(rgcst[icst].dwStatus & CONN_MULTILINE)
PrintSockLine(s, ".");
InitDmcc(icst);
} else {
/* An unexpected error has occurred. Forcibly terminate the
* connection */
CloseConn(icst);
}
} else if(rgcst[icst].dmcc.BytesRemaining == 0) {
/* If we've reached the end of our data stream, we can give the
* appropriate response */
LPCSTR pszResp;
/* We may have an extended response, in which case we need to
* prepare for it */
rgcst[icst].dwStatus &= ~CONN_BINARY;
rgcst[icst].dmcc.BytesRemaining = -1;
switch(hr) {
case XBDM_MULTIRESPONSE:
rgcst[icst].dwStatus |= CONN_MULTILINE;
rgcst[icst].dmcc.DataSize = -1;
break;
case XBDM_BINRESPONSE:
rgcst[icst].dwStatus |= CONN_BINSEND;
break;
default:
InitDmcc(icst);
break;
}
if(rgcst[icst].dwStatus & (CONN_MULTILINE | CONN_BINSEND | CONN_BINARY) &&
!rgcst[icst].dmcc.HandlingFunction)
{
/* We gotta fail this */
hr = E_UNEXPECTED;
rgcst[icst].dwStatus &= ~(CONN_MULTILINE | CONN_BINSEND | CONN_BINARY);
szResp[0] = 0;
}
pszResp = szResp[0] ? szResp : SzStdResponse(hr);
SendHrSzResp(s, hr, pszResp, szBuf);
}
if(rgcst[icst].dmcc.Buffer == szBuf)
rgcst[icst].dmcc.Buffer = NULL;
}
PECH PechFindHandler(LPCSTR sz, int cch)
{
int iech;
/* Look up the command processor */
for(iech = 0; iech < MAX_ECH; ++iech) {
if(rgpech[iech] && FEqualRgch(rgpech[iech]->szExtName, sz, cch))
return rgpech[iech];
}
return NULL;
}
void HandleCommand(int icst, LPCSTR sz)
{
int cchCmd = CchOfWord(sz);
HRESULT hr = 0;
int ich;
char szResp[512];
LPCSTR pszResp;
PDM_CMDCONT pdmcc;
DWORD dmplCur;
PECH pech = NULL;
/* Make sure we're prepared to call this thing */
if(icst >= 0) {
pdmcc = &rgcst[icst].dmcc;
InitDmcc(icst);
dmplCur = rgcst[icst].dmplCur;
} else {
pdmcc = NULL;
dmplCur = -1;
}
szResp[5] = 0;
/* If this command belongs to an external command processor, we send it
* there */
for(ich = 0; ich < cchCmd; ++ich) {
if(sz[ich] == '!') {
BOOL fMatchingConnection;
/* Look up the command processor */
pech = PechFindHandler(sz, ich);
if(pech) {
/* All external commands require control permission */
if(dmplCur & DMPL_PRIV_CONTROL) {
/* Make sure this handler was appropriately dedicated, if
* necessary */
if(pech == rgcst[icst].pechDedicated)
fMatchingConnection = TRUE;
else if(!pech->pfnCreateThread && !rgcst[icst].pechDedicated)
fMatchingConnection = TRUE;
else
fMatchingConnection = FALSE;
if(fMatchingConnection) {
hr = pech->pfnHandler(sz, szResp + 5,
sizeof szResp - 5, pdmcc);
if(!hr)
hr = XBDM_NOERR;
} else
hr = XBDM_MUSTBEDEDICATED;
} else
hr = XBDM_CANNOTACCESS;
} else {
/* No command handler, can't do anything */
hr = XBDM_INVALIDCMD;
*szResp = 0;
}
break;
} else if(FIsSpace(sz[ich]))
break;
}
if(!hr) {
/* Nobody has handled it yet, so we use our own internal handler.
* Internal commands must be run on the server thread to avoid
* possible deadlock if xapi is stopped */
hr = HrHandleBuiltinCommand(dmplCur, sz, cchCmd, szResp + 5,
sizeof szResp - 5, pdmcc);
}
if(hr && icst >= 0) {
/* We have some sort of response to process. First convert to a known
* error code */
if(szResp[5])
pszResp = szResp;
else
pszResp = SzStdResponse(hr);
/* We may have to indicate a special mode */
switch(hr) {
case XBDM_MULTIRESPONSE:
rgcst[icst].dwStatus |= CONN_MULTILINE;
rgcst[icst].dmcc.DataSize = -1;
break;
case XBDM_BINRESPONSE:
rgcst[icst].dwStatus |= CONN_BINSEND;
break;
case XBDM_READYFORBIN:
rgcst[icst].dwStatus |= CONN_BINARY;
break;
}
if(rgcst[icst].dwStatus & (CONN_MULTILINE | CONN_BINSEND | CONN_BINARY) &&
!rgcst[icst].dmcc.HandlingFunction)
{
/* We gotta fail this */
hr = E_UNEXPECTED;
rgcst[icst].dwStatus &= ~(CONN_MULTILINE | CONN_BINSEND | CONN_BINARY);
}
if(hr == XBDM_DEDICATED) {
/* We need to spin off a thread to handle this connection */
if(FDedicateConnection(icst, pech))
/* The new thread will send the dedicated message */
hr = 0;
else
hr = E_FAIL;
}
}
if(hr && icst >= 0) {
/* Now send our response */
SendHrSzResp(rgcst[icst].s, hr, pszResp, szResp);
if(hr == XBDM_READYFORBIN && pdmcc->BytesRemaining == 0)
/* Zero-length receive, so fake up the receive now to end it */
DoSendReceive(icst, FALSE);
}
}
void AnswerName(SOCKET s)
{
struct {
BYTE bRequest;
BYTE cchName;
char szName[256];
} nm;
struct sockaddr_in sin;
int cbAddr = sizeof sin;
int cbPkt;
cbPkt = recvfrom(s, (PVOID)&nm, sizeof nm, 0, (struct sockaddr *)&sin,
&cbAddr);
switch(nm.bRequest) {
case 1:
case 3:
/* Name request. 1 is to match the name, 3 is to match anything */
if(nm.bRequest == 1) {
nm.bRequest = 0;
/* Need to ensure more than two bytes received before we can
* look at nm.cchName */
if(cbPkt < 2 || cbPkt != 2 + nm.cchName)
/* Ill-formed packet, do nothing */
break;
nm.szName[nm.cchName] = 0;
/* Is it us? */
if(!nm.cchName || !FEqualRgch(nm.szName, rgchDbgName, nm.cchName)
|| rgchDbgName[nm.cchName] != 0)
/* Not for us */
break;
}
/* Answer the request */
nm.bRequest = 2;
for(nm.cchName = 0; rgchDbgName[nm.cchName]; ++nm.cchName)
nm.szName[nm.cchName] = rgchDbgName[nm.cchName];
break;
default:
/* We won't answer a request we don't recognize */
nm.bRequest = 0;
break;
}
if(nm.bRequest)
sendto(s, (PVOID)&nm, nm.cchName + 2, 0, (struct sockaddr *)&sin,
cbAddr);
}
void HandleSocketRead(int icst)
{
if(rgcst[icst].dwStatus & CONN_BINARY)
DoSendReceive(icst, FALSE);
else {
/* Text data coming in; read it */
int cbRcv;
int cbBuf;
char *pchBuf;
char rgbBufT[64];
int ich;
if(rgcst[icst].dwStatus & CONN_OVERFLOW) {
/* Overflowed the buffer, ignore the data */
pchBuf = rgbBufT;
cbBuf = MAX_CONN_BUF - sizeof rgbBufT;
} else {
cbBuf = rgcst[icst].dwStatus & CONN_RECEIVED;
pchBuf = &rgcst[icst].szBuf[cbBuf];
}
cbRcv = recv(rgcst[icst].s, pchBuf, MAX_CONN_BUF -
cbBuf, 0);
if(cbRcv <= 0) {
/* Connection is bad, give up */
CloseConn(icst);
return;
}
if(!(rgcst[icst].dwStatus & CONN_OVERFLOW)) {
rgcst[icst].dwStatus += cbRcv;
if((rgcst[icst].dwStatus & CONN_RECEIVED) ==
MAX_CONN_BUF)
rgcst[icst].dwStatus |= CONN_OVERFLOW;
}
if(pchBuf != rgcst[icst].szBuf) {
/* We're receiving additional data, so we may need to look
* back one for the CR */
--pchBuf;
++cbRcv;
}
/* Let's see if we got our end-of-line, which must be of the
* form CR-LF or CR-0 */
for(ich = 0; ich < cbRcv; ++ich)
if(pchBuf[ich] == '\015')
break;
if(ich < cbRcv - 1) {
/* Got EOL, so let's do our requisite processing */
if(rgcst[icst].dwStatus & CONN_OVERFLOW) {
PrintSockLine(rgcst[icst].s, "406- line too long");
rgcst[icst].dwStatus &= ~(CONN_OVERFLOW | CONN_RECEIVED);
} else {
pchBuf[ich++] = 0;
HandleCommand(icst, rgcst[icst].szBuf);
if(rgcst[icst].s != INVALID_SOCKET) {
int ichNew = 0;
while(++ich < cbRcv)
rgcst[icst].szBuf[ichNew++] = pchBuf[ich];
rgcst[icst].dwStatus &= ~CONN_RECEIVED;
rgcst[icst].dwStatus |= ichNew;
}
}
}
}
}
DWORD DedicatedServThread(LPVOID lpv)
{
int icst = (int)lpv;
DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
rgcst[icst].pthrDedicated = PsGetCurrentThread();
while(rgcst[icst].s != INVALID_SOCKET && rgcst[icst].pthrDedicated ==
PsGetCurrentThread())
{
fd_set fds;
BOOL fSend;
FD_ZERO(&fds);
FD_SET(rgcst[icst].s, &fds);
fSend = rgcst[icst].dwStatus & (CONN_BINSEND | CONN_MULTILINE);
while(select(0, fSend ? NULL : &fds, fSend ? &fds : NULL, NULL, 0) == 0);
if(!FD_ISSET(rgcst[icst].s, &fds))
/* How did this happen? */
CloseConn(icst);
else if(fSend)
DoSendReceive(icst, TRUE);
else
HandleSocketRead(icst);
}
return 0;
}
DWORD ServerThread(LPVOID pv)
{
SOCKET sockListen;
SOCKET sockName;
int icst;
struct sockaddr_in sin;
fd_set fds;
fd_set fdsSend;
NTSTATUS st;
char sz[128];
PULARGE_INTEGER plu;
//Boost the base priority of this thread so that the dm is response, especially
//important xbcp.
KeSetBasePriorityThread(KeGetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
pthrServ = PsGetCurrentThread();
for(icst = 0; icst < MAX_CONNECTIONS; ++icst)
rgcst[icst].s = INVALID_SOCKET;
sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockListen == INVALID_SOCKET)
return 0;
st = TRUE;
if(0 != setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, (PVOID)&st,
sizeof st))
{
_asm int 3
closesocket(sockListen);
return 0;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(DEBUGGER_PORT);
if(0 != bind(sockListen, (struct sockaddr *)&sin, sizeof sin)) {
_asm int 3
closesocket(sockListen);
return 0;
}
if(0 != listen(sockListen, MAX_CONNECTIONS)) {
_asm int 3
closesocket(sockListen);
return 0;
}
/* Set up our name answering socket */
sockName = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockName != INVALID_SOCKET) {
BOOL fReuse = TRUE;
if(0 != setsockopt(sockName, SOL_SOCKET, SO_REUSEADDR, (PVOID)&fReuse,
sizeof fReuse))
{
NameErr:
closesocket(sockName);
sockName = INVALID_SOCKET;
} else {
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(DEBUGGER_PORT);
if(0 != bind(sockName, (struct sockaddr *)&sin, sizeof sin))
goto NameErr;
}
}
// DbgPrint("dm: listening\n");
KeSetEvent(&kevtServ, EVENT_INCREMENT, FALSE);
/* Now we wait for connections and answer what's there */
for(;;) {
do {
FD_ZERO(&fds);
FD_ZERO(&fdsSend);
FD_SET(sockListen, &fds);
if(sockName != INVALID_SOCKET)
FD_SET(sockName, &fds);
for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
if(rgcst[icst].s != INVALID_SOCKET &&
rgcst[icst].pthrDedicated == PsGetCurrentThread())
{
FD_SET(rgcst[icst].s, rgcst[icst].dwStatus &
(CONN_BINSEND | CONN_MULTILINE) ? &fdsSend : &fds);
}
}
} while(select(0, &fds, &fdsSend, NULL, NULL) <= 0);
/* Accept new connections */
if(FD_ISSET(sockListen, &fds)) {
SOCKET sockNew;
int cb = sizeof sin;
sockNew = accept(sockListen, (struct sockaddr *)&sin, &cb);
if(sockNew == INVALID_SOCKET)
continue;
EnterCriticalSection(&csAccept);
if(g_fServShutdown)
icst = MAX_CONNECTIONS;
else {
for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
if(rgcst[icst].s == INVALID_SOCKET)
break;
}
}
if(icst < MAX_CONNECTIONS) {
rgcst[icst].s = sockNew;
rgcst[icst].pthrDedicated = pthrServ;
}
LeaveCriticalSection(&csAccept);
if(icst == MAX_CONNECTIONS) {
PrintSockLine(sockNew, "401- max number of connections exceeded");
fAllowKd = TRUE; // we're going to need to debug if this happens
closesocket(sockNew);
} else {
if(g_fLockLevel) {
rgcst[icst].dmplCur = 0;
plu = &rgcst[icst].luConnectNonce;
KeQuerySystemTime((PLARGE_INTEGER)plu);
_asm {
mov ecx, plu
rdtsc
xor [ecx], eax
xor [ecx+4], edx
}
sprintf(sz, "201- boxid=0q%08x%08x nonce=0q%08x%08x",
g_luBoxId.HighPart, g_luBoxId.LowPart, plu->HighPart,
plu->LowPart);
PrintSockLine(sockNew, sz);
} else {
rgcst[icst].dmplCur = -1;
PrintSockLine(sockNew, "201- connected");
}
rgcst[icst].dwStatus = 0;
InitDmcc(icst);
}
} else if(FD_ISSET(sockName, &fds)) {
/* Answer name requests */
AnswerName(sockName);
} else for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
if(rgcst[icst].s == INVALID_SOCKET)
continue;
if(FD_ISSET(rgcst[icst].s, &fdsSend)) {
DoSendReceive(icst, TRUE);
continue;
}
if(FD_ISSET(rgcst[icst].s, &fds))
HandleSocketRead(icst);
}
}
}
BOOL FStartDebugService(void)
{
NTSTATUS st;
HANDLE hthr;
st = PsCreateSystemThread(&hthr, NULL, ServerThread, NULL, TRUE);
return NT_SUCCESS(st);
}
HRESULT DmSetXboxName(LPCSTR szName)
{
if(szName) {
strncpy(rgchDbgName, szName, sizeof rgchDbgName);
rgchDbgName[sizeof rgchDbgName - 1] = 0;
} else
rgchDbgName[0] = 0;
WriteIniFile();
return XBDM_NOERR;
}
HRESULT DmGetXboxName(LPSTR szName, LPDWORD pcch)
{
DWORD ich;
if (!szName || !pcch)
return E_INVALIDARG;
for(ich = 0; rgchDbgName[ich] && ich < *pcch - 1; ++ich)
*szName++ = rgchDbgName[ich];
*szName = 0;
return rgchDbgName[ich] ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
}
HRESULT DmRegisterCommandProcessor(LPCSTR sz, PDM_CMDPROC pfn)
{
return DmRegisterCommandProcessorEx(sz, pfn, NULL);
}
HRESULT DmRegisterCommandProcessorEx(LPCSTR sz, PDM_CMDPROC pfn,
PVOID pfnCreateThread)
{
int ich;
int iech;
ECH *pech;
HRESULT hr;
if (!sz)
return E_INVALIDARG;
RtlEnterCriticalSection(&csEch);
if(!pfn) {
/* We're going to unregister, so find the matching handler and pull
* it */
for(ich = 0; sz[ich]; ++ich);
for(iech = 0; iech < MAX_ECH; ++iech) {
if(rgpech[iech] && FEqualRgch(rgpech[iech]->szExtName, sz, ich)) {
DmFreePool(rgpech[iech]);
rgpech[iech] = NULL;
}
}
hr = XBDM_NOERR;
} else {
/* Let's find somewhere to put it */
for(iech = 0; iech < MAX_ECH; ++iech)
if(!rgpech[iech])
break;
if(iech < MAX_ECH) {
pech = DmAllocatePoolWithTag(sizeof *pech, 'hcmd');
} else
pech = NULL;
if(pech) {
for(ich = 0; sz[ich] && ich < sizeof pech->szExtName - 1; ++ich)
pech->szExtName[ich] = sz[ich];
if(sz[ich]) {
DmFreePool(pech);
hr = E_INVALIDARG;
} else {
pech->szExtName[ich] = 0;
pech->pfnHandler = pfn;
pech->pfnCreateThread = pfnCreateThread;
rgpech[iech] = pech;
hr = XBDM_NOERR;
}
} else
hr = E_OUTOFMEMORY;
}
RtlLeaveCriticalSection(&csEch);
return hr;
}
HRESULT HrDedicateConnection(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szHandler[256];
int icst;
PECH pech;
if(!pdmcc)
return E_INVALIDARG;
if(PchGetParam(sz, "global", FALSE))
pech = NULL;
else if(!FGetSzParam(sz, "handler", szHandler, sizeof szHandler)) {
strcpy(szResp, "missing handler");
return E_FAIL;
} else {
pech = PechFindHandler(szHandler, strlen(szHandler));
if(!pech)
return XBDM_NOSUCHFILE;
}
icst = (PCST)pdmcc - rgcst;
/* Rededicate the connection. If this is successful, the success
* message will be sent in the new thread */
return FDedicateConnection(icst, pech) ? 0 : E_FAIL;
}
HRESULT DmThreadUserData(DWORD tid, PDWORD *ppdwRet)
{
PETHREAD pthr;
NTSTATUS st;
HRESULT hr = XBDM_NOTHREAD;
if (!ppdwRet)
return E_INVALIDARG;
if(tid == DM_CURRENT_THREAD) {
pthr = PsGetCurrentThread();
ObfReferenceObject(pthr);
st = STATUS_SUCCESS;
} else
st = PsLookupThreadByThreadId((HANDLE)tid, &pthr);
if(NT_SUCCESS(st)) {
DMTD *pdmtd = DmGetCurrentDmtd();
if(pdmtd) {
*ppdwRet = &pdmtd->UserData;
hr = XBDM_NOERR;
}
ObDereferenceObject(pthr);
}
return hr;
}
HRESULT HrEndConversation(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
int icst;
if(!pdmcc)
return E_INVALIDARG;
icst = (PCST)pdmcc - rgcst;
PrintSockLine(rgcst[icst].s, "200- bye");
CloseConn(icst);
return 0;
}
HRESULT HrSetDbgName(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szNewName[sizeof rgchDbgName];
HRESULT hr;
KIRQL irqlSav;
if(!FGetSzParam(sz, "name", szNewName, sizeof szNewName)) {
/* We're querying for the current name */
strcpy(szResp, rgchDbgName);
hr = XBDM_NOERR;
} else if(!FConnectionPermission(pdmcc, DMPL_PRIV_CONFIGURE))
hr = XBDM_CANNOTACCESS;
else {
irqlSav = KeRaiseIrqlToDpcLevel();
memcpy(rgchDbgName, szNewName, sizeof rgchDbgName);
hr = XBDM_NOERR;
KeLowerIrql(irqlSav);
WriteIniFile();
}
return hr;
}
HRESULT HrToggleGPUCounters(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
HRESULT hr;
if(PchGetParam(sz, "enable", FALSE))
hr = DmEnableGPUCounter(TRUE);
else if(PchGetParam(sz, "disable", FALSE))
hr = DmEnableGPUCounter(FALSE);
return hr;
}
HRESULT HrToggleKDState(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
HRESULT hr = XBDM_NOERR;
if(PchGetParam(sz, "enable", FALSE)) {
fAllowKd = TRUE;
strcpy(szResp, "kd enabled");
} else if(PchGetParam(sz, "disable", FALSE)) {
fAllowKd = FALSE;
strcpy(szResp, "kd disabled");
} else if(PchGetParam(sz, "except", FALSE)) {
fAllowKd = TRUE;
ExceptionsToKd();
} else if(PchGetParam(sz, "exceptif", FALSE)) {
/* Forward exceptions to KD only if KD is connected */
if(*KdDebuggerEnabled) {
fAllowKd = TRUE;
ExceptionsToKd();
}
} else {
strcpy(szResp, "improper command");
hr = E_FAIL;
}
return hr;
}
HRESULT HrSetupNotifyAt(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
HRESULT hr;
int icst;
if(!pdmcc)
return E_FAIL;
icst = (PCST)pdmcc - rgcst;
hr = FNotifyAtCmd(rgcst[icst].s, sz, FALSE) ? XBDM_NOERR :
E_FAIL;
/* Keep the ini file up-to-date */
if(SUCCEEDED(hr))
WriteIniFile();
return hr;
}
HRESULT HrSetupNotify(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
int icst;
HRESULT hr;
if(!pdmcc)
return E_FAIL;
icst = (PCST)pdmcc - rgcst;
if(FConvertToNotifySock(rgcst[icst].s)) {
/* This socket is no longer our responsibility */
rgcst[icst].s = INVALID_SOCKET;
hr = 0;
} else {
strcpy(szResp, "notification limit exceeded");
hr = E_FAIL;
}
return hr;
}
HRESULT HrDoStopon(BOOL fStop, LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DWORD dw = 0;
if(PchGetParam(sz, "all", FALSE)) {
dw = -1;
strcpy(szResp, fStop ? "stop on all" : "stop on none");
} else {
if(PchGetParam(sz, "fce", FALSE))
dw |= DMSTOP_FCE;
if(PchGetParam(sz, "debugstr", FALSE))
dw |= DMSTOP_DEBUGSTR;
if(PchGetParam(sz, "createthread", FALSE))
dw |= DMSTOP_CREATETHREAD;
}
return DmStopOn(dw, fStop);
}
HRESULT HrStopon(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
return HrDoStopon(TRUE, sz, szResp, cchResp, pdmcc);
}
HRESULT HrNostopon(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
return HrDoStopon(FALSE, sz, szResp, cchResp, pdmcc);
}
HRESULT HrGo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
if(dwExecState == DMN_EXEC_START) {
sprintf(szResp, "not stopped");
return E_FAIL;
}
DmGo();
return XBDM_NOERR;
}
HRESULT HrStop(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
if(dwExecState != DMN_EXEC_START) {
sprintf(szResp, "already stopped");
return E_FAIL;
}
DmStop();
return XBDM_NOERR;
}
BOOL FTitleExists(LPCSTR szDir, LPCSTR szTitle, LPCSTR szCmdLine, BOOL fMapIt)
{
HANDLE h;
OBJECT_ATTRIBUTES oa;
OBJECT_STRING objectName;
IO_STATUS_BLOCK iosb;
BOOL fRet;
NTSTATUS st;
OCHAR oszPath[MAX_OBJ_PATH+1];
OCHAR *pochTitle;
OCHAR *pochMax;
int ich;
if(!szDir || !szDir[0]) {
/* No dir name, so we expect szTitle to have the complete path name
* to the title */
if(!szTitle || !FFileNameToObName(szTitle, oszPath, sizeof oszPath /
sizeof(OCHAR)))
{
return FALSE;
}
pochTitle = NULL;
} else {
if(!FFileNameToObName(szDir, oszPath, sizeof oszPath / sizeof(OCHAR)))
return FALSE;
pochTitle = oszPath;
}
if(!szTitle)
szTitle = "default.xbe";
if(pochTitle) {
/* Construct a complete path for the title and remember where both
* parts go */
pochMax = oszPath + sizeof oszPath / sizeof(OCHAR);
while(*pochTitle)
++pochTitle;
/* If we're in a root dir, don't add the extra '\' */
if(pochTitle > oszPath && pochTitle[-1] != '\\')
*pochTitle++ = '\\';
for(ich = 0; &pochTitle[ich] < pochMax && szTitle[ich]; ++ich)
pochTitle[ich] = szTitle[ich];
if(&pochTitle[ich] >= pochMax - 1)
return FALSE;
pochTitle[ich] = 0;
}
/* Verify the file exists */
RtlInitObjectString(&objectName, oszPath);
InitializeObjectAttributes(&oa, &objectName, OBJ_CASE_INSENSITIVE, NULL,
NULL);
st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
fRet = NT_SUCCESS(st);
if(fRet) {
NtClose(h);
if(fMapIt) {
if(pochTitle) {
pochTitle[-1] = 0;
pochMax = oszPath;
} else {
pochMax = NULL;
pochTitle = oszPath;
}
/* We require that the command line passed in is the size of the
* launch data, so we don't need to do any copying here */
fRet = NT_SUCCESS(XWriteTitleInfoNoReboot(pochTitle, pochMax,
szCmdLine ? LDT_FROM_DEBUGGER_CMDLINE : LDT_NONE, 0,
(PLAUNCH_DATA) szCmdLine));
}
}
return fRet;
}
HRESULT HrReceiveFileData(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
FILE_DISPOSITION_INFORMATION fdi;
IO_STATUS_BLOCK iosb;
if(pdmcc->DataSize) {
if(pccs->rgbBuf)
/* Digest the data in the buffer */
pccs->ib += pdmcc->DataSize;
else
pccs->ib = DISK_BUF_SIZE;
pdmcc->BytesRemaining -= pdmcc->DataSize;
} else {
/* If there's no data, then either we had a zero-length file, or
* we've lost our socket. In the latter case, we need to delete
* the file */
if(pdmcc->BytesRemaining) {
pdmcc->BytesRemaining = 0;
fdi.DeleteFile = TRUE;
NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
FileDispositionInformation);
}
}
/* See if we're ready for a disk write */
if(!pdmcc->BytesRemaining || pccs->ib == DISK_BUF_SIZE) {
NTSTATUS st;
if(pccs->hFile) {
st = NtWriteFile(pccs->hFile, NULL, NULL, NULL, &iosb,
pccs->rgbBuf, pccs->ib, NULL);
/* Close the file if we're out of data */
if(!NT_SUCCESS(st)) {
/* If an error, try to delete the file and signal an error
* condition */
fdi.DeleteFile = TRUE;
NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
FileDispositionInformation);
NtClose(pccs->hFile);
pccs->hFile = NULL;
pccs->fError = TRUE;
} else if(!pdmcc->BytesRemaining) {
NtClose(pccs->hFile);
pccs->hFile = NULL;
}
}
pccs->ib = 0;
if(!pdmcc->BytesRemaining && pccs->rgbBuf)
/* Clean up our buffer */
DmFreePool(pccs->rgbBuf);
}
/* Make sure the buffer pointer is set up */
if(pccs->rgbBuf) {
pdmcc->Buffer = pccs->rgbBuf + pccs->ib;
pdmcc->BufferSize = DISK_BUF_SIZE - pccs->ib;
}
return pccs->fError ? E_UNEXPECTED : XBDM_NOERR;
}
HRESULT HrReceiveFile(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
LARGE_INTEGER liSize;
NTSTATUS st;
char szName[MAX_OBJ_PATH+1];
CCS *pccs;
IO_STATUS_BLOCK iosb;
FILE_END_OF_FILE_INFORMATION feof;
FILE_DISPOSITION_INFORMATION fdi;
HRESULT hr;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
if(!FGetNamedDwParam(sz, "length", &pdmcc->BytesRemaining, szResp))
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
liSize.HighPart = 0;
liSize.LowPart = pdmcc->BytesRemaining;
st = FCreateFile(&pccs->hFile, GENERIC_WRITE | DELETE | SYNCHRONIZE,
szName, &liSize, 0, 0, FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
/* Set the size of the file now */
feof.EndOfFile = liSize;
st = NtSetInformationFile(pccs->hFile, &iosb, &feof, sizeof(feof),
FileEndOfFileInformation);
if(NT_SUCCESS(st)) {
/* Make sure we'll have a buffer for received data */
pccs->ib = 0;
pccs->rgbBuf = DmAllocatePoolWithTag(DISK_BUF_SIZE, 'bdbd');
/* Even if the alloc fails, we'll still have a small static buffer */
pdmcc->Buffer = pccs->rgbBuf;
pdmcc->BufferSize = DISK_BUF_SIZE;
pccs->fError = FALSE;
pdmcc->HandlingFunction = HrReceiveFileData;
return XBDM_READYFORBIN;
} else {
/* Mark the file for deletion and close the file */
fdi.DeleteFile = TRUE;
NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
FileDispositionInformation);
NtClose(pccs->hFile);
pccs->hFile = NULL;
}
}
return HrFromStatus(st, XBDM_CANNOTCREATE);
}
HRESULT HrSendFileData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
if(pdmcc->BytesRemaining == 0) {
/* End of file */
NtClose(pccs->hFile);
if(pccs->rgbBuf)
DmFreePool(pccs->rgbBuf);
return XBDM_ENDOFLIST;
}
if(!pccs->fDidSize) {
/* First we need to send the length */
pdmcc->BytesRemaining = pccs->ib;
memcpy(pdmcc->Buffer, &pccs->ib, sizeof(DWORD));
pdmcc->DataSize = sizeof(DWORD);
pccs->fDidSize = TRUE;
} else {
NTSTATUS st;
IO_STATUS_BLOCK iosb;
DWORD cb;
cb = pdmcc->BufferSize;
if(cb > pdmcc->BytesRemaining)
cb = pdmcc->BytesRemaining;
st = NtReadFile(pccs->h, NULL, NULL, NULL, &iosb, pdmcc->Buffer, cb,
NULL);
if(!NT_SUCCESS(st))
// BUG BUG -- need to signal read failure
RtlZeroMemory(pdmcc->Buffer, cb);
pdmcc->BytesRemaining -= cb;
pdmcc->DataSize = cb;
}
return XBDM_NOERR;
}
HRESULT HrSendFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS st;
IO_STATUS_BLOCK iosb;
FILE_NETWORK_OPEN_INFORMATION ni;
char szName[MAX_OBJ_PATH+1];
CCS *pccs;
HRESULT hr;
int icst;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
pccs = (CCS *)pdmcc->CustomData;
st = FCreateFile(&pccs->hFile, GENERIC_READ | SYNCHRONIZE, szName,
NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
st = NtQueryInformationFile(pccs->hFile, &iosb, &ni, sizeof ni,
FileNetworkOpenInformation);
if(NT_SUCCESS(st)) {
if(ni.EndOfFile.HighPart) {
/* File is too big to transfer */
sprintf(szResp, "file is too big");
hr = E_FAIL;
} else {
/* Get a buffer for sending if we can */
pdmcc->BufferSize = 1024;
pccs->rgbBuf = pdmcc->Buffer = DmAllocatePoolWithTag(
pdmcc->BufferSize, 'bdbd');
pccs->ib = ni.EndOfFile.LowPart;
pccs->fDidSize = FALSE;
pdmcc->BytesRemaining = 1;
pdmcc->HandlingFunction = HrSendFileData;
return XBDM_BINRESPONSE;
}
} else
hr = E_UNEXPECTED;
NtClose(pccs->h);
} else
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
return hr;
}
void GetFileAttrSz(LPSTR sz, ULONG dwAttributes, PLARGE_INTEGER pliChange,
PLARGE_INTEGER pliCreate, PLARGE_INTEGER pliSize)
{
sprintf(sz, "sizehi=0x%x sizelo=0x%x createhi=0x%08x createlo=0x%08x "
"changehi=0x%08x changelo=0x%08x%s%s%s", pliSize->HighPart,
pliSize->LowPart, pliCreate->HighPart, pliCreate->LowPart,
pliChange->HighPart, pliChange->LowPart,
dwAttributes & FILE_ATTRIBUTE_DIRECTORY ? " directory" : "",
dwAttributes & FILE_ATTRIBUTE_READONLY ? " readonly" : "",
dwAttributes & FILE_ATTRIBUTE_HIDDEN ? " hidden" : "");
}
HRESULT HrReportFileAttributes(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
for(;;) {
switch(pdmcc->BytesRemaining++) {
case 1:
GetFileAttrSz(pdmcc->Buffer, pccs->fba.FileAttributes,
&pccs->fba.LastWriteTime, &pccs->fba.CreationTime,
&pccs->fba.EndOfFile);
return XBDM_NOERR;
default:
return XBDM_ENDOFLIST;
}
}
}
HRESULT HrGetFileAttributes(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
OCHAR oszFilename[MAX_OBJ_PATH+1];
NTSTATUS st;
OBJECT_STRING objFilename;
OBJECT_ATTRIBUTES oa;
char szName[MAX_OBJ_PATH+1];
CCS *pccs;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
/* Turn the filename into an object manager name*/
if(!FFileNameToObName(szName, oszFilename, sizeof oszFilename /
sizeof(OCHAR)))
return XBDM_NOSUCHFILE;
RtlInitObjectString(&objFilename, oszFilename);
InitializeObjectAttributes(&oa, &objFilename, OBJ_CASE_INSENSITIVE, NULL,
NULL);
pccs = (CCS *)pdmcc->CustomData;
pdmcc->HandlingFunction = HrReportFileAttributes;
pdmcc->BytesRemaining = 1;
st = NtQueryFullAttributesFile(&oa, &pccs->fba);
return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
XBDM_CANNOTACCESS);
}
HRESULT HrSetFileAttributes(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
NTSTATUS st;
HANDLE h;
IO_STATUS_BLOCK iosb;
char szName[MAX_OBJ_PATH+1];
HRESULT hr;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
st = FCreateFile(&h, SYNCHRONIZE, szName, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
FILE_NETWORK_OPEN_INFORMATION fna;
st = NtQueryInformationFile(h, &iosb, &fna, sizeof fna,
FileNetworkOpenInformation);
if(NT_SUCCESS(st)) {
FILE_BASIC_INFORMATION fba;
DWORD dwAttrMask = 0;
DWORD dwAttrNew = 0;
DWORD dwT;
RtlZeroMemory(&fba, sizeof fba);
if(FGetDwParam(sz, "readonly", &dwT)) {
dwAttrMask |= FILE_ATTRIBUTE_READONLY;
if(dwT)
dwAttrNew |= FILE_ATTRIBUTE_READONLY;
}
if(FGetDwParam(sz, "hidden", &dwT)) {
dwAttrMask |= FILE_ATTRIBUTE_HIDDEN;
if(dwT)
dwAttrNew |= FILE_ATTRIBUTE_HIDDEN;
}
FGetDwParam(sz, "createhi", &fba.CreationTime.HighPart);
FGetDwParam(sz, "createlo", &fba.CreationTime.LowPart);
FGetDwParam(sz, "changehi", &fba.LastWriteTime.HighPart);
FGetDwParam(sz, "changelo", &fba.LastWriteTime.LowPart);
fba.FileAttributes = fna.FileAttributes ^ ((fna.FileAttributes ^
dwAttrNew) & dwAttrMask);
if(!fba.FileAttributes)
fba.FileAttributes = FILE_ATTRIBUTE_NORMAL;
st = NtSetInformationFile(h, &iosb, &fba, sizeof fba,
FileBasicInformation);
hr = NT_SUCCESS(st) ? XBDM_NOERR : HrFromStatus(st, E_FAIL);
}
NtClose(h);
} else
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
return hr;
}
HRESULT HrRenameFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS st;
HANDLE h;
IO_STATUS_BLOCK iosb;
char szName[MAX_OBJ_PATH+1];
OCHAR oszNewName[MAX_OBJ_PATH+1];
FILE_RENAME_INFORMATION fri;
HRESULT hr;
if(!FGetSzParam(sz, "newname", szName, sizeof szName)) {
strcpy(szResp, "missing new name");
return E_FAIL;
}
if(!FFileNameToObName(szName, oszNewName, MAX_OBJ_PATH+1)) {
strcpy(szResp, "new name does not exist");
return XBDM_NOSUCHFILE;
}
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
/* Get a handle to the original file */
st = FCreateFile(&h, GENERIC_WRITE | SYNCHRONIZE, szName, NULL, 0, 0,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
RtlZeroMemory(&fri, sizeof fri);
RtlInitObjectString(&fri.FileName, oszNewName);
st = NtSetInformationFile(h, &iosb, &fri, sizeof fri,
FileRenameInformation);
NtClose(h);
switch(st) {
case STATUS_NOT_SAME_DEVICE:
hr = XBDM_MUSTCOPY;
break;
default:
if(NT_SUCCESS(st))
hr = XBDM_NOERR;
else
hr = HrFromStatus(st, XBDM_CANNOTCREATE);
break;
}
} else
hr = XBDM_CANNOTACCESS;
return hr;
}
HRESULT HrDeleteFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS st;
HANDLE h;
IO_STATUS_BLOCK iosb;
FILE_DISPOSITION_INFORMATION fdi;
char szName[MAX_OBJ_PATH+1];
HRESULT hr;
BOOL fIsDirectory;
char *pch;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
fIsDirectory = PchGetParam(sz, "dir", FALSE) != NULL;
/* We're going to prohibit the deletion of any drives */
pch = szName;
if(*pch == 'x' || *pch == 'X')
++pch;
if(pch[1] == ':' && pch[2] == '\\' && pch[3] == 0)
/* Looks like a drive name */
return XBDM_CANNOTACCESS;
st = FCreateFile(&h, DELETE | SYNCHRONIZE, szName, NULL, 0, 0,
FILE_OPEN, fIsDirectory ?
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE :
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
if(NT_SUCCESS(st)) {
RtlZeroMemory(&fdi, sizeof fdi);
fdi.DeleteFile = TRUE;
st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
FileDispositionInformation);
if(NT_SUCCESS(st))
hr = XBDM_NOERR;
else
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
NtClose(h);
} else {
switch(st) {
case STATUS_NOT_A_DIRECTORY :
sprintf(szResp, "not a directory");
hr = XBDM_CANNOTACCESS;
break;
case STATUS_FILE_IS_A_DIRECTORY:
sprintf(szResp, "is a directory");
hr = XBDM_CANNOTACCESS;
break;
default:
hr = HrFromStatus(st, XBDM_CANNOTACCESS);
break;
}
}
return hr;
}
HRESULT HrReportDirList(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
NTSTATUS st;
IO_STATUS_BLOCK iosb;
struct
{
FILE_DIRECTORY_INFORMATION fna;
OCHAR ozName[MAX_OBJ_PATH+1];
} fna;
CCS *pccs = (CCS *)pdmcc->CustomData;
HRESULT hr;
LPSTR szResp;
st = NtQueryDirectoryFile(pccs->h, NULL, NULL, NULL, &iosb,
&fna.fna, sizeof fna, FileDirectoryInformation,
NULL, (BOOLEAN)(pccs->i == 0));
if(pdmcc->BytesRemaining && NT_SUCCESS(st))
{
ULONG ich;
++pccs->i;
fna.fna.FileNameLength /= sizeof(OCHAR); // from bytes to cwch
if(fna.fna.FileNameLength >= pdmcc->BufferSize)
fna.fna.FileNameLength = pdmcc->BufferSize - 1;
strcpy(pdmcc->Buffer, "name=\"");
for(szResp = pdmcc->Buffer; *szResp; ++szResp);
for(ich = 0; ich < fna.fna.FileNameLength; ++ich)
szResp[ich] = (char)fna.fna.FileName[ich];
szResp[ich++] = '"';
szResp[ich++] = ' ';
GetFileAttrSz(szResp + ich, fna.fna.FileAttributes,
&fna.fna.LastWriteTime, &fna.fna.CreationTime, &fna.fna.EndOfFile);
hr = XBDM_NOERR;
} else {
NtClose(pccs->h);
DmFreePool(pdmcc->Buffer);
hr = XBDM_ENDOFLIST;
}
return hr;
}
HRESULT HrGetDirList(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS st;
char szName[MAX_OBJ_PATH+1];
CCS *pccs;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
pccs = (CCS *)pdmcc->CustomData;
pccs->i = 0;
pdmcc->BufferSize = 512;
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
if(!pdmcc->Buffer)
/* We can't fall back on the default 256-byte buffer in this case */
return E_OUTOFMEMORY;
pdmcc->HandlingFunction = HrReportDirList;
pdmcc->DataSize = -1;
st = FCreateFile(&pccs->h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
XBDM_CANNOTACCESS);
}
HRESULT HrMkdir(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS st;
HANDLE h;
char szName[MAX_OBJ_PATH+1];
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
st = FCreateFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName,
NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
NtClose(h);
return XBDM_NOERR;
} else if(st == STATUS_OBJECT_NAME_COLLISION)
return XBDM_ALREADYEXISTS;
else
return HrFromStatus(st, XBDM_CANNOTCREATE);
}
HRESULT HrFormatFAT(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
DWORD dwPartition;
OCHAR szPartition[MAX_OBJ_PATH + 1];
OBJECT_STRING ost;
if(dwExecState != DMN_EXEC_PENDING) {
strcpy(szResp, "not stopped");
return E_FAIL;
}
if(!FGetNamedDwParam(sz, "partition", &dwPartition, szResp))
return E_FAIL;
if(dwPartition < 1 || dwPartition > 5)
return XBDM_NOSUCHFILE;
sprintf(szPartition, "\\Device\\Harddisk0\\Partition%d", dwPartition);
RtlInitObjectString(&ost, szPartition);
if(XapiFormatFATVolume(&ost))
return XBDM_NOERR;
strcpy(szResp, "unable to format");
return XBDM_CANNOTACCESS;
}
HRESULT DmSetTitle(LPCSTR szDir, LPCSTR szTitle, LPCSTR pchCmdLine)
{
if (!szTitle)
return E_INVALIDARG;
if(pchCmdLine) {
KIRQL irql = KeRaiseIrqlToDpcLevel();
if(pszCmdLine)
DmFreePool(pszCmdLine);
pszCmdLine = DmAllocatePool(sizeof(LD_FROM_DEBUGGER_CMDLINE));
if(pszCmdLine)
strcpy(pszCmdLine, pchCmdLine);
KeLowerIrql(irql);
if(!pszCmdLine)
return E_OUTOFMEMORY;
}
if(!FTitleExists(szDir, szTitle, NULL, FALSE))
return XBDM_CANNOTACCESS;
if(szDir)
strcpy(rgchTitleDir, szDir);
else
rgchTitleDir[0] = 0;
strcpy(rgchTitleName, szTitle);
return XBDM_NOERR;
}
HRESULT HrSetTitle(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szNewDir[sizeof rgchTitleDir];
char szNewName[sizeof rgchTitleName];
char *pszDir;
pszDir = FGetSzParam(sz, "dir", szNewDir, sizeof szNewDir) ?
szNewDir : NULL;
if(!FGetSzParam(sz, "name", szNewName, sizeof szNewName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
return DmSetTitle(pszDir, szNewName, PchGetParam(sz, "cmdline", TRUE));
}
HRESULT HrSetDefaultTitle(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szLaunchData[MAX_LAUNCH_PATH + 1];
char szLaunchDir[MAX_PATH + 1];
NTSTATUS st;
HANDLE h;
IO_STATUS_BLOCK iosb;
HRESULT hr;
char *pch;
OBJECT_STRING ost;
OBJECT_ATTRIBUTES oa;
FILE_DISPOSITION_INFORMATION fdi;
// Make sure dashboard.xbx file isn't read-only
InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
st = NtOpenFile(&h, FILE_WRITE_ATTRIBUTES|SYNCHRONIZE, &oa, &iosb, 0,
FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(st)) {
FILE_BASIC_INFORMATION BasicInfo;
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
NtSetInformationFile(h, &iosb, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
NtClose(h);
}
if(PchGetParam(sz, "none", FALSE)) {
// Set the default title to be the dashboard
InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
st = NtOpenFile(&h, DELETE | SYNCHRONIZE, &oa, &iosb, 0,
FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
RtlZeroMemory(&fdi, sizeof fdi);
fdi.DeleteFile = TRUE;
st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
FileDispositionInformation);
hr = NT_SUCCESS(st) ? XBDM_NOERR : E_UNEXPECTED;
NtClose(h);
} else
hr = XBDM_NOERR;
return hr;
}
RtlZeroMemory(szLaunchData, sizeof szLaunchData);
if (PchGetParam(sz, "launcher", FALSE)) {
// Set the default title to be the xdk launcher
strcpy(szLaunchData, XdkLauncherPathname);
pch = strrchr(szLaunchData, '\\');
ASSERT(pch != NULL);
pch += 1;
} else {
// Set the default title to be the specified XBE
if(!FGetSzParam(sz, "dir", szLaunchDir, sizeof szLaunchDir - 1)) {
strcpy(szResp, "missing dir");
return E_FAIL;
}
if(!FFileNameToObName(szLaunchDir, szLaunchData, sizeof szLaunchData - 1))
return XBDM_NOSUCHFILE;
for(pch = szLaunchData; *pch; ++pch);
if(pch[-1] != '\\')
*pch++ = '\\';
if(!FGetSzParam(sz, "name", pch, szLaunchData + (sizeof szLaunchData - 1) - pch))
{
strcpy(szResp, "missing name");
return E_FAIL;
}
}
/* Verify the file exists */
RtlInitObjectString(&ost, szLaunchData);
InitializeObjectAttributes(&oa, &ost, OBJ_CASE_INSENSITIVE, NULL, NULL);
st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if(!NT_SUCCESS(st))
return HrFromStatus(st, XBDM_CANNOTACCESS);
NtClose(h);
/* We've found the file, so now write out the name */
pch[-1] = TITLE_PATH_DELIMITER;
InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
st = NtCreateFile(&h, GENERIC_WRITE | DELETE | SYNCHRONIZE,
&oa, &iosb, NULL, 0, 0, FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
st = NtWriteFile(h, NULL, NULL, NULL, &iosb, szLaunchData, sizeof
szLaunchData - 1, NULL);
if(!NT_SUCCESS(st)) {
/* Can't write, so try to delete */
hr = HrFromStatus(st, E_UNEXPECTED);
RtlZeroMemory(&fdi, sizeof fdi);
fdi.DeleteFile = TRUE;
st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
FileDispositionInformation);
hr = NT_SUCCESS(st) ? XBDM_NOERR : E_UNEXPECTED;
} else
hr = XBDM_NOERR;
NtClose(h);
} else
hr = E_FAIL;
return hr;
}
HRESULT HrReportMemory(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
/* We print 32 bytes per line, which takes 64 characters + 2 newline chars
* (66 chars) to constitute a line */
DWORD cb;
DWORD dwPageBase;
BYTE b;
DWORD ibLim = 32;
DWORD ib;
LPSTR sz = pdmcc->Buffer;
BOOL fPageValid;
if(!pdmcc->BytesRemaining) {
/* No more data */
if(pdmcc->Buffer && pdmcc->BufferSize == 1056) {
DmFreePool(pdmcc->Buffer);
pdmcc->Buffer = NULL;
}
return XBDM_ENDOFLIST;
}
cb = pdmcc->BufferSize / 66;
if(cb > pdmcc->BytesRemaining)
cb = pdmcc->BytesRemaining;
dwPageBase = (DWORD)pccs->pb + 0x1000;
pdmcc->BytesRemaining -= cb;
while(cb) {
ibLim = 32;
if(cb < ibLim)
ibLim = cb;
for(ib = 0; ib < ibLim; ++ib, ++pccs->pb) {
if(((DWORD)pccs->pb ^ dwPageBase) & 0xfffff000) {
dwPageBase = (DWORD)pccs->pb & 0xfffff000;
fPageValid = MmIsAddressValid(pccs->pb);
}
if(fPageValid)
/* If this address has an associated breakpoint, then the
* memory we want isn't the int 3, but the breakpoint data */
fPageValid = FGetMemory(pccs->pb, &b);
if(fPageValid) {
sz[2*ib] = rgchHex[(b >> 4) & 0xf];
sz[2*ib+1] = rgchHex[b & 0xf];
} else
sz[2*ib] = sz[2*ib+1] = '?';
}
sz[2*ib] = '\015';
sz[2*ib+1] = '\012';
sz += 2*ib + 2;
cb -= ibLim;
}
pdmcc->DataSize = sz - (LPSTR)pdmcc->Buffer;
return XBDM_NOERR;
}
HRESULT HrGetMemory(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs;
if(!pdmcc)
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pccs->pb, szResp))
return E_FAIL;
if(!FGetNamedDwParam(sz, "length", &pdmcc->BytesRemaining, szResp))
return E_FAIL;
strcpy(szResp, "memory data follows");
pdmcc->HandlingFunction = HrReportMemory;
/* We'd like a ~1k buffer for sending data, but if we can't find the
* memory, we'll just get the default buffer */
pdmcc->BufferSize = 1056;
pdmcc->Buffer = DmAllocatePoolWithTag(pdmcc->BufferSize, 'mdbX');
return XBDM_MULTIRESPONSE;
}
HRESULT HrSetMemory(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
int i;
char rgch[5];
BYTE b;
DWORD dwPageBase;
BOOL fPageValid;
int cb = 0;
LPCSTR szData;
BYTE *pb;
if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pb, szResp))
return E_FAIL;
szData = PchGetParam(sz, "data", FALSE);
dwPageBase = (DWORD)pb + 0x1000;
rgch[0] = '0';
rgch[1] = 'x';
rgch[4] = 0;
if(szData) {
for(;;++cb, ++pb) {
if(FIsSpace(*szData))
break;
for(i = 0; i < 2; ++i) {
if(!(*szData >= '0' && *szData <= '9' ||
*szData >= 'a' && *szData <= 'f' ||
*szData >= 'A' && *szData <= 'F')) {
strcpy(szResp, "400- syntax error in data");
return E_FAIL;
}
rgch[2+i] = *szData++;
}
b = (BYTE)DwFromSz(rgch, NULL);
if(((DWORD)pb ^ dwPageBase) & 0xfffff000) {
dwPageBase = (DWORD)pb & 0xfffff000;
fPageValid = MmIsAddressValid(pb);
}
if(fPageValid)
/* If there's a breakpoint at this address, we need to set
* the breakpoint data, not change the int 3 */
fPageValid = FSetMemory(pb, b);
if(!fPageValid) {
sprintf(szResp, "set %d bytes", cb);
return XBDM_MEMUNMAPPED;
}
}
}
sprintf(szResp, "set %d bytes", cb);
return XBDM_NOERR;
}
HRESULT HrReportThreads(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
int ich;
if(!pdmcc->BytesRemaining || !pccs->i) {
DmFreePool(pccs->h);
return XBDM_ENDOFLIST;
}
/* Pack as many thread ids into the buffer as we can */
for(ich = 0; (DWORD)ich < pdmcc->BufferSize - 10 && pccs->i; ) {
sprintf((LPSTR)pdmcc->Buffer + ich, "%d\015\012",
((LPDWORD)pccs->h)[--pccs->i]);
while(((LPSTR)pdmcc->Buffer)[ich])
++ich;
}
pdmcc->DataSize = ich;
return XBDM_NOERR;
}
HRESULT HrListThreads(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
CCS *pccs;
HRESULT hr;
if(!pdmcc)
return E_FAIL;
/* Make a list that we think will be big enough */
pccs = (CCS *)pdmcc->CustomData;
pccs->h = DmAllocatePoolWithTag(256 * sizeof(DWORD), 'mdbX');
if(!pccs->h)
return E_OUTOFMEMORY;
pccs->i = 256;
hr = DmGetThreadList((LPDWORD)pccs->h, (LPDWORD)&pccs->i);
if(FAILED(hr)) {
if(hr == XBDM_BUFFER_TOO_SMALL)
hr = E_UNEXPECTED;
DmFreePool(pccs->h);
} else {
hr = XBDM_MULTIRESPONSE;
strcpy(szResp, "thread list follows");
}
pdmcc->HandlingFunction = HrReportThreads;
return hr;
}
HRESULT HrHaltThread(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
DWORD tid;
if(!FGetDwParam(sz, "thread", &tid))
tid = 0;
return DmHaltThread(tid);
}
HRESULT HrContinueThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DWORD tid;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
return DmContinueThread(tid, (BOOL)PchGetParam(sz, "exception", FALSE));
}
HRESULT HrSuspendThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DWORD tid;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
return DmSuspendThread(tid);
}
HRESULT HrResumeThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DWORD tid;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
return DmResumeThread(tid);
}
HRESULT HrFunctionCall(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DWORD tid;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
return DmSetupFunctionCall(tid);
}
typedef struct _CREG {
char *szName;
DWORD dwOffset;
DWORD cb;
} CREG;
#define CtxOffset(field) ((DWORD)(&((PCONTEXT)0)->field))
#define Creg(reg) { #reg, CtxOffset(reg), sizeof ((PCONTEXT)0)->reg }
#define FpCreg(reg) { #reg, CtxOffset(FloatSave.reg), \
sizeof ((PCONTEXT)0)->FloatSave.reg }
static CREG rgcregCtrl[] = {
Creg(Ebp), Creg(Esp), Creg(Eip), Creg(EFlags)
};
static CREG rgcregInt[] = {
Creg(Eax), Creg(Ebx), Creg(Ecx), Creg(Edx), Creg(Edi), Creg(Esi)
};
static CREG rgcregFPCtrl[] = {
FpCreg(Cr0NpxState)
};
typedef struct _RCC {
CONTEXT cr;
union {
struct {
int irgcreg;
CREG *pcreg;
int icreg;
CREG *pcregNext;
};
struct {
DWORD tid;
BOOL fSend;
int iSend;
int ib;
};
};
} RCC;
CREG *PcregNext(RCC *prcc)
{
while(!prcc->icreg) {
/* Time to switch to the next register set */
switch(prcc->irgcreg) {
case 0:
if((prcc->cr.ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
prcc->pcreg = rgcregCtrl;
prcc->icreg = sizeof rgcregCtrl / sizeof(CREG);
} else
prcc->icreg = 0;
++prcc->irgcreg;
break;
case 1:
if((prcc->cr.ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
prcc->pcreg = rgcregInt;
prcc->icreg = sizeof rgcregInt / sizeof(CREG);
} else
prcc->icreg = 0;
++prcc->irgcreg;
break;
case 2:
if((prcc->cr.ContextFlags & CONTEXT_FLOATING_POINT) ==
CONTEXT_FLOATING_POINT)
{
prcc->pcreg = rgcregFPCtrl;
prcc->icreg = sizeof rgcregFPCtrl / sizeof(CREG);
} else
prcc->icreg = 0;
++prcc->irgcreg;
break;
default:
/* Nothing left */
return prcc->pcregNext = NULL;
}
}
--prcc->icreg;
return prcc->pcregNext = prcc->pcreg++;
}
#define SIZE_OF_EXT_REGISTERS 0x200
HRESULT HrDoExtContext(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
RCC *prcc = (RCC *)pdmcc->CustomData;
HRESULT hr = XBDM_NOERR;
BOOL fDone = FALSE;
if(prcc->fSend) {
switch(prcc->iSend++) {
case 0:
pdmcc->DataSize = sizeof(DWORD);
memcpy(pdmcc->Buffer, &pdmcc->BytesRemaining, pdmcc->DataSize);
break;
case 1:
pdmcc->Buffer = &prcc->cr.FloatSave;
pdmcc->DataSize = pdmcc->BytesRemaining;
break;
default:
pdmcc->BytesRemaining = 0;
break;
}
if(!pdmcc->BytesRemaining) {
fDone = TRUE;
hr = XBDM_ENDOFLIST;
}
} else if(pdmcc->DataSize != 0) {
DWORD cbCopy = SIZE_OF_EXT_REGISTERS - prcc->ib;
if(cbCopy > pdmcc->DataSize)
cbCopy = pdmcc->DataSize;
if(cbCopy)
memcpy((BYTE *)&prcc->cr.FloatSave + prcc->ib, pdmcc->Buffer,
cbCopy);
pdmcc->BytesRemaining -= pdmcc->DataSize;
prcc->ib += cbCopy;
if(!pdmcc->BytesRemaining) {
fDone = TRUE;
prcc->cr.ContextFlags = CONTEXT_EXTENDED_REGISTERS;
hr = DmSetThreadContext(prcc->tid, &prcc->cr);
} else
hr = XBDM_NOERR;
} else
fDone = TRUE;
if(fDone)
DmFreePool(prcc);
return hr;
}
HRESULT HrGetExtContext(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
RCC *prcc;
CONTEXT cr;
HRESULT hr;
DWORD tid;
if(!pdmcc)
return E_FAIL;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
cr.ContextFlags = CONTEXT_EXTENDED_REGISTERS;
hr = DmGetThreadContext(tid, &cr);
if(SUCCEEDED(hr)) {
if((cr.ContextFlags & CONTEXT_EXTENDED_REGISTERS) ==
CONTEXT_EXTENDED_REGISTERS)
{
prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
if(!prcc)
return E_OUTOFMEMORY;
memcpy(&prcc->cr, &cr, sizeof cr);
prcc->iSend = 0;
prcc->fSend = TRUE;
pdmcc->CustomData = prcc;
pdmcc->HandlingFunction = HrDoExtContext;
ASSERT(SIZE_OF_EXT_REGISTERS <= sizeof prcc->cr.FloatSave);
pdmcc->BytesRemaining = SIZE_OF_EXT_REGISTERS;
hr = XBDM_BINRESPONSE;
} else
hr = XBDM_NOTSTOPPED;
}
return hr;
}
HRESULT HrReportContext(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
RCC *prcc = (RCC *)pdmcc->CustomData;
DWORD ich = 0;
DWORD ichLim = pdmcc->BufferSize - 22;
CREG *pcreg;
if(!pdmcc->BytesRemaining || !(pcreg = prcc->pcregNext)) {
DmFreePool(prcc);
return XBDM_ENDOFLIST;
}
/* Fill as much of the buffer as we can */
while(pcreg && ich < ichLim) {
if(pcreg->cb == 4)
sprintf((char *)pdmcc->Buffer + ich, "%s=0x%08x\015\012",
pcreg->szName, *(DWORD *)((BYTE *)&prcc->cr + pcreg->dwOffset));
else
sprintf((char *)pdmcc->Buffer + ich, "%s=0x%04x\015\012",
pcreg->szName, *(USHORT *)((BYTE *)&prcc->cr + pcreg->dwOffset));
while(((char *)pdmcc->Buffer)[ich])
++ich;
pcreg = PcregNext(prcc);
}
pdmcc->DataSize = ich;
return XBDM_NOERR;
}
HRESULT HrGetContext(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
RCC *prcc;
CONTEXT cr;
HRESULT hr;
DWORD tid;
if(!pdmcc)
return E_FAIL;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
cr.ContextFlags = 0;
if(PchGetParam(sz, "control", FALSE))
cr.ContextFlags |= CONTEXT_CONTROL;
if(PchGetParam(sz, "int", FALSE))
cr.ContextFlags |= CONTEXT_INTEGER;
if(PchGetParam(sz, "full", FALSE))
cr.ContextFlags |= CONTEXT_FULL;
if(PchGetParam(sz, "fp", FALSE))
cr.ContextFlags |= CONTEXT_FLOATING_POINT;
hr = DmGetThreadContext(tid, &cr);
if(SUCCEEDED(hr)) {
prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
if(!prcc)
return E_OUTOFMEMORY;
memcpy(&prcc->cr, &cr, sizeof cr);
prcc->icreg = 0;
prcc->irgcreg = 0;
PcregNext(prcc);
pdmcc->CustomData = prcc;
pdmcc->HandlingFunction = HrReportContext;
hr = XBDM_MULTIRESPONSE;
}
return hr;
}
HRESULT HrSetContext(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CONTEXT cr;
HRESULT hr;
DWORD dw;
DWORD tid;
RCC *prcc;
DWORD cbExtRegs;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
/* If we're going to set the extended context, make sure we'll be able to
* do so */
if(FGetDwParam(sz, "ext", &cbExtRegs)) {
if(!pdmcc)
return E_FAIL;
pdmcc->BytesRemaining = cbExtRegs;
prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
if(!prcc)
return E_OUTOFMEMORY;
} else
prcc = NULL;
/* We need the current context and then we'll modify the appropriate
* fields */
cr.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
hr = DmGetThreadContext(tid, &cr);
if(SUCCEEDED(hr)) {
int icreg;
int irgcreg;
CREG *pcreg;
for(irgcreg = 0; irgcreg < 2; ++irgcreg) {
switch(irgcreg) {
case 0:
pcreg = rgcregCtrl;
icreg = sizeof rgcregCtrl / sizeof(CREG);
break;
case 1:
pcreg = rgcregInt;
icreg = sizeof rgcregInt / sizeof(CREG);
break;
case 2:
pcreg = rgcregFPCtrl;
icreg = sizeof rgcregFPCtrl / sizeof(CREG);
break;
}
for(; icreg--; ++pcreg) {
if(FGetDwParam(sz, pcreg->szName, &dw)) {
if(pcreg->cb == 4)
*(DWORD *)((BYTE *)&cr + pcreg->dwOffset) = dw;
else
*(USHORT *)((BYTE *)&cr + pcreg->dwOffset) = (USHORT)dw;
}
}
}
hr = DmSetThreadContext(tid, &cr);
}
/* Now see if we want to capture the extended context */
if(prcc) {
if(SUCCEEDED(hr)) {
/* We're not going to be getting the cr0 state when the data
* comes in, so we'll put it in now */
prcc->cr.FloatSave.Cr0NpxState = cr.FloatSave.Cr0NpxState;
pdmcc->CustomData = prcc;
pdmcc->Buffer = NULL;
prcc->fSend = FALSE;
prcc->ib = 0;
prcc->tid = tid;
pdmcc->HandlingFunction = HrDoExtContext;
hr = XBDM_READYFORBIN;
} else
DmFreePool(prcc);
}
return hr;
}
void FillSzFromWz(char *sz, int cch, WCHAR *wz, int cwch)
{
if(cwch < 0) {
while(*wz && cch > 1) {
*sz++ = (char)*wz++;
--cch;
}
} else {
while(cwch && cch > 1) {
*sz++ = (char)*wz++;
--cch;
--cwch;
}
}
*sz = 0;
}
HRESULT HrReportCounters(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
HRESULT hr;
DWORD cch;
DM_COUNTINFO dmci;
if(!pdmcc->BytesRemaining) {
DmCloseCounters(pccs->pdmwc);
return XBDM_ENDOFLIST;
}
cch = pdmcc->BufferSize;
hr = DmWalkPerformanceCounters(&pccs->pdmwc, &dmci);
if(SUCCEEDED(hr))
sprintf(pdmcc->Buffer, "name=\"%s\" type=0x%08x", dmci.Name,
dmci.Type);
else {
DmCloseCounters(pccs->pdmwc);
pdmcc->BytesRemaining = 0;
}
return hr;
}
HRESULT HrListCounters(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs;
if(!pdmcc)
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
pccs->pdmwc = NULL;
pdmcc->HandlingFunction = HrReportCounters;
return XBDM_MULTIRESPONSE;
}
HRESULT HrReportCounterData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
switch(pdmcc->BytesRemaining++) {
case 1:
sprintf(pdmcc->Buffer, "type=0x%08x vallo=0x%08x "
"valhi=0x%08x ratelo=0x%08x ratehi=0x%08x", pccs->dmcd.CountType,
pccs->dmcd.CountValue.LowPart, pccs->dmcd.CountValue.HighPart,
pccs->dmcd.RateValue.LowPart, pccs->dmcd.RateValue.HighPart);
break;
default:
return XBDM_ENDOFLIST;
}
return XBDM_NOERR;
}
HRESULT HrQueryPerformanceCounter(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs;
HRESULT hr;
char szName[256];
HANDLE h;
DWORD dwType;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
if(!FGetNamedDwParam(sz, "type", &dwType, szResp))
return E_FAIL;
hr = DmOpenPerformanceCounter(szName, &h);
if(SUCCEEDED(hr)) {
pccs = (CCS *)pdmcc->CustomData;
hr = DmQueryPerformanceCounterHandle(h, dwType, &pccs->dmcd);
NtClose(h);
}
if(SUCCEEDED(hr)) {
pdmcc->HandlingFunction = HrReportCounterData;
pdmcc->BytesRemaining = 1;
hr = XBDM_MULTIRESPONSE;
strcpy(szResp, "counter data follows");
}
return hr;
}
HRESULT HrReportModules(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
HRESULT hr;
DMN_MODLOAD dmml;
if(!pdmcc->BytesRemaining) {
DmCloseLoadedModules(pccs->pdmwm);
return XBDM_ENDOFLIST;
}
hr = DmWalkLoadedModules(&pccs->pdmwm, &dmml);
if(SUCCEEDED(hr))
GetModLoadSz(pdmcc->Buffer, &dmml);
else {
DmCloseLoadedModules(pccs->pdmwm);
pdmcc->BytesRemaining = 0;
}
return hr;
}
HRESULT HrListModules(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs;
if(!pdmcc)
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
pccs->pdmwm = NULL;
pdmcc->HandlingFunction = HrReportModules;
return XBDM_MULTIRESPONSE;
}
HRESULT HrReportModuleSections(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
DMN_SECTIONLOAD dmsl;
HRESULT hr;
if(!pdmcc->BytesRemaining) {
DmCloseModuleSections(pccs->pdmws);
return XBDM_ENDOFLIST;
}
hr = DmWalkModuleSections(&pccs->pdmws, NULL, &dmsl);
if(SUCCEEDED(hr))
GetSectLoadSz(pdmcc->Buffer, &dmsl);
else {
DmCloseModuleSections(pccs->pdmws);
pdmcc->BytesRemaining = 0;
}
return hr;
}
HRESULT HrListModuleSections(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
CCS *pccs;
char szName[MAX_OBJ_PATH+1];
HRESULT hr;
DMN_SECTIONLOAD dmsl;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
pccs = (CCS *)pdmcc->CustomData;
pccs->pdmws = NULL;
hr = DmWalkModuleSections(&pccs->pdmws, szName, &dmsl);
if(SUCCEEDED(hr) || hr == XBDM_ENDOFLIST) {
pdmcc->HandlingFunction = HrReportModuleSections;
RewindDmws(pccs->pdmws);
hr = XBDM_MULTIRESPONSE;
}
return hr;
}
HRESULT HrDoBreak(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
PVOID pvAddr;
HRESULT hr;
BOOL fClear;
#if 0
if(PchGetParam(sz, "list", FALSE)) {
DoListBreakpoints(isock);
return;
}
#endif
if(PchGetParam(sz, "now", FALSE)) {
Sleep(250);
_asm int 3
return XBDM_NOERR;
}
if(PchGetParam(sz, "start", FALSE)) {
hr = DmSetInitialBreakpoint();
if(FAILED(hr))
strcpy(szResp, "execution not pending");
return hr;
}
if(PchGetParam(sz, "clearall", FALSE)) {
RemoveAllBreakpoints();
return XBDM_NOERR;
}
fClear = PchGetParam(sz, "clear", FALSE) != 0;
if(FGetNamedDwParam(sz, "addr", (DWORD *)&pvAddr, szResp)) {
if(fClear)
/* We're only removing this breakpoint */
hr = DmRemoveBreakpoint(pvAddr);
else
hr = DmSetBreakpoint(pvAddr);
} else {
/* See if we're doing a hw breakpoint */
DWORD dwType = DMBREAK_NONE;
DWORD dwSize = 0;
if(FGetDwParam(sz, "read", (DWORD *)&pvAddr))
dwType = DMBREAK_READWRITE;
else if(FGetDwParam(sz, "write", (DWORD *)&pvAddr))
dwType = DMBREAK_WRITE;
else if(FGetDwParam(sz, "execute", (DWORD *)&pvAddr))
dwType = DMBREAK_EXECUTE;
if(dwType == DMBREAK_NONE)
/* Never saw a valid command */
hr = E_FAIL;
else if(fClear || FGetNamedDwParam(sz, "size", &dwSize, szResp)) {
szResp[0] = 0;
hr = DmSetDataBreakpoint(pvAddr, fClear ? DMBREAK_NONE : dwType,
dwSize);
} else
hr = E_FAIL;
}
return hr;
}
HRESULT HrIsBreak(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
PVOID pvAddr;
DWORD dwType;
HRESULT hr;
if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pvAddr, szResp))
return E_FAIL;
hr = DmIsBreakpoint(pvAddr, &dwType);
if(SUCCEEDED(hr))
sprintf(szResp, "type=%d", dwType);
return hr;
}
HRESULT HrSetConfig(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
ULONG ulIndex;
DWORD dwValue;
if(!FGetNamedDwParam(sz, "index", &ulIndex, szResp))
return E_FAIL;
if(!FGetNamedDwParam(sz, "value", &dwValue, szResp))
return E_FAIL;
if (!NT_SUCCESS(ExSaveNonVolatileSetting(ulIndex, REG_DWORD, &dwValue, sizeof(DWORD))))
{
strcpy(szResp, "setvalue failed");
return E_FAIL;
}
return XBDM_NOERR;
}
#if 0
void DoListBreakpoints(int isock)
{
SOCKET s = rgsockConnect[isock];
BYTE *pb = (BYTE *)-1;
char sz[64];
PrintSockLine(s, "202- breakpoint list follows");
while(FGetNextBreakpoint(&pb)) {
sprintf(sz, "0x%08x", pb);
PrintSockLine(s, sz);
}
PrintSockLine(s, ".");
}
#endif
HRESULT HrIsStopped(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
HRESULT hr;
DM_THREADSTOP dmts;
DWORD tid;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
hr = DmIsThreadStopped(tid, &dmts);
if(SUCCEEDED(hr)) {
switch(dmts.NotifiedReason) {
case DM_DEBUGSTR:
sprintf(szResp, "debugstr thread=%d stop",
dmts.u.DebugStr.ThreadId);
break;
case DM_ASSERT:
sprintf(szResp, "assert prompt thread=%d stop",
dmts.u.DebugStr.ThreadId);
break;
default:
if(!FGetNotifySz(dmts.NotifiedReason, (DWORD)&dmts.u, szResp))
strcpy(szResp, "stopped");
break;
}
}
return hr;
}
HRESULT HrReportThreadInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
switch(pdmcc->BytesRemaining++) {
case 1:
sprintf(pdmcc->Buffer, "suspend=%d priority=%d tlsbase=0x%08x",
pccs->dmti.SuspendCount, pccs->dmti.Priority, pccs->dmti.TlsBase);
break;
default:
return XBDM_ENDOFLIST;
}
return XBDM_NOERR;
}
HRESULT HrThreadInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
HRESULT hr;
DWORD tid;
CCS *pccs;
if(!pdmcc)
return E_FAIL;
if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
hr = DmGetThreadInfo(tid, &pccs->dmti);
if(SUCCEEDED(hr)) {
pdmcc->HandlingFunction = HrReportThreadInfo;
pdmcc->BytesRemaining = 1;
hr = XBDM_MULTIRESPONSE;
strcpy(szResp, "thread info follows");
}
return hr;
}
HRESULT HrReportXapiInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
switch(pdmcc->BytesRemaining++) {
case 1:
sprintf(pdmcc->Buffer, "lasterr=0x%08x", pccs->dmxd.LastErrorOffset);
break;
default:
return XBDM_ENDOFLIST;
}
return XBDM_NOERR;
}
HRESULT HrXapiInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
HRESULT hr;
DWORD tid;
CCS *pccs;
if(!pdmcc)
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
hr = DmGetXtlData(&pccs->dmxd);
if(SUCCEEDED(hr)) {
pdmcc->HandlingFunction = HrReportXapiInfo;
pdmcc->BytesRemaining = 1;
hr = XBDM_MULTIRESPONSE;
strcpy(szResp, "xtl info follows");
}
return hr;
}
HRESULT HrDoLongName(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
HRESULT hr;
char szModule[MAX_OBJ_PATH+1];
if(!FGetSzParam(sz, "name", szModule, sizeof szModule)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
hr = DmGetModuleLongName(szModule, szResp, &cchResp);
if(hr == E_FAIL)
strcpy(szResp, "no long name available");
return hr;
}
HRESULT HrReportXbeInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
switch(pccs->iiXbe++) {
case 0:
sprintf(pdmcc->Buffer, "timestamp=0x%08x", pccs->pxbe->TimeStamp);
break;
case 1:
sprintf(pdmcc->Buffer, "checksum=0x%08x", pccs->pxbe->CheckSum);
break;
case 2:
/* We're a little sneaky with the name -- we know the buffer we have
* for the name is big enough, so we slide it over, overwriting the
* timestamp and checksum values, and insert the "name=" in front */
memmove(&pccs->pxbe->LaunchPath[5], pccs->pxbe->LaunchPath,
MAX_PATH + 1);
memcpy(pccs->pxbe->LaunchPath, "name=", 5);
pdmcc->Buffer = pccs->pxbe->LaunchPath;
break;
default:
pdmcc->BytesRemaining = 0;
break;
}
if(!pdmcc->BytesRemaining) {
DmFreePool(pccs->pxbe);
return XBDM_ENDOFLIST;
}
return XBDM_NOERR;
}
HRESULT HrXbeInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
char szName[MAX_OBJ_PATH+1];
char *pszName;
CCS *pccs;
HRESULT hr;
if(!pdmcc)
return E_FAIL;
if(PchGetParam(sz, "running", FALSE))
pszName = NULL;
else if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
} else
pszName = szName;
pccs = (CCS *)pdmcc->CustomData;
pccs->pxbe = DmAllocatePoolWithTag(sizeof(DM_XBE), 'dmbX');
if(!pccs->pxbe)
return E_OUTOFMEMORY;
hr = DmGetXbeInfo(pszName, pccs->pxbe);
if(FAILED(hr))
DmFreePool(pccs->pxbe);
else {
pdmcc->HandlingFunction = HrReportXbeInfo;
pdmcc->BytesRemaining = 1;
pccs->iiXbe = 0;
hr = XBDM_MULTIRESPONSE;
}
return hr;
}
BOOL DmIsDebuggerPresent(void)
{
return g_fDebugging;
}
HRESULT HrConnectDebugger(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
if(g_dmi.Flags & DMIFLAG_RUNSHELL)
return XBDM_NOTDEBUGGABLE;
if(PchGetParam(sz, "connect", FALSE)) {
g_fDebugging = TRUE;
fAllowKd = FALSE;
} else if(PchGetParam(sz, "disconnect", FALSE))
g_fDebugging = FALSE;
else {
sprintf(szResp, "no command");
return E_FAIL;
}
return XBDM_NOERR;
}
HRESULT HrSystemTime(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
LARGE_INTEGER li;
KeQuerySystemTime(&li);
if(li.HighPart > 0x20000000)
return XBDM_CLOCKNOTSET;
sprintf(szResp, "high=0x%x low=0x%x", li.HighPart, li.LowPart);
return XBDM_NOERR;
}
HRESULT HrGetAltAddr(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
XNADDR xna;
int err;
err = XNetGetTitleXnAddr(&xna);
if(err == XNET_GET_XNADDR_NONE || err == XNET_GET_XNADDR_PENDING)
return E_FAIL;
sprintf(szResp, "addr=0x%08x", ntohl(xna.ina.s_addr));
return XBDM_NOERR;
}
HRESULT HrSetSystemTime(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
LARGE_INTEGER li;
NTSTATUS st;
DWORD tzflag;
if(!FGetDwParam(sz, "clockhi", &li.HighPart) || !FGetDwParam(sz, "clocklo", &li.LowPart))
return XBDM_CLOCKNOTSET;
st = NtSetSystemTime(&li, NULL);
// Set default timezone and language if necessary
if (FGetDwParam(sz, "tz", &tzflag)) {
XBOX_USER_SETTINGS userSettings;
ULONG type, length;
st = ExQueryNonVolatileSetting(
XC_MAX_OS,
&type,
&userSettings,
sizeof(userSettings),
&length);
if (NT_SUCCESS(st)) {
BOOL isInvalid = FALSE;
// Default to Pacific time zone if not set
if (XBOX_INVALID_TIMEZONE_SETTING(userSettings)) {
userSettings.TimeZoneBias = 480;
strncpy(userSettings.TimeZoneStdName, "PST", XC_TZNAMELEN);
strncpy(userSettings.TimeZoneDltName, "PDT", XC_TZNAMELEN);
userSettings.TimeZoneStdBias = 0;
userSettings.TimeZoneDltBias = -60;
userSettings.TimeZoneStdDate.Month = 10;
userSettings.TimeZoneStdDate.Day = 5;
userSettings.TimeZoneStdDate.DayOfWeek = 0;
userSettings.TimeZoneStdDate.Hour = 0;
userSettings.TimeZoneDltDate.Month = 4;
userSettings.TimeZoneDltDate.Day = 1;
userSettings.TimeZoneDltDate.DayOfWeek = 0;
userSettings.TimeZoneDltDate.Hour = 0;
isInvalid = TRUE;
}
// Default language to English if not set
if (userSettings.Language == XC_LANGUAGE_UNKNOWN) {
userSettings.Language = XC_LANGUAGE_ENGLISH;
isInvalid = TRUE;
}
if (isInvalid) {
st = ExSaveNonVolatileSetting(
XC_MAX_OS,
REG_BINARY,
&userSettings,
sizeof(userSettings));
}
}
}
return NT_SUCCESS(st) ? XBDM_NOERR : XBDM_CLOCKNOTSET;
}
HRESULT HrReboot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
int icst;
DWORD dw = 0;
if(!pdmcc)
return FALSE;
icst = (PCST)pdmcc - rgcst;
PrintSockLine(rgcst[icst].s, "200- OK");
if(PchGetParam(sz, "stop", FALSE))
dw |= DMBOOT_STOP;
if(PchGetParam(sz, "wait", FALSE))
dw |= DMBOOT_WAIT;
if(PchGetParam(sz, "warm", FALSE))
dw |= DMBOOT_WARM;
if(PchGetParam(sz, "nodebug", FALSE))
dw |= DMBOOT_NODEBUG;
DmReboot(dw);
return 0;
}
HRESULT HrMagicReboot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
int icst;
HANDLE h;
OBJECT_ATTRIBUTES oa;
OBJECT_STRING objectName;
IO_STATUS_BLOCK iosb;
NTSTATUS st;
OCHAR oszTitle[MAX_OBJ_PATH+1];
OCHAR *pochTitle;
OCHAR *poch;
char szTitle[MAX_OBJ_PATH+1];
DWORD dwFlags = DMBOOT_NODEBUG | DMBOOT_WARM;
if(!FGetSzParam(sz, "title", szTitle, sizeof szTitle)) {
strcpy(szResp, "missing title");
return E_FAIL;
}
if(PchGetParam(sz, "debug", FALSE))
dwFlags &= ~DMBOOT_NODEBUG;
if(!FFileNameToObName(szTitle, oszTitle, sizeof oszTitle / sizeof(OCHAR)))
return XBDM_NOSUCHFILE;
for(poch = oszTitle; *poch; ++poch)
if(*poch == '\\')
pochTitle = poch;
DmStop();
/* Verify the file exists */
RtlInitObjectString(&objectName, oszTitle);
InitializeObjectAttributes(&oa, &objectName, OBJ_CASE_INSENSITIVE, NULL,
NULL);
st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
NtClose(h);
*pochTitle++ = 0;
st = NT_SUCCESS(XWriteTitleInfoNoReboot(pochTitle, oszTitle, LDT_NONE,
0, NULL));
if(NT_SUCCESS(st)) {
icst = (PCST)pdmcc - rgcst;
PrintSockLine(rgcst[icst].s, "200- OK");
DmReboot(dwFlags);
}
}
return HrFromStatus(st, E_FAIL);
}
#include "xprofp.h"
#define XBOX_CAP_FILENAME "E:\\xboxcap.dat"
XProfpFileHeader* CAPGetFileHeader()
{
XProfpFileHeader* fileheader = (XProfpFileHeader*)
MmAllocateSystemMemory(XPROF_FILE_HEADER_SIZE, PAGE_READWRITE);
if (fileheader) {
PLIST_ENTRY list, head;
ULONG count;
memset(fileheader, 0, XPROF_FILE_HEADER_SIZE);
fileheader->version = XPROF_FILE_VERSION;
// save information about currently loaded modules
head = g_dmi.LoadedModuleList;
list = head->Flink;
for (count=0; list != head; list=list->Flink) {
PLDR_DATA_TABLE_ENTRY entry;
WCHAR* wstr;
CHAR* str;
ULONG len;
entry = CONTAINING_RECORD(list, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
fileheader->modules[count].loadaddr = (ULONG) entry->DllBase;
fileheader->modules[count].size = entry->SizeOfImage;
len = entry->BaseDllName.Length / sizeof(WCHAR);
if (len >= XPROF_MAX_MODULE_NAMELEN-1) {
// The base DLL name is too long
// Just ignore this module.
continue;
}
// Truncate unicode name to ASCII
wstr = entry->BaseDllName.Buffer;
str = fileheader->modules[count].name;
while (len--)
*str++ = (CHAR) *wstr++;
// Null-terminator
*str = 0;
count++;
}
fileheader->module_count = count;
}
return fileheader;
}
NTSTATUS CAPWriteFile(XProfpGlobals* xprofData)
{
BYTE* bufstart = (BYTE*) xprofData->bufstart;
BYTE* bufend = (BYTE*) xprofData->bufnext_stopped;
HANDLE file = NULL;
XProfpFileHeader* fileheader = NULL;
ULONG count;
NTSTATUS status;
IO_STATUS_BLOCK iostatusBlock;
FILE_END_OF_FILE_INFORMATION endOfFile;
FILE_ALLOCATION_INFORMATION allocation;
// Sanity check
if (!bufstart || !bufend || bufend <= bufstart) {
return STATUS_UNSUCCESSFUL;
}
// Create the file for writing
// we're doing sequential writes only and
// we don't need intermediate buffering
count = bufend - bufstart;
ASSERT(((ULONG_PTR) bufstart % PAGE_SIZE) == 0);
status = FCreateFile(
&file,
GENERIC_WRITE | SYNCHRONIZE,
XBOX_CAP_FILENAME,
NULL,
0,
0,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_SEQUENTIAL_ONLY |
FILE_NO_INTERMEDIATE_BUFFERING);
if (!NT_SUCCESS(status)) goto exit;
// Generate the profile data file header
fileheader = CAPGetFileHeader();
if (!fileheader) {
status = STATUS_NO_MEMORY;
goto exit;
}
ASSERT(XPROF_FILE_HEADER_SIZE % PAGE_SIZE == 0);
status = NtWriteFile(
file,
NULL,
NULL,
NULL,
&iostatusBlock,
fileheader,
XPROF_FILE_HEADER_SIZE,
NULL);
if (!NT_SUCCESS(status)) goto exit;
// Write the profile data out to disk
// size is rounded up to a multiple of 512
status = NtWriteFile(
file,
NULL,
NULL,
NULL,
&iostatusBlock,
bufstart,
(count + 511) & ~511,
NULL);
if (!NT_SUCCESS(status)) goto exit;
// Set file size
count += XPROF_FILE_HEADER_SIZE;
endOfFile.EndOfFile.QuadPart = count;
status = NtSetInformationFile(
file,
&iostatusBlock,
&endOfFile,
sizeof(endOfFile),
FileEndOfFileInformation);
if (!NT_SUCCESS(status)) goto exit;
allocation.AllocationSize.QuadPart = count;
status = NtSetInformationFile(
file,
&iostatusBlock,
&allocation,
sizeof(allocation),
FileAllocationInformation);
exit:
if (file) { NtClose(file); }
if (fileheader) { MmFreeSystemMemory(fileheader, XPROF_FILE_HEADER_SIZE); }
return status;
}
HRESULT HrCAPControl(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
if (PchGetParam(sz, "start", FALSE)) {
// Start profile data collection
status = DmCapControl(XPROF_START, 0);
} else {
// Stop profile data collection
status = DmCapControl(XPROF_STOP, 0);
// Save the profile data to disk
if (NT_SUCCESS(status)) {
XProfpGlobals* xprofData = DmProfileData;
LONG lock;
// Prevent other threads from freeing the profile data buffer
// while we're trying to write it to disk
lock = InterlockedCompareExchange(&xprofData->lock, 1, 0);
if (lock != 0) {
status = STATUS_DEVICE_BUSY;
} else {
CAPWriteFile(xprofData);
// Unlock the profile data buffer
xprofData->lock = 0;
}
}
}
if (NT_SUCCESS(status)) return XBDM_NOERR;
// Note: should we map error status to HRESULT?
sprintf(szResp, "error status 0x%08x", status);
return XBDM_UNDEFINED;
}
HRESULT HrIrtSweep(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
HANDLE file;
#ifdef _XBOX_ENABLE_PROFILING
#define IRTSWEEP_FILENAME "E:\\irtsweep.dat"
status = FCreateFile(
&file,
GENERIC_WRITE | SYNCHRONIZE,
IRTSWEEP_FILENAME,
NULL,
0,
0,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_SEQUENTIAL_ONLY |
FILE_NO_INTERMEDIATE_BUFFERING);
if (NT_SUCCESS(status)) {
status = IrtSweep(file);
NtClose(file);
}
#endif
if (NT_SUCCESS(status)) return XBDM_NOERR;
sprintf(szResp, "error status 0x%08x", status);
return XBDM_UNDEFINED;
}
HRESULT HrReceiveKernelImage(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
HRESULT hr = XBDM_NOERR;
DWORD crc;
CCS *pccs;
pccs = pdmcc->CustomData;
if (pdmcc->DataSize) {
if ( pccs->KernelImage.BytesReceived + pdmcc->DataSize <= \
pccs->KernelImage.ImageSize) {
CopyMemory(pccs->KernelImage.ImageBuffer + pccs->KernelImage.BytesReceived, \
pdmcc->Buffer, pdmcc->DataSize);
}
pdmcc->BytesRemaining -= pdmcc->DataSize;
pccs->KernelImage.BytesReceived += pdmcc->DataSize;
} else if (pdmcc->BytesRemaining) {
DmFreePool(pccs->KernelImage.ImageBuffer);
DmFreePool(pdmcc->Buffer);
return E_UNEXPECTED;
}
if (pdmcc->BytesRemaining == 0) {
crc = Crc32(0xFFFFFFFF, pccs->KernelImage.ImageBuffer, pccs->KernelImage.ImageSize);
if (pccs->KernelImage.Crc != crc) {
_snprintf(szResponse, cchResponse, "bad data detected in kernel image");
hr = E_UNEXPECTED;
} else {
_snprintf(szResponse, cchResponse, "flashing...");
Sleep(250);
hr = FlashKernelImage(pccs->KernelImage.ImageBuffer,
pccs->KernelImage.ImageSize, szResponse, cchResponse,
pccs->KernelImage.IgnoreVersionChecking);
}
DmFreePool(pccs->KernelImage.ImageBuffer);
DmFreePool(pdmcc->Buffer);
}
return hr;
}
HRESULT HrFlashKernelImage(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs;
SIZE_T ImageSize;
BOOL IgnoreVersionChecking = FALSE;
if (!pdmcc) {
return E_FAIL;
}
pccs = (CCS *)pdmcc->CustomData;
if (!FGetNamedDwParam(sz, "length", &ImageSize, szResp) || \
!FGetNamedDwParam(sz, "crc", &pccs->KernelImage.Crc, szResp)) {
_snprintf(szResp, cchResp, "Invalid parameter(s)");
return E_FAIL;
}
FGetNamedDwParam(sz, "ignoreversionchecking", &IgnoreVersionChecking, szResp);
pccs->KernelImage.IgnoreVersionChecking = IgnoreVersionChecking;
pccs->KernelImage.ImageSize = ImageSize;
pdmcc->BytesRemaining = ImageSize;
if (ImageSize < 0x80000 || (ImageSize & (ImageSize - 1)) != 0) {
_snprintf(szResp, cchResp, "Invalid kernel image size");
return E_FAIL;
}
pccs->KernelImage.ImageBuffer = DmAllocatePoolWithTag(ImageSize, 'mdbX');
if (!pccs->KernelImage.ImageBuffer) {
_snprintf(szResp, cchResp, "Not enough memory");
return HrFromStatus(STATUS_NO_MEMORY, XBDM_UNDEFINED);
}
pdmcc->Buffer = DmAllocatePoolWithTag(0x2000, 'mdbX');
if (!pdmcc->Buffer) {
DmFreePool(pccs->KernelImage.ImageBuffer);
_snprintf(szResp, cchResp, "Not enough memory");
return HrFromStatus(STATUS_NO_MEMORY, XBDM_UNDEFINED);
}
pdmcc->BufferSize = 0x2000;
pdmcc->HandlingFunction = HrReceiveKernelImage;
pccs->KernelImage.BytesReceived = 0;
return XBDM_READYFORBIN;
}
typedef struct _DRIVENAME_CONVERSION_ENTRY
{
PCOSTR ObjectName;
ULONG ObjectNameLength; //Without a terminating NULL
OCHAR DriveName;
BOOL Visible;
} DRIVENAME_TABLE_ENTRY, *PDRIVENAME_TABLE_ENTRY;
extern DRIVENAME_TABLE_ENTRY DriveNameConversionTable[];
HRESULT HrGetDriveList(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
char szName[8];
OBJECT_STRING objectName;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
HANDLE h;
DRIVENAME_TABLE_ENTRY *pdte;
struct {
OBJECT_DIRECTORY_INFORMATION odi;
OCHAR sz[256];
} odi;
RtlInitObjectString(&objectName, "");
InitializeObjectAttributes(&objectAttributes, &objectName,
OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);
status = NtOpenDirectoryObject(&h, &objectAttributes);
if(NT_SUCCESS(status)) {
int i = 0;
// Query the directory object to get the list of actual drives
for(;;) {
status = NtQueryDirectoryObject(h, &odi, sizeof odi, (i == 0), &i,
NULL);
if (!NT_SUCCESS(status))
break;
/* Reject this drive if it's not one character plus a colon */
if(odi.odi.Name.Length != 2 || odi.odi.Name.Buffer[1] != ':')
continue;
/* Compare each drive in the drivename conversion table to the
* current enumerated drive. If there is a match, then that drive
* has been remapped - let the table handling code below deal with
* it */
for(pdte = DriveNameConversionTable; pdte->ObjectName; ++pdte) {
BYTE bMask;
if(pdte->DriveName < 'A' || (pdte->DriveName & 0xDF) > 'Z')
bMask = 0xFF;
else
bMask = 0xDF;
/* Compare case-insensitive */
if (!((odi.odi.Name.Buffer[0] ^ pdte->DriveName) & bMask))
{
/* Found a matching entry - ignore the entry; it'll get
* handled in the table handling code below */
break;
}
}
if (!pdte->ObjectName)
{
HANDLE hT;
/* The current enumerated drive does not exist in the table.
* We'll need to handle it here. Is the drive mounted? */
odi.odi.Name.Buffer[2] = '\\';
odi.odi.Name.Length = 3;
InitializeObjectAttributes(&objectAttributes, &odi.odi.Name,
OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);
status = NtOpenFile(&hT, SYNCHRONIZE,&objectAttributes, &iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(status))
{
*szResp++ = odi.odi.Name.Buffer[0];
NtClose(hT);
}
}
}
NtClose(h);
}
// Enumerate through the conversion table
szName[1] = ':';
szName[2] = '\\';
szName[3] = 0;
for(pdte = DriveNameConversionTable; pdte->ObjectName; ++pdte) {
if (pdte->Visible)
{
// Is the drive mounted?
szName[0] = pdte->DriveName;
status = FCreateFile(&h, SYNCHRONIZE, szName, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(status))
{
*szResp++ = pdte->DriveName;
NtClose(h);
}
}
}
// Close off the 'list' string value
*szResp = 0;
// Mark that we've been through here once so that next time through we know we're done
return XBDM_NOERR;
}
HRESULT HrReportDriveFreeSpace(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
FILE_FS_SIZE_INFORMATION NormalSizeInfo;
CCS *pccs = (CCS *)pdmcc->CustomData;
ULARGE_INTEGER FreeBytesAvailableToCaller;
ULARGE_INTEGER TotalNumberOfBytes;
ULARGE_INTEGER BytesPerAllocationUnit;
if (pdmcc->BytesRemaining-- == 0)
{
// Already sent the data, so close the file, free up our buffer, and return "we're done!"
NtClose(pccs->h);
DmFreePool(pdmcc->Buffer);
pdmcc->Buffer = NULL;
return XBDM_ENDOFLIST;
}
// Determine the size parameters of the volume.
Status = NtQueryVolumeInformationFile(
pccs->h,
&IoStatusBlock,
&NormalSizeInfo,
sizeof(NormalSizeInfo),
FileFsSizeInformation
);
if ( !NT_SUCCESS(Status) ) {
sprintf(pdmcc->Buffer, "unknown error (%d)", Status);
return E_FAIL;
}
BytesPerAllocationUnit.QuadPart = NormalSizeInfo.BytesPerSector * NormalSizeInfo.SectorsPerAllocationUnit;
FreeBytesAvailableToCaller.QuadPart = BytesPerAllocationUnit.QuadPart * NormalSizeInfo.AvailableAllocationUnits.QuadPart;
TotalNumberOfBytes.QuadPart = BytesPerAllocationUnit.QuadPart * NormalSizeInfo.TotalAllocationUnits.QuadPart;
// Add the results to the result string
sprintf(pdmcc->Buffer, "freetocallerlo=0x%08x freetocallerhi=0x%08x totalbyteslo=0x%08x totalbyteshi=0x%08x totalfreebyteslo=0x%08x totalfreebyteshi=0x%08x",
FreeBytesAvailableToCaller.LowPart, FreeBytesAvailableToCaller.HighPart,
TotalNumberOfBytes.LowPart, TotalNumberOfBytes.HighPart,
FreeBytesAvailableToCaller.LowPart, FreeBytesAvailableToCaller.HighPart);
return XBDM_NOERR;
}
HRESULT HrGetDriveFreeSpace(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
NTSTATUS st;
char szName[MAX_OBJ_PATH+1];
CCS *pccs = (CCS *)pdmcc->CustomData;
if(!pdmcc)
return E_FAIL;
if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
pdmcc->BufferSize = 512;
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
pdmcc->HandlingFunction = HrReportDriveFreeSpace;
pdmcc->BytesRemaining = 1; // Internal counter
st = FCreateFile(&pccs->h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
XBDM_CANNOTACCESS);
}
HRESULT HrSendFrameData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
// end of data?
if (pdmcc->BytesRemaining <= 0) {
DmFreePool(pdmcc->Buffer);
pdmcc->Buffer = NULL;
// We've finished sending the data, so start the threads going again
DmGo();
return XBDM_ENDOFLIST;
}
if(pccs->Screenshot.fFirstTime == FALSE) {
/* First we need to send the various parameters (pitch, format, buffer size, etc) */
sprintf(pdmcc->Buffer, "pitch=0x%08x width=0x%08x height=0x%08x format=0x%08x, framebuffersize=0x%08x",
pccs->Screenshot.Pitch, pccs->Screenshot.Width, pccs->Screenshot.Height, pccs->Screenshot.Format, pdmcc->BytesRemaining);
pdmcc->DataSize = -1;
pccs->Screenshot.fFirstTime = TRUE;
} else {
// if (buffersize%8k != 0) need to send less data
DWORD cBytesToSend = min (pdmcc->BytesRemaining, pdmcc->BufferSize);
// send 8k of data
memcpy(pdmcc->Buffer, pccs->Screenshot.pbyFrameBuffer, cBytesToSend);
pdmcc->DataSize = cBytesToSend;
pdmcc->BytesRemaining -= cBytesToSend;
// point at next 8k of data
pccs->Screenshot.pbyFrameBuffer += cBytesToSend;
}
return XBDM_NOERR;
}
HRESULT HrScreenshot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
D3DSurface d3dsurface;
int width, height, pitch, bitdepth, format;
DWORD start;
if(!g_dmgd.Surface) {
strcpy(szResp, "can't find screen buffer");
return E_FAIL;
}
//pdmgd = DmGetCurrentValue((ULONG)((BYTE*)&dm.D3DDriverData - (BYTE*)&dm));
d3dsurface = *(g_dmgd.Surface);
// Stop all threads so that no more Presents are added to the queue.
DmStop();
// Busy-wait until all previously queues Presents are processed by the GPU
start = NtGetTickCount();
while( *(g_dmgd.FlipCounter) != *(g_dmgd.FrameCounter))
{
if ( (NtGetTickCount() - start) >= 2000 ) {
DmGo();
strcpy(szResp, "can't lock GPU");
return E_FAIL;
}
}
// Extract size information
width = (d3dsurface.Size & D3DSIZE_WIDTH_MASK) + 1;
height = ((d3dsurface.Size & D3DSIZE_HEIGHT_MASK) >> D3DSIZE_HEIGHT_SHIFT) + 1;
pitch = (((d3dsurface.Size & D3DSIZE_PITCH_MASK) >> D3DSIZE_PITCH_SHIFT) + 1) * D3DTEXTURE_PITCH_ALIGNMENT;
format = (d3dsurface.Format & D3DFORMAT_FORMAT_MASK) >> D3DFORMAT_FORMAT_SHIFT;
switch(format) {
case D3DFMT_LIN_A8R8G8B8:
bitdepth = 4;
break;
case D3DFMT_LIN_X8R8G8B8:
bitdepth = 4;
break;
case D3DFMT_LIN_R5G6B5:
bitdepth = 2;
break;
case D3DFMT_LIN_X1R5G5B5:
bitdepth = 2;
break;
default:
DmGo();
strcpy(szResp, "Unexpected framebuffer format");
return E_FAIL;
}
pccs->Screenshot.pbyFrameBuffer = (BYTE *) (d3dsurface.Data | 0xF0000000);
pccs->Screenshot.fFirstTime = FALSE;
pccs->Screenshot.Width = width;
pccs->Screenshot.Height = height;
pccs->Screenshot.Pitch = pitch;
pccs->Screenshot.Format = format;
// Set up the transfer buffer
pdmcc->BufferSize = 8192;
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
pdmcc->HandlingFunction = HrSendFrameData;
pdmcc->BytesRemaining = pitch * height;
return XBDM_BINRESPONSE;
}
HRESULT HrSendPSSnapshotData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
// end of data?
if (pdmcc->BytesRemaining <= 0)
{
pdmcc->Buffer = NULL;
*(g_dmgd.pdwOpcode) = PSSNAP_DONE; // signal D3D to release buffer
return XBDM_ENDOFLIST;
}
pdmcc->DataSize = 32768;
pdmcc->BytesRemaining = 0;
return XBDM_NOERR;
}
HRESULT HrPSSnap(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
DWORD dwX, dwY, dwOpcode;
struct
{
DWORD dwX;
DWORD dwY;
DWORD dwFlags;
DWORD dwMarker;
} args;
DWORD dwStart;
if(!FGetDwParam(sz, "x", &args.dwX))
{
strcpy(szResp, "Missing X parameter");
return E_FAIL;
}
if(!FGetDwParam(sz, "y", &args.dwY))
{
strcpy(szResp, "Missing Y parameter");
return E_FAIL;
}
if(!FGetDwParam(sz, "flags", &args.dwFlags))
args.dwFlags = 0;
if(!FGetDwParam(sz, "marker", &args.dwMarker))
args.dwMarker = 0;
if(*(g_dmgd.pdwOpcode))
{
sprintf(szResp, "D3D snapshot client in invalid state (%08x)", *(g_dmgd.pdwOpcode));
return E_FAIL;
}
// build opcode for D3D pixel shader snapshot
*(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
*(g_dmgd.pdwOpcode) = PSSNAP_REQUEST;
// wait for the opcode to change
dwStart = NtGetTickCount();
while(1)
{
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
if((dwOpcode == PSSNAP_ACK) ||
(dwOpcode == PSSNAP_ERROR))
break;
Sleep(50);
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
strcpy(szResp, "Opcode timeout: Failed to receive acknowledge");
*(g_dmgd.pdwOpcode) = PSSNAP_IDLE;
return E_FAIL;
}
}
if(dwOpcode == PSSNAP_ERROR)
{
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
{
case 1:
sprintf(szResp, "The Application is using an unsupported framebuffer format. Only A8R8G8B8 is supported at this time.");
break;
case 2:
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
break;
case 3:
sprintf(szResp, "Debugger wasn't able to allocate memory.");
break;
default:
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
break;
}
*(g_dmgd.pdwOpcode) = PSSNAP_IDLE;
return E_FAIL;
}
// Set up the transfer buffer
pdmcc->BufferSize = 32768;
pdmcc->Buffer = *(g_dmgd.ppSnapshotBuffer);
pdmcc->HandlingFunction = HrSendPSSnapshotData;
pdmcc->BytesRemaining = 32768;
return XBDM_BINRESPONSE;
}
HRESULT HrSendVSSnapshotData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
// end of data?
if (pdmcc->BytesRemaining <= 0)
{
pdmcc->Buffer = NULL;
*(g_dmgd.pdwOpcode) = VSSNAP_DONE; // signal D3D to release buffer
return XBDM_ENDOFLIST;
}
pdmcc->DataSize = 32768;
pdmcc->BytesRemaining = 0;
return XBDM_NOERR;
}
HRESULT HrVSSnap(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
DWORD dwOpcode;
DWORD dwStart;
struct
{
DWORD dwFirst;
DWORD dwLast;
DWORD dwFlags;
DWORD dwMarker;
} args;
if(*(g_dmgd.pdwOpcode))
{
sprintf(szResp, "D3D snapshot client in invalid state (%08x)", *(g_dmgd.pdwOpcode));
return E_FAIL;
}
if(!FGetDwParam(sz, "first", &args.dwFirst))
{
strcpy(szResp, sz);
return E_FAIL;
}
if(!FGetDwParam(sz, "last", &args.dwLast))
{
strcpy(szResp, "Missing Last parameter");
return E_FAIL;
}
if(!FGetDwParam(sz, "flags", &args.dwFlags))
args.dwFlags = 0;
if(!FGetDwParam(sz, "marker", &args.dwMarker))
args.dwMarker = 0;
if(args.dwFlags & 0x08000000)
{
// xray sequence instead
*(g_dmgd.pdwOpcode) = XRAY_BEGINREQ;
*(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
// wait for the opcode to change
dwStart = NtGetTickCount();
while(1)
{
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
if((dwOpcode == XRAY_BEGINACK) ||
(dwOpcode == XRAY_ERROR))
break;
Sleep(50);
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
sprintf(szResp, "Opcode timeout: Failed to receive acknowledge: %08x", dwOpcode);
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
return E_FAIL;
}
}
if(dwOpcode == XRAY_ERROR)
{
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
{
case 2:
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
break;
default:
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
break;
}
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
return E_FAIL;
}
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
return XBDM_NOERR;
}
else
{
if(args.dwFlags & 0x04000000)
{
// end xray sequence instead
*(g_dmgd.pdwOpcode) = XRAY_ENDREQ;
// wait for the opcode to change
dwStart = NtGetTickCount();
while(1)
{
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
if((dwOpcode == XRAY_ENDACK) ||
(dwOpcode == XRAY_ERROR))
break;
Sleep(50);
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
strcpy(szResp, "Opcode timeout: Failed to receive acknowledge");
*(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
return E_FAIL;
}
}
if(dwOpcode == XRAY_ERROR)
{
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
{
case 2:
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
break;
default:
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
break;
}
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
return E_FAIL;
}
*(g_dmgd.pdwOpcode) = XRAY_IDLE;
return XBDM_NOERR;
}
}
// build opcode for D3D vertex shader snapshot
dwOpcode = VSSNAP_REQUEST;
*(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
*(g_dmgd.pdwOpcode) = dwOpcode;
// wait for the opcode to change
dwStart = NtGetTickCount();
while(1)
{
dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
if((dwOpcode == VSSNAP_ACK) ||
(dwOpcode == VSSNAP_ERROR))
break;
Sleep(50);
if ( (NtGetTickCount() - dwStart) >= 2000 ) {
strcpy(szResp, "Opcode timeout: Failed to receive acknowledge");
*(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
return E_FAIL;
}
}
if(dwOpcode == VSSNAP_ERROR)
{
switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
{
case 2:
sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
break;
case 3:
sprintf(szResp, "Debugger wasn't able to allocate memory.");
break;
default:
sprintf(szResp, "D3D snapshot client returned error %d", *(g_dmgd.pdwOpcode) & 0x00ffffff);
break;
}
*(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
return E_FAIL;
}
// Set up the transfer buffer
pdmcc->BufferSize = 32768;
pdmcc->Buffer = *(g_dmgd.ppSnapshotBuffer);
pdmcc->HandlingFunction = HrSendVSSnapshotData;
pdmcc->BytesRemaining = 32768;
return XBDM_BINRESPONSE;
}
HRESULT HrSetLockMode(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
HRESULT hr;
if(PchGetParam(sz, "unlock", FALSE)) {
g_fLockLevel = FALSE;
hr = XBDM_NOERR;
} else if(FGetQwordParam(sz, "boxid", &g_luBoxId)) {
g_fLockLevel = TRUE;
hr = XBDM_NOERR;
} else {
/* Potential failure */
hr = E_FAIL;
strcpy(szResp, "missing parameters");
}
if(PchGetParam(sz, "encrypt", FALSE)) {
if(g_fLockLevel) {
g_fLockLevel = 2;
hr = XBDM_NOERR;
} else
/* If unlock was specified, we'll return success as set above;
* if not, we'll return failure. In either case, hr is already
* set correctly, so we'll just make sure the error message
* is sufficient */
strcpy(szResp, "box not locked; file encryption not enabled");
}
RemoveAllUsers();
WriteIniFile();
return hr;
}
HRESULT HrSetDebugSecureMode(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
int icst;
if(!g_fDebugSecureMode)
return XBDM_INVALIDCMD;
if(!pdmcc)
return E_FAIL;
icst = (PCST)pdmcc - rgcst;
rgcst[icst].dmplCur = -1;
return XBDM_NOERR;
}
HRESULT HrGetBoxId(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
if(!g_fLockLevel)
return XBDM_NOTLOCKED;
sprintf(szResp, "boxid=0q%08x%08x", g_luBoxId.HighPart, g_luBoxId.LowPart);
return XBDM_NOERR;
}
HRESULT HrAuthenticateUser(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szName[256];
ULARGE_INTEGER luResponse;
int icst;
BOOL fKeyXchg;
LPCSTR pszName;
if(!pdmcc)
return E_FAIL;
icst = (PCST)pdmcc - rgcst;
if((rgcst[icst].dwStatus & CONN_DHKEY) && FGetQwordParam(sz, "passwd",
&luResponse))
{
luResponse.LowPart ^= rgcst[icst].luDHKey.LowPart;
luResponse.HighPart ^= rgcst[icst].luDHKey.HighPart;
fKeyXchg = TRUE;
} else if(!FGetQwordParam(sz, "resp", &luResponse)) {
noresp:
strcpy(szResp, "missing response");
return E_FAIL;
} else
fKeyXchg = FALSE;
if(PchGetParam(sz, "admin", FALSE)) {
/* Trying to authenticate using the admin passwd. This requires a
* valid response */
if(fKeyXchg)
goto noresp;
pszName = NULL;
} else if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
strcpy(szResp, "missing name");
return E_FAIL;
} else
pszName = szName;
rgcst[icst].dmplCur = DmplAuthenticateUser(pszName,
&rgcst[icst].luConnectNonce, &luResponse, &fKeyXchg);
if(!rgcst[icst].dmplCur)
return fKeyXchg ? XBDM_KEYXCHG : XBDM_CANNOTACCESS;
return XBDM_NOERR;
}
HRESULT HrSetAdminPasswd(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
int icst;
ULARGE_INTEGER luPasswd;
ULARGE_INTEGER luT;
PULARGE_INTEGER plu;
if(PchGetParam(sz, "none", FALSE)) {
g_fAdminPasswd = FALSE;
WriteIniFile();
return XBDM_NOERR;
}
/* We need to establish a key-exchange key. But if we're running from the
* ini file, we'll just use a key-exchange key of 0 to load in the
* plaintext password */
if(!pdmcc) {
luT.QuadPart = 0;
plu = &luT;
} else {
icst = (PCST)pdmcc - rgcst;
if(!(rgcst[icst].dwStatus & CONN_DHKEY))
return XBDM_KEYXCHG;
plu = &rgcst[icst].luDHKey;
}
if(!FGetQwordParam(sz, "passwd", &luPasswd)) {
strcpy(szResp, "missing passwd");
return E_FAIL;
}
g_luAdminPasswd.LowPart = luPasswd.LowPart ^ plu->LowPart;
g_luAdminPasswd.HighPart = luPasswd.HighPart ^ plu->HighPart;
g_fAdminPasswd = TRUE;
WriteIniFile();
return XBDM_NOERR;
}
HRESULT HrGetUserPriv(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szUserName[128];
DWORD dwPrivileges;
HRESULT hr;
int icst;
dwPrivileges = DmplOfConnection(pdmcc);
if(PchGetParam(sz, "me", FALSE)) {
/* We're interested in the privileges for this connection */
hr = pdmcc ? XBDM_NOERR : E_FAIL;
} else if(!(dwPrivileges & DMPL_PRIV_MANAGE))
hr = XBDM_CANNOTACCESS;
else if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
strcpy(szResp, "missing name");
hr = E_FAIL;
} else
hr = DmGetUserAccess(szUserName, &dwPrivileges);
if(SUCCEEDED(hr)) {
if(dwPrivileges)
FillAccessPrivSz(szResp-1, dwPrivileges);
else
strcpy(szResp, "none");
}
return hr;
}
HRESULT HrSetUserPriv(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
char szUserName[128];
DWORD dwPrivileges;
HRESULT hr;
if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
dwPrivileges = DmplFromSz(sz);
return DmSetUserAccess(szUserName, dwPrivileges);
}
HRESULT HrReportUsers(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
HRESULT hr;
DM_USER dusr;
hr = DmWalkUserList(&pccs->pdmwu, &dusr);
if(!pdmcc->BytesRemaining)
hr = XBDM_ENDOFLIST;
else if(SUCCEEDED(hr))
FillUserInfoSz(pdmcc->Buffer, dusr.UserName, dusr.AccessPrivileges,
NULL);
else {
DmCloseUserList(pccs->pdmwu);
hr = XBDM_ENDOFLIST;
}
return hr;
}
HRESULT HrListUsers(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
CCS *pccs;
if(!g_fLockLevel)
return XBDM_NOTLOCKED;
if(!pdmcc)
return E_FAIL;
pccs = (CCS *)pdmcc->CustomData;
pdmcc->HandlingFunction = HrReportUsers;
pccs->pdmwu = NULL;
return XBDM_MULTIRESPONSE;
}
HRESULT HrPerformDHExchange(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
extern BYTE g_abOakleyGroup1Base[];
extern BYTE g_abOakleyGroup1Mod[];
BYTE rgbDHKey[96];
BYTE rgbY[96];
int icst;
HRESULT hr;
if(pdmcc->CustomData) {
/* We're sending, so send all of the data */
if(pdmcc->BytesRemaining) {
pdmcc->DataSize = 96;
pdmcc->BytesRemaining = 0;
hr = XBDM_NOERR;
} else {
DmFreePool(pdmcc->Buffer);
hr = XBDM_ENDOFLIST;
}
} else if(pdmcc->DataSize) {
/* Grab the data that's here */
memcpy((PUCHAR)pdmcc->Buffer + 2 * 96 - pdmcc->BytesRemaining,
pdmcc->Buffer, pdmcc->DataSize);
pdmcc->BytesRemaining -= pdmcc->DataSize;
if(!pdmcc->BytesRemaining) {
/* OK, now we've got g^x at buffer+96. We need to compute y,
* g^xy, and g^y. First generate y */
XNetRandom(rgbY, 96);
/* Now compute g^xy */
XcModExp((PULONG)rgbDHKey, (PULONG)((PUCHAR)pdmcc->Buffer + 96),
(PULONG)rgbY, (PULONG)g_abOakleyGroup1Mod,
96 / 4);
/* And prepare g^y to be sent */
XcModExp((PULONG)pdmcc->Buffer, (PULONG)g_abOakleyGroup1Base,
(PULONG)rgbY, (PULONG)g_abOakleyGroup1Mod,
96 / 4);
pdmcc->CustomData = pdmcc->Buffer;
/* Now that we have g^xy, we can hash it and mark the key
* exchange complete */
icst = (PCST)pdmcc - rgcst;
rgcst[icst].luDHKey.QuadPart = 0;
XBCHashData(&rgcst[icst].luDHKey, rgbDHKey, sizeof rgbDHKey);
rgcst[icst].dwStatus |= CONN_DHKEY;
/* Now get ready to send g^y */
hr = XBDM_BINRESPONSE;
} else
hr = XBDM_NOERR;
} else {
/* Connection died on receive */
DmFreePool(pdmcc->Buffer);
pdmcc->BytesRemaining = 0;
hr = E_UNEXPECTED;
}
return hr;
}
HRESULT HrKeyExchange(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
if(!pdmcc)
return E_FAIL;
/* We need to allocate a buffer to build up the remote constant --
* we can't let it trickle in via the standard buffer */
pdmcc->BufferSize = 2 * 96;
pdmcc->Buffer = DmAllocatePoolWithTag(pdmcc->BufferSize, 'HDmd');
if(!pdmcc->Buffer)
return E_OUTOFMEMORY;
/* Set up to receive the remote constant */
pdmcc->BytesRemaining = 96;
pdmcc->HandlingFunction = HrPerformDHExchange;
pdmcc->CustomData = NULL;
return XBDM_READYFORBIN;
}
HRESULT HrReportMmGlobal(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
extern PMMGLOBALDATA MmGlobalData;
PMMGLOBALDATA p = MmGlobalData;
ULONG MmHighestPhysicalPage;
PCI_SLOT_NUMBER PCISlotNumber;
MM_STATISTICS mmstat;
UCHAR MemoryTop;
PMMPFN MmPfnDatabase;
HRESULT hr;
int n;
switch (pdmcc->BytesRemaining++) {
case 1:
//
// Access the host bridge's configuration space.
//
PCISlotNumber.u.AsULONG = 0;
PCISlotNumber.u.bits.DeviceNumber = XPCICFG_HOSTBRIDGE_DEVICE_ID;
PCISlotNumber.u.bits.FunctionNumber = XPCICFG_HOSTBRIDGE_FUNCTION_ID;
HalReadPCISpace(0, PCISlotNumber.u.AsULONG, CR_CPU_MEMTOP_LIMIT,
&MemoryTop, sizeof(UCHAR));
MmHighestPhysicalPage = (((ULONG)MemoryTop + 1) * 4096) - 1;
MmPfnDatabase = (PMMPFN)(MM_SYSTEM_PHYSICAL_MAP +
(MiGetPdeAddress(MM_SYSTEM_PHYSICAL_MAP)->Hard.PageFrameNumber << PAGE_SHIFT));
n = _snprintf(pdmcc->Buffer, pdmcc->BufferSize,
"MmHighestPhysicalPage=0x%x RetailPfnRegion=0x%x SystemPteRange=0x%x "
"AvailablePages=0x%x AllocatedPagesByUsage=0x%x PfnDatabase=0x%x",
MmHighestPhysicalPage, p->RetailPfnRegion, p->SystemPteRange,
p->AvailablePages, p->AllocatedPagesByUsage, MmPfnDatabase);
hr = n < 0 ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
break;
case 2:
mmstat.Length = sizeof(mmstat);
MmQueryStatistics(&mmstat);
n = _snprintf(pdmcc->Buffer, pdmcc->BufferSize,
"AddressSpaceLock=0x%x VadRoot=0x%x VadHint=0x%x VadFreeHint=0x%x "
"MmNumberOfPhysicalPages=0x%x MmAvailablePages=0x%x",
p->AddressSpaceLock, p->VadRoot, p->VadHint, p->VadFreeHint,
mmstat.TotalPhysicalPages, mmstat.AvailablePages);
hr = n < 0 ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
break;
default:
hr = XBDM_ENDOFLIST;
}
return hr;
}
HRESULT HrGetMmGlobal(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
pdmcc->HandlingFunction = HrReportMmGlobal;
pdmcc->BytesRemaining = 1;
return XBDM_MULTIRESPONSE;
}
HRESULT HrReturnBuffer(PDM_CMDCONT pdmcc, LPSTR szResponse,
DWORD cchResponse)
{
if(!pdmcc->BytesRemaining) {
DmFreePool(pdmcc->Buffer);
return XBDM_ENDOFLIST;
}
pdmcc->DataSize = pdmcc->BytesRemaining;
pdmcc->BytesRemaining = 0;
return XBDM_NOERR;
}
HRESULT HrGetDvdBlk(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DWORD dwBlk;
CCS *pccs;
HANDLE h;
IO_STATUS_BLOCK iosb;
OBJECT_ATTRIBUTES oa;
OBJECT_STRING ost;
NTSTATUS st;
LARGE_INTEGER li;
PDEVICE_OBJECT pdev;
if(!pdmcc)
return E_FAIL;
if(!FGetNamedDwParam(sz, "block", &dwBlk, szResp))
return E_FAIL;
RtlInitObjectString(&ost, "\\Device\\Cdrom0");
st = ObReferenceObjectByName(&ost, 0, IoDeviceObjectType, NULL, &pdev);
if(!NT_SUCCESS(st))
return E_FAIL;
pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize = 2048);
if(!pdmcc->Buffer) {
ObDereferenceObject(pdev);
return E_FAIL;
}
li.QuadPart = dwBlk;
li.QuadPart *= 2048;
st = IoSynchronousFsdRequest(IRP_MJ_READ, pdev, pdmcc->Buffer,
2048, &li);
ObDereferenceObject(pdev);
if(!NT_SUCCESS(st)) {
ExFreePool(pdmcc->Buffer);
return E_FAIL;
}
pdmcc->HandlingFunction = HrReturnBuffer;
pdmcc->BytesRemaining = 2048;
return XBDM_BINRESPONSE;
}
/* This list must be sorted */
CHH rgchh[] = {
{ "adminpw", DMPL_PRIV_MANAGE, HrSetAdminPasswd },
{ "altaddr", DMPL_PRIV_CONTROL, HrGetAltAddr },
{ "authuser", 0, HrAuthenticateUser },
{ "boxid", 0, HrGetBoxId },
{ "break", DMPL_PRIV_CONTROL, HrDoBreak },
{ "bye", 0, HrEndConversation, 0, CHH_ANYTHREAD },
{ "capcontrol", DMPL_PRIV_CONTROL, HrCAPControl },
{ "continue", DMPL_PRIV_CONTROL, HrContinueThread },
{ "dbgname", 0, HrSetDbgName },
{ "debugger", DMPL_PRIV_CONTROL, HrConnectDebugger },
{ "debugmode", 0, HrSetDebugSecureMode },
{ "dedicate", 0, HrDedicateConnection, 0, CHH_ANYTHREAD },
{ "deftitle", DMPL_PRIV_CONTROL, HrSetDefaultTitle },
{ "delete", 0, HrDeleteFile, DMPL_PRIV_READ | DMPL_PRIV_WRITE },
{ "dirlist", DMPL_PRIV_READ, HrGetDirList },
{ "drivefreespace", DMPL_PRIV_READ, HrGetDriveFreeSpace },
{ "drivelist", DMPL_PRIV_READ, HrGetDriveList },
{ "dvdblk", DMPL_PRIV_READ, HrGetDvdBlk },
{ "flash", DMPL_PRIV_CONFIGURE, HrFlashKernelImage },
{ "fmtfat", DMPL_PRIV_CONFIGURE, HrFormatFAT },
{ "funccall", DMPL_PRIV_CONTROL, HrFunctionCall },
{ "getcontext", DMPL_PRIV_CONTROL, HrGetContext },
{ "getextcontext", DMPL_PRIV_CONTROL, HrGetExtContext },
{ "getfile", DMPL_PRIV_READ, HrSendFile },
{ "getfileattributes", DMPL_PRIV_READ | DMPL_PRIV_WRITE, HrGetFileAttributes },
{ "getmem", DMPL_PRIV_CONTROL, HrGetMemory },
{ "getuserpriv", 0, HrGetUserPriv },
{ "go", DMPL_PRIV_CONTROL, HrGo },
{ "gpucount", DMPL_PRIV_CONTROL, HrToggleGPUCounters },
{ "halt", DMPL_PRIV_CONTROL, HrHaltThread },
{ "irtsweep", DMPL_PRIV_CONTROL, HrIrtSweep },
{ "isbreak", DMPL_PRIV_CONTROL, HrIsBreak },
{ "isstopped", DMPL_PRIV_CONTROL, HrIsStopped },
{ "kd", DMPL_PRIV_CONTROL, HrToggleKDState },
{ "keyxchg", 0, HrKeyExchange },
{ "lockmode", DMPL_PRIV_MANAGE, HrSetLockMode },
{ "magicboot", DMPL_PRIV_CONTROL, HrMagicReboot },
{ "mkdir", DMPL_PRIV_WRITE, HrMkdir },
{ "mmglobal", DMPL_PRIV_CONTROL, HrGetMmGlobal },
{ "modlong", DMPL_PRIV_CONTROL, HrDoLongName },
{ "modsections", DMPL_PRIV_CONTROL, HrListModuleSections },
{ "modules", DMPL_PRIV_CONTROL, HrListModules },
{ "nostopon", DMPL_PRIV_CONTROL, HrNostopon },
{ "notify", DMPL_PRIV_CONTROL, HrSetupNotify },
{ "notifyat", DMPL_PRIV_CONTROL, HrSetupNotifyAt },
{ "pclist", DMPL_PRIV_CONTROL, HrListCounters },
{ "pssnap", DMPL_PRIV_CONTROL, HrPSSnap },
{ "querypc", DMPL_PRIV_CONTROL, HrQueryPerformanceCounter },
{ "reboot", DMPL_PRIV_CONTROL, HrReboot },
{ "rename", 0, HrRenameFile, DMPL_PRIV_READ | DMPL_PRIV_WRITE },
{ "resume", DMPL_PRIV_CONTROL, HrResumeThread },
{ "screenshot", DMPL_PRIV_CONTROL, HrScreenshot },
{ "sendfile", DMPL_PRIV_WRITE, HrReceiveFile },
{ "setconfig", DMPL_PRIV_CONFIGURE, HrSetConfig },
{ "setcontext", DMPL_PRIV_CONTROL, HrSetContext },
{ "setfileattributes", DMPL_PRIV_WRITE, HrSetFileAttributes },
{ "setmem", DMPL_PRIV_CONTROL, HrSetMemory },
{ "setsystime", DMPL_PRIV_CONFIGURE, HrSetSystemTime },
{ "setuserpriv", DMPL_PRIV_MANAGE, HrSetUserPriv },
{ "stop", DMPL_PRIV_CONTROL, HrStop },
{ "stopon", DMPL_PRIV_CONTROL, HrStopon },
{ "suspend", DMPL_PRIV_CONTROL, HrSuspendThread },
{ "sysfileupd", DMPL_PRIV_CONTROL, HrUpdateSystemFile },
{ "systime", 0, HrSystemTime },
{ "threadinfo", DMPL_PRIV_CONTROL, HrThreadInfo },
{ "threads", DMPL_PRIV_CONTROL, HrListThreads },
{ "title", DMPL_PRIV_CONTROL, HrSetTitle },
{ "user", DMPL_PRIV_MANAGE, HrAddUserCommand },
{ "userlist", DMPL_PRIV_MANAGE, HrListUsers },
{ "vssnap", DMPL_PRIV_CONTROL, HrVSSnap },
{ "xbeinfo", DMPL_PRIV_CONTROL, HrXbeInfo },
{ "xtlinfo", DMPL_PRIV_CONTROL, HrXapiInfo },
};
int cchh = sizeof rgchh / sizeof rgchh[0];