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.

609 lines
18 KiB

  1. /*++
  2. Copyright (c) 1996-1998 Microsoft Corporation
  3. Module Name:
  4. simulate.c
  5. Abstract:
  6. This module contains the code that drives the intel instruction
  7. execution process.
  8. Author:
  9. Dave Hastings (daveh) creation-date 09-Jul-1994
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <windows.h>
  16. #define _WX86CPUAPI_
  17. #include "wx86nt.h"
  18. #include "wx86.h"
  19. #include "wx86cpu.h"
  20. #include "cpuassrt.h"
  21. #include "threadst.h"
  22. #include "instr.h"
  23. #include "config.h"
  24. #include "entrypt.h"
  25. #include "compilep.h"
  26. #include "compiler.h"
  27. #include "instr.h"
  28. #include "frag.h"
  29. #include "cpumain.h"
  30. #include "mrsw.h"
  31. #include "cpunotif.h"
  32. #include "tc.h"
  33. #include "atomic.h"
  34. ASSERTNAME;
  35. //
  36. // Private definition of what a WX86_CPUHINT really contains.
  37. // The CPUHINT allows the CPU to bypass an expensive NativeAddressFromEip()
  38. // call to map the Intel EIP value into a RISC address. Most calls to
  39. // CpuSimulate() are from RISC-to-x86 callbacks, and they have two DWORDS
  40. // which the CPU uses to cache the NativeAddressFromEip() results.
  41. //
  42. // Timestamp -- value of TranslationCacheTimestamp when the CPUHINT was
  43. // filled in. This is used to determine if the Translation Cache
  44. // has been flushed. If so, the EntryPoint pointer is now
  45. // invalid.
  46. // EntryPoint -- pointer to the ENTRYPOINT describing the Intel Address
  47. // corresponding to this callback.
  48. //
  49. //
  50. typedef struct _CpuHint {
  51. DWORD Timestamp;
  52. PENTRYPOINT EntryPoint;
  53. } CPUHINT, *PCPUHINT;
  54. //
  55. // These values are modified by the wx86e debugger extension whenever it
  56. // writes into this process's address space. It is used whenever Int3
  57. // instructions are added or removed from Intel code. The CPU examines
  58. // these variables whenever CPUNOTIFY_DBGFLUSHTC is set.
  59. //
  60. ULONG DbgDirtyMemoryAddr = 0xffffffff;
  61. ULONG DbgDirtyMemoryLength;
  62. #ifdef PROFILE
  63. //
  64. // Wrap our assembly entrypoint so we can see it in the cap output
  65. //
  66. VOID
  67. _ProfStartTranslatedCode(
  68. PTHREADSTATE ThreadState,
  69. PVOID NativeCode
  70. )
  71. {
  72. StartTranslatedCode(ThreadState, NativeCode);
  73. }
  74. #endif
  75. VOID
  76. MsCpuSimulate(
  77. PWX86_CPUHINT Wx86CpuHint
  78. )
  79. /*++
  80. Routine Description:
  81. This is the cpu internal routine that causes intel instructions
  82. to be executed. Execution continues until something interesting
  83. happens (such as BOP Unsimulate)
  84. Arguments:
  85. None.
  86. Return Value:
  87. None.
  88. --*/
  89. {
  90. PVOID NativeCode;
  91. DWORD CpuNotify;
  92. DWORD OldCompilerFlags = CompilerFlags;
  93. DECLARE_CPU;
  94. CPUASSERT(sizeof(CPUHINT) == sizeof(WX86_CPUHINT));
  95. //
  96. // Check CpuNotify to see if the Translation Cache needs flushing.
  97. //
  98. CpuNotify = cpu->CpuNotify;
  99. cpu->CpuNotify &= ~(ULONG)CPUNOTIFY_DBGFLUSHTC;
  100. if (cpu->flag_tf) {
  101. CpuNotify |= CPUNOTIFY_MODECHANGE;
  102. CompilerFlags = COMPFL_SLOW;
  103. }
  104. if (CpuNotify & (CPUNOTIFY_DBGFLUSHTC|CPUNOTIFY_MODECHANGE)) {
  105. if (CpuNotify & CPUNOTIFY_MODECHANGE) {
  106. //
  107. // On a fast/slow compiler mode change, flush the whole cache
  108. //
  109. DbgDirtyMemoryAddr = 0;
  110. DbgDirtyMemoryLength = 0xffffffff;
  111. }
  112. //
  113. // The debugger has modified memory - flush the Translation Cache
  114. //
  115. CpuFlushInstructionCache((PVOID)DbgDirtyMemoryAddr,
  116. DbgDirtyMemoryLength);
  117. DbgDirtyMemoryAddr = 0xffffffff;
  118. DbgDirtyMemoryLength = 0;
  119. }
  120. //
  121. // Flag ourseleves as having the TC lock.
  122. //
  123. CPUASSERTMSG(cpu->fTCUnlocked != FALSE,
  124. "CPU has been reentered with the TC already locked.\n");
  125. cpu->fTCUnlocked = FALSE;
  126. //
  127. // The caller has already pushed a return address on the stack.
  128. // (Probably to a BOP FE). Get the callstack in sync.
  129. //
  130. PUSH_CALLSTACK(*(DWORD *)cpu->Esp.i4, 0)
  131. if (Wx86CpuHint) {
  132. PCPUHINT CpuHint = (PCPUHINT)Wx86CpuHint;
  133. PVOID Eip = (PVOID)cpu->eipReg.i4;
  134. //
  135. // CpuNotify isn't set, and a hint is present...try to use it.
  136. //
  137. MrswReaderEnter(&MrswTC);
  138. if (CpuHint->Timestamp != TranslationCacheTimestamp ||
  139. CpuHint->EntryPoint->intelStart != Eip) {
  140. //
  141. // The hint is present, but invalid. Get the new address and
  142. // update the hint
  143. //
  144. MrswReaderExit(&MrswTC);
  145. #if 0
  146. LOGPRINT((DEBUGLOG, "CPU: CpuHint was invalid: got (%X,%X) expected (%X,%X)\r\n",
  147. CpuHint->Timestamp, ((CpuHint->Timestamp)?CpuHint->EntryPoint->intelStart:0),
  148. TranslationCacheTimestamp, Eip));
  149. #endif
  150. CpuHint->EntryPoint = NativeAddressFromEip(Eip, FALSE);
  151. CpuHint->Timestamp = TranslationCacheTimestamp;
  152. }
  153. NativeCode = CpuHint->EntryPoint->nativeStart;
  154. } else {
  155. //
  156. // Find the address of the Native code to execute, and lock the
  157. // Translation cache
  158. //
  159. NativeCode = NativeAddressFromEip((PVOID)cpu->eipReg.i4, FALSE)->nativeStart;
  160. }
  161. while (TRUE) {
  162. if (cpu->CSTimestamp != TranslationCacheTimestamp) {
  163. //
  164. // The timestamp associated with the callstack is different
  165. // than the timestamp for the Translation Cache. Therefore,
  166. // the TC has been flushed. We must flush the callstack, too.
  167. //
  168. FlushCallstack(cpu);
  169. }
  170. //
  171. // Go execute the code
  172. //
  173. #ifdef PROFILE
  174. _ProfStartTranslatedCode(cpu, NativeCode);
  175. #else
  176. StartTranslatedCode(cpu, NativeCode);
  177. #endif
  178. CompilerFlags = OldCompilerFlags;
  179. //
  180. // Release the translation cache
  181. //
  182. MrswReaderExit(&MrswTC);
  183. //
  184. // if TF flag is set, then switch the compiler to SLOW_MODE
  185. // and set the CPUNOTIFY_TRACEFLAG to generate an x86 single-step
  186. // exception.
  187. //
  188. cpu->CpuNotify |= cpu->flag_tf;
  189. //
  190. // Check and see if anything needs to be done
  191. //
  192. if (cpu->CpuNotify) {
  193. //
  194. // Atomically get CpuNotify and clear the appropriate bits
  195. //
  196. CpuNotify = cpu->CpuNotify;
  197. cpu->CpuNotify &= (CPUNOTIFY_TRACEADDR|CPUNOTIFY_SLOWMODE|CPUNOTIFY_TRACEFLAG);
  198. //
  199. // Indicate we have left the Translation Cache
  200. //
  201. cpu->fTCUnlocked = TRUE;
  202. if (CpuNotify & CPUNOTIFY_UNSIMULATE) {
  203. break;
  204. }
  205. if (CpuNotify & CPUNOTIFY_EXITTC) {
  206. // There is no work to do - The Translation Cache is going
  207. // away, so all active reader threads needed to leave the
  208. // cache ASAP and block inside NativeAddressFromEip() until
  209. // the cache flush has completed.
  210. }
  211. if (CpuNotify & CPUNOTIFY_SUSPEND) {
  212. //
  213. // Another thread wants to suspend us. Notify that
  214. // thread that we're in a consistent state, then wait
  215. // until we are resumed.
  216. //
  217. CpupSuspendCurrentThread();
  218. }
  219. if (CpuNotify & CPUNOTIFY_SLOWMODE) {
  220. // log the instruction address for debugging purposes
  221. cpu->eipLog[cpu->eipLogIndex++] = cpu->eipReg.i4;
  222. cpu->eipLogIndex %= EIPLOGSIZE;
  223. }
  224. if (CpuNotify & CPUNOTIFY_INTX) {
  225. BYTE intnum;
  226. //
  227. // Get the interrupt number from the code stream, and
  228. // advance Eip to the start of the next instruction.
  229. //
  230. intnum = *(PBYTE)cpu->eipReg.i4;
  231. cpu->eipReg.i4 += 1;
  232. if (intnum == 0xcc) {
  233. intnum = 3;
  234. } else {
  235. cpu->eipReg.i4 += 1;
  236. }
  237. CpupDoInterrupt(intnum);
  238. //
  239. // Flush the entire translation cache since we don't know what memory
  240. // areas the debugger has changed. We do this by simulating
  241. // a compiler mode change.
  242. //
  243. CpuNotify |= CPUNOTIFY_MODECHANGE;
  244. } else if (CpuNotify & (CPUNOTIFY_TRACEADDR|CPUNOTIFY_TRACEFLAG)) {
  245. if ((CpuNotify & CPUNOTIFY_TRACEADDR) &&
  246. ((DWORD)(ULONGLONG)cpu->TraceAddress == cpu->eipReg.i4)
  247. ) {
  248. cpu->TraceAddress = NULL;
  249. cpu->CpuNotify &= ~(ULONG)CPUNOTIFY_TRACEADDR;
  250. Wx86RaiseStatus(WX86CPU_SINGLE_STEP);
  251. }
  252. if (CpuNotify & CPUNOTIFY_TRACEFLAG) {
  253. cpu->flag_tf = 0;
  254. cpu->CpuNotify &= ~(ULONG)CPUNOTIFY_TRACEFLAG;
  255. Wx86RaiseStatus(WX86CPU_SINGLE_STEP);
  256. }
  257. //
  258. // Flush the entire translation cache since we don't know what memory
  259. // areas the debugger has changed. We do this by simulating
  260. // a compiler mode change.
  261. //
  262. CpuNotify |= CPUNOTIFY_MODECHANGE;
  263. }
  264. if (CpuNotify & (CPUNOTIFY_DBGFLUSHTC|CPUNOTIFY_MODECHANGE)) {
  265. if (CpuNotify & CPUNOTIFY_MODECHANGE) {
  266. //
  267. // On a fast/slow compiler mode change, flush whole cache
  268. //
  269. DbgDirtyMemoryAddr = 0;
  270. DbgDirtyMemoryLength = 0xffffffff;
  271. }
  272. //
  273. // The debugger has modified memory - flush the Translation
  274. // Cache
  275. //
  276. CpuFlushInstructionCache((PVOID)DbgDirtyMemoryAddr,
  277. DbgDirtyMemoryLength);
  278. DbgDirtyMemoryAddr = 0xffffffff;
  279. DbgDirtyMemoryLength = 0;
  280. }
  281. //
  282. // Indicate we are re-entering the Translation Cache
  283. //
  284. cpu->fTCUnlocked = FALSE;
  285. }
  286. if (cpu->flag_tf) {
  287. OldCompilerFlags = CompilerFlags;
  288. CompilerFlags = COMPFL_SLOW;
  289. if (!(CpuNotify & CPUNOTIFY_MODECHANGE)) {
  290. CpuFlushInstructionCache(NULL, 0xffffffff);
  291. }
  292. }
  293. //
  294. // Find the address of the Native code to execute, and lock the
  295. // Translation cache
  296. //
  297. NativeCode = NativeAddressFromEip((PVOID)cpu->eipReg.i4, FALSE)->nativeStart;
  298. }
  299. }
  300. PENTRYPOINT
  301. NativeAddressFromEip(
  302. PVOID Eip,
  303. BOOL LockTCForWrite
  304. )
  305. /*++
  306. Routine Description:
  307. This routine finds (or creates) the native code for the specified Intel
  308. code.
  309. NOTE: This function can only be called when the Translation Cache is
  310. not locked (either read or write) by the current thread.
  311. Arguments:
  312. Eip -- Supplies the address of the Intel code
  313. LockTCForWrite -- TRUE if caller wants TC locked for WRITE, FALSE if the
  314. call wants it locked for READ.
  315. Return Value:
  316. Entrypoint whose nativeStart Address corresponds to the Intel Address
  317. passed in.
  318. --*/
  319. {
  320. PENTRYPOINT Entrypoint;
  321. typedef VOID (*pfnMrswCall)(PMRSWOBJECT);
  322. pfnMrswCall MrswCall;
  323. DWORD OldEntrypointTimestamp;
  324. //
  325. // Assume we are going to call MrswReaderExit(&MrswEP) at the end
  326. // of this function.
  327. //
  328. MrswCall = MrswReaderExit;
  329. //
  330. // Lock the Entrypoint for reading
  331. //
  332. MrswReaderEnter(&MrswEP);
  333. //
  334. // Find the location of the Risc code corresponding to the
  335. // Intel EIP register
  336. //
  337. Entrypoint = EPFromIntelAddr(Eip);
  338. //
  339. // If there is no entrypoint, compile up the code
  340. //
  341. if (Entrypoint == NULL || Entrypoint->intelStart != Eip) {
  342. //
  343. // Unlock the Entrypoint read
  344. //
  345. OldEntrypointTimestamp = EntrypointTimestamp;
  346. MrswReaderExit(&MrswEP);
  347. //
  348. // Lock the Entrypoint for write, and change the function to be
  349. // called at the end of the function to be MrswWriterExit(&MrswEP)
  350. //
  351. MrswWriterEnter(&MrswEP);
  352. MrswCall = MrswWriterExit;
  353. //
  354. // See if another thread compiled the Entrypoint while we were
  355. // switching from read mode to write mode
  356. //
  357. if (OldEntrypointTimestamp != EntrypointTimestamp) {
  358. //
  359. // Timestamp has changed. There is a possibility that another
  360. // thread has compiled code at Eip for us, so retry the search.
  361. //
  362. Entrypoint = EPFromIntelAddr(Eip);
  363. }
  364. //
  365. // Call the compiler. It will do one of the following things:
  366. // 1. if Entrypoint==NULL, it will compile new code
  367. // 2. if Entrypoint!=NULL and Entrypoint->Eip == Eip, it will
  368. // return Entrypoint unchanged
  369. // 3. otherwise, the Entrypoint needs splitting. It will do so,
  370. // and compile a subset of the code described by Entrypoint and
  371. // then return a new Entrypoint
  372. //
  373. Entrypoint = Compile(Entrypoint, Eip);
  374. }
  375. //
  376. // Instruction was found - grab the translation cache for either
  377. // read or write, then free the entrypoint write lock. The
  378. // order is important as it prevents the TC from being flushed
  379. // between the two Mrsw calls.
  380. //
  381. if (LockTCForWrite) {
  382. InterlockedIncrement(&ProcessCpuNotify);
  383. MrswWriterEnter(&MrswTC);
  384. InterlockedDecrement(&ProcessCpuNotify);
  385. } else {
  386. MrswReaderEnter(&MrswTC);
  387. }
  388. (*MrswCall)(&MrswEP); // Either MrswReaderExit() or MrswWriterExit()
  389. return Entrypoint;
  390. }
  391. PVOID
  392. NativeAddressFromEipNoCompile(
  393. PVOID Eip
  394. )
  395. /*++
  396. Routine Description:
  397. This routine finds the native code for the specified Intel code, if it
  398. exists. No new code is compiled.
  399. NOTE: This function can only be called when the Translation Cache is
  400. not locked (either read or write) by the current thread.
  401. Arguments:
  402. Eip -- Supplies the address of the Intel code
  403. Return Value:
  404. Address of corresponding native code, or NULL if none exists. Translation
  405. cache locked for WRITE if native code exists for Eip. TC is locked for
  406. READ if no code exitss.
  407. --*/
  408. {
  409. PENTRYPOINT Entrypoint;
  410. DWORD OldEntrypointTimestamp;
  411. //
  412. // Lock the Entrypoint for reading
  413. //
  414. MrswReaderEnter(&MrswEP);
  415. //
  416. // Find the location of the Risc code corresponding to the
  417. // Intel EIP register
  418. //
  419. Entrypoint = EPFromIntelAddr(Eip);
  420. if (Entrypoint == NULL) {
  421. //
  422. // Entrypoint not found - no native code exists for this Intel address
  423. //
  424. MrswReaderEnter(&MrswTC);
  425. MrswReaderExit(&MrswEP);
  426. return NULL;
  427. } else if (Entrypoint->intelStart == Eip) {
  428. //
  429. // Exact instruction found - return the NATIVE address
  430. //
  431. InterlockedIncrement(&ProcessCpuNotify);
  432. MrswWriterEnter(&MrswTC);
  433. InterlockedDecrement(&ProcessCpuNotify);
  434. MrswReaderExit(&MrswEP);
  435. return Entrypoint->nativeStart;
  436. }
  437. //
  438. // Else the entrypoint contains the Intel address. Nothing can
  439. // be done. Release EP write and get TC read.
  440. //
  441. MrswReaderExit(&MrswEP);
  442. MrswReaderEnter(&MrswTC);
  443. return NULL;
  444. }
  445. PENTRYPOINT
  446. NativeAddressFromEipNoCompileEPWrite(
  447. PVOID Eip
  448. )
  449. /*++
  450. Routine Description:
  451. This routine finds the native code for the specified Intel code, if it
  452. exists. No new code is compiled. This function is called by functions
  453. in patchfn.c during compile time when they need to decide whether to
  454. directly place the patched version in the Translation Cache or not.
  455. NOTE: This function can only be called when the Translation Cache is
  456. not locked (either read or write) by the current thread.
  457. NOTE: The difference between this function and NativeAddressFromEipNoCompile
  458. is that here we assume that we have the Entry Point write lock upon
  459. entry to the function. This function makes no calls to MRSW functions
  460. for any locks.
  461. Arguments:
  462. Eip -- Supplies the address of the Intel code
  463. Return Value:
  464. Address of corresponding native code, or NULL if none exists. All MRSW objects
  465. are in exactly the same state they were when we entered this function.
  466. --*/
  467. {
  468. PENTRYPOINT Entrypoint;
  469. //
  470. // Find the location of the Risc code corresponding to the
  471. // Intel EIP register
  472. //
  473. Entrypoint = EPFromIntelAddr(Eip);
  474. if (Entrypoint == NULL) {
  475. //
  476. // Entrypoint not found - no native code exists for this Intel address
  477. //
  478. return NULL;
  479. } else if (Entrypoint->intelStart == Eip) {
  480. //
  481. // Exact instruction found - return the NATIVE address
  482. //
  483. return Entrypoint;
  484. }
  485. //
  486. // Entrypoint needs to be split. Can't do that without compiling.
  487. //
  488. return NULL;
  489. }