summaryrefslogtreecommitdiffstats
path: root/private/mvdm/softpc.new/host/src/nt_msscs.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/mvdm/softpc.new/host/src/nt_msscs.c')
-rw-r--r--private/mvdm/softpc.new/host/src/nt_msscs.c1416
1 files changed, 1416 insertions, 0 deletions
diff --git a/private/mvdm/softpc.new/host/src/nt_msscs.c b/private/mvdm/softpc.new/host/src/nt_msscs.c
new file mode 100644
index 000000000..d9286056a
--- /dev/null
+++ b/private/mvdm/softpc.new/host/src/nt_msscs.c
@@ -0,0 +1,1416 @@
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <vdmapi.h>
+#include <vdm.h>
+#include "insignia.h"
+#include "host_def.h"
+#include "conapi.h"
+#include "ctype.h"
+#include "stdlib.h"
+#include "stdio.h"
+#include "string.h"
+#include <io.h>
+#include <fcntl.h>
+
+#include "xt.h"
+#include CpuH
+#include "error.h"
+#include "sas.h"
+#include "ios.h"
+#include "umb.h"
+#include "gvi.h"
+#include "sim32.h"
+#include "bios.h"
+
+#include "nt_eoi.h"
+#include "nt_uis.h"
+#include "nt_event.h"
+#include "nt_graph.h"
+#include "nt_event.h"
+#include "nt_reset.h"
+#include "config.h"
+#include <nt_vdd.h> // DO NOT USE vddsvc.h
+#include <nt_vddp.h>
+#include <host_emm.h>
+#include "emm.h"
+#include <demexp.h>
+#include <vint.h>
+
+PMEM_HOOK_DATA MemHookHead = NULL;
+PVDD_USER_HANDLERS UserHookHead= NULL;
+
+extern BOOL CMDInit (int argc,char *argv[]);
+extern BOOL XMSInit (int argc,char *argv[]);
+extern BOOL DBGInit (int argc,char *argv[]);
+extern DWORD TlsDirectError;
+extern VOID FloppyTerminatePDB(USHORT PDB);
+extern VOID FdiskTerminatePDB(USHORT PDB);
+
+// internal function prototypes
+VOID SetupInstallableVDD (VOID);
+void AddSystemFiles(void);
+
+void scs_init(int argc, char **argv)
+{
+ PSZ psz;
+ BOOL IsFirst;
+
+ IsFirst = GetNextVDMCommand(NULL);
+ if (IsFirst) {
+ AddSystemFiles();
+ }
+
+ // Initialize SCS
+
+ CMDInit (argc,argv);
+
+ // Initialize DOSEm
+
+ if(!DemInit (argc,argv)) {
+ host_error(EG_OWNUP, ERR_QUIT, "NTVDM:DemInit fails");
+ TerminateVDM();
+ }
+
+ // Initialize XMS
+
+ if(!XMSInit (argc,argv)) {
+ host_error(EG_OWNUP, ERR_QUIT, "NTVDM:XMSInit fails");
+ TerminateVDM();
+ }
+
+ // Initialize DBG
+
+ if(!DBGInit (argc,argv)) {
+#ifndef PROD
+ printf("NTVDM:DBGInit fails\n");
+ HostDebugBreak();
+#endif
+ TerminateVDM();
+ }
+}
+
+//
+// This routine contains the Dos Emulation initialisation code, called from
+// main(). We currently do not support container files.
+//
+
+
+InitialiseDosEmulation(int argc, char **argv)
+{
+ HANDLE hFile;
+ DWORD FileSize;
+ DWORD BytesRead;
+ DWORD dw;
+ ULONG fVirtualInt;
+ host_addr pDOSAddr;
+ CHAR buffer[MAX_PATH*2];
+#ifdef LIM
+ LIM_CONFIG_DATA lim_config_data;
+#endif
+
+ //
+ // first order of bussiness, initialize virtual interrupt flag in
+ // dos arena. this has to be done here before it gets changed
+ // by reading in ntio.sys
+ //
+
+ sas_loads((ULONG)FIXED_NTVDMSTATE_LINEAR,
+ (PCHAR)&fVirtualInt,
+ FIXED_NTVDMSTATE_SIZE
+ );
+#ifndef i386
+ fVirtualInt |= MIPS_BIT_MASK;
+#else
+ fVirtualInt &= ~MIPS_BIT_MASK;
+#endif
+ sas_storedw((ULONG)FIXED_NTVDMSTATE_LINEAR,fVirtualInt);
+
+ io_init();
+
+ //
+ // Allocate per thread local storage.
+ // Currently we only need to store one DWORD, so we
+ // don't need any per thread memory.
+ //
+ TlsDirectError = TlsAlloc();
+#ifndef PROD
+ if (TlsDirectError == 0xFFFFFFFF)
+ printf("NTVDM: TlsDirectError==0xFFFFFFFF GLE=%ld\n", GetLastError);
+#endif
+
+
+ // SetupInstallableVDD ();
+
+ /*................................................... Execute reset */
+ reset();
+
+ SetupInstallableVDD ();
+ /* reserve lim block after all vdd are installed.
+ the pif file settings tell us if it is necessary to
+ reserve the block
+ */
+
+#ifdef LIM
+ /* initialize lim page frames after all vdd are installed.
+ the pif file settings tell us if it is necessary to
+ reserve the block.
+ */
+ if (get_lim_configuration_data(&lim_config_data))
+ lim_page_frame_init(&lim_config_data);
+
+#endif
+
+ scs_init(argc, argv); // Initialise single command shell
+
+ /*................................................. Load DOSEM code */
+
+ dw = GetSystemDirectory(buffer, sizeof(buffer));
+ if (!dw || dw >= sizeof(buffer)) {
+ host_error(EG_OWNUP, ERR_QUIT, "NTVDM:InitialiseDosEmulation fails");
+ TerminateVDM();
+ }
+
+ strcat(buffer, "\\ntio.sys");
+
+ hFile = CreateFile(buffer,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if (hFile == INVALID_HANDLE_VALUE ||
+ !(FileSize = GetFileSize(hFile, &BytesRead)) ||
+ BytesRead )
+ {
+#ifndef PROD
+ printf("NTVDM:Fatal Error, Invalid file or missing - %s\n",buffer);
+#endif
+ host_error(EG_SYS_MISSING_FILE, ERR_QUIT, buffer);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hFile);
+ }
+ return (-1);
+ }
+
+
+ pDOSAddr = get_byte_addr(((NTIO_LOAD_SEGMENT<<4)+NTIO_LOAD_OFFSET));
+
+ if (!ReadFile(hFile, pDOSAddr, FileSize, &BytesRead, NULL) ||
+ FileSize != BytesRead)
+ {
+
+#ifndef PROD
+ printf("NTVDM:Fatal Error, Read file error - %s\n",buffer);
+#endif
+ host_error(EG_SYS_MISSING_FILE, ERR_QUIT, buffer);
+ CloseHandle(hFile);
+ return (-1);
+ }
+
+ CloseHandle(hFile);
+
+ // oops ... restore the virtual interrupt state,
+ // which we just overwrote in the file read, and reset.
+ sas_storedw((ULONG)FIXED_NTVDMSTATE_LINEAR, fVirtualInt);
+
+ setCS(NTIO_LOAD_SEGMENT);
+ setIP(NTIO_LOAD_OFFSET); // Start CPU at DosEm initialisation entry point
+
+
+ //
+ // Ensure that WOW VDM runs at NORMAL priorty
+ //
+ if (VDMForWOW) {
+ SetPriorityClass (NtCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ }
+
+ //
+ // Don't allow dos vdm to run at realtime
+ //
+ else if (GetPriorityClass(NtCurrentProcess()) == REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(NtCurrentProcess(), HIGH_PRIORITY_CLASS);
+ }
+
+
+ return 0;
+}
+
+
+/*
+ * AddSystemFiles
+ *
+ * If the system file IBMDOS.SYS|MSDOS.SYS doesn't exist
+ * in the root of c: create zero len MSDOS.SYS
+ *
+ * If the system file IO.SYS does not exist create
+ * a zero len IO.SYS
+ *
+ * This hack is put in especially for the Brief 3.1 install
+ * program which looks for the system files, and if they are
+ * not found screws up the config.sys file.
+ *
+ */
+void AddSystemFiles(void)
+{
+ HANDLE hFile, hFind;
+ WIN32_FIND_DATA wfd;
+ char *pchIOSYS ="C:\\IO.SYS";
+ char *pchMSDOSSYS ="C:\\MSDOS.SYS";
+
+
+ hFind = FindFirstFile(pchMSDOSSYS, &wfd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ hFind = FindFirstFile("C:\\IBMDOS.SYS", &wfd);
+ }
+
+ if (hFind != INVALID_HANDLE_VALUE) {
+ FindClose(hFind);
+ }
+ else {
+ hFile = CreateFile(pchMSDOSSYS,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_READONLY,
+ 0);
+ if (hFile != INVALID_HANDLE_VALUE) { // not much we can do if fails
+ CloseHandle(hFile);
+ }
+
+ }
+
+ hFind = FindFirstFile(pchIOSYS, &wfd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ hFind = FindFirstFile("C:\\IBMBIO.SYS", &wfd);
+ }
+
+ if (hFind != INVALID_HANDLE_VALUE) {
+ FindClose(hFind);
+ }
+ else {
+ hFile = CreateFile(pchIOSYS,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_READONLY,
+ 0);
+ if (hFile != INVALID_HANDLE_VALUE) { // not much we can do if fails
+ CloseHandle(hFile);
+ }
+
+ }
+}
+
+
+#ifdef LIM
+/* parse EMM= line in config.nt to collect EMM parameters. The EMM line has
+ * the following syntax:
+ * EMM=[a=altregs][b=segment][i=segment1-segment2][x=segment1-segment2] [RAM]
+ * where "a=altregs" specifies how many alternative mapping register set
+ * "b=segment" specifies the backfill starting segment address.
+ * "RAM" indicates that the system should only allocate 64KB from
+ * UMB to use as EMM page frame.
+ * "i=segment1 - segment2" specifies a particular range of
+ * address that the system should include as EMM page frame
+ * "x=segment1 - segment2" specifies a particular range of
+ * address that the system should NOT use as page frame.
+ *
+ * input: pointer to LIM_PARAMS
+ * output: LIM_PARAMS is filled with data
+ *
+ */
+
+#define IS_EOL_CHAR(c) (c == '\n' || c == '\r' || c == '\0')
+#define SKIP_WHITE_CHARS(size, ptr) while (size && isspace(*ptr)) \
+ { ptr++; size--; }
+
+#define TOINT(c) ((c >= '0' && c <= '9') ? (c - '0') : \
+ ((c >= 'A' && c <= 'F') ? (c - 'A' + 10) : \
+ ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) : 0) \
+ )\
+ )
+
+boolean init_lim_configuration_data(PLIM_CONFIG_DATA lim_data)
+{
+ char config_sys_pathname[MAX_PATH];
+ HANDLE handle;
+ DWORD file_size, bytes_read, size;
+ char *buffer, *ptr;
+ short lim_size, base_segment, total_altreg_sets;
+ boolean ram_flag_found, reserve_umb_status, parsing_error;
+ sys_addr page_frame;
+ int i;
+
+
+ /* initialize some default values */
+ base_segment = 0x4000;
+ total_altreg_sets = 8;
+ ram_flag_found = FALSE;
+
+ parsing_error = FALSE;
+
+ /* if we can not find config.nt, we can not go on */
+ GetPIFConfigFiles(TRUE, config_sys_pathname);
+ if (*config_sys_pathname == '\0')
+ return FALSE;
+
+ handle = CreateFile(config_sys_pathname,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+ if (handle == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ file_size = GetFileSize(handle, NULL);
+ if (file_size == 0 || file_size == 0xFFFFFFFF) {
+ CloseHandle(handle);
+ return FALSE;
+ }
+ buffer = malloc(file_size);
+ if (buffer == NULL) {
+ CloseHandle(handle);
+ host_error(EG_MALLOC_FAILURE, ERR_QUIT, "");
+ return FALSE;
+ }
+ if (!ReadFile(handle, buffer, file_size, &bytes_read, NULL) ||
+ bytes_read != file_size)
+ {
+ CloseHandle(handle);
+ free(buffer);
+ return FALSE;
+ }
+ CloseHandle(handle);
+
+ ptr = buffer;
+
+ while(file_size) {
+ /* skip leading white characters on each line */
+ SKIP_WHITE_CHARS(file_size, ptr);
+ /* nothing meaningful in the file, break */
+ if (!file_size)
+ break;
+ /* looking for EMM */
+ if (file_size < 3 || toupper(ptr[0]) != 'E' ||
+ toupper(ptr[1]) != 'M' || toupper(ptr[2]) != 'M')
+ {
+ /* we don't want this line, skip it by looking for the first EOL
+ * char in the line
+ */
+ do {
+ file_size--;
+ ptr++;
+ } while(file_size && !IS_EOL_CHAR(*ptr));
+
+ /* either there are nothing left in the file or we have EOL
+ * char(s) in the line, loop through to skip all consecutive
+ * EOL char(s)
+ */
+ while(file_size && IS_EOL_CHAR(*ptr)) {
+ file_size--;
+ ptr++;
+ }
+ }
+ else {
+ /* got "EMM", looking for '=' */
+ file_size -= 3;
+ ptr += 3;
+ SKIP_WHITE_CHARS(file_size, ptr);
+ if (!file_size || *ptr != '=')
+ parsing_error = TRUE;
+ else {
+ file_size--;
+ ptr++;
+ SKIP_WHITE_CHARS(file_size, ptr);
+ /* "EMM=" is a valid EMM command line */
+ }
+ break;
+ }
+ }
+ /* we have three possibilities here:
+ * (1). we found pasring error while we were looking for "EMM="
+ * (2). no "EMM=" line was found
+ * (3). "EMM=" was found and ptr points to the first nonwhite
+ * char after '='.
+ */
+ while (file_size && !parsing_error && !IS_EOL_CHAR(*ptr)) {
+ SKIP_WHITE_CHARS(file_size, ptr);
+ switch (*ptr) {
+ case 'a':
+ case 'A':
+
+ /* no white chars allowed between 'a' and its
+ * parameter
+ */
+ if (!(--file_size) || *(++ptr) != '='){
+ parsing_error = TRUE;
+ break;
+ }
+ file_size--;
+ ptr++;
+ /* about to parsing 'a=' switch, reset the preset value to 0 */
+ total_altreg_sets = 0;
+
+ while(file_size && isdigit(*ptr)) {
+ total_altreg_sets = total_altreg_sets * 10 + (*ptr - '0');
+ file_size--;
+ ptr++;
+ if (total_altreg_sets > 255) {
+ parsing_error = TRUE;
+ break;
+ }
+ }
+ if (!total_altreg_sets || total_altreg_sets > 255)
+ parsing_error = TRUE;
+ break;
+
+ case 'b':
+ case 'B':
+ /* no white chars allowed between 'b' and its
+ * parameter
+ */
+ if (!(--file_size) || *(++ptr) != '='){
+ parsing_error = TRUE;
+ break;
+ }
+ file_size--;
+ ptr++;
+ base_segment = 0;
+ while(file_size && isxdigit(*ptr)) {
+ base_segment = (base_segment << 4) + TOINT(*ptr);
+ file_size--;
+ ptr++;
+ if (base_segment > 0x4000) {
+ parsing_error = TRUE;
+ break;
+ }
+ }
+ /* x01000 <= base_segment <= 0x4000 */
+
+ if (base_segment >= 0x1000 && base_segment <= 0x4000)
+ /* round down the segment to EMM_PAGE_SIZE boundary */
+ base_segment = ((((ULONG)base_segment * 16) / EMM_PAGE_SIZE)
+ * EMM_PAGE_SIZE) / 16;
+ else
+ parsing_error = TRUE;
+ break;
+
+ case 'r':
+ case 'R':
+ if (file_size >= 3 &&
+ (ptr[1] == 'a' || ptr[1] == 'A') &&
+ (ptr[2] == 'm' || ptr[2] == 'M'))
+ {
+ file_size -= 3;
+ ptr += 3;
+ ram_flag_found = TRUE;
+ break;
+ }
+ /* fall through if it is not RAM */
+
+ default:
+ parsing_error = TRUE;
+ break;
+ } /* switch */
+
+ } /* while */
+
+ free(buffer);
+ if (parsing_error) {
+ host_error(EG_BAD_EMM_LINE, ERR_QUIT, "");
+ /* reset parameters because the emm command line is not reliable */
+ base_segment = 0x4000;
+ total_altreg_sets = 8;
+ ram_flag_found = FALSE;
+ }
+
+ /* we got here if (1). no parsing error or (2). user opted to ignore
+ * the parsing error
+ */
+
+ lim_data->total_altreg_sets = total_altreg_sets;
+
+ lim_data->backfill = (640 * 1024) - (base_segment * 16);
+
+ lim_data->base_segment = base_segment;
+ lim_data->use_all_umb = !ram_flag_found;
+
+#ifdef EMM_DEBUG
+ printf("base segment=%x, backfill =%lx; altreg sets=%d\n",
+ base_segment, lim_data->backfill, total_altreg_sets);
+#endif
+
+ return TRUE;
+}
+
+unsigned short get_lim_page_frames(USHORT * page_table,
+ PLIM_CONFIG_DATA lim_data
+ )
+{
+
+ USHORT total_phys_pages, base_segment, i;
+ BOOL reserve_umb_status;
+ ULONG page_frame, size;
+
+ /* we search for the primary EMM page frame first from 0xE0000.
+ * if we can not find it there, then look for anywhere in UMB area.
+ * if the primary EMM page frame is found, and RAM is not specified,
+ * collect every possible page frame in the UMB.
+ * if RAM has been specified, only allocate the primary page frame.
+ */
+ total_phys_pages = 0;
+ base_segment = lim_data->base_segment;
+ reserve_umb_status = FALSE;
+
+ /* specificaly ask for 0xE0000 */
+ page_frame = 0xE0000;
+ /* primary page frames are always EMM_PAGE_SIZE * 4 */
+ size = EMM_PAGE_SIZE * 4;
+ reserve_umb_status = ReserveUMB(UMB_OWNER_EMM, (PVOID *)&page_frame, &size);
+ /* if failed to find the primary page frame at 0xE0000, search for anywhere
+ * available in the UMB area
+ */
+ if (!reserve_umb_status) {
+ page_frame = 0;
+ size = 0x10000;
+ reserve_umb_status = ReserveUMB(UMB_OWNER_EMM, (PVOID *)&page_frame, &size);
+ }
+ if (!reserve_umb_status) {
+#ifdef EMM_DEBUG
+ printf("couldn't find primary page frame\n");
+#endif
+ return FALSE;
+ }
+ page_table[0] = (short)(page_frame / 16);
+ page_table[1] = (short)((page_frame + 1 * EMM_PAGE_SIZE) / 16);
+ page_table[2] = (short)((page_frame + 2 * EMM_PAGE_SIZE) / 16);
+ page_table[3] = (short)((page_frame + 3 * EMM_PAGE_SIZE) / 16);
+
+
+ total_phys_pages = 4;
+
+ /* now add back fill page frames */
+ for (i = lim_data->backfill / EMM_PAGE_SIZE; i != 0 ; i--) {
+ page_table[total_phys_pages++] = base_segment;
+ base_segment += EMM_PAGE_SIZE / 16;
+ }
+
+ /* RAM is not specified in the command line, grab every possible
+ * page frame from UMB
+ */
+ if (lim_data->use_all_umb) {
+ while (TRUE) {
+ page_frame = 0;
+ size = EMM_PAGE_SIZE;
+ if (ReserveUMB(UMB_OWNER_EMM, (PVOID *)&page_frame, &size))
+ page_table[total_phys_pages++] = (short)(page_frame / 16);
+ else
+ break;
+ }
+ }
+
+#ifdef EMM_DEBUG
+ printf("page frames:\n");
+ for (i = 0; i < total_phys_pages; i++)
+ printf("page number %d, segment %x\n",i, page_table[i]);
+#endif
+ return total_phys_pages;
+}
+#endif /* LIM */
+
+
+VOID SetupInstallableVDD (VOID)
+{
+HANDLE hVDD;
+HKEY VDDKey;
+CHAR szClass [MAX_CLASS_LEN];
+DWORD dwClassLen = MAX_CLASS_LEN;
+DWORD nKeys,cbMaxKey,cbMaxClass,nValues=0,cbMaxValueName,cbMaxValueData,dwSec;
+DWORD dwType;
+PCHAR pszName,pszValue;
+FILETIME ft;
+PCHAR pKeyName = "SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers";
+
+ if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE,
+ pKeyName,
+ 0,
+ KEY_QUERY_VALUE,
+ &VDDKey
+ ) != ERROR_SUCCESS){
+ RcErrorDialogBox(ED_REGVDD, pKeyName, NULL);
+ return;
+ }
+
+ pszName = "VDD";
+
+ // get size of VDD value
+ if (RegQueryInfoKey (VDDKey,
+ (LPTSTR)szClass,
+ &dwClassLen,
+ NULL,
+ &nKeys,
+ &cbMaxKey,
+ &cbMaxClass,
+ &nValues,
+ &cbMaxValueName,
+ &cbMaxValueData,
+ &dwSec,
+ &ft
+ ) != ERROR_SUCCESS) {
+ RcErrorDialogBox(ED_REGVDD, pKeyName, pszName);
+ RegCloseKey (VDDKey);
+ return;
+ }
+
+
+ // alloc temp memory for the VDD value (multi-string)
+ if ((pszValue = (PCHAR) malloc (cbMaxValueData)) == NULL) {
+ RcErrorDialogBox(ED_MEMORYVDD, pKeyName, pszName);
+ RegCloseKey (VDDKey);
+ return;
+ }
+
+
+ // finally get the VDD value (multi-string)
+ if (RegQueryValueEx (VDDKey,
+ (LPTSTR)pszName,
+ NULL,
+ &dwType,
+ (LPBYTE)pszValue,
+ &cbMaxValueData
+ ) != ERROR_SUCCESS || dwType != REG_MULTI_SZ) {
+ RcErrorDialogBox(ED_REGVDD, pKeyName, pszName);
+ RegCloseKey (VDDKey);
+ free (pszValue);
+ return;
+ }
+
+ pszName = pszValue;
+
+ while (*pszValue) {
+ if ((hVDD = SafeLoadLibrary(pszValue)) == NULL){
+ RcErrorDialogBox(ED_LOADVDD, pszValue, NULL);
+ }
+ pszValue =(PCHAR)strchr (pszValue,'\0') + 1;
+ }
+
+ RegCloseKey (VDDKey);
+ free (pszName);
+ return;
+}
+
+/*** VDDInstallMemoryHook - This service is provided for VDDs to hook the
+ * Memory Mapped IO addresses they are resposible
+ * for.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ * addr : Starting linear address
+ * count : Number of bytes
+ * MemoryHandler : VDD handler for the memory addresses
+ *
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * NOTES
+ * 1. The first one to hook an address will get the control. There
+ * is no concept of chaining the hooks. VDD should grab the
+ * memory range in its initialization routine. After all
+ * the VDDs are loaded, EMM will eat up all the remaining
+ * memory ranges for UMB support.
+ *
+ * 2. Memory handler will be called with the address on which the
+ * page fault occured. It wont say whether it was a read operation
+ * or write operation or what were the operand value. If a VDD
+ * is interested in such information it has to get the CS:IP and
+ * decode the instruction.
+ *
+ * 3. On returning from the hook handler it will be assumed that
+ * the page fault was handled and the return will go back to the
+ * VDM.
+ *
+ * 4. Installing a hook on a memory range will result in the
+ * consumption of memory based upon page boundaries. The Starting
+ * address is rounded down, and the count is rounded up to the
+ * next page boundary. The VDD's memory hook handler will be
+ * invoked for all addreses within the page(s) hooked. The page(s)
+ * will be set aside as mapped reserved sections, and will no
+ * longer be available for use by NTVDM or other VDDs. The VDD is
+ * permitted to manipulate the memory (commit, free, etc) as needed.
+ *
+ * 5. After calling the MemoryHandler, NTVDM will return to the
+ * faulting cs:ip in the 16bit app. If the VDD does'nt want
+ * that to happen it should adjust cs:ip appropriatly by using
+ * setCS and setIP.
+ */
+
+BOOL VDDInstallMemoryHook (
+ HANDLE hVDD,
+ PVOID pStart,
+ DWORD count,
+ PVDD_MEMORY_HANDLER MemoryHandler
+ )
+{
+PMEM_HOOK_DATA pmh = MemHookHead,pmhNew,pmhLast=NULL;
+
+ DWORD dwStart;
+
+
+ if (count == 0 || pStart == (PVOID)NULL || count > 0x20000) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ // round addr down to next page boundary
+ // round count up to next page boundary
+ dwStart = (DWORD)pStart & ~(HOST_PAGE_SIZE-1);
+ count += (DWORD)pStart - dwStart;
+ count = (count + HOST_PAGE_SIZE - 1) & ~(HOST_PAGE_SIZE-1);
+
+ if (dwStart < 0xC0000) {
+ SetLastError (ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+
+ while (pmh) {
+ // the requested block can never be overlapped with any other
+ // existing blocks
+ if(dwStart >= pmh->StartAddr + pmh->Count ||
+ dwStart + count <= pmh->StartAddr){
+ pmhLast = pmh;
+ pmh = pmh->next;
+ continue;
+ }
+
+ // failure case
+ SetLastError (ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+ if ((pmhNew = (PMEM_HOOK_DATA) malloc (sizeof(MEM_HOOK_DATA))) == NULL) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+ // the request block is not overlapped with existing blocks,
+ // request the UMB managing function to allocate the block
+ if (!ReserveUMB(UMB_OWNER_VDD, (PVOID *)&dwStart, &count)) {
+ free(pmhNew);
+ SetLastError(ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+ // now set up the new node to get to know it
+ pmhNew->Count = count;
+ pmhNew->StartAddr = dwStart;
+ pmhNew->hvdd = hVDD;
+ pmhNew->MemHandler = MemoryHandler;
+ pmhNew->next = NULL;
+
+ // Check if the record is to be added in the begining
+ if (MemHookHead == NULL || pmhLast == NULL) {
+ MemHookHead = pmhNew;
+ return TRUE;
+ }
+
+ pmhLast->next = pmhNew;
+ return TRUE;
+}
+
+/*** VDDDeInstallMemoryHook - This service is provided for VDDs to unhook the
+ * Memory Mapped IO addresses.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ * addr : Starting linear address
+ * count : Number of addresses
+ *
+ * OUTPUT
+ * None
+ *
+ * NOTES
+ * 1. On Deinstalling a hook, the memory range becomes invalid.
+ * VDM's access of this memory range will cause a page fault.
+ *
+ */
+
+BOOL VDDDeInstallMemoryHook (
+ HANDLE hVDD,
+ PVOID pStart,
+ DWORD count
+ )
+{
+PMEM_HOOK_DATA pmh = MemHookHead,pmhLast=NULL;
+
+ DWORD dwStart;
+
+ if (count == 0 || pStart == (PVOID)NULL || count > 0x20000) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ // round addr down to next page boundary
+ // round count up to next page boundary
+ dwStart = (DWORD)pStart & ~(HOST_PAGE_SIZE-1);
+ count += (DWORD)pStart - dwStart;
+ count = (count + HOST_PAGE_SIZE - 1) & ~(HOST_PAGE_SIZE-1);
+ while (pmh) {
+ if (pmh->hvdd == hVDD &&
+ pmh->StartAddr == dwStart &&
+ pmh->Count == count ) {
+ if (pmhLast)
+ pmhLast->next = pmh->next;
+ else
+ MemHookHead = pmh->next;
+
+ // free the UMB for other purpose.
+ // Note that VDDs may have committed memory for their memory
+ // hook and forgot to decommit the memory before calling
+ // this function. If that is the case, the ReleaseUMB will take
+ // care of this. It is because we want to maintain a single
+ // version of VDD support routines while move platform depedend
+ // routines into the other module.
+ if (ReleaseUMB(UMB_OWNER_VDD,(PVOID)dwStart, count)) {
+ // free the node.
+ free(pmh);
+ return TRUE;
+ }
+ else {
+ printf("Failed to release VDD memory\n");
+ }
+ }
+ pmhLast = pmh;
+ pmh = pmh->next;
+ }
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+
+
+BOOL
+VDDAllocMem(
+HANDLE hVDD,
+PVOID pStart,
+DWORD count
+)
+{
+ PMEM_HOOK_DATA pmh = MemHookHead;
+ DWORD dwStart;
+
+ if (count == 0 || pStart == (PVOID)NULL || count > 0x20000) {
+ SetLastError(ERROR_INVALID_ADDRESS);
+ return FALSE;
+ }
+ // round addr down to next page boundary
+ // round count up to next page boundary
+ dwStart = (DWORD)pStart & ~(HOST_PAGE_SIZE-1);
+ count += (DWORD)pStart - dwStart;
+ count = (count + HOST_PAGE_SIZE - 1) & ~(HOST_PAGE_SIZE-1);
+
+ while(pmh) {
+ if (pmh->hvdd == hVDD &&
+ pmh->StartAddr <= dwStart &&
+ pmh->StartAddr + pmh->Count >= dwStart + count)
+ return(VDDCommitUMB((PVOID)dwStart, count));
+ pmh = pmh->next;
+ }
+ SetLastError(ERROR_INVALID_ADDRESS);
+ return FALSE;
+}
+
+
+BOOL
+VDDFreeMem(
+HANDLE hVDD,
+PVOID pStart,
+DWORD count
+)
+{
+ PMEM_HOOK_DATA pmh = MemHookHead;
+ DWORD dwStart;
+
+
+ if (count == 0 || pStart == (PVOID)NULL || count > 0x20000) {
+ SetLastError(ERROR_INVALID_ADDRESS);
+ return FALSE;
+ }
+ // round addr down to next page boundary
+ // round count up to next page boundary
+ dwStart = (DWORD)pStart & ~(HOST_PAGE_SIZE-1);
+ count += (DWORD)pStart - dwStart;
+ count = (count + HOST_PAGE_SIZE - 1) & ~(HOST_PAGE_SIZE-1);
+
+ while(pmh) {
+ if (pmh->hvdd == hVDD &&
+ pmh->StartAddr <= dwStart &&
+ pmh->StartAddr + pmh->Count >= dwStart + count)
+ return(VDDDeCommitUMB((PVOID)dwStart, count));
+ pmh = pmh->next;
+ }
+ SetLastError(ERROR_INVALID_ADDRESS);
+ return FALSE;
+}
+
+
+ // Will publish the following two functions someday.
+ // Please change ntvdm.def, nt_vdd.h and nt_umb.c
+ // if you remove the #if 0
+BOOL
+VDDIncludeMem(
+HANDLE hVDD,
+PVOID pStart,
+DWORD count
+)
+{
+ DWORD dwStart;
+
+ if (count == 0 || pStart == NULL){
+ SetLastError(ERROR_INVALID_ADDRESS);
+ return FALSE;
+ }
+ // round addr down to next page boundary
+ // round count up to next page boundary
+ dwStart = (DWORD)pStart & ~(HOST_PAGE_SIZE-1);
+ count += (DWORD)pStart - dwStart;
+ count = (count + HOST_PAGE_SIZE - 1) & ~(HOST_PAGE_SIZE-1);
+ return(ReserveUMB(UMB_OWNER_NONE, (PVOID *) &dwStart, &count));
+}
+
+BOOL
+VDDExcludeMem(
+HANDLE hVDD,
+PVOID pStart,
+DWORD count
+)
+{
+
+ DWORD dwStart;
+
+ if (count == 0 || pStart == NULL) {
+ SetLastError(ERROR_INVALID_ADDRESS);
+ return FALSE;
+ }
+ // round addr down to next page boundary
+ // round count up to next page boundary
+ dwStart = (DWORD)pStart & ~(HOST_PAGE_SIZE-1);
+ count += (DWORD)pStart - dwStart;
+ count = (count + HOST_PAGE_SIZE - 1) & ~(HOST_PAGE_SIZE-1);
+ return(ReserveUMB(UMB_OWNER_ROM, (PVOID *) &dwStart, &count));
+}
+
+
+
+VOID
+VDDTerminateVDM()
+{
+ TerminateVDM();
+}
+
+VOID DispatchPageFault (
+ ULONG FaultAddr,
+ ULONG RWMode
+ )
+{
+PMEM_HOOK_DATA pmh = MemHookHead;
+
+ // dispatch intel linear address always
+ FaultAddr -= (ULONG)Sim32GetVDMPointer(0, 0, FALSE);
+ // Find the VDD and its handler which is to be called for this fault
+ while (pmh) {
+ if (pmh->StartAddr <= FaultAddr &&
+ FaultAddr <= (pmh->StartAddr + pmh->Count)) {
+
+ // Call the VDD's memory hook handler
+ (*pmh->MemHandler) ((PVOID)FaultAddr, RWMode);
+ return;
+ }
+ else {
+ pmh = pmh->next;
+ continue;
+ }
+ }
+
+ // A page fault occured on an address for which we could'nt find a
+ // VDD. Raise the exception.
+ RaiseException ((DWORD)STATUS_ACCESS_VIOLATION,
+ EXCEPTION_NONCONTINUABLE,
+ 0,
+ NULL);
+
+}
+
+
+/**
+ *
+ * Input - TRUE means redirection is effective
+ * FALSE means no redirection
+ *
+ * This routine will get called after every GetNextVDMCommand i.e.
+ * on every DOS app that a user runs from the prompt. I think
+ * you can safely ignore this callout for WOW.
+ *
+ **/
+void nt_std_handle_notification (BOOL fIsRedirection)
+{
+ /*
+ ** Set global so we know when redirection is active.
+ */
+
+ stdoutRedirected = fIsRedirection;
+
+#ifdef X86GFX
+
+ if( !fIsRedirection && sc.ScreenState==FULLSCREEN )
+ {
+ half_word mode = 3,
+ lines = 0;
+
+ //
+ // WORD 6 and other apps cause this code path to be followed
+ // on application startup. now if line==0, SelectMouseBuffer
+ // causes a 640 x 200 buffer to be selected. This is not
+ // correct if the app is in a 43 or 50 text line mode.
+ // Therefore, since the BIOS data area location 40:84 holds
+ // the number of rows - 1 at this point (if the app uses int 10h
+ // function 11 to change mode) then pick up the correct value
+ // from here. Andy!
+
+ if(sc.ModeType == TEXT)
+ {
+ sas_load(0x484,&lines);
+
+ //
+ // The value is pulled from the BIOS data area.
+ // This is one less than the number of rows. So
+ // increment to give SelectMouseBuffer what it
+ // expects. Let this function do the necessary
+ // handling of non 25, 43 and 50 values.
+ //
+
+ lines++;
+ }
+
+ SelectMouseBuffer(mode, lines);
+ }
+#endif //X86GFX
+}
+
+/*** VDDInstallUserHook
+ *
+ * This service is provided for VDDs to hook callback events.
+ * These callback events include, PDB (DOS Process) creation, PDB
+ * termination, VDM block and VDM resume. Whenever DOS creates (
+ * for example int21/exec) or terminates (for example int21/exit)
+ * a 16bit process VDD could get a notification for that. A VDM in
+ * which a DOS app runs, is attached to the console window in which
+ * the DOS app is running. VDM gets created when first DOS binary
+ * runs in that console. When that DOS binary terminates, VDM stays
+ * with the console window and waits for the next DOS binary to be
+ * launched. When VDM is waiting for this next DOS binary all its
+ * components including VDDs should block. For this purpose, VDDs
+ * could hook VDM Block and Resume events. On Block event VDDs
+ * should block all their worker threads and cleanup any other
+ * operation they might have started. On resume they can restart
+ * worker threads.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ * Ucr_handler: handle on creating function (OPTIONAL)
+ * Entry - 16bit DOS PDB
+ * EXIT - None
+ * Uterm_handler: handle on terminating function (OPTIONAL)
+ * Entry - 16bit DOS PDB
+ * EXIT - None
+ * Ublock_handler: handle on block (of ntvdm) function (OPTIONAL)
+ * Entry - None
+ * EXIT - None
+ * Uresume_handler: handle on resume (of ntvdm) function (OPTIONAL)
+ * Entry - None
+ * EXIT - None
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * NOTES:
+ * If hvdd in not valid it will return ERROR_INVALID_PARAMETER.
+ * VDD can provide whatever event hook they may choose. Not providing
+ * any handler has no effect. There are lots of requests in DOS world
+ * for which there is no explicit Close operation. For instance
+ * printing via int17h. A VDD supporting printing will never be able to
+ * detect when to flush the int17 characters, if its spolling them.
+ * But with the help of PDB create/terminate the VDD can achieve it.
+ */
+
+BOOL VDDInstallUserHook (
+ HANDLE hVDD,
+ PFNVDD_UCREATE Ucr_Handler,
+ PFNVDD_UTERMINATE Uterm_Handler,
+ PFNVDD_UBLOCK Ublock_handler,
+ PFNVDD_URESUME Uresume_handler
+)
+{
+ PVDD_USER_HANDLERS puh = UserHookHead;
+ PVDD_USER_HANDLERS puhNew;
+
+
+ if (!hVDD) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if ((puhNew = (PVDD_USER_HANDLERS) malloc (sizeof(VDD_USER_HANDLERS))) == NULL) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ // now set up the new node to get to know it
+ puhNew->hvdd = hVDD;
+ puhNew->ucr_handler = Ucr_Handler;
+ puhNew->uterm_handler = Uterm_Handler;
+ puhNew->ublock_handler = Ublock_handler;
+ puhNew->uresume_handler = Uresume_handler;
+
+ // Check if the record is to be added in the begining
+ if (UserHookHead == NULL) {
+ puhNew->next = NULL;
+ UserHookHead = puhNew;
+ return TRUE;
+ }
+
+ puhNew->next = UserHookHead;
+ UserHookHead = puhNew;
+ return TRUE;
+}
+
+/*** VDDDeInstallUserHook
+ *
+ * This service is provided for VDDs to unhook callback events.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * NOTES
+ * This service will deinstall all the events hooked earlier
+ * using VDDInstallUserHook.
+ */
+
+BOOL VDDDeInstallUserHook (
+ HANDLE hVDD)
+{
+
+ PVDD_USER_HANDLERS puh = UserHookHead;
+ PVDD_USER_HANDLERS puhLast = NULL;
+
+
+ if (!hVDD) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ while (puh) {
+ if (puh->hvdd == hVDD) {
+
+ if (puhLast)
+ puhLast->next = puh->next;
+ else
+ UserHookHead = puh->next;
+
+ free(puh);
+ return TRUE;
+ }
+ puhLast = puh;
+ puh = puh->next;
+ }
+
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+/*** VDDTerminateUserHook - This service is provided for VDDs to hook
+ * for callback services
+ *
+ * INPUT:
+ * USHORT DosPDB
+ *
+ * OUTPUT
+ * None
+ *
+ */
+
+VOID VDDTerminateUserHook(USHORT DosPDB)
+{
+
+ PVDD_USER_HANDLERS puh = UserHookHead;
+
+ while(puh) {
+ if(puh->uterm_handler)
+ (puh->uterm_handler)(DosPDB);
+ puh = puh->next;
+ }
+ return;
+}
+
+/*** VDDCreateUserHook - This service is provided for VDDs to hook
+ * for callback services
+ *
+ * INPUT:
+ * USHORT DosPDB
+ *
+ * OUTPUT
+ * None
+ *
+ */
+
+VOID VDDCreateUserHook(USHORT DosPDB)
+{
+
+ PVDD_USER_HANDLERS puh = UserHookHead;
+
+ while(puh) {
+ if(puh->ucr_handler)
+ (puh->ucr_handler)(DosPDB);
+ puh = puh->next;
+ }
+ return;
+}
+
+/*** VDDBlockUserHook - This service is provided for VDDs to hook
+ * for callback services
+ *
+ * INPUT:
+ * None
+ *
+ * OUTPUT
+ * None
+ *
+ */
+
+VOID VDDBlockUserHook(VOID)
+{
+
+ PVDD_USER_HANDLERS puh = UserHookHead;
+
+ while(puh) {
+ if(puh->ublock_handler)
+ (puh->ublock_handler)();
+ puh = puh->next;
+ }
+ return;
+}
+
+/*** VDDResumeUserHook - This service is provided for VDDs to hook
+ * for callback services
+ *
+ * INPUT:
+ * None
+ *
+ * OUTPUT
+ * None
+ *
+ */
+
+VOID VDDResumeUserHook(VOID)
+{
+
+ PVDD_USER_HANDLERS puh = UserHookHead;
+
+ while(puh) {
+ if(puh->uresume_handler)
+ (puh->uresume_handler)();
+ puh = puh->next;
+ }
+ return;
+}
+
+/*** VDDSimulate16
+ *
+ * This service causes the simulation of intel instructions to start.
+ *
+ * INPUT
+ * None
+ *
+ * OUTPUT
+ * None
+ *
+ * NOTES
+ * This service is similar to VDDSimulateInterrupt except that
+ * it does'nt require a hardware interrupt to be supported by the
+ * 16bit stub device driver. This service allows VDD to execute
+ * a routine in its 16bit driver and come back when its done, kind
+ * of a far call. Before calling VDDSimulate16, VDD should preserve
+ * all the 16bit registers which its routine might destroy. Minimally
+ * it should at least preserve cs and ip. Then it should set the
+ * cs and ip for the 16bit routine. VDD could also use registers
+ * like ax,bx.. to pass parametrs to its 16bit routines. At the
+ * end of the 16bit routine VDDUnSimulate16 macro should be used
+ * which will send the control back to the VDD just after the
+ * call VDDSimulate16. Note very carefully that this simulation
+ * to 16bit is synchronous, i.e. VDD gets blocked in VDDSimulate16
+ * and only comes back when stub-driver does a VDDUnSimulate16.
+ * Here is an example:
+ *
+ * vdd:
+ * SaveCS = getCS();
+ * SaveIP = getIP();
+ * SaveAX = getAX();
+ * setCS (16BitRoutineCS);
+ * setIP (16BitRoutineIP);
+ * setAX (DO_X_OPERATION);
+ * VDDSimulate16 ();
+ * setCS (SavwCS);
+ * setIP (SaveIP);
+ * setAX (SaveAX);
+ * ..
+ * ..
+ *
+ * Stub Driver: (Initialization part)
+ *
+ * RegisterModule ; Loads VDD
+ * push cs
+ * pop ax
+ * mov bx, offset Simulate16
+ * DispatchCall ; Passes the address of worker
+ * ; routine to VDD in ax:bx.
+ *
+ * Stub Driver (Run Time)
+ *
+ * Simulate16:
+ * ..
+ * .. ; do the operation index passed in ax
+ *
+ * VDDUnSimulate16
+ *
+ */
+
+VOID VDDSimulate16(VOID)
+{
+ cpu_simulate();
+}
+
+VOID HostTerminatePDB(USHORT PDB)
+{
+ FloppyTerminatePDB(PDB);
+ FdiskTerminatePDB(PDB);
+
+}