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

524 lines
15 KiB
C

/*
**
** dminit.c
**
** Debug module initialization
**
*/
#include "dmp.h"
#include <xboxp.h>
#include "xconfig.h"
#include <xprofp.h>
BOOL fBootWait; // 2 means wait forever, 3 means wait once with no timeout
PKPROCESS pprocSystem;
DMINIT g_dmi;
DMGD g_dmgd;
DMXAPI g_dmxapi;
char rgchTitleDir[MAX_OBJ_PATH+1];
char rgchTitleName[64];
char *pszCmdLine;
RTL_CRITICAL_SECTION csIniFile;
HANDLE g_hIniFile;
BOOL g_fFinishedLoadingIniFile;
HAL_SHUTDOWN_REGISTRATION hsrAPIC = { DisableAPIC, 0 };
ULONG g_ulDbgIP;
KEVENT kevtNull;
// This is what we're calling our ini file for now. It will change someday
char szIniName[] = "E:\\xbdm.ini";
char szDxtDir[] = "E:\\dxt";
OCHAR oszDxtTemplate[] = OTEXT("*.DXT");
VOID CreateThreadNotify(HANDLE pid, HANDLE tid, BOOLEAN fCreate);
void LoadIniFile(void);
void LoadPlugIns(void);
ULONG DmEntryPoint(PVOID pvXbdmBase, PDMINIT pdmi, ULONG unused)
{
WSADATA wsad;
NTSTATUS st;
KIRQL irqlSav;
// Pass a pointer to the kernel's global profile data structure
DmProfileData = (XProfpGlobals*)pdmi->XProfpDataPtr;
/* Finish the process of fixing up our image */
if(!FFixupXbdm(pvXbdmBase, pdmi))
return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
/* Set things up */
if(!FInitThreadDebugData(PsGetCurrentThread()))
return STATUS_NO_MEMORY;
pprocSystem = PsGetCurrentProcess();
g_dmi = *pdmi;
g_dmi.D3DDriverData = &g_dmgd;
g_dmi.XapiData = &g_dmxapi;
InitializeCriticalSection(&csIniFile);
HalRegisterShutdownNotification(&hsrAPIC, TRUE);
KeInitializeEvent(&kevtNull, SynchronizationEvent, TRUE);
InitSecurity();
InitBreakpoints();
InitNotifications();
InitServ();
InitLoader();
InitCounters();
KeGetCurrentPrcb()->DebugMonitorData = &g_dmi;
dwExecState = DMN_EXEC_PENDING;
// register for thread notifications (so we can keep our per-thread data)
st = PsSetCreateThreadNotifyRoutine(CreateThreadNotify);
if(!NT_SUCCESS(st)) {
ASSERT(FALSE);
return st;
}
/* If we've loaded from CD, mark our paths correctly */
if(pdmi->Flags & DMIFLAG_CDBOOT)
szIniName[0] = szDxtDir[0] = 1;
// Load the ini file
LoadIniFile();
// start networking
st = XNetStartup(NULL);
ASSERT(st == NO_ERROR);
// start winsock
if(0 != WSAStartup(2, &wsad))
return STATUS_NO_MEMORY;
/* Start our listening thread */
if(!FStartDebugService())
return STATUS_NO_MEMORY;
/* Reconnect persistent notification channels. We're not necessarily
* ready to start getting commands from them, but we won't give them any
* reason to do anything yet */
EnsurePersistentSockets();
/* Load all plugins */
LoadPlugIns();
/* If we're in an active debug session, we need to wait until we've
* reconnected with the host */
if(fBootWait && DmGetCurrentDmtd()->DebugEvent) {
LARGE_INTEGER li;
BOOL fWait = fBootWait;
DbgPrint("dm: waiting for debugger connection\n");
if(fBootWait != 2) {
/* One wait is all we'll do */
fBootWait = FALSE;
WriteIniFile();
}
DmGetCurrentDmtd()->DebugFlags |= DMFLAG_STOPPED;
NotifyComponents(DM_EXEC, dwExecState = DMN_EXEC_PENDING);
/* We'll sit in a wait state for only fifteen seconds unless we've been
* told to wait forever */
li.QuadPart = Int32x32To64(15000, -10000);
st = KeWaitForSingleObject(DmGetCurrentDmtd()->DebugEvent, UserRequest,
UserMode, FALSE, fWait == 1 ? &li : NULL);
if(st == STATUS_TIMEOUT)
NotifyComponents(DM_EXEC, dwExecState = DMN_EXEC_START);
DmGetCurrentDmtd()->DebugFlags &= ~(DMFLAG_STOPPED | DMFLAG_EXCEPTION);
} else
NotifyComponents(DM_EXEC, dwExecState = DMN_EXEC_START);
/* If we have a specified title directory, let's ensure it's a valid
* directory and then map it in */
FTitleExists(*rgchTitleDir ? rgchTitleDir : 0, rgchTitleName, pszCmdLine,
TRUE);
irqlSav = KeRaiseIrqlToDpcLevel();
if(pszCmdLine) {
DmFreePool(pszCmdLine);
pszCmdLine = NULL;
}
KeLowerIrql(irqlSav);
*pdmi = g_dmi;
return STATUS_SUCCESS;
}
VOID CreateThreadNotify(HANDLE pid, HANDLE tid, BOOLEAN fCreate)
{
if(fCreate) {
NTSTATUS st;
PETHREAD pthr;
st = PsLookupThreadByThreadId(tid, &pthr);
if(NT_SUCCESS(st)) {
if(!FInitThreadDebugData(pthr))
DbgPrint("dm: could not alloc thread data\n");
else if(pthr->Tcb.TlsData) {
/* We want to advertise this thread's creation, but we need to
* do that within the context of the created thread, not our
* thread, so we replace the thread's system routine with our
* own, which will send the appropriate notifications */
PDMTD pdmtd = (PDMTD)pthr->DebugData;
PKSWITCHFRAME pksf = (PKSWITCHFRAME)pthr->Tcb.KernelStack;
PKSYSTEM_ROUTINE *ppfn = (PVOID)(pksf + 1);
pdmtd->DebugFlags |= DMFLAG_STARTTHREAD;
pdmtd->SystemStartupRoutine = *ppfn;
*ppfn = DmpThreadStartup;
}
ObDereferenceObject(pthr);
} else
DbgPrint("dm: could not get new thread\n");
} else {
DMTD *pdmtd;
/* Advertise this thread's destruction */
if(PsGetCurrentThread()->Tcb.TlsData)
NotifyComponents(DM_DESTROYTHREAD,
(DWORD)PsGetCurrentThread()->UniqueThread);
pdmtd = PsGetCurrentThread()->DebugData;
if(pdmtd) {
PsGetCurrentThread()->DebugData = NULL;
if(pdmtd->ExceptionStack) {
MmDeleteKernelStack(pdmtd->ExceptionStack,
(PUCHAR)pdmtd->ExceptionStack - 0x2000);
}
if(pdmtd->DebugEvent) {
pdmtd->DebugEvent = NULL;
/* There shouldn't be anybody waiting on this event */
ASSERT(IsListEmpty(&pdmtd->DebugEventData.Header.WaitListHead));
}
DmFreePool(pdmtd);
}
}
}
VOID SetIPParam(ULONG ulValueIndex, LPCSTR pszStaticIP)
{
ULONG ulAddr = htonl(UlAddrFromSz(pszStaticIP));
ExSaveNonVolatileSetting(ulValueIndex, REG_DWORD, &ulAddr, sizeof(ulAddr));
}
BOOL FInitThreadDebugData(PETHREAD pthr)
{
PDMTD pdmtd;
/* Allocate a new thread data structure, if we don't already have one */
pdmtd = pthr->DebugData;
if(!pdmtd) {
pdmtd = pthr->DebugData = DmAllocatePoolWithTag(sizeof(DMTD), 'dtmD');
if(!pdmtd)
return FALSE;
RtlZeroMemory(pdmtd, sizeof(DMTD));
/* Allocate an 8k stack on which we can process exceptions. If this
* allocation fails, we'll run exceptions on the thread's stack and
* hope we don't run out of stack */
pdmtd->ExceptionStack = MmCreateKernelStack(0x2000, TRUE);
}
/* Mark this as an XAPI thread */
if(pthr->Tcb.TlsData)
pdmtd->DebugFlags |= DMFLAG_XAPITHREAD;
/* Set up a synchronization event for exception notification */
if(pdmtd->DebugEvent) {
ASSERT(FALSE);
return TRUE;
}
KeInitializeEvent(&pdmtd->DebugEventData, NotificationEvent, FALSE);
pdmtd->DebugEvent = &pdmtd->DebugEventData;
return TRUE;
}
void DoIniLine(LPCSTR sz)
{
char rgchIPAddr[20];
int cchCmd = CchOfWord(sz);
if(cchCmd == 0); // nothing to do
else if(FEqualRgch("wait", sz, cchCmd)) {
if(PchGetParam(sz, "forever", FALSE))
fBootWait = 2;
else if(PchGetParam(sz, "stop", FALSE))
fBootWait = 3;
else
fBootWait = 1;
} else if(FEqualRgch("notify", sz, cchCmd)) {
/* Set up a notification channel here */
} else if(FEqualRgch("notifyat", sz, cchCmd)) {
FNotifyAtCmd(INVALID_SOCKET, sz, TRUE);
} else if(FEqualRgch("staticip", sz, cchCmd)) {
FGetDwParam(sz, "addr", &g_ulDbgIP);
#if 0
// deprecated network commands
} else if(FEqualRgch("staticip", sz, cchCmd)) {
if(!FGetSzParam(sz, "addr", rgchIPAddr, sizeof rgchIPAddr)) {
*rgchIPAddr = 0;
}
SetIPParam(XC_ONLINE_IP_ADDRESS, rgchIPAddr);
} else if(FEqualRgch("subnetmask", sz, cchCmd)) {
if(!FGetSzParam(sz, "addr", rgchIPAddr, sizeof rgchIPAddr)) {
*rgchIPAddr = 0;
}
SetIPParam(XC_ONLINE_SUBNET_ADDRESS, rgchIPAddr);
} else if(FEqualRgch("defgateway", sz, cchCmd)) {
if(!FGetSzParam(sz, "addr", rgchIPAddr, sizeof rgchIPAddr)) {
*rgchIPAddr = 0;
}
SetIPParam(XC_ONLINE_DEFAULT_GATEWAY_ADDRESS, rgchIPAddr);
#endif
} else
/* Process this as a normal command if we can */
HandleCommand(-1, sz);
}
void LoadIniFile(void)
{
NTSTATUS st;
IO_STATUS_BLOCK iosb;
char *rgchBuf;
char *pch;
char *pchLine;
int cb;
const int cbBuf = 0x1000;
/* Make sure we can get a read buffer */
rgchBuf = DmAllocatePoolWithTag(cbBuf + 1, 'mdbX');
if(!rgchBuf)
return;
RtlEnterCriticalSection(&csIniFile);
/* We leave the ini file open so that we can keep a reference to the FCB
* and restrict access to the ini file if somebody tries to copy it */
st = FCreateFile(&g_hIniFile, GENERIC_READ | SYNCHRONIZE, szIniName,
NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
/* Repeatedly read in chunks of file until there's no more file */
cb = 0;
for(;;) {
st = NtReadFile(g_hIniFile, NULL, NULL, NULL, &iosb, rgchBuf + cb,
cbBuf - cb, NULL);
if(NT_SUCCESS(st))
cb += iosb.Information;
if(!cb)
break;
/* Keep pulling lines out of this block to process */
pchLine = rgchBuf;
for(pch = rgchBuf; pch < rgchBuf + cb; ++pch) {
if(*pch == '\012' || *pch == '\015') {
*pch = 0;
DoIniLine(pchLine);
pchLine = pch + 1;
}
}
/* If we've miraculously failed to hit any line breaks in the
* data we have, we'll treat the whole thing as one big line */
if(pchLine == rgchBuf) {
*pch = 0;
DoIniLine(pchLine);
cb = 0;
} else {
/* We've processed a bunch of lines and have a partial line
* left over, so set it aside and get ready to read more
* file */
memmove(rgchBuf, pchLine, rgchBuf + cb - pchLine);
cb -= pchLine - rgchBuf;
}
}
}
g_fFinishedLoadingIniFile = TRUE;
RtlLeaveCriticalSection(&csIniFile);
DmFreePool(rgchBuf);
}
#if 0
void LoadPlugIn(const OCHAR *oszName)
{
PVOID pvImage;
HANDLE h;
if(SUCCEEDED(DmLoadExtension(oszName, &h, &pvImage))) {
PDM_ENTRYPROC pfn;
pfn = (PDM_ENTRYPROC)((ULONG_PTR)pvImage +
RtlImageNtHeader(pvImage)->OptionalHeader.AddressOfEntryPoint);
//_asm int 3
pfn();
}
}
#endif
void LoadPlugIns(void)
{
HANDLE h;
NTSTATUS st;
IO_STATUS_BLOCK iosb;
FILE_DIRECTORY_INFORMATION fda;
OCHAR oszName[256];
struct
{
FILE_DIRECTORY_INFORMATION fna;
OCHAR ozName[256];
} fna;
BOOLEAN fFirst = TRUE;
OBJECT_STRING templateName;
st = FCreateFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szDxtDir, NULL,
0, FILE_SHARE_READ, FILE_OPEN, FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
templateName.Buffer = oszDxtTemplate;
templateName.Length = sizeof oszDxtTemplate - sizeof(OCHAR);
templateName.MaximumLength = sizeof oszDxtTemplate;
do {
st = NtQueryDirectoryFile(h, NULL, NULL, NULL, &iosb, &fna.fna,
sizeof fna, FileDirectoryInformation, &templateName, fFirst);
if(NT_SUCCESS(st)) {
int cch = fna.fna.FileNameLength / sizeof(OCHAR);
soprintf(oszName, OTEXT("%s\\%*.*s"),
szDxtDir, cch, cch, fna.fna.FileName);
DmLoadExtension(oszName, NULL, NULL);
}
fFirst = FALSE;
} while (NT_SUCCESS(st));
NtClose(h);
}
}
void WriteIniData(INF *pinf, const BYTE *pb, int cb)
{
IO_STATUS_BLOCK iosb;
if(pb) {
while(cb) {
int cbT = pinf->cbBuf - pinf->cbUsed;
if(cb >= cbT) {
memcpy(pinf->pbBuf + pinf->cbUsed, pb, cbT);
NtWriteFile(pinf->h, NULL, NULL, NULL, &iosb, pinf->pbBuf,
pinf->cbBuf, NULL);
pb += cbT;
cb -= cbT;
pinf->cbUsed = 0;
} else {
memcpy(pinf->pbBuf + pinf->cbUsed, pb, cb);
pb += cb;
pinf->cbUsed += cb;
cb = 0;
}
}
} else {
/* Flush */
NtWriteFile(pinf->h, NULL, NULL, NULL, &iosb, pinf->pbBuf,
pinf->cbUsed, NULL);
pinf->cbUsed = 0;
}
}
void WriteIniSzNoCRLF(INF *pinf, LPCSTR sz)
{
WriteIniData(pinf, sz, strlen(sz));
}
void WriteIniSz(INF *pinf, LPCSTR sz)
{
WriteIniSzNoCRLF(pinf, sz);
WriteIniData(pinf, "\015\012", 2);
}
void WriteIniFile(void)
{
NTSTATUS st;
INF inf;
char sz[64];
/* We don't write if we're a CD boot. We also don't write if we haven't
* finished reading the ini file yet */
if((g_dmi.Flags & DMIFLAG_CDBOOT) || !g_fFinishedLoadingIniFile)
return;
inf.cbBuf = 0x1000;
inf.cbUsed = 0;
/* Make sure we can get a write buffer */
inf.pbBuf = DmAllocatePoolWithTag(inf.cbBuf, 'mdbX');
if(!inf.pbBuf)
return;
RtlEnterCriticalSection(&csIniFile);
if(g_hIniFile)
NtClose(g_hIniFile);
st = FCreateFile(&inf.h, GENERIC_WRITE | SYNCHRONIZE, szIniName, NULL,
0, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(st)) {
g_hIniFile = inf.h;
WriteIniSz(&inf, "[xbdm]");
/* Write all the data we know about */
switch(fBootWait) {
case 1:
WriteIniSz(&inf, "wait");
break;
case 2:
WriteIniSz(&inf, "wait forever");
break;
case 3:
WriteIniSz(&inf, "wait stop");
break;
}
#if 0
/* For now, titledir is not reboot persistent */
if(rgchTitleDir[0]) {
WriteIniSzNoCRLF(&inf, "titledir name=");
WriteIniSz(&inf, rgchTitleDir);
}
#endif
if(rgchDbgName[0]) {
WriteIniSzNoCRLF(&inf, "dbgname name=");
WriteIniSz(&inf, rgchDbgName);
}
if(g_ulDbgIP) {
sprintf(sz, "staticip addr=0x%08x", g_ulDbgIP);
WriteIniSz(&inf, sz);
}
WriteIniNotifiers(&inf);
WriteIniSecurity(&inf);
/* Flush */
WriteIniData(&inf, NULL, 0);
} else
g_hIniFile = NULL;
RtlLeaveCriticalSection(&csIniFile);
DmFreePool(inf.pbBuf);
}
__declspec(naked) void DisableAPIC(PHAL_SHUTDOWN_REGISTRATION phsr)
{
_asm {
// Find the local APIC
xor edx, edx
xor eax, eax
mov ecx, 0x1b
rdmsr
// Do nothing if already disabled
test ax, 0x800
jz disabled
// Disable it -- shouldn't need to mark as s/w disabled
and ah, 0xf7
wrmsr
disabled:
ret 4
}
}
/* And here are some functions just to replace missing kernel functions */
#undef PsGetCurrentThreadId
HANDLE PsGetCurrentThreadId( VOID )
{
return PsGetCurrentThread()->UniqueThread;
}