Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

729 lines
18 KiB

  1. /*++
  2. Copyright (c) 1995-1998 Microsoft Corporation
  3. Module Name:
  4. tc.c
  5. Abstract:
  6. This module implements the Translation Cache, where Intel code is
  7. translated into native code.
  8. Author:
  9. Dave Hastings (daveh) creation-date 26-Jul-1995
  10. Revision History:
  11. 24-Aug-1999 [askhalid] copied from 32-bit wx86 directory and make work for 64bit.
  12. --*/
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <ntldr.h>
  17. #include <windows.h>
  18. #define _WX86CPUAPI_
  19. #include "wx86.h"
  20. #include "wx86nt.h"
  21. #include "wx86cpu.h"
  22. #include "cpuassrt.h"
  23. #include "config.h"
  24. #include "tc.h"
  25. #include "entrypt.h"
  26. #include "mrsw.h"
  27. #include "cpunotif.h"
  28. #include "cpumain.h"
  29. #include "instr.h"
  30. #include "threadst.h"
  31. #include "frag.h"
  32. #include "atomic.h"
  33. #ifdef CODEGEN_PROFILE
  34. #include <coded.h>
  35. #endif
  36. ASSERTNAME;
  37. #if MIPS
  38. #define DBG_FILL_VALUE 0x73737373 // an illegal instruction
  39. #else
  40. #define DBG_FILL_VALUE 0x01110111
  41. #endif
  42. #ifdef CODEGEN_PROFILE
  43. extern DWORD EPSequence;
  44. #endif
  45. //
  46. // Descriptor for a range of the Translation Cache.
  47. //
  48. typedef struct _CacheInfo {
  49. PBYTE StartAddress; // base address for the cache
  50. LONGLONG MaxSize; // max size of the cache (in bytes)
  51. LONGLONG MinCommit; // min amount that can be committed (bytes)
  52. LONGLONG NextIndex; // next free address in the cache
  53. LONGLONG CommitIndex; // next uncommitted address in the cache
  54. LONGLONG ChunkSize; // amount to commit by
  55. ULONG LastCommitTime; // time of last commit
  56. } CACHEINFO, *PCACHEINFO;
  57. //
  58. // Pointers to the start and end of the function prolog for StartTranslatedCode
  59. //
  60. extern CHAR StartTranslatedCode[];
  61. extern CHAR StartTranslatedCodePrologEnd[];
  62. ULONG TranslationCacheTimestamp = 1;
  63. CACHEINFO DynCache; // Descriptor for dynamically allocated TC
  64. RUNTIME_FUNCTION DynCacheFunctionTable;
  65. BOOL fTCInitialized;
  66. extern DWORD TranslationCacheFlags;
  67. BOOL
  68. InitializeTranslationCache(
  69. VOID
  70. )
  71. /*++
  72. Routine Description:
  73. Per-process initialization for the Translation Cache.
  74. Arguments:
  75. .
  76. Return Value:
  77. .
  78. --*/
  79. {
  80. NTSTATUS Status;
  81. ULONGLONG pNewAllocation;
  82. ULONGLONG RegionSize;
  83. LONG PrologSize;
  84. //
  85. // Initialize non-zero fields in the CACHEINFO
  86. //
  87. DynCache.MaxSize = CpuCacheReserve;
  88. DynCache.MinCommit = CpuCacheCommit;
  89. DynCache.ChunkSize = CpuCacheChunkSize;
  90. //
  91. // Reserve DynCache.MaxSize bytes of memory.
  92. //
  93. RegionSize = DynCache.MaxSize;
  94. Status = NtAllocateVirtualMemory(NtCurrentProcess(),
  95. &(PVOID)DynCache.StartAddress,
  96. 0,
  97. (ULONGLONG *)&DynCache.MaxSize,
  98. MEM_RESERVE,
  99. PAGE_EXECUTE_READWRITE
  100. );
  101. if (!NT_SUCCESS(Status)) {
  102. return FALSE;
  103. }
  104. //
  105. // Commit enough memory to store the function prolog.
  106. //
  107. pNewAllocation = (ULONGLONG)DynCache.StartAddress;
  108. Status = NtAllocateVirtualMemory(NtCurrentProcess(),
  109. &(PVOID)pNewAllocation,
  110. 0,
  111. &DynCache.MinCommit,
  112. MEM_COMMIT,
  113. PAGE_READWRITE);
  114. if (!NT_SUCCESS(Status)) {
  115. //
  116. // Commit failed. Free the reserve and bail.
  117. //
  118. ErrorFreeReserve:
  119. RegionSize = 0;
  120. NtFreeVirtualMemory(NtCurrentProcess(),
  121. &(PVOID)DynCache.StartAddress,
  122. &RegionSize,
  123. MEM_RELEASE
  124. );
  125. return FALSE;
  126. }
  127. #if DBG
  128. //
  129. // Fill the TC with a unique illegal value, so we can distinguish
  130. // old code from new code and detect overwrites.
  131. //
  132. RtlFillMemoryUlong(DynCache.StartAddress, DynCache.MinCommit, DBG_FILL_VALUE);
  133. #endif
  134. //
  135. // Copy the prolog from StartTranslatedCode into the start of the cache.
  136. //
  137. PrologSize = (LONG)(StartTranslatedCodePrologEnd - StartTranslatedCode);
  138. CPUASSERT(PrologSize >= 0 && PrologSize < MAX_PROLOG_SIZE);
  139. RtlCopyMemory(DynCache.StartAddress, StartTranslatedCode, PrologSize);
  140. //
  141. // Notify the exception unwinder that this memory is going to contain
  142. // executable code.
  143. //
  144. DynCacheFunctionTable.BeginAddress = (UINT_PTR)DynCache.StartAddress;
  145. DynCacheFunctionTable.EndAddress = (UINT_PTR)(DynCache.StartAddress + DynCache.MaxSize);
  146. DynCacheFunctionTable.ExceptionHandler = NULL;
  147. DynCacheFunctionTable.HandlerData = NULL;
  148. DynCacheFunctionTable.PrologEndAddress = (UINT_PTR)(DynCache.StartAddress + MAX_PROLOG_SIZE);
  149. if (RtlAddFunctionTable(&DynCacheFunctionTable, 1) == FALSE) {
  150. goto ErrorFreeReserve;
  151. }
  152. //
  153. // Adjust the DynCache.StartAddress up by MAX_PROLOG_SIZE so cache
  154. // flushes don't erase it.
  155. //
  156. DynCache.StartAddress += MAX_PROLOG_SIZE;
  157. fTCInitialized = TRUE;
  158. return TRUE;
  159. }
  160. PCHAR
  161. AllocateFromCache(
  162. PCACHEINFO Cache,
  163. ULONG Size
  164. )
  165. /*++
  166. Routine Description:
  167. Allocate space within a Translation Cache. If there is insufficient
  168. space, the allocation will fail.
  169. Arguments:
  170. Cache - Data about the cache
  171. Size - Size of the allocation request, in bytes
  172. Return Value:
  173. Pointer to DWORD-aligned memory of 'Size' bytes. NULL if insufficient
  174. space.
  175. --*/
  176. {
  177. PBYTE Address;
  178. // Ensure parameters and cache state are acceptable
  179. CPUASSERTMSG((Cache->NextIndex & 3)==0, "Cache not DWORD aligned");
  180. CPUASSERTMSG(Cache->NextIndex == 0 || *(DWORD *)&Cache->StartAddress[Cache->NextIndex-4] != DBG_FILL_VALUE, "Cache Corrupted");
  181. CPUASSERT(Cache->NextIndex == Cache->CommitIndex || *(DWORD *)&Cache->StartAddress[Cache->NextIndex] == DBG_FILL_VALUE);
  182. if ((Cache->NextIndex + Size) >= Cache->MaxSize) {
  183. //
  184. // Not enough space in the cache.
  185. //
  186. return FALSE;
  187. }
  188. Address = &Cache->StartAddress[Cache->NextIndex];
  189. Cache->NextIndex += Size;
  190. if (Cache->NextIndex > Cache->CommitIndex) {
  191. //
  192. // Need to commit more of the cache
  193. //
  194. LONGLONG RegionSize;
  195. NTSTATUS Status;
  196. PVOID pAllocation;
  197. ULONG CommitTime = NtGetTickCount();
  198. if (Cache->LastCommitTime) {
  199. if ((CommitTime-Cache->LastCommitTime) < CpuCacheGrowTicks) {
  200. //
  201. // Commits are happening too frequently. Bump up the size of
  202. // each commit.
  203. //
  204. if (Cache->ChunkSize < CpuCacheChunkMax) {
  205. Cache->ChunkSize *= 2;
  206. }
  207. } else if ((CommitTime-Cache->LastCommitTime) > CpuCacheShrinkTicks) {
  208. //
  209. // Commits are happening too slowly. Reduce the size of each
  210. // Commit.
  211. //
  212. if (Cache->ChunkSize > CpuCacheChunkMin) {
  213. Cache->ChunkSize /= 2;
  214. }
  215. }
  216. }
  217. RegionSize = Cache->ChunkSize;
  218. if (RegionSize < Size) {
  219. //
  220. // The commit size is smaller than the requested allocation.
  221. // Commit enough to satisfy the allocation plus one more like it.
  222. //
  223. RegionSize = Size*2;
  224. }
  225. if (RegionSize+Cache->CommitIndex >= Cache->MaxSize) {
  226. //
  227. // The ChunkSize is larger than the remaining free space in the
  228. // cache. Use whatever space is left.
  229. //
  230. RegionSize = Cache->MaxSize - Cache->CommitIndex;
  231. }
  232. pAllocation = &Cache->StartAddress[Cache->CommitIndex];
  233. Status = NtAllocateVirtualMemory(NtCurrentProcess(),
  234. &pAllocation,
  235. 0,
  236. &RegionSize,
  237. MEM_COMMIT,
  238. PAGE_READWRITE);
  239. if (!NT_SUCCESS(Status)) {
  240. //
  241. // Commit failed. Caller may flush the caches in order to
  242. // force success (as the static cache has no commit).
  243. //
  244. return NULL;
  245. }
  246. CPUASSERT((pAllocation == (&Cache->StartAddress[Cache->CommitIndex])))
  247. #if DBG
  248. //
  249. // Fill the TC with a unique illegal value, so we can distinguish
  250. // old code from new code and detect overwrites.
  251. //
  252. RtlFillMemoryUlong(&Cache->StartAddress[Cache->CommitIndex],
  253. RegionSize,
  254. DBG_FILL_VALUE
  255. );
  256. #endif
  257. Cache->CommitIndex += RegionSize;
  258. Cache->LastCommitTime = CommitTime;
  259. }
  260. return Address;
  261. }
  262. VOID
  263. FlushCache(
  264. PCACHEINFO Cache
  265. )
  266. /*++
  267. Routine Description:
  268. Flush out a Translation Cache.
  269. Arguments:
  270. Cache - cache to flush
  271. Return Value:
  272. .
  273. --*/
  274. {
  275. NTSTATUS Status;
  276. ULONGLONG RegionSize;
  277. PVOID pAllocation;
  278. //
  279. // Only decommit pages if the current commit size is >= the size
  280. // we want to shrink to. It may not be that big if somebody called
  281. // CpuFlushInstructionCache() before the commit got too big.
  282. //
  283. if (Cache->CommitIndex > Cache->MinCommit) {
  284. Cache->LastCommitTime = NtGetTickCount();
  285. RegionSize = Cache->CommitIndex - Cache->MinCommit;
  286. pAllocation = &Cache->StartAddress[Cache->MinCommit];
  287. Status = NtFreeVirtualMemory(NtCurrentProcess(),
  288. &pAllocation,
  289. &RegionSize,
  290. MEM_DECOMMIT);
  291. if (!NT_SUCCESS(Status)) {
  292. LOGPRINT((ERRORLOG, "NtFreeVM(%x, %x) failed %x\n",
  293. &Cache->StartAddress[Cache->MinCommit],
  294. Cache->CommitIndex - Cache->MinCommit,
  295. Status));
  296. ProxyDebugBreak();
  297. }
  298. CPUASSERTMSG(NT_SUCCESS(Status), "Failed to decommit TranslationCache chunk");
  299. Cache->CommitIndex = Cache->MinCommit;
  300. }
  301. #if DBG
  302. //
  303. // Fill the Cache with a unique illegal value, so we can
  304. // distinguish old code from new code and detect overwrites.
  305. //
  306. RtlFillMemoryUlong(Cache->StartAddress, Cache->CommitIndex, DBG_FILL_VALUE);
  307. #endif
  308. Cache->NextIndex = 0;
  309. }
  310. PCHAR
  311. AllocateTranslationCache(
  312. ULONG Size
  313. )
  314. /*++
  315. Routine Description:
  316. Allocate space within the Translation Cache. If there is insufficient
  317. space, the cache will be flushed. Allocations are guaranteed to
  318. succeed.
  319. Arguments:
  320. Size - Size of the allocation request, in bytes
  321. Return Value:
  322. Pointer to DWORD-aligned memory of 'Size' bytes. Always non-NULL.
  323. --*/
  324. {
  325. PCHAR Address;
  326. //
  327. // Check parameters
  328. //
  329. CPUASSERT(Size <= CpuCacheReserve);
  330. CPUASSERTMSG((Size & 3) == 0, "Requested allocation size DWORD-aligned")
  331. //
  332. // Make sure there is only one thread with access to the translation
  333. // cache.
  334. //
  335. CPUASSERT( (MrswTC.Counters.WriterCount > 0 && MrswTC.WriterThreadId == ProxyGetCurrentThreadId()) ||
  336. (MrswEP.Counters.WriterCount > 0 && MrswEP.WriterThreadId == ProxyGetCurrentThreadId()) );
  337. //
  338. // Try to allocate from the cache
  339. //
  340. Address = AllocateFromCache(&DynCache, Size);
  341. if (!Address) {
  342. //
  343. // Translation cache is full - time to flush Translation Cache
  344. // (Both Dyn and Stat caches go at once).
  345. //
  346. #ifdef CODEGEN_PROFILE
  347. DumpAllocFailure();
  348. #endif
  349. FlushTranslationCache(0, 0xffffffff);
  350. Address = AllocateFromCache(&DynCache, Size);
  351. CPUASSERT(Address); // Alloc from cache after a flush
  352. }
  353. return Address;
  354. }
  355. VOID
  356. FreeUnusedTranslationCache(
  357. PCHAR StartOfFree
  358. )
  359. /*++
  360. Routine Description:
  361. After allocating from the TranlsationCache, a caller can free the tail-
  362. end of the last allocation.
  363. Arguments:
  364. StartOfFree -- address of first unused byte in the last allocation
  365. Return Value:
  366. .
  367. --*/
  368. {
  369. CPUASSERT(StartOfFree > (PCHAR)DynCache.StartAddress &&
  370. StartOfFree < (PCHAR)DynCache.StartAddress + DynCache.NextIndex);
  371. DynCache.NextIndex = StartOfFree - DynCache.StartAddress;
  372. }
  373. VOID
  374. FlushTranslationCache(
  375. PVOID IntelAddr,
  376. DWORD IntelLength
  377. )
  378. /*++
  379. Routine Description:
  380. Indicates that a range of Intel memory has changed and that any
  381. native code in the cache which corresponds to that Intel memory is stale
  382. and needs to be flushed.
  383. The caller *must* have the EP write lock before calling. This routine
  384. locks the TC for write, then unlocks the TC when done.
  385. IntelAddr = 0, IntelLength = 0xffffffff guarantees the entire cache is
  386. flushed.
  387. Arguments:
  388. IntelAddr -- Intel address of the start of the range to flush
  389. IntelLength -- Length (in bytes) of memory to flush
  390. Return Value:
  391. .
  392. --*/
  393. {
  394. if (IntelLength == 0xffffffff ||
  395. IsIntelRangeInCache(IntelAddr, IntelLength)) {
  396. DECLARE_CPU;
  397. //
  398. // Tell active readers to bail out of the Translation Cache, then
  399. // get the TC write lock. The MrswWriterEnter() call will block
  400. // until the last active reader leaves the cache.
  401. //
  402. InterlockedIncrement(&ProcessCpuNotify);
  403. MrswWriterEnter(&MrswTC);
  404. InterlockedDecrement(&ProcessCpuNotify);
  405. //
  406. // Bump the timestamp
  407. //
  408. TranslationCacheTimestamp++;
  409. #ifdef CODEGEN_PROFILE
  410. //
  411. // Write the contents of the translation cache and entrypoints to
  412. // disk.
  413. //
  414. DumpCodeDescriptions(TRUE);
  415. EPSequence = 0;
  416. #endif
  417. //
  418. // Flush the per-process data structures. Per-thread data structures
  419. // should be flushed in the CpuSimulate() loop by examining the
  420. // value of TranslationCacheTimestamp.
  421. //
  422. FlushEntrypoints();
  423. FlushIndirControlTransferTable();
  424. FlushCallstack(cpu);
  425. FlushCache(&DynCache);
  426. TranslationCacheFlags = 0;
  427. //
  428. // Allow other threads to become TC readers again.
  429. //
  430. MrswWriterExit(&MrswTC);
  431. }
  432. }
  433. VOID
  434. CpuFlushInstructionCache(
  435. PVOID IntelAddr,
  436. DWORD IntelLength
  437. )
  438. /*++
  439. Routine Description:
  440. Indicates that a range of Intel memory has changed and that any
  441. native code in the cache which corresponds to that Intel memory is stale
  442. and needs to be flushed.
  443. IntelAddr = 0, IntelLength = 0xffffffff guarantees the entire cache is
  444. flushed.
  445. Arguments:
  446. IntelAddr -- Intel address of the start of the range to flush
  447. IntelLength -- Length (in bytes) of memory to flush
  448. Return Value:
  449. .
  450. --*/
  451. {
  452. if (!fTCInitialized) {
  453. // we may be called before the CpuProcessInit() has been run if
  454. // a Dll is mapped because of a forwarder from one Dll to another.
  455. return;
  456. }
  457. MrswWriterEnter(&MrswEP);
  458. FlushTranslationCache(IntelAddr, IntelLength);
  459. MrswWriterExit(&MrswEP);
  460. }
  461. VOID
  462. CpuStallExecutionInThisProcess(
  463. VOID
  464. )
  465. /*++
  466. Routine Description:
  467. Get all threads out of the Translation Cache and into a state where
  468. their x86 register sets are accessible via the Get/SetReg APIs.
  469. The caller is guaranteed to call CpuResumeExecutionInThisProcess()
  470. a short time after calling this API.
  471. Arguments:
  472. None.
  473. Return Value:
  474. None. This API may wait for a long time if there are many threads, but
  475. it is guaranteed to return.
  476. --*/
  477. {
  478. //
  479. // Prevent additional threads from compiling code.
  480. //
  481. MrswWriterEnter(&MrswEP);
  482. //
  483. // Tell active readers to bail out of the Translation Cache, then
  484. // get the TC write lock. The MrswWriterEnter() call will block
  485. // until the last active reader leaves the cache.
  486. //
  487. InterlockedIncrement(&ProcessCpuNotify);
  488. MrswWriterEnter(&MrswTC);
  489. InterlockedDecrement(&ProcessCpuNotify);
  490. }
  491. VOID
  492. CpuResumeExecutionInThisProcess(
  493. VOID
  494. )
  495. /*++
  496. Routine Description:
  497. Allow threads to start running inside the Translation Cache again.
  498. Arguments:
  499. None.
  500. Return Value:
  501. None.
  502. --*/
  503. {
  504. //
  505. // Allow other threads to become EP and TC writers again.
  506. //
  507. MrswWriterExit(&MrswEP);
  508. MrswWriterExit(&MrswTC);
  509. }
  510. BOOL
  511. AddressInTranslationCache(
  512. DWORD Address
  513. )
  514. /*++
  515. Routine Description:
  516. Determines if a RISC address is within the bounds of the Translation
  517. Cache.
  518. Arguments:
  519. Address -- Address to examine
  520. Return Value:
  521. TRUE if Address is within the Translation Cache
  522. FALSE if not.
  523. --*/
  524. {
  525. PBYTE ptr = (PBYTE)Address;
  526. if (
  527. ((ptr >= DynCache.StartAddress) &&
  528. (ptr <= DynCache.StartAddress+DynCache.NextIndex))
  529. ) {
  530. ASSERTPtrInTC(ptr);
  531. return TRUE;
  532. }
  533. return FALSE;
  534. }
  535. #if DBG
  536. VOID
  537. ASSERTPtrInTC(
  538. PVOID ptr
  539. )
  540. /*++
  541. Routine Description:
  542. (Checked-build-only). CPUASSERTs if a particular native address pointer
  543. does not point into the Translation Cache.
  544. Arguments:
  545. ptr - native pointer in question
  546. Return Value:
  547. none - either asserts or returns
  548. --*/
  549. {
  550. // Verify pointer is DWORD aligned.
  551. CPUASSERT(((LONGLONG)ptr & 3) == 0);
  552. if (
  553. (((PBYTE)ptr >= DynCache.StartAddress) &&
  554. ((PBYTE)ptr <= DynCache.StartAddress+DynCache.NextIndex))
  555. ) {
  556. // Verify the pointer points into allocated space in the cache
  557. CPUASSERT(*(PULONG)ptr != DBG_FILL_VALUE);
  558. return;
  559. }
  560. CPUASSERTMSG(FALSE, "Pointer is not within a Translation Cache");
  561. }
  562. #endif