313 lines
8.3 KiB
C
313 lines
8.3 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
abiosc.c
|
||
|
||
Abstract:
|
||
|
||
This module implements ROM BIOS support C routines for i386 NT.
|
||
|
||
Author:
|
||
|
||
Shie-Lin Tzong (shielint) 10-Sept-1992
|
||
|
||
Environment:
|
||
|
||
Kernel mode.
|
||
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "ki.h"
|
||
#pragma hdrstop
|
||
#include "vdmntos.h"
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,Ke386CallBios)
|
||
#endif
|
||
|
||
|
||
//
|
||
// Never change these equates without checking biosa.asm
|
||
//
|
||
|
||
#define V86_CODE_ADDRESS 0x10000
|
||
#define INT_OPCODE 0xcd
|
||
#define V86_BOP_OPCODE 0xfec4c4
|
||
#define V86_STACK_POINTER 0x1ffe
|
||
#define IOPM_OFFSET FIELD_OFFSET(KTSS, IoMaps[0].IoMap)
|
||
#define VDM_TIB_ADDRESS 0x12000
|
||
#define INT_10_TEB 0x13000
|
||
|
||
//
|
||
// External References
|
||
//
|
||
|
||
PVOID Ki386IopmSaveArea;
|
||
VOID
|
||
Ki386SetupAndExitToV86Code (
|
||
PVOID ExecutionAddress
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
Ke386CallBios (
|
||
IN ULONG BiosCommand,
|
||
IN OUT PCONTEXT BiosArguments
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function invokes specified ROM BIOS code by executing
|
||
"INT BiosCommand." Before executing the BIOS code, this function
|
||
will setup VDM context, change stack pointer ...etc. If for some reason
|
||
the operation fails, a status code will be returned. Otherwise, this
|
||
function always returns success regardless of the result of the BIOS
|
||
call.
|
||
|
||
N.B. This implementation relies on the fact that the direct
|
||
I/O access operations between apps are serialized by win user.
|
||
|
||
Arguments:
|
||
|
||
BiosCommand - Supplies which ROM BIOS function to invoke.
|
||
|
||
BiosArguments - Supplies a pointer to the context which will be used
|
||
to invoke ROM BIOS.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code to specify the failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PVDM_TIB VdmTib;
|
||
PUCHAR BaseAddress = (PUCHAR)V86_CODE_ADDRESS;
|
||
PTEB UserInt10Teb = (PTEB)INT_10_TEB;
|
||
PKTSS Tss;
|
||
PKPROCESS Process;
|
||
PKTHREAD Thread;
|
||
USHORT OldIopmOffset, OldIoMapBase;
|
||
PVDM_PROCESS_OBJECTS VdmObjects;
|
||
ULONG ContextLength;
|
||
BOOLEAN ThreadDebugActive;
|
||
|
||
//
|
||
// Map in ROM BIOS area to perform the int 10 code
|
||
//
|
||
|
||
try {
|
||
|
||
RtlZeroMemory(UserInt10Teb, sizeof(TEB));
|
||
|
||
//
|
||
// Write "Int BiosCommand; bop" to reserved user space (0x1000).
|
||
// Later control will transfer to the user space to execute
|
||
// these two instructions.
|
||
//
|
||
|
||
*BaseAddress++ = INT_OPCODE;
|
||
*BaseAddress++ = (UCHAR)BiosCommand;
|
||
*(PULONG)BaseAddress = V86_BOP_OPCODE;
|
||
|
||
//
|
||
// Set up Vdm(v86) context to execute the int BiosCommand
|
||
// instruction by copying user supplied context to VdmContext
|
||
// and updating the control registers to predefined values.
|
||
//
|
||
|
||
//
|
||
// We want to use a constant number for the int10.
|
||
//
|
||
// Create a fake TEB so we can switch the thread to it while we
|
||
// do an int10
|
||
//
|
||
|
||
UserInt10Teb->Vdm = (PVOID)VDM_TIB_ADDRESS;
|
||
VdmTib = (PVDM_TIB)VDM_TIB_ADDRESS;
|
||
RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
|
||
VdmTib->Size = sizeof(VDM_TIB);
|
||
|
||
*FIXED_NTVDMSTATE_LINEAR_PC_AT = 0;
|
||
|
||
//
|
||
// extended registers are never going to matter to
|
||
// an Int10 call, so only copy the old part of the
|
||
// context record.
|
||
//
|
||
ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
||
|
||
RtlCopyMemory(&(VdmTib->VdmContext), BiosArguments, ContextLength);
|
||
VdmTib->VdmContext.SegCs = (ULONG)BaseAddress >> 4;
|
||
VdmTib->VdmContext.SegSs = (ULONG)BaseAddress >> 4;
|
||
VdmTib->VdmContext.Eip = 0;
|
||
VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG);
|
||
VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
|
||
VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// The vdm kernel code finds the Tib by looking at a pointer cached in
|
||
// kernel memory, which was probed at Vdm creation time. Since the
|
||
// creation semantics for this vdm are peculiar, we do something similar
|
||
// here.
|
||
//
|
||
|
||
//
|
||
// We never get here on a process that is a real vdm. If we do,
|
||
// bad things will happen (pool leak, failure to execute dos and
|
||
// windows apps).
|
||
//
|
||
|
||
ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
|
||
|
||
VdmObjects = ExAllocatePoolWithTag (NonPagedPool,
|
||
sizeof(VDM_PROCESS_OBJECTS),
|
||
' eK'
|
||
);
|
||
|
||
//
|
||
// Since we are doing this on behalf of CSR not a user process, we aren't
|
||
// charging quota.
|
||
//
|
||
|
||
if (VdmObjects == NULL) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
//
|
||
// We are only initializing the VdmTib pointer, because that's the only
|
||
// part of the VdmObjects we use for ROM calls. We aren't set up
|
||
// to simulate interrupts, or any of the other stuff that would be done
|
||
// in a conventional vdm
|
||
//
|
||
|
||
RtlZeroMemory( VdmObjects, sizeof(VDM_PROCESS_OBJECTS));
|
||
|
||
VdmObjects->VdmTib = VdmTib;
|
||
|
||
PsGetCurrentProcess()->VdmObjects = VdmObjects;
|
||
|
||
//
|
||
// Since we are going to v86 mode and accessing some I/O ports, we
|
||
// need to make sure the IopmOffset is set correctly across context
|
||
// swap and the I/O bit map has all the bits cleared.
|
||
// N.B. This implementation assumes that there is only one full
|
||
// screen DOS app and the io access between full screen DOS
|
||
// app and the server code is serialized by win user. That
|
||
// means even we change the IOPM, the full screen dos app won't
|
||
// be able to run on this IOPM.
|
||
// * In another words, IF THERE IS
|
||
// * MORE THAN ONE FULL SCREEN DOS APPS, THIS CODE IS BROKEN.*
|
||
//
|
||
// NOTE This code works on the assumption that winuser serializes
|
||
// direct I/O access operations.
|
||
//
|
||
|
||
//
|
||
// Call the bios from the processor which booted the machine.
|
||
//
|
||
|
||
Thread = KeGetCurrentThread();
|
||
KeSetSystemAffinityThread(1);
|
||
Tss = KeGetPcr()->TSS;
|
||
|
||
//
|
||
// Save away the original IOPM bit map and clear all the IOPM bits
|
||
// to allow v86 int 10 code to access ALL the io ports.
|
||
//
|
||
|
||
//
|
||
// Make sure there are at least 2 IOPM maps.
|
||
//
|
||
|
||
ASSERT(KeGetPcr()->GDT[KGDT_TSS / 8].LimitLow >= (0x2000 + IOPM_OFFSET - 1));
|
||
RtlCopyMemory (Ki386IopmSaveArea,
|
||
(PVOID)&Tss->IoMaps[0].IoMap,
|
||
PAGE_SIZE * 2
|
||
);
|
||
RtlZeroMemory ((PVOID)&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
|
||
|
||
Process = Thread->ApcState.Process;
|
||
OldIopmOffset = Process->IopmOffset;
|
||
OldIoMapBase = Tss->IoMapBase;
|
||
Process->IopmOffset = (USHORT)(IOPM_OFFSET); // Set Process IoPmOffset before
|
||
Tss->IoMapBase = (USHORT)(IOPM_OFFSET); // updating Tss IoMapBase
|
||
|
||
//
|
||
// The context setup for the BIOS will not have valid debug registers
|
||
// in it, don't try to load them.
|
||
//
|
||
|
||
ThreadDebugActive = Thread->DebugActive;
|
||
Thread->DebugActive = FALSE;
|
||
KeGetPcr()->DebugActive = FALSE;
|
||
|
||
//
|
||
// Call ASM routine to switch stack to exit to v86 mode to
|
||
// run Int BiosCommand.
|
||
//
|
||
|
||
Ki386SetupAndExitToV86Code(UserInt10Teb);
|
||
|
||
//
|
||
// After we return from v86 mode, the control comes here.
|
||
//
|
||
// Restore Thread's DebugActive flag.
|
||
//
|
||
|
||
KeGetPcr()->DebugActive = ThreadDebugActive;
|
||
Thread->DebugActive = ThreadDebugActive;
|
||
|
||
//
|
||
// Restore old IOPM
|
||
//
|
||
|
||
RtlCopyMemory ((PVOID)&Tss->IoMaps[0].IoMap,
|
||
Ki386IopmSaveArea,
|
||
PAGE_SIZE * 2
|
||
);
|
||
|
||
Process->IopmOffset = OldIopmOffset;
|
||
Tss->IoMapBase = OldIoMapBase;
|
||
|
||
//
|
||
// Restore old affinity for current thread.
|
||
//
|
||
|
||
KeRevertToUserAffinityThread();
|
||
|
||
//
|
||
// Copy 16 bit vdm context back to caller.
|
||
//
|
||
// Extended register state is not going to matter,
|
||
// so copy only the old part of the context record.
|
||
//
|
||
|
||
ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
||
RtlCopyMemory(BiosArguments, &(VdmTib->VdmContext), ContextLength);
|
||
BiosArguments->ContextFlags = CONTEXT_FULL;
|
||
|
||
//
|
||
// Free the pool used for the VdmTib pointer
|
||
//
|
||
|
||
PsGetCurrentProcess()->VdmObjects = NULL;
|
||
ExFreePool(VdmObjects);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|