-
Notifications
You must be signed in to change notification settings - Fork 2
/
DirtboxThreading.cpp
160 lines (128 loc) · 4.67 KB
/
DirtboxThreading.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Threading - thread local storage and thread information block
#include "DirtboxEmulator.h"
#include "Native.h"
#include <malloc.h>
namespace Dirtbox
{
CRITICAL_SECTION ThreadingLock;
BOOL FreeDescriptors[MAXIMUM_XBOX_THREADS];
NTSTATUS AllocateLdtEntry(PWORD Selector, DWORD Base, DWORD LimitSize);
NTSTATUS FreeLdtEntry(WORD Selector);
}
VOID Dirtbox::InitializeThreading()
{
InitializeCriticalSection(&ThreadingLock);
for(int i = 0; i < MAXIMUM_XBOX_THREADS; i++)
FreeDescriptors[i] = TRUE;
DebugPrint("InitializeThreading: Threading initialized successfully.");
}
WORD Dirtbox::GetFS()
{
__asm
{
mov ax, fs
}
}
// The million dollar question here is: is NT_TIB.StackBase required
// to be aligned?
UINT WINAPI Dirtbox::ShimCallback(PVOID ShimContextPtr)
{
SHIM_CONTEXT ShimContext = *(PSHIM_CONTEXT)ShimContextPtr;
free(ShimContextPtr);
PNT_TIB OldNtTib = (PNT_TIB)__readfsdword(NT_TIB_SELF);
PBYTE Tls;
ETHREAD Ethread;
KPCR Kpcr;
Tls = (PBYTE)_alloca(ShimContext.TlsDataSize);
memset(&Ethread, 0, sizeof(ETHREAD));
memset(&Kpcr, 0, sizeof(KPCR));
// Initialize Ethread structure
Ethread.Tcb.TlsData = Tls;
Ethread.UniqueThread = (PVOID)GetCurrentThreadId();
// Initialize subsystem independent part
Kpcr.NtTib.ExceptionList = OldNtTib->ExceptionList;
// Xbox XAPI assumes that the thread-local storage is located
// at the stack base. (see beginning of function)
Kpcr.NtTib.StackBase = &Tls[ShimContext.TlsDataSize];
Kpcr.NtTib.StackLimit = OldNtTib->StackLimit;
Kpcr.NtTib.ArbitraryUserPointer = (PVOID)GetFS();
Kpcr.NtTib.Self = &Kpcr.NtTib;
// Initialize Xbox subsystem part
Kpcr.SelfPcr = &Kpcr;
Kpcr.Prcb = &Kpcr.PrcbData;
Kpcr.Irql = 0;
Kpcr.Prcb->CurrentThread = (PKTHREAD)&Ethread;
// Allocate LDT entry for new TIB and store selector in old TIB
AllocateLdtEntry(
(PWORD)&OldNtTib->ArbitraryUserPointer, (DWORD)&Kpcr, sizeof(KPCR)
);
SwapTibs();
ShimContext.SystemRoutine(ShimContext.StartRoutine, ShimContext.StartContext);
FatalPrint("ShimCallback: Should never get here.");
return 0;
}
NTSTATUS Dirtbox::AllocateLdtEntry(PWORD Selector, DWORD Base, DWORD LimitSize)
{
DWORD Limit = Base + LimitSize;
LDT_ENTRY LdtEntry;
EnterCriticalSection(&ThreadingLock);
// Locate a free LDT entry
int i;
for(i = 0; i < MAXIMUM_XBOX_THREADS; i++)
if(FreeDescriptors[i])
break;
if(i == MAXIMUM_XBOX_THREADS)
{
LeaveCriticalSection(&ThreadingLock);
DebugPrint("AllocateLdtEntry: Could not locate free LDT entry.");
return STATUS_TOO_MANY_THREADS;
}
// Set up selector information
LdtEntry.BaseLow = (WORD)(Base & 0xFFFF);
LdtEntry.HighWord.Bits.BaseMid = (Base >> 16) & 0xFF;
LdtEntry.HighWord.Bits.BaseHi = (Base >> 24) & 0xFF;
LdtEntry.HighWord.Bits.Type = 0x13; // RW data segment
LdtEntry.HighWord.Bits.Dpl = 3; // user segment
LdtEntry.HighWord.Bits.Pres = 1; // present
LdtEntry.HighWord.Bits.Sys = 0;
LdtEntry.HighWord.Bits.Reserved_0 = 0;
LdtEntry.HighWord.Bits.Default_Big = 1; // 386 segment
LdtEntry.HighWord.Bits.Granularity = 0; // byte-level granularity
LdtEntry.LimitLow = (WORD)(Limit & 0xFFFF);
LdtEntry.HighWord.Bits.LimitHi = (Limit >> 16) & 0xF;
WORD Sel = ((i + 1) << 3) | 0x7;
// Allocate selector
NTSTATUS Res = NtSetLdtEntries(Sel, LdtEntry, 0, 0, 0);
if(!NT_SUCCESS(Res))
{
LeaveCriticalSection(&ThreadingLock);
DebugPrint("AllocateLdtEntry: Could not set LDT entries.");
return Res;
}
FreeDescriptors[i] = FALSE;
*Selector = Sel;
LeaveCriticalSection(&ThreadingLock);
return STATUS_SUCCESS;
}
NTSTATUS Dirtbox::FreeLdtEntry(WORD Selector)
{
LDT_ENTRY LdtEntry;
EnterCriticalSection(&ThreadingLock);
memset(&LdtEntry, 0, sizeof(LDT_ENTRY));
NTSTATUS Res = NtSetLdtEntries(Selector, LdtEntry, 0, 0, 0);
if (!NT_SUCCESS(Res))
{
LeaveCriticalSection(&ThreadingLock);
DebugPrint("FreeLdtEntry: Could not set LDT entries.");
return Res;
}
FreeDescriptors[(Selector >> 3)-1] = TRUE;
LeaveCriticalSection(&ThreadingLock);
return STATUS_SUCCESS;
}
// Assumes that we are in Windows TIB
NTSTATUS Dirtbox::FreeTib()
{
WORD Selector = __readfsword(NT_TIB_USER_POINTER);
return FreeLdtEntry(Selector);
}