Author: hbelusca
Date: Fri Feb 21 20:31:56 2014
New Revision: 62283
URL:
http://svn.reactos.org/svn/reactos?rev=62283&view=rev
Log:
[NTVDM]
** WARNING! WARNING! WORK IN PROGRESS!! **
** MEGA HUGE IRC SPAM IN SIGHT!! **
Commit a starting point implementation for 16-bit callbacks from 32-bit code that work
together with the EmulatorSimulate / EmulatorUnsimulate functions.
That needs HUUUGE reviewing (go to the END to skip details and going to the current
drawbacks of the implementation) <-- [TheFlash], Vampyre, others...
How it is intended to work:
1- Add callback.c to the CMakeLists.txt file, and remove int32.c from it.
2- In some 32-bit module that will get called by 16-bit code sooner or later (e.g. bios32
or dos32), include callback.h instead of int23.h.
3- Add a CALLBACK16 MyContext; // definition
4- In some initialization code for this module, call: InitializeContext(&MyContext,
custom_segment, custom_offset);
This allows you to define a zone of memory custom_segment:custom_offset that will be
used as a trampoline zone. FIXME/TODO: we can return from this call the size of this zone
(not implemented at the moment), so that we can know the zone that will be reserved for
calls (it will be used in the following steps).
5- Now you can register e.g. 32-bit interrupt handlers with calls like:
MyContext.NextOffset += RegisterInt32(MAKELONG(MyContext.NextOffset,
MyContext.Segment),
IntNumber, Int32Handler, NULL);
Now comes the tricky part: since we want to be able to give precise memory addresses to
where to put interrupt stubs (whose addresses will be stored in the IVT) (it's because
it happens that some programs expect some few code interrupts to be placed at given places
in memory; or one can argue that it is "for IBM bios compatibility"....), we
pass a far pointer instead of the context structure as the first parameter. Then,
currently the function returns the size of the written code (and it returns too via its
last optional parameter).
6- You can do almost the same with RegisterInt16, where now instead of giving a 32-bit
interrupt handler, you give the code of a 16-bit interrupt. What changes here is that in
the 32-bit case, the 16-bit interrupt code was generated (to call back the 32-bit handler)
whereas here you control it fully. For 16-bit interrupt code you need to use IRETs
operands.
7- There is a RegisterCallback16 function, which registers at a given place in memory a
chunk of 16-bit code. This code is expected to return with RETs. Its accompanying function
RunCallback16 is untested at the moment.
8- Now the magic: Calling this code: an example is given in the following (from
DosFastConOut fucntion of Dos32):
<code_snip>
/* Save AX and BX */
USHORT AX = getAX();
USHORT BX = getBX();
setBL(DOS_CHAR_ATTRIBUTE);
setBH(Bda->VideoPage);
setAH(0x0E);
Int32Call(&DosContext, 0x10);
/* Restore AX and BX */
setAX(AX);
setBX(BX);
</code_snip>
Here (after saving some registers and setting some parameters for the INT 10h being
called), we call interrupt 10h with Int32Call(&DosContext, 0x10); // where DosContext
is a CALLBACK16 context.
The call is done "synchronously", i.e. we restart here CPU simulation. The
simulation then stops because the generated trampoline code has a suitable BOP_UNSIMULATE
instruction.
CURRENT DRAWBACKS:
==================
1- The module which is going to use those callbacks need to know where in memory the code
will be placed. Nothing is done "automatically". Otherwise we would have to:
* maintain a (gobal, and finite) table of callbacks (in some way or another), and/or
* be able to place the code in some "shadowed" memory zone of the emulator
that gets hidden for all the programs emulated BUT the emulator.
2- Linked to the previous second point comes the problem of trampoline code. It is needed
because we need to put a BOP_UNSIMULATE operand after the code to stop the 16-bit code
simulation and go back to 32-bit. We need also to call e.g. INT 0x10 from 16-bit to be
able to run the interrupt that could be hooked by some program. Some may argue that one
can first call EmulatorInterrupt(0x10); but then we would have to find a means of stopping
the simulation as soon as the interrupt exits...
3- For calling "regular" 16-bit code (with RunCallback16) we need a suitable
callback: "call far_pointer; BOP_UNSIMULATE" .
4- Currently we build the trampolines on-the-fly when calling either RunCallback16 or
Int32Call, at the memory location given when initializing CALLBACK16 context. If a given
32-bit module calls some 16-bit code that calls in turn a 32-bit function from the SAME
module which calls also a 16-bit callback, then the trampoline will be overwritten. In
RunCallback16 (and Int32Call) we save it, push a new one and then call the 16-bit code,
and then restore the old one after the call. It seems to work fine currently, but I've
found a problem, I think, which is the following:
* Suppose that a 16-bit program calls some VDD function,
* this VDD function creates a thread,
* the two running VDD functions then call back the 16-bit program, or trigger an
interrupt from whatever nature which both call 32-bit functions from a given 32-bit module
(bios, dos). In this situation many problems arise:
a. Our current implementation of the emulator doesn't support that since you are
going to play concurrently with the unique CONTEXT of the emulated CPU... (i.e. flags /
registers corruption in sight)
b. If the 32-bit functions (called concurrently, and which are from the same 32-bit
module) call some 16-bit code / interrupt, then they are going to use the same memory zone
for the trampoline stub and there are very high risks of corruption. A solution for that
would be to generate the trampolines once and for all for each registered 16-bit interrupt
stub / 16-bit generic callback, retrieve their addresses and store them in some place
(that also get shared amongst all of the 32-bit modules of the NTVDM emulator), so that
each (possible concurrent) call go to the trampoline and just make the CPU point at it...
Voilà !
Added:
branches/ntvdm/subsystems/ntvdm/callback.c
- copied, changed from r62251, branches/ntvdm/subsystems/ntvdm/int32.c
branches/ntvdm/subsystems/ntvdm/callback.h
- copied, changed from r62251, branches/ntvdm/subsystems/ntvdm/int32.h
Copied: branches/ntvdm/subsystems/ntvdm/callback.c (from r62251,
branches/ntvdm/subsystems/ntvdm/int32.c)
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/callback…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/int32.c [iso-8859-1] (original)
+++ branches/ntvdm/subsystems/ntvdm/callback.c [iso-8859-1] Fri Feb 21 20:31:56 2014
@@ -1,20 +1,21 @@
/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
- * FILE: int32.c
- * PURPOSE: 32-bit Interrupt Handlers
+ * FILE: callback.c
+ * PURPOSE: 16 and 32-bit Callbacks Support
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
*/
/* INCLUDES *******************************************************************/
-// #define NDEBUG
+#define NDEBUG
#include "emulator.h"
-#include "int32.h"
+#include "callback.h"
#include "bop.h"
+#include <isvbop.h>
/* PRIVATE VARIABLES **********************************************************/
@@ -30,7 +31,234 @@
/* 32-bit Interrupt dispatcher function code for the Control BOP Handler */
#define BOP_CONTROL_INT32 0xFF
+
+// #define ALIGN(ptr) (((ULONG_PTR)(ptr) + 15) & ~15)
+#define ALIGN(ptr) (ptr)
+
+#define BOP(num) LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), (num)
+#define UnSimulate16 MAKELONG(EMULATOR_BOP, BOP_UNSIMULATE) //
BOP(BOP_UNSIMULATE)
+
+#define CALL16_TRAMPOLINE_SIZE (2 * sizeof(ULONGLONG))
+#define INT16_TRAMPOLINE_SIZE (1 * sizeof(ULONGLONG))
+
+/* 16-bit generic interrupt code for calling a 32-bit interrupt handler */
+BYTE Int16To32[] =
+{
+ 0xFA, // cli
+
+ /* Push the value of the interrupt to be called */
+ 0x6A, 0xFF, // push i (patchable to 0x6A, 0xIntNum)
+
+ /* The counter variable (initialized to 0) */
+ 0x6A, 0x00, // push 0
+
+ /* Stack variables */
+ 0x83, 0xEC, 0x04, // sub sp, 4
+
+ /* The BOP Sequence */
+// BOP_SEQ:
+ 0xF8, // clc
+ BOP(BOP_CONTROL), // Control BOP
+ BOP_CONTROL_INT32, // 32-bit Interrupt dispatcher
+
+ 0x73, 0x04, // jnc EXIT (offset +4)
+
+ 0xFB, // sti
+
+ // HACK: The following instruction should be HLT!
+ 0x90, // nop
+
+ 0xEB, 0xF5, // jmp BOP_SEQ (offset -11)
+
+// EXIT:
+ 0x83, 0xC4, 0x08, // add sp, 8
+ 0xCF, // iret
+};
+
/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID
+InitializeContext(IN PCALLBACK16 Context,
+ IN USHORT Segment,
+ IN USHORT Offset)
+{
+ Context->TrampolineFarPtr = MAKELONG(Offset, Segment);
+ Context->Segment = Segment;
+ Context->NextOffset = Offset + max(CALL16_TRAMPOLINE_SIZE,
+ INT16_TRAMPOLINE_SIZE);
+}
+
+VOID
+Call16(IN USHORT Segment,
+ IN USHORT Offset)
+{
+ /* Save CS:IP */
+ USHORT OrgCS = getCS();
+ USHORT OrgIP = getIP();
+
+ /* Set the new CS:IP */
+ setCS(Segment);
+ setIP(Offset);
+
+ DPRINT("Call16(0x%04X, 0x%04X)\n", Segment, Offset);
+
+ /* Start simulation */
+ EmulatorSimulate();
+
+ /* Restore CS:IP */
+ setCS(OrgCS);
+ setIP(OrgIP);
+}
+
+
+
+ULONG
+RegisterCallback16(IN ULONG FarPtr,
+ IN LPBYTE CallbackCode,
+ IN SIZE_T CallbackSize,
+ OUT PSIZE_T CodeSize OPTIONAL)
+{
+ LPBYTE CodeStart = (LPBYTE)FAR_POINTER(FarPtr);
+ LPBYTE Code = CodeStart;
+
+ SIZE_T OurCodeSize = CallbackSize;
+
+ if (CallbackCode == NULL) CallbackSize = 0;
+
+ if (CallbackCode)
+ {
+ /* 16-bit interrupt code */
+ RtlCopyMemory(Code, CallbackCode, CallbackSize);
+ Code += CallbackSize;
+ }
+
+ /* Return the real size of the code if needed */
+ if (CodeSize) *CodeSize = OurCodeSize; // == (ULONG_PTR)Code - (ULONG_PTR)CodeStart;
+
+ // /* Return the entry-point address for 32-bit calls */
+ // return (ULONG_PTR)(CodeStart + CallbackSize);
+ return OurCodeSize;
+}
+
+VOID
+RunCallback16(IN PCALLBACK16 Context,
+ IN ULONG FarPtr)
+{
+ PUCHAR TrampolineBase = (PUCHAR)FAR_POINTER(Context->TrampolineFarPtr);
+ PUCHAR Trampoline = TrampolineBase;
+ UCHAR OldTrampoline[CALL16_TRAMPOLINE_SIZE];
+
+ /* Save the old trampoline */
+ // RtlCopyMemory(OldTrampoline, TrampolineBase, sizeof(OldTrampoline));
+ ((PULONGLONG)&OldTrampoline)[0] = ((PULONGLONG)TrampolineBase)[0];
+ ((PULONGLONG)&OldTrampoline)[1] = ((PULONGLONG)TrampolineBase)[1];
+
+ /* Build the generic entry-point for 32-bit calls */
+ *Trampoline++ = 0x9A; // Call far seg:off
+ *(PULONG)Trampoline = FarPtr;
+ Trampoline += sizeof(ULONG);
+ *(PULONG)Trampoline = UnSimulate16;
+
+ /* Perform the call */
+ Call16(HIWORD(Context->TrampolineFarPtr),
+ LOWORD(Context->TrampolineFarPtr));
+
+ /* Restore the old trampoline */
+ // RtlCopyMemory(TrampolineBase, OldTrampoline, sizeof(OldTrampoline));
+ ((PULONGLONG)TrampolineBase)[1] = ((PULONGLONG)&OldTrampoline)[1];
+ ((PULONGLONG)TrampolineBase)[0] = ((PULONGLONG)&OldTrampoline)[0];
+}
+
+
+
+ULONG
+RegisterInt16(IN ULONG FarPtr,
+ IN BYTE IntNumber,
+ IN LPBYTE CallbackCode,
+ IN SIZE_T CallbackSize,
+ OUT PSIZE_T CodeSize OPTIONAL)
+{
+ /* Get a pointer to the IVT and set the corresponding entry (far pointer) */
+ LPDWORD IntVecTable = (LPDWORD)SEG_OFF_TO_PTR(0x0000, 0x0000);
+ IntVecTable[IntNumber] = FarPtr;
+
+ /* Register the 16-bit callback */
+ return RegisterCallback16(FarPtr,
+ CallbackCode,
+ CallbackSize,
+ CodeSize);
+}
+
+ULONG
+RegisterInt32(IN ULONG FarPtr,
+ IN BYTE IntNumber,
+ IN EMULATOR_INT32_PROC IntHandler,
+ OUT PSIZE_T CodeSize OPTIONAL)
+{
+ /* Array for holding our copy of the 16-bit interrupt callback */
+ BYTE IntCallback[sizeof(Int16To32)/sizeof(BYTE)];
+
+ /* Check whether the 32-bit interrupt was already registered */
+ // if (Int32Proc[IntNumber] != NULL)
+ // {
+ // DPRINT1("RegisterInt32: Interrupt 0x%X already registered!\n",
IntNumber);
+ // return 0;
+ // }
+
+ /* Register the 32-bit interrupt handler */
+ Int32Proc[IntNumber] = IntHandler;
+
+ /* Copy the generic 16-bit interrupt callback and patch it */
+ RtlCopyMemory(IntCallback, Int16To32, sizeof(Int16To32));
+ IntCallback[2] = IntNumber;
+
+ /* Register the 16-bit interrupt callback */
+ return RegisterInt16(FarPtr,
+ IntNumber,
+ IntCallback,
+ sizeof(IntCallback),
+ CodeSize);
+}
+
+VOID
+Int32Call(IN PCALLBACK16 Context,
+ IN BYTE IntNumber)
+{
+ PUCHAR TrampolineBase = (PUCHAR)FAR_POINTER(Context->TrampolineFarPtr);
+ PUCHAR Trampoline = TrampolineBase;
+ UCHAR OldTrampoline[INT16_TRAMPOLINE_SIZE];
+
+ DPRINT("Int32Call(0x%X)\n", IntNumber);
+
+ /* Save the old trampoline */
+ // RtlCopyMemory(OldTrampoline, TrampolineBase, sizeof(OldTrampoline));
+ ((PULONGLONG)&OldTrampoline)[0] = ((PULONGLONG)TrampolineBase)[0];
+
+ /* Build the generic entry-point for 32-bit calls */
+ if (IntNumber == 0x03)
+ {
+ /* We are redefining for INT 03h */
+ *Trampoline++ = 0xCC; // Call INT 03h
+ /** *Trampoline++ = 0x90; // nop **/
+ }
+ else
+ {
+ /* Normal interrupt */
+ *Trampoline++ = 0xCD; // Call INT XXh
+ *Trampoline++ = IntNumber;
+ }
+ *(PULONG)Trampoline = UnSimulate16;
+
+ /* Perform the call */
+ Call16(HIWORD(Context->TrampolineFarPtr),
+ LOWORD(Context->TrampolineFarPtr));
+
+ /* Restore the old trampoline */
+ // RtlCopyMemory(TrampolineBase, OldTrampoline, sizeof(OldTrampoline));
+ ((PULONGLONG)TrampolineBase)[0] = ((PULONGLONG)&OldTrampoline)[0];
+}
+
+
VOID WINAPI Int32Dispatch(LPWORD Stack)
{
@@ -41,7 +269,7 @@
if (Int32Proc[IntNum] != NULL)
Int32Proc[IntNum](Stack);
else
- DPRINT("Unhandled 32-bit interrupt: 0x%02X, AX = 0x%04X\n", IntNum,
getAX());
+ DPRINT1("Unhandled 32-bit interrupt: 0x%02X, AX = 0x%04X\n", IntNum,
getAX());
}
VOID WINAPI ControlBop(LPWORD Stack)
@@ -53,79 +281,14 @@
if (FuncNum == BOP_CONTROL_INT32)
Int32Dispatch(Stack);
else
- DPRINT("Unassigned Control BOP Function: 0x%02X\n", FuncNum);
-}
-
-VOID InitializeInt32(WORD BiosSegment)
-{
- LPDWORD IntVecTable = (LPDWORD)BaseAddress;
- LPBYTE BiosCode = (LPBYTE)SEG_OFF_TO_PTR(BiosSegment, 0);
- USHORT i;
- WORD BopSeqOffset, Offset = 0;
-
- /* Generate ISR stubs and fill the IVT */
- for (i = 0x00; i <= 0xFF; i++)
- {
- Offset = INT_HANDLER_OFFSET + (i << 4);
- IntVecTable[i] = MAKELONG(Offset, BiosSegment);
-
- BiosCode[Offset++] = 0xFA; // cli
-
- BiosCode[Offset++] = 0x6A; // push i
- BiosCode[Offset++] = (UCHAR)i;
-
- /* The counter variable (initialized to 0) */
- BiosCode[Offset++] = 0x6A; // push 0
- BiosCode[Offset++] = 0x00;
-
- /* Stack variables */
- BiosCode[Offset++] = 0x83; // sub sp, 4
- BiosCode[Offset++] = 0xEC;
- BiosCode[Offset++] = 0x04;
-
- BopSeqOffset = COMMON_STUB_OFFSET - (Offset + 3);
-
- BiosCode[Offset++] = 0xE9; // jmp near BOP_SEQ
- BiosCode[Offset++] = LOBYTE(BopSeqOffset);
- BiosCode[Offset++] = HIBYTE(BopSeqOffset);
- }
-
- /* Write the common stub code */
- Offset = COMMON_STUB_OFFSET;
-
-// BOP_SEQ:
- BiosCode[Offset++] = 0xF8; // clc
-
- BiosCode[Offset++] = LOBYTE(EMULATOR_BOP); // BOP sequence
- BiosCode[Offset++] = HIBYTE(EMULATOR_BOP);
- BiosCode[Offset++] = BOP_CONTROL; // Control BOP
- BiosCode[Offset++] = BOP_CONTROL_INT32; // 32-bit Interrupt dispatcher
-
- BiosCode[Offset++] = 0x73; // jnc EXIT (offset +4)
- BiosCode[Offset++] = 0x04;
-
- BiosCode[Offset++] = 0xFB; // sti
-
- // HACK: The following instruction should be HLT!
- BiosCode[Offset++] = 0x90; // nop
-
- BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -11)
- BiosCode[Offset++] = 0xF5;
-
-// EXIT:
- BiosCode[Offset++] = 0x83; // add sp, 8
- BiosCode[Offset++] = 0xC4;
- BiosCode[Offset++] = 0x08;
-
- BiosCode[Offset++] = 0xCF; // iret
-
+ // DPRINT1("Unassigned Control BOP Function: 0x%02X\n", FuncNum);
+ DisplayMessage(L"Unassigned Control BOP Function: 0x%02X\n", FuncNum);
+}
+
+VOID InitializeCallbacks(VOID)
+{
/* Register the Control BOP */
RegisterBop(BOP_CONTROL, ControlBop);
}
-VOID RegisterInt32(BYTE IntNumber, EMULATOR_INT32_PROC IntHandler)
-{
- Int32Proc[IntNumber] = IntHandler;
-}
-
/* EOF */
Copied: branches/ntvdm/subsystems/ntvdm/callback.h (from r62251,
branches/ntvdm/subsystems/ntvdm/int32.h)
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/callback…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/int32.h [iso-8859-1] (original)
+++ branches/ntvdm/subsystems/ntvdm/callback.h [iso-8859-1] Fri Feb 21 20:31:56 2014
@@ -1,14 +1,14 @@
/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
- * FILE: int32.h
+ * FILE: callback.h
* PURPOSE: 32-bit Interrupt Handlers
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
*/
-#ifndef _INT32_H_
-#define _INT32_H_
+#ifndef _CALLBACK_H_
+#define _CALLBACK_H_
/* DEFINES ********************************************************************/
@@ -18,14 +18,38 @@
#define INT_HANDLER_OFFSET 0x1000
#define COMMON_STUB_OFFSET 0x2000
+
+typedef struct _CALLBACK16
+{
+ ULONG TrampolineFarPtr; // Where the trampoline zone is placed
+ USHORT Segment;
+ USHORT NextOffset;
+} CALLBACK16, *PCALLBACK16;
+
+
/* FUNCTIONS ******************************************************************/
typedef VOID (WINAPI *EMULATOR_INT32_PROC)(LPWORD Stack);
+VOID
+InitializeContext(IN PCALLBACK16 Context,
+ IN USHORT Segment,
+ IN USHORT Offset);
+
VOID WINAPI Int32Dispatch(LPWORD Stack);
-VOID InitializeInt32(WORD BiosSegment);
-VOID RegisterInt32(BYTE IntNumber, EMULATOR_INT32_PROC IntHandler);
-#endif // _INT32_H_
+ULONG
+RegisterInt32(IN ULONG FarPtr,
+ IN BYTE IntNumber,
+ IN EMULATOR_INT32_PROC IntHandler,
+ OUT PSIZE_T CodeSize OPTIONAL);
+
+VOID
+Int32Call(IN PCALLBACK16 Context,
+ IN BYTE IntNumber);
+
+VOID InitializeCallbacks(VOID);
+
+#endif // _CALLBACK_H_
/* EOF */