-/*\r
- * PROJECT: ReactOS Kernel\r
- * LICENSE: GPL - See COPYING in the top level directory\r
- * FILE: ntoskrnl/vdm/vdmexec.c\r
- * PURPOSE: Support for executing VDM code and context swapping.\r
- * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)\r
- */\r
-\r
-/* INCLUDES *****************************************************************/\r
-\r
-#include <ntoskrnl.h>\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-/* GLOBALS *******************************************************************/\r
-\r
-ULONG VdmBopCount;\r
-\r
-/* FUNCTIONS *****************************************************************/\r
-\r
-NTSTATUS\r
-NTAPI\r
-VdmpGetVdmTib(OUT PVDM_TIB *VdmTib)\r
-{\r
- PVDM_TIB Tib;\r
- PAGED_CODE();\r
-\r
- /* Assume vailure */\r
- *VdmTib = NULL;\r
-\r
- /* Get the current TIB */\r
- Tib = NtCurrentTeb()->Vdm;\r
- if (!Tib) return STATUS_INVALID_SYSTEM_SERVICE;\r
-\r
- /* Validate the size */\r
- if (Tib->Size != sizeof(VDM_TIB)) return STATUS_INVALID_SYSTEM_SERVICE;\r
-\r
- /* Return it */\r
- *VdmTib = Tib;\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-VdmSwapContext(IN PKTRAP_FRAME TrapFrame,\r
- IN PCONTEXT OutContext,\r
- IN PCONTEXT InContext)\r
-{\r
- ULONG EFlags, OldEFlags;\r
-\r
- /* Make sure that we're at APC_LEVEL and that this is a valid frame */\r
- ASSERT_IRQL(APC_LEVEL);\r
- ASSERT(TrapFrame->DbgArgMark = 0xBADB0D00);\r
-\r
- /* Check if this is a V86 frame */\r
- if (TrapFrame->EFlags & EFLAGS_V86_MASK)\r
- {\r
- /* Copy segment registers */\r
- OutContext->SegGs = TrapFrame->V86Gs;\r
- OutContext->SegFs = TrapFrame->V86Fs;\r
- OutContext->SegEs = TrapFrame->V86Es;\r
- OutContext->SegDs = TrapFrame->V86Ds;\r
- }\r
- else if (TrapFrame->SegCs == (KGDT_R3_CODE | RPL_MASK))\r
- {\r
- /* This was user mode, copy segment registers */\r
- OutContext->SegGs = TrapFrame->SegGs;\r
- OutContext->SegFs = TrapFrame->SegFs;\r
- OutContext->SegEs = TrapFrame->SegEs;\r
- OutContext->SegDs = TrapFrame->SegDs;\r
- }\r
-\r
- /* Copy CS and SS */\r
- OutContext->SegCs = TrapFrame->SegCs;\r
- OutContext->SegSs = TrapFrame->HardwareSegSs;\r
-\r
- /* Copy general purpose registers */\r
- OutContext->Eax = TrapFrame->Eax;\r
- OutContext->Ebx = TrapFrame->Ebx;\r
- OutContext->Ecx = TrapFrame->Ecx;\r
- OutContext->Edx = TrapFrame->Edx;\r
- OutContext->Esi = TrapFrame->Esi;\r
- OutContext->Edi = TrapFrame->Edi;\r
-\r
- /* Copy stack and counter */\r
- OutContext->Ebp = TrapFrame->Ebp;\r
- OutContext->Esp = TrapFrame->HardwareEsp;\r
- OutContext->Eip = TrapFrame->Eip;\r
-\r
- /* Finally the flags */\r
- OutContext->EFlags = TrapFrame->EFlags;\r
-\r
- /* Now copy from the in frame to the trap frame */\r
- TrapFrame->SegCs = InContext->SegCs;\r
- TrapFrame->HardwareSegSs = InContext->SegSs;\r
-\r
- /* Copy the general purpose registers */\r
- TrapFrame->Eax = InContext->Eax;\r
- TrapFrame->Ebx = InContext->Ebx;\r
- TrapFrame->Ecx = InContext->Ecx;\r
- TrapFrame->Edx = InContext->Edx;\r
- TrapFrame->Esi = InContext->Esi;\r
- TrapFrame->Edi = InContext->Edi;\r
-\r
- /* Copy the stack and counter */\r
- TrapFrame->Ebp = InContext->Ebp;\r
- TrapFrame->HardwareEsp = InContext->Esp;\r
- TrapFrame->Eip = InContext->Eip;\r
-\r
- /* Check if the context is from V86 */\r
- EFlags = InContext->EFlags;\r
- if (EFlags & EFLAGS_V86_MASK)\r
- {\r
- /* Sanitize the flags for V86 */\r
- EFlags &= KeI386EFlagsAndMaskV86;\r
- EFlags |= KeI386EFlagsOrMaskV86;\r
- }\r
- else\r
- {\r
- /* Add RPL_MASK to segments */\r
- TrapFrame->SegCs |= RPL_MASK;\r
- TrapFrame->HardwareSegSs |= RPL_MASK;\r
-\r
- /* Check for bogus CS */\r
- if (TrapFrame->SegCs < KGDT_R0_CODE)\r
- {\r
- /* Set user-mode */\r
- TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;\r
- }\r
-\r
- /* Sanitize flags and add interrupt mask */\r
- EFlags &= EFLAGS_USER_SANITIZE;\r
- EFlags |=EFLAGS_INTERRUPT_MASK;\r
- }\r
-\r
- /* Save the new eflags */\r
- OldEFlags = TrapFrame->EFlags;\r
- TrapFrame->EFlags = EFlags;\r
-\r
- /* Check if we need to fixup ESP0 */\r
- if ((OldEFlags ^ EFlags) & EFLAGS_V86_MASK)\r
- {\r
- /* Fix it up */\r
- Ki386AdjustEsp0(TrapFrame);\r
- }\r
-\r
- /* Check if this is a V86 context */\r
- if (InContext->EFlags & EFLAGS_V86_MASK)\r
- {\r
- /* Copy VDM segments */\r
- TrapFrame->V86Gs = InContext->SegGs;\r
- TrapFrame->V86Fs = InContext->SegFs;\r
- TrapFrame->V86Es = InContext->SegEs;\r
- TrapFrame->V86Ds = InContext->SegDs;\r
- }\r
- else\r
- {\r
- /* Copy monitor segments */\r
- TrapFrame->SegGs = InContext->SegGs;\r
- TrapFrame->SegFs = InContext->SegFs;\r
- TrapFrame->SegEs = InContext->SegEs;\r
- TrapFrame->SegDs = InContext->SegDs;\r
- }\r
-\r
- /* Clear the exception list and return */\r
- TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-VdmpStartExecution(VOID)\r
-{\r
- PETHREAD Thread = PsGetCurrentThread();\r
- PKTRAP_FRAME VdmFrame;\r
- NTSTATUS Status;\r
- PVDM_TIB VdmTib;\r
- BOOLEAN Interrupts;\r
- KIRQL OldIrql;\r
- CONTEXT VdmContext;\r
- PAGED_CODE();\r
-\r
- /* Get the thread's VDM frame and TIB */\r
- VdmFrame = (PVOID)((ULONG_PTR)Thread->Tcb.InitialStack -\r
- sizeof(FX_SAVE_AREA) -\r
- sizeof(KTRAP_FRAME));\r
- Status = VdmpGetVdmTib(&VdmTib);\r
- if (!NT_SUCCESS(Status)) return STATUS_INVALID_SYSTEM_SERVICE;\r
-\r
- /* Go to APC level */\r
- KeRaiseIrql(APC_LEVEL, &OldIrql);\r
-\r
- /* Check if interrupts are enabled */\r
- Interrupts = (BOOLEAN)(VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK);\r
-\r
- /* We don't support full VDM yet, this shouldn't happen */\r
- ASSERT(*VdmState == 0);\r
- ASSERT(VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK);\r
-\r
- /* Get the VDM context and make sure it's an edited frame */\r
- VdmContext = VdmTib->VdmContext;\r
- if (!(VdmContext.SegCs & FRAME_EDITED))\r
- {\r
- /* Fail */\r
- KeLowerIrql(OldIrql);\r
- return STATUS_INVALID_SYSTEM_SERVICE;\r
- }\r
-\r
- /* FIXME: Support VME */\r
- ASSERT(VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK);\r
-\r
- /* Set interrupt state in the VDM State */\r
- if (VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK)\r
- {\r
- /* Enable them as well */\r
- InterlockedOr((PLONG)VdmState, EFLAGS_INTERRUPT_MASK);\r
- }\r
- else\r
- {\r
- /* Disable them */\r
- InterlockedAnd((PLONG)VdmState, ~EFLAGS_INTERRUPT_MASK);\r
- }\r
-\r
- /* Enable the interrupt flag */\r
- VdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;\r
-\r
- /* Now do the VDM Swap */\r
- VdmSwapContext(VdmFrame, &VdmTib->MonitorContext, &VdmContext);\r
-\r
- /* Lower the IRQL and return EAX */\r
- KeLowerIrql(OldIrql);\r
- return VdmFrame->Eax;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-VdmEndExecution(IN PKTRAP_FRAME TrapFrame,\r
- IN PVDM_TIB VdmTib)\r
-{\r
- KIRQL OldIrql;\r
- CONTEXT Context;\r
- PAGED_CODE();\r
-\r
- /* Sanity check */\r
- ASSERT((TrapFrame->EFlags & EFLAGS_V86_MASK) ||\r
- (TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK)));\r
-\r
- /* Raise to APC_LEVEL */\r
- KeRaiseIrql(APC_LEVEL, &OldIrql);\r
-\r
- /* Set success */\r
- VdmTib->MonitorContext.Eax = STATUS_SUCCESS;\r
-\r
- /* Make a copy of the monitor context */\r
- RtlCopyMemory(&Context, &VdmTib->MonitorContext, sizeof(CONTEXT));\r
-\r
- /* Switch contexts */\r
- VdmSwapContext(TrapFrame, &VdmTib->VdmContext, &Context);\r
-\r
- /* FIXME: Support VME */\r
-\r
- /* Set the EFLAGS */\r
- VdmTib->VdmContext.EFlags = (VdmTib->VdmContext.EFlags &\r
- ~EFLAGS_INTERRUPT_MASK) |\r
- (*VdmState & EFLAGS_INTERRUPT_MASK);\r
-\r
- /* Lower IRQL and reutrn */\r
- KeLowerIrql(OldIrql);\r
-}\r
-\r
-BOOLEAN\r
-NTAPI\r
-VdmDispatchBop(IN PKTRAP_FRAME TrapFrame)\r
-{\r
- PUCHAR Eip;\r
- PVDM_TIB VdmTib;\r
-\r
- /* Check if this is from V86 mode */\r
- if (TrapFrame->EFlags & EFLAGS_V86_MASK)\r
- {\r
- /* Calculate flat EIP */\r
- Eip = (PUCHAR)((TrapFrame->Eip & 0xFFFF) +\r
- ((TrapFrame->SegCs & 0xFFFF) << 4));\r
-\r
- /* Check if this is a BOP */\r
- if (*(PUSHORT)Eip == 0xC4C4)\r
- {\r
- /* Check sure its the DOS Bop */\r
- if (Eip[2] == 0x50)\r
- {\r
- /* FIXME: No VDM Support */\r
- ASSERT(FALSE);\r
- }\r
-\r
- /* Increase the number of BOP operations */\r
- VdmBopCount++;\r
-\r
- /* Get the TIB */\r
- VdmTib = NtCurrentTeb()->Vdm;\r
-\r
- /* Fill out a VDM Event */\r
- VdmTib->EventInfo.InstructionSize = 3;\r
- VdmTib->EventInfo.BopNumber = Eip[2];\r
- VdmTib->EventInfo.Event = VdmBop;\r
-\r
- /* End VDM Execution */\r
- VdmEndExecution(TrapFrame, VdmTib);\r
- }\r
- else\r
- {\r
- /* Not a BOP */\r
- return FALSE;\r
- }\r
- }\r
- else\r
- {\r
- /* FIXME: Shouldn't happen on ROS */\r
- ASSERT(FALSE);\r
- }\r
-\r
- /* Return success */\r
- return TRUE;\r
-}\r
-\r
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: ntoskrnl/vdm/vdmexec.c
+ * PURPOSE: Support for executing VDM code and context swapping.
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS *******************************************************************/
+
+ULONG VdmBopCount;
+
+/* FUNCTIONS *****************************************************************/
+
+NTSTATUS
+NTAPI
+VdmpGetVdmTib(OUT PVDM_TIB *VdmTib)
+{
+ PVDM_TIB Tib;
+ PAGED_CODE();
+
+ /* Assume vailure */
+ *VdmTib = NULL;
+
+ /* Get the current TIB */
+ Tib = NtCurrentTeb()->Vdm;
+ if (!Tib) return STATUS_INVALID_SYSTEM_SERVICE;
+
+ /* Validate the size */
+ if (Tib->Size != sizeof(VDM_TIB)) return STATUS_INVALID_SYSTEM_SERVICE;
+
+ /* Return it */
+ *VdmTib = Tib;
+ return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+VdmSwapContext(IN PKTRAP_FRAME TrapFrame,
+ IN PCONTEXT OutContext,
+ IN PCONTEXT InContext)
+{
+ ULONG EFlags, OldEFlags;
+
+ /* Make sure that we're at APC_LEVEL and that this is a valid frame */
+ ASSERT_IRQL(APC_LEVEL);
+ ASSERT(TrapFrame->DbgArgMark = 0xBADB0D00);
+
+ /* Check if this is a V86 frame */
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK)
+ {
+ /* Copy segment registers */
+ OutContext->SegGs = TrapFrame->V86Gs;
+ OutContext->SegFs = TrapFrame->V86Fs;
+ OutContext->SegEs = TrapFrame->V86Es;
+ OutContext->SegDs = TrapFrame->V86Ds;
+ }
+ else if (TrapFrame->SegCs == (KGDT_R3_CODE | RPL_MASK))
+ {
+ /* This was user mode, copy segment registers */
+ OutContext->SegGs = TrapFrame->SegGs;
+ OutContext->SegFs = TrapFrame->SegFs;
+ OutContext->SegEs = TrapFrame->SegEs;
+ OutContext->SegDs = TrapFrame->SegDs;
+ }
+
+ /* Copy CS and SS */
+ OutContext->SegCs = TrapFrame->SegCs;
+ OutContext->SegSs = TrapFrame->HardwareSegSs;
+
+ /* Copy general purpose registers */
+ OutContext->Eax = TrapFrame->Eax;
+ OutContext->Ebx = TrapFrame->Ebx;
+ OutContext->Ecx = TrapFrame->Ecx;
+ OutContext->Edx = TrapFrame->Edx;
+ OutContext->Esi = TrapFrame->Esi;
+ OutContext->Edi = TrapFrame->Edi;
+
+ /* Copy stack and counter */
+ OutContext->Ebp = TrapFrame->Ebp;
+ OutContext->Esp = TrapFrame->HardwareEsp;
+ OutContext->Eip = TrapFrame->Eip;
+
+ /* Finally the flags */
+ OutContext->EFlags = TrapFrame->EFlags;
+
+ /* Now copy from the in frame to the trap frame */
+ TrapFrame->SegCs = InContext->SegCs;
+ TrapFrame->HardwareSegSs = InContext->SegSs;
+
+ /* Copy the general purpose registers */
+ TrapFrame->Eax = InContext->Eax;
+ TrapFrame->Ebx = InContext->Ebx;
+ TrapFrame->Ecx = InContext->Ecx;
+ TrapFrame->Edx = InContext->Edx;
+ TrapFrame->Esi = InContext->Esi;
+ TrapFrame->Edi = InContext->Edi;
+
+ /* Copy the stack and counter */
+ TrapFrame->Ebp = InContext->Ebp;
+ TrapFrame->HardwareEsp = InContext->Esp;
+ TrapFrame->Eip = InContext->Eip;
+
+ /* Check if the context is from V86 */
+ EFlags = InContext->EFlags;
+ if (EFlags & EFLAGS_V86_MASK)
+ {
+ /* Sanitize the flags for V86 */
+ EFlags &= KeI386EFlagsAndMaskV86;
+ EFlags |= KeI386EFlagsOrMaskV86;
+ }
+ else
+ {
+ /* Add RPL_MASK to segments */
+ TrapFrame->SegCs |= RPL_MASK;
+ TrapFrame->HardwareSegSs |= RPL_MASK;
+
+ /* Check for bogus CS */
+ if (TrapFrame->SegCs < KGDT_R0_CODE)
+ {
+ /* Set user-mode */
+ TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
+ }
+
+ /* Sanitize flags and add interrupt mask */
+ EFlags &= EFLAGS_USER_SANITIZE;
+ EFlags |=EFLAGS_INTERRUPT_MASK;
+ }
+
+ /* Save the new eflags */
+ OldEFlags = TrapFrame->EFlags;
+ TrapFrame->EFlags = EFlags;
+
+ /* Check if we need to fixup ESP0 */
+ if ((OldEFlags ^ EFlags) & EFLAGS_V86_MASK)
+ {
+ /* Fix it up */
+ Ki386AdjustEsp0(TrapFrame);
+ }
+
+ /* Check if this is a V86 context */
+ if (InContext->EFlags & EFLAGS_V86_MASK)
+ {
+ /* Copy VDM segments */
+ TrapFrame->V86Gs = InContext->SegGs;
+ TrapFrame->V86Fs = InContext->SegFs;
+ TrapFrame->V86Es = InContext->SegEs;
+ TrapFrame->V86Ds = InContext->SegDs;
+ }
+ else
+ {
+ /* Copy monitor segments */
+ TrapFrame->SegGs = InContext->SegGs;
+ TrapFrame->SegFs = InContext->SegFs;
+ TrapFrame->SegEs = InContext->SegEs;
+ TrapFrame->SegDs = InContext->SegDs;
+ }
+
+ /* Clear the exception list and return */
+ TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
+}
+
+NTSTATUS
+NTAPI
+VdmpStartExecution(VOID)
+{
+ PETHREAD Thread = PsGetCurrentThread();
+ PKTRAP_FRAME VdmFrame;
+ NTSTATUS Status;
+ PVDM_TIB VdmTib;
+ BOOLEAN Interrupts;
+ KIRQL OldIrql;
+ CONTEXT VdmContext;
+ PAGED_CODE();
+
+ /* Get the thread's VDM frame and TIB */
+ VdmFrame = (PVOID)((ULONG_PTR)Thread->Tcb.InitialStack -
+ sizeof(FX_SAVE_AREA) -
+ sizeof(KTRAP_FRAME));
+ Status = VdmpGetVdmTib(&VdmTib);
+ if (!NT_SUCCESS(Status)) return STATUS_INVALID_SYSTEM_SERVICE;
+
+ /* Go to APC level */
+ KeRaiseIrql(APC_LEVEL, &OldIrql);
+
+ /* Check if interrupts are enabled */
+ Interrupts = (BOOLEAN)(VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK);
+
+ /* We don't support full VDM yet, this shouldn't happen */
+ ASSERT(*VdmState == 0);
+ ASSERT(VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK);
+
+ /* Get the VDM context and make sure it's an edited frame */
+ VdmContext = VdmTib->VdmContext;
+ if (!(VdmContext.SegCs & FRAME_EDITED))
+ {
+ /* Fail */
+ KeLowerIrql(OldIrql);
+ return STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* FIXME: Support VME */
+ ASSERT(VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK);
+
+ /* Set interrupt state in the VDM State */
+ if (VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK)
+ {
+ /* Enable them as well */
+ InterlockedOr((PLONG)VdmState, EFLAGS_INTERRUPT_MASK);
+ }
+ else
+ {
+ /* Disable them */
+ InterlockedAnd((PLONG)VdmState, ~EFLAGS_INTERRUPT_MASK);
+ }
+
+ /* Enable the interrupt flag */
+ VdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;
+
+ /* Now do the VDM Swap */
+ VdmSwapContext(VdmFrame, &VdmTib->MonitorContext, &VdmContext);
+
+ /* Lower the IRQL and return EAX */
+ KeLowerIrql(OldIrql);
+ return VdmFrame->Eax;
+}
+
+VOID
+NTAPI
+VdmEndExecution(IN PKTRAP_FRAME TrapFrame,
+ IN PVDM_TIB VdmTib)
+{
+ KIRQL OldIrql;
+ CONTEXT Context;
+ PAGED_CODE();
+
+ /* Sanity check */
+ ASSERT((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
+ (TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK)));
+
+ /* Raise to APC_LEVEL */
+ KeRaiseIrql(APC_LEVEL, &OldIrql);
+
+ /* Set success */
+ VdmTib->MonitorContext.Eax = STATUS_SUCCESS;
+
+ /* Make a copy of the monitor context */
+ RtlCopyMemory(&Context, &VdmTib->MonitorContext, sizeof(CONTEXT));
+
+ /* Switch contexts */
+ VdmSwapContext(TrapFrame, &VdmTib->VdmContext, &Context);
+
+ /* FIXME: Support VME */
+
+ /* Set the EFLAGS */
+ VdmTib->VdmContext.EFlags = (VdmTib->VdmContext.EFlags &
+ ~EFLAGS_INTERRUPT_MASK) |
+ (*VdmState & EFLAGS_INTERRUPT_MASK);
+
+ /* Lower IRQL and reutrn */
+ KeLowerIrql(OldIrql);
+}
+
+BOOLEAN
+NTAPI
+VdmDispatchBop(IN PKTRAP_FRAME TrapFrame)
+{
+ PUCHAR Eip;
+ PVDM_TIB VdmTib;
+
+ /* Check if this is from V86 mode */
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK)
+ {
+ /* Calculate flat EIP */
+ Eip = (PUCHAR)((TrapFrame->Eip & 0xFFFF) +
+ ((TrapFrame->SegCs & 0xFFFF) << 4));
+
+ /* Check if this is a BOP */
+ if (*(PUSHORT)Eip == 0xC4C4)
+ {
+ /* Check sure its the DOS Bop */
+ if (Eip[2] == 0x50)
+ {
+ /* FIXME: No VDM Support */
+ ASSERT(FALSE);
+ }
+
+ /* Increase the number of BOP operations */
+ VdmBopCount++;
+
+ /* Get the TIB */
+ VdmTib = NtCurrentTeb()->Vdm;
+
+ /* Fill out a VDM Event */
+ VdmTib->EventInfo.InstructionSize = 3;
+ VdmTib->EventInfo.BopNumber = Eip[2];
+ VdmTib->EventInfo.Event = VdmBop;
+
+ /* End VDM Execution */
+ VdmEndExecution(TrapFrame, VdmTib);
+ }
+ else
+ {
+ /* Not a BOP */
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* FIXME: Shouldn't happen on ROS */
+ ASSERT(FALSE);
+ }
+
+ /* Return success */
+ return TRUE;
+}
+