Source code of Windows XP (NT5)
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.

312 lines
8.3 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. abiosc.c
  5. Abstract:
  6. This module implements ROM BIOS support C routines for i386 NT.
  7. Author:
  8. Shie-Lin Tzong (shielint) 10-Sept-1992
  9. Environment:
  10. Kernel mode.
  11. Revision History:
  12. --*/
  13. #include "ki.h"
  14. #pragma hdrstop
  15. #include "vdmntos.h"
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE,Ke386CallBios)
  18. #endif
  19. //
  20. // Never change these equates without checking biosa.asm
  21. //
  22. #define V86_CODE_ADDRESS 0x10000
  23. #define INT_OPCODE 0xcd
  24. #define V86_BOP_OPCODE 0xfec4c4
  25. #define V86_STACK_POINTER 0x1ffe
  26. #define IOPM_OFFSET FIELD_OFFSET(KTSS, IoMaps[0].IoMap)
  27. #define VDM_TIB_ADDRESS 0x12000
  28. #define INT_10_TEB 0x13000
  29. //
  30. // External References
  31. //
  32. PVOID Ki386IopmSaveArea;
  33. VOID
  34. Ki386SetupAndExitToV86Code (
  35. PVOID ExecutionAddress
  36. );
  37. NTSTATUS
  38. Ke386CallBios (
  39. IN ULONG BiosCommand,
  40. IN OUT PCONTEXT BiosArguments
  41. )
  42. /*++
  43. Routine Description:
  44. This function invokes specified ROM BIOS code by executing
  45. "INT BiosCommand." Before executing the BIOS code, this function
  46. will setup VDM context, change stack pointer ...etc. If for some reason
  47. the operation fails, a status code will be returned. Otherwise, this
  48. function always returns success regardless of the result of the BIOS
  49. call.
  50. N.B. This implementation relies on the fact that the direct
  51. I/O access operations between apps are serialized by win user.
  52. Arguments:
  53. BiosCommand - Supplies which ROM BIOS function to invoke.
  54. BiosArguments - Supplies a pointer to the context which will be used
  55. to invoke ROM BIOS.
  56. Return Value:
  57. NTSTATUS code to specify the failure.
  58. --*/
  59. {
  60. PVDM_TIB VdmTib;
  61. PUCHAR BaseAddress = (PUCHAR)V86_CODE_ADDRESS;
  62. PTEB UserInt10Teb = (PTEB)INT_10_TEB;
  63. PKTSS Tss;
  64. PKPROCESS Process;
  65. PKTHREAD Thread;
  66. USHORT OldIopmOffset, OldIoMapBase;
  67. PVDM_PROCESS_OBJECTS VdmObjects;
  68. ULONG ContextLength;
  69. BOOLEAN ThreadDebugActive;
  70. //
  71. // Map in ROM BIOS area to perform the int 10 code
  72. //
  73. try {
  74. RtlZeroMemory(UserInt10Teb, sizeof(TEB));
  75. //
  76. // Write "Int BiosCommand; bop" to reserved user space (0x1000).
  77. // Later control will transfer to the user space to execute
  78. // these two instructions.
  79. //
  80. *BaseAddress++ = INT_OPCODE;
  81. *BaseAddress++ = (UCHAR)BiosCommand;
  82. *(PULONG)BaseAddress = V86_BOP_OPCODE;
  83. //
  84. // Set up Vdm(v86) context to execute the int BiosCommand
  85. // instruction by copying user supplied context to VdmContext
  86. // and updating the control registers to predefined values.
  87. //
  88. //
  89. // We want to use a constant number for the int10.
  90. //
  91. // Create a fake TEB so we can switch the thread to it while we
  92. // do an int10
  93. //
  94. UserInt10Teb->Vdm = (PVOID)VDM_TIB_ADDRESS;
  95. VdmTib = (PVDM_TIB)VDM_TIB_ADDRESS;
  96. RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
  97. VdmTib->Size = sizeof(VDM_TIB);
  98. *FIXED_NTVDMSTATE_LINEAR_PC_AT = 0;
  99. //
  100. // extended registers are never going to matter to
  101. // an Int10 call, so only copy the old part of the
  102. // context record.
  103. //
  104. ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
  105. RtlCopyMemory(&(VdmTib->VdmContext), BiosArguments, ContextLength);
  106. VdmTib->VdmContext.SegCs = (ULONG)BaseAddress >> 4;
  107. VdmTib->VdmContext.SegSs = (ULONG)BaseAddress >> 4;
  108. VdmTib->VdmContext.Eip = 0;
  109. VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG);
  110. VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
  111. VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
  112. } except (EXCEPTION_EXECUTE_HANDLER) {
  113. return GetExceptionCode();
  114. }
  115. //
  116. // The vdm kernel code finds the Tib by looking at a pointer cached in
  117. // kernel memory, which was probed at Vdm creation time. Since the
  118. // creation semantics for this vdm are peculiar, we do something similar
  119. // here.
  120. //
  121. //
  122. // We never get here on a process that is a real vdm. If we do,
  123. // bad things will happen (pool leak, failure to execute dos and
  124. // windows apps).
  125. //
  126. ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
  127. VdmObjects = ExAllocatePoolWithTag (NonPagedPool,
  128. sizeof(VDM_PROCESS_OBJECTS),
  129. ' eK'
  130. );
  131. //
  132. // Since we are doing this on behalf of CSR not a user process, we aren't
  133. // charging quota.
  134. //
  135. if (VdmObjects == NULL) {
  136. return STATUS_NO_MEMORY;
  137. }
  138. //
  139. // We are only initializing the VdmTib pointer, because that's the only
  140. // part of the VdmObjects we use for ROM calls. We aren't set up
  141. // to simulate interrupts, or any of the other stuff that would be done
  142. // in a conventional vdm
  143. //
  144. RtlZeroMemory( VdmObjects, sizeof(VDM_PROCESS_OBJECTS));
  145. VdmObjects->VdmTib = VdmTib;
  146. PsGetCurrentProcess()->VdmObjects = VdmObjects;
  147. //
  148. // Since we are going to v86 mode and accessing some I/O ports, we
  149. // need to make sure the IopmOffset is set correctly across context
  150. // swap and the I/O bit map has all the bits cleared.
  151. // N.B. This implementation assumes that there is only one full
  152. // screen DOS app and the io access between full screen DOS
  153. // app and the server code is serialized by win user. That
  154. // means even we change the IOPM, the full screen dos app won't
  155. // be able to run on this IOPM.
  156. // * In another words, IF THERE IS
  157. // * MORE THAN ONE FULL SCREEN DOS APPS, THIS CODE IS BROKEN.*
  158. //
  159. // NOTE This code works on the assumption that winuser serializes
  160. // direct I/O access operations.
  161. //
  162. //
  163. // Call the bios from the processor which booted the machine.
  164. //
  165. Thread = KeGetCurrentThread();
  166. KeSetSystemAffinityThread(1);
  167. Tss = KeGetPcr()->TSS;
  168. //
  169. // Save away the original IOPM bit map and clear all the IOPM bits
  170. // to allow v86 int 10 code to access ALL the io ports.
  171. //
  172. //
  173. // Make sure there are at least 2 IOPM maps.
  174. //
  175. ASSERT(KeGetPcr()->GDT[KGDT_TSS / 8].LimitLow >= (0x2000 + IOPM_OFFSET - 1));
  176. RtlCopyMemory (Ki386IopmSaveArea,
  177. (PVOID)&Tss->IoMaps[0].IoMap,
  178. PAGE_SIZE * 2
  179. );
  180. RtlZeroMemory ((PVOID)&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
  181. Process = Thread->ApcState.Process;
  182. OldIopmOffset = Process->IopmOffset;
  183. OldIoMapBase = Tss->IoMapBase;
  184. Process->IopmOffset = (USHORT)(IOPM_OFFSET); // Set Process IoPmOffset before
  185. Tss->IoMapBase = (USHORT)(IOPM_OFFSET); // updating Tss IoMapBase
  186. //
  187. // The context setup for the BIOS will not have valid debug registers
  188. // in it, don't try to load them.
  189. //
  190. ThreadDebugActive = Thread->DebugActive;
  191. Thread->DebugActive = FALSE;
  192. KeGetPcr()->DebugActive = FALSE;
  193. //
  194. // Call ASM routine to switch stack to exit to v86 mode to
  195. // run Int BiosCommand.
  196. //
  197. Ki386SetupAndExitToV86Code(UserInt10Teb);
  198. //
  199. // After we return from v86 mode, the control comes here.
  200. //
  201. // Restore Thread's DebugActive flag.
  202. //
  203. KeGetPcr()->DebugActive = ThreadDebugActive;
  204. Thread->DebugActive = ThreadDebugActive;
  205. //
  206. // Restore old IOPM
  207. //
  208. RtlCopyMemory ((PVOID)&Tss->IoMaps[0].IoMap,
  209. Ki386IopmSaveArea,
  210. PAGE_SIZE * 2
  211. );
  212. Process->IopmOffset = OldIopmOffset;
  213. Tss->IoMapBase = OldIoMapBase;
  214. //
  215. // Restore old affinity for current thread.
  216. //
  217. KeRevertToUserAffinityThread();
  218. //
  219. // Copy 16 bit vdm context back to caller.
  220. //
  221. // Extended register state is not going to matter,
  222. // so copy only the old part of the context record.
  223. //
  224. ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
  225. RtlCopyMemory(BiosArguments, &(VdmTib->VdmContext), ContextLength);
  226. BiosArguments->ContextFlags = CONTEXT_FULL;
  227. //
  228. // Free the pool used for the VdmTib pointer
  229. //
  230. PsGetCurrentProcess()->VdmObjects = NULL;
  231. ExFreePool(VdmObjects);
  232. return STATUS_SUCCESS;
  233. }