/********************************************************/ /* * nt_vdd.c - NT support for VDD DLLs * * Ade Brownlow * * 19/11/91 * */ #include "windows.h" #include "insignia.h" #include "host_def.h" #include #include "xt.h" #include CpuH #include "sas.h" #include "error.h" #include "config.h" #include "ios.h" #include "dma.h" #include "nt_vdd.h" #include "nt_vddp.h" #ifdef ANSI /* MS bop grabbing stuff */ GLOBAL half_word get_MS_bop_index (void *); GLOBAL void free_MS_bop_index (half_word); GLOBAL void ms_bop (void); LOCAL void ms_not_a_bop (void); /* IO slot grabbers */ GLOBAL half_word io_get_spare_slot (void); GLOBAL void io_release_spare_slot (half_word); #else /* MS bop grabbing stuff */ GLOBAL half_word get_MS_bop_index (); GLOBAL void free_MS_bop_index (); GLOBAL void ms_bop (); LOCAL void ms_not_a_bop (); /* IO slot grabbers */ GLOBAL half_word io_get_spare_slot (); GLOBAL void io_release_spare_slot (); #endif /*::::::::::::::::::::::::::::::::::::::::::::::::::::: Local data structures */ #define MAX_SLOTS (10) LOCAL void (*MS_bop_tab[MAX_SLOTS])(); /* MS bop table */ /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ /* Microsoft BOP vectoring code references MS_bop_tab above and calls function * as directed by AH */ /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ GLOBAL void ms_bop () /* called from MS_bop_5 ie bop 0x55 */ { half_word ah = getAH(); /* get the value in AH */ /*........................................Valid then call the MS function */ if(ah >= MAX_SLOTS || MS_bop_tab[ah] == NULL) ms_not_a_bop(); else (*MS_bop_tab[ah])(); } /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ /*::::: Dummy for unset AH values - stops us zipping into hyperspace :::::::::*/ /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ LOCAL void ms_not_a_bop() { #ifndef PROD printf ("AH=%x, This is not a valid value for an MS BOP\n", getAH()); illegal_bop(); #ifdef YODA force_yoda (); #endif #endif } /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ /*::: Give an index to our table which can be used for the passed function :::*/ /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ GLOBAL half_word get_ms_bop_index (void (*func)()) { register half_word index; for(index = 0; index < MAX_SLOTS; index++) { if(MS_bop_tab[index] == NULL) { MS_bop_tab[index] = func; break; } } return (index == MAX_SLOTS ? (half_word) 0xff : index); } /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ /*:::::::::::::::::::::::: free the bop index passed :::::::::::::::::::::::::*/ /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/ GLOBAL void free_MS_bop_index IFN1(half_word, index) { MS_bop_tab[index] = NULL; } /* * ========================================================================== * Imports * ========================================================================== */ IMPORT VOID host_ica_lock(), host_ica_unlock(); /********************************************************/ /* IO stuff */ // VddAdapter Table (Adapter X hVdd table) // there is only one adapter per VDD HANDLE VddAdapter[NUMBER_SPARE_ADAPTERS]; #define MAX_IRQ_LINE 15 // Bugbug need to initialize this cleanly HANDLE IrqLines[MAX_IRQ_LINE+1] = {(HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)1, (HANDLE)0, (HANDLE)0, (HANDLE)1, (HANDLE)1, (HANDLE)0}; /* GetVddAdapter * * Retrieves the current adapter number for the Vdd * If none is assigned, then one is assigned * * entry: HANDLE hVdd - handle forthe vdd * exit : WORD wAdaptor - Assigned Adaptor Num * (Zero for failure) * WinLastError Codes: * * ERROR_ALREADY_EXISTS - Adaptor already exists for the Vdd * ERROR_OUTOFMEMORY - No adaptor slots available * */ WORD GetVddAdapter(HANDLE hVdd) { WORD w; // // search VddAdapter table to see if adapter already assigned // for (w = 0; w < NUMBER_SPARE_ADAPTERS; w++) { if (VddAdapter[w] == hVdd) { SetLastError(ERROR_ALREADY_EXISTS); return 0; } } // // assume not assigned, so look for first available slot // for (w = 0; w < NUMBER_SPARE_ADAPTERS; w++) { if (VddAdapter[w] == 0) { VddAdapter[w] = hVdd; return (w + SPARE_ADAPTER1); } } // none found return error SetLastError(ERROR_OUTOFMEMORY); return 0; } /* FreeVddAdapter * * Frees the current adaptor for the specified VDD * * entry: HANDLE hVdd * exit: WORD AdaptorNumber that was freed, * Zero for not found * */ WORD FreeVddAdapter(HANDLE hVdd) { WORD w; // // search VddAdapter table by hVdd for adaptor // and mark it as available // w = NUMBER_SPARE_ADAPTERS; while (w--) { if (VddAdapter[w] == hVdd) { VddAdapter[w] = 0; return w; } } return 0; } #ifdef MONITOR extern BOOLEAN MonitorVddConnectPrinter(WORD Adapter, HANDLE hVdd, BOOLEAN Connect); #endif /* MONITOR */ /*** VDDInstallIOHook - This service is provided for VDDs to hook the * IO ports they are responsible for. * * INPUT: * hVDD ; VDD Handle * cPortRange; Number of VDD_IO_PORTRANGE structures * pPortRange; Pointer to array of VDD_IO_PORTRANGE * IOhandler : VDD handler for the ports. * * OUTPUT * SUCCESS : Returns TRUE * FAILURE : Returns FALSE * GetLastError has the extended error information. * * NOTES: * 1. The first one to hook a port will get control. Subsequent * requests will be failed. There is no concept of chaining * the hooks. * * 2. IOHandler must atleast provide a byte read and a byte write * handler. Others can be NULL. * * 3. If word or string handlers are not provided, their effect * will be emulated using byte handlers. * * 4. VDDs should not hook DMA ports. NTVDM manages it for all * the clients and services are provided to perform DMA * operations and to access and modify DMA data. * * 5. VDDs should not hook video ports as well. Such a hooking * will succeed but there is no gurantee that the IO handler will * get called. * * 6. Each Vdd is allowed to install only one set of IO hooks * at a time. * * 7. Extended Error codes: * * ERROR_ACCESS_DENIED - One of the requested ports is already hooked * ERROR_ALREADY_EXISTS - Vdd already has active IO port handlers * ERROR_OUTOFMEMORY - Insufficient resources for additional VDD * Port handler set. * ERROR_INVALID_ADDRESS - One of the IO port handlers has an invalid * address. */ BOOL VDDInstallIOHook ( HANDLE hVdd, WORD cPortRange, PVDD_IO_PORTRANGE pPortRange, PVDD_IO_HANDLERS pIOFn) { WORD w, i; WORD wAdapter; PVDD_IO_PORTRANGE pPRange; #ifdef MONITOR WORD lptAdapter = 0; #endif // check parameters // the inb and outb handlers must be valid // the rest must be either NULL or valid // if (IsBadCodePtr((FARPROC)pIOFn->inb_handler) || IsBadCodePtr((FARPROC)pIOFn->outb_handler)) { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } if ((pIOFn->inw_handler && IsBadCodePtr((FARPROC)pIOFn->inw_handler)) || (pIOFn->insb_handler && IsBadCodePtr((FARPROC)pIOFn->insb_handler)) || (pIOFn->insw_handler && IsBadCodePtr((FARPROC)pIOFn->insw_handler)) || (pIOFn->outw_handler && IsBadCodePtr((FARPROC)pIOFn->outw_handler)) || (pIOFn->outsb_handler && IsBadCodePtr((FARPROC)pIOFn->outsb_handler))|| (pIOFn->outsw_handler && IsBadCodePtr((FARPROC)pIOFn->outsw_handler)) ) { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } // Get an adapter wAdapter = GetVddAdapter(hVdd); if (!wAdapter) { return FALSE; } // register io handlers for this adapter io_define_in_routines((half_word)wAdapter, pIOFn->inb_handler, pIOFn->inw_handler, pIOFn->insb_handler, pIOFn->insw_handler); io_define_out_routines((half_word)wAdapter, pIOFn->outb_handler, pIOFn->outw_handler, pIOFn->outsb_handler, pIOFn->outsw_handler); // register ports for this adapter\vdd i = cPortRange; pPRange = pPortRange; while (i) { for (w = pPRange->First; w <= pPRange->Last; w++) { #ifdef MONITOR // watch out for lpt ports // note that the vdd must hook every port assoicated with // the lpt. Just imanging that the vdd traps the control // port while leaves the rest for softpc-- we are going // to screw up badly and so does the vdd. // QUESTION: How can we enforece this???? if (w >= LPT1_PORT_START && w < LPT1_PORT_END) lptAdapter |= 1; else if (w >= LPT2_PORT_START && w < LPT2_PORT_END) lptAdapter |= 2; else if (w >= LPT3_PORT_START && w < LPT3_PORT_END) lptAdapter |= 4; #endif if (!io_connect_port(w, (half_word)wAdapter, IO_READ_WRITE)) { // if one of the port connects failed // undo the connects that succeeded and ret error i = w; while (pPortRange < pPRange) { for (w = pPortRange->First; w <= pPortRange->Last; w++) { io_disconnect_port(w, (half_word)wAdapter); } pPortRange++; } for (w = pPortRange->First; w < i; w++) { io_disconnect_port(w, (half_word)wAdapter); } FreeVddAdapter(hVdd); SetLastError(ERROR_ACCESS_DENIED); return FALSE; } } pPRange++; i--; } #ifdef MONITOR // i/o ports are hooked successfully, stop printer status port // kernel emulation if the they are in the hooked range if (lptAdapter & 1) MonitorVddConnectPrinter(0, hVdd, TRUE); if (lptAdapter & 2) MonitorVddConnectPrinter(1, hVdd, TRUE); if (lptAdapter & 4) MonitorVddConnectPrinter(2, hVdd, TRUE); #endif /* MONITOR */ return TRUE; } /*** VDDDeInstallIOHook - This service is provided for VDDs to unhook the * IO ports they have hooked. * * INPUT: * hVDD : VDD Handle * * OUTPUT * None * * NOTES * * 1. On Deinstalling a hook, the defult hook is placed back on * those ports. Default hook returns 0xff on reading * and ignores the write operations. * */ VOID VDDDeInstallIOHook ( HANDLE hVdd, WORD cPortRange, PVDD_IO_PORTRANGE pPortRange) { WORD w; WORD wAdapter; #ifdef MONITOR WORD lptAdapter = 0; #endif wAdapter = FreeVddAdapter(hVdd); if (!wAdapter) { return; } // deregister ports for this adapter\vdd while (cPortRange--) { for (w = pPortRange->First; w <= pPortRange->Last; w++) { #ifdef MONITOR // watch out for lpt status ports // note that the vdd must unhook every port assoicated with // the lpt. Just imanging that the vdd traps the control // port while leaves the rest for softpc-- we are going // to screw up badly and so does the vdd. // QUESTION: How can we enforece this???? if (w >= LPT1_PORT_START && w < LPT1_PORT_END) lptAdapter |= 1; else if (w >= LPT2_PORT_START && w < LPT2_PORT_END) lptAdapter |= 2; else if (w >= LPT3_PORT_START && w < LPT3_PORT_END) lptAdapter |= 4; #endif io_disconnect_port(w, (half_word)wAdapter); } pPortRange++; } #ifdef MONITOR // i/o ports are Unhooked successfully, resume printer status port // kernel emulation if the they are in the hooked range if (lptAdapter & 1) MonitorVddConnectPrinter(0, hVdd, FALSE); if (lptAdapter & 2) MonitorVddConnectPrinter(1, hVdd, FALSE); if (lptAdapter & 4) MonitorVddConnectPrinter(2, hVdd, FALSE); #endif /* MONITOR */ } /*** VDDReserveIrqLine - This service resolves contention between VDDs * over Irq lines. * * Parameters: * hVDD : VDD Handle * IrqLine ; the specific IrqLine number to reserve, or -1 to search for * a free line. * * Return Value * VDDReserveIrqLine returns the IrqLine number (0-15) if successful. * Otherwise, this function returns 0xFFFF and logs an error. The * extended error code will be ERROR_INVALID_PARAMETER. * * Comments: * The value of an IrqLine number may range from 0-15 and correspond to * the irq line numbers of the virtual PICs (8259) emulated by the ntvdm * subsystem. Many of the line numbers are already used by the system * (e.g. for Timer, Keyboard, etc.), but there are a few free lines. VDDs * can take advantage of this and use the VDDSimulateInterrupt service to * reflect virtual interrupts specific to that VDD. This service provides * a way to manage the contention for the free irq lines. * * This service does not prevent VDDs that do not own a given IrqLine from * calling VDDSimulateInterrupt specifying that IrqLine. So it is important * to rely on this service, rather than expecting VDDSimulateInterrupt to * fail, to determine that a given IrqLine is available for use. * * This service may be called at any time. Typically, VDDs will use this * service at init time, and pass the number of the reserved IrqLine to * the vdm application/driver code. This code can then hook the corresponding * interrupt vector (8-15, 70-77) using the DOS Set Vector function * (Int 21h, func 25h) in order to handle the interrupts generated with * the VDDSimulateInterrupt service. */ WORD VDDReserveIrqLine ( HANDLE hVdd, WORD IrqLine) { WORD ReturnValue = 0xFFFF; if ((!hVdd) || ((IrqLine > MAX_IRQ_LINE) && (IrqLine != 0xFFFF)) ) { SetLastError(ERROR_INVALID_PARAMETER); return(ReturnValue); } host_ica_lock(); // acquire critical section if (IrqLine == 0xFFFF) { for (IrqLine = MAX_IRQ_LINE; IrqLine < 0xFFFF; IrqLine--) { if (IrqLines[IrqLine] == 0) { IrqLines[IrqLine] = hVdd; ReturnValue = IrqLine; break; } } } else if (IrqLines[IrqLine] == 0) { IrqLines[IrqLine] = hVdd; ReturnValue = IrqLine; } host_ica_unlock(); if (ReturnValue == 0xFFFF) SetLastError(ERROR_INVALID_PARAMETER); return(ReturnValue); } /*** VDDReleaseIrqLine - This service releases a lock on an Irq Line * obtained with VDDReserveIrqLine * * Parameters: * hVDD : VDD Handle * IrqLine : The specific IrqLine number (0-15) to release. * * Return Value: * VDDReleaseIrqLine returns TRUE if successful. * Otherwise, this function returns FALSE and logs an error. The * extended error code will be ERROR_INVALID_PARAMETER. * * Comments: * Upon successful execution of this function, the specified IrqLine will * be available to other VDDs. * * This service may be called at any time. */ BOOL VDDReleaseIrqLine ( HANDLE hVdd, WORD IrqLine) { BOOL Status = FALSE; if ((!hVdd) || (IrqLine > MAX_IRQ_LINE) ) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } host_ica_lock(); // acquire critical section if (IrqLines[IrqLine] == hVdd) { IrqLines[IrqLine] = 0; Status = TRUE; } host_ica_unlock(); if (!Status) SetLastError(ERROR_INVALID_PARAMETER); return(Status); } /********************************************************/ /* DMA stuff */ /*** VDDRequestDMA - This service is provided for VDDs to request a DMA * transfer. * * INPUT: * hVDD VDD Handle * iChannel DMA Channel on which the operation to take place * Buffer Buffer where to or from transfer to take place * length Transfer Count (in bytes) * * If Zero, returns the Current VDMA transfer count * in bytes. * * OUTPUT * DWORD returns bytes transferred * if Zero check GetLastError to determine if the * call failed or succeeded * GetLastError has the extended error information. * * NOTES * 1. This service is intended for those VDDs which do not want to * carry on the DMA operation on their own. Carrying on a DMA * operation involves understanding all the DMA registers and * figuring out what has to be copied, from where and how much. * * 2. This service will be slower than using VDDQueryDMA/VDDSetDMA and * doing the transfer on your own. * * 3. Extended Error codes: * * ERROR_ALREADY_EXISTS - Vdd already has active IO port handlers * ERROR_OUTOFMEMORY - Insufficient resources for additional VDD * Port handler set. * ERROR_INVALID_ADDRESS - One of the IO port handlers has an invalid * address. * */ DWORD VDDRequestDMA ( HANDLE hVDD, WORD iChannel, PVOID Buffer, DWORD length ) { DMA_ADAPT *pDmaAdp; DMA_CNTRL *pDcp; WORD Chan; WORD Size; WORD tCount; BOOL bMore; if (iChannel > DMA_CONTROLLER_CHANNELS*DMA_ADAPTOR_CONTROLLERS) { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } pDmaAdp = dmaGetAdaptor(); pDcp = &pDmaAdp->controller[dma_physical_controller(iChannel)]; Chan = dma_physical_channel(iChannel); Size = dma_unit_size(iChannel); // if the controller or the channel is disabled, return 0 if (pDcp->command.bits.controller_disable == 1 || (pDcp->mask & (1 << Chan)) == 0) return (0); tCount = ((WORD)pDcp->current_count[Chan][1] << 8) | (WORD)pDcp->current_count[Chan][0]; SetLastError(0); // assume success // return requested transfer count (in bytes) if (!length) { return (DWORD)Size*((DWORD)tCount + 1); } length = length/Size - 1; if (length > 0xFFFF) { length = 0xFFFF; } try { bMore = (BOOL) dma_request((half_word)iChannel, Buffer, (word) length); } except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_INVALID_ADDRESS); return 0; } if (!bMore) { // terminal count has been reached return ((DWORD)tCount+1) * (DWORD)Size; } tCount -= ((WORD)pDcp->current_count[Chan][1] << 8) | (WORD)pDcp->current_count[Chan][0]; return ((DWORD)tCount + 1) * (DWORD)Size; } /*** VDDQueryDMA - This service is provided for VDDs to collect all the DMA * data. * * INPUT: * hVDD VDD Handle * iChannel DMA Channel for which to query * Buffer Buffer where information will be returned * * OUTPUT * SUCCESS : Returns TRUE * FAILURE : Returns FALSE * GetLastError has the extended error information. * * * NOTES * 1. This service is intended for those VDD which are doing * performance critical work. These VDD can do their own DMA * transfers and avoid one extra buffer copying which is a * overhead in using VDDRequestDMA. * * 2. VDDs should use VDDSetDMA to properly update the state of * DMA after carrying on the operation. * * 3. Extended Error codes: * * ERROR_INVALID_ADDRESS - Invalid channel * */ BOOL VDDQueryDMA ( HANDLE hVDD, WORD iChannel, PVDD_DMA_INFO pDmaInfo) { DMA_ADAPT *pDmaAdp; DMA_CNTRL *pDcp; WORD Chan; if (iChannel > DMA_CONTROLLER_CHANNELS*DMA_ADAPTOR_CONTROLLERS) { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } pDmaAdp = dmaGetAdaptor(); pDcp = &pDmaAdp->controller[dma_physical_controller(iChannel)]; Chan = dma_physical_channel(iChannel); pDmaInfo->addr = ((WORD)pDcp->current_address[Chan][1] << 8) | (WORD)pDcp->current_address[Chan][0]; pDmaInfo->count = ((WORD)pDcp->current_count[Chan][1] << 8) | (WORD)pDcp->current_count[Chan][0]; pDmaInfo->page = (WORD) pDmaAdp->pages.page[iChannel]; pDmaInfo->status = (BYTE) pDcp->status.all; pDmaInfo->mode = (BYTE) pDcp->mode[Chan].all; pDmaInfo->mask = (BYTE) pDcp->mask; return TRUE; } /*** VDDSetDMA - This service is provided for VDDs to set the DMA data. * * INPUT: * hVDD VDD Handle * iChannel DMA Channel for which to query * fDMA Bit Mask indicating which DMA data fields are to be set * VDD_DMA_ADDR * VDD_DMA_COUNT * VDD_DMA_PAGE * VDD_DMA_STATUS * Buffer Buffer with DMA data * * OUTPUT * SUCCESS : Returns TRUE * FAILURE : Returns FALSE * GetLastError has the extended error information. * * NOTES * * 1. Extended Error codes: * * ERROR_INVALID_ADDRESS - Invalid channel * */ BOOL VDDSetDMA ( HANDLE hVDD, WORD iChannel, WORD fDMA, PVDD_DMA_INFO pDmaInfo) { DMA_ADAPT *pDmaAdp; DMA_CNTRL *pDcp; WORD Chan; if (iChannel > DMA_CONTROLLER_CHANNELS*DMA_ADAPTOR_CONTROLLERS) { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } pDmaAdp = dmaGetAdaptor(); pDcp = &pDmaAdp->controller[dma_physical_controller(iChannel)]; Chan = dma_physical_channel(iChannel); if (fDMA & VDD_DMA_ADDR) { pDcp->current_address[Chan][1] = (half_word)HIBYTE(pDmaInfo->addr); pDcp->current_address[Chan][0] = (half_word)LOBYTE(pDmaInfo->addr); } if (fDMA & VDD_DMA_COUNT) { pDcp->current_count[Chan][1] = (half_word)HIBYTE(pDmaInfo->count); pDcp->current_count[Chan][0] = (half_word)LOBYTE(pDmaInfo->count); } if (fDMA & VDD_DMA_PAGE) { pDmaAdp->pages.page[iChannel] = (half_word)pDmaInfo->page; } if (fDMA & VDD_DMA_STATUS) { pDcp->status.all = (BYTE) pDmaInfo->status; } return TRUE; }